blob: d57e57db9ba1dcadc5a33b48189a010c92abace1 [file] [log] [blame]
[email protected]11158e2d2013-02-01 02:31:561// 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
prabhur53bb9182014-11-13 03:25:177#include "base/metrics/histogram.h"
[email protected]348fbaac2013-06-11 06:31:518#include "base/strings/string16.h"
[email protected]9367e032014-03-07 19:42:379#include "base/strings/string_number_conversions.h"
[email protected]348fbaac2013-06-11 06:31:5110#include "base/strings/stringprintf.h"
[email protected]11158e2d2013-02-01 02:31:5611#include "content/public/browser/browser_thread.h"
prabhur53bb9182014-11-13 03:25:1712#include "content/public/browser/notification_observer.h"
13#include "content/public/browser/notification_registrar.h"
14#include "content/public/browser/notification_service.h"
15#include "content/public/browser/notification_types.h"
16#include "content/public/browser/render_process_host.h"
[email protected]11158e2d2013-02-01 02:31:5617#include "content/public/browser/web_ui.h"
[email protected]e2fd1f72013-08-16 00:34:2518#include "media/audio/audio_parameters.h"
[email protected]11158e2d2013-02-01 02:31:5619#include "media/base/media_log_event.h"
prabhurc4812392014-12-05 19:59:4220#include "media/filters/decrypting_video_decoder.h"
prabhur957d46c72014-11-19 03:01:1621#include "media/filters/gpu_video_decoder.h"
[email protected]11158e2d2013-02-01 02:31:5622
[email protected]69946cf2013-11-27 00:11:4223namespace {
24
25static base::LazyInstance<content::MediaInternals>::Leaky g_media_internals =
26 LAZY_INSTANCE_INITIALIZER;
27
[email protected]fcf75d42013-12-03 20:11:2628base::string16 SerializeUpdate(const std::string& function,
29 const base::Value* value) {
[email protected]69946cf2013-11-27 00:11:4230 return content::WebUI::GetJavascriptCall(
31 function, std::vector<const base::Value*>(1, value));
32}
33
[email protected]9367e032014-03-07 19:42:3734std::string EffectsToString(int effects) {
35 if (effects == media::AudioParameters::NO_EFFECTS)
36 return "NO_EFFECTS";
37
38 struct {
39 int flag;
40 const char* name;
41 } flags[] = {
42 { media::AudioParameters::ECHO_CANCELLER, "ECHO_CANCELLER" },
43 { media::AudioParameters::DUCKING, "DUCKING" },
[email protected]81495eb2014-03-19 06:08:1844 { media::AudioParameters::KEYBOARD_MIC, "KEYBOARD_MIC" },
[email protected]9367e032014-03-07 19:42:3745 };
46
47 std::string ret;
viettrungluu2dfaba72014-10-16 05:30:2548 for (size_t i = 0; i < arraysize(flags); ++i) {
[email protected]9367e032014-03-07 19:42:3749 if (effects & flags[i].flag) {
50 if (!ret.empty())
51 ret += " | ";
52 ret += flags[i].name;
53 effects &= ~flags[i].flag;
54 }
55 }
56
57 if (effects) {
58 if (!ret.empty())
59 ret += " | ";
60 ret += base::IntToString(effects);
61 }
62
63 return ret;
64}
65
[email protected]69946cf2013-11-27 00:11:4266const char kAudioLogStatusKey[] = "status";
67const char kAudioLogUpdateFunction[] = "media.updateAudioComponent";
68
69} // namespace
70
[email protected]11158e2d2013-02-01 02:31:5671namespace content {
72
[email protected]69946cf2013-11-27 00:11:4273class AudioLogImpl : public media::AudioLog {
74 public:
75 AudioLogImpl(int owner_id,
76 media::AudioLogFactory::AudioComponent component,
77 content::MediaInternals* media_internals);
dchengc2282aa2014-10-21 12:07:5878 ~AudioLogImpl() override;
[email protected]69946cf2013-11-27 00:11:4279
dchengc2282aa2014-10-21 12:07:5880 void OnCreated(int component_id,
81 const media::AudioParameters& params,
82 const std::string& device_id) override;
83 void OnStarted(int component_id) override;
84 void OnStopped(int component_id) override;
85 void OnClosed(int component_id) override;
86 void OnError(int component_id) override;
87 void OnSetVolume(int component_id, double volume) override;
[email protected]69946cf2013-11-27 00:11:4288
89 private:
90 void SendSingleStringUpdate(int component_id,
91 const std::string& key,
92 const std::string& value);
93 void StoreComponentMetadata(int component_id, base::DictionaryValue* dict);
94 std::string FormatCacheKey(int component_id);
95
96 const int owner_id_;
97 const media::AudioLogFactory::AudioComponent component_;
98 content::MediaInternals* const media_internals_;
99
100 DISALLOW_COPY_AND_ASSIGN(AudioLogImpl);
101};
102
103AudioLogImpl::AudioLogImpl(int owner_id,
104 media::AudioLogFactory::AudioComponent component,
105 content::MediaInternals* media_internals)
106 : owner_id_(owner_id),
107 component_(component),
108 media_internals_(media_internals) {}
109
110AudioLogImpl::~AudioLogImpl() {}
111
112void AudioLogImpl::OnCreated(int component_id,
113 const media::AudioParameters& params,
[email protected]25d7f892014-02-13 15:22:45114 const std::string& device_id) {
[email protected]69946cf2013-11-27 00:11:42115 base::DictionaryValue dict;
116 StoreComponentMetadata(component_id, &dict);
117
118 dict.SetString(kAudioLogStatusKey, "created");
[email protected]25d7f892014-02-13 15:22:45119 dict.SetString("device_id", device_id);
[email protected]69946cf2013-11-27 00:11:42120 dict.SetInteger("frames_per_buffer", params.frames_per_buffer());
121 dict.SetInteger("sample_rate", params.sample_rate());
[email protected]ad82f412013-11-27 04:20:41122 dict.SetInteger("channels", params.channels());
123 dict.SetString("channel_layout",
[email protected]69946cf2013-11-27 00:11:42124 ChannelLayoutToString(params.channel_layout()));
[email protected]9367e032014-03-07 19:42:37125 dict.SetString("effects", EffectsToString(params.effects()));
[email protected]69946cf2013-11-27 00:11:42126
mcasasfcb5c7de2014-10-11 20:22:17127 media_internals_->SendUpdateAndCacheAudioStreamKey(
[email protected]69946cf2013-11-27 00:11:42128 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
129}
130
131void AudioLogImpl::OnStarted(int component_id) {
132 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "started");
133}
134
135void AudioLogImpl::OnStopped(int component_id) {
136 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "stopped");
137}
138
139void AudioLogImpl::OnClosed(int component_id) {
140 base::DictionaryValue dict;
141 StoreComponentMetadata(component_id, &dict);
142 dict.SetString(kAudioLogStatusKey, "closed");
mcasasfcb5c7de2014-10-11 20:22:17143 media_internals_->SendUpdateAndPurgeAudioStreamCache(
[email protected]69946cf2013-11-27 00:11:42144 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
145}
146
147void AudioLogImpl::OnError(int component_id) {
148 SendSingleStringUpdate(component_id, "error_occurred", "true");
149}
150
151void AudioLogImpl::OnSetVolume(int component_id, double volume) {
152 base::DictionaryValue dict;
153 StoreComponentMetadata(component_id, &dict);
154 dict.SetDouble("volume", volume);
mcasasfcb5c7de2014-10-11 20:22:17155 media_internals_->SendUpdateAndCacheAudioStreamKey(
[email protected]69946cf2013-11-27 00:11:42156 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
157}
158
159std::string AudioLogImpl::FormatCacheKey(int component_id) {
160 return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id);
161}
162
163void AudioLogImpl::SendSingleStringUpdate(int component_id,
164 const std::string& key,
165 const std::string& value) {
166 base::DictionaryValue dict;
167 StoreComponentMetadata(component_id, &dict);
168 dict.SetString(key, value);
mcasasfcb5c7de2014-10-11 20:22:17169 media_internals_->SendUpdateAndCacheAudioStreamKey(
[email protected]69946cf2013-11-27 00:11:42170 FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
171}
172
173void AudioLogImpl::StoreComponentMetadata(int component_id,
174 base::DictionaryValue* dict) {
175 dict->SetInteger("owner_id", owner_id_);
176 dict->SetInteger("component_id", component_id);
177 dict->SetInteger("component_type", component_);
178}
179
prabhur53bb9182014-11-13 03:25:17180class MediaInternals::MediaInternalsUMAHandler : public NotificationObserver {
181 public:
182 MediaInternalsUMAHandler();
183
184 // NotificationObserver implementation.
185 void Observe(int type,
186 const NotificationSource& source,
187 const NotificationDetails& details) override;
188
189 // Reports the pipeline status to UMA for every player
190 // associated with the renderer process and then deletes the player state.
191 void LogAndClearPlayersInRenderer(int render_process_id);
192
193 // Helper function to save the event payload to RendererPlayerMap.
194 void SavePlayerState(const media::MediaLogEvent& event,
195 int render_process_id);
196
197 private:
198 struct PipelineInfo {
199 media::PipelineStatus last_pipeline_status;
200 bool has_audio;
201 bool has_video;
prabhurc4812392014-12-05 19:59:42202 bool video_dds;
prabhur53bb9182014-11-13 03:25:17203 std::string audio_codec_name;
204 std::string video_codec_name;
205 std::string video_decoder;
206 PipelineInfo()
207 : last_pipeline_status(media::PIPELINE_OK),
208 has_audio(false),
prabhurc4812392014-12-05 19:59:42209 has_video(false),
210 video_dds(false) {}
prabhur53bb9182014-11-13 03:25:17211 };
212
213 // Helper function to report PipelineStatus associated with a player to UMA.
214 void ReportUMAForPipelineStatus(const PipelineInfo& player_info);
215
prabhurc4812392014-12-05 19:59:42216 // Helper to generate PipelineStatus UMA name for AudioVideo streams.
217 std::string GetUMANameForAVStream(const PipelineInfo& player_info);
218
prabhur53bb9182014-11-13 03:25:17219 // Key is playerid
220 typedef std::map<int, PipelineInfo> PlayerInfoMap;
221
222 // Key is renderer id
223 typedef std::map<int, PlayerInfoMap> RendererPlayerMap;
224
225 // Stores player information per renderer
226 RendererPlayerMap renderer_info_;
227
228 NotificationRegistrar registrar_;
229
230 DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler);
231};
232
233MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler() {
234 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
235 NotificationService::AllBrowserContextsAndSources());
236}
237
238void MediaInternals::MediaInternalsUMAHandler::Observe(
239 int type,
240 const NotificationSource& source,
241 const NotificationDetails& details) {
242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
244 RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
245
246 // Post the task to the IO thread to avoid race in updating renderer_info_ map
247 // by both SavePlayerState & LogAndClearPlayersInRenderer from different
248 // threads.
249 // Using base::Unretained() on MediaInternalsUMAHandler is safe since
250 // it is owned by MediaInternals and share the same lifetime
251 BrowserThread::PostTask(
252 BrowserThread::IO, FROM_HERE,
253 base::Bind(&MediaInternalsUMAHandler::LogAndClearPlayersInRenderer,
254 base::Unretained(this), process->GetID()));
255}
256
257void MediaInternals::MediaInternalsUMAHandler::SavePlayerState(
258 const media::MediaLogEvent& event,
259 int render_process_id) {
260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
261 PlayerInfoMap& player_info = renderer_info_[render_process_id];
262 switch (event.type) {
263 case media::MediaLogEvent::WEBMEDIAPLAYER_CREATED: {
264 // Nothing to do here
265 break;
266 }
267 case media::MediaLogEvent::PIPELINE_ERROR: {
268 int status;
269 event.params.GetInteger("pipeline_error", &status);
270 player_info[event.id].last_pipeline_status =
271 static_cast<media::PipelineStatus>(status);
272 break;
273 }
274 case media::MediaLogEvent::PROPERTY_CHANGE:
275 if (event.params.HasKey("found_audio_stream")) {
276 event.params.GetBoolean("found_audio_stream",
277 &player_info[event.id].has_audio);
278 }
279 if (event.params.HasKey("found_video_stream")) {
280 event.params.GetBoolean("found_video_stream",
281 &player_info[event.id].has_video);
282 }
283 if (event.params.HasKey("audio_codec_name")) {
284 event.params.GetString("audio_codec_name",
285 &player_info[event.id].audio_codec_name);
286 }
287 if (event.params.HasKey("video_codec_name")) {
288 event.params.GetString("video_codec_name",
289 &player_info[event.id].video_codec_name);
290 }
291 if (event.params.HasKey("video_decoder")) {
292 event.params.GetString("video_decoder",
293 &player_info[event.id].video_decoder);
294 }
prabhurc4812392014-12-05 19:59:42295 if (event.params.HasKey("video_dds")) {
296 event.params.GetBoolean("video_dds", &player_info[event.id].video_dds);
297 }
prabhur53bb9182014-11-13 03:25:17298 break;
299 default:
300 break;
301 }
302 return;
303}
304
prabhurc4812392014-12-05 19:59:42305std::string MediaInternals::MediaInternalsUMAHandler::GetUMANameForAVStream(
306 const PipelineInfo& player_info) {
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
308 static const char kPipelineUmaPrefix[] = "Media.PipelineStatus.AudioVideo.";
309 std::string uma_name = kPipelineUmaPrefix;
310 if (player_info.video_codec_name == "vp8") {
311 uma_name += "VP8.";
312 } else if (player_info.video_codec_name == "vp9") {
313 uma_name += "VP9.";
314 } else if (player_info.video_codec_name == "h264") {
315 uma_name += "H264.";
316 } else {
317 return uma_name + "Other";
318 }
319
320 if (player_info.video_decoder ==
321 media::DecryptingVideoDecoder::kDecoderName) {
322 return uma_name + "DVD";
323 }
324
325 if (player_info.video_dds) {
326 uma_name += "DDS.";
327 }
328
329 if (player_info.video_decoder == media::GpuVideoDecoder::kDecoderName) {
330 uma_name += "HW";
331 } else {
332 uma_name += "SW";
333 }
334 return uma_name;
335}
336
prabhur53bb9182014-11-13 03:25:17337void MediaInternals::MediaInternalsUMAHandler::ReportUMAForPipelineStatus(
338 const PipelineInfo& player_info) {
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
340 if (player_info.has_video && player_info.has_audio) {
prabhurc4812392014-12-05 19:59:42341 base::LinearHistogram::FactoryGet(
342 GetUMANameForAVStream(player_info), 1, media::PIPELINE_STATUS_MAX,
343 media::PIPELINE_STATUS_MAX + 1,
344 base::HistogramBase::kUmaTargetedHistogramFlag)
345 ->Add(player_info.last_pipeline_status);
prabhur53bb9182014-11-13 03:25:17346 } else if (player_info.has_audio) {
prabhurdc15c402014-11-21 22:53:01347 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.AudioOnly",
prabhur53bb9182014-11-13 03:25:17348 player_info.last_pipeline_status,
349 media::PIPELINE_STATUS_MAX + 1);
350 } else if (player_info.has_video) {
prabhurdc15c402014-11-21 22:53:01351 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.VideoOnly",
prabhur53bb9182014-11-13 03:25:17352 player_info.last_pipeline_status,
353 media::PIPELINE_STATUS_MAX + 1);
354 } else {
355 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.Unsupported",
356 player_info.last_pipeline_status,
357 media::PIPELINE_STATUS_MAX + 1);
358 }
359}
360
361void MediaInternals::MediaInternalsUMAHandler::LogAndClearPlayersInRenderer(
362 int render_process_id) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
364 auto players_it = renderer_info_.find(render_process_id);
365 if (players_it == renderer_info_.end())
366 return;
367 auto it = players_it->second.begin();
368 while (it != players_it->second.end()) {
369 ReportUMAForPipelineStatus(it->second);
370 players_it->second.erase(it++);
371 }
372}
373
[email protected]11158e2d2013-02-01 02:31:56374MediaInternals* MediaInternals::GetInstance() {
[email protected]69946cf2013-11-27 00:11:42375 return g_media_internals.Pointer();
[email protected]11158e2d2013-02-01 02:31:56376}
377
prabhur53bb9182014-11-13 03:25:17378MediaInternals::MediaInternals()
379 : owner_ids_(), uma_handler_(new MediaInternalsUMAHandler()) {
380}
381
[email protected]11158e2d2013-02-01 02:31:56382MediaInternals::~MediaInternals() {}
383
[email protected]0e7ee582013-05-04 14:06:59384void MediaInternals::OnMediaEvents(
385 int render_process_id, const std::vector<media::MediaLogEvent>& events) {
[email protected]11158e2d2013-02-01 02:31:56386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]69946cf2013-11-27 00:11:42387 // Notify observers that |event| has occurred.
prabhur53bb9182014-11-13 03:25:17388 for (auto event = events.begin(); event != events.end(); ++event) {
[email protected]0e7ee582013-05-04 14:06:59389 base::DictionaryValue dict;
390 dict.SetInteger("renderer", render_process_id);
391 dict.SetInteger("player", event->id);
392 dict.SetString("type", media::MediaLog::EventTypeToString(event->type));
[email protected]fbc46bd2013-06-26 04:21:41393
[email protected]69946cf2013-11-27 00:11:42394 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be
395 // converted to to a human readable time format. See base/time/time.h.
396 const double ticks = event->time.ToInternalValue();
397 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
[email protected]fbc46bd2013-06-26 04:21:41398 dict.SetDouble("ticksMillis", ticks_millis);
prabhur53bb9182014-11-13 03:25:17399
400 // Convert PipelineStatus to human readable string
401 if (event->type == media::MediaLogEvent::PIPELINE_ERROR) {
402 int status;
403 event->params.GetInteger("pipeline_error", &status);
404 media::PipelineStatus error = static_cast<media::PipelineStatus>(status);
405 dict.SetString("params.pipeline_error",
406 media::MediaLog::PipelineStatusToString(error));
407 } else {
408 dict.Set("params", event->params.DeepCopy());
409 }
410
[email protected]69946cf2013-11-27 00:11:42411 SendUpdate(SerializeUpdate("media.onMediaEvent", &dict));
prabhur53bb9182014-11-13 03:25:17412 uma_handler_->SavePlayerState(*event, render_process_id);
[email protected]0e7ee582013-05-04 14:06:59413 }
[email protected]11158e2d2013-02-01 02:31:56414}
415
416void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) {
[email protected]69946cf2013-11-27 00:11:42417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]11158e2d2013-02-01 02:31:56418 update_callbacks_.push_back(callback);
419}
420
421void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
[email protected]69946cf2013-11-27 00:11:42422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]11158e2d2013-02-01 02:31:56423 for (size_t i = 0; i < update_callbacks_.size(); ++i) {
424 if (update_callbacks_[i].Equals(callback)) {
425 update_callbacks_.erase(update_callbacks_.begin() + i);
426 return;
427 }
428 }
429 NOTREACHED();
430}
431
mcasasfcb5c7de2014-10-11 20:22:17432void MediaInternals::SendAudioStreamData() {
433 base::string16 audio_stream_update;
[email protected]69946cf2013-11-27 00:11:42434 {
435 base::AutoLock auto_lock(lock_);
mcasasfcb5c7de2014-10-11 20:22:17436 audio_stream_update = SerializeUpdate(
437 "media.onReceiveAudioStreamData", &audio_streams_cached_data_);
[email protected]11158e2d2013-02-01 02:31:56438 }
mcasasfcb5c7de2014-10-11 20:22:17439 SendUpdate(audio_stream_update);
440}
441
burnik71963562014-10-17 14:57:14442void MediaInternals::SendVideoCaptureDeviceCapabilities() {
443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
444 SendUpdate(SerializeUpdate("media.onReceiveVideoCaptureCapabilities",
445 &video_capture_capabilities_cached_data_));
446}
447
mcasasfcb5c7de2014-10-11 20:22:17448void MediaInternals::UpdateVideoCaptureDeviceCapabilities(
449 const media::VideoCaptureDeviceInfos& video_capture_device_infos) {
burnik71963562014-10-17 14:57:14450 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
451 video_capture_capabilities_cached_data_.Clear();
mcasasfcb5c7de2014-10-11 20:22:17452
453 for (const auto& video_capture_device_info : video_capture_device_infos) {
burnik71963562014-10-17 14:57:14454 base::ListValue* format_list = new base::ListValue();
455 for (const auto& format : video_capture_device_info.supported_formats)
456 format_list->AppendString(format.ToString());
457
458 base::DictionaryValue* device_dict = new base::DictionaryValue();
459 device_dict->SetString("id", video_capture_device_info.name.id());
460 device_dict->SetString(
461 "name", video_capture_device_info.name.GetNameAndModel());
462 device_dict->Set("formats", format_list);
mcasasfcb5c7de2014-10-11 20:22:17463#if defined(OS_WIN) || defined(OS_MACOSX)
burnik4bf4ed12014-10-21 22:35:01464 device_dict->SetString(
burnik71963562014-10-17 14:57:14465 "captureApi",
burnik4bf4ed12014-10-21 22:35:01466 video_capture_device_info.name.GetCaptureApiTypeString());
mcasasfcb5c7de2014-10-11 20:22:17467#endif
burnik71963562014-10-17 14:57:14468 video_capture_capabilities_cached_data_.Append(device_dict);
mcasasfcb5c7de2014-10-11 20:22:17469 }
burnik71963562014-10-17 14:57:14470
471 if (update_callbacks_.size() > 0)
472 SendVideoCaptureDeviceCapabilities();
mcasasfcb5c7de2014-10-11 20:22:17473}
474
475scoped_ptr<media::AudioLog> MediaInternals::CreateAudioLog(
476 AudioComponent component) {
477 base::AutoLock auto_lock(lock_);
478 return scoped_ptr<media::AudioLog>(new AudioLogImpl(
479 owner_ids_[component]++, component, this));
[email protected]e2fd1f72013-08-16 00:34:25480}
481
[email protected]fcf75d42013-12-03 20:11:26482void MediaInternals::SendUpdate(const base::string16& update) {
[email protected]69946cf2013-11-27 00:11:42483 // SendUpdate() may be called from any thread, but must run on the IO thread.
484 // TODO(dalecurtis): This is pretty silly since the update callbacks simply
485 // forward the calls to the UI thread. We should avoid the extra hop.
486 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
487 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
488 &MediaInternals::SendUpdate, base::Unretained(this), update));
[email protected]11158e2d2013-02-01 02:31:56489 return;
[email protected]69946cf2013-11-27 00:11:42490 }
[email protected]11158e2d2013-02-01 02:31:56491
[email protected]11158e2d2013-02-01 02:31:56492 for (size_t i = 0; i < update_callbacks_.size(); i++)
493 update_callbacks_[i].Run(update);
494}
495
mcasasfcb5c7de2014-10-11 20:22:17496void MediaInternals::SendUpdateAndCacheAudioStreamKey(
497 const std::string& cache_key,
498 const std::string& function,
499 const base::DictionaryValue* value) {
[email protected]69946cf2013-11-27 00:11:42500 SendUpdate(SerializeUpdate(function, value));
501
502 base::AutoLock auto_lock(lock_);
mcasasfcb5c7de2014-10-11 20:22:17503 if (!audio_streams_cached_data_.HasKey(cache_key)) {
504 audio_streams_cached_data_.Set(cache_key, value->DeepCopy());
[email protected]69946cf2013-11-27 00:11:42505 return;
506 }
507
508 base::DictionaryValue* existing_dict = NULL;
mcasasfcb5c7de2014-10-11 20:22:17509 CHECK(audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict));
[email protected]69946cf2013-11-27 00:11:42510 existing_dict->MergeDictionary(value);
511}
512
mcasasfcb5c7de2014-10-11 20:22:17513void MediaInternals::SendUpdateAndPurgeAudioStreamCache(
[email protected]69946cf2013-11-27 00:11:42514 const std::string& cache_key,
515 const std::string& function,
516 const base::DictionaryValue* value) {
517 SendUpdate(SerializeUpdate(function, value));
518
519 base::AutoLock auto_lock(lock_);
520 scoped_ptr<base::Value> out_value;
mcasasfcb5c7de2014-10-11 20:22:17521 CHECK(audio_streams_cached_data_.Remove(cache_key, &out_value));
[email protected]69946cf2013-11-27 00:11:42522}
523
[email protected]11158e2d2013-02-01 02:31:56524} // namespace content