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/logging.h" 9 #include "base/memory/weak_ptr.h" 10 #include "chrome/renderer/media/cast_session.h" 11 #include "chrome/renderer/media/cast_udp_transport.h" 12 #include "content/public/renderer/media_stream_audio_sink.h" 13 #include "content/public/renderer/media_stream_video_sink.h" 14 #include "media/base/audio_bus.h" 15 #include "media/cast/cast_config.h" 16 #include "media/cast/cast_defines.h" 17 #include "media/cast/cast_sender.h" 18 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h" 19 20 using media::cast::AudioSenderConfig; 21 using media::cast::VideoSenderConfig; 22 23 namespace { 24 const char kCodecNameOpus[] = "OPUS"; 25 const char kCodecNameVp8[] = "VP8"; 26 27 CastRtpPayloadParams DefaultOpusPayload() { 28 CastRtpPayloadParams payload; 29 payload.payload_type = 111; 30 payload.codec_name = kCodecNameOpus; 31 payload.clock_rate = 48000; 32 payload.channels = 2; 33 payload.min_bitrate = payload.max_bitrate = 34 media::cast::kDefaultAudioEncoderBitrate; 35 return payload; 36 } 37 38 CastRtpPayloadParams DefaultVp8Payload() { 39 CastRtpPayloadParams payload; 40 payload.payload_type = 100; 41 payload.codec_name = kCodecNameVp8; 42 payload.clock_rate = 90000; 43 payload.width = 1280; 44 payload.height = 720; 45 payload.min_bitrate = 50 * 1000; 46 payload.max_bitrate = 2000 * 1000; 47 return payload; 48 } 49 50 CastRtpCaps DefaultAudioCaps() { 51 CastRtpCaps caps; 52 caps.payloads.push_back(DefaultOpusPayload()); 53 // TODO(hclam): Fill in |rtcp_features| and |fec_mechanisms|. 54 return caps; 55 } 56 57 CastRtpCaps DefaultVideoCaps() { 58 CastRtpCaps caps; 59 caps.payloads.push_back(DefaultVp8Payload()); 60 // TODO(hclam): Fill in |rtcp_features| and |fec_mechanisms|. 61 return caps; 62 } 63 64 bool ToAudioSenderConfig(const CastRtpParams& params, 65 AudioSenderConfig* config) { 66 if (params.payloads.empty()) 67 return false; 68 const CastRtpPayloadParams& payload_params = params.payloads[0]; 69 config->sender_ssrc = payload_params.ssrc; 70 config->use_external_encoder = false; 71 config->frequency = payload_params.clock_rate; 72 config->channels = payload_params.channels; 73 config->bitrate = payload_params.max_bitrate; 74 config->codec = media::cast::kPcm16; 75 if (payload_params.codec_name == kCodecNameOpus) 76 config->codec = media::cast::kOpus; 77 else 78 return false; 79 return true; 80 } 81 82 bool ToVideoSenderConfig(const CastRtpParams& params, 83 VideoSenderConfig* config) { 84 if (params.payloads.empty()) 85 return false; 86 const CastRtpPayloadParams& payload_params = params.payloads[0]; 87 config->sender_ssrc = payload_params.ssrc; 88 config->use_external_encoder = false; 89 config->width = payload_params.width; 90 config->height = payload_params.height; 91 config->min_bitrate = config->start_bitrate = payload_params.min_bitrate; 92 config->max_bitrate = payload_params.max_bitrate; 93 if (payload_params.codec_name == kCodecNameVp8) 94 config->codec = media::cast::kVp8; 95 else 96 return false; 97 return true; 98 } 99 100 void DeleteAudioBus(scoped_ptr<media::AudioBus> audio_bus) { 101 // Do nothing as |audio_bus| will be deleted. 102 } 103 104 } // namespace 105 106 // This class receives MediaStreamTrack events and video frames from a 107 // MediaStreamTrack. Video frames are submitted to media::cast::FrameInput. 108 // 109 // Threading: Video frames are received on the render thread. 110 class CastVideoSink : public base::SupportsWeakPtr<CastVideoSink>, 111 public content::MediaStreamVideoSink { 112 public: 113 explicit CastVideoSink(const blink::WebMediaStreamTrack& track) 114 : track_(track), sink_added_(false) { 115 } 116 117 virtual ~CastVideoSink() { 118 if (sink_added_) 119 RemoveFromVideoTrack(this, track_); 120 } 121 122 // content::MediaStreamVideoSink implementation. 123 virtual void OnVideoFrame( 124 const scoped_refptr<media::VideoFrame>& frame) OVERRIDE { 125 // TODO(hclam): Pass in the accurate capture time to have good 126 // audio/video sync. 127 frame_input_->InsertRawVideoFrame(frame, 128 base::TimeTicks::Now()); 129 } 130 131 // Attach this sink to MediaStreamTrack. This method call must 132 // be made on the render thread. Incoming data can then be 133 // passed to media::cast::FrameInput on any thread. 134 void AddToTrack( 135 const scoped_refptr<media::cast::FrameInput>& frame_input) { 136 DCHECK(!sink_added_); 137 frame_input_ = frame_input; 138 AddToVideoTrack(this, track_); 139 sink_added_ = true; 140 } 141 142 private: 143 blink::WebMediaStreamTrack track_; 144 scoped_refptr<media::cast::FrameInput> frame_input_; 145 bool sink_added_; 146 147 DISALLOW_COPY_AND_ASSIGN(CastVideoSink); 148 }; 149 150 // Receives audio data from a MediaStreamTrack. Data is submitted to 151 // media::cast::FrameInput. 152 // 153 // Threading: Audio frames are received on the real-time audio thread. 154 class CastAudioSink : public base::SupportsWeakPtr<CastAudioSink>, 155 public content::MediaStreamAudioSink { 156 public: 157 explicit CastAudioSink(const blink::WebMediaStreamTrack& track) 158 : track_(track), sink_added_(false) { 159 } 160 161 virtual ~CastAudioSink() { 162 if (sink_added_) 163 RemoveFromAudioTrack(this, track_); 164 } 165 166 // content::MediaStreamAudioSink implementation. 167 virtual void OnData(const int16* audio_data, 168 int sample_rate, 169 int number_of_channels, 170 int number_of_frames) OVERRIDE { 171 scoped_ptr<media::AudioBus> audio_bus( 172 media::AudioBus::Create(number_of_channels, 173 number_of_frames)); 174 audio_bus->FromInterleaved(audio_data, number_of_frames, 2); 175 176 // TODO(hclam): Pass in the accurate capture time to have good 177 // audio/video sync. 178 media::AudioBus* audio_bus_ptr = audio_bus.get(); 179 frame_input_->InsertAudio( 180 audio_bus_ptr, 181 base::TimeTicks::Now(), 182 base::Bind(&DeleteAudioBus, base::Passed(&audio_bus))); 183 } 184 185 virtual void OnSetFormat( 186 const media::AudioParameters& params) OVERRIDE{ 187 NOTIMPLEMENTED(); 188 } 189 190 // See CastVideoSink for details. 191 void AddToTrack( 192 const scoped_refptr<media::cast::FrameInput>& frame_input) { 193 DCHECK(!sink_added_); 194 frame_input_ = frame_input; 195 AddToAudioTrack(this, track_); 196 sink_added_ = true; 197 } 198 199 private: 200 blink::WebMediaStreamTrack track_; 201 scoped_refptr<media::cast::FrameInput> frame_input_; 202 bool sink_added_; 203 204 DISALLOW_COPY_AND_ASSIGN(CastAudioSink); 205 }; 206 207 CastCodecSpecificParams::CastCodecSpecificParams() { 208 } 209 210 CastCodecSpecificParams::~CastCodecSpecificParams() { 211 } 212 213 CastRtpPayloadParams::CastRtpPayloadParams() 214 : payload_type(0), 215 ssrc(0), 216 clock_rate(0), 217 max_bitrate(0), 218 min_bitrate(0), 219 channels(0), 220 width(0), 221 height(0) { 222 } 223 224 CastRtpPayloadParams::~CastRtpPayloadParams() { 225 } 226 227 CastRtpCaps::CastRtpCaps() { 228 } 229 230 CastRtpCaps::~CastRtpCaps() { 231 } 232 233 CastRtpStream::CastRtpStream( 234 const blink::WebMediaStreamTrack& track, 235 const scoped_refptr<CastSession>& session) 236 : track_(track), 237 cast_session_(session) { 238 } 239 240 CastRtpStream::~CastRtpStream() { 241 } 242 243 CastRtpCaps CastRtpStream::GetCaps() { 244 if (IsAudio()) 245 return DefaultAudioCaps(); 246 else 247 return DefaultVideoCaps(); 248 } 249 250 CastRtpParams CastRtpStream::GetParams() { 251 return params_; 252 } 253 254 void CastRtpStream::Start(const CastRtpParams& params) { 255 if (IsAudio()) { 256 AudioSenderConfig config; 257 if (!ToAudioSenderConfig(params, &config)) { 258 DVLOG(1) << "Invalid parameters for audio."; 259 } 260 audio_sink_.reset(new CastAudioSink(track_)); 261 cast_session_->StartAudio( 262 config, 263 base::Bind(&CastAudioSink::AddToTrack, 264 audio_sink_->AsWeakPtr())); 265 } else { 266 VideoSenderConfig config; 267 if (!ToVideoSenderConfig(params, &config)) { 268 DVLOG(1) << "Invalid parameters for video."; 269 } 270 video_sink_.reset(new CastVideoSink(track_)); 271 cast_session_->StartVideo( 272 config, 273 base::Bind(&CastVideoSink::AddToTrack, 274 video_sink_->AsWeakPtr())); 275 } 276 } 277 278 void CastRtpStream::Stop() { 279 audio_sink_.reset(); 280 video_sink_.reset(); 281 } 282 283 bool CastRtpStream::IsAudio() const { 284 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio; 285 } 286