ppb_audio_shared.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 "ppapi/shared_impl/ppb_audio_shared.h"
6
7#include "base/logging.h"
8#include "media/audio/shared_memory_util.h"
9#include "ppapi/shared_impl/ppapi_globals.h"
10#include "ppapi/shared_impl/ppb_audio_config_shared.h"
11#include "ppapi/shared_impl/proxy_lock.h"
12
13namespace ppapi {
14
15#if defined(OS_NACL)
16namespace {
17// Because this is static, the function pointers will be NULL initially.
18PP_ThreadFunctions thread_functions;
19}
20#endif  // defined(OS_NACL)
21
22AudioCallbackCombined::AudioCallbackCombined() : callback_1_0_(NULL),
23                                                 callback_(NULL) {
24}
25
26AudioCallbackCombined::AudioCallbackCombined(
27    PPB_Audio_Callback_1_0 callback_1_0)
28    : callback_1_0_(callback_1_0),
29      callback_(NULL) {
30}
31
32AudioCallbackCombined::AudioCallbackCombined(PPB_Audio_Callback callback)
33    : callback_1_0_(NULL),
34      callback_(callback) {
35}
36
37AudioCallbackCombined::~AudioCallbackCombined() {
38}
39
40bool AudioCallbackCombined::IsValid() const {
41  return callback_1_0_ || callback_;
42}
43
44void AudioCallbackCombined::Run(void* sample_buffer,
45                                uint32_t buffer_size_in_bytes,
46                                PP_TimeDelta latency,
47                                void* user_data) const {
48  if (callback_) {
49    callback_(sample_buffer, buffer_size_in_bytes, latency, user_data);
50  } else if (callback_1_0_) {
51    callback_1_0_(sample_buffer, buffer_size_in_bytes, user_data);
52  } else {
53    NOTREACHED();
54  }
55}
56
57PPB_Audio_Shared::PPB_Audio_Shared()
58    : playing_(false),
59      shared_memory_size_(0),
60#if defined(OS_NACL)
61      thread_id_(0),
62      thread_active_(false),
63#endif
64      user_data_(NULL),
65      client_buffer_size_bytes_(0),
66      bytes_per_second_(0) {
67}
68
69PPB_Audio_Shared::~PPB_Audio_Shared() {
70  // Shut down the socket to escape any hanging |Receive|s.
71  if (socket_.get())
72    socket_->Shutdown();
73  StopThread();
74}
75
76void PPB_Audio_Shared::SetCallback(const AudioCallbackCombined& callback,
77                                   void* user_data) {
78  callback_ = callback;
79  user_data_ = user_data;
80}
81
82void PPB_Audio_Shared::SetStartPlaybackState() {
83  DCHECK(!playing_);
84#if !defined(OS_NACL)
85  DCHECK(!audio_thread_.get());
86#else
87  DCHECK(!thread_active_);
88#endif
89  // If the socket doesn't exist, that means that the plugin has started before
90  // the browser has had a chance to create all the shared memory info and
91  // notify us. This is a common case. In this case, we just set the playing_
92  // flag and the playback will automatically start when that data is available
93  // in SetStreamInfo.
94  playing_ = true;
95  StartThread();
96}
97
98void PPB_Audio_Shared::SetStopPlaybackState() {
99  DCHECK(playing_);
100  StopThread();
101  playing_ = false;
102}
103
104void PPB_Audio_Shared::SetStreamInfo(
105    PP_Instance instance,
106    base::SharedMemoryHandle shared_memory_handle,
107    size_t shared_memory_size,
108    base::SyncSocket::Handle socket_handle,
109    PP_AudioSampleRate sample_rate,
110    int sample_frame_count) {
111  socket_.reset(new base::CancelableSyncSocket(socket_handle));
112  shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
113  shared_memory_size_ = shared_memory_size;
114  bytes_per_second_ = kAudioOutputChannels * (kBitsPerAudioOutputSample / 8) *
115                      sample_rate;
116
117  if (!shared_memory_->Map(
118          media::TotalSharedMemorySizeInBytes(shared_memory_size_))) {
119    PpapiGlobals::Get()->LogWithSource(
120        instance,
121        PP_LOGLEVEL_WARNING,
122        std::string(),
123        "Failed to map shared memory for PPB_Audio_Shared.");
124  } else {
125    audio_bus_ = media::AudioBus::WrapMemory(
126        kAudioOutputChannels, sample_frame_count, shared_memory_->memory());
127    // Setup integer audio buffer for user audio data.
128    client_buffer_size_bytes_ =
129        audio_bus_->frames() * audio_bus_->channels() *
130        kBitsPerAudioOutputSample / 8;
131    client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
132  }
133
134  StartThread();
135}
136
137void PPB_Audio_Shared::StartThread() {
138  // Don't start the thread unless all our state is set up correctly.
139  if (!playing_ || !callback_.IsValid() || !socket_.get() ||
140      !shared_memory_->memory() || !audio_bus_.get() || !client_buffer_.get() ||
141      bytes_per_second_ == 0)
142    return;
143  // Clear contents of shm buffer before starting audio thread. This will
144  // prevent a burst of static if for some reason the audio thread doesn't
145  // start up quickly enough.
146  memset(shared_memory_->memory(), 0, shared_memory_size_);
147  memset(client_buffer_.get(), 0, client_buffer_size_bytes_);
148#if !defined(OS_NACL)
149  DCHECK(!audio_thread_.get());
150  audio_thread_.reset(new base::DelegateSimpleThread(
151      this, "plugin_audio_thread"));
152  audio_thread_->Start();
153#else
154  // Use NaCl's special API for IRT code that creates threads that call back
155  // into user code.
156  if (NULL == thread_functions.thread_create ||
157      NULL == thread_functions.thread_join)
158    return;
159
160  int result = thread_functions.thread_create(&thread_id_, CallRun, this);
161  DCHECK_EQ(result, 0);
162  thread_active_ = true;
163#endif
164}
165
166void PPB_Audio_Shared::StopThread() {
167#if !defined(OS_NACL)
168  if (audio_thread_.get()) {
169    // In general, the audio thread should not do Pepper calls, but it might
170    // anyway (for example, our Audio test does CallOnMainThread). If it did
171    // a pepper call which acquires the lock (most of them do), and we try to
172    // shut down the thread and Join it while holding the lock, we would
173    // deadlock. So we give up the lock here so that the thread at least _can_
174    // make Pepper calls without causing deadlock.
175    CallWhileUnlocked(base::Bind(&base::DelegateSimpleThread::Join,
176                                 base::Unretained(audio_thread_.get())));
177    audio_thread_.reset();
178  }
179#else
180  if (thread_active_) {
181    // See comment above about why we unlock here.
182    int result = CallWhileUnlocked(thread_functions.thread_join, thread_id_);
183    DCHECK_EQ(0, result);
184    thread_active_ = false;
185  }
186#endif
187}
188
189#if defined(OS_NACL)
190// static
191void PPB_Audio_Shared::SetThreadFunctions(
192    const struct PP_ThreadFunctions* functions) {
193  DCHECK(thread_functions.thread_create == NULL);
194  DCHECK(thread_functions.thread_join == NULL);
195  thread_functions = *functions;
196}
197
198// static
199void PPB_Audio_Shared::CallRun(void* self) {
200  PPB_Audio_Shared* audio = static_cast<PPB_Audio_Shared*>(self);
201  audio->Run();
202}
203#endif
204
205void PPB_Audio_Shared::Run() {
206  int pending_data;
207  const int bytes_per_frame =
208      sizeof(*audio_bus_->channel(0)) * audio_bus_->channels();
209
210  while (sizeof(pending_data) ==
211      socket_->Receive(&pending_data, sizeof(pending_data)) &&
212      pending_data != media::kPauseMark) {
213    PP_TimeDelta latency =
214        static_cast<double>(pending_data) / bytes_per_second_;
215    callback_.Run(client_buffer_.get(), client_buffer_size_bytes_, latency,
216                  user_data_);
217
218    // Deinterleave the audio data into the shared memory as float.
219    audio_bus_->FromInterleaved(
220        client_buffer_.get(), audio_bus_->frames(),
221        kBitsPerAudioOutputSample / 8);
222
223    // Let the host know we are done.
224    // TODO(dalecurtis): Technically this is not the exact size.  Due to channel
225    // padding for alignment, there may be more data available than this.  We're
226    // relying on AudioSyncReader::Read() to parse this with that in mind.
227    // Rename these methods to Set/GetActualFrameCount().
228    media::SetActualDataSizeInBytes(
229        shared_memory_.get(), shared_memory_size_,
230        audio_bus_->frames() * bytes_per_frame);
231  }
232}
233
234}  // namespace ppapi
235