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/renderer/media/audio_message_filter.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/message_loop/message_loop_proxy.h"
      9 #include "content/common/media/audio_messages.h"
     10 #include "content/renderer/render_thread_impl.h"
     11 #include "ipc/ipc_logging.h"
     12 
     13 namespace content {
     14 
     15 namespace {
     16 const int kStreamIDNotSet = -1;
     17 }
     18 
     19 class AudioMessageFilter::AudioOutputIPCImpl
     20     : public NON_EXPORTED_BASE(media::AudioOutputIPC) {
     21  public:
     22   AudioOutputIPCImpl(const scoped_refptr<AudioMessageFilter>& filter,
     23                      int render_view_id);
     24   virtual ~AudioOutputIPCImpl();
     25 
     26   // media::AudioOutputIPC implementation.
     27   virtual void CreateStream(media::AudioOutputIPCDelegate* delegate,
     28                             const media::AudioParameters& params,
     29                             int session_id) OVERRIDE;
     30   virtual void PlayStream() OVERRIDE;
     31   virtual void PauseStream() OVERRIDE;
     32   virtual void CloseStream() OVERRIDE;
     33   virtual void SetVolume(double volume) OVERRIDE;
     34 
     35  private:
     36   const scoped_refptr<AudioMessageFilter> filter_;
     37   const int render_view_id_;
     38   int stream_id_;
     39 };
     40 
     41 AudioMessageFilter* AudioMessageFilter::g_filter = NULL;
     42 
     43 AudioMessageFilter::AudioMessageFilter(
     44     const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
     45     : channel_(NULL),
     46       audio_hardware_config_(NULL),
     47       io_message_loop_(io_message_loop) {
     48   DCHECK(!g_filter);
     49   g_filter = this;
     50 }
     51 
     52 AudioMessageFilter::~AudioMessageFilter() {
     53   DCHECK_EQ(g_filter, this);
     54   g_filter = NULL;
     55 }
     56 
     57 // static
     58 AudioMessageFilter* AudioMessageFilter::Get() {
     59   return g_filter;
     60 }
     61 
     62 AudioMessageFilter::AudioOutputIPCImpl::AudioOutputIPCImpl(
     63     const scoped_refptr<AudioMessageFilter>& filter, int render_view_id)
     64     : filter_(filter),
     65       render_view_id_(render_view_id),
     66       stream_id_(kStreamIDNotSet) {}
     67 
     68 AudioMessageFilter::AudioOutputIPCImpl::~AudioOutputIPCImpl() {}
     69 
     70 scoped_ptr<media::AudioOutputIPC> AudioMessageFilter::CreateAudioOutputIPC(
     71     int render_view_id) {
     72   DCHECK_GT(render_view_id, 0);
     73   return scoped_ptr<media::AudioOutputIPC>(
     74       new AudioOutputIPCImpl(this, render_view_id));
     75 }
     76 
     77 void AudioMessageFilter::AudioOutputIPCImpl::CreateStream(
     78     media::AudioOutputIPCDelegate* delegate,
     79     const media::AudioParameters& params,
     80     int session_id) {
     81   DCHECK(filter_->io_message_loop_->BelongsToCurrentThread());
     82   DCHECK(delegate);
     83   DCHECK_EQ(stream_id_, kStreamIDNotSet);
     84   stream_id_ = filter_->delegates_.Add(delegate);
     85   filter_->Send(new AudioHostMsg_CreateStream(
     86       stream_id_, render_view_id_, session_id, params));
     87 }
     88 
     89 void AudioMessageFilter::AudioOutputIPCImpl::PlayStream() {
     90   DCHECK_NE(stream_id_, kStreamIDNotSet);
     91   filter_->Send(new AudioHostMsg_PlayStream(stream_id_));
     92 }
     93 
     94 void AudioMessageFilter::AudioOutputIPCImpl::PauseStream() {
     95   DCHECK_NE(stream_id_, kStreamIDNotSet);
     96   filter_->Send(new AudioHostMsg_PauseStream(stream_id_));
     97 }
     98 
     99 void AudioMessageFilter::AudioOutputIPCImpl::CloseStream() {
    100   DCHECK(filter_->io_message_loop_->BelongsToCurrentThread());
    101   DCHECK_NE(stream_id_, kStreamIDNotSet);
    102   filter_->Send(new AudioHostMsg_CloseStream(stream_id_));
    103   filter_->delegates_.Remove(stream_id_);
    104   stream_id_ = kStreamIDNotSet;
    105 }
    106 
    107 void AudioMessageFilter::AudioOutputIPCImpl::SetVolume(double volume) {
    108   DCHECK_NE(stream_id_, kStreamIDNotSet);
    109   filter_->Send(new AudioHostMsg_SetVolume(stream_id_, volume));
    110 }
    111 
    112 void AudioMessageFilter::Send(IPC::Message* message) {
    113   DCHECK(io_message_loop_->BelongsToCurrentThread());
    114   if (!channel_) {
    115     delete message;
    116   } else {
    117     channel_->Send(message);
    118   }
    119 }
    120 
    121 bool AudioMessageFilter::OnMessageReceived(const IPC::Message& message) {
    122   DCHECK(io_message_loop_->BelongsToCurrentThread());
    123   bool handled = true;
    124   IPC_BEGIN_MESSAGE_MAP(AudioMessageFilter, message)
    125     IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated, OnStreamCreated)
    126     IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged, OnStreamStateChanged)
    127     IPC_MESSAGE_HANDLER(AudioMsg_NotifyDeviceChanged, OnOutputDeviceChanged)
    128     IPC_MESSAGE_UNHANDLED(handled = false)
    129   IPC_END_MESSAGE_MAP()
    130   return handled;
    131 }
    132 
    133 void AudioMessageFilter::OnFilterAdded(IPC::Channel* channel) {
    134   DCHECK(io_message_loop_->BelongsToCurrentThread());
    135   channel_ = channel;
    136 }
    137 
    138 void AudioMessageFilter::OnFilterRemoved() {
    139   DCHECK(io_message_loop_->BelongsToCurrentThread());
    140 
    141   // Once removed, a filter will not be used again.  At this time all
    142   // delegates must be notified so they release their reference.
    143   OnChannelClosing();
    144 }
    145 
    146 void AudioMessageFilter::OnChannelClosing() {
    147   DCHECK(io_message_loop_->BelongsToCurrentThread());
    148   channel_ = NULL;
    149 
    150   DLOG_IF(WARNING, !delegates_.IsEmpty())
    151       << "Not all audio devices have been closed.";
    152 
    153   IDMap<media::AudioOutputIPCDelegate>::iterator it(&delegates_);
    154   while (!it.IsAtEnd()) {
    155     it.GetCurrentValue()->OnIPCClosed();
    156     delegates_.Remove(it.GetCurrentKey());
    157     it.Advance();
    158   }
    159 }
    160 
    161 void AudioMessageFilter::OnStreamCreated(
    162     int stream_id,
    163     base::SharedMemoryHandle handle,
    164 #if defined(OS_WIN)
    165     base::SyncSocket::Handle socket_handle,
    166 #else
    167     base::FileDescriptor socket_descriptor,
    168 #endif
    169     uint32 length) {
    170   DCHECK(io_message_loop_->BelongsToCurrentThread());
    171 
    172 #if !defined(OS_WIN)
    173   base::SyncSocket::Handle socket_handle = socket_descriptor.fd;
    174 #endif
    175 
    176   media::AudioOutputIPCDelegate* delegate = delegates_.Lookup(stream_id);
    177   if (!delegate) {
    178     DLOG(WARNING) << "Got OnStreamCreated() event for a non-existent or removed"
    179                   << " audio renderer. (stream_id=" << stream_id << ").";
    180     base::SharedMemory::CloseHandle(handle);
    181     base::SyncSocket socket(socket_handle);
    182     return;
    183   }
    184   delegate->OnStreamCreated(handle, socket_handle, length);
    185 }
    186 
    187 void AudioMessageFilter::OnStreamStateChanged(
    188     int stream_id, media::AudioOutputIPCDelegate::State state) {
    189   DCHECK(io_message_loop_->BelongsToCurrentThread());
    190   media::AudioOutputIPCDelegate* delegate = delegates_.Lookup(stream_id);
    191   if (!delegate) {
    192     DLOG(WARNING) << "Got OnStreamStateChanged() event for a non-existent or"
    193                   << " removed audio renderer.  State: " << state;
    194     return;
    195   }
    196   delegate->OnStateChanged(state);
    197 }
    198 
    199 void AudioMessageFilter::OnOutputDeviceChanged(int stream_id,
    200                                                int new_buffer_size,
    201                                                int new_sample_rate) {
    202   DCHECK(io_message_loop_->BelongsToCurrentThread());
    203   base::AutoLock auto_lock(lock_);
    204 
    205   // Ignore the message if an audio hardware config hasn't been created; this
    206   // can occur if the renderer is using the high latency audio path.
    207   // TODO(dalecurtis): After http://crbug.com/173435 is fixed, convert to CHECK.
    208   if (!audio_hardware_config_)
    209     return;
    210 
    211   // TODO(crogers): fix OnOutputDeviceChanged() to pass AudioParameters.
    212   media::ChannelLayout channel_layout =
    213       audio_hardware_config_->GetOutputChannelLayout();
    214   int channels = audio_hardware_config_->GetOutputChannels();
    215 
    216   media::AudioParameters output_params;
    217   output_params.Reset(
    218       media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
    219       channel_layout,
    220       channels,
    221       0,
    222       new_sample_rate,
    223       16,
    224       new_buffer_size);
    225 
    226   audio_hardware_config_->UpdateOutputConfig(output_params);
    227 }
    228 
    229 void AudioMessageFilter::SetAudioHardwareConfig(
    230     media::AudioHardwareConfig* config) {
    231   base::AutoLock auto_lock(lock_);
    232   audio_hardware_config_ = config;
    233 }
    234 
    235 }  // namespace content
    236