1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/media/media_internals.h" 6 7 #include "base/strings/string16.h" 8 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/stringprintf.h" 10 #include "content/public/browser/browser_thread.h" 11 #include "content/public/browser/web_ui.h" 12 #include "media/audio/audio_parameters.h" 13 #include "media/base/media_log.h" 14 #include "media/base/media_log_event.h" 15 16 namespace { 17 18 static base::LazyInstance<content::MediaInternals>::Leaky g_media_internals = 19 LAZY_INSTANCE_INITIALIZER; 20 21 base::string16 SerializeUpdate(const std::string& function, 22 const base::Value* value) { 23 return content::WebUI::GetJavascriptCall( 24 function, std::vector<const base::Value*>(1, value)); 25 } 26 27 std::string EffectsToString(int effects) { 28 if (effects == media::AudioParameters::NO_EFFECTS) 29 return "NO_EFFECTS"; 30 31 struct { 32 int flag; 33 const char* name; 34 } flags[] = { 35 { media::AudioParameters::ECHO_CANCELLER, "ECHO_CANCELLER" }, 36 { media::AudioParameters::DUCKING, "DUCKING" }, 37 { media::AudioParameters::KEYBOARD_MIC, "KEYBOARD_MIC" }, 38 }; 39 40 std::string ret; 41 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(flags); ++i) { 42 if (effects & flags[i].flag) { 43 if (!ret.empty()) 44 ret += " | "; 45 ret += flags[i].name; 46 effects &= ~flags[i].flag; 47 } 48 } 49 50 if (effects) { 51 if (!ret.empty()) 52 ret += " | "; 53 ret += base::IntToString(effects); 54 } 55 56 return ret; 57 } 58 59 const char kAudioLogStatusKey[] = "status"; 60 const char kAudioLogUpdateFunction[] = "media.updateAudioComponent"; 61 62 } // namespace 63 64 namespace content { 65 66 class AudioLogImpl : public media::AudioLog { 67 public: 68 AudioLogImpl(int owner_id, 69 media::AudioLogFactory::AudioComponent component, 70 content::MediaInternals* media_internals); 71 virtual ~AudioLogImpl(); 72 73 virtual void OnCreated(int component_id, 74 const media::AudioParameters& params, 75 const std::string& device_id) OVERRIDE; 76 virtual void OnStarted(int component_id) OVERRIDE; 77 virtual void OnStopped(int component_id) OVERRIDE; 78 virtual void OnClosed(int component_id) OVERRIDE; 79 virtual void OnError(int component_id) OVERRIDE; 80 virtual void OnSetVolume(int component_id, double volume) OVERRIDE; 81 82 private: 83 void SendSingleStringUpdate(int component_id, 84 const std::string& key, 85 const std::string& value); 86 void StoreComponentMetadata(int component_id, base::DictionaryValue* dict); 87 std::string FormatCacheKey(int component_id); 88 89 const int owner_id_; 90 const media::AudioLogFactory::AudioComponent component_; 91 content::MediaInternals* const media_internals_; 92 93 DISALLOW_COPY_AND_ASSIGN(AudioLogImpl); 94 }; 95 96 AudioLogImpl::AudioLogImpl(int owner_id, 97 media::AudioLogFactory::AudioComponent component, 98 content::MediaInternals* media_internals) 99 : owner_id_(owner_id), 100 component_(component), 101 media_internals_(media_internals) {} 102 103 AudioLogImpl::~AudioLogImpl() {} 104 105 void AudioLogImpl::OnCreated(int component_id, 106 const media::AudioParameters& params, 107 const std::string& device_id) { 108 base::DictionaryValue dict; 109 StoreComponentMetadata(component_id, &dict); 110 111 dict.SetString(kAudioLogStatusKey, "created"); 112 dict.SetString("device_id", device_id); 113 dict.SetInteger("input_channels", params.input_channels()); 114 dict.SetInteger("frames_per_buffer", params.frames_per_buffer()); 115 dict.SetInteger("sample_rate", params.sample_rate()); 116 dict.SetInteger("channels", params.channels()); 117 dict.SetString("channel_layout", 118 ChannelLayoutToString(params.channel_layout())); 119 dict.SetString("effects", EffectsToString(params.effects())); 120 121 media_internals_->SendUpdateAndCache( 122 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); 123 } 124 125 void AudioLogImpl::OnStarted(int component_id) { 126 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "started"); 127 } 128 129 void AudioLogImpl::OnStopped(int component_id) { 130 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "stopped"); 131 } 132 133 void AudioLogImpl::OnClosed(int component_id) { 134 base::DictionaryValue dict; 135 StoreComponentMetadata(component_id, &dict); 136 dict.SetString(kAudioLogStatusKey, "closed"); 137 media_internals_->SendUpdateAndPurgeCache( 138 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); 139 } 140 141 void AudioLogImpl::OnError(int component_id) { 142 SendSingleStringUpdate(component_id, "error_occurred", "true"); 143 } 144 145 void AudioLogImpl::OnSetVolume(int component_id, double volume) { 146 base::DictionaryValue dict; 147 StoreComponentMetadata(component_id, &dict); 148 dict.SetDouble("volume", volume); 149 media_internals_->SendUpdateAndCache( 150 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); 151 } 152 153 std::string AudioLogImpl::FormatCacheKey(int component_id) { 154 return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id); 155 } 156 157 void AudioLogImpl::SendSingleStringUpdate(int component_id, 158 const std::string& key, 159 const std::string& value) { 160 base::DictionaryValue dict; 161 StoreComponentMetadata(component_id, &dict); 162 dict.SetString(key, value); 163 media_internals_->SendUpdateAndCache( 164 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); 165 } 166 167 void AudioLogImpl::StoreComponentMetadata(int component_id, 168 base::DictionaryValue* dict) { 169 dict->SetInteger("owner_id", owner_id_); 170 dict->SetInteger("component_id", component_id); 171 dict->SetInteger("component_type", component_); 172 } 173 174 MediaInternals* MediaInternals::GetInstance() { 175 return g_media_internals.Pointer(); 176 } 177 178 MediaInternals::MediaInternals() : owner_ids_() {} 179 MediaInternals::~MediaInternals() {} 180 181 void MediaInternals::OnMediaEvents( 182 int render_process_id, const std::vector<media::MediaLogEvent>& events) { 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 184 // Notify observers that |event| has occurred. 185 for (std::vector<media::MediaLogEvent>::const_iterator event = events.begin(); 186 event != events.end(); ++event) { 187 base::DictionaryValue dict; 188 dict.SetInteger("renderer", render_process_id); 189 dict.SetInteger("player", event->id); 190 dict.SetString("type", media::MediaLog::EventTypeToString(event->type)); 191 192 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be 193 // converted to to a human readable time format. See base/time/time.h. 194 const double ticks = event->time.ToInternalValue(); 195 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond; 196 dict.SetDouble("ticksMillis", ticks_millis); 197 dict.Set("params", event->params.DeepCopy()); 198 SendUpdate(SerializeUpdate("media.onMediaEvent", &dict)); 199 } 200 } 201 202 void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) { 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 204 update_callbacks_.push_back(callback); 205 } 206 207 void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) { 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 209 for (size_t i = 0; i < update_callbacks_.size(); ++i) { 210 if (update_callbacks_[i].Equals(callback)) { 211 update_callbacks_.erase(update_callbacks_.begin() + i); 212 return; 213 } 214 } 215 NOTREACHED(); 216 } 217 218 void MediaInternals::SendEverything() { 219 base::string16 everything_update; 220 { 221 base::AutoLock auto_lock(lock_); 222 everything_update = SerializeUpdate( 223 "media.onReceiveEverything", &cached_data_); 224 } 225 SendUpdate(everything_update); 226 } 227 228 void MediaInternals::SendUpdate(const base::string16& update) { 229 // SendUpdate() may be called from any thread, but must run on the IO thread. 230 // TODO(dalecurtis): This is pretty silly since the update callbacks simply 231 // forward the calls to the UI thread. We should avoid the extra hop. 232 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 233 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 234 &MediaInternals::SendUpdate, base::Unretained(this), update)); 235 return; 236 } 237 238 for (size_t i = 0; i < update_callbacks_.size(); i++) 239 update_callbacks_[i].Run(update); 240 } 241 242 scoped_ptr<media::AudioLog> MediaInternals::CreateAudioLog( 243 AudioComponent component) { 244 base::AutoLock auto_lock(lock_); 245 return scoped_ptr<media::AudioLog>(new AudioLogImpl( 246 owner_ids_[component]++, component, this)); 247 } 248 249 void MediaInternals::SendUpdateAndCache(const std::string& cache_key, 250 const std::string& function, 251 const base::DictionaryValue* value) { 252 SendUpdate(SerializeUpdate(function, value)); 253 254 base::AutoLock auto_lock(lock_); 255 if (!cached_data_.HasKey(cache_key)) { 256 cached_data_.Set(cache_key, value->DeepCopy()); 257 return; 258 } 259 260 base::DictionaryValue* existing_dict = NULL; 261 CHECK(cached_data_.GetDictionary(cache_key, &existing_dict)); 262 existing_dict->MergeDictionary(value); 263 } 264 265 void MediaInternals::SendUpdateAndPurgeCache( 266 const std::string& cache_key, 267 const std::string& function, 268 const base::DictionaryValue* value) { 269 SendUpdate(SerializeUpdate(function, value)); 270 271 base::AutoLock auto_lock(lock_); 272 scoped_ptr<base::Value> out_value; 273 CHECK(cached_data_.Remove(cache_key, &out_value)); 274 } 275 276 } // namespace content 277