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