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.
5 #include "content/browser/renderer_host/media/audio_renderer_host.h"
8 #include "base/metrics/histogram.h"
9 #include "base/process.h"
10 #include "base/shared_memory.h"
11 #include "content/browser/browser_main_loop.h"
12 #include "content/browser/renderer_host/media/audio_sync_reader.h"
13 #include "content/common/media/audio_messages.h"
14 #include "content/public/browser/media_observer.h"
15 #include "media/audio/shared_memory_util.h"
16 #include "media/base/audio_bus.h"
17 #include "media/base/limits.h"
19 using media::AudioBus
;
23 struct AudioRendererHost::AudioEntry
{
27 // The AudioOutputController that manages the audio stream.
28 scoped_refptr
<media::AudioOutputController
> controller
;
30 // The audio stream ID.
33 // Shared memory for transmission of the audio data.
34 base::SharedMemory shared_memory
;
36 // The synchronous reader to be used by the controller. We have the
37 // ownership of the reader.
38 scoped_ptr
<media::AudioOutputController::SyncReader
> reader
;
40 // Set to true after we called Close() for the controller.
44 AudioRendererHost::AudioEntry::AudioEntry()
46 pending_close(false) {
49 AudioRendererHost::AudioEntry::~AudioEntry() {}
51 ///////////////////////////////////////////////////////////////////////////////
52 // AudioRendererHost implementations.
53 AudioRendererHost::AudioRendererHost(
54 media::AudioManager
* audio_manager
, MediaObserver
* media_observer
)
55 : audio_manager_(audio_manager
),
56 media_observer_(media_observer
) {
59 AudioRendererHost::~AudioRendererHost() {
60 DCHECK(audio_entries_
.empty());
63 void AudioRendererHost::OnChannelClosing() {
64 BrowserMessageFilter::OnChannelClosing();
66 // Since the IPC channel is gone, close all requested audio streams.
70 void AudioRendererHost::OnDestruct() const {
71 BrowserThread::DeleteOnIOThread::Destruct(this);
74 ///////////////////////////////////////////////////////////////////////////////
75 // media::AudioOutputController::EventHandler implementations.
76 void AudioRendererHost::OnCreated(media::AudioOutputController
* controller
) {
77 BrowserThread::PostTask(
81 &AudioRendererHost::DoCompleteCreation
,
83 make_scoped_refptr(controller
)));
86 void AudioRendererHost::OnPlaying(media::AudioOutputController
* controller
) {
87 BrowserThread::PostTask(
91 &AudioRendererHost::DoSendPlayingMessage
,
93 make_scoped_refptr(controller
)));
96 void AudioRendererHost::OnPaused(media::AudioOutputController
* controller
) {
97 BrowserThread::PostTask(
101 &AudioRendererHost::DoSendPausedMessage
,
103 make_scoped_refptr(controller
)));
106 void AudioRendererHost::OnError(media::AudioOutputController
* controller
,
108 BrowserThread::PostTask(
111 base::Bind(&AudioRendererHost::DoHandleError
,
112 this, make_scoped_refptr(controller
), error_code
));
115 void AudioRendererHost::DoCompleteCreation(
116 media::AudioOutputController
* controller
) {
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
119 AudioEntry
* entry
= LookupByController(controller
);
123 if (!peer_handle()) {
124 NOTREACHED() << "Renderer process handle is invalid.";
125 DeleteEntryOnError(entry
);
129 // Once the audio stream is created then complete the creation process by
130 // mapping shared memory and sharing with the renderer process.
131 base::SharedMemoryHandle foreign_memory_handle
;
132 if (!entry
->shared_memory
.ShareToProcess(peer_handle(),
133 &foreign_memory_handle
)) {
134 // If we failed to map and share the shared memory then close the audio
135 // stream and send an error message.
136 DeleteEntryOnError(entry
);
140 AudioSyncReader
* reader
=
141 static_cast<AudioSyncReader
*>(entry
->reader
.get());
144 base::SyncSocket::Handle foreign_socket_handle
;
146 base::FileDescriptor foreign_socket_handle
;
149 // If we failed to prepare the sync socket for the renderer then we fail
150 // the construction of audio stream.
151 if (!reader
->PrepareForeignSocketHandle(peer_handle(),
152 &foreign_socket_handle
)) {
153 DeleteEntryOnError(entry
);
157 Send(new AudioMsg_NotifyStreamCreated(
159 foreign_memory_handle
,
160 foreign_socket_handle
,
161 media::PacketSizeInBytes(entry
->shared_memory
.created_size())));
164 void AudioRendererHost::DoSendPlayingMessage(
165 media::AudioOutputController
* controller
) {
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
168 AudioEntry
* entry
= LookupByController(controller
);
172 Send(new AudioMsg_NotifyStreamStateChanged(
173 entry
->stream_id
, media::AudioOutputIPCDelegate::kPlaying
));
176 void AudioRendererHost::DoSendPausedMessage(
177 media::AudioOutputController
* controller
) {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
180 AudioEntry
* entry
= LookupByController(controller
);
184 Send(new AudioMsg_NotifyStreamStateChanged(
185 entry
->stream_id
, media::AudioOutputIPCDelegate::kPaused
));
188 void AudioRendererHost::DoHandleError(media::AudioOutputController
* controller
,
190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
192 AudioEntry
* entry
= LookupByController(controller
);
196 DeleteEntryOnError(entry
);
199 ///////////////////////////////////////////////////////////////////////////////
200 // IPC Messages handler
201 bool AudioRendererHost::OnMessageReceived(const IPC::Message
& message
,
202 bool* message_was_ok
) {
204 IPC_BEGIN_MESSAGE_MAP_EX(AudioRendererHost
, message
, *message_was_ok
)
205 IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream
, OnCreateStream
)
206 IPC_MESSAGE_HANDLER(AudioHostMsg_AssociateStreamWithProducer
,
207 OnAssociateStreamWithProducer
)
208 IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream
, OnPlayStream
)
209 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream
, OnPauseStream
)
210 IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream
, OnFlushStream
)
211 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream
, OnCloseStream
)
212 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume
, OnSetVolume
)
213 IPC_MESSAGE_UNHANDLED(handled
= false)
214 IPC_END_MESSAGE_MAP_EX()
219 void AudioRendererHost::OnCreateStream(
220 int stream_id
, const media::AudioParameters
& params
, int input_channels
) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
222 DCHECK(LookupById(stream_id
) == NULL
);
224 media::AudioParameters
audio_params(params
);
225 uint32 buffer_size
= media::AudioBus::CalculateMemorySize(audio_params
);
226 DCHECK_GT(buffer_size
, 0U);
227 DCHECK_LE(buffer_size
,
228 static_cast<uint32
>(media::limits::kMaxPacketSizeInBytes
));
230 DCHECK_GE(input_channels
, 0);
231 DCHECK_LT(input_channels
, media::limits::kMaxChannels
);
233 // Calculate output and input memory size.
234 int output_memory_size
= AudioBus::CalculateMemorySize(audio_params
);
235 DCHECK_GT(output_memory_size
, 0);
237 int frames
= audio_params
.frames_per_buffer();
238 int input_memory_size
=
239 AudioBus::CalculateMemorySize(input_channels
, frames
);
241 DCHECK_GE(input_memory_size
, 0);
243 scoped_ptr
<AudioEntry
> entry(new AudioEntry());
245 // Create the shared memory and share with the renderer process.
246 // For synchronized I/O (if input_channels > 0) then we allocate
247 // extra memory after the output data for the input data.
248 uint32 io_buffer_size
= output_memory_size
+ input_memory_size
;
250 uint32 shared_memory_size
=
251 media::TotalSharedMemorySizeInBytes(io_buffer_size
);
252 if (!entry
->shared_memory
.CreateAndMapAnonymous(shared_memory_size
)) {
253 // If creation of shared memory failed then send an error message.
254 SendErrorMessage(stream_id
);
258 // Create sync reader and try to initialize it.
259 scoped_ptr
<AudioSyncReader
> reader(
260 new AudioSyncReader(&entry
->shared_memory
, params
, input_channels
));
262 if (!reader
->Init()) {
263 SendErrorMessage(stream_id
);
267 // If we have successfully created the SyncReader then assign it to the
268 // entry and construct an AudioOutputController.
269 entry
->reader
.reset(reader
.release());
270 entry
->controller
= media::AudioOutputController::Create(
271 audio_manager_
, this, audio_params
, entry
->reader
.get());
273 if (!entry
->controller
) {
274 SendErrorMessage(stream_id
);
278 // If we have created the controller successfully, create an entry and add it
280 entry
->stream_id
= stream_id
;
281 audio_entries_
.insert(std::make_pair(stream_id
, entry
.release()));
283 media_observer_
->OnSetAudioStreamStatus(this, stream_id
, "created");
286 void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id
,
287 int render_view_id
) {
288 // TODO(miu): Will use render_view_id in upcoming change.
289 DVLOG(1) << "AudioRendererHost@" << this
290 << "::OnAssociateStreamWithProducer(stream_id=" << stream_id
291 << ", render_view_id=" << render_view_id
<< ")";
294 void AudioRendererHost::OnPlayStream(int stream_id
) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
297 AudioEntry
* entry
= LookupById(stream_id
);
299 SendErrorMessage(stream_id
);
303 entry
->controller
->Play();
305 media_observer_
->OnSetAudioStreamPlaying(this, stream_id
, true);
308 void AudioRendererHost::OnPauseStream(int stream_id
) {
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
311 AudioEntry
* entry
= LookupById(stream_id
);
313 SendErrorMessage(stream_id
);
317 entry
->controller
->Pause();
319 media_observer_
->OnSetAudioStreamPlaying(this, stream_id
, false);
322 void AudioRendererHost::OnFlushStream(int stream_id
) {
323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
325 AudioEntry
* entry
= LookupById(stream_id
);
327 SendErrorMessage(stream_id
);
331 entry
->controller
->Flush();
333 media_observer_
->OnSetAudioStreamStatus(this, stream_id
, "flushed");
336 void AudioRendererHost::OnCloseStream(int stream_id
) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
340 media_observer_
->OnSetAudioStreamStatus(this, stream_id
, "closed");
342 AudioEntry
* entry
= LookupById(stream_id
);
345 CloseAndDeleteStream(entry
);
348 void AudioRendererHost::OnSetVolume(int stream_id
, double volume
) {
349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
351 AudioEntry
* entry
= LookupById(stream_id
);
353 SendErrorMessage(stream_id
);
357 // Make sure the volume is valid.
358 if (volume
< 0 || volume
> 1.0)
360 entry
->controller
->SetVolume(volume
);
362 media_observer_
->OnSetAudioStreamVolume(this, stream_id
, volume
);
365 void AudioRendererHost::SendErrorMessage(int32 stream_id
) {
366 Send(new AudioMsg_NotifyStreamStateChanged(
367 stream_id
, media::AudioOutputIPCDelegate::kError
));
370 void AudioRendererHost::DeleteEntries() {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
373 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
374 i
!= audio_entries_
.end(); ++i
) {
375 CloseAndDeleteStream(i
->second
);
379 void AudioRendererHost::CloseAndDeleteStream(AudioEntry
* entry
) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
382 if (!entry
->pending_close
) {
383 entry
->controller
->Close(
384 base::Bind(&AudioRendererHost::DeleteEntry
, this, entry
));
385 entry
->pending_close
= true;
389 void AudioRendererHost::DeleteEntry(AudioEntry
* entry
) {
390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
392 // Delete the entry when this method goes out of scope.
393 scoped_ptr
<AudioEntry
> entry_deleter(entry
);
395 // Erase the entry identified by |stream_id| from the map.
396 audio_entries_
.erase(entry
->stream_id
);
398 // Notify the media observer.
400 media_observer_
->OnDeleteAudioStream(this, entry
->stream_id
);
403 void AudioRendererHost::DeleteEntryOnError(AudioEntry
* entry
) {
404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
406 // Sends the error message first before we close the stream because
407 // |entry| is destroyed in DeleteEntry().
408 SendErrorMessage(entry
->stream_id
);
411 media_observer_
->OnSetAudioStreamStatus(this, entry
->stream_id
, "error");
412 CloseAndDeleteStream(entry
);
415 AudioRendererHost::AudioEntry
* AudioRendererHost::LookupById(int stream_id
) {
416 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
418 AudioEntryMap::iterator i
= audio_entries_
.find(stream_id
);
419 if (i
!= audio_entries_
.end() && !i
->second
->pending_close
)
424 AudioRendererHost::AudioEntry
* AudioRendererHost::LookupByController(
425 media::AudioOutputController
* controller
) {
426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
428 // Iterate the map of entries.
429 // TODO(hclam): Implement a faster look up method.
430 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
431 i
!= audio_entries_
.end(); ++i
) {
432 if (!i
->second
->pending_close
&& controller
== i
->second
->controller
.get())
438 media::AudioOutputController
* AudioRendererHost::LookupControllerByIdForTesting(
440 AudioEntry
* const entry
= LookupById(stream_id
);
441 return entry
? entry
->controller
: NULL
;
444 } // namespace content