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 |