audio_renderer_host.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/browser/renderer_host/media/audio_renderer_host.h"
6
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/media/media_internals.h"
13#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
14#include "content/browser/renderer_host/media/audio_sync_reader.h"
15#include "content/common/media/audio_messages.h"
16#include "content/public/browser/content_browser_client.h"
17#include "content/public/browser/media_observer.h"
18#include "media/audio/shared_memory_util.h"
19#include "media/base/audio_bus.h"
20#include "media/base/limits.h"
21
22// TODO(miu): Re-enable after M27 branch cut.  This feature is scheduled for
23// release in M28.
24#define DISABLED_AUDIO_INDICATOR_TRIGGERS_FOR_M27_ONLY
25
26using media::AudioBus;
27
28namespace content {
29
30struct AudioRendererHost::AudioEntry {
31  AudioEntry();
32  ~AudioEntry();
33
34  // The AudioOutputController that manages the audio stream.
35  scoped_refptr<media::AudioOutputController> controller;
36
37  // The audio stream ID.
38  int stream_id;
39
40  // The routing ID of the source render view.
41  int render_view_id;
42
43  // Shared memory for transmission of the audio data.
44  base::SharedMemory shared_memory;
45
46  // The synchronous reader to be used by the controller. We have the
47  // ownership of the reader.
48  scoped_ptr<media::AudioOutputController::SyncReader> reader;
49
50  // Set to true after we called Close() for the controller.
51  bool pending_close;
52};
53
54AudioRendererHost::AudioEntry::AudioEntry()
55    : stream_id(0),
56      render_view_id(MSG_ROUTING_NONE),
57      pending_close(false) {
58}
59
60AudioRendererHost::AudioEntry::~AudioEntry() {}
61
62///////////////////////////////////////////////////////////////////////////////
63// AudioRendererHost implementations.
64AudioRendererHost::AudioRendererHost(
65    int render_process_id,
66    media::AudioManager* audio_manager,
67    AudioMirroringManager* mirroring_manager,
68    MediaInternals* media_internals)
69    : render_process_id_(render_process_id),
70      audio_manager_(audio_manager),
71      mirroring_manager_(mirroring_manager),
72      media_internals_(media_internals) {
73  DCHECK(audio_manager_);
74}
75
76AudioRendererHost::~AudioRendererHost() {
77  DCHECK(audio_entries_.empty());
78}
79
80void AudioRendererHost::OnChannelClosing() {
81  BrowserMessageFilter::OnChannelClosing();
82
83  // Since the IPC channel is gone, close all requested audio streams.
84  DeleteEntries();
85}
86
87void AudioRendererHost::OnDestruct() const {
88  BrowserThread::DeleteOnIOThread::Destruct(this);
89}
90
91///////////////////////////////////////////////////////////////////////////////
92// media::AudioOutputController::EventHandler implementations.
93void AudioRendererHost::OnCreated(media::AudioOutputController* controller) {
94  BrowserThread::PostTask(
95      BrowserThread::IO,
96      FROM_HERE,
97      base::Bind(
98          &AudioRendererHost::DoCompleteCreation,
99          this,
100          make_scoped_refptr(controller)));
101}
102
103void AudioRendererHost::OnPlaying(media::AudioOutputController* controller) {
104  BrowserThread::PostTask(
105      BrowserThread::IO,
106      FROM_HERE,
107      base::Bind(
108          &AudioRendererHost::DoSendPlayingMessage,
109          this,
110          make_scoped_refptr(controller)));
111}
112
113void AudioRendererHost::OnPaused(media::AudioOutputController* controller) {
114  BrowserThread::PostTask(
115      BrowserThread::IO,
116      FROM_HERE,
117      base::Bind(
118          &AudioRendererHost::DoSendPausedMessage,
119          this,
120          make_scoped_refptr(controller)));
121}
122
123void AudioRendererHost::OnError(media::AudioOutputController* controller) {
124  BrowserThread::PostTask(
125      BrowserThread::IO,
126      FROM_HERE,
127      base::Bind(
128          &AudioRendererHost::DoHandleError,
129          this,
130          make_scoped_refptr(controller)));
131}
132
133void AudioRendererHost::OnDeviceChange(media::AudioOutputController* controller,
134                                       int new_buffer_size,
135                                       int new_sample_rate) {
136  BrowserThread::PostTask(
137      BrowserThread::IO,
138      FROM_HERE,
139      base::Bind(&AudioRendererHost::DoSendDeviceChangeMessage,
140                 this, make_scoped_refptr(controller), new_buffer_size,
141                 new_sample_rate));
142}
143
144void AudioRendererHost::DoCompleteCreation(
145    media::AudioOutputController* controller) {
146  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
147
148  AudioEntry* entry = LookupByController(controller);
149  if (!entry)
150    return;
151
152  if (!peer_handle()) {
153    NOTREACHED() << "Renderer process handle is invalid.";
154    DeleteEntryOnError(entry);
155    return;
156  }
157
158  // Once the audio stream is created then complete the creation process by
159  // mapping shared memory and sharing with the renderer process.
160  base::SharedMemoryHandle foreign_memory_handle;
161  if (!entry->shared_memory.ShareToProcess(peer_handle(),
162                                           &foreign_memory_handle)) {
163    // If we failed to map and share the shared memory then close the audio
164    // stream and send an error message.
165    DeleteEntryOnError(entry);
166    return;
167  }
168
169  AudioSyncReader* reader =
170      static_cast<AudioSyncReader*>(entry->reader.get());
171
172#if defined(OS_WIN)
173  base::SyncSocket::Handle foreign_socket_handle;
174#else
175  base::FileDescriptor foreign_socket_handle;
176#endif
177
178  // If we failed to prepare the sync socket for the renderer then we fail
179  // the construction of audio stream.
180  if (!reader->PrepareForeignSocketHandle(peer_handle(),
181                                          &foreign_socket_handle)) {
182    DeleteEntryOnError(entry);
183    return;
184  }
185
186  Send(new AudioMsg_NotifyStreamCreated(
187      entry->stream_id,
188      foreign_memory_handle,
189      foreign_socket_handle,
190      media::PacketSizeInBytes(entry->shared_memory.created_size())));
191}
192
193void AudioRendererHost::DoSendPlayingMessage(
194    media::AudioOutputController* controller) {
195  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
196
197  AudioEntry* entry = LookupByController(controller);
198  if (!entry)
199    return;
200
201  Send(new AudioMsg_NotifyStreamStateChanged(
202      entry->stream_id, media::AudioOutputIPCDelegate::kPlaying));
203}
204
205void AudioRendererHost::DoSendPausedMessage(
206    media::AudioOutputController* controller) {
207  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
208
209  AudioEntry* entry = LookupByController(controller);
210  if (!entry)
211    return;
212
213  Send(new AudioMsg_NotifyStreamStateChanged(
214      entry->stream_id, media::AudioOutputIPCDelegate::kPaused));
215}
216
217void AudioRendererHost::DoSendDeviceChangeMessage(
218    media::AudioOutputController* controller, int new_buffer_size,
219    int new_sample_rate) {
220  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
221
222  AudioEntry* entry = LookupByController(controller);
223  if (!entry)
224    return;
225
226  Send(new AudioMsg_NotifyDeviceChanged(
227      entry->stream_id, new_buffer_size, new_sample_rate));
228}
229
230void AudioRendererHost::DoHandleError(
231    media::AudioOutputController* controller) {
232  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
233
234  AudioEntry* entry = LookupByController(controller);
235  if (!entry)
236    return;
237
238  DeleteEntryOnError(entry);
239}
240
241///////////////////////////////////////////////////////////////////////////////
242// IPC Messages handler
243bool AudioRendererHost::OnMessageReceived(const IPC::Message& message,
244                                          bool* message_was_ok) {
245  bool handled = true;
246  IPC_BEGIN_MESSAGE_MAP_EX(AudioRendererHost, message, *message_was_ok)
247    IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream)
248    IPC_MESSAGE_HANDLER(AudioHostMsg_AssociateStreamWithProducer,
249                        OnAssociateStreamWithProducer)
250    IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream)
251    IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream)
252    IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream, OnFlushStream)
253    IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream)
254    IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume)
255    IPC_MESSAGE_UNHANDLED(handled = false)
256  IPC_END_MESSAGE_MAP_EX()
257
258  return handled;
259}
260
261void AudioRendererHost::OnCreateStream(
262    int stream_id, const media::AudioParameters& params) {
263  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
264  // media::AudioParameters is validated in the deserializer.
265  int input_channels = params.input_channels();
266  if (input_channels < 0 ||
267      input_channels > media::limits::kMaxChannels ||
268      LookupById(stream_id) != NULL) {
269    SendErrorMessage(stream_id);
270    return;
271  }
272
273  media::AudioParameters audio_params(params);
274
275  // Calculate output and input memory size.
276  int output_memory_size = AudioBus::CalculateMemorySize(audio_params);
277
278  int frames = audio_params.frames_per_buffer();
279  int input_memory_size =
280      AudioBus::CalculateMemorySize(input_channels, frames);
281
282  scoped_ptr<AudioEntry> entry(new AudioEntry());
283
284  // Create the shared memory and share with the renderer process.
285  // For synchronized I/O (if input_channels > 0) then we allocate
286  // extra memory after the output data for the input data.
287  uint32 io_buffer_size = output_memory_size + input_memory_size;
288
289  uint32 shared_memory_size =
290      media::TotalSharedMemorySizeInBytes(io_buffer_size);
291  if (!entry->shared_memory.CreateAndMapAnonymous(shared_memory_size)) {
292    // If creation of shared memory failed then send an error message.
293    SendErrorMessage(stream_id);
294    return;
295  }
296
297  // Create sync reader and try to initialize it.
298  scoped_ptr<AudioSyncReader> reader(
299      new AudioSyncReader(&entry->shared_memory, params, input_channels));
300
301  if (!reader->Init()) {
302    SendErrorMessage(stream_id);
303    return;
304  }
305
306  // If we have successfully created the SyncReader then assign it to the
307  // entry and construct an AudioOutputController.
308  entry->reader.reset(reader.release());
309  entry->controller = media::AudioOutputController::Create(
310      audio_manager_, this, audio_params, entry->reader.get());
311
312  if (!entry->controller) {
313    SendErrorMessage(stream_id);
314    return;
315  }
316
317  // If we have created the controller successfully, create an entry and add it
318  // to the map.
319  entry->stream_id = stream_id;
320  audio_entries_.insert(std::make_pair(stream_id, entry.release()));
321  if (media_internals_)
322    media_internals_->OnSetAudioStreamStatus(this, stream_id, "created");
323}
324
325void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id,
326                                                      int render_view_id) {
327  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
328
329  DVLOG(1) << "AudioRendererHost@" << this
330           << "::OnAssociateStreamWithProducer(stream_id=" << stream_id
331           << ", render_view_id=" << render_view_id << ")";
332
333  AudioEntry* const entry = LookupById(stream_id);
334  if (!entry) {
335    SendErrorMessage(stream_id);
336    return;
337  }
338
339  if (entry->render_view_id == render_view_id)
340    return;
341
342  // TODO(miu): Merge "AssociateWithProducer" message into "CreateStream"
343  // message so AudioRendererHost can assume a simpler "render_view_id is set
344  // once" scheme. http://crbug.com/166779
345  if (mirroring_manager_) {
346    mirroring_manager_->RemoveDiverter(
347        render_process_id_, entry->render_view_id, entry->controller);
348  }
349  entry->render_view_id = render_view_id;
350  if (mirroring_manager_) {
351    mirroring_manager_->AddDiverter(
352        render_process_id_, entry->render_view_id, entry->controller);
353  }
354}
355
356void AudioRendererHost::OnPlayStream(int stream_id) {
357  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
358
359  AudioEntry* entry = LookupById(stream_id);
360  if (!entry) {
361    SendErrorMessage(stream_id);
362    return;
363  }
364
365  entry->controller->Play();
366  if (media_internals_)
367    media_internals_->OnSetAudioStreamPlaying(this, stream_id, true);
368
369#ifndef DISABLED_AUDIO_INDICATOR_TRIGGERS_FOR_M27_ONLY
370  MediaObserver* media_observer =
371      GetContentClient()->browser()->GetMediaObserver();
372  if (media_observer) {
373    media_observer->OnAudioStreamPlayingChanged(
374        render_process_id_, entry->render_view_id, stream_id, true);
375  }
376#endif
377}
378
379void AudioRendererHost::OnPauseStream(int stream_id) {
380  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
381
382  AudioEntry* entry = LookupById(stream_id);
383  if (!entry) {
384    SendErrorMessage(stream_id);
385    return;
386  }
387
388  entry->controller->Pause();
389  if (media_internals_)
390    media_internals_->OnSetAudioStreamPlaying(this, stream_id, false);
391
392#ifndef DISABLED_AUDIO_INDICATOR_TRIGGERS_FOR_M27_ONLY
393  MediaObserver* media_observer =
394      GetContentClient()->browser()->GetMediaObserver();
395  if (media_observer) {
396    media_observer->OnAudioStreamPlayingChanged(
397        render_process_id_, entry->render_view_id, stream_id, false);
398  }
399#endif
400}
401
402void AudioRendererHost::OnFlushStream(int stream_id) {
403  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
404
405  AudioEntry* entry = LookupById(stream_id);
406  if (!entry) {
407    SendErrorMessage(stream_id);
408    return;
409  }
410
411  entry->controller->Flush();
412  if (media_internals_)
413    media_internals_->OnSetAudioStreamStatus(this, stream_id, "flushed");
414}
415
416void AudioRendererHost::OnCloseStream(int stream_id) {
417  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
418
419  if (media_internals_)
420    media_internals_->OnSetAudioStreamStatus(this, stream_id, "closed");
421
422  AudioEntry* entry = LookupById(stream_id);
423
424  if (!entry)
425    return;
426
427  CloseAndDeleteStream(entry);
428}
429
430void AudioRendererHost::OnSetVolume(int stream_id, double volume) {
431  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
432
433  AudioEntry* entry = LookupById(stream_id);
434  if (!entry) {
435    SendErrorMessage(stream_id);
436    return;
437  }
438
439  // Make sure the volume is valid.
440  if (volume < 0 || volume > 1.0)
441    return;
442  entry->controller->SetVolume(volume);
443  if (media_internals_)
444    media_internals_->OnSetAudioStreamVolume(this, stream_id, volume);
445
446#ifndef DISABLED_AUDIO_INDICATOR_TRIGGERS_FOR_M27_ONLY
447  MediaObserver* media_observer =
448      GetContentClient()->browser()->GetMediaObserver();
449  if (media_observer) {
450    bool playing = volume > 0;
451    media_observer->OnAudioStreamPlayingChanged(
452        render_process_id_, entry->render_view_id, stream_id, playing);
453  }
454#endif
455}
456
457void AudioRendererHost::SendErrorMessage(int32 stream_id) {
458  Send(new AudioMsg_NotifyStreamStateChanged(
459      stream_id, media::AudioOutputIPCDelegate::kError));
460}
461
462void AudioRendererHost::DeleteEntries() {
463  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
464
465  for (AudioEntryMap::iterator i = audio_entries_.begin();
466       i != audio_entries_.end(); ++i) {
467    CloseAndDeleteStream(i->second);
468  }
469}
470
471void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
472  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
473
474#ifndef DISABLED_AUDIO_INDICATOR_TRIGGERS_FOR_M27_ONLY
475  MediaObserver* media_observer =
476      GetContentClient()->browser()->GetMediaObserver();
477  if (media_observer) {
478    media_observer->OnAudioStreamPlayingChanged(
479        render_process_id_, entry->render_view_id, entry->stream_id, false);
480  }
481#endif
482  if (!entry->pending_close) {
483    if (mirroring_manager_) {
484      mirroring_manager_->RemoveDiverter(
485          render_process_id_, entry->render_view_id, entry->controller);
486    }
487    entry->controller->Close(
488        base::Bind(&AudioRendererHost::DeleteEntry, this, entry));
489    entry->pending_close = true;
490  }
491}
492
493void AudioRendererHost::DeleteEntry(AudioEntry* entry) {
494  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
495
496  // Delete the entry when this method goes out of scope.
497  scoped_ptr<AudioEntry> entry_deleter(entry);
498
499  // Erase the entry identified by |stream_id| from the map.
500  audio_entries_.erase(entry->stream_id);
501
502  // Notify the media observer.
503  if (media_internals_)
504    media_internals_->OnDeleteAudioStream(this, entry->stream_id);
505}
506
507void AudioRendererHost::DeleteEntryOnError(AudioEntry* entry) {
508  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
509
510  // Sends the error message first before we close the stream because
511  // |entry| is destroyed in DeleteEntry().
512  SendErrorMessage(entry->stream_id);
513
514  if (media_internals_)
515    media_internals_->OnSetAudioStreamStatus(this, entry->stream_id, "error");
516  CloseAndDeleteStream(entry);
517}
518
519AudioRendererHost::AudioEntry* AudioRendererHost::LookupById(int stream_id) {
520  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
521
522  AudioEntryMap::iterator i = audio_entries_.find(stream_id);
523  if (i != audio_entries_.end() && !i->second->pending_close)
524    return i->second;
525  return NULL;
526}
527
528AudioRendererHost::AudioEntry* AudioRendererHost::LookupByController(
529    media::AudioOutputController* controller) {
530  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
531
532  // Iterate the map of entries.
533  // TODO(hclam): Implement a faster look up method.
534  for (AudioEntryMap::iterator i = audio_entries_.begin();
535       i != audio_entries_.end(); ++i) {
536    if (!i->second->pending_close && controller == i->second->controller.get())
537      return i->second;
538  }
539  return NULL;
540}
541
542media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting(
543    int stream_id) {
544  AudioEntry* const entry = LookupById(stream_id);
545  return entry ? entry->controller : NULL;
546}
547
548}  // namespace content
549