Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(93)

Side by Side Diff: content/renderer/media/track_audio_renderer.cc

Issue 1633423002: MediaStream audio rendering: Bypass audio processing for non-WebRTC cases. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/webrtc_local_audio_renderer.h" 5 #include "content/renderer/media/track_audio_renderer.h"
6
7 #include <utility>
8 6
9 #include "base/location.h" 7 #include "base/location.h"
10 #include "base/logging.h" 8 #include "base/logging.h"
11 #include "base/metrics/histogram.h" 9 #include "base/metrics/histogram.h"
12 #include "base/synchronization/lock.h" 10 #include "base/synchronization/lock.h"
13 #include "base/thread_task_runner_handle.h" 11 #include "base/thread_task_runner_handle.h"
14 #include "base/trace_event/trace_event.h" 12 #include "base/trace_event/trace_event.h"
15 #include "content/renderer/media/audio_device_factory.h" 13 #include "content/renderer/media/audio_device_factory.h"
16 #include "content/renderer/media/media_stream_dispatcher.h" 14 #include "content/renderer/media/media_stream_audio_track.h"
17 #include "content/renderer/media/webrtc_audio_capturer.h"
18 #include "content/renderer/media/webrtc_audio_renderer.h"
19 #include "content/renderer/render_frame_impl.h"
20 #include "media/audio/audio_output_device.h" 15 #include "media/audio/audio_output_device.h"
21 #include "media/base/audio_bus.h" 16 #include "media/base/audio_bus.h"
22 #include "media/base/audio_shifter.h" 17 #include "media/base/audio_shifter.h"
23 18
24 namespace content { 19 namespace content {
25 20
26 namespace { 21 namespace {
27 22
28 enum LocalRendererSinkStates { 23 enum LocalRendererSinkStates {
29 kSinkStarted = 0, 24 kSinkStarted = 0,
30 kSinkNeverStarted, 25 kSinkNeverStarted,
31 kSinkStatesMax // Must always be last! 26 kSinkStatesMax // Must always be last!
32 }; 27 };
33 28
29 // Translates |num_samples_rendered| into a TimeDelta duration and adds it to
30 // |prior_elapsed_render_time|.
31 base::TimeDelta ComputeTotalElapsedRenderTime(
32 base::TimeDelta prior_elapsed_render_time,
33 int64_t num_samples_rendered,
34 int sample_rate) {
35 return prior_elapsed_render_time + base::TimeDelta::FromMicroseconds(
36 num_samples_rendered * base::Time::kMicrosecondsPerSecond / sample_rate);
37 }
38
34 } // namespace 39 } // namespace
35 40
36 // media::AudioRendererSink::RenderCallback implementation 41 // media::AudioRendererSink::RenderCallback implementation
37 int WebRtcLocalAudioRenderer::Render(media::AudioBus* audio_bus, 42 int TrackAudioRenderer::Render(media::AudioBus* audio_bus,
38 uint32_t audio_delay_milliseconds, 43 uint32_t audio_delay_milliseconds,
39 uint32_t frames_skipped) { 44 uint32_t frames_skipped) {
40 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render"); 45 TRACE_EVENT0("audio", "TrackAudioRenderer::Render");
41 base::AutoLock auto_lock(thread_lock_); 46 base::AutoLock auto_lock(thread_lock_);
42 47
43 if (!playing_ || !volume_ || !audio_shifter_) { 48 if (!audio_shifter_) {
44 audio_bus->Zero(); 49 audio_bus->Zero();
45 return 0; 50 return 0;
46 } 51 }
47 52
48 audio_shifter_->Pull( 53 // TODO(miu): Plumbing is needed to determine the actual playout timestamp
49 audio_bus, 54 // of the audio, instead of just snapshotting TimeTicks::Now(), for proper
50 base::TimeTicks::Now() - 55 // audio/video sync. http://crbug.com/335335
51 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds)); 56 const base::TimeTicks playout_time =
52 57 base::TimeTicks::Now() +
58 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds);
59 DVLOG(2) << "Pulling audio out of shifter to be played "
60 << audio_delay_milliseconds << " ms from now.";
61 audio_shifter_->Pull(audio_bus, playout_time);
62 num_samples_rendered_ += audio_bus->frames();
53 return audio_bus->frames(); 63 return audio_bus->frames();
54 } 64 }
55 65
56 void WebRtcLocalAudioRenderer::OnRenderError() { 66 void TrackAudioRenderer::OnRenderError() {
57 NOTIMPLEMENTED(); 67 NOTIMPLEMENTED();
58 } 68 }
59 69
60 // content::MediaStreamAudioSink implementation 70 // content::MediaStreamAudioSink implementation
61 void WebRtcLocalAudioRenderer::OnData(const media::AudioBus& audio_bus, 71 void TrackAudioRenderer::OnData(const media::AudioBus& audio_bus,
62 base::TimeTicks estimated_capture_time) { 72 base::TimeTicks reference_time) {
63 DCHECK(capture_thread_checker_.CalledOnValidThread()); 73 DCHECK(audio_thread_checker_.CalledOnValidThread());
64 DCHECK(!estimated_capture_time.is_null()); 74 DCHECK(!reference_time.is_null());
65 75
66 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData"); 76 TRACE_EVENT0("audio", "TrackAudioRenderer::CaptureData");
67 77
68 base::AutoLock auto_lock(thread_lock_); 78 base::AutoLock auto_lock(thread_lock_);
69 if (!playing_ || !volume_ || !audio_shifter_) 79 if (!audio_shifter_)
70 return; 80 return;
71 81
72 scoped_ptr<media::AudioBus> audio_data( 82 scoped_ptr<media::AudioBus> audio_data(
73 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames())); 83 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()));
74 audio_bus.CopyTo(audio_data.get()); 84 audio_bus.CopyTo(audio_data.get());
75 audio_shifter_->Push(std::move(audio_data), estimated_capture_time); 85 // Note: For remote audio sources, |reference_time| is the local playout time,
76 const base::TimeTicks now = base::TimeTicks::Now(); 86 // the ideal point-in-time at which the first audio sample should be played
77 total_render_time_ += now - last_render_time_; 87 // out in the future. For local sources, |reference_time| is the
78 last_render_time_ = now; 88 // point-in-time at which the first audio sample was captured in the past. In
89 // either case, AudioShifter will auto-detect and do the right thing when
90 // audio is pulled from it.
91 audio_shifter_->Push(std::move(audio_data), reference_time);
79 } 92 }
80 93
81 void WebRtcLocalAudioRenderer::OnSetFormat( 94 void TrackAudioRenderer::OnSetFormat(const media::AudioParameters& params) {
82 const media::AudioParameters& params) { 95 DVLOG(1) << "TrackAudioRenderer::OnSetFormat()";
83 DVLOG(1) << "WebRtcLocalAudioRenderer::OnSetFormat()";
84 // If the source is restarted, we might have changed to another capture 96 // If the source is restarted, we might have changed to another capture
85 // thread. 97 // thread.
86 capture_thread_checker_.DetachFromThread(); 98 audio_thread_checker_.DetachFromThread();
87 DCHECK(capture_thread_checker_.CalledOnValidThread()); 99 DCHECK(audio_thread_checker_.CalledOnValidThread());
100
101 // If the parameters changed, the audio in the AudioShifter is invalid and
102 // should be dropped.
103 {
104 base::AutoLock auto_lock(thread_lock_);
105 if (audio_shifter_ &&
106 (audio_shifter_->sample_rate() != params.sample_rate() ||
107 audio_shifter_->channels() != params.channels())) {
108 HaltAudioFlowWhileLockHeld();
109 }
110 }
88 111
89 // Post a task on the main render thread to reconfigure the |sink_| with the 112 // Post a task on the main render thread to reconfigure the |sink_| with the
90 // new format. 113 // new format.
91 task_runner_->PostTask( 114 task_runner_->PostTask(
92 FROM_HERE, 115 FROM_HERE,
93 base::Bind(&WebRtcLocalAudioRenderer::ReconfigureSink, this, params)); 116 base::Bind(&TrackAudioRenderer::ReconfigureSink, this, params));
94 } 117 }
95 118
96 // WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation. 119 TrackAudioRenderer::TrackAudioRenderer(
97 WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer(
98 const blink::WebMediaStreamTrack& audio_track, 120 const blink::WebMediaStreamTrack& audio_track,
99 int source_render_frame_id, 121 int playout_render_frame_id,
100 int session_id, 122 int session_id,
101 const std::string& device_id, 123 const std::string& device_id,
102 const url::Origin& security_origin) 124 const url::Origin& security_origin)
103 : audio_track_(audio_track), 125 : audio_track_(audio_track),
104 source_render_frame_id_(source_render_frame_id), 126 playout_render_frame_id_(playout_render_frame_id),
105 session_id_(session_id), 127 session_id_(session_id),
106 task_runner_(base::ThreadTaskRunnerHandle::Get()), 128 task_runner_(base::ThreadTaskRunnerHandle::Get()),
129 num_samples_rendered_(0),
107 playing_(false), 130 playing_(false),
108 output_device_id_(device_id), 131 output_device_id_(device_id),
109 security_origin_(security_origin), 132 security_origin_(security_origin),
110 volume_(0.0), 133 volume_(0.0),
111 sink_started_(false) { 134 sink_started_(false) {
112 DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()"; 135 DVLOG(1) << "TrackAudioRenderer::TrackAudioRenderer()";
113 } 136 }
114 137
115 WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() { 138 TrackAudioRenderer::~TrackAudioRenderer() {
116 DCHECK(task_runner_->BelongsToCurrentThread()); 139 DCHECK(task_runner_->BelongsToCurrentThread());
117 DCHECK(!sink_.get()); 140 DCHECK(!sink_.get());
118 DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()"; 141 DVLOG(1) << "TrackAudioRenderer::~TrackAudioRenderer()";
119 } 142 }
120 143
121 void WebRtcLocalAudioRenderer::Start() { 144 void TrackAudioRenderer::Start() {
122 DVLOG(1) << "WebRtcLocalAudioRenderer::Start()"; 145 DVLOG(1) << "TrackAudioRenderer::Start()";
123 DCHECK(task_runner_->BelongsToCurrentThread()); 146 DCHECK(task_runner_->BelongsToCurrentThread());
124 147
125 // We get audio data from |audio_track_|... 148 // We get audio data from |audio_track_|...
126 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_); 149 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_);
127 // ...and |sink_| will get audio data from us. 150 // ...and |sink_| will get audio data from us.
128 DCHECK(!sink_.get()); 151 DCHECK(!sink_.get());
129 sink_ = 152 sink_ =
130 AudioDeviceFactory::NewOutputDevice(source_render_frame_id_, session_id_, 153 AudioDeviceFactory::NewOutputDevice(playout_render_frame_id_, session_id_,
131 output_device_id_, security_origin_); 154 output_device_id_, security_origin_);
155 sink_->SetVolume(volume_);
156
157 playing_ = false;
tommi (sloooow) - chröme 2016/02/01 20:32:24 shouldn't this be assumed to be false already? i.e
miu 2016/02/03 03:48:45 Good point. Done.
132 158
133 base::AutoLock auto_lock(thread_lock_); 159 base::AutoLock auto_lock(thread_lock_);
134 last_render_time_ = base::TimeTicks::Now(); 160 prior_elapsed_render_time_ = base::TimeDelta();
135 playing_ = false; 161 num_samples_rendered_ = 0;
136 } 162 }
137 163
138 void WebRtcLocalAudioRenderer::Stop() { 164 void TrackAudioRenderer::Stop() {
139 DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()"; 165 DVLOG(1) << "TrackAudioRenderer::Stop()";
140 DCHECK(task_runner_->BelongsToCurrentThread()); 166 DCHECK(task_runner_->BelongsToCurrentThread());
141 167
168 playing_ = false;
tommi (sloooow) - chröme 2016/02/01 20:32:24 Call Pause()?
miu 2016/02/03 03:48:45 Done.
169
142 { 170 {
143 base::AutoLock auto_lock(thread_lock_); 171 base::AutoLock auto_lock(thread_lock_);
144 playing_ = false; 172 HaltAudioFlowWhileLockHeld();
145 audio_shifter_.reset();
146 } 173 }
147 174
148 // Stop the output audio stream, i.e, stop asking for data to render. 175 // Stop the output audio stream, i.e, stop asking for data to render.
149 // It is safer to call Stop() on the |sink_| to clean up the resources even 176 // It is safer to call Stop() on the |sink_| to clean up the resources even
150 // when the |sink_| is never started. 177 // when the |sink_| is never started.
151 if (sink_.get()) { 178 if (sink_.get()) {
152 sink_->Stop(); 179 sink_->Stop();
153 sink_ = NULL; 180 sink_ = NULL;
154 } 181 }
155 182
156 if (!sink_started_) { 183 if (!sink_started_ && IsLocalRenderer()) {
157 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", 184 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
158 kSinkNeverStarted, kSinkStatesMax); 185 kSinkNeverStarted, kSinkStatesMax);
159 } 186 }
160 sink_started_ = false; 187 sink_started_ = false;
161 188
162 // Ensure that the capturer stops feeding us with captured audio. 189 // Ensure that the capturer stops feeding us with captured audio.
163 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_); 190 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_);
164 } 191 }
165 192
166 void WebRtcLocalAudioRenderer::Play() { 193 void TrackAudioRenderer::Play() {
167 DVLOG(1) << "WebRtcLocalAudioRenderer::Play()"; 194 DVLOG(1) << "TrackAudioRenderer::Play()";
168 DCHECK(task_runner_->BelongsToCurrentThread()); 195 DCHECK(task_runner_->BelongsToCurrentThread());
169 196
170 if (!sink_.get()) 197 if (!sink_.get())
171 return; 198 return;
172 199
173 { 200 playing_ = true;
174 base::AutoLock auto_lock(thread_lock_);
175 // Resumes rendering by ensuring that WebRtcLocalAudioRenderer::Render()
176 // now reads data from the local FIFO.
177 playing_ = true;
178 last_render_time_ = base::TimeTicks::Now();
179 }
180 201
181 // Note: If volume_ is currently muted, the |sink_| will not be started yet.
182 MaybeStartSink(); 202 MaybeStartSink();
183 } 203 }
184 204
185 void WebRtcLocalAudioRenderer::Pause() { 205 void TrackAudioRenderer::Pause() {
186 DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()"; 206 DVLOG(1) << "TrackAudioRenderer::Pause()";
187 DCHECK(task_runner_->BelongsToCurrentThread()); 207 DCHECK(task_runner_->BelongsToCurrentThread());
188 208
189 if (!sink_.get()) 209 if (!sink_.get())
190 return; 210 return;
191 211
212 playing_ = false;
213
192 base::AutoLock auto_lock(thread_lock_); 214 base::AutoLock auto_lock(thread_lock_);
193 // Temporarily suspends rendering audio. 215 HaltAudioFlowWhileLockHeld();
194 // WebRtcLocalAudioRenderer::Render() will return early during this state
195 // and only zeros will be provided to the active sink.
196 playing_ = false;
197 } 216 }
198 217
199 void WebRtcLocalAudioRenderer::SetVolume(float volume) { 218 void TrackAudioRenderer::SetVolume(float volume) {
200 DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume << ")"; 219 DVLOG(1) << "TrackAudioRenderer::SetVolume(" << volume << ")";
220 DCHECK(task_runner_->BelongsToCurrentThread());
221
222 // Cache the volume.
tommi (sloooow) - chröme 2016/02/01 20:32:24 Maybe document why we do this instead?
miu 2016/02/03 03:48:45 Done.
223 volume_ = volume;
224 if (sink_.get())
225 sink_->SetVolume(volume);
226 }
227
228 media::OutputDevice* TrackAudioRenderer::GetOutputDevice() {
229 DCHECK(task_runner_->BelongsToCurrentThread());
230 return this;
231 }
232
233 base::TimeDelta TrackAudioRenderer::GetCurrentRenderTime() const {
234 DCHECK(task_runner_->BelongsToCurrentThread());
235 base::AutoLock auto_lock(thread_lock_);
236 if (source_params_.IsValid()) {
237 return ComputeTotalElapsedRenderTime(prior_elapsed_render_time_,
238 num_samples_rendered_,
239 source_params_.sample_rate());
240 }
241 return prior_elapsed_render_time_;
242 }
243
244 bool TrackAudioRenderer::IsLocalRenderer() const {
245 DCHECK(task_runner_->BelongsToCurrentThread());
246 MediaStreamAudioTrack* const track =
247 MediaStreamAudioTrack::GetTrack(audio_track_);
248 CHECK(track);
tommi (sloooow) - chröme 2016/02/01 20:32:24 nit: not needed due to the next line
miu 2016/02/03 03:48:45 Cleaned this up: I moved the null-check to the con
249 return track->is_local_track();
250 }
251
252 void TrackAudioRenderer::SwitchOutputDevice(
253 const std::string& device_id,
254 const url::Origin& security_origin,
255 const media::SwitchOutputDeviceCB& callback) {
256 DVLOG(1) << "TrackAudioRenderer::SwitchOutputDevice()";
201 DCHECK(task_runner_->BelongsToCurrentThread()); 257 DCHECK(task_runner_->BelongsToCurrentThread());
202 258
203 { 259 {
204 base::AutoLock auto_lock(thread_lock_); 260 base::AutoLock auto_lock(thread_lock_);
205 // Cache the volume. 261 HaltAudioFlowWhileLockHeld();
206 volume_ = volume;
207 } 262 }
208 263
209 // Lazily start the |sink_| when the local renderer is unmuted during
210 // playing.
211 MaybeStartSink();
212
213 if (sink_.get())
214 sink_->SetVolume(volume);
215 }
216
217 media::OutputDevice* WebRtcLocalAudioRenderer::GetOutputDevice() {
218 DCHECK(task_runner_->BelongsToCurrentThread());
219 return this;
220 }
221
222 base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
223 DCHECK(task_runner_->BelongsToCurrentThread());
224 base::AutoLock auto_lock(thread_lock_);
225 if (!sink_.get())
226 return base::TimeDelta();
227 return total_render_time();
228 }
229
230 bool WebRtcLocalAudioRenderer::IsLocalRenderer() const {
231 return true;
232 }
233
234 void WebRtcLocalAudioRenderer::SwitchOutputDevice(
235 const std::string& device_id,
236 const url::Origin& security_origin,
237 const media::SwitchOutputDeviceCB& callback) {
238 DVLOG(1) << "WebRtcLocalAudioRenderer::SwitchOutputDevice()";
239 DCHECK(task_runner_->BelongsToCurrentThread());
240
241 scoped_refptr<media::AudioOutputDevice> new_sink = 264 scoped_refptr<media::AudioOutputDevice> new_sink =
242 AudioDeviceFactory::NewOutputDevice(source_render_frame_id_, session_id_, 265 AudioDeviceFactory::NewOutputDevice(playout_render_frame_id_, session_id_,
243 device_id, security_origin); 266 device_id, security_origin);
244 if (new_sink->GetDeviceStatus() != media::OUTPUT_DEVICE_STATUS_OK) { 267 if (new_sink->GetDeviceStatus() != media::OUTPUT_DEVICE_STATUS_OK) {
245 callback.Run(new_sink->GetDeviceStatus()); 268 callback.Run(new_sink->GetDeviceStatus());
246 return; 269 return;
247 } 270 }
248 271
249 output_device_id_ = device_id; 272 output_device_id_ = device_id;
250 security_origin_ = security_origin; 273 security_origin_ = security_origin;
251 bool was_sink_started = sink_started_; 274 bool was_sink_started = sink_started_;
252 275
253 if (sink_.get()) 276 if (sink_.get())
254 sink_->Stop(); 277 sink_->Stop();
255 278
256 sink_started_ = false; 279 sink_started_ = false;
257 sink_ = new_sink; 280 sink_ = new_sink;
258 int frames_per_buffer = sink_->GetOutputParameters().frames_per_buffer(); 281 sink_->SetVolume(volume_);
259 sink_params_ = source_params_;
260 sink_params_.set_frames_per_buffer(WebRtcAudioRenderer::GetOptimalBufferSize(
261 source_params_.sample_rate(), frames_per_buffer));
262
263 if (was_sink_started) 282 if (was_sink_started)
264 MaybeStartSink(); 283 MaybeStartSink();
265 284
266 callback.Run(media::OUTPUT_DEVICE_STATUS_OK); 285 callback.Run(media::OUTPUT_DEVICE_STATUS_OK);
267 } 286 }
268 287
269 media::AudioParameters WebRtcLocalAudioRenderer::GetOutputParameters() { 288 media::AudioParameters TrackAudioRenderer::GetOutputParameters() {
270 DCHECK(task_runner_->BelongsToCurrentThread()); 289 DCHECK(task_runner_->BelongsToCurrentThread());
271 if (!sink_.get()) 290 if (!sink_ || !source_params_.IsValid())
272 return media::AudioParameters(); 291 return media::AudioParameters();
273 292
274 return sink_->GetOutputParameters(); 293 // Output parameters consist of the same channel layout and sample rate as the
294 // source, but having the buffer duration preferred by the hardware.
295 const media::AudioParameters& preferred_params = sink_->GetOutputParameters();
296 return media::AudioParameters(
297 preferred_params.format(), source_params_.channel_layout(),
298 source_params_.sample_rate(), source_params_.bits_per_sample(),
299 preferred_params.frames_per_buffer() * source_params_.sample_rate() /
300 preferred_params.sample_rate());
275 } 301 }
276 302
277 media::OutputDeviceStatus WebRtcLocalAudioRenderer::GetDeviceStatus() { 303 media::OutputDeviceStatus TrackAudioRenderer::GetDeviceStatus() {
278 DCHECK(task_runner_->BelongsToCurrentThread()); 304 DCHECK(task_runner_->BelongsToCurrentThread());
279 if (!sink_.get()) 305 if (!sink_.get())
280 return media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL; 306 return media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL;
281 307
282 return sink_->GetDeviceStatus(); 308 return sink_->GetDeviceStatus();
283 } 309 }
284 310
285 void WebRtcLocalAudioRenderer::MaybeStartSink() { 311 void TrackAudioRenderer::MaybeStartSink() {
286 DCHECK(task_runner_->BelongsToCurrentThread()); 312 DCHECK(task_runner_->BelongsToCurrentThread());
287 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink()"; 313 DVLOG(1) << "TrackAudioRenderer::MaybeStartSink()";
288 314
289 if (!sink_.get() || !source_params_.IsValid()) 315 if (!sink_.get() || !source_params_.IsValid() || !playing_)
290 return; 316 return;
291 317
292 { 318 // Re-create the AudioShifter to drop old audio data and reset to a starting
293 // Clear up the old data in the FIFO. 319 // state. MaybeStartSink() is always called in a situation where either the
294 base::AutoLock auto_lock(thread_lock_); 320 // source or sink has changed somehow and so all of AudioShifter's internal
295 audio_shifter_->Flush(); 321 // time-sync state is invalid.
296 } 322 CreateAudioShifter();
297 323
298 if (!sink_params_.IsValid() || !playing_ || !volume_ || sink_started_ || 324 if (sink_started_ ||
299 sink_->GetDeviceStatus() != media::OUTPUT_DEVICE_STATUS_OK) 325 sink_->GetDeviceStatus() != media::OUTPUT_DEVICE_STATUS_OK)
tommi (sloooow) - chröme 2016/02/01 20:32:24 nit: add {}?
miu 2016/02/03 03:48:45 Done.
300 return; 326 return;
301 327
302 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink() -- Starting sink_."; 328 DVLOG(1) << ("TrackAudioRenderer::MaybeStartSink() -- Starting sink. "
303 sink_->Initialize(sink_params_, this); 329 "source_params_={")
330 << source_params_.AsHumanReadableString() << "}, sink parameters={"
331 << GetOutputParameters().AsHumanReadableString() << '}';
332 sink_->Initialize(GetOutputParameters(), this);
304 sink_->Start(); 333 sink_->Start();
305 sink_started_ = true; 334 sink_started_ = true;
306 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", 335 if (IsLocalRenderer()) {
307 kSinkStarted, kSinkStatesMax); 336 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", kSinkStarted,
337 kSinkStatesMax);
338 }
308 } 339 }
309 340
310 void WebRtcLocalAudioRenderer::ReconfigureSink( 341 void TrackAudioRenderer::ReconfigureSink(const media::AudioParameters& params) {
311 const media::AudioParameters& params) {
312 DCHECK(task_runner_->BelongsToCurrentThread()); 342 DCHECK(task_runner_->BelongsToCurrentThread());
313 343
314 DVLOG(1) << "WebRtcLocalAudioRenderer::ReconfigureSink()"; 344 DVLOG(1) << "TrackAudioRenderer::ReconfigureSink()";
315 345
316 if (source_params_.Equals(params)) 346 if (source_params_.Equals(params))
317 return; 347 return;
318
319 // Reset the |source_params_|, |sink_params_| and |loopback_fifo_| to match
320 // the new format.
321
322 source_params_ = params; 348 source_params_ = params;
323 {
324 // Note: The max buffer is fairly large, but will rarely be used.
325 // Cast needs the buffer to hold at least one second of audio.
326 // The clock accuracy is set to 20ms because clock accuracy is
327 // ~15ms on windows.
328 media::AudioShifter* const new_shifter = new media::AudioShifter(
329 base::TimeDelta::FromSeconds(2),
330 base::TimeDelta::FromMilliseconds(20),
331 base::TimeDelta::FromSeconds(20),
332 source_params_.sample_rate(),
333 params.channels());
334
335 base::AutoLock auto_lock(thread_lock_);
336 audio_shifter_.reset(new_shifter);
337 }
338 349
339 if (!sink_.get()) 350 if (!sink_.get())
340 return; // WebRtcLocalAudioRenderer has not yet been started. 351 return; // TrackAudioRenderer has not yet been started.
341 352
342 // Stop |sink_| and re-create a new one to be initialized with different audio 353 // Stop |sink_| and re-create a new one to be initialized with different audio
343 // parameters. Then, invoke MaybeStartSink() to restart everything again. 354 // parameters. Then, invoke MaybeStartSink() to restart everything again.
344 sink_->Stop(); 355 sink_->Stop();
345 sink_started_ = false; 356 sink_started_ = false;
346 sink_ = 357 sink_ =
347 AudioDeviceFactory::NewOutputDevice(source_render_frame_id_, session_id_, 358 AudioDeviceFactory::NewOutputDevice(playout_render_frame_id_, session_id_,
348 output_device_id_, security_origin_); 359 output_device_id_, security_origin_);
349 int frames_per_buffer = sink_->GetOutputParameters().frames_per_buffer(); 360 sink_->SetVolume(volume_);
350 sink_params_ = source_params_;
351 sink_params_.set_frames_per_buffer(WebRtcAudioRenderer::GetOptimalBufferSize(
352 source_params_.sample_rate(), frames_per_buffer));
353 MaybeStartSink(); 361 MaybeStartSink();
354 } 362 }
355 363
364 void TrackAudioRenderer::CreateAudioShifter() {
365 DCHECK(task_runner_->BelongsToCurrentThread());
366
367 // Note 1: The max buffer is fairly large to cover the case where
368 // remotely-sourced audio is delivered well ahead of its scheduled playout
369 // time (e.g., content streaming with a very large end-to-end
370 // latency). However, there is no penalty for making it large in the
371 // low-latency use cases since AudioShifter will discard data as soon as it is
372 // no longer needed.
373 //
374 // Note 2: The clock accuracy is set to 20ms because clock accuracy is
375 // ~15ms on Windows machines without a working high-resolution clock. See
376 // comments in base/time/time.h for details.
377 media::AudioShifter* const new_shifter = new media::AudioShifter(
tommi (sloooow) - chröme 2016/02/01 20:32:24 thanks for keeping the scope of the lock down :)
miu 2016/02/03 03:48:45 Acknowledged.
378 base::TimeDelta::FromSeconds(5), base::TimeDelta::FromMilliseconds(20),
379 base::TimeDelta::FromSeconds(20), source_params_.sample_rate(),
380 source_params_.channels());
381
382 base::AutoLock auto_lock(thread_lock_);
383 audio_shifter_.reset(new_shifter);
384 }
385
386 void TrackAudioRenderer::HaltAudioFlowWhileLockHeld() {
387 thread_lock_.AssertAcquired();
388
389 audio_shifter_.reset();
390
391 if (source_params_.IsValid()) {
392 prior_elapsed_render_time_ =
393 ComputeTotalElapsedRenderTime(prior_elapsed_render_time_,
394 num_samples_rendered_,
395 source_params_.sample_rate());
396 num_samples_rendered_ = 0;
397 }
398 }
399
356 } // namespace content 400 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698