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& 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 rendering_thread_(WTF::WrapUnique( |
| 71 Platform::Current()->CreateThread("WebAudio Rendering Thread"))), |
| 72 fifo_(WTF::WrapUnique( |
| 73 new PushPullFIFO(number_of_output_channels, kFIFOSize))), |
68 output_bus_(AudioBus::Create(number_of_output_channels, | 74 output_bus_(AudioBus::Create(number_of_output_channels, |
69 AudioUtilities::kRenderQuantumFrames, | 75 AudioUtilities::kRenderQuantumFrames, |
70 false)), | 76 false)), |
71 render_bus_(AudioBus::Create(number_of_output_channels, | 77 render_bus_(AudioBus::Create(number_of_output_channels, |
72 AudioUtilities::kRenderQuantumFrames)), | 78 AudioUtilities::kRenderQuantumFrames)), |
73 fifo_(WTF::WrapUnique( | 79 callback_(callback), |
74 new PushPullFIFO(number_of_output_channels, kFIFOSize))), | |
75 frames_elapsed_(0) { | 80 frames_elapsed_(0) { |
76 // Create WebAudioDevice. blink::WebAudioDevice is designed to support the | 81 // Create WebAudioDevice. blink::WebAudioDevice is designed to support the |
77 // local input (e.g. loopback from OS audio system), but Chromium's media | 82 // 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 | 83 // renderer does not support it currently. Thus, we use zero for the number |
79 // of input channels. | 84 // of input channels. |
80 web_audio_device_ = WTF::WrapUnique(Platform::Current()->CreateAudioDevice( | 85 web_audio_device_ = WTF::WrapUnique(Platform::Current()->CreateAudioDevice( |
81 0, number_of_output_channels, latency_hint, this, String(), | 86 0, number_of_output_channels, latency_hint, this, String(), |
82 std::move(security_origin))); | 87 std::move(security_origin))); |
83 DCHECK(web_audio_device_); | 88 DCHECK(web_audio_device_); |
84 | 89 |
85 callback_buffer_size_ = web_audio_device_->FramesPerBuffer(); | 90 callback_buffer_size_ = web_audio_device_->FramesPerBuffer(); |
86 if (!CheckBufferSize()) { | 91 if (!CheckBufferSize()) { |
87 NOTREACHED(); | 92 NOTREACHED(); |
88 } | 93 } |
89 } | 94 } |
90 | 95 |
91 AudioDestination::~AudioDestination() { | 96 AudioDestination::~AudioDestination() { |
92 Stop(); | 97 Stop(); |
93 } | 98 } |
94 | 99 |
95 void AudioDestination::Render(const WebVector<float*>& destination_data, | 100 void AudioDestination::Render(const WebVector<float*>& destination_data, |
96 size_t number_of_frames, | 101 size_t number_of_frames, |
97 double delay, | 102 double delay, |
98 double delay_timestamp, | 103 double delay_timestamp, |
99 size_t prior_frames_skipped) { | 104 size_t prior_frames_skipped) { |
| 105 // This method is called by AudioDeviceThread. |
| 106 DCHECK(!IsRenderingThread()); |
| 107 |
100 CHECK_EQ(destination_data.size(), number_of_output_channels_); | 108 CHECK_EQ(destination_data.size(), number_of_output_channels_); |
101 CHECK_EQ(number_of_frames, callback_buffer_size_); | 109 CHECK_EQ(number_of_frames, callback_buffer_size_); |
102 | 110 |
103 // Note that this method is called by AudioDeviceThread. If FIFO is not ready, | 111 // 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. | 112 // or the requested render size is greater than FIFO size return here. |
105 // (crbug.com/692423) | 113 // (crbug.com/692423) |
106 if (!fifo_ || fifo_->length() < number_of_frames) | 114 if (!fifo_ || fifo_->length() < number_of_frames) |
107 return; | 115 return; |
108 | 116 |
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 | 117 // Associate the destination data array with the output bus then fill the |
118 // FIFO. | 118 // FIFO. |
119 for (unsigned i = 0; i < number_of_output_channels_; ++i) | 119 for (unsigned i = 0; i < number_of_output_channels_; ++i) |
120 output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames); | 120 output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames); |
121 | 121 |
122 // Number of frames to render via WebAudio graph. |framesToRender > 0| means | 122 size_t frames_to_render = fifo_->Pull(output_bus_.Get(), number_of_frames); |
123 // the frames in FIFO is not enough to fulfill the requested frames from the | 123 |
124 // audio device. | 124 rendering_thread_->GetWebTaskRunner()->PostTask( |
125 size_t frames_to_render = number_of_frames > fifo_->FramesAvailable() | 125 BLINK_FROM_HERE, |
126 ? number_of_frames - fifo_->FramesAvailable() | 126 CrossThreadBind(&AudioDestination::RequestRenderOnWebThread, |
127 : 0; | 127 CrossThreadUnretained(this), |
| 128 number_of_frames, frames_to_render, |
| 129 delay, delay_timestamp, prior_frames_skipped)); |
| 130 } |
| 131 |
| 132 void AudioDestination::RequestRenderOnWebThread(size_t frames_requested, |
| 133 size_t frames_to_render, |
| 134 double delay, |
| 135 double delay_timestamp, |
| 136 size_t prior_frames_skipped) { |
| 137 // This method is called by WebThread. |
| 138 DCHECK(IsRenderingThread()); |
| 139 |
| 140 frames_elapsed_ -= std::min(frames_elapsed_, prior_frames_skipped); |
| 141 AudioIOPosition output_position; |
| 142 output_position.position = |
| 143 frames_elapsed_ / static_cast<double>(web_audio_device_->SampleRate()) - |
| 144 delay; |
| 145 output_position.timestamp = delay_timestamp; |
| 146 base::TimeTicks received_timestamp = base::TimeTicks::Now(); |
128 | 147 |
129 for (size_t pushed_frames = 0; pushed_frames < frames_to_render; | 148 for (size_t pushed_frames = 0; pushed_frames < frames_to_render; |
130 pushed_frames += AudioUtilities::kRenderQuantumFrames) { | 149 pushed_frames += AudioUtilities::kRenderQuantumFrames) { |
131 // If platform buffer is more than two times longer than |framesToProcess| | 150 // 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 | 151 // 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. | 152 // using the elapsed time from the moment it was initially obtained. |
134 if (callback_buffer_size_ > AudioUtilities::kRenderQuantumFrames * 2) { | 153 if (callback_buffer_size_ > AudioUtilities::kRenderQuantumFrames * 2) { |
135 double delta = | 154 double delta = (base::TimeTicks::Now() - received_timestamp).InSecondsF(); |
136 (base::TimeTicks::Now() - output_position_received_timestamp_) | 155 output_position.position += delta; |
137 .InSecondsF(); | 156 output_position.timestamp += delta; |
138 output_position_.position += delta; | |
139 output_position_.timestamp += delta; | |
140 } | 157 } |
141 | 158 |
142 // Some implementations give only rough estimation of |delay| so | 159 // Some implementations give only rough estimation of |delay| so |
143 // we might have negative estimation |outputPosition| value. | 160 // we might have negative estimation |outputPosition| value. |
144 if (output_position_.position < 0.0) | 161 if (output_position.position < 0.0) |
145 output_position_.position = 0.0; | 162 output_position.position = 0.0; |
146 | 163 |
147 // Process WebAudio graph and push the rendered output to FIFO. | 164 // Process WebAudio graph and push the rendered output to FIFO. |
148 callback_.Render(nullptr, render_bus_.Get(), | 165 callback_.Render(nullptr, render_bus_.Get(), |
149 AudioUtilities::kRenderQuantumFrames, output_position_); | 166 AudioUtilities::kRenderQuantumFrames, output_position); |
150 fifo_->Push(render_bus_.Get()); | 167 fifo_->Push(render_bus_.Get()); |
151 } | 168 } |
152 | 169 |
153 fifo_->Pull(output_bus_.Get(), number_of_frames); | 170 frames_elapsed_ += frames_requested; |
154 | |
155 frames_elapsed_ += number_of_frames; | |
156 } | 171 } |
157 | 172 |
158 void AudioDestination::Start() { | 173 void AudioDestination::Start() { |
159 if (web_audio_device_ && !is_playing_) { | 174 if (web_audio_device_ && !is_playing_) { |
160 web_audio_device_->Start(); | 175 web_audio_device_->Start(); |
161 is_playing_ = true; | 176 is_playing_ = true; |
162 } | 177 } |
163 } | 178 } |
164 | 179 |
165 void AudioDestination::Stop() { | 180 void AudioDestination::Stop() { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
197 hardware_buffer_size_histogram.Sample(HardwareBufferSize()); | 212 hardware_buffer_size_histogram.Sample(HardwareBufferSize()); |
198 callback_buffer_size_histogram.Sample(callback_buffer_size_); | 213 callback_buffer_size_histogram.Sample(callback_buffer_size_); |
199 | 214 |
200 // Check if the requested buffer size is too large. | 215 // Check if the requested buffer size is too large. |
201 bool is_buffer_size_valid = | 216 bool is_buffer_size_valid = |
202 callback_buffer_size_ + AudioUtilities::kRenderQuantumFrames <= kFIFOSize; | 217 callback_buffer_size_ + AudioUtilities::kRenderQuantumFrames <= kFIFOSize; |
203 DCHECK(is_buffer_size_valid); | 218 DCHECK(is_buffer_size_valid); |
204 return is_buffer_size_valid; | 219 return is_buffer_size_valid; |
205 } | 220 } |
206 | 221 |
| 222 bool AudioDestination::IsRenderingThread() { |
| 223 return static_cast<ThreadIdentifier>(rendering_thread_->ThreadId()) == |
| 224 CurrentThread(); |
| 225 } |
| 226 |
207 } // namespace blink | 227 } // namespace blink |
OLD | NEW |