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