Associate audio streams with their source/destination RenderView.
[chromium-blink-merge.git] / content / browser / renderer_host / media / audio_renderer_host.cc
blob83b5f2510baaa405905af5e07c561e76283b9ade
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"
7 #include "base/bind.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;
21 namespace content {
23 struct AudioRendererHost::AudioEntry {
24 AudioEntry();
25 ~AudioEntry();
27 // The AudioOutputController that manages the audio stream.
28 scoped_refptr<media::AudioOutputController> controller;
30 // The audio stream ID.
31 int 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.
41 bool pending_close;
44 AudioRendererHost::AudioEntry::AudioEntry()
45 : stream_id(0),
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.
67 DeleteEntries();
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(
78 BrowserThread::IO,
79 FROM_HERE,
80 base::Bind(
81 &AudioRendererHost::DoCompleteCreation,
82 this,
83 make_scoped_refptr(controller)));
86 void AudioRendererHost::OnPlaying(media::AudioOutputController* controller) {
87 BrowserThread::PostTask(
88 BrowserThread::IO,
89 FROM_HERE,
90 base::Bind(
91 &AudioRendererHost::DoSendPlayingMessage,
92 this,
93 make_scoped_refptr(controller)));
96 void AudioRendererHost::OnPaused(media::AudioOutputController* controller) {
97 BrowserThread::PostTask(
98 BrowserThread::IO,
99 FROM_HERE,
100 base::Bind(
101 &AudioRendererHost::DoSendPausedMessage,
102 this,
103 make_scoped_refptr(controller)));
106 void AudioRendererHost::OnError(media::AudioOutputController* controller,
107 int error_code) {
108 BrowserThread::PostTask(
109 BrowserThread::IO,
110 FROM_HERE,
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);
120 if (!entry)
121 return;
123 if (!peer_handle()) {
124 NOTREACHED() << "Renderer process handle is invalid.";
125 DeleteEntryOnError(entry);
126 return;
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);
137 return;
140 AudioSyncReader* reader =
141 static_cast<AudioSyncReader*>(entry->reader.get());
143 #if defined(OS_WIN)
144 base::SyncSocket::Handle foreign_socket_handle;
145 #else
146 base::FileDescriptor foreign_socket_handle;
147 #endif
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);
154 return;
157 Send(new AudioMsg_NotifyStreamCreated(
158 entry->stream_id,
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);
169 if (!entry)
170 return;
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);
181 if (!entry)
182 return;
184 Send(new AudioMsg_NotifyStreamStateChanged(
185 entry->stream_id, media::AudioOutputIPCDelegate::kPaused));
188 void AudioRendererHost::DoHandleError(media::AudioOutputController* controller,
189 int error_code) {
190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
192 AudioEntry* entry = LookupByController(controller);
193 if (!entry)
194 return;
196 DeleteEntryOnError(entry);
199 ///////////////////////////////////////////////////////////////////////////////
200 // IPC Messages handler
201 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message,
202 bool* message_was_ok) {
203 bool handled = true;
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()
216 return handled;
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);
255 return;
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);
264 return;
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);
275 return;
278 // If we have created the controller successfully, create an entry and add it
279 // to the map.
280 entry->stream_id = stream_id;
281 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
282 if (media_observer_)
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);
298 if (!entry) {
299 SendErrorMessage(stream_id);
300 return;
303 entry->controller->Play();
304 if (media_observer_)
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);
312 if (!entry) {
313 SendErrorMessage(stream_id);
314 return;
317 entry->controller->Pause();
318 if (media_observer_)
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);
326 if (!entry) {
327 SendErrorMessage(stream_id);
328 return;
331 entry->controller->Flush();
332 if (media_observer_)
333 media_observer_->OnSetAudioStreamStatus(this, stream_id, "flushed");
336 void AudioRendererHost::OnCloseStream(int stream_id) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
339 if (media_observer_)
340 media_observer_->OnSetAudioStreamStatus(this, stream_id, "closed");
342 AudioEntry* entry = LookupById(stream_id);
344 if (entry)
345 CloseAndDeleteStream(entry);
348 void AudioRendererHost::OnSetVolume(int stream_id, double volume) {
349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
351 AudioEntry* entry = LookupById(stream_id);
352 if (!entry) {
353 SendErrorMessage(stream_id);
354 return;
357 // Make sure the volume is valid.
358 if (volume < 0 || volume > 1.0)
359 return;
360 entry->controller->SetVolume(volume);
361 if (media_observer_)
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.
399 if (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);
410 if (media_observer_)
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)
420 return i->second;
421 return NULL;
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())
433 return i->second;
435 return NULL;
438 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting(
439 int stream_id) {
440 AudioEntry* const entry = LookupById(stream_id);
441 return entry ? entry->controller : NULL;
444 } // namespace content