• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/debug/trace_event.h"
8 #include "base/logging.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"
13 
14 namespace ppapi {
15 
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;
20 }
21 
AudioCallbackCombined()22 AudioCallbackCombined::AudioCallbackCombined()
23     : callback_1_0_(NULL), callback_(NULL) {}
24 
AudioCallbackCombined(PPB_Audio_Callback_1_0 callback_1_0)25 AudioCallbackCombined::AudioCallbackCombined(
26     PPB_Audio_Callback_1_0 callback_1_0)
27     : callback_1_0_(callback_1_0), callback_(NULL) {}
28 
AudioCallbackCombined(PPB_Audio_Callback callback)29 AudioCallbackCombined::AudioCallbackCombined(PPB_Audio_Callback callback)
30     : callback_1_0_(NULL), callback_(callback) {}
31 
~AudioCallbackCombined()32 AudioCallbackCombined::~AudioCallbackCombined() {}
33 
IsValid() const34 bool AudioCallbackCombined::IsValid() const {
35   return callback_1_0_ || callback_;
36 }
37 
Run(void * sample_buffer,uint32_t buffer_size_in_bytes,PP_TimeDelta latency,void * user_data) const38 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();
48   }
49 }
50 
PPB_Audio_Shared()51 PPB_Audio_Shared::PPB_Audio_Shared()
52     : playing_(false),
53       shared_memory_size_(0),
54       nacl_thread_active_(false),
55       user_data_(NULL),
56       client_buffer_size_bytes_(0),
57       bytes_per_second_(0),
58       buffer_index_(0) {
59 }
60 
~PPB_Audio_Shared()61 PPB_Audio_Shared::~PPB_Audio_Shared() {
62   // Shut down the socket to escape any hanging |Receive|s.
63   if (socket_.get())
64     socket_->Shutdown();
65   StopThread();
66 }
67 
SetCallback(const AudioCallbackCombined & callback,void * user_data)68 void PPB_Audio_Shared::SetCallback(const AudioCallbackCombined& callback,
69                                    void* user_data) {
70   callback_ = callback;
71   user_data_ = user_data;
72 }
73 
SetStartPlaybackState()74 void PPB_Audio_Shared::SetStartPlaybackState() {
75   DCHECK(!playing_);
76   DCHECK(!audio_thread_.get());
77   DCHECK(!nacl_thread_active_);
78   // If the socket doesn't exist, that means that the plugin has started before
79   // the browser has had a chance to create all the shared memory info and
80   // notify us. This is a common case. In this case, we just set the playing_
81   // flag and the playback will automatically start when that data is available
82   // in SetStreamInfo.
83   playing_ = true;
84   StartThread();
85 }
86 
SetStopPlaybackState()87 void PPB_Audio_Shared::SetStopPlaybackState() {
88   DCHECK(playing_);
89   StopThread();
90   playing_ = false;
91 }
92 
SetStreamInfo(PP_Instance instance,base::SharedMemoryHandle shared_memory_handle,size_t shared_memory_size,base::SyncSocket::Handle socket_handle,PP_AudioSampleRate sample_rate,int sample_frame_count)93 void PPB_Audio_Shared::SetStreamInfo(
94     PP_Instance instance,
95     base::SharedMemoryHandle shared_memory_handle,
96     size_t shared_memory_size,
97     base::SyncSocket::Handle socket_handle,
98     PP_AudioSampleRate sample_rate,
99     int sample_frame_count) {
100   socket_.reset(new base::CancelableSyncSocket(socket_handle));
101   shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
102   shared_memory_size_ = shared_memory_size;
103   bytes_per_second_ =
104       kAudioOutputChannels * (kBitsPerAudioOutputSample / 8) * sample_rate;
105   buffer_index_ = 0;
106 
107   if (!shared_memory_->Map(shared_memory_size_)) {
108     PpapiGlobals::Get()->LogWithSource(
109         instance,
110         PP_LOGLEVEL_WARNING,
111         std::string(),
112         "Failed to map shared memory for PPB_Audio_Shared.");
113   } else {
114     audio_bus_ = media::AudioBus::WrapMemory(
115         kAudioOutputChannels, sample_frame_count, shared_memory_->memory());
116     // Setup integer audio buffer for user audio data.
117     client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() *
118                                 kBitsPerAudioOutputSample / 8;
119     client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
120   }
121 
122   StartThread();
123 }
124 
StartThread()125 void PPB_Audio_Shared::StartThread() {
126   // Don't start the thread unless all our state is set up correctly.
127   if (!playing_ || !callback_.IsValid() || !socket_.get() ||
128       !shared_memory_->memory() || !audio_bus_.get() || !client_buffer_.get() ||
129       bytes_per_second_ == 0)
130     return;
131   // Clear contents of shm buffer before starting audio thread. This will
132   // prevent a burst of static if for some reason the audio thread doesn't
133   // start up quickly enough.
134   memset(shared_memory_->memory(), 0, shared_memory_size_);
135   memset(client_buffer_.get(), 0, client_buffer_size_bytes_);
136 
137   if (g_nacl_mode) {
138     // Use NaCl's special API for IRT code that creates threads that call back
139     // into user code.
140     if (!IsThreadFunctionReady())
141       return;
142 
143     DCHECK(!nacl_thread_active_);
144     int result =
145         g_thread_functions.thread_create(&nacl_thread_id_, CallRun, this);
146     DCHECK_EQ(0, result);
147     nacl_thread_active_ = true;
148   } else {
149     DCHECK(!audio_thread_.get());
150     audio_thread_.reset(
151         new base::DelegateSimpleThread(this, "plugin_audio_thread"));
152     audio_thread_->Start();
153   }
154 }
155 
StopThread()156 void PPB_Audio_Shared::StopThread() {
157   // In general, the audio thread should not do Pepper calls, but it might
158   // anyway (for example, our Audio test does CallOnMainThread). If it did a
159   // pepper call which acquires the lock (most of them do), and we try to shut
160   // down the thread and Join it while holding the lock, we would deadlock. So
161   // we give up the lock here so that the thread at least _can_ make Pepper
162   // calls without causing deadlock.
163   if (g_nacl_mode) {
164     if (nacl_thread_active_) {
165       int result =
166           CallWhileUnlocked(g_thread_functions.thread_join, nacl_thread_id_);
167       DCHECK_EQ(0, result);
168       nacl_thread_active_ = false;
169     }
170   } else {
171     if (audio_thread_.get()) {
172       CallWhileUnlocked(base::Bind(&base::DelegateSimpleThread::Join,
173                                    base::Unretained(audio_thread_.get())));
174       audio_thread_.reset();
175     }
176   }
177 }
178 
179 // static
IsThreadFunctionReady()180 bool PPB_Audio_Shared::IsThreadFunctionReady() {
181   if (!g_nacl_mode)
182     return true;
183 
184   return (g_thread_functions.thread_create != NULL &&
185           g_thread_functions.thread_join != NULL);
186 }
187 
188 // static
SetNaClMode()189 void PPB_Audio_Shared::SetNaClMode() {
190   g_nacl_mode = true;
191 }
192 
193 // static
SetThreadFunctions(const struct PP_ThreadFunctions * functions)194 void PPB_Audio_Shared::SetThreadFunctions(
195     const struct PP_ThreadFunctions* functions) {
196   DCHECK(g_nacl_mode);
197   g_thread_functions = *functions;
198 }
199 
200 // static
CallRun(void * self)201 void PPB_Audio_Shared::CallRun(void* self) {
202   PPB_Audio_Shared* audio = static_cast<PPB_Audio_Shared*>(self);
203   audio->Run();
204 }
205 
Run()206 void PPB_Audio_Shared::Run() {
207   int pending_data = 0;
208   while (sizeof(pending_data) ==
209          socket_->Receive(&pending_data, sizeof(pending_data))) {
210     // |buffer_index_| must track the number of Receive() calls.  See the Send()
211     // call below for why this is important.
212     ++buffer_index_;
213     if (pending_data < 0)
214       break;
215 
216     {
217       TRACE_EVENT0("audio", "PPB_Audio_Shared::FireRenderCallback");
218       PP_TimeDelta latency =
219           static_cast<double>(pending_data) / bytes_per_second_;
220       callback_.Run(
221           client_buffer_.get(), client_buffer_size_bytes_, latency, user_data_);
222     }
223 
224     // Deinterleave the audio data into the shared memory as floats.
225     audio_bus_->FromInterleaved(client_buffer_.get(),
226                                 audio_bus_->frames(),
227                                 kBitsPerAudioOutputSample / 8);
228 
229     // Let the other end know which buffer we just filled.  The buffer index is
230     // used to ensure the other end is getting the buffer it expects.  For more
231     // details on how this works see AudioSyncReader::WaitUntilDataIsReady().
232     size_t bytes_sent = socket_->Send(&buffer_index_, sizeof(buffer_index_));
233     if (bytes_sent != sizeof(buffer_index_))
234       break;
235   }
236 }
237 
238 }  // namespace ppapi
239