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