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 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("output_channels", params.channels()); | |
| 87 dict.SetString("output_channel_layout", | |
| 88 ChannelLayoutToString(params.channel_layout())); | |
| 89 | |
| 90 media_internals_->SendUpdateAndCache( | |
| 91 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); | |
| 20 } | 92 } |
| 21 | 93 |
| 22 MediaInternals::~MediaInternals() {} | 94 void AudioLogImpl::OnStarted(int component_id) { |
| 23 | 95 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "started"); |
| 24 namespace { | |
| 25 std::string FormatAudioStreamName(void* host, int stream_id) { | |
| 26 return base::StringPrintf("audio_streams.%p:%d", host, stream_id); | |
| 27 } | |
| 28 } | 96 } |
| 29 | 97 |
| 30 void MediaInternals::OnDeleteAudioStream(void* host, int stream_id) { | 98 void AudioLogImpl::OnStopped(int component_id) { |
| 31 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 99 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "stopped"); |
| 32 DeleteItem(FormatAudioStreamName(host, stream_id)); | |
| 33 } | 100 } |
| 34 | 101 |
| 35 void MediaInternals::OnSetAudioStreamPlaying( | 102 void AudioLogImpl::OnClosed(int component_id) { |
| 36 void* host, int stream_id, bool playing) { | 103 base::DictionaryValue dict; |
| 37 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 104 StoreComponentMetadata(component_id, &dict); |
| 38 UpdateAudioStream(host, stream_id, | 105 dict.SetString(kAudioLogStatusKey, "closed"); |
| 39 "playing", new base::FundamentalValue(playing)); | 106 media_internals_->SendUpdateAndPurgeCache( |
| 107 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); | |
| 40 } | 108 } |
| 41 | 109 |
| 42 void MediaInternals::OnAudioStreamCreated(void* host, | 110 void AudioLogImpl::OnError(int component_id) { |
| 43 int stream_id, | 111 SendSingleStringUpdate(component_id, "error_occurred", "true"); |
| 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 } | 112 } |
| 87 | 113 |
| 88 void MediaInternals::OnSetAudioStreamStatus(void* host, | 114 void AudioLogImpl::OnSetVolume(int component_id, double volume) { |
| 89 int stream_id, | 115 base::DictionaryValue dict; |
| 90 const std::string& status) { | 116 StoreComponentMetadata(component_id, &dict); |
| 91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 117 dict.SetDouble("volume", volume); |
| 92 UpdateAudioStream(host, stream_id, | 118 media_internals_->SendUpdateAndCache( |
| 93 "status", new base::StringValue(status)); | 119 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict); |
| 94 } | 120 } |
| 95 | 121 |
| 96 void MediaInternals::OnSetAudioStreamVolume( | 122 std::string AudioLogImpl::FormatCacheKey(int component_id) { |
| 97 void* host, int stream_id, double volume) { | 123 return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id); |
| 98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 99 UpdateAudioStream(host, stream_id, | |
| 100 "volume", new base::FundamentalValue(volume)); | |
| 101 } | 124 } |
| 102 | 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 | |
| 103 void MediaInternals::OnMediaEvents( | 150 void MediaInternals::OnMediaEvents( |
| 104 int render_process_id, const std::vector<media::MediaLogEvent>& events) { | 151 int render_process_id, const std::vector<media::MediaLogEvent>& events) { |
| 105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 106 | 153 // Notify observers that |event| has occurred. |
| 107 // Notify observers that |event| has occured. | |
| 108 for (std::vector<media::MediaLogEvent>::const_iterator event = events.begin(); | 154 for (std::vector<media::MediaLogEvent>::const_iterator event = events.begin(); |
| 109 event != events.end(); ++event) { | 155 event != events.end(); ++event) { |
| 110 base::DictionaryValue dict; | 156 base::DictionaryValue dict; |
| 111 dict.SetInteger("renderer", render_process_id); | 157 dict.SetInteger("renderer", render_process_id); |
| 112 dict.SetInteger("player", event->id); | 158 dict.SetInteger("player", event->id); |
| 113 dict.SetString("type", media::MediaLog::EventTypeToString(event->type)); | 159 dict.SetString("type", media::MediaLog::EventTypeToString(event->type)); |
| 114 | 160 |
| 115 int64 ticks = event->time.ToInternalValue(); | 161 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be |
| 116 double ticks_millis = | 162 // converted to to a human readable time format. See base/time/time.h. |
| 117 ticks / static_cast<double>(base::Time::kMicrosecondsPerMillisecond); | 163 const double ticks = event->time.ToInternalValue(); |
| 118 | 164 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond; |
| 119 dict.SetDouble("ticksMillis", ticks_millis); | 165 dict.SetDouble("ticksMillis", ticks_millis); |
| 120 dict.Set("params", event->params.DeepCopy()); | 166 dict.Set("params", event->params.DeepCopy()); |
| 121 SendUpdate("media.onMediaEvent", &dict); | 167 SendUpdate(SerializeUpdate("media.onMediaEvent", &dict)); |
| 122 } | 168 } |
| 123 } | 169 } |
| 124 | 170 |
| 125 void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) { | 171 void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) { |
| 172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 126 update_callbacks_.push_back(callback); | 173 update_callbacks_.push_back(callback); |
| 127 } | 174 } |
| 128 | 175 |
| 129 void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) { | 176 void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) { |
| 177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 130 for (size_t i = 0; i < update_callbacks_.size(); ++i) { | 178 for (size_t i = 0; i < update_callbacks_.size(); ++i) { |
| 131 if (update_callbacks_[i].Equals(callback)) { | 179 if (update_callbacks_[i].Equals(callback)) { |
| 132 update_callbacks_.erase(update_callbacks_.begin() + i); | 180 update_callbacks_.erase(update_callbacks_.begin() + i); |
| 133 return; | 181 return; |
| 134 } | 182 } |
| 135 } | 183 } |
| 136 NOTREACHED(); | 184 NOTREACHED(); |
| 137 } | 185 } |
| 138 | 186 |
| 139 void MediaInternals::SendEverything() { | 187 void MediaInternals::SendEverything() { |
| 140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 188 base::AutoLock auto_lock(cache_lock_); |
| 141 SendUpdate("media.onReceiveEverything", &data_); | 189 SendUpdate(SerializeUpdate("media.onReceiveEverything", &cached_data_)); |
| 142 } | 190 } |
| 143 | 191 |
| 144 MediaInternals::MediaInternals() { | 192 void MediaInternals::SendUpdate(const string16& update) { |
| 145 } | 193 // SendUpdate() may be called from any thread, but must run on the IO thread. |
| 194 // TODO(dalecurtis): This is pretty silly since the update callbacks simply | |
| 195 // forward the calls to the UI thread. We should avoid the extra hop. | |
| 196 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
| 197 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( | |
| 198 &MediaInternals::SendUpdate, base::Unretained(this), update)); | |
| 199 return; | |
| 200 } | |
| 146 | 201 |
| 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++) | 202 for (size_t i = 0; i < update_callbacks_.size(); i++) |
| 201 update_callbacks_[i].Run(update); | 203 update_callbacks_[i].Run(update); |
| 202 } | 204 } |
| 203 | 205 |
| 206 scoped_ptr<media::AudioLog> MediaInternals::CreateAudioLog( | |
| 207 AudioComponent component) { | |
| 208 base::AutoLock auto_lock(cache_lock_); | |
| 209 return scoped_ptr<media::AudioLog>(new AudioLogImpl( | |
| 210 owner_ids_[component]++, component, this)); | |
| 211 } | |
| 212 | |
| 213 void MediaInternals::SendUpdateAndCache(const std::string& cache_key, | |
| 214 const std::string& function, | |
| 215 const base::DictionaryValue* value) { | |
| 216 base::AutoLock auto_lock(cache_lock_); | |
| 217 SendUpdate(SerializeUpdate(function, value)); | |
|
acolwell GONE FROM CHROMIUM
2013/11/26 01:33:17
This holds the lock while dispatching to arbitrary
DaleCurtis
2013/11/26 03:02:21
Done.
| |
| 218 | |
| 219 if (!cached_data_.HasKey(cache_key)) { | |
| 220 cached_data_.Set(cache_key, value->DeepCopy()); | |
| 221 return; | |
| 222 } | |
| 223 | |
| 224 base::DictionaryValue* existing_dict = NULL; | |
| 225 CHECK(cached_data_.GetDictionary(cache_key, &existing_dict)); | |
| 226 existing_dict->MergeDictionary(value); | |
| 227 } | |
| 228 | |
| 229 void MediaInternals::SendUpdateAndPurgeCache( | |
| 230 const std::string& cache_key, | |
| 231 const std::string& function, | |
| 232 const base::DictionaryValue* value) { | |
| 233 base::AutoLock auto_lock(cache_lock_); | |
| 234 SendUpdate(SerializeUpdate(function, value)); | |
| 235 | |
| 236 scoped_ptr<base::Value> out_value; | |
| 237 CHECK(cached_data_.Remove(cache_key, &out_value)); | |
| 238 } | |
| 239 | |
| 204 } // namespace content | 240 } // namespace content |
| OLD | NEW |