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("frames_per_buffer", params.frames_per_buffer());
    114   dict.SetInteger("sample_rate", params.sample_rate());
    115   dict.SetInteger("channels", params.channels());
    116   dict.SetString("channel_layout",
    117                  ChannelLayoutToString(params.channel_layout()));
    118   dict.SetString("effects", EffectsToString(params.effects()));
    119 
    120   media_internals_->SendUpdateAndCache(
    121       FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
    122 }
    123 
    124 void AudioLogImpl::OnStarted(int component_id) {
    125   SendSingleStringUpdate(component_id, kAudioLogStatusKey, "started");
    126 }
    127 
    128 void AudioLogImpl::OnStopped(int component_id) {
    129   SendSingleStringUpdate(component_id, kAudioLogStatusKey, "stopped");
    130 }
    131 
    132 void AudioLogImpl::OnClosed(int component_id) {
    133   base::DictionaryValue dict;
    134   StoreComponentMetadata(component_id, &dict);
    135   dict.SetString(kAudioLogStatusKey, "closed");
    136   media_internals_->SendUpdateAndPurgeCache(
    137       FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
    138 }
    139 
    140 void AudioLogImpl::OnError(int component_id) {
    141   SendSingleStringUpdate(component_id, "error_occurred", "true");
    142 }
    143 
    144 void AudioLogImpl::OnSetVolume(int component_id, double volume) {
    145   base::DictionaryValue dict;
    146   StoreComponentMetadata(component_id, &dict);
    147   dict.SetDouble("volume", volume);
    148   media_internals_->SendUpdateAndCache(
    149       FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
    150 }
    151 
    152 std::string AudioLogImpl::FormatCacheKey(int component_id) {
    153   return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id);
    154 }
    155 
    156 void AudioLogImpl::SendSingleStringUpdate(int component_id,
    157                                           const std::string& key,
    158                                           const std::string& value) {
    159   base::DictionaryValue dict;
    160   StoreComponentMetadata(component_id, &dict);
    161   dict.SetString(key, value);
    162   media_internals_->SendUpdateAndCache(
    163       FormatCacheKey(component_id), kAudioLogUpdateFunction, &dict);
    164 }
    165 
    166 void AudioLogImpl::StoreComponentMetadata(int component_id,
    167                                           base::DictionaryValue* dict) {
    168   dict->SetInteger("owner_id", owner_id_);
    169   dict->SetInteger("component_id", component_id);
    170   dict->SetInteger("component_type", component_);
    171 }
    172 
    173 MediaInternals* MediaInternals::GetInstance() {
    174   return g_media_internals.Pointer();
    175 }
    176 
    177 MediaInternals::MediaInternals() : owner_ids_() {}
    178 MediaInternals::~MediaInternals() {}
    179 
    180 void MediaInternals::OnMediaEvents(
    181     int render_process_id, const std::vector<media::MediaLogEvent>& events) {
    182   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    183   // Notify observers that |event| has occurred.
    184   for (std::vector<media::MediaLogEvent>::const_iterator event = events.begin();
    185        event != events.end(); ++event) {
    186     base::DictionaryValue dict;
    187     dict.SetInteger("renderer", render_process_id);
    188     dict.SetInteger("player", event->id);
    189     dict.SetString("type", media::MediaLog::EventTypeToString(event->type));
    190 
    191     // TODO(dalecurtis): This is technically not correct.  TimeTicks "can't" be
    192     // converted to to a human readable time format.  See base/time/time.h.
    193     const double ticks = event->time.ToInternalValue();
    194     const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
    195     dict.SetDouble("ticksMillis", ticks_millis);
    196     dict.Set("params", event->params.DeepCopy());
    197     SendUpdate(SerializeUpdate("media.onMediaEvent", &dict));
    198   }
    199 }
    200 
    201 void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) {
    202   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    203   update_callbacks_.push_back(callback);
    204 }
    205 
    206 void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
    207   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    208   for (size_t i = 0; i < update_callbacks_.size(); ++i) {
    209     if (update_callbacks_[i].Equals(callback)) {
    210       update_callbacks_.erase(update_callbacks_.begin() + i);
    211       return;
    212     }
    213   }
    214   NOTREACHED();
    215 }
    216 
    217 void MediaInternals::SendEverything() {
    218   base::string16 everything_update;
    219   {
    220     base::AutoLock auto_lock(lock_);
    221     everything_update = SerializeUpdate(
    222         "media.onReceiveEverything", &cached_data_);
    223   }
    224   SendUpdate(everything_update);
    225 }
    226 
    227 void MediaInternals::SendUpdate(const base::string16& update) {
    228   // SendUpdate() may be called from any thread, but must run on the IO thread.
    229   // TODO(dalecurtis): This is pretty silly since the update callbacks simply
    230   // forward the calls to the UI thread.  We should avoid the extra hop.
    231   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    232     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
    233         &MediaInternals::SendUpdate, base::Unretained(this), update));
    234     return;
    235   }
    236 
    237   for (size_t i = 0; i < update_callbacks_.size(); i++)
    238     update_callbacks_[i].Run(update);
    239 }
    240 
    241 scoped_ptr<media::AudioLog> MediaInternals::CreateAudioLog(
    242     AudioComponent component) {
    243   base::AutoLock auto_lock(lock_);
    244   return scoped_ptr<media::AudioLog>(new AudioLogImpl(
    245       owner_ids_[component]++, component, this));
    246 }
    247 
    248 void MediaInternals::SendUpdateAndCache(const std::string& cache_key,
    249                                         const std::string& function,
    250                                         const base::DictionaryValue* value) {
    251   SendUpdate(SerializeUpdate(function, value));
    252 
    253   base::AutoLock auto_lock(lock_);
    254   if (!cached_data_.HasKey(cache_key)) {
    255     cached_data_.Set(cache_key, value->DeepCopy());
    256     return;
    257   }
    258 
    259   base::DictionaryValue* existing_dict = NULL;
    260   CHECK(cached_data_.GetDictionary(cache_key, &existing_dict));
    261   existing_dict->MergeDictionary(value);
    262 }
    263 
    264 void MediaInternals::SendUpdateAndPurgeCache(
    265     const std::string& cache_key,
    266     const std::string& function,
    267     const base::DictionaryValue* value) {
    268   SendUpdate(SerializeUpdate(function, value));
    269 
    270   base::AutoLock auto_lock(lock_);
    271   scoped_ptr<base::Value> out_value;
    272   CHECK(cached_data_.Remove(cache_key, &out_value));
    273 }
    274 
    275 }  // namespace content
    276