[email protected] | 028b90f6 | 2012-04-05 01:44:13 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 9a57839 | 2011-12-07 18:59:27 | [diff] [blame] | 5 | #include "ppapi/shared_impl/ppb_audio_shared.h" |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 6 | |
dcheng | d2b9f61 | 2015-12-18 19:08:51 | [diff] [blame] | 7 | #include <utility> |
| 8 | |
Sebastien Marchand | 6d0558fd | 2019-01-25 16:49:37 | [diff] [blame] | 9 | #include "base/bind.h" |
[email protected] | d1bbf6fd | 2011-07-15 22:09:28 | [diff] [blame] | 10 | #include "base/logging.h" |
primiano | 3ca2296 | 2015-01-30 17:08:37 | [diff] [blame] | 11 | #include "base/trace_event/trace_event.h" |
jrummell | 37a54c0 | 2016-04-22 19:54:27 | [diff] [blame] | 12 | #include "media/base/audio_parameters.h" |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 13 | #include "ppapi/nacl_irt/public/irt_ppapi.h" |
[email protected] | 9672468 | 2012-04-26 20:53:37 | [diff] [blame] | 14 | #include "ppapi/shared_impl/ppapi_globals.h" |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 15 | #include "ppapi/shared_impl/ppb_audio_config_shared.h" |
[email protected] | 17a17b0 | 2013-07-25 21:07:39 | [diff] [blame] | 16 | #include "ppapi/shared_impl/proxy_lock.h" |
[email protected] | d1bbf6fd | 2011-07-15 22:09:28 | [diff] [blame] | 17 | |
[email protected] | 55cdf605 | 2011-05-13 19:22:53 | [diff] [blame] | 18 | namespace ppapi { |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 19 | |
[email protected] | 420ade4 | 2012-07-25 21:47:31 | [diff] [blame] | 20 | namespace { |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 21 | bool g_nacl_mode = false; |
[email protected] | 420ade4 | 2012-07-25 21:47:31 | [diff] [blame] | 22 | // Because this is static, the function pointers will be NULL initially. |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 23 | PP_ThreadFunctions g_thread_functions; |
[email protected] | 420ade4 | 2012-07-25 21:47:31 | [diff] [blame] | 24 | } |
[email protected] | 420ade4 | 2012-07-25 21:47:31 | [diff] [blame] | 25 | |
[email protected] | 665b5c54 | 2014-02-22 08:06:26 | [diff] [blame] | 26 | AudioCallbackCombined::AudioCallbackCombined() |
| 27 | : callback_1_0_(NULL), callback_(NULL) {} |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 28 | |
| 29 | AudioCallbackCombined::AudioCallbackCombined( |
| 30 | PPB_Audio_Callback_1_0 callback_1_0) |
[email protected] | 665b5c54 | 2014-02-22 08:06:26 | [diff] [blame] | 31 | : callback_1_0_(callback_1_0), callback_(NULL) {} |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 32 | |
| 33 | AudioCallbackCombined::AudioCallbackCombined(PPB_Audio_Callback callback) |
[email protected] | 665b5c54 | 2014-02-22 08:06:26 | [diff] [blame] | 34 | : callback_1_0_(NULL), callback_(callback) {} |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 35 | |
[email protected] | 665b5c54 | 2014-02-22 08:06:26 | [diff] [blame] | 36 | AudioCallbackCombined::~AudioCallbackCombined() {} |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 37 | |
| 38 | bool AudioCallbackCombined::IsValid() const { |
| 39 | return callback_1_0_ || callback_; |
| 40 | } |
| 41 | |
| 42 | void AudioCallbackCombined::Run(void* sample_buffer, |
| 43 | uint32_t buffer_size_in_bytes, |
| 44 | PP_TimeDelta latency, |
| 45 | void* user_data) const { |
| 46 | if (callback_) { |
| 47 | callback_(sample_buffer, buffer_size_in_bytes, latency, user_data); |
| 48 | } else if (callback_1_0_) { |
| 49 | callback_1_0_(sample_buffer, buffer_size_in_bytes, user_data); |
| 50 | } else { |
| 51 | NOTREACHED(); |
| 52 | } |
| 53 | } |
| 54 | |
[email protected] | 9a57839 | 2011-12-07 18:59:27 | [diff] [blame] | 55 | PPB_Audio_Shared::PPB_Audio_Shared() |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 56 | : playing_(false), |
| 57 | shared_memory_size_(0), |
tommycli | 43d8cc4 | 2015-03-20 17:48:19 | [diff] [blame] | 58 | nacl_thread_id_(0), |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 59 | nacl_thread_active_(false), |
[email protected] | dfc48e4c | 2012-09-05 13:39:21 | [diff] [blame] | 60 | user_data_(NULL), |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 61 | client_buffer_size_bytes_(0), |
[email protected] | 1caf0aa | 2013-10-31 07:58:02 | [diff] [blame] | 62 | bytes_per_second_(0), |
| 63 | buffer_index_(0) { |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 64 | } |
| 65 | |
[email protected] | 9a57839 | 2011-12-07 18:59:27 | [diff] [blame] | 66 | PPB_Audio_Shared::~PPB_Audio_Shared() { |
[email protected] | 7b388d1 | 2012-08-25 00:22:52 | [diff] [blame] | 67 | // Shut down the socket to escape any hanging |Receive|s. |
| 68 | if (socket_.get()) |
| 69 | socket_->Shutdown(); |
[email protected] | 0e8d17b1 | 2012-05-15 21:32:25 | [diff] [blame] | 70 | StopThread(); |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 71 | } |
| 72 | |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 73 | void PPB_Audio_Shared::SetCallback(const AudioCallbackCombined& callback, |
[email protected] | 9a57839 | 2011-12-07 18:59:27 | [diff] [blame] | 74 | void* user_data) { |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 75 | callback_ = callback; |
| 76 | user_data_ = user_data; |
| 77 | } |
| 78 | |
[email protected] | 9a57839 | 2011-12-07 18:59:27 | [diff] [blame] | 79 | void PPB_Audio_Shared::SetStartPlaybackState() { |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 80 | DCHECK(!playing_); |
| 81 | DCHECK(!audio_thread_.get()); |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 82 | DCHECK(!nacl_thread_active_); |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 83 | // If the socket doesn't exist, that means that the plugin has started before |
| 84 | // the browser has had a chance to create all the shared memory info and |
| 85 | // notify us. This is a common case. In this case, we just set the playing_ |
| 86 | // flag and the playback will automatically start when that data is available |
| 87 | // in SetStreamInfo. |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 88 | playing_ = true; |
[email protected] | 9672468 | 2012-04-26 20:53:37 | [diff] [blame] | 89 | StartThread(); |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 90 | } |
| 91 | |
[email protected] | 9a57839 | 2011-12-07 18:59:27 | [diff] [blame] | 92 | void PPB_Audio_Shared::SetStopPlaybackState() { |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 93 | DCHECK(playing_); |
[email protected] | 0e8d17b1 | 2012-05-15 21:32:25 | [diff] [blame] | 94 | StopThread(); |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 95 | playing_ = false; |
| 96 | } |
| 97 | |
[email protected] | 9a57839 | 2011-12-07 18:59:27 | [diff] [blame] | 98 | void PPB_Audio_Shared::SetStreamInfo( |
[email protected] | 9672468 | 2012-04-26 20:53:37 | [diff] [blame] | 99 | PP_Instance instance, |
Alexandr Ilin | 20f2841c | 2018-06-01 11:56:18 | [diff] [blame] | 100 | base::UnsafeSharedMemoryRegion shared_memory_region, |
[email protected] | dfc48e4c | 2012-09-05 13:39:21 | [diff] [blame] | 101 | base::SyncSocket::Handle socket_handle, |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 102 | PP_AudioSampleRate sample_rate, |
[email protected] | dfc48e4c | 2012-09-05 13:39:21 | [diff] [blame] | 103 | int sample_frame_count) { |
[email protected] | 0e8d17b1 | 2012-05-15 21:32:25 | [diff] [blame] | 104 | socket_.reset(new base::CancelableSyncSocket(socket_handle)); |
Max Morin | 30996fb | 2017-09-01 13:28:35 | [diff] [blame] | 105 | shared_memory_size_ = media::ComputeAudioOutputBufferSize( |
| 106 | kAudioOutputChannels, sample_frame_count); |
Alexandr Ilin | 20f2841c | 2018-06-01 11:56:18 | [diff] [blame] | 107 | DCHECK_GE(shared_memory_region.GetSize(), shared_memory_size_); |
[email protected] | 665b5c54 | 2014-02-22 08:06:26 | [diff] [blame] | 108 | bytes_per_second_ = |
| 109 | kAudioOutputChannels * (kBitsPerAudioOutputSample / 8) * sample_rate; |
[email protected] | 1caf0aa | 2013-10-31 07:58:02 | [diff] [blame] | 110 | buffer_index_ = 0; |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 111 | |
Alexandr Ilin | 20f2841c | 2018-06-01 11:56:18 | [diff] [blame] | 112 | shared_memory_ = shared_memory_region.MapAt(0, shared_memory_size_); |
| 113 | if (!shared_memory_.IsValid()) { |
[email protected] | 007b3f8 | 2013-04-09 08:46:45 | [diff] [blame] | 114 | PpapiGlobals::Get()->LogWithSource( |
| 115 | instance, |
| 116 | PP_LOGLEVEL_WARNING, |
| 117 | std::string(), |
| 118 | "Failed to map shared memory for PPB_Audio_Shared."); |
[email protected] | dfc48e4c | 2012-09-05 13:39:21 | [diff] [blame] | 119 | } else { |
grunell | 09a6d793 | 2015-12-18 11:28:21 | [diff] [blame] | 120 | media::AudioOutputBuffer* buffer = |
Alexandr Ilin | 20f2841c | 2018-06-01 11:56:18 | [diff] [blame] | 121 | reinterpret_cast<media::AudioOutputBuffer*>(shared_memory_.memory()); |
grunell | 09a6d793 | 2015-12-18 11:28:21 | [diff] [blame] | 122 | audio_bus_ = media::AudioBus::WrapMemory(kAudioOutputChannels, |
| 123 | sample_frame_count, buffer->audio); |
[email protected] | dfc48e4c | 2012-09-05 13:39:21 | [diff] [blame] | 124 | // Setup integer audio buffer for user audio data. |
[email protected] | 665b5c54 | 2014-02-22 08:06:26 | [diff] [blame] | 125 | client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() * |
| 126 | kBitsPerAudioOutputSample / 8; |
[email protected] | dfc48e4c | 2012-09-05 13:39:21 | [diff] [blame] | 127 | client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]); |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 128 | } |
[email protected] | 9672468 | 2012-04-26 20:53:37 | [diff] [blame] | 129 | |
| 130 | StartThread(); |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 131 | } |
| 132 | |
[email protected] | 9a57839 | 2011-12-07 18:59:27 | [diff] [blame] | 133 | void PPB_Audio_Shared::StartThread() { |
[email protected] | 9672468 | 2012-04-26 20:53:37 | [diff] [blame] | 134 | // Don't start the thread unless all our state is set up correctly. |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 135 | if (!playing_ || !callback_.IsValid() || !socket_.get() || |
Alexandr Ilin | 20f2841c | 2018-06-01 11:56:18 | [diff] [blame] | 136 | !shared_memory_.memory() || !audio_bus_.get() || !client_buffer_.get() || |
[email protected] | 6b151126 | 2013-09-06 21:46:34 | [diff] [blame] | 137 | bytes_per_second_ == 0) |
[email protected] | 9672468 | 2012-04-26 20:53:37 | [diff] [blame] | 138 | return; |
[email protected] | 420ade4 | 2012-07-25 21:47:31 | [diff] [blame] | 139 | // Clear contents of shm buffer before starting audio thread. This will |
| 140 | // prevent a burst of static if for some reason the audio thread doesn't |
| 141 | // start up quickly enough. |
Alexandr Ilin | 20f2841c | 2018-06-01 11:56:18 | [diff] [blame] | 142 | memset(shared_memory_.memory(), 0, shared_memory_size_); |
[email protected] | dfc48e4c | 2012-09-05 13:39:21 | [diff] [blame] | 143 | memset(client_buffer_.get(), 0, client_buffer_size_bytes_); |
[email protected] | 420ade4 | 2012-07-25 21:47:31 | [diff] [blame] | 144 | |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 145 | if (g_nacl_mode) { |
| 146 | // Use NaCl's special API for IRT code that creates threads that call back |
| 147 | // into user code. |
| 148 | if (!IsThreadFunctionReady()) |
| 149 | return; |
| 150 | |
| 151 | DCHECK(!nacl_thread_active_); |
| 152 | int result = |
| 153 | g_thread_functions.thread_create(&nacl_thread_id_, CallRun, this); |
| 154 | DCHECK_EQ(0, result); |
| 155 | nacl_thread_active_ = true; |
| 156 | } else { |
| 157 | DCHECK(!audio_thread_.get()); |
| 158 | audio_thread_.reset( |
| 159 | new base::DelegateSimpleThread(this, "plugin_audio_thread")); |
| 160 | audio_thread_->Start(); |
| 161 | } |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 162 | } |
| 163 | |
[email protected] | 0e8d17b1 | 2012-05-15 21:32:25 | [diff] [blame] | 164 | void PPB_Audio_Shared::StopThread() { |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 165 | // In general, the audio thread should not do Pepper calls, but it might |
| 166 | // anyway (for example, our Audio test does CallOnMainThread). If it did a |
| 167 | // pepper call which acquires the lock (most of them do), and we try to shut |
| 168 | // down the thread and Join it while holding the lock, we would deadlock. So |
| 169 | // we give up the lock here so that the thread at least _can_ make Pepper |
| 170 | // calls without causing deadlock. |
bbudge | 338f969 | 2015-09-10 00:58:15 | [diff] [blame] | 171 | // IMPORTANT: This instance's thread state should be reset to uninitialized |
| 172 | // before we release the proxy lock, so any calls from the plugin while we're |
| 173 | // unlocked can't access the joined thread. |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 174 | if (g_nacl_mode) { |
| 175 | if (nacl_thread_active_) { |
bbudge | 338f969 | 2015-09-10 00:58:15 | [diff] [blame] | 176 | nacl_thread_active_ = false; |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 177 | int result = |
| 178 | CallWhileUnlocked(g_thread_functions.thread_join, nacl_thread_id_); |
| 179 | DCHECK_EQ(0, result); |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 180 | } |
| 181 | } else { |
| 182 | if (audio_thread_.get()) { |
dcheng | d2b9f61 | 2015-12-18 19:08:51 | [diff] [blame] | 183 | auto local_audio_thread(std::move(audio_thread_)); |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 184 | CallWhileUnlocked(base::Bind(&base::DelegateSimpleThread::Join, |
bbudge | 338f969 | 2015-09-10 00:58:15 | [diff] [blame] | 185 | base::Unretained(local_audio_thread.get()))); |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 186 | } |
[email protected] | 0e8d17b1 | 2012-05-15 21:32:25 | [diff] [blame] | 187 | } |
| 188 | } |
| 189 | |
[email protected] | a4a01e4 | 2014-04-21 10:36:21 | [diff] [blame] | 190 | // static |
| 191 | bool PPB_Audio_Shared::IsThreadFunctionReady() { |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 192 | if (!g_nacl_mode) |
| 193 | return true; |
| 194 | |
| 195 | return (g_thread_functions.thread_create != NULL && |
| 196 | g_thread_functions.thread_join != NULL); |
[email protected] | a4a01e4 | 2014-04-21 10:36:21 | [diff] [blame] | 197 | } |
| 198 | |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 199 | // static |
| 200 | void PPB_Audio_Shared::SetNaClMode() { |
| 201 | g_nacl_mode = true; |
| 202 | } |
| 203 | |
[email protected] | 420ade4 | 2012-07-25 21:47:31 | [diff] [blame] | 204 | // static |
| 205 | void PPB_Audio_Shared::SetThreadFunctions( |
| 206 | const struct PP_ThreadFunctions* functions) { |
[email protected] | d40ece1 | 2014-04-23 16:11:39 | [diff] [blame] | 207 | DCHECK(g_nacl_mode); |
| 208 | g_thread_functions = *functions; |
[email protected] | 420ade4 | 2012-07-25 21:47:31 | [diff] [blame] | 209 | } |
| 210 | |
| 211 | // static |
| 212 | void PPB_Audio_Shared::CallRun(void* self) { |
| 213 | PPB_Audio_Shared* audio = static_cast<PPB_Audio_Shared*>(self); |
| 214 | audio->Run(); |
| 215 | } |
[email protected] | 420ade4 | 2012-07-25 21:47:31 | [diff] [blame] | 216 | |
[email protected] | 9a57839 | 2011-12-07 18:59:27 | [diff] [blame] | 217 | void PPB_Audio_Shared::Run() { |
mikhail.pozdnyakov | a6a331a | 2016-12-01 12:40:54 | [diff] [blame] | 218 | int control_signal = 0; |
| 219 | while (sizeof(control_signal) == |
| 220 | socket_->Receive(&control_signal, sizeof(control_signal))) { |
[email protected] | 1caf0aa | 2013-10-31 07:58:02 | [diff] [blame] | 221 | // |buffer_index_| must track the number of Receive() calls. See the Send() |
| 222 | // call below for why this is important. |
| 223 | ++buffer_index_; |
mikhail.pozdnyakov | a6a331a | 2016-12-01 12:40:54 | [diff] [blame] | 224 | if (control_signal < 0) |
[email protected] | 1caf0aa | 2013-10-31 07:58:02 | [diff] [blame] | 225 | break; |
| 226 | |
[email protected] | 386778b6 | 2014-06-03 01:05:09 | [diff] [blame] | 227 | { |
| 228 | TRACE_EVENT0("audio", "PPB_Audio_Shared::FireRenderCallback"); |
mikhail.pozdnyakov | a6a331a | 2016-12-01 12:40:54 | [diff] [blame] | 229 | media::AudioOutputBuffer* buffer = |
Alexandr Ilin | 20f2841c | 2018-06-01 11:56:18 | [diff] [blame] | 230 | reinterpret_cast<media::AudioOutputBuffer*>(shared_memory_.memory()); |
mikhail.pozdnyakov | a6a331a | 2016-12-01 12:40:54 | [diff] [blame] | 231 | base::TimeDelta delay = |
Lizhi Fan | 058bf2f | 2018-03-26 14:56:42 | [diff] [blame] | 232 | base::TimeDelta::FromMicroseconds(buffer->params.delay_us); |
mikhail.pozdnyakov | a6a331a | 2016-12-01 12:40:54 | [diff] [blame] | 233 | |
| 234 | callback_.Run(client_buffer_.get(), client_buffer_size_bytes_, |
| 235 | delay.InSecondsF(), user_data_); |
[email protected] | 386778b6 | 2014-06-03 01:05:09 | [diff] [blame] | 236 | } |
[email protected] | dfc48e4c | 2012-09-05 13:39:21 | [diff] [blame] | 237 | |
[email protected] | 1caf0aa | 2013-10-31 07:58:02 | [diff] [blame] | 238 | // Deinterleave the audio data into the shared memory as floats. |
Raul Tambre | b1da244 | 2019-04-07 18:18:03 | [diff] [blame] | 239 | static_assert(kBitsPerAudioOutputSample == 16, |
| 240 | "FromInterleaved expects 2 bytes."); |
| 241 | audio_bus_->FromInterleaved<media::SignedInt16SampleTypeTraits>( |
| 242 | reinterpret_cast<int16_t*>(client_buffer_.get()), audio_bus_->frames()); |
[email protected] | 3e3715e | 2012-04-18 05:50:15 | [diff] [blame] | 243 | |
[email protected] | 1caf0aa | 2013-10-31 07:58:02 | [diff] [blame] | 244 | // Let the other end know which buffer we just filled. The buffer index is |
| 245 | // used to ensure the other end is getting the buffer it expects. For more |
| 246 | // details on how this works see AudioSyncReader::WaitUntilDataIsReady(). |
| 247 | size_t bytes_sent = socket_->Send(&buffer_index_, sizeof(buffer_index_)); |
| 248 | if (bytes_sent != sizeof(buffer_index_)) |
[email protected] | 078c07bd | 2013-10-23 21:33:46 | [diff] [blame] | 249 | break; |
[email protected] | 5d84d01 | 2010-12-02 17:17:21 | [diff] [blame] | 250 | } |
| 251 | } |
| 252 | |
[email protected] | 55cdf605 | 2011-05-13 19:22:53 | [diff] [blame] | 253 | } // namespace ppapi |