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 4b20c600c93c2dcc681dec64d32838d28405cb73..da8f742d29daf6e0091e627f70496254ace1e420 100644 |
--- a/chrome/renderer/extensions/cast_streaming_native_handler.cc |
+++ b/chrome/renderer/extensions/cast_streaming_native_handler.cc |
@@ -21,6 +21,7 @@ |
#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" |
@@ -33,6 +34,7 @@ |
#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" |
@@ -43,10 +45,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; |
@@ -54,36 +56,28 @@ using extensions::api::cast_streaming_udp_transport::IPEndPoint; |
namespace extensions { |
namespace { |
-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; |
-} |
+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; |
-namespace { |
bool HexDecode(const std::string& input, std::string* output) { |
std::vector<uint8_t> bytes; |
if (!base::HexStringToBytes(input, &bytes)) |
@@ -91,107 +85,189 @@ bool HexDecode(const std::string& input, std::string* output) { |
output->assign(reinterpret_cast<const char*>(&bytes[0]), bytes.size()); |
return true; |
} |
-} // 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)) { |
+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)) { |
isolate->ThrowException(v8::Exception::Error( |
v8::String::NewFromUtf8(isolate, kInvalidAesKey))); |
return false; |
} |
if (ext_params.aes_iv_mask && |
- !HexDecode(*ext_params.aes_iv_mask, &cast_params->aes_iv_mask)) { |
+ !HexDecode(*ext_params.aes_iv_mask, &config->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 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; |
+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)); |
} |
} // namespace |
@@ -399,13 +475,12 @@ void CastStreamingNativeHandler::GetSupportedParamsCastRtpStream( |
return; |
std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create()); |
- std::vector<CastRtpParams> cast_params = transport->GetSupportedParams(); |
+ std::vector<FrameSenderConfig> configs = transport->GetSupportedConfigs(); |
v8::Local<v8::Array> result = |
- v8::Array::New(args.GetIsolate(), |
- static_cast<int>(cast_params.size())); |
- for (size_t i = 0; i < cast_params.size(); ++i) { |
+ v8::Array::New(args.GetIsolate(), static_cast<int>(configs.size())); |
+ for (size_t i = 0; i < configs.size(); ++i) { |
RtpParams params; |
- FromCastRtpParams(cast_params[i], ¶ms); |
+ FromFrameSenderConfig(configs[i], ¶ms.payload); |
std::unique_ptr<base::DictionaryValue> params_value = params.ToValue(); |
result->Set( |
static_cast<int>(i), |
@@ -440,9 +515,9 @@ void CastStreamingNativeHandler::StartCastRtpStream( |
return; |
} |
- CastRtpParams cast_params; |
+ FrameSenderConfig config; |
v8::Isolate* isolate = context()->v8_context()->GetIsolate(); |
- if (!ToCastRtpParamsOrThrow(isolate, *params, &cast_params)) |
+ if (!ToFrameSenderConfigOrThrow(isolate, params->payload, &config)) |
return; |
base::Closure start_callback = |
@@ -457,7 +532,7 @@ void CastStreamingNativeHandler::StartCastRtpStream( |
base::Bind(&CastStreamingNativeHandler::CallErrorCallback, |
weak_factory_.GetWeakPtr(), |
transport_id); |
- transport->Start(cast_params, start_callback, stop_callback, error_callback); |
+ transport->Start(config, start_callback, stop_callback, error_callback); |
} |
void CastStreamingNativeHandler::StopCastRtpStream( |
@@ -816,7 +891,8 @@ void CastStreamingNativeHandler::StartCastRtpReceiver( |
media::AudioParameters::AUDIO_PCM_LINEAR, |
media::GuessChannelLayout(audio_config.channels), |
audio_config.rtp_timebase, // sampling rate |
- 16, audio_config.rtp_timebase / audio_config.target_frame_rate); |
+ 16, static_cast<int>(audio_config.rtp_timebase / |
+ audio_config.target_frame_rate)); |
if (!params.IsValid()) { |
args.GetIsolate()->ThrowException(v8::Exception::TypeError( |