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

Side by Side Diff: chrome/renderer/media/cast_rtp_stream.cc

Issue 187493002: Cast: Resample input audio to the configured sample rate (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fixed perf Created 6 years, 9 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 | Annotate | Revision Log
« no previous file with comments | « chrome/renderer/media/DEPS ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "chrome/renderer/media/cast_rtp_stream.h" 5 #include "chrome/renderer/media/cast_rtp_stream.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/memory/weak_ptr.h" 9 #include "base/memory/weak_ptr.h"
10 #include "chrome/renderer/media/cast_session.h" 10 #include "chrome/renderer/media/cast_session.h"
11 #include "chrome/renderer/media/cast_udp_transport.h" 11 #include "chrome/renderer/media/cast_udp_transport.h"
12 #include "content/public/renderer/media_stream_audio_sink.h" 12 #include "content/public/renderer/media_stream_audio_sink.h"
13 #include "content/public/renderer/media_stream_video_sink.h" 13 #include "content/public/renderer/media_stream_video_sink.h"
14 #include "content/public/renderer/render_thread.h" 14 #include "content/public/renderer/render_thread.h"
15 #include "media/audio/audio_parameters.h"
15 #include "media/base/audio_bus.h" 16 #include "media/base/audio_bus.h"
17 #include "media/base/audio_fifo.h"
16 #include "media/base/bind_to_current_loop.h" 18 #include "media/base/bind_to_current_loop.h"
19 #include "media/base/multi_channel_resampler.h"
17 #include "media/cast/cast_config.h" 20 #include "media/cast/cast_config.h"
18 #include "media/cast/cast_defines.h" 21 #include "media/cast/cast_defines.h"
19 #include "media/cast/cast_sender.h" 22 #include "media/cast/cast_sender.h"
20 #include "media/cast/transport/cast_transport_config.h" 23 #include "media/cast/transport/cast_transport_config.h"
21 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h" 24 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
22 25
23 using media::cast::AudioSenderConfig; 26 using media::cast::AudioSenderConfig;
24 using media::cast::VideoSenderConfig; 27 using media::cast::VideoSenderConfig;
25 28
26 namespace { 29 namespace {
30
27 const char kCodecNameOpus[] = "OPUS"; 31 const char kCodecNameOpus[] = "OPUS";
28 const char kCodecNameVp8[] = "VP8"; 32 const char kCodecNameVp8[] = "VP8";
29 33
34 // This constant defines the number of sets of audio data to buffer
35 // in the FIFO. If input audio and output data have different resampling
36 // rates then buffer is necessary to avoid audio glitches.
37 // See CastAudioSink::ResampleData() and CastAudioSink::OnSetFormat()
38 // for more defaults.
39 const int kBufferAudioData = 2;
40
30 CastRtpPayloadParams DefaultOpusPayload() { 41 CastRtpPayloadParams DefaultOpusPayload() {
31 CastRtpPayloadParams payload; 42 CastRtpPayloadParams payload;
32 payload.ssrc = 1; 43 payload.ssrc = 1;
33 payload.feedback_ssrc = 1; 44 payload.feedback_ssrc = 1;
34 payload.payload_type = 127; 45 payload.payload_type = 127;
35 payload.codec_name = kCodecNameOpus; 46 payload.codec_name = kCodecNameOpus;
36 payload.clock_rate = 48000; 47 payload.clock_rate = 48000;
37 payload.channels = 2; 48 payload.channels = 2;
38 payload.min_bitrate = payload.max_bitrate = 49 payload.min_bitrate = payload.max_bitrate =
39 media::cast::kDefaultAudioEncoderBitrate; 50 media::cast::kDefaultAudioEncoderBitrate;
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 // Receives audio data from a MediaStreamTrack. Data is submitted to 178 // Receives audio data from a MediaStreamTrack. Data is submitted to
168 // media::cast::FrameInput. 179 // media::cast::FrameInput.
169 // 180 //
170 // Threading: Audio frames are received on the real-time audio thread. 181 // Threading: Audio frames are received on the real-time audio thread.
171 class CastAudioSink : public base::SupportsWeakPtr<CastAudioSink>, 182 class CastAudioSink : public base::SupportsWeakPtr<CastAudioSink>,
172 public content::MediaStreamAudioSink { 183 public content::MediaStreamAudioSink {
173 public: 184 public:
174 // |track| provides data for this sink. 185 // |track| provides data for this sink.
175 // |error_callback| is called if audio formats don't match. 186 // |error_callback| is called if audio formats don't match.
176 CastAudioSink(const blink::WebMediaStreamTrack& track, 187 CastAudioSink(const blink::WebMediaStreamTrack& track,
177 const CastRtpStream::ErrorCallback& error_callback) 188 const CastRtpStream::ErrorCallback& error_callback,
189 int output_channels,
190 int output_sample_rate)
178 : track_(track), 191 : track_(track),
179 sink_added_(false), 192 sink_added_(false),
180 error_callback_(error_callback), 193 error_callback_(error_callback),
181 weak_factory_(this), 194 weak_factory_(this),
182 render_thread_task_runner_(content::RenderThread::Get() 195 render_thread_task_runner_(content::RenderThread::Get()
183 ->GetMessageLoop() 196 ->GetMessageLoop()
184 ->message_loop_proxy()) {} 197 ->message_loop_proxy()),
198 input_preroll_(0),
199 output_channels_(output_channels),
200 output_sample_rate_(output_sample_rate) {}
185 201
186 virtual ~CastAudioSink() { 202 virtual ~CastAudioSink() {
187 if (sink_added_) 203 if (sink_added_)
188 RemoveFromAudioTrack(this, track_); 204 RemoveFromAudioTrack(this, track_);
189 } 205 }
190 206
191 // Called on real-time audio thread. 207 // Called on real-time audio thread.
192 // content::MediaStreamAudioSink implementation. 208 // content::MediaStreamAudioSink implementation.
193 virtual void OnData(const int16* audio_data, 209 virtual void OnData(const int16* audio_data,
194 int sample_rate, 210 int sample_rate,
195 int number_of_channels, 211 int number_of_channels,
196 int number_of_frames) OVERRIDE { 212 int number_of_frames) OVERRIDE {
197 scoped_ptr<media::AudioBus> audio_bus( 213 scoped_ptr<media::AudioBus> input_bus;
198 media::AudioBus::Create(number_of_channels, number_of_frames)); 214 if (resampler_) {
199 audio_bus->FromInterleaved(audio_data, number_of_frames, 2); 215 input_bus = ResampleData(
216 audio_data, sample_rate, number_of_channels, number_of_frames);
217 if (!input_bus)
218 return;
219 } else {
220 input_bus = media::AudioBus::Create(
221 number_of_channels, number_of_frames).Pass();
DaleCurtis 2014/03/07 19:16:42 No .Pass necessary.
222 input_bus->FromInterleaved(
223 audio_data, number_of_frames, number_of_channels);
224 }
200 225
201 // TODO(hclam): Pass in the accurate capture time to have good 226 // TODO(hclam): Pass in the accurate capture time to have good
202 // audio / video sync. 227 // audio / video sync.
203 228
204 // TODO(hclam): We shouldn't hop through the render thread. 229 // TODO(hclam): We shouldn't hop through the render thread.
205 // Bounce the call from the real-time audio thread to the render thread. 230 // Bounce the call from the real-time audio thread to the render thread.
206 // Needed since frame_input_ can be changed runtime by the render thread. 231 // Needed since frame_input_ can be changed runtime by the render thread.
207 media::AudioBus* const audio_bus_ptr = audio_bus.get(); 232 media::AudioBus* const input_bus_ptr = input_bus.get();
208 render_thread_task_runner_->PostTask( 233 render_thread_task_runner_->PostTask(
209 FROM_HERE, 234 FROM_HERE,
210 base::Bind(&CastAudioSink::SendAudio, 235 base::Bind(&CastAudioSink::SendAudio,
211 weak_factory_.GetWeakPtr(), 236 weak_factory_.GetWeakPtr(),
212 audio_bus_ptr, 237 input_bus_ptr,
213 base::TimeTicks::Now(), 238 base::TimeTicks::Now(),
214 base::Bind(&DeleteAudioBus, base::Passed(&audio_bus)))); 239 base::Bind(&DeleteAudioBus, base::Passed(&input_bus))));
215 } 240 }
216 241
217 void SendAudio(const media::AudioBus* audio_bus_ptr, 242 // Return a resampled audio data from input. This is called when the
243 // input sample rate doesn't match the output.
244 // The flow of data is as follows:
245 // |audio_data| ->
246 // AudioFifo |fifo_| ->
247 // MultiChannelResampler |resampler|.
248 //
249 // The resampler pulls data out of the FIFO and resample the data in
250 // frequency domain. It might call |fifo_| for more than once. But no more
251 // than |kBufferAudioData| times. We preroll audio data into the FIFO to
252 // make sure there's enough data for resampling.
253 scoped_ptr<media::AudioBus> ResampleData(
254 const int16* audio_data,
255 int sample_rate,
256 int number_of_channels,
257 int number_of_frames) {
258 DCHECK_EQ(number_of_channels, output_channels_);
259 fifo_input_bus_->FromInterleaved(
260 audio_data, number_of_frames, number_of_channels);
261 fifo_->Push(fifo_input_bus_.get());
262
263 if (input_preroll_ < kBufferAudioData - 1) {
264 ++input_preroll_;
265 return scoped_ptr<media::AudioBus>();
266 }
267
268 scoped_ptr<media::AudioBus> output_bus(
269 media::AudioBus::Create(
270 output_channels_,
271 output_sample_rate_ * fifo_input_bus_->frames() / sample_rate));
DaleCurtis 2014/03/07 19:16:42 This should be fine for the sample rates your usin
272
273 // Resampler will then call ProvideData() below to fetch data from
274 // |input_data_|.
275 resampler_->Resample(output_bus->frames(), output_bus.get());
276 return output_bus.Pass();
277 }
278
279 void SendAudio(const media::AudioBus* audio_bus,
218 const base::TimeTicks& recorded_time, 280 const base::TimeTicks& recorded_time,
219 const base::Closure& done_callback) { 281 const base::Closure& done_callback) {
220 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); 282 DCHECK(render_thread_task_runner_->BelongsToCurrentThread());
221 DCHECK(frame_input_); 283 DCHECK(frame_input_);
222 frame_input_->InsertAudio(audio_bus_ptr, recorded_time, done_callback); 284 frame_input_->InsertAudio(audio_bus, recorded_time, done_callback);
223 } 285 }
224 286
225 // Called on real-time audio thread. 287 // Called on real-time audio thread.
226 virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE { 288 virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE {
227 NOTIMPLEMENTED(); 289 if (params.sample_rate() == output_sample_rate_)
290 return;
291 fifo_.reset(new media::AudioFifo(
292 output_channels_,
293 kBufferAudioData * params.frames_per_buffer()));
294 fifo_input_bus_ = media::AudioBus::Create(
295 params.channels(), params.frames_per_buffer()).Pass();
DaleCurtis 2014/03/07 19:16:42 No need for .Pass().
296 resampler_.reset(new media::MultiChannelResampler(
297 output_channels_,
298 static_cast<double>(params.sample_rate()) / output_sample_rate_,
299 params.frames_per_buffer(),
300 base::Bind(&CastAudioSink::ProvideData, base::Unretained(this))));
228 } 301 }
229 302
230 // See CastVideoSink for details. 303 // See CastVideoSink for details.
231 void AddToTrack(const scoped_refptr<media::cast::FrameInput>& frame_input) { 304 void AddToTrack(const scoped_refptr<media::cast::FrameInput>& frame_input) {
232 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); 305 DCHECK(render_thread_task_runner_->BelongsToCurrentThread());
233 frame_input_ = frame_input; 306 frame_input_ = frame_input;
234 if (!sink_added_) { 307 if (!sink_added_) {
235 AddToAudioTrack(this, track_); 308 AddToAudioTrack(this, track_);
236 sink_added_ = true; 309 sink_added_ = true;
237 } 310 }
238 } 311 }
239 312
313 void ProvideData(int frame_delay, media::AudioBus* output_bus) {
314 fifo_->Consume(output_bus, 0, output_bus->frames());
315 }
316
240 private: 317 private:
241 blink::WebMediaStreamTrack track_; 318 blink::WebMediaStreamTrack track_;
242 scoped_refptr<media::cast::FrameInput> frame_input_; 319 scoped_refptr<media::cast::FrameInput> frame_input_;
243 bool sink_added_; 320 bool sink_added_;
244 CastRtpStream::ErrorCallback error_callback_; 321 CastRtpStream::ErrorCallback error_callback_;
245 base::WeakPtrFactory<CastAudioSink> weak_factory_; 322 base::WeakPtrFactory<CastAudioSink> weak_factory_;
246 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_; 323 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_;
247 324
325 scoped_ptr<media::MultiChannelResampler> resampler_;
326 scoped_ptr<media::AudioFifo> fifo_;
327 scoped_ptr<media::AudioBus> fifo_input_bus_;
328 int input_preroll_;
329 const int output_channels_;
330 const int output_sample_rate_;
331
248 DISALLOW_COPY_AND_ASSIGN(CastAudioSink); 332 DISALLOW_COPY_AND_ASSIGN(CastAudioSink);
249 }; 333 };
250 334
251 CastRtpParams::CastRtpParams(const CastRtpPayloadParams& payload_params) 335 CastRtpParams::CastRtpParams(const CastRtpPayloadParams& payload_params)
252 : payload(payload_params) {} 336 : payload(payload_params) {}
253 337
254 CastCodecSpecificParams::CastCodecSpecificParams() {} 338 CastCodecSpecificParams::CastCodecSpecificParams() {}
255 339
256 CastCodecSpecificParams::~CastCodecSpecificParams() {} 340 CastCodecSpecificParams::~CastCodecSpecificParams() {}
257 341
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
302 const ErrorCallback& error_callback) { 386 const ErrorCallback& error_callback) {
303 stop_callback_ = stop_callback; 387 stop_callback_ = stop_callback;
304 error_callback_ = error_callback; 388 error_callback_ = error_callback;
305 389
306 if (IsAudio()) { 390 if (IsAudio()) {
307 AudioSenderConfig config; 391 AudioSenderConfig config;
308 if (!ToAudioSenderConfig(params, &config)) { 392 if (!ToAudioSenderConfig(params, &config)) {
309 DidEncounterError("Invalid parameters for audio."); 393 DidEncounterError("Invalid parameters for audio.");
310 return; 394 return;
311 } 395 }
396
312 // In case of error we have to go through DidEncounterError() to stop 397 // In case of error we have to go through DidEncounterError() to stop
313 // the streaming after reporting the error. 398 // the streaming after reporting the error.
314 audio_sink_.reset(new CastAudioSink( 399 audio_sink_.reset(new CastAudioSink(
315 track_, 400 track_,
316 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError, 401 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError,
317 weak_factory_.GetWeakPtr())))); 402 weak_factory_.GetWeakPtr())),
403 params.payload.channels,
404 params.payload.clock_rate));
318 cast_session_->StartAudio( 405 cast_session_->StartAudio(
319 config, 406 config,
320 base::Bind(&CastAudioSink::AddToTrack, 407 base::Bind(&CastAudioSink::AddToTrack,
321 audio_sink_->AsWeakPtr())); 408 audio_sink_->AsWeakPtr()));
322 start_callback.Run(); 409 start_callback.Run();
323 } else { 410 } else {
324 VideoSenderConfig config; 411 VideoSenderConfig config;
325 if (!ToVideoSenderConfig(params, &config)) { 412 if (!ToVideoSenderConfig(params, &config)) {
326 DidEncounterError("Invalid parameters for video."); 413 DidEncounterError("Invalid parameters for video.");
327 return; 414 return;
(...skipping 27 matching lines...) Expand all
355 } 442 }
356 443
357 bool CastRtpStream::IsAudio() const { 444 bool CastRtpStream::IsAudio() const {
358 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio; 445 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio;
359 } 446 }
360 447
361 void CastRtpStream::DidEncounterError(const std::string& message) { 448 void CastRtpStream::DidEncounterError(const std::string& message) {
362 error_callback_.Run(message); 449 error_callback_.Run(message);
363 Stop(); 450 Stop();
364 } 451 }
OLDNEW
« no previous file with comments | « chrome/renderer/media/DEPS ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698