| Index: content/browser/media/media_internals.cc
|
| diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
|
| index 22f164ddccc5d2f056a15b6c24142ce3ef748ec0..9f420e8abaeb51b09fb2108d1b345ac3525dbde8 100644
|
| --- a/content/browser/media/media_internals.cc
|
| +++ b/content/browser/media/media_internals.cc
|
| @@ -4,7 +4,6 @@
|
|
|
| #include "content/browser/media/media_internals.h"
|
|
|
| -#include "base/memory/scoped_ptr.h"
|
| #include "base/strings/string16.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "content/public/browser/browser_thread.h"
|
| @@ -13,120 +12,169 @@
|
| #include "media/base/media_log.h"
|
| #include "media/base/media_log_event.h"
|
|
|
| -namespace content {
|
| +namespace {
|
|
|
| -MediaInternals* MediaInternals::GetInstance() {
|
| - return Singleton<MediaInternals>::get();
|
| +static base::LazyInstance<content::MediaInternals>::Leaky g_media_internals =
|
| + LAZY_INSTANCE_INITIALIZER;
|
| +
|
| +string16 SerializeUpdate(const std::string& function,
|
| + const base::Value* value) {
|
| + return content::WebUI::GetJavascriptCall(
|
| + function, std::vector<const base::Value*>(1, value));
|
| }
|
|
|
| -MediaInternals::~MediaInternals() {}
|
| +const char kAudioLogStatusKey[] = "status";
|
| +const char kAudioLogUpdateFunction[] = "media.updateAudioComponent";
|
|
|
| -namespace {
|
| -std::string FormatAudioStreamName(void* host, int stream_id) {
|
| - return base::StringPrintf("audio_streams.%p:%d", host, stream_id);
|
| -}
|
| -}
|
| +} // namespace
|
|
|
| -void MediaInternals::OnDeleteAudioStream(void* host, int stream_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| - DeleteItem(FormatAudioStreamName(host, stream_id));
|
| -}
|
| +namespace content {
|
|
|
| -void MediaInternals::OnSetAudioStreamPlaying(
|
| - void* host, int stream_id, bool playing) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| - UpdateAudioStream(host, stream_id,
|
| - "playing", new base::FundamentalValue(playing));
|
| -}
|
| +class AudioLogImpl : public media::AudioLog {
|
| + public:
|
| + AudioLogImpl(int owner_id,
|
| + media::AudioLogFactory::AudioComponent component,
|
| + content::MediaInternals* media_internals);
|
| + virtual ~AudioLogImpl();
|
| +
|
| + virtual void OnCreated(int component_id,
|
| + const media::AudioParameters& params,
|
| + const std::string& input_device_id,
|
| + const std::string& output_device_id) OVERRIDE;
|
| + virtual void OnStarted(int component_id) OVERRIDE;
|
| + virtual void OnStopped(int component_id) OVERRIDE;
|
| + virtual void OnClosed(int component_id) OVERRIDE;
|
| + virtual void OnError(int component_id) OVERRIDE;
|
| + virtual void OnSetVolume(int component_id, double volume) OVERRIDE;
|
|
|
| -void MediaInternals::OnAudioStreamCreated(void* host,
|
| - int stream_id,
|
| - const media::AudioParameters& params,
|
| - const std::string& input_device_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + private:
|
| + void SendSingleStringUpdate(int component_id,
|
| + const std::string& key,
|
| + const std::string& value);
|
| + void StoreComponentMetadata(int component_id, base::DictionaryValue* dict);
|
| + std::string FormatCacheKey(int component_id);
|
|
|
| - StoreAudioStream(host,
|
| - stream_id,
|
| - "input_device_id",
|
| - Value::CreateStringValue(input_device_id));
|
| + const int owner_id_;
|
| + const media::AudioLogFactory::AudioComponent component_;
|
| + content::MediaInternals* const media_internals_;
|
|
|
| - StoreAudioStream(
|
| - host, stream_id, "status", Value::CreateStringValue("created"));
|
| + DISALLOW_COPY_AND_ASSIGN(AudioLogImpl);
|
| +};
|
|
|
| - StoreAudioStream(
|
| - host, stream_id, "stream_id", Value::CreateIntegerValue(stream_id));
|
| +AudioLogImpl::AudioLogImpl(int owner_id,
|
| + media::AudioLogFactory::AudioComponent component,
|
| + content::MediaInternals* media_internals)
|
| + : owner_id_(owner_id),
|
| + component_(component),
|
| + media_internals_(media_internals) {}
|
|
|
| - StoreAudioStream(host,
|
| - stream_id,
|
| - "input_channels",
|
| - Value::CreateIntegerValue(params.input_channels()));
|
| +AudioLogImpl::~AudioLogImpl() {}
|
|
|
| - StoreAudioStream(host,
|
| - stream_id,
|
| - "frames_per_buffer",
|
| - Value::CreateIntegerValue(params.frames_per_buffer()));
|
| +void AudioLogImpl::OnCreated(int component_id,
|
| + const media::AudioParameters& params,
|
| + const std::string& input_device_id,
|
| + const std::string& output_device_id) {
|
| + base::DictionaryValue dict;
|
| + StoreComponentMetadata(component_id, &dict);
|
|
|
| - StoreAudioStream(host,
|
| - stream_id,
|
| - "sample_rate",
|
| - Value::CreateIntegerValue(params.sample_rate()));
|
| + dict.SetString(kAudioLogStatusKey, "created");
|
| + dict.SetString("input_device_id", input_device_id);
|
| + dict.SetInteger("input_channels", params.input_channels());
|
| + dict.SetInteger("frames_per_buffer", params.frames_per_buffer());
|
| + dict.SetInteger("sample_rate", params.sample_rate());
|
| + dict.SetString("output_device_id", output_device_id);
|
| + dict.SetInteger("output_channels", params.channels());
|
| + dict.SetString("output_channel_layout",
|
| + ChannelLayoutToString(params.channel_layout()));
|
|
|
| - StoreAudioStream(host,
|
| - stream_id,
|
| - "output_channels",
|
| - Value::CreateIntegerValue(params.channels()));
|
| + media_internals_->SendUpdateAndCache(
|
| + FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
|
| +}
|
|
|
| - StoreAudioStream(
|
| - host,
|
| - stream_id,
|
| - "channel_layout",
|
| - Value::CreateStringValue(ChannelLayoutToString(params.channel_layout())));
|
| +void AudioLogImpl::OnStarted(int component_id) {
|
| + SendSingleStringUpdate(component_id, kAudioLogStatusKey, "started");
|
| +}
|
|
|
| - SendEverything();
|
| +void AudioLogImpl::OnStopped(int component_id) {
|
| + SendSingleStringUpdate(component_id, kAudioLogStatusKey, "stopped");
|
| }
|
|
|
| -void MediaInternals::OnSetAudioStreamStatus(void* host,
|
| - int stream_id,
|
| - const std::string& status) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| - UpdateAudioStream(host, stream_id,
|
| - "status", new base::StringValue(status));
|
| +void AudioLogImpl::OnClosed(int component_id) {
|
| + base::DictionaryValue dict;
|
| + StoreComponentMetadata(component_id, &dict);
|
| + dict.SetString(kAudioLogStatusKey, "closed");
|
| + media_internals_->SendUpdateAndPurgeCache(
|
| + FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
|
| }
|
|
|
| -void MediaInternals::OnSetAudioStreamVolume(
|
| - void* host, int stream_id, double volume) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| - UpdateAudioStream(host, stream_id,
|
| - "volume", new base::FundamentalValue(volume));
|
| +void AudioLogImpl::OnError(int component_id) {
|
| + SendSingleStringUpdate(component_id, "error_occurred", "true");
|
| +}
|
| +
|
| +void AudioLogImpl::OnSetVolume(int component_id, double volume) {
|
| + base::DictionaryValue dict;
|
| + StoreComponentMetadata(component_id, &dict);
|
| + dict.SetDouble("volume", volume);
|
| + media_internals_->SendUpdateAndCache(
|
| + FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
|
| +}
|
| +
|
| +std::string AudioLogImpl::FormatCacheKey(int component_id) {
|
| + return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id);
|
| +}
|
| +
|
| +void AudioLogImpl::SendSingleStringUpdate(int component_id,
|
| + const std::string& key,
|
| + const std::string& value) {
|
| + base::DictionaryValue dict;
|
| + StoreComponentMetadata(component_id, &dict);
|
| + dict.SetString(key, value);
|
| + media_internals_->SendUpdateAndCache(
|
| + FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
|
| }
|
|
|
| +void AudioLogImpl::StoreComponentMetadata(int component_id,
|
| + base::DictionaryValue* dict) {
|
| + dict->SetInteger("owner_id", owner_id_);
|
| + dict->SetInteger("component_id", component_id);
|
| + dict->SetInteger("component_type", component_);
|
| +}
|
| +
|
| +MediaInternals* MediaInternals::GetInstance() {
|
| + return g_media_internals.Pointer();
|
| +}
|
| +
|
| +MediaInternals::MediaInternals() : owner_ids_() {}
|
| +MediaInternals::~MediaInternals() {}
|
| +
|
| void MediaInternals::OnMediaEvents(
|
| int render_process_id, const std::vector<media::MediaLogEvent>& events) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| -
|
| - // Notify observers that |event| has occured.
|
| + // Notify observers that |event| has occurred.
|
| for (std::vector<media::MediaLogEvent>::const_iterator event = events.begin();
|
| - event != events.end(); ++event) {
|
| + event != events.end(); ++event) {
|
| base::DictionaryValue dict;
|
| dict.SetInteger("renderer", render_process_id);
|
| dict.SetInteger("player", event->id);
|
| dict.SetString("type", media::MediaLog::EventTypeToString(event->type));
|
|
|
| - int64 ticks = event->time.ToInternalValue();
|
| - double ticks_millis =
|
| - ticks / static_cast<double>(base::Time::kMicrosecondsPerMillisecond);
|
| -
|
| + // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be
|
| + // converted to to a human readable time format. See base/time/time.h.
|
| + const double ticks = event->time.ToInternalValue();
|
| + const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
|
| dict.SetDouble("ticksMillis", ticks_millis);
|
| dict.Set("params", event->params.DeepCopy());
|
| - SendUpdate("media.onMediaEvent", &dict);
|
| + SendUpdate(SerializeUpdate("media.onMediaEvent", &dict));
|
| }
|
| }
|
|
|
| void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| update_callbacks_.push_back(callback);
|
| }
|
|
|
| void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| for (size_t i = 0; i < update_callbacks_.size(); ++i) {
|
| if (update_callbacks_[i].Equals(callback)) {
|
| update_callbacks_.erase(update_callbacks_.begin() + i);
|
| @@ -137,68 +185,61 @@ void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
|
| }
|
|
|
| void MediaInternals::SendEverything() {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| - SendUpdate("media.onReceiveEverything", &data_);
|
| + string16 everything_update;
|
| + {
|
| + base::AutoLock auto_lock(lock_);
|
| + everything_update = SerializeUpdate(
|
| + "media.onReceiveEverything", &cached_data_);
|
| + }
|
| + SendUpdate(everything_update);
|
| }
|
|
|
| -MediaInternals::MediaInternals() {
|
| -}
|
| +void MediaInternals::SendUpdate(const string16& update) {
|
| + // SendUpdate() may be called from any thread, but must run on the IO thread.
|
| + // TODO(dalecurtis): This is pretty silly since the update callbacks simply
|
| + // forward the calls to the UI thread. We should avoid the extra hop.
|
| + if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
|
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
|
| + &MediaInternals::SendUpdate, base::Unretained(this), update));
|
| + return;
|
| + }
|
|
|
| -void MediaInternals::StoreAudioStream(void* host,
|
| - int stream_id,
|
| - const std::string& property,
|
| - base::Value* value) {
|
| - StoreItem(FormatAudioStreamName(host, stream_id), property, value);
|
| + for (size_t i = 0; i < update_callbacks_.size(); i++)
|
| + update_callbacks_[i].Run(update);
|
| }
|
|
|
| -void MediaInternals::UpdateAudioStream(void* host,
|
| - int stream_id,
|
| - const std::string& property,
|
| - base::Value* value) {
|
| - UpdateItem("media.updateAudioStream",
|
| - FormatAudioStreamName(host, stream_id),
|
| - property,
|
| - value);
|
| +scoped_ptr<media::AudioLog> MediaInternals::CreateAudioLog(
|
| + AudioComponent component) {
|
| + base::AutoLock auto_lock(lock_);
|
| + return scoped_ptr<media::AudioLog>(new AudioLogImpl(
|
| + owner_ids_[component]++, component, this));
|
| }
|
|
|
| -void MediaInternals::DeleteItem(const std::string& item) {
|
| - data_.Remove(item, NULL);
|
| - scoped_ptr<base::Value> value(new base::StringValue(item));
|
| - SendUpdate("media.onItemDeleted", value.get());
|
| -}
|
| +void MediaInternals::SendUpdateAndCache(const std::string& cache_key,
|
| + const std::string& function,
|
| + const base::DictionaryValue* value) {
|
| + SendUpdate(SerializeUpdate(function, value));
|
|
|
| -base::DictionaryValue* MediaInternals::StoreItem(const std::string& id,
|
| - const std::string& property,
|
| - base::Value* value) {
|
| - base::DictionaryValue* item_properties;
|
| - if (!data_.GetDictionary(id, &item_properties)) {
|
| - item_properties = new base::DictionaryValue();
|
| - data_.Set(id, item_properties);
|
| - item_properties->SetString("id", id);
|
| + base::AutoLock auto_lock(lock_);
|
| + if (!cached_data_.HasKey(cache_key)) {
|
| + cached_data_.Set(cache_key, value->DeepCopy());
|
| + return;
|
| }
|
| - item_properties->Set(property, value);
|
| - return item_properties;
|
| -}
|
|
|
| -void MediaInternals::UpdateItem(const std::string& update_fn,
|
| - const std::string& id,
|
| - const std::string& property,
|
| - base::Value* value) {
|
| - base::DictionaryValue* item_properties = StoreItem(id, property, value);
|
| - SendUpdate(update_fn, item_properties);
|
| + base::DictionaryValue* existing_dict = NULL;
|
| + CHECK(cached_data_.GetDictionary(cache_key, &existing_dict));
|
| + existing_dict->MergeDictionary(value);
|
| }
|
|
|
| -void MediaInternals::SendUpdate(const std::string& function,
|
| - base::Value* value) {
|
| - // Only bother serializing the update to JSON if someone is watching.
|
| - if (update_callbacks_.empty())
|
| - return;
|
| +void MediaInternals::SendUpdateAndPurgeCache(
|
| + const std::string& cache_key,
|
| + const std::string& function,
|
| + const base::DictionaryValue* value) {
|
| + SendUpdate(SerializeUpdate(function, value));
|
|
|
| - std::vector<const base::Value*> args;
|
| - args.push_back(value);
|
| - string16 update = WebUI::GetJavascriptCall(function, args);
|
| - for (size_t i = 0; i < update_callbacks_.size(); i++)
|
| - update_callbacks_[i].Run(update);
|
| + base::AutoLock auto_lock(lock_);
|
| + scoped_ptr<base::Value> out_value;
|
| + CHECK(cached_data_.Remove(cache_key, &out_value));
|
| }
|
|
|
| } // namespace content
|
|
|