Elim cr-checkbox
[chromium-blink-merge.git] / ppapi / shared_impl / ppb_audio_shared.cc
blob8747cc4adebdf04cdb851b32856f771ba01c4e28
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 "ppapi/shared_impl/ppb_audio_shared.h"
7 #include "base/logging.h"
8 #include "base/trace_event/trace_event.h"
9 #include "ppapi/nacl_irt/public/irt_ppapi.h"
10 #include "ppapi/shared_impl/ppapi_globals.h"
11 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
12 #include "ppapi/shared_impl/proxy_lock.h"
14 namespace ppapi {
16 namespace {
17 bool g_nacl_mode = false;
18 // Because this is static, the function pointers will be NULL initially.
19 PP_ThreadFunctions g_thread_functions;
22 AudioCallbackCombined::AudioCallbackCombined()
23 : callback_1_0_(NULL), callback_(NULL) {}
25 AudioCallbackCombined::AudioCallbackCombined(
26 PPB_Audio_Callback_1_0 callback_1_0)
27 : callback_1_0_(callback_1_0), callback_(NULL) {}
29 AudioCallbackCombined::AudioCallbackCombined(PPB_Audio_Callback callback)
30 : callback_1_0_(NULL), callback_(callback) {}
32 AudioCallbackCombined::~AudioCallbackCombined() {}
34 bool AudioCallbackCombined::IsValid() const {
35 return callback_1_0_ || callback_;
38 void AudioCallbackCombined::Run(void* sample_buffer,
39 uint32_t buffer_size_in_bytes,
40 PP_TimeDelta latency,
41 void* user_data) const {
42 if (callback_) {
43 callback_(sample_buffer, buffer_size_in_bytes, latency, user_data);
44 } else if (callback_1_0_) {
45 callback_1_0_(sample_buffer, buffer_size_in_bytes, user_data);
46 } else {
47 NOTREACHED();
51 PPB_Audio_Shared::PPB_Audio_Shared()
52 : playing_(false),
53 shared_memory_size_(0),
54 nacl_thread_id_(0),
55 nacl_thread_active_(false),
56 user_data_(NULL),
57 client_buffer_size_bytes_(0),
58 bytes_per_second_(0),
59 buffer_index_(0) {
62 PPB_Audio_Shared::~PPB_Audio_Shared() {
63 // Shut down the socket to escape any hanging |Receive|s.
64 if (socket_.get())
65 socket_->Shutdown();
66 StopThread();
69 void PPB_Audio_Shared::SetCallback(const AudioCallbackCombined& callback,
70 void* user_data) {
71 callback_ = callback;
72 user_data_ = user_data;
75 void PPB_Audio_Shared::SetStartPlaybackState() {
76 DCHECK(!playing_);
77 DCHECK(!audio_thread_.get());
78 DCHECK(!nacl_thread_active_);
79 // If the socket doesn't exist, that means that the plugin has started before
80 // the browser has had a chance to create all the shared memory info and
81 // notify us. This is a common case. In this case, we just set the playing_
82 // flag and the playback will automatically start when that data is available
83 // in SetStreamInfo.
84 playing_ = true;
85 StartThread();
88 void PPB_Audio_Shared::SetStopPlaybackState() {
89 DCHECK(playing_);
90 StopThread();
91 playing_ = false;
94 void PPB_Audio_Shared::SetStreamInfo(
95 PP_Instance instance,
96 base::SharedMemoryHandle shared_memory_handle,
97 size_t shared_memory_size,
98 base::SyncSocket::Handle socket_handle,
99 PP_AudioSampleRate sample_rate,
100 int sample_frame_count) {
101 socket_.reset(new base::CancelableSyncSocket(socket_handle));
102 shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
103 shared_memory_size_ = shared_memory_size;
104 bytes_per_second_ =
105 kAudioOutputChannels * (kBitsPerAudioOutputSample / 8) * sample_rate;
106 buffer_index_ = 0;
108 if (!shared_memory_->Map(shared_memory_size_)) {
109 PpapiGlobals::Get()->LogWithSource(
110 instance,
111 PP_LOGLEVEL_WARNING,
112 std::string(),
113 "Failed to map shared memory for PPB_Audio_Shared.");
114 } else {
115 audio_bus_ = media::AudioBus::WrapMemory(
116 kAudioOutputChannels, sample_frame_count, shared_memory_->memory());
117 // Setup integer audio buffer for user audio data.
118 client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() *
119 kBitsPerAudioOutputSample / 8;
120 client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
123 StartThread();
126 void PPB_Audio_Shared::StartThread() {
127 // Don't start the thread unless all our state is set up correctly.
128 if (!playing_ || !callback_.IsValid() || !socket_.get() ||
129 !shared_memory_->memory() || !audio_bus_.get() || !client_buffer_.get() ||
130 bytes_per_second_ == 0)
131 return;
132 // Clear contents of shm buffer before starting audio thread. This will
133 // prevent a burst of static if for some reason the audio thread doesn't
134 // start up quickly enough.
135 memset(shared_memory_->memory(), 0, shared_memory_size_);
136 memset(client_buffer_.get(), 0, client_buffer_size_bytes_);
138 if (g_nacl_mode) {
139 // Use NaCl's special API for IRT code that creates threads that call back
140 // into user code.
141 if (!IsThreadFunctionReady())
142 return;
144 DCHECK(!nacl_thread_active_);
145 int result =
146 g_thread_functions.thread_create(&nacl_thread_id_, CallRun, this);
147 DCHECK_EQ(0, result);
148 nacl_thread_active_ = true;
149 } else {
150 DCHECK(!audio_thread_.get());
151 audio_thread_.reset(
152 new base::DelegateSimpleThread(this, "plugin_audio_thread"));
153 audio_thread_->Start();
157 void PPB_Audio_Shared::StopThread() {
158 // In general, the audio thread should not do Pepper calls, but it might
159 // anyway (for example, our Audio test does CallOnMainThread). If it did a
160 // pepper call which acquires the lock (most of them do), and we try to shut
161 // down the thread and Join it while holding the lock, we would deadlock. So
162 // we give up the lock here so that the thread at least _can_ make Pepper
163 // calls without causing deadlock.
164 // IMPORTANT: This instance's thread state should be reset to uninitialized
165 // before we release the proxy lock, so any calls from the plugin while we're
166 // unlocked can't access the joined thread.
167 if (g_nacl_mode) {
168 if (nacl_thread_active_) {
169 nacl_thread_active_ = false;
170 int result =
171 CallWhileUnlocked(g_thread_functions.thread_join, nacl_thread_id_);
172 DCHECK_EQ(0, result);
174 } else {
175 if (audio_thread_.get()) {
176 auto local_audio_thread(audio_thread_.Pass());
177 CallWhileUnlocked(base::Bind(&base::DelegateSimpleThread::Join,
178 base::Unretained(local_audio_thread.get())));
183 // static
184 bool PPB_Audio_Shared::IsThreadFunctionReady() {
185 if (!g_nacl_mode)
186 return true;
188 return (g_thread_functions.thread_create != NULL &&
189 g_thread_functions.thread_join != NULL);
192 // static
193 void PPB_Audio_Shared::SetNaClMode() {
194 g_nacl_mode = true;
197 // static
198 void PPB_Audio_Shared::SetThreadFunctions(
199 const struct PP_ThreadFunctions* functions) {
200 DCHECK(g_nacl_mode);
201 g_thread_functions = *functions;
204 // static
205 void PPB_Audio_Shared::CallRun(void* self) {
206 PPB_Audio_Shared* audio = static_cast<PPB_Audio_Shared*>(self);
207 audio->Run();
210 void PPB_Audio_Shared::Run() {
211 int pending_data = 0;
212 while (sizeof(pending_data) ==
213 socket_->Receive(&pending_data, sizeof(pending_data))) {
214 // |buffer_index_| must track the number of Receive() calls. See the Send()
215 // call below for why this is important.
216 ++buffer_index_;
217 if (pending_data < 0)
218 break;
221 TRACE_EVENT0("audio", "PPB_Audio_Shared::FireRenderCallback");
222 PP_TimeDelta latency =
223 static_cast<double>(pending_data) / bytes_per_second_;
224 callback_.Run(
225 client_buffer_.get(), client_buffer_size_bytes_, latency, user_data_);
228 // Deinterleave the audio data into the shared memory as floats.
229 audio_bus_->FromInterleaved(client_buffer_.get(),
230 audio_bus_->frames(),
231 kBitsPerAudioOutputSample / 8);
233 // Let the other end know which buffer we just filled. The buffer index is
234 // used to ensure the other end is getting the buffer it expects. For more
235 // details on how this works see AudioSyncReader::WaitUntilDataIsReady().
236 size_t bytes_sent = socket_->Send(&buffer_index_, sizeof(buffer_index_));
237 if (bytes_sent != sizeof(buffer_index_))
238 break;
242 } // namespace ppapi