1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/extensions/cast_streaming_native_handler.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/common/extensions/api/cast_streaming_receiver_session.h"
14 #include "chrome/common/extensions/api/cast_streaming_rtp_stream.h"
15 #include "chrome/common/extensions/api/cast_streaming_udp_transport.h"
16 #include "chrome/renderer/media/cast_receiver_session.h"
17 #include "chrome/renderer/media/cast_rtp_stream.h"
18 #include "chrome/renderer/media/cast_session.h"
19 #include "chrome/renderer/media/cast_udp_transport.h"
20 #include "content/public/child/v8_value_converter.h"
21 #include "content/public/renderer/media_stream_api.h"
22 #include "extensions/renderer/script_context.h"
23 #include "media/audio/audio_parameters.h"
24 #include "net/base/host_port_pair.h"
25 #include "third_party/WebKit/public/platform/WebMediaStream.h"
26 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
27 #include "third_party/WebKit/public/platform/WebURL.h"
28 #include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h"
29 #include "third_party/WebKit/public/web/WebMediaStreamRegistry.h"
32 using content::V8ValueConverter
;
35 using extensions::api::cast_streaming_receiver_session::RtpReceiverParams
;
36 using extensions::api::cast_streaming_rtp_stream::CodecSpecificParams
;
37 using extensions::api::cast_streaming_rtp_stream::RtpParams
;
38 using extensions::api::cast_streaming_rtp_stream::RtpPayloadParams
;
39 using extensions::api::cast_streaming_udp_transport::IPEndPoint
;
41 namespace extensions
{
44 const char kInvalidAesIvMask
[] = "Invalid value for AES IV mask";
45 const char kInvalidAesKey
[] = "Invalid value for AES key";
46 const char kInvalidAudioParams
[] = "Invalid audio params";
47 const char kInvalidDestination
[] = "Invalid destination";
48 const char kInvalidFPS
[] = "Invalid FPS";
49 const char kInvalidMediaStreamURL
[] = "Invalid MediaStream URL";
50 const char kInvalidRtpParams
[] = "Invalid value for RTP params";
51 const char kInvalidLatency
[] = "Invalid value for max_latency. (0-1000)";
52 const char kInvalidRtpTimebase
[] = "Invalid rtp_timebase. (1000-1000000)";
53 const char kInvalidStreamArgs
[] = "Invalid stream arguments";
54 const char kRtpStreamNotFound
[] = "The RTP stream cannot be found";
55 const char kUdpTransportNotFound
[] = "The UDP transport cannot be found";
56 const char kUnableToConvertArgs
[] = "Unable to convert arguments";
57 const char kUnableToConvertParams
[] = "Unable to convert params";
59 // These helper methods are used to convert between Extension API
60 // types and Cast types.
61 void ToCastCodecSpecificParams(const CodecSpecificParams
& ext_params
,
62 CastCodecSpecificParams
* cast_params
) {
63 cast_params
->key
= ext_params
.key
;
64 cast_params
->value
= ext_params
.value
;
67 void FromCastCodecSpecificParams(const CastCodecSpecificParams
& cast_params
,
68 CodecSpecificParams
* ext_params
) {
69 ext_params
->key
= cast_params
.key
;
70 ext_params
->value
= cast_params
.value
;
74 bool HexDecode(const std::string
& input
, std::string
* output
) {
75 std::vector
<uint8
> bytes
;
76 if (!base::HexStringToBytes(input
, &bytes
))
78 output
->assign(reinterpret_cast<const char*>(&bytes
[0]), bytes
.size());
83 bool ToCastRtpPayloadParamsOrThrow(v8::Isolate
* isolate
,
84 const RtpPayloadParams
& ext_params
,
85 CastRtpPayloadParams
* cast_params
) {
86 cast_params
->payload_type
= ext_params
.payload_type
;
87 cast_params
->max_latency_ms
= ext_params
.max_latency
;
88 cast_params
->min_latency_ms
=
89 ext_params
.min_latency
? *ext_params
.min_latency
: ext_params
.max_latency
;
90 cast_params
->codec_name
= ext_params
.codec_name
;
91 cast_params
->ssrc
= ext_params
.ssrc
;
92 cast_params
->feedback_ssrc
= ext_params
.feedback_ssrc
;
93 cast_params
->clock_rate
= ext_params
.clock_rate
? *ext_params
.clock_rate
: 0;
94 cast_params
->min_bitrate
=
95 ext_params
.min_bitrate
? *ext_params
.min_bitrate
: 0;
96 cast_params
->max_bitrate
=
97 ext_params
.max_bitrate
? *ext_params
.max_bitrate
: 0;
98 cast_params
->channels
= ext_params
.channels
? *ext_params
.channels
: 0;
99 cast_params
->max_frame_rate
=
100 ext_params
.max_frame_rate
? *ext_params
.max_frame_rate
: 0.0;
101 if (ext_params
.aes_key
&&
102 !HexDecode(*ext_params
.aes_key
, &cast_params
->aes_key
)) {
103 isolate
->ThrowException(v8::Exception::Error(
104 v8::String::NewFromUtf8(isolate
, kInvalidAesKey
)));
107 if (ext_params
.aes_iv_mask
&&
108 !HexDecode(*ext_params
.aes_iv_mask
, &cast_params
->aes_iv_mask
)) {
109 isolate
->ThrowException(v8::Exception::Error(
110 v8::String::NewFromUtf8(isolate
, kInvalidAesIvMask
)));
113 for (size_t i
= 0; i
< ext_params
.codec_specific_params
.size(); ++i
) {
114 CastCodecSpecificParams cast_codec_params
;
115 ToCastCodecSpecificParams(*ext_params
.codec_specific_params
[i
],
117 cast_params
->codec_specific_params
.push_back(cast_codec_params
);
122 void FromCastRtpPayloadParams(const CastRtpPayloadParams
& cast_params
,
123 RtpPayloadParams
* ext_params
) {
124 ext_params
->payload_type
= cast_params
.payload_type
;
125 ext_params
->max_latency
= cast_params
.max_latency_ms
;
126 ext_params
->min_latency
.reset(new int(cast_params
.min_latency_ms
));
127 ext_params
->codec_name
= cast_params
.codec_name
;
128 ext_params
->ssrc
= cast_params
.ssrc
;
129 ext_params
->feedback_ssrc
= cast_params
.feedback_ssrc
;
130 if (cast_params
.clock_rate
)
131 ext_params
->clock_rate
.reset(new int(cast_params
.clock_rate
));
132 if (cast_params
.min_bitrate
)
133 ext_params
->min_bitrate
.reset(new int(cast_params
.min_bitrate
));
134 if (cast_params
.max_bitrate
)
135 ext_params
->max_bitrate
.reset(new int(cast_params
.max_bitrate
));
136 if (cast_params
.channels
)
137 ext_params
->channels
.reset(new int(cast_params
.channels
));
138 if (cast_params
.max_frame_rate
> 0.0)
139 ext_params
->max_frame_rate
.reset(new double(cast_params
.max_frame_rate
));
140 for (size_t i
= 0; i
< cast_params
.codec_specific_params
.size(); ++i
) {
141 linked_ptr
<CodecSpecificParams
> ext_codec_params(
142 new CodecSpecificParams());
143 FromCastCodecSpecificParams(cast_params
.codec_specific_params
[i
],
144 ext_codec_params
.get());
145 ext_params
->codec_specific_params
.push_back(ext_codec_params
);
149 void FromCastRtpParams(const CastRtpParams
& cast_params
,
150 RtpParams
* ext_params
) {
151 std::copy(cast_params
.rtcp_features
.begin(),
152 cast_params
.rtcp_features
.end(),
153 std::back_inserter(ext_params
->rtcp_features
));
154 FromCastRtpPayloadParams(cast_params
.payload
, &ext_params
->payload
);
157 bool ToCastRtpParamsOrThrow(v8::Isolate
* isolate
,
158 const RtpParams
& ext_params
,
159 CastRtpParams
* cast_params
) {
160 std::copy(ext_params
.rtcp_features
.begin(),
161 ext_params
.rtcp_features
.end(),
162 std::back_inserter(cast_params
->rtcp_features
));
163 if (!ToCastRtpPayloadParamsOrThrow(isolate
,
165 &cast_params
->payload
)) {
173 CastStreamingNativeHandler::CastStreamingNativeHandler(ScriptContext
* context
)
174 : ObjectBackedNativeHandler(context
),
175 last_transport_id_(1),
176 weak_factory_(this) {
177 RouteFunction("CreateSession",
178 base::Bind(&CastStreamingNativeHandler::CreateCastSession
,
179 base::Unretained(this)));
180 RouteFunction("DestroyCastRtpStream",
181 base::Bind(&CastStreamingNativeHandler::DestroyCastRtpStream
,
182 base::Unretained(this)));
183 RouteFunction("GetSupportedParamsCastRtpStream",
184 base::Bind(&CastStreamingNativeHandler::GetSupportedParamsCastRtpStream
,
185 base::Unretained(this)));
186 RouteFunction("StartCastRtpStream",
187 base::Bind(&CastStreamingNativeHandler::StartCastRtpStream
,
188 base::Unretained(this)));
189 RouteFunction("StopCastRtpStream",
190 base::Bind(&CastStreamingNativeHandler::StopCastRtpStream
,
191 base::Unretained(this)));
192 RouteFunction("DestroyCastUdpTransport",
193 base::Bind(&CastStreamingNativeHandler::DestroyCastUdpTransport
,
194 base::Unretained(this)));
195 RouteFunction("SetDestinationCastUdpTransport",
196 base::Bind(&CastStreamingNativeHandler::SetDestinationCastUdpTransport
,
197 base::Unretained(this)));
198 RouteFunction("SetOptionsCastUdpTransport",
199 base::Bind(&CastStreamingNativeHandler::SetOptionsCastUdpTransport
,
200 base::Unretained(this)));
201 RouteFunction("ToggleLogging",
202 base::Bind(&CastStreamingNativeHandler::ToggleLogging
,
203 base::Unretained(this)));
204 RouteFunction("GetRawEvents",
205 base::Bind(&CastStreamingNativeHandler::GetRawEvents
,
206 base::Unretained(this)));
207 RouteFunction("GetStats",
208 base::Bind(&CastStreamingNativeHandler::GetStats
,
209 base::Unretained(this)));
210 RouteFunction("StartCastRtpReceiver",
211 base::Bind(&CastStreamingNativeHandler::StartCastRtpReceiver
,
212 base::Unretained(this)));
215 CastStreamingNativeHandler::~CastStreamingNativeHandler() {
218 void CastStreamingNativeHandler::CreateCastSession(
219 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
220 CHECK_EQ(3, args
.Length());
221 CHECK(args
[2]->IsFunction());
223 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
224 if ((args
[0]->IsNull() || args
[0]->IsUndefined()) &&
225 (args
[1]->IsNull() || args
[1]->IsUndefined())) {
226 isolate
->ThrowException(v8::Exception::Error(
227 v8::String::NewFromUtf8(isolate
, kInvalidStreamArgs
)));
231 scoped_refptr
<CastSession
> session(new CastSession());
232 scoped_ptr
<CastRtpStream
> stream1
, stream2
;
233 if (!args
[0]->IsNull() && !args
[0]->IsUndefined()) {
234 CHECK(args
[0]->IsObject());
235 blink::WebDOMMediaStreamTrack track
=
236 blink::WebDOMMediaStreamTrack::fromV8Value(args
[0]);
237 if (track
.isNull()) {
238 isolate
->ThrowException(v8::Exception::Error(
239 v8::String::NewFromUtf8(isolate
, kInvalidStreamArgs
)));
242 stream1
.reset(new CastRtpStream(track
.component(), session
));
244 if (!args
[1]->IsNull() && !args
[1]->IsUndefined()) {
245 CHECK(args
[1]->IsObject());
246 blink::WebDOMMediaStreamTrack track
=
247 blink::WebDOMMediaStreamTrack::fromV8Value(args
[1]);
248 if (track
.isNull()) {
249 isolate
->ThrowException(v8::Exception::Error(
250 v8::String::NewFromUtf8(isolate
, kInvalidStreamArgs
)));
253 stream2
.reset(new CastRtpStream(track
.component(), session
));
255 scoped_ptr
<CastUdpTransport
> udp_transport(
256 new CastUdpTransport(session
));
258 // TODO(imcheng): Use a weak reference to ensure we don't call into an
259 // invalid context when the callback is invoked.
260 create_callback_
.Reset(isolate
, args
[2].As
<v8::Function
>());
262 base::MessageLoop::current()->PostTask(
265 &CastStreamingNativeHandler::CallCreateCallback
,
266 weak_factory_
.GetWeakPtr(),
267 base::Passed(&stream1
),
268 base::Passed(&stream2
),
269 base::Passed(&udp_transport
)));
272 void CastStreamingNativeHandler::CallCreateCallback(
273 scoped_ptr
<CastRtpStream
> stream1
,
274 scoped_ptr
<CastRtpStream
> stream2
,
275 scoped_ptr
<CastUdpTransport
> udp_transport
) {
276 v8::Isolate
* isolate
= context()->isolate();
277 v8::HandleScope
handle_scope(isolate
);
278 v8::Context::Scope
context_scope(context()->v8_context());
280 v8::Handle
<v8::Value
> callback_args
[3];
281 callback_args
[0] = v8::Null(isolate
);
282 callback_args
[1] = v8::Null(isolate
);
285 const int stream1_id
= last_transport_id_
++;
286 callback_args
[0] = v8::Integer::New(isolate
, stream1_id
);
287 rtp_stream_map_
[stream1_id
] =
288 linked_ptr
<CastRtpStream
>(stream1
.release());
291 const int stream2_id
= last_transport_id_
++;
292 callback_args
[1] = v8::Integer::New(isolate
, stream2_id
);
293 rtp_stream_map_
[stream2_id
] =
294 linked_ptr
<CastRtpStream
>(stream2
.release());
296 const int udp_id
= last_transport_id_
++;
297 udp_transport_map_
[udp_id
] =
298 linked_ptr
<CastUdpTransport
>(udp_transport
.release());
299 callback_args
[2] = v8::Integer::New(isolate
, udp_id
);
300 context()->CallFunction(
301 v8::Local
<v8::Function
>::New(isolate
, create_callback_
), 3,
303 create_callback_
.Reset();
306 void CastStreamingNativeHandler::CallStartCallback(int stream_id
) {
307 v8::Isolate
* isolate
= context()->isolate();
308 v8::HandleScope
handle_scope(isolate
);
309 v8::Context::Scope
context_scope(context()->v8_context());
310 v8::Handle
<v8::Array
> event_args
= v8::Array::New(isolate
, 1);
311 event_args
->Set(0, v8::Integer::New(isolate
, stream_id
));
312 context()->DispatchEvent("cast.streaming.rtpStream.onStarted", event_args
);
315 void CastStreamingNativeHandler::CallStopCallback(int stream_id
) {
316 v8::Isolate
* isolate
= context()->isolate();
317 v8::HandleScope
handle_scope(isolate
);
318 v8::Context::Scope
context_scope(context()->v8_context());
319 v8::Handle
<v8::Array
> event_args
= v8::Array::New(isolate
, 1);
320 event_args
->Set(0, v8::Integer::New(isolate
, stream_id
));
321 context()->DispatchEvent("cast.streaming.rtpStream.onStopped", event_args
);
324 void CastStreamingNativeHandler::CallErrorCallback(int stream_id
,
325 const std::string
& message
) {
326 v8::Isolate
* isolate
= context()->isolate();
327 v8::HandleScope
handle_scope(isolate
);
328 v8::Context::Scope
context_scope(context()->v8_context());
329 v8::Handle
<v8::Array
> event_args
= v8::Array::New(isolate
, 2);
330 event_args
->Set(0, v8::Integer::New(isolate
, stream_id
));
333 v8::String::NewFromUtf8(
334 isolate
, message
.data(), v8::String::kNormalString
, message
.size()));
335 context()->DispatchEvent("cast.streaming.rtpStream.onError", event_args
);
338 void CastStreamingNativeHandler::DestroyCastRtpStream(
339 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
340 CHECK_EQ(1, args
.Length());
341 CHECK(args
[0]->IsInt32());
343 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
344 if (!GetRtpStreamOrThrow(transport_id
))
346 rtp_stream_map_
.erase(transport_id
);
349 void CastStreamingNativeHandler::GetSupportedParamsCastRtpStream(
350 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
351 CHECK_EQ(1, args
.Length());
352 CHECK(args
[0]->IsInt32());
354 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
355 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
359 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
360 std::vector
<CastRtpParams
> cast_params
= transport
->GetSupportedParams();
361 v8::Handle
<v8::Array
> result
=
362 v8::Array::New(args
.GetIsolate(),
363 static_cast<int>(cast_params
.size()));
364 for (size_t i
= 0; i
< cast_params
.size(); ++i
) {
366 FromCastRtpParams(cast_params
[i
], ¶ms
);
367 scoped_ptr
<base::DictionaryValue
> params_value
= params
.ToValue();
370 converter
->ToV8Value(params_value
.get(), context()->v8_context()));
372 args
.GetReturnValue().Set(result
);
375 void CastStreamingNativeHandler::StartCastRtpStream(
376 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
377 CHECK_EQ(2, args
.Length());
378 CHECK(args
[0]->IsInt32());
379 CHECK(args
[1]->IsObject());
381 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
382 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
386 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
387 scoped_ptr
<base::Value
> params_value(
388 converter
->FromV8Value(args
[1], context()->v8_context()));
390 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
391 v8::String::NewFromUtf8(args
.GetIsolate(), kUnableToConvertParams
)));
394 scoped_ptr
<RtpParams
> params
= RtpParams::FromValue(*params_value
);
396 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
397 v8::String::NewFromUtf8(args
.GetIsolate(), kInvalidRtpParams
)));
401 CastRtpParams cast_params
;
402 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
403 if (!ToCastRtpParamsOrThrow(isolate
, *params
, &cast_params
))
406 base::Closure start_callback
=
407 base::Bind(&CastStreamingNativeHandler::CallStartCallback
,
408 weak_factory_
.GetWeakPtr(),
410 base::Closure stop_callback
=
411 base::Bind(&CastStreamingNativeHandler::CallStopCallback
,
412 weak_factory_
.GetWeakPtr(),
414 CastRtpStream::ErrorCallback error_callback
=
415 base::Bind(&CastStreamingNativeHandler::CallErrorCallback
,
416 weak_factory_
.GetWeakPtr(),
418 transport
->Start(cast_params
, start_callback
, stop_callback
, error_callback
);
421 void CastStreamingNativeHandler::StopCastRtpStream(
422 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
423 CHECK_EQ(1, args
.Length());
424 CHECK(args
[0]->IsInt32());
426 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
427 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
433 void CastStreamingNativeHandler::DestroyCastUdpTransport(
434 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
435 CHECK_EQ(1, args
.Length());
436 CHECK(args
[0]->IsInt32());
438 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
439 if (!GetUdpTransportOrThrow(transport_id
))
441 udp_transport_map_
.erase(transport_id
);
444 void CastStreamingNativeHandler::SetDestinationCastUdpTransport(
445 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
446 CHECK_EQ(2, args
.Length());
447 CHECK(args
[0]->IsInt32());
448 CHECK(args
[1]->IsObject());
450 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
451 CastUdpTransport
* transport
= GetUdpTransportOrThrow(transport_id
);
455 net::IPEndPoint dest
;
456 if (!IPEndPointFromArg(args
.GetIsolate(),
461 transport
->SetDestination(
463 base::Bind(&CastStreamingNativeHandler::CallErrorCallback
,
464 weak_factory_
.GetWeakPtr(),
468 void CastStreamingNativeHandler::SetOptionsCastUdpTransport(
469 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
470 CHECK_EQ(2, args
.Length());
471 CHECK(args
[0]->IsInt32());
472 CHECK(args
[1]->IsObject());
474 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
475 CastUdpTransport
* transport
= GetUdpTransportOrThrow(transport_id
);
479 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
480 base::Value
* options_value
=
481 converter
->FromV8Value(args
[1], context()->v8_context());
482 base::DictionaryValue
* options
;
483 if (!options_value
|| !options_value
->GetAsDictionary(&options
)) {
484 delete options_value
;
485 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
486 v8::String::NewFromUtf8(args
.GetIsolate(), kUnableToConvertArgs
)));
489 transport
->SetOptions(make_scoped_ptr(options
));
492 void CastStreamingNativeHandler::ToggleLogging(
493 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
494 CHECK_EQ(2, args
.Length());
495 CHECK(args
[0]->IsInt32());
496 CHECK(args
[1]->IsBoolean());
498 const int stream_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
499 CastRtpStream
* stream
= GetRtpStreamOrThrow(stream_id
);
503 const bool enable
= args
[1]->ToBoolean(args
.GetIsolate())->Value();
504 stream
->ToggleLogging(enable
);
507 void CastStreamingNativeHandler::GetRawEvents(
508 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
509 CHECK_EQ(3, args
.Length());
510 CHECK(args
[0]->IsInt32());
511 CHECK(args
[1]->IsNull() || args
[1]->IsString());
512 CHECK(args
[2]->IsFunction());
514 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
515 // TODO(imcheng): Use a weak reference to ensure we don't call into an
516 // invalid context when the callback is invoked.
517 linked_ptr
<v8::Global
<v8::Function
>> callback(new v8::Global
<v8::Function
>(
518 args
.GetIsolate(), args
[2].As
<v8::Function
>()));
519 std::string extra_data
;
520 if (!args
[1]->IsNull()) {
521 extra_data
= *v8::String::Utf8Value(args
[1]);
524 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
528 get_raw_events_callbacks_
.insert(std::make_pair(transport_id
, callback
));
530 transport
->GetRawEvents(
531 base::Bind(&CastStreamingNativeHandler::CallGetRawEventsCallback
,
532 weak_factory_
.GetWeakPtr(),
537 void CastStreamingNativeHandler::GetStats(
538 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
539 CHECK_EQ(2, args
.Length());
540 CHECK(args
[0]->IsInt32());
541 CHECK(args
[1]->IsFunction());
542 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
543 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
547 // TODO(imcheng): Use a weak reference to ensure we don't call into an
548 // invalid context when the callback is invoked.
549 linked_ptr
<v8::Global
<v8::Function
>> callback(new v8::Global
<v8::Function
>(
550 args
.GetIsolate(), args
[1].As
<v8::Function
>()));
551 get_stats_callbacks_
.insert(std::make_pair(transport_id
, callback
));
554 base::Bind(&CastStreamingNativeHandler::CallGetStatsCallback
,
555 weak_factory_
.GetWeakPtr(),
559 void CastStreamingNativeHandler::CallGetRawEventsCallback(
561 scoped_ptr
<base::BinaryValue
> raw_events
) {
562 v8::Isolate
* isolate
= context()->isolate();
563 v8::HandleScope
handle_scope(isolate
);
564 v8::Context::Scope
context_scope(context()->v8_context());
566 RtpStreamCallbackMap::iterator it
=
567 get_raw_events_callbacks_
.find(transport_id
);
568 if (it
== get_raw_events_callbacks_
.end())
570 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
571 v8::Handle
<v8::Value
> callback_args
[] = {
572 converter
->ToV8Value(raw_events
.get(), context()->v8_context())};
573 context()->CallFunction(v8::Local
<v8::Function
>::New(isolate
, *it
->second
),
574 arraysize(callback_args
), callback_args
);
575 get_raw_events_callbacks_
.erase(it
);
578 void CastStreamingNativeHandler::CallGetStatsCallback(
580 scoped_ptr
<base::DictionaryValue
> stats
) {
581 v8::Isolate
* isolate
= context()->isolate();
582 v8::HandleScope
handle_scope(isolate
);
583 v8::Context::Scope
context_scope(context()->v8_context());
585 RtpStreamCallbackMap::iterator it
= get_stats_callbacks_
.find(transport_id
);
586 if (it
== get_stats_callbacks_
.end())
589 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
590 v8::Handle
<v8::Value
> callback_args
[] = {
591 converter
->ToV8Value(stats
.get(), context()->v8_context())};
592 context()->CallFunction(v8::Local
<v8::Function
>::New(isolate
, *it
->second
),
593 arraysize(callback_args
), callback_args
);
594 get_stats_callbacks_
.erase(it
);
597 CastRtpStream
* CastStreamingNativeHandler::GetRtpStreamOrThrow(
598 int transport_id
) const {
599 RtpStreamMap::const_iterator iter
= rtp_stream_map_
.find(
601 if (iter
!= rtp_stream_map_
.end())
602 return iter
->second
.get();
603 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
604 isolate
->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8(
605 isolate
, kRtpStreamNotFound
)));
609 CastUdpTransport
* CastStreamingNativeHandler::GetUdpTransportOrThrow(
610 int transport_id
) const {
611 UdpTransportMap::const_iterator iter
= udp_transport_map_
.find(
613 if (iter
!= udp_transport_map_
.end())
614 return iter
->second
.get();
615 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
616 isolate
->ThrowException(v8::Exception::RangeError(
617 v8::String::NewFromUtf8(isolate
, kUdpTransportNotFound
)));
621 bool CastStreamingNativeHandler::FrameReceiverConfigFromArg(
622 v8::Isolate
* isolate
,
623 const v8::Handle
<v8::Value
>& arg
,
624 media::cast::FrameReceiverConfig
* config
) {
626 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
627 scoped_ptr
<base::Value
> params_value(
628 converter
->FromV8Value(arg
, context()->v8_context()));
630 isolate
->ThrowException(v8::Exception::TypeError(
631 v8::String::NewFromUtf8(isolate
, kUnableToConvertParams
)));
634 scoped_ptr
<RtpReceiverParams
> params
=
635 RtpReceiverParams::FromValue(*params_value
);
637 isolate
->ThrowException(v8::Exception::TypeError(
638 v8::String::NewFromUtf8(isolate
, kInvalidRtpParams
)));
642 config
->receiver_ssrc
= params
->receiver_ssrc
;
643 config
->sender_ssrc
= params
->sender_ssrc
;
644 config
->rtp_max_delay_ms
= params
->max_latency
;
645 if (config
->rtp_max_delay_ms
< 0 || config
->rtp_max_delay_ms
> 1000) {
646 isolate
->ThrowException(v8::Exception::TypeError(
647 v8::String::NewFromUtf8(isolate
, kInvalidLatency
)));
650 config
->channels
= 2;
651 if (params
->codec_name
== "OPUS") {
652 config
->codec
= media::cast::CODEC_AUDIO_OPUS
;
653 config
->rtp_timebase
= 48000;
654 config
->rtp_payload_type
= 127;
655 } else if (params
->codec_name
== "PCM16") {
656 config
->codec
= media::cast::CODEC_AUDIO_PCM16
;
657 config
->rtp_timebase
= 48000;
658 config
->rtp_payload_type
= 127;
659 } else if (params
->codec_name
== "AAC") {
660 config
->codec
= media::cast::CODEC_AUDIO_AAC
;
661 config
->rtp_timebase
= 48000;
662 config
->rtp_payload_type
= 127;
663 } else if (params
->codec_name
== "VP8") {
664 config
->codec
= media::cast::CODEC_VIDEO_VP8
;
665 config
->rtp_timebase
= 90000;
666 config
->rtp_payload_type
= 96;
667 } else if (params
->codec_name
== "H264") {
668 config
->codec
= media::cast::CODEC_VIDEO_H264
;
669 config
->rtp_timebase
= 90000;
670 config
->rtp_payload_type
= 96;
672 if (params
->rtp_timebase
) {
673 config
->rtp_timebase
= *params
->rtp_timebase
;
674 if (config
->rtp_timebase
< 1000 || config
->rtp_timebase
> 1000000) {
675 isolate
->ThrowException(v8::Exception::TypeError(
676 v8::String::NewFromUtf8(isolate
, kInvalidRtpTimebase
)));
680 if (params
->aes_key
&&
681 !HexDecode(*params
->aes_key
, &config
->aes_key
)) {
682 isolate
->ThrowException(v8::Exception::Error(
683 v8::String::NewFromUtf8(isolate
, kInvalidAesKey
)));
686 if (params
->aes_iv_mask
&&
687 !HexDecode(*params
->aes_iv_mask
, &config
->aes_iv_mask
)) {
688 isolate
->ThrowException(v8::Exception::Error(
689 v8::String::NewFromUtf8(isolate
, kInvalidAesIvMask
)));
695 bool CastStreamingNativeHandler::IPEndPointFromArg(
696 v8::Isolate
* isolate
,
697 const v8::Handle
<v8::Value
>& arg
,
698 net::IPEndPoint
* ip_endpoint
) {
699 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
700 scoped_ptr
<base::Value
> destination_value(
701 converter
->FromV8Value(arg
, context()->v8_context()));
702 if (!destination_value
) {
703 isolate
->ThrowException(v8::Exception::TypeError(
704 v8::String::NewFromUtf8(isolate
, kInvalidAesIvMask
)));
707 scoped_ptr
<IPEndPoint
> destination
=
708 IPEndPoint::FromValue(*destination_value
);
710 isolate
->ThrowException(v8::Exception::TypeError(
711 v8::String::NewFromUtf8(isolate
, kInvalidDestination
)));
714 net::IPAddressNumber ip
;
715 if (!net::ParseIPLiteralToNumber(destination
->address
, &ip
)) {
716 isolate
->ThrowException(v8::Exception::TypeError(
717 v8::String::NewFromUtf8(isolate
, kInvalidDestination
)));
720 *ip_endpoint
= net::IPEndPoint(ip
, destination
->port
);
724 void CastStreamingNativeHandler::StartCastRtpReceiver(
725 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
726 if (args
.Length() < 8 || args
.Length() > 9 ||
727 !args
[0]->IsObject() ||
728 !args
[1]->IsObject() ||
729 !args
[2]->IsObject() ||
730 !args
[3]->IsInt32() ||
731 !args
[4]->IsInt32() ||
732 !args
[5]->IsNumber() ||
733 !args
[6]->IsString()) {
734 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
735 v8::String::NewFromUtf8(args
.GetIsolate(), kUnableToConvertArgs
)));
739 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
741 scoped_refptr
<CastReceiverSession
> session(
742 new CastReceiverSession());
743 media::cast::FrameReceiverConfig audio_config
;
744 media::cast::FrameReceiverConfig video_config
;
745 net::IPEndPoint local_endpoint
;
746 net::IPEndPoint remote_endpoint
;
748 if (!FrameReceiverConfigFromArg(isolate
, args
[0], &audio_config
) ||
749 !FrameReceiverConfigFromArg(isolate
, args
[1], &video_config
) ||
750 !IPEndPointFromArg(isolate
, args
[2], &local_endpoint
)) {
754 const std::string url
= *v8::String::Utf8Value(args
[7]);
755 blink::WebMediaStream stream
=
756 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url
));
758 if (stream
.isNull()) {
759 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
760 v8::String::NewFromUtf8(args
.GetIsolate(), kInvalidMediaStreamURL
)));
764 const int max_width
= args
[3]->ToInt32(args
.GetIsolate())->Value();
765 const int max_height
= args
[4]->ToInt32(args
.GetIsolate())->Value();
766 const double fps
= args
[5]->NumberValue();
769 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
770 v8::String::NewFromUtf8(args
.GetIsolate(), kInvalidFPS
)));
774 media::VideoCaptureFormat
capture_format(
775 gfx::Size(max_width
, max_height
),
777 media::PIXEL_FORMAT_I420
);
779 video_config
.target_frame_rate
= fps
;
780 audio_config
.target_frame_rate
= 100;
782 media::AudioParameters
params(
783 media::AudioParameters::AUDIO_PCM_LINEAR
,
784 media::CHANNEL_LAYOUT_STEREO
,
785 audio_config
.rtp_timebase
, // sampling rate
787 audio_config
.rtp_timebase
/ audio_config
.target_frame_rate
);
789 if (!params
.IsValid()) {
790 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
791 v8::String::NewFromUtf8(args
.GetIsolate(), kInvalidAudioParams
)));
795 base::DictionaryValue
* options
= NULL
;
796 if (args
.Length() >= 10) {
797 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
798 base::Value
* options_value
=
799 converter
->FromV8Value(args
[8], context()->v8_context());
800 if (!options_value
->IsType(base::Value::TYPE_NULL
)) {
801 if (!options_value
|| !options_value
->GetAsDictionary(&options
)) {
802 delete options_value
;
803 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
804 v8::String::NewFromUtf8(args
.GetIsolate(), kUnableToConvertArgs
)));
811 options
= new base::DictionaryValue();
814 v8::CopyablePersistentTraits
<v8::Function
>::CopyablePersistent error_callback
;
815 error_callback
.Reset(args
.GetIsolate(),
816 v8::Handle
<v8::Function
>(args
[7].As
<v8::Function
>()));
823 make_scoped_ptr(options
),
825 base::Bind(&CastStreamingNativeHandler::AddTracksToMediaStream
,
826 weak_factory_
.GetWeakPtr(),
829 base::Bind(&CastStreamingNativeHandler::CallReceiverErrorCallback
,
830 weak_factory_
.GetWeakPtr(),
834 void CastStreamingNativeHandler::CallReceiverErrorCallback(
835 v8::CopyablePersistentTraits
<v8::Function
>::CopyablePersistent function
,
836 const std::string
& error_message
) {
837 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
838 v8::Handle
<v8::Value
> arg
= v8::String::NewFromUtf8(isolate
,
839 error_message
.data(),
840 v8::String::kNormalString
,
841 error_message
.size());
842 context()->CallFunction(
843 v8::Local
<v8::Function
>::New(isolate
, function
), 1, &arg
);
847 void CastStreamingNativeHandler::AddTracksToMediaStream(
848 const std::string
& url
,
849 const media::AudioParameters
& params
,
850 scoped_refptr
<media::AudioCapturerSource
> audio
,
851 scoped_ptr
<media::VideoCapturerSource
> video
) {
852 content::AddAudioTrackToMediaStream(audio
, params
, true, true, url
);
853 content::AddVideoTrackToMediaStream(video
.Pass(), true, true, url
);
856 } // namespace extensions