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.
5 #include "chrome/renderer/media/cast_rtp_stream.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 "content/public/renderer/render_thread.h"
15 #include "media/base/audio_bus.h"
16 #include "media/base/bind_to_current_loop.h"
17 #include "media/cast/cast_config.h"
18 #include "media/cast/cast_defines.h"
19 #include "media/cast/cast_sender.h"
20 #include "media/cast/transport/cast_transport_config.h"
21 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
23 using media::cast::AudioSenderConfig;
24 using media::cast::VideoSenderConfig;
27 const char kCodecNameOpus[] = "OPUS";
28 const char kCodecNameVp8[] = "VP8";
30 CastRtpPayloadParams DefaultOpusPayload() {
31 CastRtpPayloadParams payload;
33 payload.feedback_ssrc = 1;
34 payload.payload_type = 127;
35 payload.codec_name = kCodecNameOpus;
36 payload.clock_rate = 48000;
38 payload.min_bitrate = payload.max_bitrate =
39 media::cast::kDefaultAudioEncoderBitrate;
43 CastRtpPayloadParams DefaultVp8Payload() {
44 CastRtpPayloadParams payload;
46 payload.feedback_ssrc = 12;
47 payload.payload_type = 96;
48 payload.codec_name = kCodecNameVp8;
49 payload.clock_rate = 90000;
52 payload.min_bitrate = 50 * 1000;
53 payload.max_bitrate = 2000 * 1000;
57 std::vector<CastRtpParams> SupportedAudioParams() {
58 // TODO(hclam): Fill in more codecs here.
59 std::vector<CastRtpParams> supported_params;
60 supported_params.push_back(CastRtpParams(DefaultOpusPayload()));
61 return supported_params;
64 std::vector<CastRtpParams> SupportedVideoParams() {
65 // TODO(hclam): Fill in H264 here.
66 std::vector<CastRtpParams> supported_params;
67 supported_params.push_back(CastRtpParams(DefaultVp8Payload()));
68 return supported_params;
71 bool ToAudioSenderConfig(const CastRtpParams& params,
72 AudioSenderConfig* config) {
73 config->sender_ssrc = params.payload.ssrc;
74 config->incoming_feedback_ssrc = params.payload.feedback_ssrc;
75 config->rtp_config.payload_type = params.payload.payload_type;
76 config->use_external_encoder = false;
77 config->frequency = params.payload.clock_rate;
78 config->channels = params.payload.channels;
79 config->bitrate = params.payload.max_bitrate;
80 config->codec = media::cast::transport::kPcm16;
81 if (params.payload.codec_name == kCodecNameOpus)
82 config->codec = media::cast::transport::kOpus;
88 bool ToVideoSenderConfig(const CastRtpParams& params,
89 VideoSenderConfig* config) {
90 config->sender_ssrc = params.payload.ssrc;
91 config->incoming_feedback_ssrc = params.payload.feedback_ssrc;
92 config->rtp_config.payload_type = params.payload.payload_type;
93 config->use_external_encoder = false;
94 config->width = params.payload.width;
95 config->height = params.payload.height;
96 config->min_bitrate = config->start_bitrate = params.payload.min_bitrate;
97 config->max_bitrate = params.payload.max_bitrate;
98 if (params.payload.codec_name == kCodecNameVp8)
99 config->codec = media::cast::transport::kVp8;
105 void DeleteAudioBus(scoped_ptr<media::AudioBus> audio_bus) {
106 // Do nothing as |audio_bus| will be deleted.
111 // This class receives MediaStreamTrack events and video frames from a
112 // MediaStreamTrack. Video frames are submitted to media::cast::FrameInput.
114 // Threading: Video frames are received on the render thread.
115 class CastVideoSink : public base::SupportsWeakPtr<CastVideoSink>,
116 public content::MediaStreamVideoSink {
118 // |track| provides data for this sink.
119 // |error_callback| is called if video formats don't match.
120 CastVideoSink(const blink::WebMediaStreamTrack& track,
121 const CastRtpStream::ErrorCallback& error_callback)
124 error_callback_(error_callback),
125 render_thread_task_runner_(content::RenderThread::Get()
127 ->message_loop_proxy()) {}
129 virtual ~CastVideoSink() {
131 RemoveFromVideoTrack(this, track_);
134 // content::MediaStreamVideoSink implementation.
135 virtual void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame)
137 DCHECK(render_thread_task_runner_->BelongsToCurrentThread());
138 DCHECK(frame_input_);
139 // TODO(hclam): Pass in the accurate capture time to have good
141 frame_input_->InsertRawVideoFrame(frame, base::TimeTicks::Now());
144 // Attach this sink to MediaStreamTrack. This method call must
145 // be made on the render thread. Incoming data can then be
146 // passed to media::cast::FrameInput on any thread.
147 void AddToTrack(const scoped_refptr<media::cast::FrameInput>& frame_input) {
148 DCHECK(render_thread_task_runner_->BelongsToCurrentThread());
150 frame_input_ = frame_input;
152 AddToVideoTrack(this, track_);
158 blink::WebMediaStreamTrack track_;
159 scoped_refptr<media::cast::FrameInput> frame_input_;
161 CastRtpStream::ErrorCallback error_callback_;
162 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_;
164 DISALLOW_COPY_AND_ASSIGN(CastVideoSink);
167 // Receives audio data from a MediaStreamTrack. Data is submitted to
168 // media::cast::FrameInput.
170 // Threading: Audio frames are received on the real-time audio thread.
171 class CastAudioSink : public base::SupportsWeakPtr<CastAudioSink>,
172 public content::MediaStreamAudioSink {
174 // |track| provides data for this sink.
175 // |error_callback| is called if audio formats don't match.
176 CastAudioSink(const blink::WebMediaStreamTrack& track,
177 const CastRtpStream::ErrorCallback& error_callback)
180 error_callback_(error_callback),
182 render_thread_task_runner_(content::RenderThread::Get()
184 ->message_loop_proxy()) {}
186 virtual ~CastAudioSink() {
188 RemoveFromAudioTrack(this, track_);
191 // Called on real-time audio thread.
192 // content::MediaStreamAudioSink implementation.
193 virtual void OnData(const int16* audio_data,
195 int number_of_channels,
196 int number_of_frames) OVERRIDE {
197 scoped_ptr<media::AudioBus> audio_bus(
198 media::AudioBus::Create(number_of_channels, number_of_frames));
199 audio_bus->FromInterleaved(audio_data, number_of_frames, 2);
201 // TODO(hclam): Pass in the accurate capture time to have good
202 // audio / video sync.
204 // TODO(hclam): We shouldn't hop through the render thread.
205 // 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.
207 media::AudioBus* const audio_bus_ptr = audio_bus.get();
208 render_thread_task_runner_->PostTask(
210 base::Bind(&CastAudioSink::SendAudio,
211 weak_factory_.GetWeakPtr(),
213 base::TimeTicks::Now(),
214 base::Bind(&DeleteAudioBus, base::Passed(&audio_bus))));
217 void SendAudio(const media::AudioBus* audio_bus_ptr,
218 const base::TimeTicks& recorded_time,
219 const base::Closure& done_callback) {
220 DCHECK(render_thread_task_runner_->BelongsToCurrentThread());
221 DCHECK(frame_input_);
222 frame_input_->InsertAudio(audio_bus_ptr, recorded_time, done_callback);
225 // Called on real-time audio thread.
226 virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE {
230 // See CastVideoSink for details.
231 void AddToTrack(const scoped_refptr<media::cast::FrameInput>& frame_input) {
232 DCHECK(render_thread_task_runner_->BelongsToCurrentThread());
233 frame_input_ = frame_input;
235 AddToAudioTrack(this, track_);
241 blink::WebMediaStreamTrack track_;
242 scoped_refptr<media::cast::FrameInput> frame_input_;
244 CastRtpStream::ErrorCallback error_callback_;
245 base::WeakPtrFactory<CastAudioSink> weak_factory_;
246 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_;
248 DISALLOW_COPY_AND_ASSIGN(CastAudioSink);
251 CastRtpParams::CastRtpParams(const CastRtpPayloadParams& payload_params)
252 : payload(payload_params) {}
254 CastCodecSpecificParams::CastCodecSpecificParams() {}
256 CastCodecSpecificParams::~CastCodecSpecificParams() {}
258 CastRtpPayloadParams::CastRtpPayloadParams()
270 CastRtpPayloadParams::~CastRtpPayloadParams() {
273 CastRtpParams::CastRtpParams() {
276 CastRtpParams::~CastRtpParams() {
279 CastRtpStream::CastRtpStream(const blink::WebMediaStreamTrack& track,
280 const scoped_refptr<CastSession>& session)
281 : track_(track), cast_session_(session), weak_factory_(this) {}
283 CastRtpStream::~CastRtpStream() {
286 std::vector<CastRtpParams> CastRtpStream::GetSupportedParams() {
288 return SupportedAudioParams();
290 return SupportedVideoParams();
293 CastRtpParams CastRtpStream::GetParams() {
297 void CastRtpStream::Start(const CastRtpParams& params,
298 const base::Closure& start_callback,
299 const base::Closure& stop_callback,
300 const ErrorCallback& error_callback) {
301 stop_callback_ = stop_callback;
302 error_callback_ = error_callback;
305 AudioSenderConfig config;
306 if (!ToAudioSenderConfig(params, &config)) {
307 DidEncounterError("Invalid parameters for audio.");
310 // In case of error we have to go through DidEncounterError() to stop
311 // the streaming after reporting the error.
312 audio_sink_.reset(new CastAudioSink(
314 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError,
315 weak_factory_.GetWeakPtr()))));
316 cast_session_->StartAudio(
318 base::Bind(&CastAudioSink::AddToTrack,
319 audio_sink_->AsWeakPtr()));
320 start_callback.Run();
322 VideoSenderConfig config;
323 if (!ToVideoSenderConfig(params, &config)) {
324 DidEncounterError("Invalid parameters for video.");
327 // See the code for audio above for explanation of callbacks.
328 video_sink_.reset(new CastVideoSink(
330 media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError,
331 weak_factory_.GetWeakPtr()))));
332 cast_session_->StartVideo(
334 base::Bind(&CastVideoSink::AddToTrack,
335 video_sink_->AsWeakPtr()));
336 start_callback.Run();
340 void CastRtpStream::Stop() {
343 stop_callback_.Run();
346 bool CastRtpStream::IsAudio() const {
347 return track_.source().type() == blink::WebMediaStreamSource::TypeAudio;
350 void CastRtpStream::DidEncounterError(const std::string& message) {
351 error_callback_.Run(message);