| Index: chrome/renderer/extensions/cast_streaming_native_handler.cc
|
| diff --git a/chrome/renderer/extensions/cast_streaming_native_handler.cc b/chrome/renderer/extensions/cast_streaming_native_handler.cc
|
| index da8f742d29daf6e0091e627f70496254ace1e420..4b20c600c93c2dcc681dec64d32838d28405cb73 100644
|
| --- a/chrome/renderer/extensions/cast_streaming_native_handler.cc
|
| +++ b/chrome/renderer/extensions/cast_streaming_native_handler.cc
|
| @@ -21,7 +21,6 @@
|
| #include "base/memory/ptr_util.h"
|
| #include "base/single_thread_task_runner.h"
|
| #include "base/strings/string_number_conversions.h"
|
| -#include "base/sys_info.h"
|
| #include "base/threading/thread_task_runner_handle.h"
|
| #include "chrome/common/extensions/api/cast_streaming_receiver_session.h"
|
| #include "chrome/common/extensions/api/cast_streaming_rtp_stream.h"
|
| @@ -34,7 +33,6 @@
|
| #include "content/public/renderer/media_stream_utils.h"
|
| #include "extensions/renderer/script_context.h"
|
| #include "media/base/audio_parameters.h"
|
| -#include "media/base/limits.h"
|
| #include "net/base/host_port_pair.h"
|
| #include "net/base/ip_address.h"
|
| #include "third_party/WebKit/public/platform/WebMediaStream.h"
|
| @@ -45,10 +43,10 @@
|
| #include "url/gurl.h"
|
|
|
| using content::V8ValueConverter;
|
| -using media::cast::FrameSenderConfig;
|
|
|
| // Extension types.
|
| using extensions::api::cast_streaming_receiver_session::RtpReceiverParams;
|
| +using extensions::api::cast_streaming_rtp_stream::CodecSpecificParams;
|
| using extensions::api::cast_streaming_rtp_stream::RtpParams;
|
| using extensions::api::cast_streaming_rtp_stream::RtpPayloadParams;
|
| using extensions::api::cast_streaming_udp_transport::IPEndPoint;
|
| @@ -56,28 +54,36 @@
|
| namespace extensions {
|
|
|
| namespace {
|
| -
|
| -constexpr char kInvalidAesIvMask[] = "Invalid value for AES IV mask";
|
| -constexpr char kInvalidAesKey[] = "Invalid value for AES key";
|
| -constexpr char kInvalidAudioParams[] = "Invalid audio params";
|
| -constexpr char kInvalidDestination[] = "Invalid destination";
|
| -constexpr char kInvalidFPS[] = "Invalid FPS";
|
| -constexpr char kInvalidMediaStreamURL[] = "Invalid MediaStream URL";
|
| -constexpr char kInvalidRtpParams[] = "Invalid value for RTP params";
|
| -constexpr char kInvalidLatency[] = "Invalid value for max_latency. (0-1000)";
|
| -constexpr char kInvalidRtpTimebase[] = "Invalid rtp_timebase. (1000-1000000)";
|
| -constexpr char kInvalidStreamArgs[] = "Invalid stream arguments";
|
| -constexpr char kRtpStreamNotFound[] = "The RTP stream cannot be found";
|
| -constexpr char kUdpTransportNotFound[] = "The UDP transport cannot be found";
|
| -constexpr char kUnableToConvertArgs[] = "Unable to convert arguments";
|
| -constexpr char kUnableToConvertParams[] = "Unable to convert params";
|
| -constexpr char kCodecNameOpus[] = "OPUS";
|
| -constexpr char kCodecNameVp8[] = "VP8";
|
| -constexpr char kCodecNameH264[] = "H264";
|
| -
|
| -// To convert from kilobits per second to bits per second.
|
| -constexpr int kBitsPerKilobit = 1000;
|
| -
|
| +const char kInvalidAesIvMask[] = "Invalid value for AES IV mask";
|
| +const char kInvalidAesKey[] = "Invalid value for AES key";
|
| +const char kInvalidAudioParams[] = "Invalid audio params";
|
| +const char kInvalidDestination[] = "Invalid destination";
|
| +const char kInvalidFPS[] = "Invalid FPS";
|
| +const char kInvalidMediaStreamURL[] = "Invalid MediaStream URL";
|
| +const char kInvalidRtpParams[] = "Invalid value for RTP params";
|
| +const char kInvalidLatency[] = "Invalid value for max_latency. (0-1000)";
|
| +const char kInvalidRtpTimebase[] = "Invalid rtp_timebase. (1000-1000000)";
|
| +const char kInvalidStreamArgs[] = "Invalid stream arguments";
|
| +const char kRtpStreamNotFound[] = "The RTP stream cannot be found";
|
| +const char kUdpTransportNotFound[] = "The UDP transport cannot be found";
|
| +const char kUnableToConvertArgs[] = "Unable to convert arguments";
|
| +const char kUnableToConvertParams[] = "Unable to convert params";
|
| +
|
| +// These helper methods are used to convert between Extension API
|
| +// types and Cast types.
|
| +void ToCastCodecSpecificParams(const CodecSpecificParams& ext_params,
|
| + CastCodecSpecificParams* cast_params) {
|
| + cast_params->key = ext_params.key;
|
| + cast_params->value = ext_params.value;
|
| +}
|
| +
|
| +void FromCastCodecSpecificParams(const CastCodecSpecificParams& cast_params,
|
| + CodecSpecificParams* ext_params) {
|
| + ext_params->key = cast_params.key;
|
| + ext_params->value = cast_params.value;
|
| +}
|
| +
|
| +namespace {
|
| bool HexDecode(const std::string& input, std::string* output) {
|
| std::vector<uint8_t> bytes;
|
| if (!base::HexStringToBytes(input, &bytes))
|
| @@ -85,189 +91,107 @@
|
| output->assign(reinterpret_cast<const char*>(&bytes[0]), bytes.size());
|
| return true;
|
| }
|
| -
|
| -int NumberOfEncodeThreads() {
|
| - // Do not saturate CPU utilization just for encoding. On a lower-end system
|
| - // with only 1 or 2 cores, use only one thread for encoding. On systems with
|
| - // more cores, allow half of the cores to be used for encoding.
|
| - return std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2);
|
| -}
|
| -
|
| -bool ToFrameSenderConfigOrThrow(v8::Isolate* isolate,
|
| - const RtpPayloadParams& ext_params,
|
| - FrameSenderConfig* config) {
|
| - config->sender_ssrc = ext_params.ssrc;
|
| - config->receiver_ssrc = ext_params.feedback_ssrc;
|
| - if (config->sender_ssrc == config->receiver_ssrc) {
|
| - DVLOG(1) << "sender_ssrc " << config->sender_ssrc
|
| - << " cannot be equal to receiver_ssrc";
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - return false;
|
| - }
|
| - config->min_playout_delay = base::TimeDelta::FromMilliseconds(
|
| - ext_params.min_latency ? *ext_params.min_latency
|
| - : ext_params.max_latency);
|
| - config->max_playout_delay =
|
| - base::TimeDelta::FromMilliseconds(ext_params.max_latency);
|
| - config->animated_playout_delay = base::TimeDelta::FromMilliseconds(
|
| - ext_params.animated_latency ? *ext_params.animated_latency
|
| - : ext_params.max_latency);
|
| - if (config->min_playout_delay <= base::TimeDelta()) {
|
| - DVLOG(1) << "min_playout_delay " << config->min_playout_delay
|
| - << " must be greater than zero";
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - return false;
|
| - }
|
| - if (config->min_playout_delay > config->max_playout_delay) {
|
| - DVLOG(1) << "min_playout_delay " << config->min_playout_delay
|
| - << " must be less than or equal to max_palyout_delay";
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - return false;
|
| - }
|
| - if (config->animated_playout_delay < config->min_playout_delay ||
|
| - config->animated_playout_delay > config->max_playout_delay) {
|
| - DVLOG(1) << "animated_playout_delay " << config->animated_playout_delay
|
| - << " must be between (inclusive) the min and max playout delay";
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - return false;
|
| - }
|
| - if (ext_params.codec_name == kCodecNameOpus) {
|
| - config->rtp_payload_type = media::cast::RtpPayloadType::AUDIO_OPUS;
|
| - config->use_external_encoder = false;
|
| - config->rtp_timebase = ext_params.clock_rate
|
| - ? *ext_params.clock_rate
|
| - : media::cast::kDefaultAudioSamplingRate;
|
| - // Sampling rate must be one of the Opus-supported values.
|
| - switch (config->rtp_timebase) {
|
| - case 48000:
|
| - case 24000:
|
| - case 16000:
|
| - case 12000:
|
| - case 8000:
|
| - break;
|
| - default:
|
| - DVLOG(1) << "rtp_timebase " << config->rtp_timebase << " is invalid";
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - return false;
|
| - }
|
| - config->channels = ext_params.channels ? *ext_params.channels : 2;
|
| - if (config->channels != 1 && config->channels != 2) {
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - DVLOG(1) << "channels " << config->channels << " is invalid";
|
| - return false;
|
| - }
|
| - config->min_bitrate = config->start_bitrate = config->max_bitrate =
|
| - ext_params.max_bitrate ? (*ext_params.max_bitrate) * kBitsPerKilobit
|
| - : media::cast::kDefaultAudioEncoderBitrate;
|
| - config->max_frame_rate = 100; // 10ms audio frames.
|
| - config->codec = media::cast::CODEC_AUDIO_OPUS;
|
| - } else if (ext_params.codec_name == kCodecNameVp8 ||
|
| - ext_params.codec_name == kCodecNameH264) {
|
| - config->rtp_timebase = media::cast::kVideoFrequency;
|
| - config->channels = ext_params.channels ? *ext_params.channels : 1;
|
| - if (config->channels != 1) {
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - DVLOG(1) << "channels " << config->channels << " is invalid";
|
| - return false;
|
| - }
|
| - config->min_bitrate = ext_params.min_bitrate
|
| - ? (*ext_params.min_bitrate) * kBitsPerKilobit
|
| - : media::cast::kDefaultMinVideoBitrate;
|
| - config->max_bitrate = ext_params.max_bitrate
|
| - ? (*ext_params.max_bitrate) * kBitsPerKilobit
|
| - : media::cast::kDefaultMaxVideoBitrate;
|
| - if (config->min_bitrate > config->max_bitrate) {
|
| - DVLOG(1) << "min_bitrate " << config->min_bitrate << " is larger than "
|
| - << "max_bitrate " << config->max_bitrate;
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - return false;
|
| - }
|
| - config->start_bitrate = config->min_bitrate;
|
| - config->max_frame_rate = std::max(
|
| - 1.0, ext_params.max_frame_rate ? *ext_params.max_frame_rate : 0.0);
|
| - if (config->max_frame_rate > media::limits::kMaxFramesPerSecond) {
|
| - DVLOG(1) << "max_frame_rate " << config->max_frame_rate << " is invalid";
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - return false;
|
| - }
|
| - if (ext_params.codec_name == kCodecNameVp8) {
|
| - config->rtp_payload_type = media::cast::RtpPayloadType::VIDEO_VP8;
|
| - config->codec = media::cast::CODEC_VIDEO_VP8;
|
| - config->use_external_encoder =
|
| - CastRtpStream::IsHardwareVP8EncodingSupported();
|
| - } else {
|
| - config->rtp_payload_type = media::cast::RtpPayloadType::VIDEO_H264;
|
| - config->codec = media::cast::CODEC_VIDEO_H264;
|
| - config->use_external_encoder =
|
| - CastRtpStream::IsHardwareH264EncodingSupported();
|
| - }
|
| - if (!config->use_external_encoder)
|
| - config->video_codec_params.number_of_encode_threads =
|
| - NumberOfEncodeThreads();
|
| - } else {
|
| - DVLOG(1) << "codec_name " << ext_params.codec_name << " is invalid";
|
| - isolate->ThrowException(v8::Exception::Error(
|
| - v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
|
| - return false;
|
| - }
|
| - if (ext_params.aes_key && !HexDecode(*ext_params.aes_key, &config->aes_key)) {
|
| +} // namespace
|
| +
|
| +bool ToCastRtpPayloadParamsOrThrow(v8::Isolate* isolate,
|
| + const RtpPayloadParams& ext_params,
|
| + CastRtpPayloadParams* cast_params) {
|
| + cast_params->max_latency_ms = ext_params.max_latency;
|
| + cast_params->min_latency_ms =
|
| + ext_params.min_latency ? *ext_params.min_latency : ext_params.max_latency;
|
| + cast_params->animated_latency_ms = ext_params.animated_latency
|
| + ? *ext_params.animated_latency
|
| + : ext_params.max_latency;
|
| + cast_params->codec_name = ext_params.codec_name;
|
| + if (cast_params->codec_name == "OPUS") {
|
| + cast_params->payload_type = media::cast::RtpPayloadType::AUDIO_OPUS;
|
| + } else if (cast_params->codec_name == "PCM16") {
|
| + cast_params->payload_type = media::cast::RtpPayloadType::AUDIO_PCM16;
|
| + } else if (cast_params->codec_name == "AAC") {
|
| + cast_params->payload_type = media::cast::RtpPayloadType::AUDIO_AAC;
|
| + } else if (cast_params->codec_name == "VP8") {
|
| + cast_params->payload_type = media::cast::RtpPayloadType::VIDEO_VP8;
|
| + } else if (cast_params->codec_name == "H264") {
|
| + cast_params->payload_type = media::cast::RtpPayloadType::VIDEO_H264;
|
| + }
|
| + cast_params->ssrc = ext_params.ssrc;
|
| + cast_params->feedback_ssrc = ext_params.feedback_ssrc;
|
| + cast_params->clock_rate = ext_params.clock_rate ? *ext_params.clock_rate : 0;
|
| + cast_params->min_bitrate =
|
| + ext_params.min_bitrate ? *ext_params.min_bitrate : 0;
|
| + cast_params->max_bitrate =
|
| + ext_params.max_bitrate ? *ext_params.max_bitrate : 0;
|
| + cast_params->channels = ext_params.channels ? *ext_params.channels : 0;
|
| + cast_params->max_frame_rate =
|
| + ext_params.max_frame_rate ? *ext_params.max_frame_rate : 0.0;
|
| + if (ext_params.aes_key &&
|
| + !HexDecode(*ext_params.aes_key, &cast_params->aes_key)) {
|
| isolate->ThrowException(v8::Exception::Error(
|
| v8::String::NewFromUtf8(isolate, kInvalidAesKey)));
|
| return false;
|
| }
|
| if (ext_params.aes_iv_mask &&
|
| - !HexDecode(*ext_params.aes_iv_mask, &config->aes_iv_mask)) {
|
| + !HexDecode(*ext_params.aes_iv_mask, &cast_params->aes_iv_mask)) {
|
| isolate->ThrowException(v8::Exception::Error(
|
| v8::String::NewFromUtf8(isolate, kInvalidAesIvMask)));
|
| return false;
|
| }
|
| + for (size_t i = 0; i < ext_params.codec_specific_params.size(); ++i) {
|
| + CastCodecSpecificParams cast_codec_params;
|
| + ToCastCodecSpecificParams(ext_params.codec_specific_params[i],
|
| + &cast_codec_params);
|
| + cast_params->codec_specific_params.push_back(cast_codec_params);
|
| + }
|
| return true;
|
| }
|
|
|
| -void FromFrameSenderConfig(const FrameSenderConfig& config,
|
| - RtpPayloadParams* ext_params) {
|
| - ext_params->payload_type = static_cast<int>(config.rtp_payload_type);
|
| - ext_params->max_latency = config.max_playout_delay.InMilliseconds();
|
| - ext_params->min_latency.reset(
|
| - new int(config.min_playout_delay.InMilliseconds()));
|
| - ext_params->animated_latency.reset(
|
| - new int(config.animated_playout_delay.InMilliseconds()));
|
| - switch (config.codec) {
|
| - case media::cast::CODEC_AUDIO_OPUS:
|
| - ext_params->codec_name = kCodecNameOpus;
|
| - break;
|
| - case media::cast::CODEC_VIDEO_VP8:
|
| - ext_params->codec_name = kCodecNameVp8;
|
| - break;
|
| - case media::cast::CODEC_VIDEO_H264:
|
| - ext_params->codec_name = kCodecNameH264;
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - }
|
| - ext_params->ssrc = config.sender_ssrc;
|
| - ext_params->feedback_ssrc = config.receiver_ssrc;
|
| - if (config.rtp_timebase)
|
| - ext_params->clock_rate.reset(new int(config.rtp_timebase));
|
| - if (config.min_bitrate)
|
| - ext_params->min_bitrate.reset(
|
| - new int(config.min_bitrate / kBitsPerKilobit));
|
| - if (config.max_bitrate)
|
| - ext_params->max_bitrate.reset(
|
| - new int(config.max_bitrate / kBitsPerKilobit));
|
| - if (config.channels)
|
| - ext_params->channels.reset(new int(config.channels));
|
| - if (config.max_frame_rate > 0.0)
|
| - ext_params->max_frame_rate.reset(new double(config.max_frame_rate));
|
| +void FromCastRtpPayloadParams(const CastRtpPayloadParams& cast_params,
|
| + RtpPayloadParams* ext_params) {
|
| + ext_params->payload_type = static_cast<int>(cast_params.payload_type);
|
| + ext_params->max_latency = cast_params.max_latency_ms;
|
| + ext_params->min_latency.reset(new int(cast_params.min_latency_ms));
|
| + ext_params->animated_latency.reset(new int(cast_params.animated_latency_ms));
|
| + ext_params->codec_name = cast_params.codec_name;
|
| + ext_params->ssrc = cast_params.ssrc;
|
| + ext_params->feedback_ssrc = cast_params.feedback_ssrc;
|
| + if (cast_params.clock_rate)
|
| + ext_params->clock_rate.reset(new int(cast_params.clock_rate));
|
| + if (cast_params.min_bitrate)
|
| + ext_params->min_bitrate.reset(new int(cast_params.min_bitrate));
|
| + if (cast_params.max_bitrate)
|
| + ext_params->max_bitrate.reset(new int(cast_params.max_bitrate));
|
| + if (cast_params.channels)
|
| + ext_params->channels.reset(new int(cast_params.channels));
|
| + if (cast_params.max_frame_rate > 0.0)
|
| + ext_params->max_frame_rate.reset(new double(cast_params.max_frame_rate));
|
| + for (size_t i = 0; i < cast_params.codec_specific_params.size(); ++i) {
|
| + CodecSpecificParams ext_codec_params;
|
| + FromCastCodecSpecificParams(cast_params.codec_specific_params[i],
|
| + &ext_codec_params);
|
| + ext_params->codec_specific_params.push_back(std::move(ext_codec_params));
|
| + }
|
| +}
|
| +
|
| +void FromCastRtpParams(const CastRtpParams& cast_params,
|
| + RtpParams* ext_params) {
|
| + std::copy(cast_params.rtcp_features.begin(),
|
| + cast_params.rtcp_features.end(),
|
| + std::back_inserter(ext_params->rtcp_features));
|
| + FromCastRtpPayloadParams(cast_params.payload, &ext_params->payload);
|
| +}
|
| +
|
| +bool ToCastRtpParamsOrThrow(v8::Isolate* isolate,
|
| + const RtpParams& ext_params,
|
| + CastRtpParams* cast_params) {
|
| + std::copy(ext_params.rtcp_features.begin(),
|
| + ext_params.rtcp_features.end(),
|
| + std::back_inserter(cast_params->rtcp_features));
|
| + if (!ToCastRtpPayloadParamsOrThrow(isolate,
|
| + ext_params.payload,
|
| + &cast_params->payload)) {
|
| + return false;
|
| + }
|
| + return true;
|
| }
|
|
|
| } // namespace
|
| @@ -475,12 +399,13 @@
|
| return;
|
|
|
| std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
|
| - std::vector<FrameSenderConfig> configs = transport->GetSupportedConfigs();
|
| + std::vector<CastRtpParams> cast_params = transport->GetSupportedParams();
|
| v8::Local<v8::Array> result =
|
| - v8::Array::New(args.GetIsolate(), static_cast<int>(configs.size()));
|
| - for (size_t i = 0; i < configs.size(); ++i) {
|
| + v8::Array::New(args.GetIsolate(),
|
| + static_cast<int>(cast_params.size()));
|
| + for (size_t i = 0; i < cast_params.size(); ++i) {
|
| RtpParams params;
|
| - FromFrameSenderConfig(configs[i], ¶ms.payload);
|
| + FromCastRtpParams(cast_params[i], ¶ms);
|
| std::unique_ptr<base::DictionaryValue> params_value = params.ToValue();
|
| result->Set(
|
| static_cast<int>(i),
|
| @@ -515,9 +440,9 @@
|
| return;
|
| }
|
|
|
| - FrameSenderConfig config;
|
| + CastRtpParams cast_params;
|
| v8::Isolate* isolate = context()->v8_context()->GetIsolate();
|
| - if (!ToFrameSenderConfigOrThrow(isolate, params->payload, &config))
|
| + if (!ToCastRtpParamsOrThrow(isolate, *params, &cast_params))
|
| return;
|
|
|
| base::Closure start_callback =
|
| @@ -532,7 +457,7 @@
|
| base::Bind(&CastStreamingNativeHandler::CallErrorCallback,
|
| weak_factory_.GetWeakPtr(),
|
| transport_id);
|
| - transport->Start(config, start_callback, stop_callback, error_callback);
|
| + transport->Start(cast_params, start_callback, stop_callback, error_callback);
|
| }
|
|
|
| void CastStreamingNativeHandler::StopCastRtpStream(
|
| @@ -891,8 +816,7 @@
|
| media::AudioParameters::AUDIO_PCM_LINEAR,
|
| media::GuessChannelLayout(audio_config.channels),
|
| audio_config.rtp_timebase, // sampling rate
|
| - 16, static_cast<int>(audio_config.rtp_timebase /
|
| - audio_config.target_frame_rate));
|
| + 16, audio_config.rtp_timebase / audio_config.target_frame_rate);
|
|
|
| if (!params.IsValid()) {
|
| args.GetIsolate()->ThrowException(v8::Exception::TypeError(
|
|
|