| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 */ | 27 */ |
| 28 | 28 |
| 29 #include "platform/audio/AudioDestination.h" | 29 #include "platform/audio/AudioDestination.h" |
| 30 | 30 |
| 31 #include <memory> | 31 #include <memory> |
| 32 #include "platform/CrossThreadFunctional.h" | 32 #include "platform/CrossThreadFunctional.h" |
| 33 #include "platform/Histogram.h" | 33 #include "platform/Histogram.h" |
| 34 #include "platform/RuntimeEnabledFeatures.h" |
| 34 #include "platform/WebTaskRunner.h" | 35 #include "platform/WebTaskRunner.h" |
| 35 #include "platform/audio/AudioUtilities.h" | 36 #include "platform/audio/AudioUtilities.h" |
| 36 #include "platform/audio/PushPullFIFO.h" | 37 #include "platform/audio/PushPullFIFO.h" |
| 37 #include "platform/instrumentation/tracing/TraceEvent.h" | 38 #include "platform/instrumentation/tracing/TraceEvent.h" |
| 38 #include "platform/weborigin/SecurityOrigin.h" | 39 #include "platform/weborigin/SecurityOrigin.h" |
| 39 #include "platform/wtf/PtrUtil.h" | 40 #include "platform/wtf/PtrUtil.h" |
| 40 #include "public/platform/Platform.h" | 41 #include "public/platform/Platform.h" |
| 41 #include "public/platform/WebAudioLatencyHint.h" | 42 #include "public/platform/WebAudioLatencyHint.h" |
| 42 #include "public/platform/WebSecurityOrigin.h" | 43 #include "public/platform/WebSecurityOrigin.h" |
| 43 #include "public/platform/WebThread.h" | 44 #include "public/platform/WebThread.h" |
| (...skipping 25 matching lines...) Expand all Loading... |
| 69 : number_of_output_channels_(number_of_output_channels), | 70 : number_of_output_channels_(number_of_output_channels), |
| 70 is_playing_(false), | 71 is_playing_(false), |
| 71 fifo_(WTF::WrapUnique( | 72 fifo_(WTF::WrapUnique( |
| 72 new PushPullFIFO(number_of_output_channels, kFIFOSize))), | 73 new PushPullFIFO(number_of_output_channels, kFIFOSize))), |
| 73 output_bus_(AudioBus::Create(number_of_output_channels, | 74 output_bus_(AudioBus::Create(number_of_output_channels, |
| 74 AudioUtilities::kRenderQuantumFrames, | 75 AudioUtilities::kRenderQuantumFrames, |
| 75 false)), | 76 false)), |
| 76 render_bus_(AudioBus::Create(number_of_output_channels, | 77 render_bus_(AudioBus::Create(number_of_output_channels, |
| 77 AudioUtilities::kRenderQuantumFrames)), | 78 AudioUtilities::kRenderQuantumFrames)), |
| 78 callback_(callback), | 79 callback_(callback), |
| 79 frames_elapsed_(0) { | 80 frames_elapsed_(0), |
| 81 is_audio_worklet_enabled(RuntimeEnabledFeatures::audioWorkletEnabled()) { |
| 80 // Create WebAudioDevice. blink::WebAudioDevice is designed to support the | 82 // Create WebAudioDevice. blink::WebAudioDevice is designed to support the |
| 81 // local input (e.g. loopback from OS audio system), but Chromium's media | 83 // local input (e.g. loopback from OS audio system), but Chromium's media |
| 82 // renderer does not support it currently. Thus, we use zero for the number | 84 // renderer does not support it currently. Thus, we use zero for the number |
| 83 // of input channels. | 85 // of input channels. |
| 84 web_audio_device_ = Platform::Current()->CreateAudioDevice( | 86 web_audio_device_ = Platform::Current()->CreateAudioDevice( |
| 85 0, number_of_output_channels, latency_hint, this, String(), | 87 0, number_of_output_channels, latency_hint, this, String(), |
| 86 std::move(security_origin)); | 88 std::move(security_origin)); |
| 87 DCHECK(web_audio_device_); | 89 DCHECK(web_audio_device_); |
| 88 | 90 |
| 89 callback_buffer_size_ = web_audio_device_->FramesPerBuffer(); | 91 callback_buffer_size_ = web_audio_device_->FramesPerBuffer(); |
| 90 if (!CheckBufferSize()) { | 92 if (!CheckBufferSize()) { |
| 91 NOTREACHED(); | 93 NOTREACHED(); |
| 92 } | 94 } |
| 95 |
| 96 LOG(INFO) << "AudioDestination::IsAudioWorkletEnabled() = " |
| 97 << IsAudioWorkletEnabled(); |
| 93 } | 98 } |
| 94 | 99 |
| 95 AudioDestination::~AudioDestination() { | 100 AudioDestination::~AudioDestination() { |
| 96 Stop(); | 101 Stop(); |
| 97 } | 102 } |
| 98 | 103 |
| 99 void AudioDestination::Render(const WebVector<float*>& destination_data, | 104 void AudioDestination::Render(const WebVector<float*>& destination_data, |
| 100 size_t number_of_frames, | 105 size_t number_of_frames, |
| 101 double delay, | 106 double delay, |
| 102 double delay_timestamp, | 107 double delay_timestamp, |
| 103 size_t prior_frames_skipped) { | 108 size_t prior_frames_skipped) { |
| 104 TRACE_EVENT1("webaudio", "AudioDestination::Render", | 109 TRACE_EVENT1("webaudio", "AudioDestination::Render", |
| 105 "callback_buffer_size", number_of_frames); | 110 "callback_buffer_size", number_of_frames); |
| 106 | 111 |
| 107 // This method is called by AudioDeviceThread. | 112 DCHECK(!IsMainThread()); |
| 108 DCHECK(!IsRenderingThread()); | |
| 109 | 113 |
| 110 CHECK_EQ(destination_data.size(), number_of_output_channels_); | 114 CHECK_EQ(destination_data.size(), number_of_output_channels_); |
| 111 CHECK_EQ(number_of_frames, callback_buffer_size_); | 115 CHECK_EQ(number_of_frames, callback_buffer_size_); |
| 112 | 116 |
| 113 // Note that this method is called by AudioDeviceThread. If FIFO is not ready, | 117 // Note that this method is called by AudioDeviceThread. If FIFO is not ready, |
| 114 // or the requested render size is greater than FIFO size return here. | 118 // or the requested render size is greater than FIFO size return here. |
| 115 // (crbug.com/692423) | 119 // (crbug.com/692423) |
| 116 if (!fifo_ || fifo_->length() < number_of_frames) | 120 if (!fifo_ || fifo_->length() < number_of_frames) |
| 117 return; | 121 return; |
| 118 | 122 |
| 119 // Associate the destination data array with the output bus then fill the | 123 // Associate the destination data array with the output bus then fill the |
| 120 // FIFO. | 124 // FIFO. |
| 121 for (unsigned i = 0; i < number_of_output_channels_; ++i) | 125 for (unsigned i = 0; i < number_of_output_channels_; ++i) |
| 122 output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames); | 126 output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames); |
| 123 | 127 |
| 124 size_t frames_to_render = fifo_->Pull(output_bus_.Get(), number_of_frames); | 128 size_t frames_to_render = fifo_->Pull(output_bus_.Get(), number_of_frames); |
| 125 | 129 |
| 126 // TODO(hongchan): this check might be redundant, so consider removing later. | 130 // If AudioWorklet is enabled, use dual thread rendering. |
| 127 if (frames_to_render != 0 && rendering_thread_) { | 131 if (IsAudioWorkletEnabled()) { |
| 128 rendering_thread_->GetWebTaskRunner()->PostTask( | 132 // TODO(hongchan): this check might be redundant, so consider removing |
| 129 BLINK_FROM_HERE, | 133 // later. |
| 130 CrossThreadBind(&AudioDestination::RequestRenderOnWebThread, | 134 if (frames_to_render != 0 && rendering_thread_) { |
| 131 CrossThreadUnretained(this), number_of_frames, | 135 rendering_thread_->GetWebTaskRunner()->PostTask( |
| 132 frames_to_render, delay, delay_timestamp, | 136 BLINK_FROM_HERE, |
| 133 prior_frames_skipped)); | 137 CrossThreadBind(&AudioDestination::RequestRender, |
| 138 CrossThreadUnretained(this), number_of_frames, |
| 139 frames_to_render, delay, delay_timestamp, |
| 140 prior_frames_skipped)); |
| 141 } |
| 142 } else { |
| 143 RequestRender(number_of_frames, frames_to_render, delay, delay_timestamp, |
| 144 prior_frames_skipped); |
| 134 } | 145 } |
| 135 } | 146 } |
| 136 | 147 |
| 137 void AudioDestination::RequestRenderOnWebThread(size_t frames_requested, | 148 void AudioDestination::RequestRender(size_t frames_requested, |
| 138 size_t frames_to_render, | 149 size_t frames_to_render, |
| 139 double delay, | 150 double delay, |
| 140 double delay_timestamp, | 151 double delay_timestamp, |
| 141 size_t prior_frames_skipped) { | 152 size_t prior_frames_skipped) { |
| 142 TRACE_EVENT1("webaudio", "AudioDestination::RequestRenderOnWebThread", | 153 TRACE_EVENT1("webaudio", "AudioDestination::RequestRender", |
| 143 "frames_to_render", frames_to_render); | 154 "frames_to_render", frames_to_render); |
| 144 | 155 |
| 145 // This method is called by WebThread. | |
| 146 DCHECK(IsRenderingThread()); | |
| 147 | |
| 148 frames_elapsed_ -= std::min(frames_elapsed_, prior_frames_skipped); | 156 frames_elapsed_ -= std::min(frames_elapsed_, prior_frames_skipped); |
| 149 AudioIOPosition output_position; | 157 AudioIOPosition output_position; |
| 150 output_position.position = | 158 output_position.position = |
| 151 frames_elapsed_ / static_cast<double>(web_audio_device_->SampleRate()) - | 159 frames_elapsed_ / static_cast<double>(web_audio_device_->SampleRate()) - |
| 152 delay; | 160 delay; |
| 153 output_position.timestamp = delay_timestamp; | 161 output_position.timestamp = delay_timestamp; |
| 154 base::TimeTicks received_timestamp = base::TimeTicks::Now(); | 162 base::TimeTicks received_timestamp = base::TimeTicks::Now(); |
| 155 | 163 |
| 156 for (size_t pushed_frames = 0; pushed_frames < frames_to_render; | 164 for (size_t pushed_frames = 0; pushed_frames < frames_to_render; |
| 157 pushed_frames += AudioUtilities::kRenderQuantumFrames) { | 165 pushed_frames += AudioUtilities::kRenderQuantumFrames) { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 177 | 185 |
| 178 frames_elapsed_ += frames_requested; | 186 frames_elapsed_ += frames_requested; |
| 179 } | 187 } |
| 180 | 188 |
| 181 void AudioDestination::Start() { | 189 void AudioDestination::Start() { |
| 182 DCHECK(IsMainThread()); | 190 DCHECK(IsMainThread()); |
| 183 | 191 |
| 184 // Start the "audio device" after the rendering thread is ready. | 192 // Start the "audio device" after the rendering thread is ready. |
| 185 if (web_audio_device_ && !is_playing_) { | 193 if (web_audio_device_ && !is_playing_) { |
| 186 TRACE_EVENT0("webaudio", "AudioDestination::Start"); | 194 TRACE_EVENT0("webaudio", "AudioDestination::Start"); |
| 187 rendering_thread_ = | 195 |
| 188 Platform::Current()->CreateThread("WebAudio Rendering Thread"); | 196 if (IsAudioWorkletEnabled()) { |
| 197 rendering_thread_ = |
| 198 Platform::Current()->CreateThread("WebAudio Rendering Thread"); |
| 199 } |
| 200 |
| 189 web_audio_device_->Start(); | 201 web_audio_device_->Start(); |
| 190 is_playing_ = true; | 202 is_playing_ = true; |
| 191 } | 203 } |
| 192 } | 204 } |
| 193 | 205 |
| 194 void AudioDestination::Stop() { | 206 void AudioDestination::Stop() { |
| 195 DCHECK(IsMainThread()); | 207 DCHECK(IsMainThread()); |
| 196 | 208 |
| 197 // This assumes stopping the "audio device" is synchronous and dumping the | 209 // This assumes stopping the "audio device" is synchronous and dumping the |
| 198 // rendering thread is safe after that. | 210 // rendering thread is safe after that. |
| 199 if (web_audio_device_ && is_playing_) { | 211 if (web_audio_device_ && is_playing_) { |
| 200 TRACE_EVENT0("webaudio", "AudioDestination::Stop"); | 212 TRACE_EVENT0("webaudio", "AudioDestination::Stop"); |
| 201 web_audio_device_->Stop(); | 213 web_audio_device_->Stop(); |
| 202 rendering_thread_.reset(); | 214 |
| 215 if (IsAudioWorkletEnabled()) { |
| 216 rendering_thread_.reset(); |
| 217 } |
| 218 |
| 203 is_playing_ = false; | 219 is_playing_ = false; |
| 204 } | 220 } |
| 205 } | 221 } |
| 206 | 222 |
| 207 size_t AudioDestination::CallbackBufferSize() const { | 223 size_t AudioDestination::CallbackBufferSize() const { |
| 208 DCHECK(IsMainThread()); | 224 DCHECK(IsMainThread()); |
| 209 return callback_buffer_size_; | 225 return callback_buffer_size_; |
| 210 } | 226 } |
| 211 | 227 |
| 212 bool AudioDestination::IsPlaying() { | 228 bool AudioDestination::IsPlaying() { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 247 hardware_buffer_size_histogram.Sample(HardwareBufferSize()); | 263 hardware_buffer_size_histogram.Sample(HardwareBufferSize()); |
| 248 callback_buffer_size_histogram.Sample(callback_buffer_size_); | 264 callback_buffer_size_histogram.Sample(callback_buffer_size_); |
| 249 | 265 |
| 250 // Check if the requested buffer size is too large. | 266 // Check if the requested buffer size is too large. |
| 251 bool is_buffer_size_valid = | 267 bool is_buffer_size_valid = |
| 252 callback_buffer_size_ + AudioUtilities::kRenderQuantumFrames <= kFIFOSize; | 268 callback_buffer_size_ + AudioUtilities::kRenderQuantumFrames <= kFIFOSize; |
| 253 DCHECK(is_buffer_size_valid); | 269 DCHECK(is_buffer_size_valid); |
| 254 return is_buffer_size_valid; | 270 return is_buffer_size_valid; |
| 255 } | 271 } |
| 256 | 272 |
| 257 bool AudioDestination::IsRenderingThread() { | |
| 258 return static_cast<ThreadIdentifier>(rendering_thread_->ThreadId()) == | |
| 259 CurrentThread(); | |
| 260 } | |
| 261 | |
| 262 } // namespace blink | 273 } // namespace blink |
| OLD | NEW |