普通文本  |  286行  |  8.15 KB

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/media/cast_rtp_stream.h"

#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "chrome/renderer/media/cast_session.h"
#include "chrome/renderer/media/cast_udp_transport.h"
#include "content/public/renderer/media_stream_audio_sink.h"
#include "content/public/renderer/media_stream_video_sink.h"
#include "media/base/audio_bus.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_defines.h"
#include "media/cast/cast_sender.h"
#include "third_party/WebKit/public/platform/WebMediaStreamSource.h"

using media::cast::AudioSenderConfig;
using media::cast::VideoSenderConfig;

namespace {
const char kCodecNameOpus[] = "OPUS";
const char kCodecNameVp8[] = "VP8";

CastRtpPayloadParams DefaultOpusPayload() {
  CastRtpPayloadParams payload;
  payload.payload_type = 111;
  payload.codec_name = kCodecNameOpus;
  payload.clock_rate = 48000;
  payload.channels = 2;
  payload.min_bitrate = payload.max_bitrate =
      media::cast::kDefaultAudioEncoderBitrate;
  return payload;
}

CastRtpPayloadParams DefaultVp8Payload() {
  CastRtpPayloadParams payload;
  payload.payload_type = 100;
  payload.codec_name = kCodecNameVp8;
  payload.clock_rate = 90000;
  payload.width = 1280;
  payload.height = 720;
  payload.min_bitrate = 50 * 1000;
  payload.max_bitrate = 2000 * 1000;
  return payload;
}

CastRtpCaps DefaultAudioCaps() {
  CastRtpCaps caps;
  caps.payloads.push_back(DefaultOpusPayload());
  // TODO(hclam): Fill in |rtcp_features| and |fec_mechanisms|.
  return caps;
}

CastRtpCaps DefaultVideoCaps() {
  CastRtpCaps caps;
  caps.payloads.push_back(DefaultVp8Payload());
  // TODO(hclam): Fill in |rtcp_features| and |fec_mechanisms|.
  return caps;
}

bool ToAudioSenderConfig(const CastRtpParams& params,
                         AudioSenderConfig* config) {
  if (params.payloads.empty())
    return false;
  const CastRtpPayloadParams& payload_params = params.payloads[0];
  config->sender_ssrc = payload_params.ssrc;
  config->use_external_encoder = false;
  config->frequency = payload_params.clock_rate;
  config->channels = payload_params.channels;
  config->bitrate = payload_params.max_bitrate;
  config->codec = media::cast::kPcm16;
  if (payload_params.codec_name == kCodecNameOpus)
    config->codec = media::cast::kOpus;
  else
    return false;
  return true;
}

bool ToVideoSenderConfig(const CastRtpParams& params,
                         VideoSenderConfig* config) {
  if (params.payloads.empty())
    return false;
  const CastRtpPayloadParams& payload_params = params.payloads[0];
  config->sender_ssrc = payload_params.ssrc;
  config->use_external_encoder = false;
  config->width = payload_params.width;
  config->height = payload_params.height;
  config->min_bitrate = config->start_bitrate = payload_params.min_bitrate;
  config->max_bitrate = payload_params.max_bitrate;
  if (payload_params.codec_name == kCodecNameVp8)
    config->codec = media::cast::kVp8;
  else
    return false;
  return true;
}

void DeleteAudioBus(scoped_ptr<media::AudioBus> audio_bus) {
  // Do nothing as |audio_bus| will be deleted.
}

}  // namespace

// This class receives MediaStreamTrack events and video frames from a
// MediaStreamTrack. Video frames are submitted to media::cast::FrameInput.
//
// Threading: Video frames are received on the render thread.
class CastVideoSink : public base::SupportsWeakPtr<CastVideoSink>,
                      public content::MediaStreamVideoSink {
 public:
  explicit CastVideoSink(const blink::WebMediaStreamTrack& track)
      : track_(track), sink_added_(false) {
  }

  virtual ~CastVideoSink() {
    if (sink_added_)
      RemoveFromVideoTrack(this, track_);
  }

  // content::MediaStreamVideoSink implementation.
  virtual void OnVideoFrame(
      const scoped_refptr<media::VideoFrame>& frame) OVERRIDE {
    // TODO(hclam): Pass in the accurate capture time to have good
    // audio/video sync.
    frame_input_->InsertRawVideoFrame(frame,
                                      base::TimeTicks::Now());
  }

  // Attach this sink to MediaStreamTrack. This method call must
  // be made on the render thread. Incoming data can then be
  // passed to media::cast::FrameInput on any thread.
  void AddToTrack(
      const scoped_refptr<media::cast::FrameInput>& frame_input) {
    DCHECK(!sink_added_);
    frame_input_ = frame_input;
    AddToVideoTrack(this, track_);
    sink_added_ = true;
  }

 private:
  blink::WebMediaStreamTrack track_;
  scoped_refptr<media::cast::FrameInput> frame_input_;
  bool sink_added_;

  DISALLOW_COPY_AND_ASSIGN(CastVideoSink);
};

