Home | History | Annotate | Download | only in media
      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