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                     int render_frame_id);
27  virtual ~AudioOutputIPCImpl();
28
29  // media::AudioOutputIPC implementation.
30  virtual void CreateStream(media::AudioOutputIPCDelegate* delegate,
31                            const media::AudioParameters& params,
32                            int session_id) OVERRIDE;
33  virtual void PlayStream() OVERRIDE;
34  virtual void PauseStream() OVERRIDE;
35  virtual void CloseStream() OVERRIDE;
36  virtual void SetVolume(double volume) OVERRIDE;
37
38 private:
39  const scoped_refptr<AudioMessageFilter> filter_;
40  const int render_view_id_;
41  const int render_frame_id_;
42  int stream_id_;
43};
44
45AudioMessageFilter* AudioMessageFilter::g_filter = NULL;
46
47AudioMessageFilter::AudioMessageFilter(
48    const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
49    : sender_(NULL),
50      audio_hardware_config_(NULL),
51      io_message_loop_(io_message_loop) {
52  DCHECK(!g_filter);
53  g_filter = this;
54}
55
56AudioMessageFilter::~AudioMessageFilter() {
57  DCHECK_EQ(g_filter, this);
58  g_filter = NULL;
59}
60
61// static
62AudioMessageFilter* AudioMessageFilter::Get() {
63  return g_filter;
64}
65
66AudioMessageFilter::AudioOutputIPCImpl::AudioOutputIPCImpl(
67    const scoped_refptr<AudioMessageFilter>& filter,
68    int render_view_id,
69    int render_frame_id)
70    : filter_(filter),
71      render_view_id_(render_view_id),
72      render_frame_id_(render_frame_id),
73      stream_id_(kStreamIDNotSet) {}
74
75AudioMessageFilter::AudioOutputIPCImpl::~AudioOutputIPCImpl() {}
76
77scoped_ptr<media::AudioOutputIPC> AudioMessageFilter::CreateAudioOutputIPC(
78    int render_view_id, int render_frame_id) {
79  DCHECK_GT(render_view_id, 0);
80  return scoped_ptr<media::AudioOutputIPC>(
81      new AudioOutputIPCImpl(this, render_view_id, render_frame_id));
82}
83
84void AudioMessageFilter::AudioOutputIPCImpl::CreateStream(
85    media::AudioOutputIPCDelegate* delegate,
86    const media::AudioParameters& params,
87    int session_id) {
88  DCHECK(filter_->io_message_loop_->BelongsToCurrentThread());
89  DCHECK(delegate);
90  DCHECK_EQ(stream_id_, kStreamIDNotSet);
91  stream_id_ = filter_->delegates_.Add(delegate);
92  filter_->Send(new AudioHostMsg_CreateStream(
93      stream_id_, render_view_id_, render_frame_id_, session_id, params));
94}
95
96void AudioMessageFilter::AudioOutputIPCImpl::PlayStream() {
97  DCHECK_NE(stream_id_, kStreamIDNotSet);
98  filter_->Send(new AudioHostMsg_PlayStream(stream_id_));
99}
100
101void AudioMessageFilter::AudioOutputIPCImpl::PauseStream() {
102  DCHECK_NE(stream_id_, kStreamIDNotSet);
103  filter_->Send(new AudioHostMsg_PauseStream(stream_id_));
104}
105
106void AudioMessageFilter::AudioOutputIPCImpl::CloseStream() {
107  DCHECK(filter_->io_message_loop_->BelongsToCurrentThread());
108  DCHECK_NE(stream_id_, kStreamIDNotSet);
109  filter_->Send(new AudioHostMsg_CloseStream(stream_id_));
110  filter_->delegates_.Remove(stream_id_);
111  stream_id_ = kStreamIDNotSet;
112}
113
114void AudioMessageFilter::AudioOutputIPCImpl::SetVolume(double volume) {
115  DCHECK_NE(stream_id_, kStreamIDNotSet);
116  filter_->Send(new AudioHostMsg_SetVolume(stream_id_, volume));
117}
118
119void AudioMessageFilter::Send(IPC::Message* message) {
120  DCHECK(io_message_loop_->BelongsToCurrentThread());
121  if (!sender_) {
122    delete message;
123  } else {
124    sender_->Send(message);
125  }
126}
127
128bool AudioMessageFilter::OnMessageReceived(const IPC::Message& message) {
129  DCHECK(io_message_loop_->BelongsToCurrentThread());
130  bool handled = true;
131  IPC_BEGIN_MESSAGE_MAP(AudioMessageFilter, message)
132    IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated, OnStreamCreated)
133    IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged, OnStreamStateChanged)
134    IPC_MESSAGE_HANDLER(AudioMsg_NotifyDeviceChanged, OnOutputDeviceChanged)
135    IPC_MESSAGE_UNHANDLED(handled = false)
136  IPC_END_MESSAGE_MAP()
137  return handled;
138}
139
140void AudioMessageFilter::OnFilterAdded(IPC::Sender* sender) {
141  DCHECK(io_message_loop_->BelongsToCurrentThread());
142  sender_ = sender;
143}
144
145void AudioMessageFilter::OnFilterRemoved() {
146  DCHECK(io_message_loop_->BelongsToCurrentThread());
147
148  // Once removed, a filter will not be used again.  At this time all
149  // delegates must be notified so they release their reference.
150  OnChannelClosing();
151}
152
153void AudioMessageFilter::OnChannelClosing() {
154  DCHECK(io_message_loop_->BelongsToCurrentThread());
155  sender_ = NULL;
156
157  DLOG_IF(WARNING, !delegates_.IsEmpty())
158      << "Not all audio devices have been closed.";
159
160  IDMap<media::AudioOutputIPCDelegate>::iterator it(&delegates_);
161  while (!it.IsAtEnd()) {
162    it.GetCurrentValue()->OnIPCClosed();
163    delegates_.Remove(it.GetCurrentKey());
164    it.Advance();
165  }
166}
167
168void AudioMessageFilter::OnStreamCreated(
169    int stream_id,
170    base::SharedMemoryHandle handle,
171    base::SyncSocket::TransitDescriptor socket_descriptor,
172    uint32 length) {
173  DCHECK(io_message_loop_->BelongsToCurrentThread());
174
175  WebRtcLogMessage(base::StringPrintf(
176      "AMF::OnStreamCreated. stream_id=%d",
177      stream_id));
178
179  base::SyncSocket::Handle socket_handle =
180      base::SyncSocket::UnwrapHandle(socket_descriptor);
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  CHECK(audio_hardware_config_);
221
222  // TODO(crogers): fix OnOutputDeviceChanged() to pass AudioParameters.
223  media::ChannelLayout channel_layout =
224      audio_hardware_config_->GetOutputChannelLayout();
225  int channels = audio_hardware_config_->GetOutputChannels();
226
227  media::AudioParameters output_params;
228  output_params.Reset(
229      media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
230      channel_layout,
231      channels,
232      new_sample_rate,
233      16,
234      new_buffer_size);
235
236  audio_hardware_config_->UpdateOutputConfig(output_params);
237}
238
239void AudioMessageFilter::SetAudioHardwareConfig(
240    media::AudioHardwareConfig* config) {
241  base::AutoLock auto_lock(lock_);
242  audio_hardware_config_ = config;
243}
244
245}  // namespace content
246