// Receives audio data from a MediaStreamTrack. Data is submitted to
// media::cast::FrameInput.
//
// Threading: Audio frames are received on the real-time audio thread.
class CastAudioSink : public base::SupportsWeakPtr<CastAudioSink>,
                      public content::MediaStreamAudioSink {
 public:
  explicit CastAudioSink(const blink::WebMediaStreamTrack& track)
      : track_(track), sink_added_(false) {
  }

  virtual ~CastAudioSink() {
    if (sink_added_)
      RemoveFromAudioTrack(this, track_);
  }

  // content::MediaStreamAudioSink implementation.
  virtual void OnData(const int16* audio_data,
                      int sample_rate,
                      int number_of_channels,
                      int number_of_frames) OVERRIDE {
    scoped_ptr<media::AudioBus> audio_bus(
        media::AudioBus::Create(number_of_channels,
                                number_of_frames));
    audio_bus->FromInterleaved(audio_data, number_of_frames, 2);

    // TODO(hclam): Pass in the accurate capture time to have good
    // audio/video sync.
    media::AudioBus* audio_bus_ptr = audio_bus.get();
    frame_input_->InsertAudio(
        audio_bus_ptr,
        base::TimeTicks::Now(),
        base::Bind(&DeleteAudioBus, base::Passed(&audio_bus)));
  }

  virtual void OnSetFormat(
      const media::AudioParameters& params) OVERRIDE{
    NOTIMPLEMENTED();
  }

  // See CastVideoSink for details.
  void AddToTrack(
      const scoped_refptr<media::cast::FrameInput>& frame_input) {
    DCHECK(!sink_added_);
    frame_input_ = frame_input;
    AddToAudioTrack(this, track_);
    sink_added_ = true;
  }

 private:
  blink::WebMediaStreamTrack track_;
  scoped_refptr<media::cast::FrameInput> frame_input_;
  bool sink_added_;

  DISALLOW_COPY_AND_ASSIGN(CastAudioSink);
};

CastCodecSpecificParams::CastCodecSpecificParams() {
}

CastCodecSpecificParams::~CastCodecSpecificParams() {
}

CastRtpPayloadParams::CastRtpPayloadParams()
    : payload_type(0),
      ssrc(0),
      clock_rate(0),
      max_bitrate(0),
      min_bitrate(0),
      channels(0),
      width(0),
      height(0) {
}

CastRtpPayloadParams::~CastRtpPayloadParams() {
}

CastRtpCaps::CastRtpCaps() {
}

CastRtpCaps::~CastRtpCaps() {
}

CastRtpStream::CastRtpStream(
    const blink::WebMediaStreamTrack& track,
    const scoped_refptr<CastSession>& session)
    : track_(track),
      cast_session_(session) {
}

CastRtpStream::~CastRtpStream() {
}

CastRtpCaps CastRtpStream::GetCaps() {
  if (IsAudio())
    return DefaultAudioCaps();
  else
    return DefaultVideoCaps();
}

CastRtpParams CastRtpStream::GetParams() {
  return params_;
}

void CastRtpStream::Start(const CastRtpParams& params) {
  if (IsAudio()) {
    AudioSenderConfig config;
    if (!ToAudioSenderConfig(params, &config)) {
      DVLOG(1) << "Invalid parameters for audio.";
    }
    audio_sink_.reset(new CastAudioSink(track_));
    cast_session_->StartAudio(
        config,
        base::Bind(&CastAudioSink::AddToTrack,
                   audio_sink_->AsWeakPtr()));
  } else {
    VideoSenderConfig config;
    if (!ToVideoSenderConfig(params, &config)) {
      DVLOG(1) << "Invalid parameters for video.";
    }
    video_sink_.reset(new CastVideoSink(track_));
    cast_session_->StartVideo(
        config,
        base::Bind(&CastVideoSink::AddToTrack,
                   video_sink_->AsWeakPtr()));
  }
}

void CastRtpStream::Stop() {
  audio_sink_.reset();
  video_sink_.reset();
}

bool CastRtpStream::IsAudio() const {
  return track_.source().type() == blink::WebMediaStreamSource::TypeAudio;
}