OLD | NEW |
---|---|
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 Loading... | |
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 output_channels_(output_channels), | |
199 output_sample_rate_(output_sample_rate) {} | |
185 | 200 |
186 virtual ~CastAudioSink() { | 201 virtual ~CastAudioSink() { |
187 if (sink_added_) | 202 if (sink_added_) |
188 RemoveFromAudioTrack(this, track_); | 203 RemoveFromAudioTrack(this, track_); |
189 } | 204 } |
190 | 205 |
191 // Called on real-time audio thread. | 206 // Called on real-time audio thread. |
192 // content::MediaStreamAudioSink implementation. | 207 // content::MediaStreamAudioSink implementation. |
193 virtual void OnData(const int16* audio_data, | 208 virtual void OnData(const int16* audio_data, |
194 int sample_rate, | 209 int sample_rate, |
195 int number_of_channels, | 210 int number_of_channels, |
196 int number_of_frames) OVERRIDE { | 211 int number_of_frames) OVERRIDE { |
197 scoped_ptr<media::AudioBus> audio_bus( | 212 scoped_ptr<media::AudioBus> input_bus = |
DaleCurtis
2014/03/07 01:14:03
Don't create this every time, it can be quite expe
Alpha Left Google
2014/03/07 01:34:01
I think the only way is to save on the allocation
DaleCurtis
2014/03/07 01:49:00
How about reflowing this block so that ResampleDat
| |
198 media::AudioBus::Create(number_of_channels, number_of_frames)); | 213 media::AudioBus::Create( |
199 audio_bus->FromInterleaved(audio_data, number_of_frames, 2); | 214 number_of_channels, number_of_frames).Pass(); |
215 input_bus->FromInterleaved( | |
216 audio_data, number_of_frames, number_of_channels); | |
217 | |
218 if (resampler_) { | |
219 DCHECK_EQ(number_of_channels, output_channels_); | |
220 input_bus = ResampleData(input_bus.Pass(), sample_rate); | |
221 if (!input_bus) | |
222 return; | |
223 } | |
200 | 224 |
201 // TODO(hclam): Pass in the accurate capture time to have good | 225 // TODO(hclam): Pass in the accurate capture time to have good |
202 // audio / video sync. | 226 // audio / video sync. |
203 | 227 |
204 // TODO(hclam): We shouldn't hop through the render thread. | 228 // TODO(hclam): We shouldn't hop through the render thread. |
205 // Bounce the call from the real-time audio thread to the render thread. | 229 // 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. | 230 // Needed since frame_input_ can be changed runtime by the render thread. |
207 media::AudioBus* const audio_bus_ptr = audio_bus.get(); | 231 media::AudioBus* const input_bus_ptr = input_bus.get(); |
208 render_thread_task_runner_->PostTask( | 232 render_thread_task_runner_->PostTask( |
209 FROM_HERE, | 233 FROM_HERE, |
210 base::Bind(&CastAudioSink::SendAudio, | 234 base::Bind(&CastAudioSink::SendAudio, |
211 weak_factory_.GetWeakPtr(), | 235 weak_factory_.GetWeakPtr(), |
212 audio_bus_ptr, | 236 input_bus_ptr, |
213 base::TimeTicks::Now(), | 237 base::TimeTicks::Now(), |
214 base::Bind(&DeleteAudioBus, base::Passed(&audio_bus)))); | 238 base::Bind(&DeleteAudioBus, base::Passed(&input_bus)))); |
215 } | 239 } |
216 | 240 |
217 void SendAudio(const media::AudioBus* audio_bus_ptr, | 241 // Return a resampled audio data from input. This is called when the |
242 // input sample rate doesn't match the output. | |
243 // The flow of data is as follows: | |
244 // |audio_data| -> | |
245 // AudioFifo |fifo_| -> | |
246 // MultiChannelResampler |resampler|. | |
247 // | |
248 // The resampler pulls data out of the FIFO and resample the data in | |
249 // frequency domain. It might call |fifo_| for more than once. But no more | |
250 // than |kBufferAudioData| times. That's why we need to ensure |fifo_| has | |
251 // at least |kBufferAudioData| sets of audio data. Each set of audio data is | |
252 // 10ms worth. | |
253 scoped_ptr<media::AudioBus> ResampleData( | |
DaleCurtis
2014/03/07 01:14:03
Seems unnecessary to extract this to a new functio
Alpha Left Google
2014/03/07 01:34:01
It looks cleaner to me to have a separate method f
| |
254 scoped_ptr<media::AudioBus> input_bus, | |
255 int input_sample_rate) { | |
256 // Make sure FIFO is completely full. | |
257 fifo_->Push(input_bus.get()); | |
258 if (fifo_->frames() != fifo_->max_frames()) | |
DaleCurtis
2014/03/07 01:14:03
I think you just want to make sure you don't respo
Alpha Left Google
2014/03/07 01:34:01
I changed it to a preroll.
| |
259 return scoped_ptr<media::AudioBus>(); | |
260 | |
261 scoped_ptr<media::AudioBus> output_bus( | |
DaleCurtis
2014/03/07 01:14:03
Ditto. Just return a bool if there's no data avai
Alpha Left Google
2014/03/07 01:34:01
Not sure I understand the suggestion here.
| |
262 media::AudioBus::Create( | |
263 output_channels_, | |
264 output_sample_rate_ * input_bus->frames() / input_sample_rate)); | |
265 | |
266 // Resampler will then call ProvideData() below to fetch data from | |
267 // |input_data_|. | |
268 resampler_->Resample(output_bus->frames(), output_bus.get()); | |
269 return output_bus.Pass(); | |
270 } | |
271 | |
272 void SendAudio(const media::AudioBus* audio_bus, | |
218 const base::TimeTicks& recorded_time, | 273 const base::TimeTicks& recorded_time, |
219 const base::Closure& done_callback) { | 274 const base::Closure& done_callback) { |
220 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); | 275 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); |
221 DCHECK(frame_input_); | 276 DCHECK(frame_input_); |
222 frame_input_->InsertAudio(audio_bus_ptr, recorded_time, done_callback); | 277 frame_input_->InsertAudio(audio_bus, recorded_time, done_callback); |
223 } | 278 } |
224 | 279 |
225 // Called on real-time audio thread. | 280 // Called on real-time audio thread. |
226 virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE { | 281 virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE { |
227 NOTIMPLEMENTED(); | 282 if (params.sample_rate() == output_sample_rate_) |
DaleCurtis
2014/03/07 01:14:03
When and who calls this? Do you need to worry abou
Alpha Left Google
2014/03/07 01:34:01
This is called once before there audio data arrive
| |
283 return; | |
284 fifo_.reset(new media::AudioFifo( | |
285 output_channels_, | |
286 kBufferAudioData * params.frames_per_buffer())); | |
287 resampler_.reset(new media::MultiChannelResampler( | |
288 output_channels_, | |
289 static_cast<double>(params.sample_rate()) / output_sample_rate_, | |
290 params.frames_per_buffer(), | |
291 base::Bind(&CastAudioSink::ProvideData, base::Unretained(this)))); | |
228 } | 292 } |
229 | 293 |
230 // See CastVideoSink for details. | 294 // See CastVideoSink for details. |
231 void AddToTrack(const scoped_refptr<media::cast::FrameInput>& frame_input) { | 295 void AddToTrack(const scoped_refptr<media::cast::FrameInput>& frame_input) { |
232 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); | 296 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); |
233 frame_input_ = frame_input; | 297 frame_input_ = frame_input; |
234 if (!sink_added_) { | 298 if (!sink_added_) { |
235 AddToAudioTrack(this, track_); | 299 AddToAudioTrack(this, track_); |
236 sink_added_ = true; | 300 sink_added_ = true; |
237 } | 301 } |
238 } | 302 } |
239 | 303 |
304 void ProvideData(int frame_delay, media::AudioBus* output_bus) { | |
305 fifo_->Consume(output_bus, 0, output_bus->frames()); | |
306 } | |
307 | |
240 private: | 308 private: |
241 blink::WebMediaStreamTrack track_; | 309 blink::WebMediaStreamTrack track_; |
242 scoped_refptr<media::cast::FrameInput> frame_input_; | 310 scoped_refptr<media::cast::FrameInput> frame_input_; |
243 bool sink_added_; | 311 bool sink_added_; |
244 CastRtpStream::ErrorCallback error_callback_; | 312 CastRtpStream::ErrorCallback error_callback_; |
245 base::WeakPtrFactory<CastAudioSink> weak_factory_; | 313 base::WeakPtrFactory<CastAudioSink> weak_factory_; |
246 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_; | 314 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_; |
247 | 315 |
316 scoped_ptr<media::MultiChannelResampler> resampler_; | |
317 scoped_ptr<media::AudioFifo> fifo_; | |
318 const int output_channels_; | |
319 const int output_sample_rate_; | |
320 | |
248 DISALLOW_COPY_AND_ASSIGN(CastAudioSink); | 321 DISALLOW_COPY_AND_ASSIGN(CastAudioSink); |
249 }; | 322 }; |
250 | 323 |
251 CastRtpParams::CastRtpParams(const CastRtpPayloadParams& payload_params) | 324 CastRtpParams::CastRtpParams(const CastRtpPayloadParams& payload_params) |
252 : payload(payload_params) {} | 325 : payload(payload_params) {} |
253 | 326 |
254 CastCodecSpecificParams::CastCodecSpecificParams() {} | 327 CastCodecSpecificParams::CastCodecSpecificParams() {} |
255 | 328 |
256 CastCodecSpecificParams::~CastCodecSpecificParams() {} | 329 CastCodecSpecificParams::~CastCodecSpecificParams() {} |
257 | 330 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
302 const ErrorCallback& error_callback) { | 375 const ErrorCallback& error_callback) { |
303 stop_callback_ = stop_callback; | 376 stop_callback_ = stop_callback; |
304 error_callback_ = error_callback; | 377 error_callback_ = error_callback; |
305 | 378 |
306 if (IsAudio()) { | 379 if (IsAudio()) { |
307 AudioSenderConfig config; | 380 AudioSenderConfig config; |
308 if (!ToAudioSenderConfig(params, &config)) { | 381 if (!ToAudioSenderConfig(params, &config)) { |
309 DidEncounterError("Invalid parameters for audio."); | 382 DidEncounterError("Invalid parameters for audio."); |
310 return; | 383 return; |
311 } | 384 } |
385 | |
312 // In case of error we have to go through DidEncounterError() to stop | 386 // In case of error we have to go through DidEncounterError() to stop |
313 // the streaming after reporting the error. | 387 // the streaming after reporting the error. |
314 audio_sink_.reset(new CastAudioSink( | 388 audio_sink_.reset(new CastAudioSink( |
315 track_, | 389 track_, |
316 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError, | 390 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError, |
317 weak_factory_.GetWeakPtr())))); | 391 weak_factory_.GetWeakPtr())), |
392 params.payload.channels, | |
393 params.payload.clock_rate)); | |
318 cast_session_->StartAudio( | 394 cast_session_->StartAudio( |
319 config, | 395 config, |
320 base::Bind(&CastAudioSink::AddToTrack, | 396 base::Bind(&CastAudioSink::AddToTrack, |
321 audio_sink_->AsWeakPtr())); | 397 audio_sink_->AsWeakPtr())); |
322 start_callback.Run(); | 398 start_callback.Run(); |
323 } else { | 399 } else { |
324 VideoSenderConfig config; | 400 VideoSenderConfig config; |
325 if (!ToVideoSenderConfig(params, &config)) { | 401 if (!ToVideoSenderConfig(params, &config)) { |
326 DidEncounterError("Invalid parameters for video."); | 402 DidEncounterError("Invalid parameters for video."); |
327 return; | 403 return; |
(...skipping 27 matching lines...) Expand all Loading... | |
355 } | 431 } |
356 | 432 |
357 bool CastRtpStream::IsAudio() const { | 433 bool CastRtpStream::IsAudio() const { |
358 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio; | 434 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio; |
359 } | 435 } |
360 | 436 |
361 void CastRtpStream::DidEncounterError(const std::string& message) { | 437 void CastRtpStream::DidEncounterError(const std::string& message) { |
362 error_callback_.Run(message); | 438 error_callback_.Run(message); |
363 Stop(); | 439 Stop(); |
364 } | 440 } |
OLD | NEW |