Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / renderer / media / cast_rtp_stream.cc
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 "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"
22
23 using media::cast::AudioSenderConfig;
24 using media::cast::VideoSenderConfig;
25
26 namespace {
27 const char kCodecNameOpus[] = "OPUS";
28 const char kCodecNameVp8[] = "VP8";
29
30 CastRtpPayloadParams DefaultOpusPayload() {
31   CastRtpPayloadParams payload;
32   payload.ssrc = 1;
33   payload.feedback_ssrc = 1;
34   payload.payload_type = 127;
35   payload.codec_name = kCodecNameOpus;
36   payload.clock_rate = 48000;
37   payload.channels = 2;
38   payload.min_bitrate = payload.max_bitrate =
39       media::cast::kDefaultAudioEncoderBitrate;
40   return payload;
41 }
42
43 CastRtpPayloadParams DefaultVp8Payload() {
44   CastRtpPayloadParams payload;
45   payload.ssrc = 11;
46   payload.feedback_ssrc = 12;
47   payload.payload_type = 96;
48   payload.codec_name = kCodecNameVp8;
49   payload.clock_rate = 90000;
50   payload.width = 1280;
51   payload.height = 720;
52   payload.min_bitrate = 50 * 1000;
53   payload.max_bitrate = 2000 * 1000;
54   return payload;
55 }
56
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;
62 }
63
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;
69 }
70
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;
83   else
84     return false;
85   return true;
86 }
87
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;
100   else
101     return false;
102   return true;
103 }
104
105 void DeleteAudioBus(scoped_ptr<media::AudioBus> audio_bus) {
106   // Do nothing as |audio_bus| will be deleted.
107 }
108
109 }  // namespace
110
111 // This class receives MediaStreamTrack events and video frames from a
112 // MediaStreamTrack. Video frames are submitted to media::cast::FrameInput.
113 //
114 // Threading: Video frames are received on the render thread.
115 class CastVideoSink : public base::SupportsWeakPtr<CastVideoSink>,
116                       public content::MediaStreamVideoSink {
117  public:
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)
122       : track_(track),
123         sink_added_(false),
124         error_callback_(error_callback),
125         render_thread_task_runner_(content::RenderThread::Get()
126                                        ->GetMessageLoop()
127                                        ->message_loop_proxy()) {}
128
129   virtual ~CastVideoSink() {
130     if (sink_added_)
131       RemoveFromVideoTrack(this, track_);
132   }
133
134   // content::MediaStreamVideoSink implementation.
135   virtual void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame)
136       OVERRIDE {
137     DCHECK(render_thread_task_runner_->BelongsToCurrentThread());
138     DCHECK(frame_input_);
139     // TODO(hclam): Pass in the accurate capture time to have good
140     // audio/video sync.
141     frame_input_->InsertRawVideoFrame(frame, base::TimeTicks::Now());
142   }
143
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());
149
150     frame_input_ = frame_input;
151     if (!sink_added_) {
152       AddToVideoTrack(this, track_);
153       sink_added_ = true;
154     }
155   }
156
157  private:
158   blink::WebMediaStreamTrack track_;
159   scoped_refptr<media::cast::FrameInput> frame_input_;
160   bool sink_added_;
161   CastRtpStream::ErrorCallback error_callback_;
162   scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_;
163
164   DISALLOW_COPY_AND_ASSIGN(CastVideoSink);
165 };
166
167 // Receives audio data from a MediaStreamTrack. Data is submitted to
168 // media::cast::FrameInput.
169 //
170 // Threading: Audio frames are received on the real-time audio thread.
171 class CastAudioSink : public base::SupportsWeakPtr<CastAudioSink>,
172                       public content::MediaStreamAudioSink {
173  public:
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)
178       : track_(track),
179         sink_added_(false),
180         error_callback_(error_callback),
181         weak_factory_(this),
182         render_thread_task_runner_(content::RenderThread::Get()
183                                        ->GetMessageLoop()
184                                        ->message_loop_proxy()) {}
185
186   virtual ~CastAudioSink() {
187     if (sink_added_)
188       RemoveFromAudioTrack(this, track_);
189   }
190
191   // Called on real-time audio thread.
192   // content::MediaStreamAudioSink implementation.
193   virtual void OnData(const int16* audio_data,
194                       int sample_rate,
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);
200
201     // TODO(hclam): Pass in the accurate capture time to have good
202     // audio / video sync.
203
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(
209         FROM_HERE,
210         base::Bind(&CastAudioSink::SendAudio,
211                    weak_factory_.GetWeakPtr(),
212                    audio_bus_ptr,
213                    base::TimeTicks::Now(),
214                    base::Bind(&DeleteAudioBus, base::Passed(&audio_bus))));
215   }
216
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);
223   }
224
225   // Called on real-time audio thread.
226   virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE {
227     NOTIMPLEMENTED();
228   }
229
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;
234     if (!sink_added_) {
235       AddToAudioTrack(this, track_);
236       sink_added_ = true;
237     }
238   }
239
240  private:
241   blink::WebMediaStreamTrack track_;
242   scoped_refptr<media::cast::FrameInput> frame_input_;
243   bool sink_added_;
244   CastRtpStream::ErrorCallback error_callback_;
245   base::WeakPtrFactory<CastAudioSink> weak_factory_;
246   scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_;
247
248   DISALLOW_COPY_AND_ASSIGN(CastAudioSink);
249 };
250
251 CastRtpParams::CastRtpParams(const CastRtpPayloadParams& payload_params)
252     : payload(payload_params) {}
253
254 CastCodecSpecificParams::CastCodecSpecificParams() {}
255
256 CastCodecSpecificParams::~CastCodecSpecificParams() {}
257
258 CastRtpPayloadParams::CastRtpPayloadParams()
259     : payload_type(0),
260       ssrc(0),
261       feedback_ssrc(0),
262       clock_rate(0),
263       max_bitrate(0),
264       min_bitrate(0),
265       channels(0),
266       width(0),
267       height(0) {
268 }
269
270 CastRtpPayloadParams::~CastRtpPayloadParams() {
271 }
272
273 CastRtpParams::CastRtpParams() {
274 }
275
276 CastRtpParams::~CastRtpParams() {
277 }
278
279 CastRtpStream::CastRtpStream(const blink::WebMediaStreamTrack& track,
280                              const scoped_refptr<CastSession>& session)
281     : track_(track), cast_session_(session), weak_factory_(this) {}
282
283 CastRtpStream::~CastRtpStream() {
284 }
285
286 std::vector<CastRtpParams> CastRtpStream::GetSupportedParams() {
287   if (IsAudio())
288     return SupportedAudioParams();
289   else
290     return SupportedVideoParams();
291 }
292
293 CastRtpParams CastRtpStream::GetParams() {
294   return params_;
295 }
296
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;
303
304   if (IsAudio()) {
305     AudioSenderConfig config;
306     if (!ToAudioSenderConfig(params, &config)) {
307       DidEncounterError("Invalid parameters for audio.");
308       return;
309     }
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(
313         track_,
314         media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError,
315                                             weak_factory_.GetWeakPtr()))));
316     cast_session_->StartAudio(
317         config,
318         base::Bind(&CastAudioSink::AddToTrack,
319                    audio_sink_->AsWeakPtr()));
320     start_callback.Run();
321   } else {
322     VideoSenderConfig config;
323     if (!ToVideoSenderConfig(params, &config)) {
324       DidEncounterError("Invalid parameters for video.");
325       return;
326     }
327     // See the code for audio above for explanation of callbacks.
328     video_sink_.reset(new CastVideoSink(
329         track_,
330         media::BindToCurrentLoop(base::Bind(&CastRtpStream::DidEncounterError,
331                                             weak_factory_.GetWeakPtr()))));
332     cast_session_->StartVideo(
333         config,
334         base::Bind(&CastVideoSink::AddToTrack,
335                    video_sink_->AsWeakPtr()));
336     start_callback.Run();
337   }
338 }
339
340 void CastRtpStream::Stop() {
341   audio_sink_.reset();
342   video_sink_.reset();
343   stop_callback_.Run();
344 }
345
346 bool CastRtpStream::IsAudio() const {
347   return track_.source().type() == blink::WebMediaStreamSource::TypeAudio;
348 }
349
350 void CastRtpStream::DidEncounterError(const std::string& message) {
351   error_callback_.Run(message);
352   Stop();
353 }