Chromium Code Reviews| 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 a10a15446d58ba9b441b07204e2cdd3e4137e573..50a1ace69f8cbb4a1c32848044771146e7d6d810 100644 |
| --- a/chrome/renderer/extensions/cast_streaming_native_handler.cc |
| +++ b/chrome/renderer/extensions/cast_streaming_native_handler.cc |
| @@ -10,20 +10,29 @@ |
| #include "base/logging.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| +#include "chrome/common/extensions/api/cast_streaming_receiver_session.h" |
| #include "chrome/common/extensions/api/cast_streaming_rtp_stream.h" |
| #include "chrome/common/extensions/api/cast_streaming_udp_transport.h" |
| +#include "chrome/renderer/media/cast_receiver_session.h" |
| #include "chrome/renderer/media/cast_rtp_stream.h" |
| #include "chrome/renderer/media/cast_session.h" |
| #include "chrome/renderer/media/cast_udp_transport.h" |
| #include "content/public/child/v8_value_converter.h" |
| +#include "content/public/renderer/media_stream_api.h" |
| #include "extensions/renderer/script_context.h" |
| +#include "media/audio/audio_parameters.h" |
| #include "net/base/host_port_pair.h" |
| +#include "third_party/WebKit/public/platform/WebMediaStream.h" |
| #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" |
| +#include "third_party/WebKit/public/platform/WebURL.h" |
| #include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h" |
| +#include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" |
| +#include "url/gurl.h" |
| using content::V8ValueConverter; |
| // 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; |
| @@ -32,13 +41,16 @@ using extensions::api::cast_streaming_udp_transport::IPEndPoint; |
| namespace extensions { |
| namespace { |
| -const char kRtpStreamNotFound[] = "The RTP stream cannot be found"; |
| -const char kUdpTransportNotFound[] = "The UDP transport cannot be found"; |
| +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 kInvalidAesKey[] = "Invalid value for AES key"; |
| -const char kInvalidAesIvMask[] = "Invalid value for AES IV mask"; |
| 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"; |
| @@ -199,6 +211,9 @@ CastStreamingNativeHandler::CastStreamingNativeHandler(ScriptContext* context) |
| RouteFunction("GetStats", |
| base::Bind(&CastStreamingNativeHandler::GetStats, |
| base::Unretained(this))); |
| + RouteFunction("StartCastRtpReceiver", |
| + base::Bind(&CastStreamingNativeHandler::StartCastRtpReceiver, |
| + base::Unretained(this))); |
| } |
| CastStreamingNativeHandler::~CastStreamingNativeHandler() { |
| @@ -440,28 +455,14 @@ void CastStreamingNativeHandler::SetDestinationCastUdpTransport( |
| if (!transport) |
| return; |
| - scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); |
| - scoped_ptr<base::Value> destination_value( |
| - converter->FromV8Value(args[1], context()->v8_context())); |
| - if (!destination_value) { |
| - args.GetIsolate()->ThrowException(v8::Exception::TypeError( |
| - v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs))); |
| + net::IPEndPoint dest; |
| + if (!IPEndPointFromArg(args.GetIsolate(), |
| + args[1], |
| + false, |
| + &dest)) { |
| return; |
| } |
| - scoped_ptr<IPEndPoint> destination = |
| - IPEndPoint::FromValue(*destination_value); |
| - if (!destination) { |
| - args.GetIsolate()->ThrowException(v8::Exception::TypeError( |
| - v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination))); |
| - return; |
| - } |
| - net::IPAddressNumber ip; |
| - if (!net::ParseIPLiteralToNumber(destination->address, &ip)) { |
| - args.GetIsolate()->ThrowException(v8::Exception::TypeError( |
| - v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination))); |
| - return; |
| - } |
| - transport->SetDestination(net::IPEndPoint(ip, destination->port)); |
| + transport->SetDestination(dest); |
| } |
| void CastStreamingNativeHandler::SetOptionsCastUdpTransport( |
| @@ -616,4 +617,209 @@ CastUdpTransport* CastStreamingNativeHandler::GetUdpTransportOrThrow( |
| return NULL; |
| } |
| +bool CastStreamingNativeHandler::FrameReceiverFromArg( |
| + v8::Isolate* isolate, |
| + const v8::Handle<v8::Value>& arg, |
| + media::cast::FrameReceiverConfig* config) { |
| + |
| + scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); |
| + scoped_ptr<base::Value> params_value( |
| + converter->FromV8Value(arg, context()->v8_context())); |
| + if (!params_value) { |
| + isolate->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(isolate, kUnableToConvertParams))); |
| + return false; |
| + } |
| + scoped_ptr<RtpReceiverParams> params = |
| + RtpReceiverParams::FromValue(*params_value); |
| + if (!params) { |
| + isolate->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(isolate, kInvalidRtpParams))); |
| + return false; |
| + } |
| + |
| + config->feedback_ssrc = params->feedback_ssrc; |
| + config->incoming_ssrc = params->ssrc; |
| + config->rtp_max_delay_ms = params->max_latency; |
|
miu
2015/02/26 06:23:34
Please range-check this value, and throw a v8 exce
hubbe
2015/02/28 00:17:12
Done.
|
| + config->rtp_payload_type = params->payload_type; |
| + config->channels = params->channels ? *params->channels : 0; |
| + if (params->codec_name == "OPUS") { |
| + config->codec = media::cast::CODEC_AUDIO_OPUS; |
| + config->rtp_timebase = 48000; |
| + } else if (params->codec_name == "PCM16") { |
| + config->codec = media::cast::CODEC_AUDIO_PCM16; |
| + config->rtp_timebase = 48000; |
| + } else if (params->codec_name == "AAC") { |
| + config->codec = media::cast::CODEC_AUDIO_AAC; |
| + config->rtp_timebase = 48000; |
| + } else if (params->codec_name == "VP8") { |
| + config->codec = media::cast::CODEC_VIDEO_VP8; |
| + config->rtp_timebase = 90000; |
| + } else if (params->codec_name == "H264") { |
| + config->codec = media::cast::CODEC_VIDEO_H264; |
| + config->rtp_timebase = 90000; |
| + } |
| + config->rtp_timebase = params->clock_rate ? *params->clock_rate : 0; |
|
miu
2015/02/26 06:23:34
This overwrites the previous assignments of config
hubbe
2015/02/28 00:17:11
Done.
|
| + if (params->aes_key && |
| + !HexDecode(*params->aes_key, &config->aes_key)) { |
| + isolate->ThrowException(v8::Exception::Error( |
| + v8::String::NewFromUtf8(isolate, kInvalidAesKey))); |
| + return false; |
| + } |
| + if (params->aes_iv_mask && |
| + !HexDecode(*params->aes_iv_mask, &config->aes_iv_mask)) { |
| + isolate->ThrowException(v8::Exception::Error( |
| + v8::String::NewFromUtf8(isolate, kInvalidAesIvMask))); |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +bool CastStreamingNativeHandler::IPEndPointFromArg( |
| + v8::Isolate* isolate, |
| + const v8::Handle<v8::Value>& arg, |
| + bool empty_ok, |
| + net::IPEndPoint* ip_endpoint) { |
| + scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); |
| + scoped_ptr<base::Value> destination_value( |
| + converter->FromV8Value(arg, context()->v8_context())); |
| + if (!destination_value) { |
| + isolate->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(isolate, kInvalidAesIvMask))); |
| + return false; |
| + } |
| + scoped_ptr<IPEndPoint> destination = |
| + IPEndPoint::FromValue(*destination_value); |
| + if (!destination) { |
| + isolate->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(isolate, kInvalidDestination))); |
| + return false; |
| + } |
| + net::IPAddressNumber ip; |
| + if (destination->address != "" || !empty_ok) { |
| + if (!net::ParseIPLiteralToNumber(destination->address, &ip)) { |
| + isolate->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(isolate, kInvalidDestination))); |
| + return false; |
| + } |
| + } |
| + *ip_endpoint = net::IPEndPoint(ip, destination->port); |
| + return true; |
| +} |
| + |
| +void CastStreamingNativeHandler::StartCastRtpReceiver( |
| + const v8::FunctionCallbackInfo<v8::Value>& args) { |
| + if (args.Length() < 8 || args.Length() > 9 || |
| + !args[0]->IsObject() || |
| + !args[1]->IsObject() || |
| + !args[2]->IsObject() || |
| + !args[3]->IsObject() || |
| + !args[4]->IsInt32() || |
| + !args[5]->IsInt32() || |
| + !args[6]->IsNumber() || |
| + !args[7]->IsString()) { |
| + args.GetIsolate()->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(args.GetIsolate(), "ARG!!!"))); |
|
Alpha Left Google
2015/02/25 20:37:28
Should be a more user friendly string.
hubbe
2015/02/28 00:17:11
OOps, fixed.
|
| + return; |
| + } |
| + |
| + v8::Isolate* isolate = context()->v8_context()->GetIsolate(); |
| + |
| + scoped_refptr<CastReceiverSession> session( |
| + new CastReceiverSession()); |
| + media::cast::FrameReceiverConfig audio_config; |
| + media::cast::FrameReceiverConfig video_config; |
| + net::IPEndPoint local_endpoint; |
| + net::IPEndPoint remote_endpoint; |
| + |
| + if (!FrameReceiverFromArg(isolate, args[0], &audio_config) || |
| + !FrameReceiverFromArg(isolate, args[1], &video_config) || |
| + !IPEndPointFromArg(isolate, args[2], true, &local_endpoint) || |
| + !IPEndPointFromArg(isolate, args[3], true, &remote_endpoint)) { |
| + return; |
|
miu
2015/02/26 06:23:34
Throw error exception here?
hubbe
2015/02/28 00:17:12
An exception will have already been raised in this
|
| + } |
| + |
| + const std::string url = *v8::String::Utf8Value(args[7]); |
| + blink::WebMediaStream stream = |
| + blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); |
| + |
| + if (stream.isNull()) { |
| + args.GetIsolate()->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(args.GetIsolate(), kInvalidMediaStreamURL))); |
| + return; |
| + } |
| + |
| + const int width = args[4]->ToInt32(args.GetIsolate())->Value(); |
| + const int height = args[5]->ToInt32(args.GetIsolate())->Value(); |
| + const double fps = args[6]->NumberValue(); |
| + |
| + if (fps <= 1) { |
| + args.GetIsolate()->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(args.GetIsolate(), kInvalidFPS))); |
| + return; |
| + } |
| + |
| + media::VideoCaptureFormat capture_format( |
| + gfx::Size(width, height), |
| + fps, |
| + media::PIXEL_FORMAT_I420); |
| + |
| + video_config.target_frame_rate = fps; |
|
Alpha Left Google
2015/02/25 20:37:28
I think the fps parameter in VideoCaptureFormat ha
miu
2015/02/26 06:23:34
Doesn't a Cast Receiver have NO concept of frame r
hubbe
2015/02/28 00:17:12
That might be true, but I don't actually know how
hubbe
2015/02/28 00:17:12
Unfortunately capture_format.IsValid() must be tru
|
| + audio_config.target_frame_rate = 100; |
| + |
| + media::AudioParameters params( |
| + media::AudioParameters::AUDIO_PCM_LINEAR, |
| + media::CHANNEL_LAYOUT_STEREO, |
| + audio_config.rtp_timebase, // sampling rate |
| + 16, |
| + audio_config.rtp_timebase / 100); // frames per buffer, |
|
Alpha Left Google
2015/02/25 20:37:28
Divide by audio_config.target_frame_rate instead.
hubbe
2015/02/28 00:17:11
Done.
|
| + |
| + if (!params.IsValid()) { |
| + args.GetIsolate()->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(args.GetIsolate(), kInvalidAudioParams))); |
| + return; |
| + } |
| + |
| + base::DictionaryValue* options = NULL; |
| + if (args.Length() >= 9) { |
| + scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); |
| + base::Value* options_value = |
| + converter->FromV8Value(args[8], context()->v8_context()); |
| + if (!options_value->IsType(base::Value::TYPE_NULL)) { |
| + if (!options_value || !options_value->GetAsDictionary(&options)) { |
| + delete options_value; |
| + args.GetIsolate()->ThrowException(v8::Exception::TypeError( |
| + v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs))); |
| + return; |
| + } |
| + } |
| + } |
| + |
| + if (!options) { |
| + options = new base::DictionaryValue(); |
| + } |
| + |
| + session->Start(audio_config, |
| + video_config, |
| + local_endpoint, |
| + remote_endpoint, |
| + make_scoped_ptr(options), |
| + capture_format, |
| + base::Bind(&CastStreamingNativeHandler::CallReceiverCB, |
| + weak_factory_.GetWeakPtr(), |
| + url, |
| + params)); |
| +} |
| + |
| +void CastStreamingNativeHandler::CallReceiverCB( |
| + const std::string& url, |
| + const media::AudioParameters& params, |
| + scoped_refptr<media::AudioCapturerSource> audio, |
| + scoped_ptr<media::VideoCapturerSource> video) { |
| + |
| + content::AddAudioTrackToMediaStream(audio, params, true, true, url); |
| + content::AddVideoTrackToMediaStream(video.Pass(), true, true, url); |
| +} |
| + |
| + |
|
Alpha Left Google
2015/02/25 20:37:28
nit: remove this extra empty line.
hubbe
2015/02/28 00:17:12
Done.
|
| } // namespace extensions |