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

Side by Side Diff: content/renderer/media/webrtc_local_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: Add comment to TrackAudioRenderer header to explain it does not handle remote WebRTC tracks. Created 4 years, 10 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
« no previous file with comments | « content/renderer/media/webrtc_local_audio_renderer.h ('k') | media/base/audio_shifter.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "content/renderer/media/webrtc_local_audio_renderer.h"
6
7 #include <utility>
8
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/synchronization/lock.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/trace_event/trace_event.h"
15 #include "content/renderer/media/audio_device_factory.h"
16 #include "content/renderer/media/media_stream_dispatcher.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"
21 #include "media/base/audio_bus.h"
22 #include "media/base/audio_shifter.h"
23
24 namespace content {
25
26 namespace {
27
28 enum LocalRendererSinkStates {
29 kSinkStarted = 0,
30 kSinkNeverStarted,
31 kSinkStatesMax // Must always be last!
32 };
33
34 } // namespace
35
36 // media::AudioRendererSink::RenderCallback implementation
37 int WebRtcLocalAudioRenderer::Render(media::AudioBus* audio_bus,
38 uint32_t audio_delay_milliseconds,
39 uint32_t frames_skipped) {
40 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render");
41 base::AutoLock auto_lock(thread_lock_);
42
43 if (!playing_ || !volume_ || !audio_shifter_) {
44 audio_bus->Zero();
45 return 0;
46 }
47
48 audio_shifter_->Pull(
49 audio_bus,
50 base::TimeTicks::Now() -
51 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds));
52
53 return audio_bus->frames();
54 }
55
56 void WebRtcLocalAudioRenderer::OnRenderError() {
57 NOTIMPLEMENTED();
58 }
59
60 // content::MediaStreamAudioSink implementation
61 void WebRtcLocalAudioRenderer::OnData(const media::AudioBus& audio_bus,
62 base::TimeTicks estimated_capture_time) {
63 DCHECK(capture_thread_checker_.CalledOnValidThread());
64 DCHECK(!estimated_capture_time.is_null());
65
66 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
67
68 base::AutoLock auto_lock(thread_lock_);
69 if (!playing_ || !volume_ || !audio_shifter_)
70 return;
71
72 scoped_ptr<media::AudioBus> audio_data(
73 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()));
74 audio_bus.CopyTo(audio_data.get());
75 audio_shifter_->Push(std::move(audio_data), estimated_capture_time);
76 const base::TimeTicks now = base::TimeTicks::Now();
77 total_render_time_ += now - last_render_time_;
78 last_render_time_ = now;
79 }
80
81 void WebRtcLocalAudioRenderer::OnSetFormat(
82 const media::AudioParameters& params) {
83 DVLOG(1) << "WebRtcLocalAudioRenderer::OnSetFormat()";
84 // If the source is restarted, we might have changed to another capture
85 // thread.
86 capture_thread_checker_.DetachFromThread();
87 DCHECK(capture_thread_checker_.CalledOnValidThread());
88
89 // Post a task on the main render thread to reconfigure the |sink_| with the
90 // new format.
91 task_runner_->PostTask(
92 FROM_HERE,
93 base::Bind(&WebRtcLocalAudioRenderer::ReconfigureSink, this, params));
94 }
95
96 // WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation.
97 WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer(
98 const blink::WebMediaStreamTrack& audio_track,
99 int source_render_frame_id,
100 int session_id,
101 const std::string& device_id,
102 const url::Origin& security_origin)
103 : audio_track_(audio_track),
104 source_render_frame_id_(source_render_frame_id),
105 session_id_(session_id),
106 task_runner_(base::ThreadTaskRunnerHandle::Get()),
107 playing_(false),
108 output_device_id_(device_id),
109 security_origin_(security_origin),
110 volume_(0.0),
111 sink_started_(false) {
112 DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()";
113 }
114
115 WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() {
116 DCHECK(task_runner_->BelongsToCurrentThread());
117 DCHECK(!sink_.get());
118 DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()";
119 }
120
121 void WebRtcLocalAudioRenderer::Start() {
122 DVLOG(1) << "WebRtcLocalAudioRenderer::Start()";
123 DCHECK(task_runner_->BelongsToCurrentThread());
124
125 // We get audio data from |audio_track_|...
126 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_);
127 // ...and |sink_| will get audio data from us.
128 DCHECK(!sink_.get());
129 sink_ =
130 AudioDeviceFactory::NewOutputDevice(source_render_frame_id_, session_id_,
131 output_device_id_, security_origin_);
132
133 base::AutoLock auto_lock(thread_lock_);
134 last_render_time_ = base::TimeTicks::Now();
135 playing_ = false;
136 }
137
138 void WebRtcLocalAudioRenderer::Stop() {
139 DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
140 DCHECK(task_runner_->BelongsToCurrentThread());
141
142 {
143 base::AutoLock auto_lock(thread_lock_);
144 playing_ = false;
145 audio_shifter_.reset();
146 }
147
148 // 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
150 // when the |sink_| is never started.
151 if (sink_.get()) {
152 sink_->Stop();
153 sink_ = NULL;
154 }
155
156 if (!sink_started_) {
157 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
158 kSinkNeverStarted, kSinkStatesMax);
159 }
160 sink_started_ = false;
161
162 // Ensure that the capturer stops feeding us with captured audio.
163 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_);
164 }
165
166 void WebRtcLocalAudioRenderer::Play() {
167 DVLOG(1) << "WebRtcLocalAudioRenderer::Play()";
168 DCHECK(task_runner_->BelongsToCurrentThread());
169
170 if (!sink_.get())
171 return;
172
173 {
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
181 // Note: If volume_ is currently muted, the |sink_| will not be started yet.
182 MaybeStartSink();
183 }
184
185 void WebRtcLocalAudioRenderer::Pause() {
186 DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()";
187 DCHECK(task_runner_->BelongsToCurrentThread());
188
189 if (!sink_.get())
190 return;
191
192 base::AutoLock auto_lock(thread_lock_);
193 // Temporarily suspends rendering audio.
194 // WebRtcLocalAudioRenderer::Render() will return early during this state
195 // and only zeros will be provided to the active sink.
196 playing_ = false;
197 }
198
199 void WebRtcLocalAudioRenderer::SetVolume(float volume) {
200 DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume << ")";
201 DCHECK(task_runner_->BelongsToCurrentThread());
202
203 {
204 base::AutoLock auto_lock(thread_lock_);
205 // Cache the volume.
206 volume_ = volume;
207 }
208
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 =
242 AudioDeviceFactory::NewOutputDevice(source_render_frame_id_, session_id_,
243 device_id, security_origin);
244 if (new_sink->GetDeviceStatus() != media::OUTPUT_DEVICE_STATUS_OK) {
245 callback.Run(new_sink->GetDeviceStatus());
246 return;
247 }
248
249 output_device_id_ = device_id;
250 security_origin_ = security_origin;
251 bool was_sink_started = sink_started_;
252
253 if (sink_.get())
254 sink_->Stop();
255
256 sink_started_ = false;
257 sink_ = new_sink;
258 int frames_per_buffer = sink_->GetOutputParameters().frames_per_buffer();
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)
264 MaybeStartSink();
265
266 callback.Run(media::OUTPUT_DEVICE_STATUS_OK);
267 }
268
269 media::AudioParameters WebRtcLocalAudioRenderer::GetOutputParameters() {
270 DCHECK(task_runner_->BelongsToCurrentThread());
271 if (!sink_.get())
272 return media::AudioParameters();
273
274 return sink_->GetOutputParameters();
275 }
276
277 media::OutputDeviceStatus WebRtcLocalAudioRenderer::GetDeviceStatus() {
278 DCHECK(task_runner_->BelongsToCurrentThread());
279 if (!sink_.get())
280 return media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL;
281
282 return sink_->GetDeviceStatus();
283 }
284
285 void WebRtcLocalAudioRenderer::MaybeStartSink() {
286 DCHECK(task_runner_->BelongsToCurrentThread());
287 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink()";
288
289 if (!sink_.get() || !source_params_.IsValid())
290 return;
291
292 {
293 // Clear up the old data in the FIFO.
294 base::AutoLock auto_lock(thread_lock_);
295 audio_shifter_->Flush();
296 }
297
298 if (!sink_params_.IsValid() || !playing_ || !volume_ || sink_started_ ||
299 sink_->GetDeviceStatus() != media::OUTPUT_DEVICE_STATUS_OK)
300 return;
301
302 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink() -- Starting sink_.";
303 sink_->Initialize(sink_params_, this);
304 sink_->Start();
305 sink_started_ = true;
306 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
307 kSinkStarted, kSinkStatesMax);
308 }
309
310 void WebRtcLocalAudioRenderer::ReconfigureSink(
311 const media::AudioParameters& params) {
312 DCHECK(task_runner_->BelongsToCurrentThread());
313
314 DVLOG(1) << "WebRtcLocalAudioRenderer::ReconfigureSink()";
315
316 if (source_params_.Equals(params))
317 return;
318
319 // Reset the |source_params_|, |sink_params_| and |loopback_fifo_| to match
320 // the new format.
321
322 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
339 if (!sink_.get())
340 return; // WebRtcLocalAudioRenderer has not yet been started.
341
342 // Stop |sink_| and re-create a new one to be initialized with different audio
343 // parameters. Then, invoke MaybeStartSink() to restart everything again.
344 sink_->Stop();
345 sink_started_ = false;
346 sink_ =
347 AudioDeviceFactory::NewOutputDevice(source_render_frame_id_, session_id_,
348 output_device_id_, security_origin_);
349 int frames_per_buffer = sink_->GetOutputParameters().frames_per_buffer();
350 sink_params_ = source_params_;
351 sink_params_.set_frames_per_buffer(WebRtcAudioRenderer::GetOptimalBufferSize(
352 source_params_.sample_rate(), frames_per_buffer));
353 MaybeStartSink();
354 }
355
356 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/media/webrtc_local_audio_renderer.h ('k') | media/base/audio_shifter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698