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
15 namespace content {
16
17 namespace {
18 const int kStreamIDNotSet = -1;
19 }
20
21 class 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
43 AudioMessageFilter* AudioMessageFilter::g_filter = NULL;
44
AudioMessageFilter(const scoped_refptr<base::MessageLoopProxy> & io_message_loop)45 AudioMessageFilter::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
~AudioMessageFilter()54 AudioMessageFilter::~AudioMessageFilter() {
55 DCHECK_EQ(g_filter, this);
56 g_filter = NULL;
57 }
58
59 // static
Get()60 AudioMessageFilter* AudioMessageFilter::Get() {
61 return g_filter;
62 }
63
AudioOutputIPCImpl(const scoped_refptr<AudioMessageFilter> & filter,int render_view_id)64 AudioMessageFilter::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
~AudioOutputIPCImpl()70 AudioMessageFilter::AudioOutputIPCImpl::~AudioOutputIPCImpl() {}
71
CreateAudioOutputIPC(int render_view_id)72 scoped_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
CreateStream(media::AudioOutputIPCDelegate * delegate,const media::AudioParameters & params,int session_id)79 void 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
PlayStream()91 void AudioMessageFilter::AudioOutputIPCImpl::PlayStream() {
92 DCHECK_NE(stream_id_, kStreamIDNotSet);
93 filter_->Send(new AudioHostMsg_PlayStream(stream_id_));
94 }
95
PauseStream()96 void AudioMessageFilter::AudioOutputIPCImpl::PauseStream() {
97 DCHECK_NE(stream_id_, kStreamIDNotSet);
98 filter_->Send(new AudioHostMsg_PauseStream(stream_id_));
99 }
100
CloseStream()101 void 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
SetVolume(double volume)109 void AudioMessageFilter::AudioOutputIPCImpl::SetVolume(double volume) {
110 DCHECK_NE(stream_id_, kStreamIDNotSet);
111 filter_->Send(new AudioHostMsg_SetVolume(stream_id_, volume));
112 }
113
Send(IPC::Message * message)114 void 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
OnMessageReceived(const IPC::Message & message)123 bool 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
OnFilterAdded(IPC::Channel * channel)135 void AudioMessageFilter::OnFilterAdded(IPC::Channel* channel) {
136 DCHECK(io_message_loop_->BelongsToCurrentThread());
137 channel_ = channel;
138 }
139
OnFilterRemoved()140 void 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
OnChannelClosing()148 void 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
OnStreamCreated(int stream_id,base::SharedMemoryHandle handle,base::SyncSocket::Handle socket_handle,uint32 length)163 void 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
OnStreamStateChanged(int stream_id,media::AudioOutputIPCDelegate::State state)193 void 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
OnOutputDeviceChanged(int stream_id,int new_buffer_size,int new_sample_rate)205 void 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
SetAudioHardwareConfig(media::AudioHardwareConfig * config)242 void AudioMessageFilter::SetAudioHardwareConfig(
243 media::AudioHardwareConfig* config) {
244 base::AutoLock auto_lock(lock_);
245 audio_hardware_config_ = config;
246 }
247
248 } // namespace content
249