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
DefaultOpusPayload()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
DefaultVp8Payload()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
DefaultAudioCaps()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
DefaultVideoCaps()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
ToAudioSenderConfig(const CastRtpParams & params,AudioSenderConfig * config)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
ToVideoSenderConfig(const CastRtpParams & params,VideoSenderConfig * config)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
DeleteAudioBus(scoped_ptr<media::AudioBus> audio_bus)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:
CastVideoSink(const blink::WebMediaStreamTrack & track)113 explicit CastVideoSink(const blink::WebMediaStreamTrack& track)
114 : track_(track), sink_added_(false) {
115 }
116
~CastVideoSink()117 virtual ~CastVideoSink() {
118 if (sink_added_)
119 RemoveFromVideoTrack(this, track_);
120 }
121
122 // content::MediaStreamVideoSink implementation.
OnVideoFrame(const scoped_refptr<media::VideoFrame> & frame)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.
AddToTrack(const scoped_refptr<media::cast::FrameInput> & frame_input)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:
CastAudioSink(const blink::WebMediaStreamTrack & track)157 explicit CastAudioSink(const blink::WebMediaStreamTrack& track)
158 : track_(track), sink_added_(false) {
159 }
160
~CastAudioSink()161 virtual ~CastAudioSink() {
162 if (sink_added_)
163 RemoveFromAudioTrack(this, track_);
164 }
165
166 // content::MediaStreamAudioSink implementation.
OnData(const int16 * audio_data,int sample_rate,int number_of_channels,int number_of_frames)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
OnSetFormat(const media::AudioParameters & params)185 virtual void OnSetFormat(
186 const media::AudioParameters& params) OVERRIDE{
187 NOTIMPLEMENTED();
188 }
189
190 // See CastVideoSink for details.
AddToTrack(const scoped_refptr<media::cast::FrameInput> & frame_input)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
CastCodecSpecificParams()207 CastCodecSpecificParams::CastCodecSpecificParams() {
208 }
209
~CastCodecSpecificParams()210 CastCodecSpecificParams::~CastCodecSpecificParams() {
211 }
212
CastRtpPayloadParams()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
~CastRtpPayloadParams()224 CastRtpPayloadParams::~CastRtpPayloadParams() {
225 }
226
CastRtpCaps()227 CastRtpCaps::CastRtpCaps() {
228 }
229
~CastRtpCaps()230 CastRtpCaps::~CastRtpCaps() {
231 }
232
CastRtpStream(const blink::WebMediaStreamTrack & track,const scoped_refptr<CastSession> & session)233 CastRtpStream::CastRtpStream(
234 const blink::WebMediaStreamTrack& track,
235 const scoped_refptr<CastSession>& session)
236 : track_(track),
237 cast_session_(session) {
238 }
239
~CastRtpStream()240 CastRtpStream::~CastRtpStream() {
241 }
242
GetCaps()243 CastRtpCaps CastRtpStream::GetCaps() {
244 if (IsAudio())
245 return DefaultAudioCaps();
246 else
247 return DefaultVideoCaps();
248 }
249
GetParams()250 CastRtpParams CastRtpStream::GetParams() {
251 return params_;
252 }
253
Start(const CastRtpParams & params)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
Stop()278 void CastRtpStream::Stop() {
279 audio_sink_.reset();
280 video_sink_.reset();
281 }
282
IsAudio() const283 bool CastRtpStream::IsAudio() const {
284 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio;
285 }
286