Chromium Code Reviews| 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" |
| 16 #include "media/base/bind_to_current_loop.h" | 17 #include "media/base/bind_to_current_loop.h" |
| 18 #include "media/base/multi_channel_resampler.h" | |
| 17 #include "media/cast/cast_config.h" | 19 #include "media/cast/cast_config.h" |
| 18 #include "media/cast/cast_defines.h" | 20 #include "media/cast/cast_defines.h" |
| 19 #include "media/cast/cast_sender.h" | 21 #include "media/cast/cast_sender.h" |
| 20 #include "media/cast/transport/cast_transport_config.h" | 22 #include "media/cast/transport/cast_transport_config.h" |
| 21 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h" | 23 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h" |
| 22 | 24 |
| 23 using media::cast::AudioSenderConfig; | 25 using media::cast::AudioSenderConfig; |
| 24 using media::cast::VideoSenderConfig; | 26 using media::cast::VideoSenderConfig; |
| 25 | 27 |
| 26 namespace { | 28 namespace { |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 // Receives audio data from a MediaStreamTrack. Data is submitted to | 169 // Receives audio data from a MediaStreamTrack. Data is submitted to |
| 168 // media::cast::FrameInput. | 170 // media::cast::FrameInput. |
| 169 // | 171 // |
| 170 // Threading: Audio frames are received on the real-time audio thread. | 172 // Threading: Audio frames are received on the real-time audio thread. |
| 171 class CastAudioSink : public base::SupportsWeakPtr<CastAudioSink>, | 173 class CastAudioSink : public base::SupportsWeakPtr<CastAudioSink>, |
| 172 public content::MediaStreamAudioSink { | 174 public content::MediaStreamAudioSink { |
| 173 public: | 175 public: |
| 174 // |track| provides data for this sink. | 176 // |track| provides data for this sink. |
| 175 // |error_callback| is called if audio formats don't match. | 177 // |error_callback| is called if audio formats don't match. |
| 176 CastAudioSink(const blink::WebMediaStreamTrack& track, | 178 CastAudioSink(const blink::WebMediaStreamTrack& track, |
| 177 const CastRtpStream::ErrorCallback& error_callback) | 179 const CastRtpStream::ErrorCallback& error_callback, |
| 180 int output_channels, | |
| 181 int output_sample_rate) | |
| 178 : track_(track), | 182 : track_(track), |
| 179 sink_added_(false), | 183 sink_added_(false), |
| 180 error_callback_(error_callback), | 184 error_callback_(error_callback), |
| 181 weak_factory_(this), | 185 weak_factory_(this), |
| 182 render_thread_task_runner_(content::RenderThread::Get() | 186 render_thread_task_runner_(content::RenderThread::Get() |
| 183 ->GetMessageLoop() | 187 ->GetMessageLoop() |
| 184 ->message_loop_proxy()) {} | 188 ->message_loop_proxy()), |
| 189 output_channels_(output_channels), | |
| 190 output_sample_rate_(output_sample_rate), | |
| 191 input_data_(NULL), | |
| 192 input_frames_(0), | |
| 193 input_bytes_per_frame_(0) {} | |
| 185 | 194 |
| 186 virtual ~CastAudioSink() { | 195 virtual ~CastAudioSink() { |
| 187 if (sink_added_) | 196 if (sink_added_) |
| 188 RemoveFromAudioTrack(this, track_); | 197 RemoveFromAudioTrack(this, track_); |
| 189 } | 198 } |
| 190 | 199 |
| 191 // Called on real-time audio thread. | 200 // Called on real-time audio thread. |
| 192 // content::MediaStreamAudioSink implementation. | 201 // content::MediaStreamAudioSink implementation. |
| 193 virtual void OnData(const int16* audio_data, | 202 virtual void OnData(const int16* audio_data, |
| 194 int sample_rate, | 203 int sample_rate, |
| 195 int number_of_channels, | 204 int number_of_channels, |
| 196 int number_of_frames) OVERRIDE { | 205 int number_of_frames) OVERRIDE { |
| 197 scoped_ptr<media::AudioBus> audio_bus( | 206 input_data_ = audio_data; |
| 198 media::AudioBus::Create(number_of_channels, number_of_frames)); | 207 input_frames_ = number_of_frames; |
| 199 audio_bus->FromInterleaved(audio_data, number_of_frames, 2); | 208 |
| 209 DCHECK_EQ(number_of_channels, output_channels_); | |
| 210 scoped_ptr<media::AudioBus> output_bus( | |
| 211 media::AudioBus::Create( | |
| 212 output_channels_, | |
| 213 output_sample_rate_ * number_of_frames / sample_rate)); | |
| 214 resampler_->Resample(output_bus->frames(), output_bus.get()); | |
|
miu
2014/03/05 20:59:00
nit: Add a comment that the resampler will invoke
Alpha Left Google
2014/03/06 00:14:56
Done.
| |
| 215 input_data_ = NULL; | |
| 216 input_frames_ = 0; | |
| 200 | 217 |
| 201 // TODO(hclam): Pass in the accurate capture time to have good | 218 // TODO(hclam): Pass in the accurate capture time to have good |
| 202 // audio / video sync. | 219 // audio / video sync. |
| 203 | 220 |
| 204 // TODO(hclam): We shouldn't hop through the render thread. | 221 // TODO(hclam): We shouldn't hop through the render thread. |
| 205 // Bounce the call from the real-time audio thread to the render thread. | 222 // 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. | 223 // Needed since frame_input_ can be changed runtime by the render thread. |
| 207 media::AudioBus* const audio_bus_ptr = audio_bus.get(); | 224 media::AudioBus* const output_bus_ptr = output_bus.get(); |
| 208 render_thread_task_runner_->PostTask( | 225 render_thread_task_runner_->PostTask( |
| 209 FROM_HERE, | 226 FROM_HERE, |
| 210 base::Bind(&CastAudioSink::SendAudio, | 227 base::Bind(&CastAudioSink::SendAudio, |
| 211 weak_factory_.GetWeakPtr(), | 228 weak_factory_.GetWeakPtr(), |
| 212 audio_bus_ptr, | 229 output_bus_ptr, |
| 213 base::TimeTicks::Now(), | 230 base::TimeTicks::Now(), |
| 214 base::Bind(&DeleteAudioBus, base::Passed(&audio_bus)))); | 231 base::Bind(&DeleteAudioBus, base::Passed(&output_bus)))); |
| 215 } | 232 } |
| 216 | 233 |
| 217 void SendAudio(const media::AudioBus* audio_bus_ptr, | 234 void SendAudio(const media::AudioBus* audio_bus, |
| 218 const base::TimeTicks& recorded_time, | 235 const base::TimeTicks& recorded_time, |
| 219 const base::Closure& done_callback) { | 236 const base::Closure& done_callback) { |
| 220 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); | 237 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); |
| 221 DCHECK(frame_input_); | 238 DCHECK(frame_input_); |
| 222 frame_input_->InsertAudio(audio_bus_ptr, recorded_time, done_callback); | 239 frame_input_->InsertAudio(audio_bus, recorded_time, done_callback); |
| 223 } | 240 } |
| 224 | 241 |
| 225 // Called on real-time audio thread. | 242 // Called on real-time audio thread. |
| 226 virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE { | 243 virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE { |
| 227 NOTIMPLEMENTED(); | 244 resampler_.reset(new media::MultiChannelResampler( |
| 245 output_channels_, | |
| 246 static_cast<double>(params.sample_rate()) / output_sample_rate_, | |
| 247 params.frames_per_buffer(), | |
| 248 base::Bind(&CastAudioSink::ProvideData, base::Unretained(this)))); | |
| 249 input_bytes_per_frame_ = params.bits_per_sample() / 8; | |
| 228 } | 250 } |
| 229 | 251 |
| 230 // See CastVideoSink for details. | 252 // See CastVideoSink for details. |
| 231 void AddToTrack(const scoped_refptr<media::cast::FrameInput>& frame_input) { | 253 void AddToTrack(const scoped_refptr<media::cast::FrameInput>& frame_input) { |
| 232 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); | 254 DCHECK(render_thread_task_runner_->BelongsToCurrentThread()); |
| 233 frame_input_ = frame_input; | 255 frame_input_ = frame_input; |
| 234 if (!sink_added_) { | 256 if (!sink_added_) { |
| 235 AddToAudioTrack(this, track_); | 257 AddToAudioTrack(this, track_); |
| 236 sink_added_ = true; | 258 sink_added_ = true; |
| 237 } | 259 } |
| 238 } | 260 } |
| 239 | 261 |
| 262 void ProvideData(int frame_delay, media::AudioBus* output_bus) { | |
| 263 DCHECK_EQ(input_frames_, output_bus->frames()); | |
| 264 output_bus->FromInterleaved(input_data_, input_frames_, | |
| 265 input_bytes_per_frame_); | |
| 266 } | |
| 267 | |
| 240 private: | 268 private: |
| 241 blink::WebMediaStreamTrack track_; | 269 blink::WebMediaStreamTrack track_; |
| 242 scoped_refptr<media::cast::FrameInput> frame_input_; | 270 scoped_refptr<media::cast::FrameInput> frame_input_; |
| 243 bool sink_added_; | 271 bool sink_added_; |
| 244 CastRtpStream::ErrorCallback error_callback_; | 272 CastRtpStream::ErrorCallback error_callback_; |
| 245 base::WeakPtrFactory<CastAudioSink> weak_factory_; | 273 base::WeakPtrFactory<CastAudioSink> weak_factory_; |
| 246 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_; | 274 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_; |
| 247 | 275 |
| 276 scoped_ptr<media::MultiChannelResampler> resampler_; | |
| 277 int output_channels_; | |
|
miu
2014/03/05 20:59:00
nit: The two output_xxx_ members should be const.
Alpha Left Google
2014/03/06 00:14:56
Done.
| |
| 278 int output_sample_rate_; | |
| 279 const void* input_data_; | |
| 280 int input_frames_; | |
| 281 int input_bytes_per_frame_; | |
| 282 | |
| 248 DISALLOW_COPY_AND_ASSIGN(CastAudioSink); | 283 DISALLOW_COPY_AND_ASSIGN(CastAudioSink); |
| 249 }; | 284 }; |
| 250 | 285 |
| 251 CastRtpParams::CastRtpParams(const CastRtpPayloadParams& payload_params) | 286 CastRtpParams::CastRtpParams(const CastRtpPayloadParams& payload_params) |
| 252 : payload(payload_params) {} | 287 : payload(payload_params) {} |
| 253 | 288 |
| 254 CastCodecSpecificParams::CastCodecSpecificParams() {} | 289 CastCodecSpecificParams::CastCodecSpecificParams() {} |
| 255 | 290 |
| 256 CastCodecSpecificParams::~CastCodecSpecificParams() {} | 291 CastCodecSpecificParams::~CastCodecSpecificParams() {} |
| 257 | 292 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 302 const ErrorCallback& error_callback) { | 337 const ErrorCallback& error_callback) { |
| 303 stop_callback_ = stop_callback; | 338 stop_callback_ = stop_callback; |
| 304 error_callback_ = error_callback; | 339 error_callback_ = error_callback; |
| 305 | 340 |
| 306 if (IsAudio()) { | 341 if (IsAudio()) { |
| 307 AudioSenderConfig config; | 342 AudioSenderConfig config; |
| 308 if (!ToAudioSenderConfig(params, &config)) { | 343 if (!ToAudioSenderConfig(params, &config)) { |
| 309 DidEncounterError("Invalid parameters for audio."); | 344 DidEncounterError("Invalid parameters for audio."); |
| 310 return; | 345 return; |
| 311 } | 346 } |
| 347 | |
| 312 // In case of error we have to go through DidEncounterError() to stop | 348 // In case of error we have to go through DidEncounterError() to stop |
| 313 // the streaming after reporting the error. | 349 // the streaming after reporting the error. |
| 314 audio_sink_.reset(new CastAudioSink( | 350 audio_sink_.reset(new CastAudioSink( |
| 315 track_, | 351 track_, |
| 316 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError, | 352 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError, |
| 317 weak_factory_.GetWeakPtr())))); | 353 weak_factory_.GetWeakPtr())), |
| 354 params.payload.channels, | |
| 355 params.payload.clock_rate)); | |
| 318 cast_session_->StartAudio( | 356 cast_session_->StartAudio( |
| 319 config, | 357 config, |
| 320 base::Bind(&CastAudioSink::AddToTrack, | 358 base::Bind(&CastAudioSink::AddToTrack, |
| 321 audio_sink_->AsWeakPtr())); | 359 audio_sink_->AsWeakPtr())); |
| 322 start_callback.Run(); | 360 start_callback.Run(); |
| 323 } else { | 361 } else { |
| 324 VideoSenderConfig config; | 362 VideoSenderConfig config; |
| 325 if (!ToVideoSenderConfig(params, &config)) { | 363 if (!ToVideoSenderConfig(params, &config)) { |
| 326 DidEncounterError("Invalid parameters for video."); | 364 DidEncounterError("Invalid parameters for video."); |
| 327 return; | 365 return; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 355 } | 393 } |
| 356 | 394 |
| 357 bool CastRtpStream::IsAudio() const { | 395 bool CastRtpStream::IsAudio() const { |
| 358 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio; | 396 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio; |
| 359 } | 397 } |
| 360 | 398 |
| 361 void CastRtpStream::DidEncounterError(const std::string& message) { | 399 void CastRtpStream::DidEncounterError(const std::string& message) { |
| 362 error_callback_.Run(message); | 400 error_callback_.Run(message); |
| 363 Stop(); | 401 Stop(); |
| 364 } | 402 } |
| OLD | NEW |