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/pepper/pepper_platform_audio_output.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "build/build_config.h"
11#include "content/child/child_process.h"
12#include "content/common/media/audio_messages.h"
13#include "content/renderer/media/audio_message_filter.h"
14#include "content/renderer/pepper/audio_helper.h"
15#include "content/renderer/render_thread_impl.h"
16#include "media/base/audio_hardware_config.h"
17#include "ppapi/shared_impl/ppb_audio_config_shared.h"
18
19namespace content {
20
21// static
22PepperPlatformAudioOutput* PepperPlatformAudioOutput::Create(
23    int sample_rate,
24    int frames_per_buffer,
25    int source_render_view_id,
26    int source_render_frame_id,
27    AudioHelper* client) {
28  scoped_refptr<PepperPlatformAudioOutput> audio_output(
29      new PepperPlatformAudioOutput());
30  if (audio_output->Initialize(sample_rate,
31                               frames_per_buffer,
32                               source_render_view_id,
33                               source_render_frame_id,
34                               client)) {
35    // Balanced by Release invoked in
36    // PepperPlatformAudioOutput::ShutDownOnIOThread().
37    audio_output->AddRef();
38    return audio_output.get();
39  }
40  return NULL;
41}
42
43bool PepperPlatformAudioOutput::StartPlayback() {
44  if (ipc_) {
45    io_message_loop_proxy_->PostTask(
46        FROM_HERE,
47        base::Bind(&PepperPlatformAudioOutput::StartPlaybackOnIOThread, this));
48    return true;
49  }
50  return false;
51}
52
53bool PepperPlatformAudioOutput::StopPlayback() {
54  if (ipc_) {
55    io_message_loop_proxy_->PostTask(
56        FROM_HERE,
57        base::Bind(&PepperPlatformAudioOutput::StopPlaybackOnIOThread, this));
58    return true;
59  }
60  return false;
61}
62
63void PepperPlatformAudioOutput::ShutDown() {
64  // Called on the main thread to stop all audio callbacks. We must only change
65  // the client on the main thread, and the delegates from the I/O thread.
66  client_ = NULL;
67  io_message_loop_proxy_->PostTask(
68      FROM_HERE,
69      base::Bind(&PepperPlatformAudioOutput::ShutDownOnIOThread, this));
70}
71
72void PepperPlatformAudioOutput::OnStateChanged(
73    media::AudioOutputIPCDelegate::State state) {}
74
75void PepperPlatformAudioOutput::OnStreamCreated(
76    base::SharedMemoryHandle handle,
77    base::SyncSocket::Handle socket_handle,
78    int length) {
79#if defined(OS_WIN)
80  DCHECK(handle);
81  DCHECK(socket_handle);
82#else
83  DCHECK_NE(-1, handle.fd);
84  DCHECK_NE(-1, socket_handle);
85#endif
86  DCHECK(length);
87
88  if (base::MessageLoopProxy::current().get() ==
89      main_message_loop_proxy_.get()) {
90    // Must dereference the client only on the main thread. Shutdown may have
91    // occurred while the request was in-flight, so we need to NULL check.
92    if (client_)
93      client_->StreamCreated(handle, length, socket_handle);
94  } else {
95    main_message_loop_proxy_->PostTask(
96        FROM_HERE,
97        base::Bind(&PepperPlatformAudioOutput::OnStreamCreated,
98                   this,
99                   handle,
100                   socket_handle,
101                   length));
102  }
103}
104
105void PepperPlatformAudioOutput::OnIPCClosed() { ipc_.reset(); }
106
107PepperPlatformAudioOutput::~PepperPlatformAudioOutput() {
108  // Make sure we have been shut down. Warning: this will usually happen on
109  // the I/O thread!
110  DCHECK(!ipc_);
111  DCHECK(!client_);
112}
113
114PepperPlatformAudioOutput::PepperPlatformAudioOutput()
115    : client_(NULL),
116      main_message_loop_proxy_(base::MessageLoopProxy::current()),
117      io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()) {
118}
119
120bool PepperPlatformAudioOutput::Initialize(int sample_rate,
121                                           int frames_per_buffer,
122                                           int source_render_view_id,
123                                           int source_render_frame_id,
124                                           AudioHelper* client) {
125  DCHECK(client);
126  client_ = client;
127
128  RenderThreadImpl* const render_thread = RenderThreadImpl::current();
129  ipc_ = render_thread->audio_message_filter()->CreateAudioOutputIPC(
130      source_render_view_id, source_render_frame_id);
131  CHECK(ipc_);
132
133  media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
134                                media::CHANNEL_LAYOUT_STEREO,
135                                sample_rate,
136                                ppapi::kBitsPerAudioOutputSample,
137                                frames_per_buffer);
138
139  io_message_loop_proxy_->PostTask(
140      FROM_HERE,
141      base::Bind(
142          &PepperPlatformAudioOutput::InitializeOnIOThread, this, params));
143  return true;
144}
145
146void PepperPlatformAudioOutput::InitializeOnIOThread(
147    const media::AudioParameters& params) {
148  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
149  const int kSessionId = 0;
150  if (ipc_)
151    ipc_->CreateStream(this, params, kSessionId);
152}
153
154void PepperPlatformAudioOutput::StartPlaybackOnIOThread() {
155  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
156  if (ipc_)
157    ipc_->PlayStream();
158}
159
160void PepperPlatformAudioOutput::StopPlaybackOnIOThread() {
161  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
162  if (ipc_)
163    ipc_->PauseStream();
164}
165
166void PepperPlatformAudioOutput::ShutDownOnIOThread() {
167  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
168
169  // Make sure we don't call shutdown more than once.
170  if (!ipc_)
171    return;
172
173  ipc_->CloseStream();
174  ipc_.reset();
175
176  Release();  // Release for the delegate, balances out the reference taken in
177              // PepperPlatformAudioOutput::Create.
178}
179
180}  // namespace content
181