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/location.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "chrome/common/extensions/api/cast_streaming_receiver_session.h"
16 #include "chrome/common/extensions/api/cast_streaming_rtp_stream.h"
17 #include "chrome/common/extensions/api/cast_streaming_udp_transport.h"
18 #include "chrome/renderer/media/cast_receiver_session.h"
19 #include "chrome/renderer/media/cast_rtp_stream.h"
20 #include "chrome/renderer/media/cast_session.h"
21 #include "chrome/renderer/media/cast_udp_transport.h"
22 #include "content/public/child/v8_value_converter.h"
23 #include "content/public/renderer/media_stream_api.h"
24 #include "extensions/renderer/script_context.h"
25 #include "media/audio/audio_parameters.h"
26 #include "net/base/host_port_pair.h"
27 #include "third_party/WebKit/public/platform/WebMediaStream.h"
28 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
29 #include "third_party/WebKit/public/platform/WebURL.h"
30 #include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h"
31 #include "third_party/WebKit/public/web/WebMediaStreamRegistry.h"
34 using content::V8ValueConverter
;
37 using extensions::api::cast_streaming_receiver_session::RtpReceiverParams
;
38 using extensions::api::cast_streaming_rtp_stream::CodecSpecificParams
;
39 using extensions::api::cast_streaming_rtp_stream::RtpParams
;
40 using extensions::api::cast_streaming_rtp_stream::RtpPayloadParams
;
41 using extensions::api::cast_streaming_udp_transport::IPEndPoint
;
43 namespace extensions
{
46 const char kInvalidAesIvMask
[] = "Invalid value for AES IV mask";
47 const char kInvalidAesKey
[] = "Invalid value for AES key";
48 const char kInvalidAudioParams
[] = "Invalid audio params";
49 const char kInvalidDestination
[] = "Invalid destination";
50 const char kInvalidFPS
[] = "Invalid FPS";
51 const char kInvalidMediaStreamURL
[] = "Invalid MediaStream URL";
52 const char kInvalidRtpParams
[] = "Invalid value for RTP params";
53 const char kInvalidLatency
[] = "Invalid value for max_latency. (0-1000)";
54 const char kInvalidRtpTimebase
[] = "Invalid rtp_timebase. (1000-1000000)";
55 const char kInvalidStreamArgs
[] = "Invalid stream arguments";
56 const char kRtpStreamNotFound
[] = "The RTP stream cannot be found";
57 const char kUdpTransportNotFound
[] = "The UDP transport cannot be found";
58 const char kUnableToConvertArgs
[] = "Unable to convert arguments";
59 const char kUnableToConvertParams
[] = "Unable to convert params";
61 // These helper methods are used to convert between Extension API
62 // types and Cast types.
63 void ToCastCodecSpecificParams(const CodecSpecificParams
& ext_params
,
64 CastCodecSpecificParams
* cast_params
) {
65 cast_params
->key
= ext_params
.key
;
66 cast_params
->value
= ext_params
.value
;
69 void FromCastCodecSpecificParams(const CastCodecSpecificParams
& cast_params
,
70 CodecSpecificParams
* ext_params
) {
71 ext_params
->key
= cast_params
.key
;
72 ext_params
->value
= cast_params
.value
;
76 bool HexDecode(const std::string
& input
, std::string
* output
) {
77 std::vector
<uint8
> bytes
;
78 if (!base::HexStringToBytes(input
, &bytes
))
80 output
->assign(reinterpret_cast<const char*>(&bytes
[0]), bytes
.size());
85 bool ToCastRtpPayloadParamsOrThrow(v8::Isolate
* isolate
,
86 const RtpPayloadParams
& ext_params
,
87 CastRtpPayloadParams
* cast_params
) {
88 cast_params
->payload_type
= ext_params
.payload_type
;
89 cast_params
->max_latency_ms
= ext_params
.max_latency
;
90 cast_params
->min_latency_ms
=
91 ext_params
.min_latency
? *ext_params
.min_latency
: ext_params
.max_latency
;
92 cast_params
->codec_name
= ext_params
.codec_name
;
93 cast_params
->ssrc
= ext_params
.ssrc
;
94 cast_params
->feedback_ssrc
= ext_params
.feedback_ssrc
;
95 cast_params
->clock_rate
= ext_params
.clock_rate
? *ext_params
.clock_rate
: 0;
96 cast_params
->min_bitrate
=
97 ext_params
.min_bitrate
? *ext_params
.min_bitrate
: 0;
98 cast_params
->max_bitrate
=
99 ext_params
.max_bitrate
? *ext_params
.max_bitrate
: 0;
100 cast_params
->channels
= ext_params
.channels
? *ext_params
.channels
: 0;
101 cast_params
->max_frame_rate
=
102 ext_params
.max_frame_rate
? *ext_params
.max_frame_rate
: 0.0;
103 if (ext_params
.aes_key
&&
104 !HexDecode(*ext_params
.aes_key
, &cast_params
->aes_key
)) {
105 isolate
->ThrowException(v8::Exception::Error(
106 v8::String::NewFromUtf8(isolate
, kInvalidAesKey
)));
109 if (ext_params
.aes_iv_mask
&&
110 !HexDecode(*ext_params
.aes_iv_mask
, &cast_params
->aes_iv_mask
)) {
111 isolate
->ThrowException(v8::Exception::Error(
112 v8::String::NewFromUtf8(isolate
, kInvalidAesIvMask
)));
115 for (size_t i
= 0; i
< ext_params
.codec_specific_params
.size(); ++i
) {
116 CastCodecSpecificParams cast_codec_params
;
117 ToCastCodecSpecificParams(*ext_params
.codec_specific_params
[i
],
119 cast_params
->codec_specific_params
.push_back(cast_codec_params
);
124 void FromCastRtpPayloadParams(const CastRtpPayloadParams
& cast_params
,
125 RtpPayloadParams
* ext_params
) {
126 ext_params
->payload_type
= cast_params
.payload_type
;
127 ext_params
->max_latency
= cast_params
.max_latency_ms
;
128 ext_params
->min_latency
.reset(new int(cast_params
.min_latency_ms
));
129 ext_params
->codec_name
= cast_params
.codec_name
;
130 ext_params
->ssrc
= cast_params
.ssrc
;
131 ext_params
->feedback_ssrc
= cast_params
.feedback_ssrc
;
132 if (cast_params
.clock_rate
)
133 ext_params
->clock_rate
.reset(new int(cast_params
.clock_rate
));
134 if (cast_params
.min_bitrate
)
135 ext_params
->min_bitrate
.reset(new int(cast_params
.min_bitrate
));
136 if (cast_params
.max_bitrate
)
137 ext_params
->max_bitrate
.reset(new int(cast_params
.max_bitrate
));
138 if (cast_params
.channels
)
139 ext_params
->channels
.reset(new int(cast_params
.channels
));
140 if (cast_params
.max_frame_rate
> 0.0)
141 ext_params
->max_frame_rate
.reset(new double(cast_params
.max_frame_rate
));
142 for (size_t i
= 0; i
< cast_params
.codec_specific_params
.size(); ++i
) {
143 linked_ptr
<CodecSpecificParams
> ext_codec_params(
144 new CodecSpecificParams());
145 FromCastCodecSpecificParams(cast_params
.codec_specific_params
[i
],
146 ext_codec_params
.get());
147 ext_params
->codec_specific_params
.push_back(ext_codec_params
);
151 void FromCastRtpParams(const CastRtpParams
& cast_params
,
152 RtpParams
* ext_params
) {
153 std::copy(cast_params
.rtcp_features
.begin(),
154 cast_params
.rtcp_features
.end(),
155 std::back_inserter(ext_params
->rtcp_features
));
156 FromCastRtpPayloadParams(cast_params
.payload
, &ext_params
->payload
);
159 bool ToCastRtpParamsOrThrow(v8::Isolate
* isolate
,
160 const RtpParams
& ext_params
,
161 CastRtpParams
* cast_params
) {
162 std::copy(ext_params
.rtcp_features
.begin(),
163 ext_params
.rtcp_features
.end(),
164 std::back_inserter(cast_params
->rtcp_features
));
165 if (!ToCastRtpPayloadParamsOrThrow(isolate
,
167 &cast_params
->payload
)) {
175 CastStreamingNativeHandler::CastStreamingNativeHandler(ScriptContext
* context
)
176 : ObjectBackedNativeHandler(context
),
177 last_transport_id_(1),
178 weak_factory_(this) {
179 RouteFunction("CreateSession",
180 base::Bind(&CastStreamingNativeHandler::CreateCastSession
,
181 weak_factory_
.GetWeakPtr()));
182 RouteFunction("DestroyCastRtpStream",
183 base::Bind(&CastStreamingNativeHandler::DestroyCastRtpStream
,
184 weak_factory_
.GetWeakPtr()));
186 "GetSupportedParamsCastRtpStream",
187 base::Bind(&CastStreamingNativeHandler::GetSupportedParamsCastRtpStream
,
188 weak_factory_
.GetWeakPtr()));
189 RouteFunction("StartCastRtpStream",
190 base::Bind(&CastStreamingNativeHandler::StartCastRtpStream
,
191 weak_factory_
.GetWeakPtr()));
192 RouteFunction("StopCastRtpStream",
193 base::Bind(&CastStreamingNativeHandler::StopCastRtpStream
,
194 weak_factory_
.GetWeakPtr()));
195 RouteFunction("DestroyCastUdpTransport",
196 base::Bind(&CastStreamingNativeHandler::DestroyCastUdpTransport
,
197 weak_factory_
.GetWeakPtr()));
199 "SetDestinationCastUdpTransport",
200 base::Bind(&CastStreamingNativeHandler::SetDestinationCastUdpTransport
,
201 weak_factory_
.GetWeakPtr()));
203 "SetOptionsCastUdpTransport",
204 base::Bind(&CastStreamingNativeHandler::SetOptionsCastUdpTransport
,
205 weak_factory_
.GetWeakPtr()));
206 RouteFunction("ToggleLogging",
207 base::Bind(&CastStreamingNativeHandler::ToggleLogging
,
208 weak_factory_
.GetWeakPtr()));
209 RouteFunction("GetRawEvents",
210 base::Bind(&CastStreamingNativeHandler::GetRawEvents
,
211 weak_factory_
.GetWeakPtr()));
212 RouteFunction("GetStats", base::Bind(&CastStreamingNativeHandler::GetStats
,
213 weak_factory_
.GetWeakPtr()));
214 RouteFunction("StartCastRtpReceiver",
215 base::Bind(&CastStreamingNativeHandler::StartCastRtpReceiver
,
216 weak_factory_
.GetWeakPtr()));
219 CastStreamingNativeHandler::~CastStreamingNativeHandler() {
220 // Note: A superclass's destructor will call Invalidate(), but Invalidate()
221 // may also be called at any time before destruction.
224 void CastStreamingNativeHandler::Invalidate() {
225 // Cancel all function call routing and callbacks.
226 weak_factory_
.InvalidateWeakPtrs();
228 // Clear all references to V8 and Cast objects, which will trigger
229 // auto-destructions (effectively stopping all sessions).
230 get_stats_callbacks_
.clear();
231 get_raw_events_callbacks_
.clear();
232 create_callback_
.Reset();
233 udp_transport_map_
.clear();
234 rtp_stream_map_
.clear();
236 ObjectBackedNativeHandler::Invalidate();
239 void CastStreamingNativeHandler::CreateCastSession(
240 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
241 CHECK_EQ(3, args
.Length());
242 CHECK(args
[2]->IsFunction());
244 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
245 if ((args
[0]->IsNull() || args
[0]->IsUndefined()) &&
246 (args
[1]->IsNull() || args
[1]->IsUndefined())) {
247 isolate
->ThrowException(v8::Exception::Error(
248 v8::String::NewFromUtf8(isolate
, kInvalidStreamArgs
)));
252 scoped_refptr
<CastSession
> session(new CastSession());
253 scoped_ptr
<CastRtpStream
> stream1
, stream2
;
254 if (!args
[0]->IsNull() && !args
[0]->IsUndefined()) {
255 CHECK(args
[0]->IsObject());
256 blink::WebDOMMediaStreamTrack track
=
257 blink::WebDOMMediaStreamTrack::fromV8Value(args
[0]);
258 if (track
.isNull()) {
259 isolate
->ThrowException(v8::Exception::Error(
260 v8::String::NewFromUtf8(isolate
, kInvalidStreamArgs
)));
263 stream1
.reset(new CastRtpStream(track
.component(), session
));
265 if (!args
[1]->IsNull() && !args
[1]->IsUndefined()) {
266 CHECK(args
[1]->IsObject());
267 blink::WebDOMMediaStreamTrack track
=
268 blink::WebDOMMediaStreamTrack::fromV8Value(args
[1]);
269 if (track
.isNull()) {
270 isolate
->ThrowException(v8::Exception::Error(
271 v8::String::NewFromUtf8(isolate
, kInvalidStreamArgs
)));
274 stream2
.reset(new CastRtpStream(track
.component(), session
));
276 scoped_ptr
<CastUdpTransport
> udp_transport(
277 new CastUdpTransport(session
));
279 create_callback_
.Reset(isolate
, args
[2].As
<v8::Function
>());
281 base::ThreadTaskRunnerHandle::Get()->PostTask(
283 base::Bind(&CastStreamingNativeHandler::CallCreateCallback
,
284 weak_factory_
.GetWeakPtr(), base::Passed(&stream1
),
285 base::Passed(&stream2
), base::Passed(&udp_transport
)));
288 void CastStreamingNativeHandler::CallCreateCallback(
289 scoped_ptr
<CastRtpStream
> stream1
,
290 scoped_ptr
<CastRtpStream
> stream2
,
291 scoped_ptr
<CastUdpTransport
> udp_transport
) {
292 v8::Isolate
* isolate
= context()->isolate();
293 v8::HandleScope
handle_scope(isolate
);
294 v8::Context::Scope
context_scope(context()->v8_context());
296 v8::Local
<v8::Value
> callback_args
[3];
297 callback_args
[0] = v8::Null(isolate
);
298 callback_args
[1] = v8::Null(isolate
);
301 const int stream1_id
= last_transport_id_
++;
302 callback_args
[0] = v8::Integer::New(isolate
, stream1_id
);
303 rtp_stream_map_
[stream1_id
] =
304 linked_ptr
<CastRtpStream
>(stream1
.release());
307 const int stream2_id
= last_transport_id_
++;
308 callback_args
[1] = v8::Integer::New(isolate
, stream2_id
);
309 rtp_stream_map_
[stream2_id
] =
310 linked_ptr
<CastRtpStream
>(stream2
.release());
312 const int udp_id
= last_transport_id_
++;
313 udp_transport_map_
[udp_id
] =
314 linked_ptr
<CastUdpTransport
>(udp_transport
.release());
315 callback_args
[2] = v8::Integer::New(isolate
, udp_id
);
316 context()->CallFunction(
317 v8::Local
<v8::Function
>::New(isolate
, create_callback_
), 3,
319 create_callback_
.Reset();
322 void CastStreamingNativeHandler::CallStartCallback(int stream_id
) const {
323 v8::Isolate
* isolate
= context()->isolate();
324 v8::HandleScope
handle_scope(isolate
);
325 v8::Context::Scope
context_scope(context()->v8_context());
326 v8::Local
<v8::Array
> event_args
= v8::Array::New(isolate
, 1);
327 event_args
->Set(0, v8::Integer::New(isolate
, stream_id
));
328 context()->DispatchEvent("cast.streaming.rtpStream.onStarted", event_args
);
331 void CastStreamingNativeHandler::CallStopCallback(int stream_id
) const {
332 v8::Isolate
* isolate
= context()->isolate();
333 v8::HandleScope
handle_scope(isolate
);
334 v8::Context::Scope
context_scope(context()->v8_context());
335 v8::Local
<v8::Array
> event_args
= v8::Array::New(isolate
, 1);
336 event_args
->Set(0, v8::Integer::New(isolate
, stream_id
));
337 context()->DispatchEvent("cast.streaming.rtpStream.onStopped", event_args
);
340 void CastStreamingNativeHandler::CallErrorCallback(
342 const std::string
& message
) const {
343 v8::Isolate
* isolate
= context()->isolate();
344 v8::HandleScope
handle_scope(isolate
);
345 v8::Context::Scope
context_scope(context()->v8_context());
346 v8::Local
<v8::Array
> event_args
= v8::Array::New(isolate
, 2);
347 event_args
->Set(0, v8::Integer::New(isolate
, stream_id
));
350 v8::String::NewFromUtf8(
351 isolate
, message
.data(), v8::String::kNormalString
, message
.size()));
352 context()->DispatchEvent("cast.streaming.rtpStream.onError", event_args
);
355 void CastStreamingNativeHandler::DestroyCastRtpStream(
356 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
357 CHECK_EQ(1, args
.Length());
358 CHECK(args
[0]->IsInt32());
360 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
361 if (!GetRtpStreamOrThrow(transport_id
))
363 rtp_stream_map_
.erase(transport_id
);
366 void CastStreamingNativeHandler::GetSupportedParamsCastRtpStream(
367 const v8::FunctionCallbackInfo
<v8::Value
>& args
) const {
368 CHECK_EQ(1, args
.Length());
369 CHECK(args
[0]->IsInt32());
371 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
372 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
376 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
377 std::vector
<CastRtpParams
> cast_params
= transport
->GetSupportedParams();
378 v8::Local
<v8::Array
> result
=
379 v8::Array::New(args
.GetIsolate(),
380 static_cast<int>(cast_params
.size()));
381 for (size_t i
= 0; i
< cast_params
.size(); ++i
) {
383 FromCastRtpParams(cast_params
[i
], ¶ms
);
384 scoped_ptr
<base::DictionaryValue
> params_value
= params
.ToValue();
387 converter
->ToV8Value(params_value
.get(), context()->v8_context()));
389 args
.GetReturnValue().Set(result
);
392 void CastStreamingNativeHandler::StartCastRtpStream(
393 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
394 CHECK_EQ(2, args
.Length());
395 CHECK(args
[0]->IsInt32());
396 CHECK(args
[1]->IsObject());
398 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
399 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
403 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
404 scoped_ptr
<base::Value
> params_value(
405 converter
->FromV8Value(args
[1], context()->v8_context()));
407 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
408 v8::String::NewFromUtf8(args
.GetIsolate(), kUnableToConvertParams
)));
411 scoped_ptr
<RtpParams
> params
= RtpParams::FromValue(*params_value
);
413 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
414 v8::String::NewFromUtf8(args
.GetIsolate(), kInvalidRtpParams
)));
418 CastRtpParams cast_params
;
419 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
420 if (!ToCastRtpParamsOrThrow(isolate
, *params
, &cast_params
))
423 base::Closure start_callback
=
424 base::Bind(&CastStreamingNativeHandler::CallStartCallback
,
425 weak_factory_
.GetWeakPtr(),
427 base::Closure stop_callback
=
428 base::Bind(&CastStreamingNativeHandler::CallStopCallback
,
429 weak_factory_
.GetWeakPtr(),
431 CastRtpStream::ErrorCallback error_callback
=
432 base::Bind(&CastStreamingNativeHandler::CallErrorCallback
,
433 weak_factory_
.GetWeakPtr(),
435 transport
->Start(cast_params
, start_callback
, stop_callback
, error_callback
);
438 void CastStreamingNativeHandler::StopCastRtpStream(
439 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
440 CHECK_EQ(1, args
.Length());
441 CHECK(args
[0]->IsInt32());
443 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
444 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
450 void CastStreamingNativeHandler::DestroyCastUdpTransport(
451 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
452 CHECK_EQ(1, args
.Length());
453 CHECK(args
[0]->IsInt32());
455 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
456 if (!GetUdpTransportOrThrow(transport_id
))
458 udp_transport_map_
.erase(transport_id
);
461 void CastStreamingNativeHandler::SetDestinationCastUdpTransport(
462 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
463 CHECK_EQ(2, args
.Length());
464 CHECK(args
[0]->IsInt32());
465 CHECK(args
[1]->IsObject());
467 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
468 CastUdpTransport
* transport
= GetUdpTransportOrThrow(transport_id
);
472 net::IPEndPoint dest
;
473 if (!IPEndPointFromArg(args
.GetIsolate(),
478 transport
->SetDestination(
480 base::Bind(&CastStreamingNativeHandler::CallErrorCallback
,
481 weak_factory_
.GetWeakPtr(),
485 void CastStreamingNativeHandler::SetOptionsCastUdpTransport(
486 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
487 CHECK_EQ(2, args
.Length());
488 CHECK(args
[0]->IsInt32());
489 CHECK(args
[1]->IsObject());
491 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
492 CastUdpTransport
* transport
= GetUdpTransportOrThrow(transport_id
);
496 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
497 base::Value
* options_value
=
498 converter
->FromV8Value(args
[1], context()->v8_context());
499 base::DictionaryValue
* options
;
500 if (!options_value
|| !options_value
->GetAsDictionary(&options
)) {
501 delete options_value
;
502 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
503 v8::String::NewFromUtf8(args
.GetIsolate(), kUnableToConvertArgs
)));
506 transport
->SetOptions(make_scoped_ptr(options
));
509 void CastStreamingNativeHandler::ToggleLogging(
510 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
511 CHECK_EQ(2, args
.Length());
512 CHECK(args
[0]->IsInt32());
513 CHECK(args
[1]->IsBoolean());
515 const int stream_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
516 CastRtpStream
* stream
= GetRtpStreamOrThrow(stream_id
);
520 const bool enable
= args
[1]->ToBoolean(args
.GetIsolate())->Value();
521 stream
->ToggleLogging(enable
);
524 void CastStreamingNativeHandler::GetRawEvents(
525 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
526 CHECK_EQ(3, args
.Length());
527 CHECK(args
[0]->IsInt32());
528 CHECK(args
[1]->IsNull() || args
[1]->IsString());
529 CHECK(args
[2]->IsFunction());
531 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
532 linked_ptr
<v8::Global
<v8::Function
>> callback(new v8::Global
<v8::Function
>(
533 args
.GetIsolate(), args
[2].As
<v8::Function
>()));
534 std::string extra_data
;
535 if (!args
[1]->IsNull()) {
536 extra_data
= *v8::String::Utf8Value(args
[1]);
539 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
543 get_raw_events_callbacks_
.insert(std::make_pair(transport_id
, callback
));
545 transport
->GetRawEvents(
546 base::Bind(&CastStreamingNativeHandler::CallGetRawEventsCallback
,
547 weak_factory_
.GetWeakPtr(),
552 void CastStreamingNativeHandler::GetStats(
553 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
554 CHECK_EQ(2, args
.Length());
555 CHECK(args
[0]->IsInt32());
556 CHECK(args
[1]->IsFunction());
557 const int transport_id
= args
[0]->ToInt32(args
.GetIsolate())->Value();
558 CastRtpStream
* transport
= GetRtpStreamOrThrow(transport_id
);
562 linked_ptr
<v8::Global
<v8::Function
>> callback(new v8::Global
<v8::Function
>(
563 args
.GetIsolate(), args
[1].As
<v8::Function
>()));
564 get_stats_callbacks_
.insert(std::make_pair(transport_id
, callback
));
567 base::Bind(&CastStreamingNativeHandler::CallGetStatsCallback
,
568 weak_factory_
.GetWeakPtr(),
572 void CastStreamingNativeHandler::CallGetRawEventsCallback(
574 scoped_ptr
<base::BinaryValue
> raw_events
) {
575 v8::Isolate
* isolate
= context()->isolate();
576 v8::HandleScope
handle_scope(isolate
);
577 v8::Context::Scope
context_scope(context()->v8_context());
579 RtpStreamCallbackMap::iterator it
=
580 get_raw_events_callbacks_
.find(transport_id
);
581 if (it
== get_raw_events_callbacks_
.end())
583 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
584 v8::Local
<v8::Value
> callback_args
[] = {
585 converter
->ToV8Value(raw_events
.get(), context()->v8_context())};
586 context()->CallFunction(v8::Local
<v8::Function
>::New(isolate
, *it
->second
),
587 arraysize(callback_args
), callback_args
);
588 get_raw_events_callbacks_
.erase(it
);
591 void CastStreamingNativeHandler::CallGetStatsCallback(
593 scoped_ptr
<base::DictionaryValue
> stats
) {
594 v8::Isolate
* isolate
= context()->isolate();
595 v8::HandleScope
handle_scope(isolate
);
596 v8::Context::Scope
context_scope(context()->v8_context());
598 RtpStreamCallbackMap::iterator it
= get_stats_callbacks_
.find(transport_id
);
599 if (it
== get_stats_callbacks_
.end())
602 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
603 v8::Local
<v8::Value
> callback_args
[] = {
604 converter
->ToV8Value(stats
.get(), context()->v8_context())};
605 context()->CallFunction(v8::Local
<v8::Function
>::New(isolate
, *it
->second
),
606 arraysize(callback_args
), callback_args
);
607 get_stats_callbacks_
.erase(it
);
610 CastRtpStream
* CastStreamingNativeHandler::GetRtpStreamOrThrow(
611 int transport_id
) const {
612 RtpStreamMap::const_iterator iter
= rtp_stream_map_
.find(
614 if (iter
!= rtp_stream_map_
.end())
615 return iter
->second
.get();
616 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
617 isolate
->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8(
618 isolate
, kRtpStreamNotFound
)));
622 CastUdpTransport
* CastStreamingNativeHandler::GetUdpTransportOrThrow(
623 int transport_id
) const {
624 UdpTransportMap::const_iterator iter
= udp_transport_map_
.find(
626 if (iter
!= udp_transport_map_
.end())
627 return iter
->second
.get();
628 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
629 isolate
->ThrowException(v8::Exception::RangeError(
630 v8::String::NewFromUtf8(isolate
, kUdpTransportNotFound
)));
634 bool CastStreamingNativeHandler::FrameReceiverConfigFromArg(
635 v8::Isolate
* isolate
,
636 const v8::Local
<v8::Value
>& arg
,
637 media::cast::FrameReceiverConfig
* config
) const {
638 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
639 scoped_ptr
<base::Value
> params_value(
640 converter
->FromV8Value(arg
, context()->v8_context()));
642 isolate
->ThrowException(v8::Exception::TypeError(
643 v8::String::NewFromUtf8(isolate
, kUnableToConvertParams
)));
646 scoped_ptr
<RtpReceiverParams
> params
=
647 RtpReceiverParams::FromValue(*params_value
);
649 isolate
->ThrowException(v8::Exception::TypeError(
650 v8::String::NewFromUtf8(isolate
, kInvalidRtpParams
)));
654 config
->receiver_ssrc
= params
->receiver_ssrc
;
655 config
->sender_ssrc
= params
->sender_ssrc
;
656 config
->rtp_max_delay_ms
= params
->max_latency
;
657 if (config
->rtp_max_delay_ms
< 0 || config
->rtp_max_delay_ms
> 1000) {
658 isolate
->ThrowException(v8::Exception::TypeError(
659 v8::String::NewFromUtf8(isolate
, kInvalidLatency
)));
662 config
->channels
= 2;
663 if (params
->codec_name
== "OPUS") {
664 config
->codec
= media::cast::CODEC_AUDIO_OPUS
;
665 config
->rtp_timebase
= 48000;
666 config
->rtp_payload_type
= 127;
667 } else if (params
->codec_name
== "PCM16") {
668 config
->codec
= media::cast::CODEC_AUDIO_PCM16
;
669 config
->rtp_timebase
= 48000;
670 config
->rtp_payload_type
= 127;
671 } else if (params
->codec_name
== "AAC") {
672 config
->codec
= media::cast::CODEC_AUDIO_AAC
;
673 config
->rtp_timebase
= 48000;
674 config
->rtp_payload_type
= 127;
675 } else if (params
->codec_name
== "VP8") {
676 config
->codec
= media::cast::CODEC_VIDEO_VP8
;
677 config
->rtp_timebase
= 90000;
678 config
->rtp_payload_type
= 96;
679 } else if (params
->codec_name
== "H264") {
680 config
->codec
= media::cast::CODEC_VIDEO_H264
;
681 config
->rtp_timebase
= 90000;
682 config
->rtp_payload_type
= 96;
684 if (params
->rtp_timebase
) {
685 config
->rtp_timebase
= *params
->rtp_timebase
;
686 if (config
->rtp_timebase
< 1000 || config
->rtp_timebase
> 1000000) {
687 isolate
->ThrowException(v8::Exception::TypeError(
688 v8::String::NewFromUtf8(isolate
, kInvalidRtpTimebase
)));
692 if (params
->aes_key
&&
693 !HexDecode(*params
->aes_key
, &config
->aes_key
)) {
694 isolate
->ThrowException(v8::Exception::Error(
695 v8::String::NewFromUtf8(isolate
, kInvalidAesKey
)));
698 if (params
->aes_iv_mask
&&
699 !HexDecode(*params
->aes_iv_mask
, &config
->aes_iv_mask
)) {
700 isolate
->ThrowException(v8::Exception::Error(
701 v8::String::NewFromUtf8(isolate
, kInvalidAesIvMask
)));
707 bool CastStreamingNativeHandler::IPEndPointFromArg(
708 v8::Isolate
* isolate
,
709 const v8::Local
<v8::Value
>& arg
,
710 net::IPEndPoint
* ip_endpoint
) const {
711 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
712 scoped_ptr
<base::Value
> destination_value(
713 converter
->FromV8Value(arg
, context()->v8_context()));
714 if (!destination_value
) {
715 isolate
->ThrowException(v8::Exception::TypeError(
716 v8::String::NewFromUtf8(isolate
, kInvalidAesIvMask
)));
719 scoped_ptr
<IPEndPoint
> destination
=
720 IPEndPoint::FromValue(*destination_value
);
722 isolate
->ThrowException(v8::Exception::TypeError(
723 v8::String::NewFromUtf8(isolate
, kInvalidDestination
)));
726 net::IPAddressNumber ip
;
727 if (!net::ParseIPLiteralToNumber(destination
->address
, &ip
)) {
728 isolate
->ThrowException(v8::Exception::TypeError(
729 v8::String::NewFromUtf8(isolate
, kInvalidDestination
)));
732 *ip_endpoint
= net::IPEndPoint(ip
, destination
->port
);
736 void CastStreamingNativeHandler::StartCastRtpReceiver(
737 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
738 if (args
.Length() < 8 || args
.Length() > 9 ||
739 !args
[0]->IsObject() ||
740 !args
[1]->IsObject() ||
741 !args
[2]->IsObject() ||
742 !args
[3]->IsInt32() ||
743 !args
[4]->IsInt32() ||
744 !args
[5]->IsNumber() ||
745 !args
[6]->IsString()) {
746 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
747 v8::String::NewFromUtf8(args
.GetIsolate(), kUnableToConvertArgs
)));
751 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
753 scoped_refptr
<CastReceiverSession
> session(
754 new CastReceiverSession());
755 media::cast::FrameReceiverConfig audio_config
;
756 media::cast::FrameReceiverConfig video_config
;
757 net::IPEndPoint local_endpoint
;
758 net::IPEndPoint remote_endpoint
;
760 if (!FrameReceiverConfigFromArg(isolate
, args
[0], &audio_config
) ||
761 !FrameReceiverConfigFromArg(isolate
, args
[1], &video_config
) ||
762 !IPEndPointFromArg(isolate
, args
[2], &local_endpoint
)) {
766 const std::string url
= *v8::String::Utf8Value(args
[7]);
767 blink::WebMediaStream stream
=
768 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url
));
770 if (stream
.isNull()) {
771 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
772 v8::String::NewFromUtf8(args
.GetIsolate(), kInvalidMediaStreamURL
)));
776 const int max_width
= args
[3]->ToInt32(args
.GetIsolate())->Value();
777 const int max_height
= args
[4]->ToInt32(args
.GetIsolate())->Value();
778 const double fps
= args
[5]->NumberValue();
781 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
782 v8::String::NewFromUtf8(args
.GetIsolate(), kInvalidFPS
)));
786 media::VideoCaptureFormat
capture_format(gfx::Size(max_width
, max_height
),
787 fps
, media::PIXEL_FORMAT_I420
);
789 video_config
.target_frame_rate
= fps
;
790 audio_config
.target_frame_rate
= 100;
792 media::AudioParameters
params(
793 media::AudioParameters::AUDIO_PCM_LINEAR
,
794 media::CHANNEL_LAYOUT_STEREO
,
795 audio_config
.rtp_timebase
, // sampling rate
797 audio_config
.rtp_timebase
/ audio_config
.target_frame_rate
);
799 if (!params
.IsValid()) {
800 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
801 v8::String::NewFromUtf8(args
.GetIsolate(), kInvalidAudioParams
)));
805 base::DictionaryValue
* options
= NULL
;
806 if (args
.Length() >= 10) {
807 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
808 base::Value
* options_value
=
809 converter
->FromV8Value(args
[8], context()->v8_context());
810 if (!options_value
->IsType(base::Value::TYPE_NULL
)) {
811 if (!options_value
|| !options_value
->GetAsDictionary(&options
)) {
812 delete options_value
;
813 args
.GetIsolate()->ThrowException(v8::Exception::TypeError(
814 v8::String::NewFromUtf8(args
.GetIsolate(), kUnableToConvertArgs
)));
821 options
= new base::DictionaryValue();
824 v8::CopyablePersistentTraits
<v8::Function
>::CopyablePersistent error_callback
;
825 error_callback
.Reset(args
.GetIsolate(),
826 v8::Local
<v8::Function
>(args
[7].As
<v8::Function
>()));
833 make_scoped_ptr(options
),
835 base::Bind(&CastStreamingNativeHandler::AddTracksToMediaStream
,
836 weak_factory_
.GetWeakPtr(),
839 base::Bind(&CastStreamingNativeHandler::CallReceiverErrorCallback
,
840 weak_factory_
.GetWeakPtr(),
844 void CastStreamingNativeHandler::CallReceiverErrorCallback(
845 v8::CopyablePersistentTraits
<v8::Function
>::CopyablePersistent function
,
846 const std::string
& error_message
) {
847 v8::Isolate
* isolate
= context()->v8_context()->GetIsolate();
848 v8::Local
<v8::Value
> arg
= v8::String::NewFromUtf8(isolate
,
849 error_message
.data(),
850 v8::String::kNormalString
,
851 error_message
.size());
852 context()->CallFunction(
853 v8::Local
<v8::Function
>::New(isolate
, function
), 1, &arg
);
857 void CastStreamingNativeHandler::AddTracksToMediaStream(
858 const std::string
& url
,
859 const media::AudioParameters
& params
,
860 scoped_refptr
<media::AudioCapturerSource
> audio
,
861 scoped_ptr
<media::VideoCapturerSource
> video
) {
862 content::AddAudioTrackToMediaStream(audio
, params
, true, true, url
);
863 content::AddVideoTrackToMediaStream(video
.Pass(), true, true, url
);
866 } // namespace extensions