#include "chrome/renderer/media/cast_rtp_stream.h"
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
+#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
+#include "chrome/common/chrome_switches.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 "media/cast/cast_config.h"
#include "media/cast/cast_defines.h"
#include "media/cast/cast_sender.h"
-#include "media/cast/transport/cast_transport_config.h"
+#include "media/cast/net/cast_transport_config.h"
#include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
#include "ui/gfx/geometry/size.h"
CastRtpPayloadParams DefaultOpusPayload() {
CastRtpPayloadParams payload;
- payload.ssrc = 1;
- payload.feedback_ssrc = 2;
payload.payload_type = 127;
payload.max_latency_ms = media::cast::kDefaultRtpMaxDelayMs;
- payload.codec_name = kCodecNameOpus;
- payload.clock_rate = 48000;
- payload.channels = 2;
+ payload.ssrc = 1;
+ payload.feedback_ssrc = 2;
+ payload.clock_rate = media::cast::kDefaultAudioSamplingRate;
// The value is 0 which means VBR.
payload.min_bitrate = payload.max_bitrate =
media::cast::kDefaultAudioEncoderBitrate;
+ payload.channels = 2;
+ payload.max_frame_rate = 100; // 10 ms audio frames
+ payload.codec_name = kCodecNameOpus;
return payload;
}
CastRtpPayloadParams DefaultVp8Payload() {
CastRtpPayloadParams payload;
- payload.ssrc = 11;
- payload.feedback_ssrc = 12;
payload.payload_type = 96;
payload.max_latency_ms = media::cast::kDefaultRtpMaxDelayMs;
- payload.codec_name = kCodecNameVp8;
- payload.clock_rate = 90000;
+ payload.ssrc = 11;
+ payload.feedback_ssrc = 12;
+ payload.clock_rate = media::cast::kVideoFrequency;
+ payload.max_bitrate = 2000;
+ payload.min_bitrate = 50;
+ payload.channels = 1;
+ payload.max_frame_rate = media::cast::kDefaultMaxFrameRate;
payload.width = 1280;
payload.height = 720;
- payload.min_bitrate = 50;
- payload.max_bitrate = 2000;
+ payload.codec_name = kCodecNameVp8;
return payload;
}
CastRtpPayloadParams payload;
// TODO(hshi): set different ssrc/rtpPayloadType values for H264 and VP8
// once b/13696137 is fixed.
- payload.ssrc = 11;
- payload.feedback_ssrc = 12;
payload.payload_type = 96;
payload.max_latency_ms = media::cast::kDefaultRtpMaxDelayMs;
- payload.codec_name = kCodecNameH264;
- payload.clock_rate = 90000;
+ payload.ssrc = 11;
+ payload.feedback_ssrc = 12;
+ payload.clock_rate = media::cast::kVideoFrequency;
+ payload.max_bitrate = 2000;
+ payload.min_bitrate = 50;
+ payload.channels = 1;
+ payload.max_frame_rate = media::cast::kDefaultMaxFrameRate;
payload.width = 1280;
payload.height = 720;
- payload.min_bitrate = 50;
- payload.max_bitrate = 2000;
+ payload.codec_name = kCodecNameH264;
return payload;
}
bool IsHardwareVP8EncodingSupported() {
+ const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+ if (cmd_line->HasSwitch(switches::kDisableCastStreamingHWEncoding)) {
+ VLOG(1) << "Disabled hardware VP8 support for Cast Streaming.";
+ return false;
+ }
+
// Query for hardware VP8 encoder support.
std::vector<media::VideoEncodeAccelerator::SupportedProfile> vea_profiles =
content::GetSupportedVideoEncodeAcceleratorProfiles();
}
bool IsHardwareH264EncodingSupported() {
+ const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+ if (cmd_line->HasSwitch(switches::kDisableCastStreamingHWEncoding)) {
+ VLOG(1) << "Disabled hardware h264 support for Cast Streaming.";
+ return false;
+ }
+
// Query for hardware H.264 encoder support.
std::vector<media::VideoEncodeAccelerator::SupportedProfile> vea_profiles =
content::GetSupportedVideoEncodeAcceleratorProfiles();
bool ToAudioSenderConfig(const CastRtpParams& params,
AudioSenderConfig* config) {
- config->rtp_config.ssrc = params.payload.ssrc;
+ config->ssrc = params.payload.ssrc;
config->incoming_feedback_ssrc = params.payload.feedback_ssrc;
- config->rtp_config.payload_type = params.payload.payload_type;
- config->rtp_config.max_delay_ms = params.payload.max_latency_ms;
- config->rtp_config.aes_key = params.payload.aes_key;
- config->rtp_config.aes_iv_mask = params.payload.aes_iv_mask;
+ if (config->ssrc == config->incoming_feedback_ssrc)
+ return false;
+ config->min_playout_delay =
+ base::TimeDelta::FromMilliseconds(
+ params.payload.min_latency_ms ?
+ params.payload.min_latency_ms :
+ params.payload.max_latency_ms);
+ config->max_playout_delay =
+ base::TimeDelta::FromMilliseconds(params.payload.max_latency_ms);
+ if (config->min_playout_delay <= base::TimeDelta())
+ return false;
+ if (config->min_playout_delay > config->max_playout_delay)
+ return false;
+ config->rtp_payload_type = params.payload.payload_type;
config->use_external_encoder = false;
config->frequency = params.payload.clock_rate;
+ if (config->frequency < 8000)
+ return false;
config->channels = params.payload.channels;
+ if (config->channels < 1)
+ return false;
config->bitrate = params.payload.max_bitrate * kBitrateMultiplier;
- config->codec = media::cast::transport::kPcm16;
if (params.payload.codec_name == kCodecNameOpus)
- config->codec = media::cast::transport::kOpus;
+ config->codec = media::cast::CODEC_AUDIO_OPUS;
else
return false;
+ config->aes_key = params.payload.aes_key;
+ config->aes_iv_mask = params.payload.aes_iv_mask;
return true;
}
bool ToVideoSenderConfig(const CastRtpParams& params,
VideoSenderConfig* config) {
- config->rtp_config.ssrc = params.payload.ssrc;
+ config->ssrc = params.payload.ssrc;
config->incoming_feedback_ssrc = params.payload.feedback_ssrc;
- config->rtp_config.payload_type = params.payload.payload_type;
- config->rtp_config.max_delay_ms = params.payload.max_latency_ms;
- config->rtp_config.aes_key = params.payload.aes_key;
- config->rtp_config.aes_iv_mask = params.payload.aes_iv_mask;
- config->use_external_encoder = false;
+ if (config->ssrc == config->incoming_feedback_ssrc)
+ return false;
+ config->min_playout_delay =
+ base::TimeDelta::FromMilliseconds(
+ params.payload.min_latency_ms ?
+ params.payload.min_latency_ms :
+ params.payload.max_latency_ms);
+ config->max_playout_delay =
+ base::TimeDelta::FromMilliseconds(params.payload.max_latency_ms);
+ if (config->min_playout_delay <= base::TimeDelta())
+ return false;
+ if (config->min_playout_delay > config->max_playout_delay)
+ return false;
+ config->rtp_payload_type = params.payload.payload_type;
config->width = params.payload.width;
config->height = params.payload.height;
+ if (config->width < 2 || config->height < 2)
+ return false;
config->min_bitrate = config->start_bitrate =
params.payload.min_bitrate * kBitrateMultiplier;
config->max_bitrate = params.payload.max_bitrate * kBitrateMultiplier;
+ if (config->min_bitrate > config->max_bitrate)
+ return false;
+ config->start_bitrate = config->min_bitrate;
+ config->max_frame_rate = static_cast<int>(
+ std::max(1.0, params.payload.max_frame_rate) + 0.5);
+ if (config->max_frame_rate > 120)
+ return false;
if (params.payload.codec_name == kCodecNameVp8) {
config->use_external_encoder = IsHardwareVP8EncodingSupported();
- config->codec = media::cast::transport::kVp8;
+ config->codec = media::cast::CODEC_VIDEO_VP8;
} else if (params.payload.codec_name == kCodecNameH264) {
config->use_external_encoder = IsHardwareH264EncodingSupported();
- config->codec = media::cast::transport::kH264;
+ config->codec = media::cast::CODEC_VIDEO_H264;
} else {
return false;
}
if (!config->use_external_encoder) {
config->number_of_encode_threads = NumberOfEncodeThreads();
}
+ config->aes_key = params.payload.aes_key;
+ config->aes_iv_mask = params.payload.aes_iv_mask;
return true;
}
public content::MediaStreamVideoSink {
public:
// |track| provides data for this sink.
- // |expected_coded_size| is the expected dimension of the video frame.
+ // |expected_natural_size| is the expected dimension of the video frame.
// |error_callback| is called if video formats don't match.
CastVideoSink(const blink::WebMediaStreamTrack& track,
- const gfx::Size& expected_coded_size,
+ const gfx::Size& expected_natural_size,
const CastRtpStream::ErrorCallback& error_callback)
: track_(track),
sink_added_(false),
- expected_coded_size_(expected_coded_size),
+ expected_natural_size_(expected_natural_size),
error_callback_(error_callback) {}
- virtual ~CastVideoSink() {
+ ~CastVideoSink() override {
if (sink_added_)
RemoveFromVideoTrack(this, track_);
}
// This static method is used to forward video frames to |frame_input|.
static void OnVideoFrame(
// These parameters are already bound when callback is created.
- const gfx::Size& expected_coded_size,
+ const gfx::Size& expected_natural_size,
const CastRtpStream::ErrorCallback& error_callback,
const scoped_refptr<media::cast::VideoFrameInput> frame_input,
// These parameters are passed for each frame.
const scoped_refptr<media::VideoFrame>& frame,
- const media::VideoCaptureFormat& format) {
- if (frame->coded_size() != expected_coded_size) {
- error_callback.Run("Video frame resolution does not match config.");
+ const media::VideoCaptureFormat& format,
+ const base::TimeTicks& estimated_capture_time) {
+ if (frame->natural_size() != expected_natural_size) {
+ error_callback.Run(
+ base::StringPrintf("Video frame resolution does not match config."
+ " Expected %dx%d. Got %dx%d.",
+ expected_natural_size.width(),
+ expected_natural_size.height(),
+ frame->natural_size().width(),
+ frame->natural_size().height()));
return;
}
- const base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeTicks timestamp;
+ if (estimated_capture_time.is_null())
+ timestamp = base::TimeTicks::Now();
+ else
+ timestamp = estimated_capture_time;
// Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
TRACE_EVENT_INSTANT2(
"cast_perf_test", "MediaStreamVideoSink::OnVideoFrame",
TRACE_EVENT_SCOPE_THREAD,
- "timestamp", now.ToInternalValue(),
+ "timestamp", timestamp.ToInternalValue(),
"time_delta", frame->timestamp().ToInternalValue());
- frame_input->InsertRawVideoFrame(frame, now);
+ frame_input->InsertRawVideoFrame(frame, timestamp);
}
// Attach this sink to a video track represented by |track_|.
this,
base::Bind(
&CastVideoSink::OnVideoFrame,
- expected_coded_size_,
+ expected_natural_size_,
error_callback_,
frame_input),
track_);
private:
blink::WebMediaStreamTrack track_;
bool sink_added_;
- gfx::Size expected_coded_size_;
+ gfx::Size expected_natural_size_;
CastRtpStream::ErrorCallback error_callback_;
DISALLOW_COPY_AND_ASSIGN(CastVideoSink);
output_sample_rate_(output_sample_rate),
input_preroll_(0) {}
- virtual ~CastAudioSink() {
+ ~CastAudioSink() override {
if (sink_added_)
RemoveFromAudioTrack(this, track_);
}
// Called on real-time audio thread.
// content::MediaStreamAudioSink implementation.
- virtual void OnData(const int16* audio_data,
- int sample_rate,
- int number_of_channels,
- int number_of_frames) OVERRIDE {
+ void OnData(const int16* audio_data,
+ int sample_rate,
+ int number_of_channels,
+ int number_of_frames) override {
scoped_ptr<media::AudioBus> input_bus;
if (resampler_) {
input_bus = ResampleData(
}
// Called on real-time audio thread.
- virtual void OnSetFormat(const media::AudioParameters& params) OVERRIDE {
+ void OnSetFormat(const media::AudioParameters& params) override {
if (params.sample_rate() == output_sample_rate_)
return;
fifo_.reset(new media::AudioFifo(
CastRtpPayloadParams::CastRtpPayloadParams()
: payload_type(0),
max_latency_ms(0),
+ min_latency_ms(0),
ssrc(0),
feedback_ssrc(0),
clock_rate(0),
max_bitrate(0),
min_bitrate(0),
channels(0),
+ max_frame_rate(0.0),
width(0),
height(0) {}
const base::Closure& start_callback,
const base::Closure& stop_callback,
const ErrorCallback& error_callback) {
- VLOG(1) << "CastRtpStream::Start = " << (IsAudio() ? "audio" : "video");
+ VLOG(1) << "CastRtpStream::Start = " << (IsAudio() ? "audio" : "video");
stop_callback_ = stop_callback;
error_callback_ = error_callback;
}
void CastRtpStream::Stop() {
- VLOG(1) << "CastRtpStream::Stop = " << (IsAudio() ? "audio" : "video");
+ VLOG(1) << "CastRtpStream::Stop = " << (IsAudio() ? "audio" : "video");
audio_sink_.reset();
video_sink_.reset();
- stop_callback_.Run();
+ if (!stop_callback_.is_null())
+ stop_callback_.Run();
}
void CastRtpStream::ToggleLogging(bool enable) {
+ VLOG(1) << "CastRtpStream::ToggleLogging(" << enable << ") = "
+ << (IsAudio() ? "audio" : "video");
cast_session_->ToggleLogging(IsAudio(), enable);
}
void CastRtpStream::GetRawEvents(
- const base::Callback<void(scoped_ptr<base::BinaryValue>)>& callback) {
- cast_session_->GetEventLogsAndReset(IsAudio(), callback);
+ const base::Callback<void(scoped_ptr<base::BinaryValue>)>& callback,
+ const std::string& extra_data) {
+ VLOG(1) << "CastRtpStream::GetRawEvents = "
+ << (IsAudio() ? "audio" : "video");
+ cast_session_->GetEventLogsAndReset(IsAudio(), extra_data, callback);
}
void CastRtpStream::GetStats(
const base::Callback<void(scoped_ptr<base::DictionaryValue>)>& callback) {
+ VLOG(1) << "CastRtpStream::GetStats = "
+ << (IsAudio() ? "audio" : "video");
cast_session_->GetStatsAndReset(IsAudio(), callback);
}
}
void CastRtpStream::DidEncounterError(const std::string& message) {
+ VLOG(1) << "CastRtpStream::DidEncounterError(" << message << ") = "
+ << (IsAudio() ? "audio" : "video");
// Save the WeakPtr first because the error callback might delete this object.
base::WeakPtr<CastRtpStream> ptr = weak_factory_.GetWeakPtr();
error_callback_.Run(message);