Chromium Code Reviews| 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 11 matching lines...) Expand all Loading... | |
| 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 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/Histogram.h" | 33 #include "platform/Histogram.h" |
| 34 #include "platform/WebTaskRunner.h" | |
| 33 #include "platform/audio/AudioUtilities.h" | 35 #include "platform/audio/AudioUtilities.h" |
| 34 #include "platform/audio/PushPullFIFO.h" | 36 #include "platform/audio/PushPullFIFO.h" |
| 35 #include "platform/weborigin/SecurityOrigin.h" | 37 #include "platform/weborigin/SecurityOrigin.h" |
| 36 #include "platform/wtf/PtrUtil.h" | 38 #include "platform/wtf/PtrUtil.h" |
| 37 #include "public/platform/Platform.h" | 39 #include "public/platform/Platform.h" |
| 38 #include "public/platform/WebAudioLatencyHint.h" | 40 #include "public/platform/WebAudioLatencyHint.h" |
| 39 #include "public/platform/WebSecurityOrigin.h" | 41 #include "public/platform/WebSecurityOrigin.h" |
| 42 #include "public/platform/WebThread.h" | |
| 40 | 43 |
| 41 namespace blink { | 44 namespace blink { |
| 42 | 45 |
| 43 // FIFO Size. | 46 // FIFO Size. |
| 44 // | 47 // |
| 45 // TODO(hongchan): This was estimated based on the largest callback buffer size | 48 // TODO(hongchan): This was estimated based on the largest callback buffer size |
| 46 // that we would ever need. The current UMA stats indicates that this is, in | 49 // that we would ever need. The current UMA stats indicates that this is, in |
| 47 // fact, probably too small. There are Android devices out there with a size of | 50 // fact, probably too small. There are Android devices out there with a size of |
| 48 // 8000 or so. We might need to make this larger. See: crbug.com/670747 | 51 // 8000 or so. We might need to make this larger. See: crbug.com/670747 |
| 49 const size_t kFIFOSize = 8192; | 52 const size_t kFIFOSize = 8192; |
| 50 | 53 |
| 51 std::unique_ptr<AudioDestination> AudioDestination::Create( | 54 std::unique_ptr<AudioDestination> AudioDestination::Create( |
| 52 AudioIOCallback& callback, | 55 AudioIOCallback& callback, |
| 53 unsigned number_of_output_channels, | 56 unsigned number_of_output_channels, |
| 54 const WebAudioLatencyHint& latency_hint, | 57 const WebAudioLatencyHint& latency_hint, |
| 55 PassRefPtr<SecurityOrigin> security_origin) { | 58 PassRefPtr<SecurityOrigin> security_origin) { |
| 56 return WTF::WrapUnique( | 59 return WTF::WrapUnique( |
| 57 new AudioDestination(callback, number_of_output_channels, latency_hint, | 60 new AudioDestination(callback, number_of_output_channels, latency_hint, |
| 58 std::move(security_origin))); | 61 std::move(security_origin))); |
| 59 } | 62 } |
| 60 | 63 |
| 61 AudioDestination::AudioDestination(AudioIOCallback& callback, | 64 AudioDestination::AudioDestination(AudioIOCallback& web_audio_render_callback, |
| 62 unsigned number_of_output_channels, | 65 unsigned number_of_output_channels, |
| 63 const WebAudioLatencyHint& latency_hint, | 66 const WebAudioLatencyHint& latency_hint, |
| 64 PassRefPtr<SecurityOrigin> security_origin) | 67 PassRefPtr<SecurityOrigin> security_origin) |
| 65 : number_of_output_channels_(number_of_output_channels), | 68 : number_of_output_channels_(number_of_output_channels), |
| 66 is_playing_(false), | 69 is_playing_(false), |
| 67 callback_(callback), | 70 // callback_(callback), |
| 68 output_bus_(AudioBus::Create(number_of_output_channels, | 71 output_bus_(AudioBus::Create(number_of_output_channels, |
| 69 AudioUtilities::kRenderQuantumFrames, | 72 AudioUtilities::kRenderQuantumFrames, |
| 70 false)), | 73 false)), |
| 71 render_bus_(AudioBus::Create(number_of_output_channels, | 74 // render_bus_(AudioBus::Create(number_of_output_channels, |
| 72 AudioUtilities::kRenderQuantumFrames)), | 75 // AudioUtilities::kRenderQuantumFrames)), |
| 73 fifo_(WTF::WrapUnique( | |
| 74 new PushPullFIFO(number_of_output_channels, kFIFOSize))), | |
| 75 frames_elapsed_(0) { | 76 frames_elapsed_(0) { |
| 77 // Create a thread |WebThread| for WebAudio graph rendering. | |
| 78 rendering_thread_ = WTF::WrapUnique( | |
| 79 Platform::Current()->CreateThread("WebAudio Rendering Thread")); | |
| 80 | |
| 81 fifo_ = WTF::WrapUnique(new PushPullFIFO(number_of_output_channels, kFIFOSize, | |
| 82 rendering_thread_->ThreadId(), | |
| 83 web_audio_render_callback)); | |
| 84 | |
| 76 // Create WebAudioDevice. blink::WebAudioDevice is designed to support the | 85 // Create WebAudioDevice. blink::WebAudioDevice is designed to support the |
| 77 // local input (e.g. loopback from OS audio system), but Chromium's media | 86 // local input (e.g. loopback from OS audio system), but Chromium's media |
| 78 // renderer does not support it currently. Thus, we use zero for the number | 87 // renderer does not support it currently. Thus, we use zero for the number |
| 79 // of input channels. | 88 // of input channels. |
| 80 web_audio_device_ = WTF::WrapUnique(Platform::Current()->CreateAudioDevice( | 89 web_audio_device_ = WTF::WrapUnique(Platform::Current()->CreateAudioDevice( |
| 81 0, number_of_output_channels, latency_hint, this, String(), | 90 0, number_of_output_channels, latency_hint, this, String(), |
| 82 std::move(security_origin))); | 91 std::move(security_origin))); |
| 83 DCHECK(web_audio_device_); | 92 DCHECK(web_audio_device_); |
| 84 | 93 |
| 85 callback_buffer_size_ = web_audio_device_->FramesPerBuffer(); | 94 callback_buffer_size_ = web_audio_device_->FramesPerBuffer(); |
| 86 if (!CheckBufferSize()) { | 95 if (!CheckBufferSize()) { |
| 87 NOTREACHED(); | 96 NOTREACHED(); |
| 88 } | 97 } |
| 89 } | 98 } |
| 90 | 99 |
| 91 AudioDestination::~AudioDestination() { | 100 AudioDestination::~AudioDestination() { |
| 92 Stop(); | 101 Stop(); |
| 93 } | 102 } |
| 94 | 103 |
| 95 void AudioDestination::Render(const WebVector<float*>& destination_data, | 104 void AudioDestination::Render(const WebVector<float*>& destination_data, |
| 96 size_t number_of_frames, | 105 size_t number_of_frames, |
| 97 double delay, | 106 double delay, |
| 98 double delay_timestamp, | 107 double delay_timestamp, |
| 99 size_t prior_frames_skipped) { | 108 size_t prior_frames_skipped) { |
| 109 // This this method is called by AudioDeviceThread. | |
| 110 DCHECK(!IsRenderingThread()); | |
| 111 | |
| 100 CHECK_EQ(destination_data.size(), number_of_output_channels_); | 112 CHECK_EQ(destination_data.size(), number_of_output_channels_); |
| 101 CHECK_EQ(number_of_frames, callback_buffer_size_); | 113 CHECK_EQ(number_of_frames, callback_buffer_size_); |
| 102 | 114 |
| 103 // Note that this method is called by AudioDeviceThread. If FIFO is not ready, | 115 // Note that this method is called by AudioDeviceThread. If FIFO is not ready, |
| 104 // or the requested render size is greater than FIFO size return here. | 116 // or the requested render size is greater than FIFO size return here. |
| 105 // (crbug.com/692423) | 117 // (crbug.com/692423) |
| 106 if (!fifo_ || fifo_->length() < number_of_frames) | 118 if (!fifo_ || fifo_->length() < number_of_frames) |
|
o1ka
2017/04/13 08:36:53
Are you keeping silent returns?
hongchan
2017/04/14 16:31:48
This inexplicable case happens only on Android. If
| |
| 107 return; | 119 return; |
| 108 | 120 |
| 109 frames_elapsed_ -= std::min(frames_elapsed_, prior_frames_skipped); | |
| 110 double output_position = | |
| 111 frames_elapsed_ / static_cast<double>(web_audio_device_->SampleRate()) - | |
| 112 delay; | |
| 113 output_position_.position = output_position; | |
| 114 output_position_.timestamp = delay_timestamp; | |
| 115 output_position_received_timestamp_ = base::TimeTicks::Now(); | |
| 116 | |
| 117 // Associate the destination data array with the output bus then fill the | 121 // Associate the destination data array with the output bus then fill the |
| 118 // FIFO. | 122 // FIFO. |
| 119 for (unsigned i = 0; i < number_of_output_channels_; ++i) | 123 for (unsigned i = 0; i < number_of_output_channels_; ++i) |
| 120 output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames); | 124 output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames); |
| 121 | 125 |
| 122 // Number of frames to render via WebAudio graph. |framesToRender > 0| means | 126 rendering_thread_->GetWebTaskRunner()->PostTask( |
| 127 BLINK_FROM_HERE, | |
| 128 CrossThreadBind(&AudioDestination::RequestRenderOnWebThread, | |
| 129 CrossThreadUnretained(this), number_of_frames, delay, | |
| 130 delay_timestamp, prior_frames_skipped)); | |
| 131 | |
| 132 fifo_->Pull(output_bus_.Get(), number_of_frames); | |
| 133 } | |
| 134 | |
| 135 void AudioDestination::RequestRenderOnWebThread(size_t number_of_frames, | |
| 136 double delay, | |
| 137 double delay_timestamp, | |
| 138 size_t prior_frames_skipped) { | |
| 139 // This this method is called by WebAudio rendering thread. | |
| 140 DCHECK(IsRenderingThread()); | |
| 141 | |
| 142 frames_elapsed_ -= std::min(frames_elapsed_, prior_frames_skipped); | |
| 143 | |
| 144 AudioIOPosition output_position; | |
| 145 output_position.position = | |
| 146 frames_elapsed_ / static_cast<double>(web_audio_device_->SampleRate()) - | |
| 147 delay; | |
| 148 output_position.timestamp = delay_timestamp; | |
| 149 | |
| 150 fifo_->FillRequestedFrames(number_of_frames, callback_buffer_size_, | |
| 151 output_position); | |
| 152 | |
| 153 // double output_position = | |
| 154 // frames_elapsed_ / static_cast<double>(web_audio_device_->SampleRate()) - | |
| 155 // delay; | |
|
o1ka
2017/04/13 08:36:53
It's really inconvenient to review patches with co
hongchan
2017/04/14 16:31:48
I am terribly sorry about this mistake. :(
| |
| 156 // output_position_.position = output_position; | |
| 157 // output_position_.timestamp = delay_timestamp; | |
| 158 // output_position_received_timestamp_ = base::TimeTicks::Now(); | |
| 159 | |
| 160 // Number of frames to render via WebAudio graph. |frames_to_render > 0| means | |
| 123 // the frames in FIFO is not enough to fulfill the requested frames from the | 161 // the frames in FIFO is not enough to fulfill the requested frames from the |
| 124 // audio device. | 162 // audio device. |
| 125 size_t frames_to_render = number_of_frames > fifo_->FramesAvailable() | 163 // size_t frames_to_render = fifo_->GetFramesToRender(number_of_frames); |
| 126 ? number_of_frames - fifo_->FramesAvailable() | |
| 127 : 0; | |
| 128 | 164 |
| 129 for (size_t pushed_frames = 0; pushed_frames < frames_to_render; | 165 // for (size_t pushed_frames = 0; pushed_frames < frames_to_render; |
| 130 pushed_frames += AudioUtilities::kRenderQuantumFrames) { | 166 // pushed_frames += AudioUtilities::kRenderQuantumFrames) { |
| 131 // If platform buffer is more than two times longer than |framesToProcess| | 167 // // If platform buffer is more than two times longer than |framesToProcess | |
| 132 // we do not want output position to get stuck so we promote it | 168 // // we do not want output position to get stuck so we promote it |
| 133 // using the elapsed time from the moment it was initially obtained. | 169 // // using the elapsed time from the moment it was initially obtained. |
| 134 if (callback_buffer_size_ > AudioUtilities::kRenderQuantumFrames * 2) { | 170 // if (callback_buffer_size_ > AudioUtilities::kRenderQuantumFrames * 2) { |
| 135 double delta = | 171 // double delta = |
| 136 (base::TimeTicks::Now() - output_position_received_timestamp_) | 172 // (base::TimeTicks::Now() - output_position_received_timestamp_) |
| 137 .InSecondsF(); | 173 // .InSecondsF(); |
| 138 output_position_.position += delta; | 174 // output_position_.position += delta; |
| 139 output_position_.timestamp += delta; | 175 // output_position_.timestamp += delta; |
| 140 } | 176 // } |
| 141 | 177 |
| 142 // Some implementations give only rough estimation of |delay| so | 178 // // Some implementations give only rough estimation of |delay| so |
| 143 // we might have negative estimation |outputPosition| value. | 179 // // we might have negative estimation |outputPosition| value. |
| 144 if (output_position_.position < 0.0) | 180 // if (output_position_.position < 0.0) |
| 145 output_position_.position = 0.0; | 181 // output_position_.position = 0.0; |
| 146 | 182 |
| 147 // Process WebAudio graph and push the rendered output to FIFO. | 183 // // Process WebAudio graph and push the rendered output to FIFO. |
| 148 callback_.Render(nullptr, render_bus_.Get(), | 184 // callback_.Render(nullptr, render_bus_.Get(), |
| 149 AudioUtilities::kRenderQuantumFrames, output_position_); | 185 // AudioUtilities::kRenderQuantumFrames, output_position_); |
| 150 fifo_->Push(render_bus_.Get()); | 186 // fifo_->Push(render_bus_.Get()); |
| 151 } | 187 // } |
| 152 | |
| 153 fifo_->Pull(output_bus_.Get(), number_of_frames); | |
| 154 | 188 |
| 155 frames_elapsed_ += number_of_frames; | 189 frames_elapsed_ += number_of_frames; |
| 156 } | 190 } |
| 157 | 191 |
| 158 void AudioDestination::Start() { | 192 void AudioDestination::Start() { |
| 159 if (web_audio_device_ && !is_playing_) { | 193 if (web_audio_device_ && !is_playing_) { |
| 160 web_audio_device_->Start(); | 194 web_audio_device_->Start(); |
| 161 is_playing_ = true; | 195 is_playing_ = true; |
| 162 } | 196 } |
| 163 } | 197 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 197 hardware_buffer_size_histogram.Sample(HardwareBufferSize()); | 231 hardware_buffer_size_histogram.Sample(HardwareBufferSize()); |
| 198 callback_buffer_size_histogram.Sample(callback_buffer_size_); | 232 callback_buffer_size_histogram.Sample(callback_buffer_size_); |
| 199 | 233 |
| 200 // Check if the requested buffer size is too large. | 234 // Check if the requested buffer size is too large. |
| 201 bool is_buffer_size_valid = | 235 bool is_buffer_size_valid = |
| 202 callback_buffer_size_ + AudioUtilities::kRenderQuantumFrames <= kFIFOSize; | 236 callback_buffer_size_ + AudioUtilities::kRenderQuantumFrames <= kFIFOSize; |
| 203 DCHECK(is_buffer_size_valid); | 237 DCHECK(is_buffer_size_valid); |
| 204 return is_buffer_size_valid; | 238 return is_buffer_size_valid; |
| 205 } | 239 } |
| 206 | 240 |
| 241 bool AudioDestination::IsRenderingThread() { | |
| 242 return static_cast<ThreadIdentifier>(rendering_thread_->ThreadId()) == | |
| 243 CurrentThread(); | |
| 244 } | |
| 245 | |
| 207 } // namespace blink | 246 } // namespace blink |
| OLD | NEW |