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