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 3 // found in the LICENSE file. 4 5 #include "chrome/renderer/media/cast_rtp_stream.h" 6 7 #include "base/bind.h" 8 #include "base/debug/trace_event.h" 9 #include "base/logging.h" 10 #include "base/memory/weak_ptr.h" 11 #include "base/sys_info.h" 12 #include "chrome/renderer/media/cast_session.h" 13 #include "chrome/renderer/media/cast_udp_transport.h" 14 #include "content/public/renderer/media_stream_audio_sink.h" 15 #include "content/public/renderer/media_stream_video_sink.h" 16 #include "content/public/renderer/render_thread.h" 17 #include "content/public/renderer/video_encode_accelerator.h" 18 #include "media/audio/audio_parameters.h" 19 #include "media/base/audio_bus.h" 20 #include "media/base/audio_fifo.h" 21 #include "media/base/bind_to_current_loop.h" 22 #include "media/base/multi_channel_resampler.h" 23 #include "media/base/video_frame.h" 24 #include "media/cast/cast_config.h" 25 #include "media/cast/cast_defines.h" 26 #include "media/cast/cast_sender.h" 27 #include "media/cast/transport/cast_transport_config.h" 28 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h" 29 #include "ui/gfx/geometry/size.h" 30 31 using media::cast::AudioSenderConfig; 32 using media::cast::VideoSenderConfig; 33 34 namespace { 35 36 const char kCodecNameOpus[] = "OPUS"; 37 const char kCodecNameVp8[] = "VP8"; 38 const char kCodecNameH264[] = "H264"; 39 40 // To convert from kilobits per second to bits to per second. 41 const int kBitrateMultiplier = 1000; 42 43 // This constant defines the number of sets of audio data to buffer 44 // in the FIFO. If input audio and output data have different resampling 45 // rates then buffer is necessary to avoid audio glitches. 46 // See CastAudioSink::ResampleData() and CastAudioSink::OnSetFormat() 47 // for more defaults. 48 const int kBufferAudioData = 2; 49 50 CastRtpPayloadParams DefaultOpusPayload() { 51 CastRtpPayloadParams payload; 52 payload.ssrc = 1; 53 payload.feedback_ssrc = 2; 54 payload.payload_type = 127; 55 payload.max_latency_ms = media::cast::kDefaultRtpMaxDelayMs; 56 payload.codec_name = kCodecNameOpus; 57 payload.clock_rate = 48000; 58 payload.channels = 2; 59 // The value is 0 which means VBR. 60 payload.min_bitrate = payload.max_bitrate = 61 media::cast::kDefaultAudioEncoderBitrate; 62 return payload; 63 } 64 65 CastRtpPayloadParams DefaultVp8Payload() { 66 CastRtpPayloadParams payload; 67 payload.ssrc = 11; 68 payload.feedback_ssrc = 12; 69 payload.payload_type = 96; 70 payload.max_latency_ms = media::cast::kDefaultRtpMaxDelayMs; 71 payload.codec_name = kCodecNameVp8; 72 payload.clock_rate = 90000; 73 payload.width = 1280; 74 payload.height = 720; 75 payload.min_bitrate = 50; 76 payload.max_bitrate = 2000; 77 return payload; 78 } 79 80 CastRtpPayloadParams DefaultH264Payload() { 81 CastRtpPayloadParams payload; 82 // TODO(hshi): set different ssrc/rtpPayloadType values for H264 and VP8 83 // once b/13696137 is fixed. 84 payload.ssrc = 11; 85 payload.feedback_ssrc = 12; 86 payload.payload_type = 96; 87 payload.max_latency_ms = media::cast::kDefaultRtpMaxDelayMs; 88 payload.codec_name = kCodecNameH264; 89 payload.clock_rate = 90000; 90 payload.width = 1280; 91 payload.height = 720; 92 payload.min_bitrate = 50; 93 payload.max_bitrate = 2000; 94 return payload; 95 } 96 97 bool IsHardwareVP8EncodingSupported() { 98 // Query for hardware VP8 encoder support. 99 std::vector<media::VideoEncodeAccelerator::SupportedProfile> vea_profiles = 100 content::GetSupportedVideoEncodeAcceleratorProfiles(); 101 for (size_t i = 0; i < vea_profiles.size(); ++i) { 102 if (vea_profiles[i].profile >= media::VP8PROFILE_MIN && 103 vea_profiles[i].profile <= media::VP8PROFILE_MAX) { 104 return true; 105 } 106 } 107 return false; 108 } 109 110 bool IsHardwareH264EncodingSupported() { 111 // Query for hardware H.264 encoder support. 112 std::vector<media::VideoEncodeAccelerator::SupportedProfile> vea_profiles = 113 content::GetSupportedVideoEncodeAcceleratorProfiles(); 114 for (size_t i = 0; i < vea_profiles.size(); ++i) { 115 if (vea_profiles[i].profile >= media::H264PROFILE_MIN && 116 vea_profiles[i].profile <= media::H264PROFILE_MAX) { 117 return true; 118 } 119 } 120 return false; 121 } 122 123 int NumberOfEncodeThreads() { 124 // We want to give CPU cycles for capturing and not to saturate the system 125 // just for encoding. So on a lower end system with only 1 or 2 cores we 126 // use only one thread for encoding. 127 if (base::SysInfo::NumberOfProcessors() <= 2) 128 return 1; 129 130 // On higher end we want to use 2 threads for encoding to reduce latency. 131 // In theory a physical CPU core has maximum 2 hyperthreads. Having 3 or 132 // more logical processors means the system has at least 2 physical cores. 133 return 2; 134 } 135 136 std::vector<CastRtpParams> SupportedAudioParams() { 137 // TODO(hclam): Fill in more codecs here. 138 std::vector<CastRtpParams> supported_params; 139 supported_params.push_back(CastRtpParams(DefaultOpusPayload())); 140 return supported_params; 141 } 142 143 std::vector<CastRtpParams> SupportedVideoParams() { 144 std::vector<CastRtpParams> supported_params; 145 if (IsHardwareH264EncodingSupported()) 146 supported_params.push_back(CastRtpParams(DefaultH264Payload())); 147 supported_params.push_back(CastRtpParams(DefaultVp8Payload())); 148 return supported_params; 149 } 150 151 bool ToAudioSenderConfig(const CastRtpParams& params, 152 AudioSenderConfig* config) { 153 config->rtp_config.ssrc = params.payload.ssrc; 154 config->incoming_feedback_ssrc = params.payload.feedback_ssrc; 155 config->rtp_config.payload_type = params.payload.payload_type; 156 config->rtp_config.max_delay_ms = params.payload.max_latency_ms; 157 config->rtp_config.aes_key = params.payload.aes_key; 158 config->rtp_config.aes_iv_mask = params.payload.aes_iv_mask; 159 config->use_external_encoder = false; 160 config->frequency = params.payload.clock_rate; 161 config->channels = params.payload.channels; 162 config->bitrate = params.payload.max_bitrate * kBitrateMultiplier; 163 config->codec = media::cast::transport::kPcm16; 164 if (params.payload.codec_name == kCodecNameOpus) 165 config->codec = media::cast::transport::kOpus; 166 else 167 return false; 168 return true; 169 } 170 171 bool ToVideoSenderConfig(const CastRtpParams& params, 172 VideoSenderConfig* config) { 173 config->rtp_config.ssrc = params.payload.ssrc; 174 config->incoming_feedback_ssrc = params.payload.feedback_ssrc; 175 config->rtp_config.payload_type = params.payload.payload_type; 176 config->rtp_config.max_delay_ms = params.payload.max_latency_ms; 177 config->rtp_config.aes_key = params.payload.aes_key; 178 config->rtp_config.aes_iv_mask = params.payload.aes_iv_mask; 179 config->use_external_encoder = false; 180 config->width = params.payload.width; 181 config->height = params.payload.height; 182 config->min_bitrate = config->start_bitrate = 183 params.payload.min_bitrate * kBitrateMultiplier; 184 config->max_bitrate = params.payload.max_bitrate * kBitrateMultiplier; 185 if (params.payload.codec_name == kCodecNameVp8) { 186 config->use_external_encoder = IsHardwareVP8EncodingSupported(); 187 config->codec = media::cast::transport::kVp8; 188 } else if (params.payload.codec_name == kCodecNameH264) { 189 config->use_external_encoder = IsHardwareH264EncodingSupported(); 190 config->codec = media::cast::transport::kH264; 191 } else { 192 return false; 193 } 194 if (!config->use_external_encoder) { 195 config->number_of_encode_threads = NumberOfEncodeThreads(); 196 } 197 return true; 198 } 199 200 } // namespace 201 202 // This class receives MediaStreamTrack events and video frames from a 203 // MediaStreamTrack. 204 // 205 // Threading: Video frames are received on the IO thread and then 206 // forwarded to media::cast::VideoFrameInput through a static method. 207 // Member variables of this class are only accessed on the render thread. 208 class CastVideoSink : public base::SupportsWeakPtr<CastVideoSink>, 209 public content::MediaStreamVideoSink { 210 public: 211 // |track| provides data for this sink. 212 // |expected_coded_size| is the expected dimension of the video frame. 213 // |error_callback| is called if video formats don't match. 214 CastVideoSink(const blink::WebMediaStreamTrack& track, 215 const gfx::Size& expected_coded_size, 216 const CastRtpStream::ErrorCallback& error_callback) 217 : track_(track), 218 sink_added_(false), 219 expected_coded_size_(expected_coded_size), 220 error_callback_(error_callback) {} 221 222 virtual ~CastVideoSink() { 223 if (sink_added_) 224 RemoveFromVideoTrack(this, track_); 225 } 226 227 // This static method is used to forward video frames to |frame_input|. 228 static void OnVideoFrame( 229 // These parameters are already bound when callback is created. 230 const gfx::Size& expected_coded_size, 231 const CastRtpStream::ErrorCallback& error_callback, 232 const scoped_refptr<media::cast::VideoFrameInput> frame_input, 233 // These parameters are passed for each frame. 234 const scoped_refptr<media::VideoFrame>& frame, 235 const media::VideoCaptureFormat& format, 236 const base::TimeTicks& estimated_capture_time) { 237 if (frame->coded_size() != expected_coded_size) { 238 error_callback.Run("Video frame resolution does not match config."); 239 return; 240 } 241 242 base::TimeTicks timestamp; 243 if (estimated_capture_time.is_null()) 244 timestamp = base::TimeTicks::Now(); 245 else 246 timestamp = estimated_capture_time; 247 248 // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc 249 TRACE_EVENT_INSTANT2( 250 "cast_perf_test", "MediaStreamVideoSink::OnVideoFrame", 251 TRACE_EVENT_SCOPE_THREAD, 252 "timestamp", timestamp.ToInternalValue(), 253 "time_delta", frame->timestamp().ToInternalValue()); 254 frame_input->InsertRawVideoFrame(frame, timestamp); 255 } 256 257 // Attach this sink to a video track represented by |track_|. 258 // Data received from the track will be submitted to |frame_input|. 259 void AddToTrack( 260 const scoped_refptr<media::cast::VideoFrameInput>& frame_input) { 261 DCHECK(!sink_added_); 262 sink_added_ = true; 263 AddToVideoTrack( 264 this, 265 base::Bind( 266 &CastVideoSink::OnVideoFrame, 267 expected_coded_size_, 268 error_callback_, 269 frame_input), 270 track_); 271 } 272 273 private: 274 blink::WebMediaStreamTrack track_; 275 bool sink_added_; 276 gfx::Size expected_coded_size_; 277 CastRtpStream::ErrorCallback error_callback_; 278 279 DISALLOW_COPY_AND_ASSIGN(CastVideoSink); 280 }; 281 282 // Receives audio data from a MediaStreamTrack. Data is submitted to 283 // media::cast::FrameInput. 284 // 285 // Threading: Audio frames are received on the real-time audio thread. 286 // Note that RemoveFromAudioTrack() is synchronous and we have 287 // gurantee that there will be no more audio data after calling it. 288 class CastAudioSink : public base::SupportsWeakPtr<CastAudioSink>, 289 public content::MediaStreamAudioSink { 290 public: 291 // |track| provides data for this sink. 292 // |error_callback| is called if audio formats don't match. 293 CastAudioSink(const blink::WebMediaStreamTrack& track, 294 const CastRtpStream::ErrorCallback& error_callback, 295 int output_channels, 296 int output_sample_rate) 297 : track_(track), 298 sink_added_(false), 299 error_callback_(error_callback), 300 weak_factory_(this), 301 output_channels_(output_channels), 302 output_sample_rate_(output_sample_rate), 303 input_preroll_(0) {} 304 305 virtual ~CastAudioSink() { 306 if (sink_added_) 307 RemoveFromAudioTrack(this, track_); 308 } 309 310 // Called on real-time audio thread. 311 // content::MediaStreamAudioSink implementation. 312 virtual void OnData(const int16* audio_data, 313 int sample_rate, 314 int number_of_channels, 315 int number_of_frames) OVERRIDE { 316 scoped_ptr<media::AudioBus> input_bus; 317 if (resampler_) { 318 input_bus = ResampleData( 319 audio_data, sample_rate, number_of_channels, number_of_frames); 320 if (!input_bus) 321 return; 322 } else { 323 input_bus = media::AudioBus::Create( 324 number_of_channels, number_of_frames); 325 input_bus->FromInterleaved( 326 audio_data, number_of_frames, number_of_channels); 327 } 328 329 // TODO(hclam): Pass in the accurate capture time to have good 330 // audio / video sync. 331 frame_input_->InsertAudio(input_bus.Pass(), base::TimeTicks::Now()); 332 } 333 334 // Return a resampled audio data from input. This is called when the 335 // input sample rate doesn't match the output. 336 // The flow of data is as follows: 337 // |audio_data| -> 338 // AudioFifo |fifo_| -> 339 // MultiChannelResampler |resampler|. 340 // 341 // The resampler pulls data out of the FIFO and resample the data in 342 // frequency domain. It might call |fifo_| for more than once. But no more 343 // than |kBufferAudioData| times. We preroll audio data into the FIFO to 344 // make sure there's enough data for resampling. 345 scoped_ptr<media::AudioBus> ResampleData( 346 const int16* audio_data, 347 int sample_rate, 348 int number_of_channels, 349 int number_of_frames) { 350 DCHECK_EQ(number_of_channels, output_channels_); 351 fifo_input_bus_->FromInterleaved( 352 audio_data, number_of_frames, number_of_channels); 353 fifo_->Push(fifo_input_bus_.get()); 354 355 if (input_preroll_ < kBufferAudioData - 1) { 356 ++input_preroll_; 357 return scoped_ptr<media::AudioBus>(); 358 } 359 360 scoped_ptr<media::AudioBus> output_bus( 361 media::AudioBus::Create( 362 output_channels_, 363 output_sample_rate_ * fifo_input_bus_->frames() / sample_rate)); 364 365 // Resampler will then call ProvideData() below to fetch data from 366 // |input_data_|. 367 resampler_->Resample(output_bus->frames(), output_bus.get()); 368 return output_bus.Pass(); 369 } 370 371 // Called on real-time audio thread. 372 virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE { 373 if (params.sample_rate() == output_sample_rate_) 374 return; 375 fifo_.reset(new media::AudioFifo( 376 output_channels_, 377 kBufferAudioData * params.frames_per_buffer())); 378 fifo_input_bus_ = media::AudioBus::Create( 379 params.channels(), params.frames_per_buffer()); 380 resampler_.reset(new media::MultiChannelResampler( 381 output_channels_, 382 static_cast<double>(params.sample_rate()) / output_sample_rate_, 383 params.frames_per_buffer(), 384 base::Bind(&CastAudioSink::ProvideData, base::Unretained(this)))); 385 } 386 387 // Add this sink to the track. Data received from the track will be 388 // submitted to |frame_input|. 389 void AddToTrack( 390 const scoped_refptr<media::cast::AudioFrameInput>& frame_input) { 391 DCHECK(!sink_added_); 392 sink_added_ = true; 393 394 // This member is written here and then accessed on the IO thread 395 // We will not get data until AddToAudioTrack is called so it is 396 // safe to access this member now. 397 frame_input_ = frame_input; 398 AddToAudioTrack(this, track_); 399 } 400 401 void ProvideData(int frame_delay, media::AudioBus* output_bus) { 402 fifo_->Consume(output_bus, 0, output_bus->frames()); 403 } 404 405 private: 406 blink::WebMediaStreamTrack track_; 407 bool sink_added_; 408 CastRtpStream::ErrorCallback error_callback_; 409 base::WeakPtrFactory<CastAudioSink> weak_factory_; 410 411 const int output_channels_; 412 const int output_sample_rate_; 413 414 // These member are accessed on the real-time audio time only. 415 scoped_refptr<media::cast::AudioFrameInput> frame_input_; 416 scoped_ptr<media::MultiChannelResampler> resampler_; 417 scoped_ptr<media::AudioFifo> fifo_; 418 scoped_ptr<media::AudioBus> fifo_input_bus_; 419 int input_preroll_; 420 421 DISALLOW_COPY_AND_ASSIGN(CastAudioSink); 422 }; 423 424 CastRtpParams::CastRtpParams(const CastRtpPayloadParams& payload_params) 425 : payload(payload_params) {} 426 427 CastCodecSpecificParams::CastCodecSpecificParams() {} 428 429 CastCodecSpecificParams::~CastCodecSpecificParams() {} 430 431 CastRtpPayloadParams::CastRtpPayloadParams() 432 : payload_type(0), 433 max_latency_ms(0), 434 ssrc(0), 435 feedback_ssrc(0), 436 clock_rate(0), 437 max_bitrate(0), 438 min_bitrate(0), 439 channels(0), 440 width(0), 441 height(0) {} 442 443 CastRtpPayloadParams::~CastRtpPayloadParams() {} 444 445 CastRtpParams::CastRtpParams() {} 446 447 CastRtpParams::~CastRtpParams() {} 448 449 CastRtpStream::CastRtpStream(const blink::WebMediaStreamTrack& track, 450 const scoped_refptr<CastSession>& session) 451 : track_(track), cast_session_(session), weak_factory_(this) {} 452 453 CastRtpStream::~CastRtpStream() {} 454 455 std::vector<CastRtpParams> CastRtpStream::GetSupportedParams() { 456 if (IsAudio()) 457 return SupportedAudioParams(); 458 else 459 return SupportedVideoParams(); 460 } 461 462 CastRtpParams CastRtpStream::GetParams() { return params_; } 463 464 void CastRtpStream::Start(const CastRtpParams& params, 465 const base::Closure& start_callback, 466 const base::Closure& stop_callback, 467 const ErrorCallback& error_callback) { 468 VLOG(1) << "CastRtpStream::Start = " << (IsAudio() ? "audio" : "video"); 469 stop_callback_ = stop_callback; 470 error_callback_ = error_callback; 471 472 if (IsAudio()) { 473 AudioSenderConfig config; 474 if (!ToAudioSenderConfig(params, &config)) { 475 DidEncounterError("Invalid parameters for audio."); 476 return; 477 } 478 479 // In case of error we have to go through DidEncounterError() to stop 480 // the streaming after reporting the error. 481 audio_sink_.reset(new CastAudioSink( 482 track_, 483 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError, 484 weak_factory_.GetWeakPtr())), 485 params.payload.channels, 486 params.payload.clock_rate)); 487 cast_session_->StartAudio( 488 config, 489 base::Bind(&CastAudioSink::AddToTrack, audio_sink_->AsWeakPtr()), 490 base::Bind(&CastRtpStream::DidEncounterError, 491 weak_factory_.GetWeakPtr())); 492 start_callback.Run(); 493 } else { 494 VideoSenderConfig config; 495 if (!ToVideoSenderConfig(params, &config)) { 496 DidEncounterError("Invalid parameters for video."); 497 return; 498 } 499 // See the code for audio above for explanation of callbacks. 500 video_sink_.reset(new CastVideoSink( 501 track_, 502 gfx::Size(config.width, config.height), 503 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError, 504 weak_factory_.GetWeakPtr())))); 505 cast_session_->StartVideo( 506 config, 507 base::Bind(&CastVideoSink::AddToTrack, video_sink_->AsWeakPtr()), 508 base::Bind(&CastRtpStream::DidEncounterError, 509 weak_factory_.GetWeakPtr())); 510 start_callback.Run(); 511 } 512 } 513 514 void CastRtpStream::Stop() { 515 VLOG(1) << "CastRtpStream::Stop = " << (IsAudio() ? "audio" : "video"); 516 audio_sink_.reset(); 517 video_sink_.reset(); 518 if (!stop_callback_.is_null()) 519 stop_callback_.Run(); 520 } 521 522 void CastRtpStream::ToggleLogging(bool enable) { 523 cast_session_->ToggleLogging(IsAudio(), enable); 524 } 525 526 void CastRtpStream::GetRawEvents( 527 const base::Callback<void(scoped_ptr<base::BinaryValue>)>& callback, 528 const std::string& extra_data) { 529 cast_session_->GetEventLogsAndReset(IsAudio(), extra_data, callback); 530 } 531 532 void CastRtpStream::GetStats( 533 const base::Callback<void(scoped_ptr<base::DictionaryValue>)>& callback) { 534 cast_session_->GetStatsAndReset(IsAudio(), callback); 535 } 536 537 bool CastRtpStream::IsAudio() const { 538 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio; 539 } 540 541 void CastRtpStream::DidEncounterError(const std::string& message) { 542 // Save the WeakPtr first because the error callback might delete this object. 543 base::WeakPtr<CastRtpStream> ptr = weak_factory_.GetWeakPtr(); 544 error_callback_.Run(message); 545 content::RenderThread::Get()->GetMessageLoop()->PostTask( 546 FROM_HERE, 547 base::Bind(&CastRtpStream::Stop, ptr)); 548 } 549