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