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("frames_per_buffer", params.frames_per_buffer()); 114 dict.SetInteger("sample_rate", params.sample_rate()); 115 dict.SetInteger("channels", params.channels()); 116 dict.SetString("channel_layout", 117 ChannelLayoutToString(params.channel_layout())); 118 dict.SetString("effects", EffectsToString(params.effects())); 119 120 media_internals_->SendUpdateAndCache( 121 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); 122 } 123 124 void AudioLogImpl::OnStarted(int component_id) { 125 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "started"); 126 } 127 128 void AudioLogImpl::OnStopped(int component_id) { 129 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "stopped"); 130 } 131 132 void AudioLogImpl::OnClosed(int component_id) { 133 base::DictionaryValue dict; 134 StoreComponentMetadata(component_id, &dict); 135 dict.SetString(kAudioLogStatusKey, "closed"); 136 media_internals_->SendUpdateAndPurgeCache( 137 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); 138 } 139 140 void AudioLogImpl::OnError(int component_id) { 141 SendSingleStringUpdate(component_id, "error_occurred", "true"); 142 } 143 144 void AudioLogImpl::OnSetVolume(int component_id, double volume) { 145 base::DictionaryValue dict; 146 StoreComponentMetadata(component_id, &dict); 147 dict.SetDouble("volume", volume); 148 media_internals_->SendUpdateAndCache( 149 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); 150 } 151 152 std::string AudioLogImpl::FormatCacheKey(int component_id) { 153 return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id); 154 } 155 156 void AudioLogImpl::SendSingleStringUpdate(int component_id, 157 const std::string& key, 158 const std::string& value) { 159 base::DictionaryValue dict; 160 StoreComponentMetadata(component_id, &dict); 161 dict.SetString(key, value); 162 media_internals_->SendUpdateAndCache( 163 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); 164 } 165 166 void AudioLogImpl::StoreComponentMetadata(int component_id, 167 base::DictionaryValue* dict) { 168 dict->SetInteger("owner_id", owner_id_); 169 dict->SetInteger("component_id", component_id); 170 dict->SetInteger("component_type", component_); 171 } 172 173 MediaInternals* MediaInternals::GetInstance() { 174 return g_media_internals.Pointer(); 175 } 176 177 MediaInternals::MediaInternals() : owner_ids_() {} 178 MediaInternals::~MediaInternals() {} 179 180 void MediaInternals::OnMediaEvents( 181 int render_process_id, const std::vector<media::MediaLogEvent>& events) { 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 183 // Notify observers that |event| has occurred. 184 for (std::vector<media::MediaLogEvent>::const_iterator event = events.begin(); 185 event != events.end(); ++event) { 186 base::DictionaryValue dict; 187 dict.SetInteger("renderer", render_process_id); 188 dict.SetInteger("player", event->id); 189 dict.SetString("type", media::MediaLog::EventTypeToString(event->type)); 190 191 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be 192 // converted to to a human readable time format. See base/time/time.h. 193 const double ticks = event->time.ToInternalValue(); 194 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond; 195 dict.SetDouble("ticksMillis", ticks_millis); 196 dict.Set("params", event->params.DeepCopy()); 197 SendUpdate(SerializeUpdate("media.onMediaEvent", &dict)); 198 } 199 } 200 201 void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 203 update_callbacks_.push_back(callback); 204 } 205 206 void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) { 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 208 for (size_t i = 0; i < update_callbacks_.size(); ++i) { 209 if (update_callbacks_[i].Equals(callback)) { 210 update_callbacks_.erase(update_callbacks_.begin() + i); 211 return; 212 } 213 } 214 NOTREACHED(); 215 } 216 217 void MediaInternals::SendEverything() { 218 base::string16 everything_update; 219 { 220 base::AutoLock auto_lock(lock_); 221 everything_update = SerializeUpdate( 222 "media.onReceiveEverything", &cached_data_); 223 } 224 SendUpdate(everything_update); 225 } 226 227 void MediaInternals::SendUpdate(const base::string16& update) { 228 // SendUpdate() may be called from any thread, but must run on the IO thread. 229 // TODO(dalecurtis): This is pretty silly since the update callbacks simply 230 // forward the calls to the UI thread. We should avoid the extra hop. 231 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 232 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 233 &MediaInternals::SendUpdate, base::Unretained(this), update)); 234 return; 235 } 236 237 for (size_t i = 0; i < update_callbacks_.size(); i++) 238 update_callbacks_[i].Run(update); 239 } 240 241 scoped_ptr<media::AudioLog> MediaInternals::CreateAudioLog( 242 AudioComponent component) { 243 base::AutoLock auto_lock(lock_); 244 return scoped_ptr<media::AudioLog>(new AudioLogImpl( 245 owner_ids_[component]++, component, this)); 246 } 247 248 void MediaInternals::SendUpdateAndCache(const std::string& cache_key, 249 const std::string& function, 250 const base::DictionaryValue* value) { 251 SendUpdate(SerializeUpdate(function, value)); 252 253 base::AutoLock auto_lock(lock_); 254 if (!cached_data_.HasKey(cache_key)) { 255 cached_data_.Set(cache_key, value->DeepCopy()); 256 return; 257 } 258 259 base::DictionaryValue* existing_dict = NULL; 260 CHECK(cached_data_.GetDictionary(cache_key, &existing_dict)); 261 existing_dict->MergeDictionary(value); 262 } 263 264 void MediaInternals::SendUpdateAndPurgeCache( 265 const std::string& cache_key, 266 const std::string& function, 267 const base::DictionaryValue* value) { 268 SendUpdate(SerializeUpdate(function, value)); 269 270 base::AutoLock auto_lock(lock_); 271 scoped_ptr<base::Value> out_value; 272 CHECK(cached_data_.Remove(cache_key, &out_value)); 273 } 274 275 } // namespace content 276