Componentize ShortcutsBackend
[chromium-blink-merge.git] / chrome / renderer / extensions / cast_streaming_native_handler.cc
blob66fb8a7428bd829e26daf8514f49159c8345a069
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"
7 #include <functional>
8 #include <iterator>
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"
32 #include "url/gurl.h"
34 using content::V8ValueConverter;
36 // Extension types.
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 {
45 namespace {
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;
75 namespace {
76 bool HexDecode(const std::string& input, std::string* output) {
77 std::vector<uint8> bytes;
78 if (!base::HexStringToBytes(input, &bytes))
79 return false;
80 output->assign(reinterpret_cast<const char*>(&bytes[0]), bytes.size());
81 return true;
83 } // namespace
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)));
107 return false;
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)));
113 return false;
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],
118 &cast_codec_params);
119 cast_params->codec_specific_params.push_back(cast_codec_params);
121 return true;
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,
166 ext_params.payload,
167 &cast_params->payload)) {
168 return false;
170 return true;
173 } // namespace
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 base::Unretained(this)));
182 RouteFunction("DestroyCastRtpStream",
183 base::Bind(&CastStreamingNativeHandler::DestroyCastRtpStream,
184 base::Unretained(this)));
185 RouteFunction("GetSupportedParamsCastRtpStream",
186 base::Bind(&CastStreamingNativeHandler::GetSupportedParamsCastRtpStream,
187 base::Unretained(this)));
188 RouteFunction("StartCastRtpStream",
189 base::Bind(&CastStreamingNativeHandler::StartCastRtpStream,
190 base::Unretained(this)));
191 RouteFunction("StopCastRtpStream",
192 base::Bind(&CastStreamingNativeHandler::StopCastRtpStream,
193 base::Unretained(this)));
194 RouteFunction("DestroyCastUdpTransport",
195 base::Bind(&CastStreamingNativeHandler::DestroyCastUdpTransport,
196 base::Unretained(this)));
197 RouteFunction("SetDestinationCastUdpTransport",
198 base::Bind(&CastStreamingNativeHandler::SetDestinationCastUdpTransport,
199 base::Unretained(this)));
200 RouteFunction("SetOptionsCastUdpTransport",
201 base::Bind(&CastStreamingNativeHandler::SetOptionsCastUdpTransport,
202 base::Unretained(this)));
203 RouteFunction("ToggleLogging",
204 base::Bind(&CastStreamingNativeHandler::ToggleLogging,
205 base::Unretained(this)));
206 RouteFunction("GetRawEvents",
207 base::Bind(&CastStreamingNativeHandler::GetRawEvents,
208 base::Unretained(this)));
209 RouteFunction("GetStats",
210 base::Bind(&CastStreamingNativeHandler::GetStats,
211 base::Unretained(this)));
212 RouteFunction("StartCastRtpReceiver",
213 base::Bind(&CastStreamingNativeHandler::StartCastRtpReceiver,
214 base::Unretained(this)));
217 CastStreamingNativeHandler::~CastStreamingNativeHandler() {
220 void CastStreamingNativeHandler::CreateCastSession(
221 const v8::FunctionCallbackInfo<v8::Value>& args) {
222 CHECK_EQ(3, args.Length());
223 CHECK(args[2]->IsFunction());
225 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
226 if ((args[0]->IsNull() || args[0]->IsUndefined()) &&
227 (args[1]->IsNull() || args[1]->IsUndefined())) {
228 isolate->ThrowException(v8::Exception::Error(
229 v8::String::NewFromUtf8(isolate, kInvalidStreamArgs)));
230 return;
233 scoped_refptr<CastSession> session(new CastSession());
234 scoped_ptr<CastRtpStream> stream1, stream2;
235 if (!args[0]->IsNull() && !args[0]->IsUndefined()) {
236 CHECK(args[0]->IsObject());
237 blink::WebDOMMediaStreamTrack track =
238 blink::WebDOMMediaStreamTrack::fromV8Value(args[0]);
239 if (track.isNull()) {
240 isolate->ThrowException(v8::Exception::Error(
241 v8::String::NewFromUtf8(isolate, kInvalidStreamArgs)));
242 return;
244 stream1.reset(new CastRtpStream(track.component(), session));
246 if (!args[1]->IsNull() && !args[1]->IsUndefined()) {
247 CHECK(args[1]->IsObject());
248 blink::WebDOMMediaStreamTrack track =
249 blink::WebDOMMediaStreamTrack::fromV8Value(args[1]);
250 if (track.isNull()) {
251 isolate->ThrowException(v8::Exception::Error(
252 v8::String::NewFromUtf8(isolate, kInvalidStreamArgs)));
253 return;
255 stream2.reset(new CastRtpStream(track.component(), session));
257 scoped_ptr<CastUdpTransport> udp_transport(
258 new CastUdpTransport(session));
260 // TODO(imcheng): Use a weak reference to ensure we don't call into an
261 // invalid context when the callback is invoked.
262 create_callback_.Reset(isolate, args[2].As<v8::Function>());
264 base::ThreadTaskRunnerHandle::Get()->PostTask(
265 FROM_HERE,
266 base::Bind(&CastStreamingNativeHandler::CallCreateCallback,
267 weak_factory_.GetWeakPtr(), base::Passed(&stream1),
268 base::Passed(&stream2), base::Passed(&udp_transport)));
271 void CastStreamingNativeHandler::CallCreateCallback(
272 scoped_ptr<CastRtpStream> stream1,
273 scoped_ptr<CastRtpStream> stream2,
274 scoped_ptr<CastUdpTransport> udp_transport) {
275 v8::Isolate* isolate = context()->isolate();
276 v8::HandleScope handle_scope(isolate);
277 v8::Context::Scope context_scope(context()->v8_context());
279 v8::Local<v8::Value> callback_args[3];
280 callback_args[0] = v8::Null(isolate);
281 callback_args[1] = v8::Null(isolate);
283 if (stream1) {
284 const int stream1_id = last_transport_id_++;
285 callback_args[0] = v8::Integer::New(isolate, stream1_id);
286 rtp_stream_map_[stream1_id] =
287 linked_ptr<CastRtpStream>(stream1.release());
289 if (stream2) {
290 const int stream2_id = last_transport_id_++;
291 callback_args[1] = v8::Integer::New(isolate, stream2_id);
292 rtp_stream_map_[stream2_id] =
293 linked_ptr<CastRtpStream>(stream2.release());
295 const int udp_id = last_transport_id_++;
296 udp_transport_map_[udp_id] =
297 linked_ptr<CastUdpTransport>(udp_transport.release());
298 callback_args[2] = v8::Integer::New(isolate, udp_id);
299 context()->CallFunction(
300 v8::Local<v8::Function>::New(isolate, create_callback_), 3,
301 callback_args);
302 create_callback_.Reset();
305 void CastStreamingNativeHandler::CallStartCallback(int stream_id) {
306 v8::Isolate* isolate = context()->isolate();
307 v8::HandleScope handle_scope(isolate);
308 v8::Context::Scope context_scope(context()->v8_context());
309 v8::Local<v8::Array> event_args = v8::Array::New(isolate, 1);
310 event_args->Set(0, v8::Integer::New(isolate, stream_id));
311 context()->DispatchEvent("cast.streaming.rtpStream.onStarted", event_args);
314 void CastStreamingNativeHandler::CallStopCallback(int stream_id) {
315 v8::Isolate* isolate = context()->isolate();
316 v8::HandleScope handle_scope(isolate);
317 v8::Context::Scope context_scope(context()->v8_context());
318 v8::Local<v8::Array> event_args = v8::Array::New(isolate, 1);
319 event_args->Set(0, v8::Integer::New(isolate, stream_id));
320 context()->DispatchEvent("cast.streaming.rtpStream.onStopped", event_args);
323 void CastStreamingNativeHandler::CallErrorCallback(int stream_id,
324 const std::string& message) {
325 v8::Isolate* isolate = context()->isolate();
326 v8::HandleScope handle_scope(isolate);
327 v8::Context::Scope context_scope(context()->v8_context());
328 v8::Local<v8::Array> event_args = v8::Array::New(isolate, 2);
329 event_args->Set(0, v8::Integer::New(isolate, stream_id));
330 event_args->Set(
332 v8::String::NewFromUtf8(
333 isolate, message.data(), v8::String::kNormalString, message.size()));
334 context()->DispatchEvent("cast.streaming.rtpStream.onError", event_args);
337 void CastStreamingNativeHandler::DestroyCastRtpStream(
338 const v8::FunctionCallbackInfo<v8::Value>& args) {
339 CHECK_EQ(1, args.Length());
340 CHECK(args[0]->IsInt32());
342 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value();
343 if (!GetRtpStreamOrThrow(transport_id))
344 return;
345 rtp_stream_map_.erase(transport_id);
348 void CastStreamingNativeHandler::GetSupportedParamsCastRtpStream(
349 const v8::FunctionCallbackInfo<v8::Value>& args) {
350 CHECK_EQ(1, args.Length());
351 CHECK(args[0]->IsInt32());
353 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value();
354 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
355 if (!transport)
356 return;
358 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
359 std::vector<CastRtpParams> cast_params = transport->GetSupportedParams();
360 v8::Local<v8::Array> result =
361 v8::Array::New(args.GetIsolate(),
362 static_cast<int>(cast_params.size()));
363 for (size_t i = 0; i < cast_params.size(); ++i) {
364 RtpParams params;
365 FromCastRtpParams(cast_params[i], &params);
366 scoped_ptr<base::DictionaryValue> params_value = params.ToValue();
367 result->Set(
368 static_cast<int>(i),
369 converter->ToV8Value(params_value.get(), context()->v8_context()));
371 args.GetReturnValue().Set(result);
374 void CastStreamingNativeHandler::StartCastRtpStream(
375 const v8::FunctionCallbackInfo<v8::Value>& args) {
376 CHECK_EQ(2, args.Length());
377 CHECK(args[0]->IsInt32());
378 CHECK(args[1]->IsObject());
380 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value();
381 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
382 if (!transport)
383 return;
385 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
386 scoped_ptr<base::Value> params_value(
387 converter->FromV8Value(args[1], context()->v8_context()));
388 if (!params_value) {
389 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
390 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertParams)));
391 return;
393 scoped_ptr<RtpParams> params = RtpParams::FromValue(*params_value);
394 if (!params) {
395 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
396 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidRtpParams)));
397 return;
400 CastRtpParams cast_params;
401 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
402 if (!ToCastRtpParamsOrThrow(isolate, *params, &cast_params))
403 return;
405 base::Closure start_callback =
406 base::Bind(&CastStreamingNativeHandler::CallStartCallback,
407 weak_factory_.GetWeakPtr(),
408 transport_id);
409 base::Closure stop_callback =
410 base::Bind(&CastStreamingNativeHandler::CallStopCallback,
411 weak_factory_.GetWeakPtr(),
412 transport_id);
413 CastRtpStream::ErrorCallback error_callback =
414 base::Bind(&CastStreamingNativeHandler::CallErrorCallback,
415 weak_factory_.GetWeakPtr(),
416 transport_id);
417 transport->Start(cast_params, start_callback, stop_callback, error_callback);
420 void CastStreamingNativeHandler::StopCastRtpStream(
421 const v8::FunctionCallbackInfo<v8::Value>& args) {
422 CHECK_EQ(1, args.Length());
423 CHECK(args[0]->IsInt32());
425 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value();
426 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
427 if (!transport)
428 return;
429 transport->Stop();
432 void CastStreamingNativeHandler::DestroyCastUdpTransport(
433 const v8::FunctionCallbackInfo<v8::Value>& args) {
434 CHECK_EQ(1, args.Length());
435 CHECK(args[0]->IsInt32());
437 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value();
438 if (!GetUdpTransportOrThrow(transport_id))
439 return;
440 udp_transport_map_.erase(transport_id);
443 void CastStreamingNativeHandler::SetDestinationCastUdpTransport(
444 const v8::FunctionCallbackInfo<v8::Value>& args) {
445 CHECK_EQ(2, args.Length());
446 CHECK(args[0]->IsInt32());
447 CHECK(args[1]->IsObject());
449 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value();
450 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id);
451 if (!transport)
452 return;
454 net::IPEndPoint dest;
455 if (!IPEndPointFromArg(args.GetIsolate(),
456 args[1],
457 &dest)) {
458 return;
460 transport->SetDestination(
461 dest,
462 base::Bind(&CastStreamingNativeHandler::CallErrorCallback,
463 weak_factory_.GetWeakPtr(),
464 transport_id));
467 void CastStreamingNativeHandler::SetOptionsCastUdpTransport(
468 const v8::FunctionCallbackInfo<v8::Value>& args) {
469 CHECK_EQ(2, args.Length());
470 CHECK(args[0]->IsInt32());
471 CHECK(args[1]->IsObject());
473 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value();
474 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id);
475 if (!transport)
476 return;
478 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
479 base::Value* options_value =
480 converter->FromV8Value(args[1], context()->v8_context());
481 base::DictionaryValue* options;
482 if (!options_value || !options_value->GetAsDictionary(&options)) {
483 delete options_value;
484 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
485 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs)));
486 return;
488 transport->SetOptions(make_scoped_ptr(options));
491 void CastStreamingNativeHandler::ToggleLogging(
492 const v8::FunctionCallbackInfo<v8::Value>& args) {
493 CHECK_EQ(2, args.Length());
494 CHECK(args[0]->IsInt32());
495 CHECK(args[1]->IsBoolean());
497 const int stream_id = args[0]->ToInt32(args.GetIsolate())->Value();
498 CastRtpStream* stream = GetRtpStreamOrThrow(stream_id);
499 if (!stream)
500 return;
502 const bool enable = args[1]->ToBoolean(args.GetIsolate())->Value();
503 stream->ToggleLogging(enable);
506 void CastStreamingNativeHandler::GetRawEvents(
507 const v8::FunctionCallbackInfo<v8::Value>& args) {
508 CHECK_EQ(3, args.Length());
509 CHECK(args[0]->IsInt32());
510 CHECK(args[1]->IsNull() || args[1]->IsString());
511 CHECK(args[2]->IsFunction());
513 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value();
514 // TODO(imcheng): Use a weak reference to ensure we don't call into an
515 // invalid context when the callback is invoked.
516 linked_ptr<v8::Global<v8::Function>> callback(new v8::Global<v8::Function>(
517 args.GetIsolate(), args[2].As<v8::Function>()));
518 std::string extra_data;
519 if (!args[1]->IsNull()) {
520 extra_data = *v8::String::Utf8Value(args[1]);
523 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
524 if (!transport)
525 return;
527 get_raw_events_callbacks_.insert(std::make_pair(transport_id, callback));
529 transport->GetRawEvents(
530 base::Bind(&CastStreamingNativeHandler::CallGetRawEventsCallback,
531 weak_factory_.GetWeakPtr(),
532 transport_id),
533 extra_data);
536 void CastStreamingNativeHandler::GetStats(
537 const v8::FunctionCallbackInfo<v8::Value>& args) {
538 CHECK_EQ(2, args.Length());
539 CHECK(args[0]->IsInt32());
540 CHECK(args[1]->IsFunction());
541 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value();
542 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
543 if (!transport)
544 return;
546 // TODO(imcheng): Use a weak reference to ensure we don't call into an
547 // invalid context when the callback is invoked.
548 linked_ptr<v8::Global<v8::Function>> callback(new v8::Global<v8::Function>(
549 args.GetIsolate(), args[1].As<v8::Function>()));
550 get_stats_callbacks_.insert(std::make_pair(transport_id, callback));
552 transport->GetStats(
553 base::Bind(&CastStreamingNativeHandler::CallGetStatsCallback,
554 weak_factory_.GetWeakPtr(),
555 transport_id));
558 void CastStreamingNativeHandler::CallGetRawEventsCallback(
559 int transport_id,
560 scoped_ptr<base::BinaryValue> raw_events) {
561 v8::Isolate* isolate = context()->isolate();
562 v8::HandleScope handle_scope(isolate);
563 v8::Context::Scope context_scope(context()->v8_context());
565 RtpStreamCallbackMap::iterator it =
566 get_raw_events_callbacks_.find(transport_id);
567 if (it == get_raw_events_callbacks_.end())
568 return;
569 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
570 v8::Local<v8::Value> callback_args[] = {
571 converter->ToV8Value(raw_events.get(), context()->v8_context())};
572 context()->CallFunction(v8::Local<v8::Function>::New(isolate, *it->second),
573 arraysize(callback_args), callback_args);
574 get_raw_events_callbacks_.erase(it);
577 void CastStreamingNativeHandler::CallGetStatsCallback(
578 int transport_id,
579 scoped_ptr<base::DictionaryValue> stats) {
580 v8::Isolate* isolate = context()->isolate();
581 v8::HandleScope handle_scope(isolate);
582 v8::Context::Scope context_scope(context()->v8_context());
584 RtpStreamCallbackMap::iterator it = get_stats_callbacks_.find(transport_id);
585 if (it == get_stats_callbacks_.end())
586 return;
588 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
589 v8::Local<v8::Value> callback_args[] = {
590 converter->ToV8Value(stats.get(), context()->v8_context())};
591 context()->CallFunction(v8::Local<v8::Function>::New(isolate, *it->second),
592 arraysize(callback_args), callback_args);
593 get_stats_callbacks_.erase(it);
596 CastRtpStream* CastStreamingNativeHandler::GetRtpStreamOrThrow(
597 int transport_id) const {
598 RtpStreamMap::const_iterator iter = rtp_stream_map_.find(
599 transport_id);
600 if (iter != rtp_stream_map_.end())
601 return iter->second.get();
602 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
603 isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8(
604 isolate, kRtpStreamNotFound)));
605 return NULL;
608 CastUdpTransport* CastStreamingNativeHandler::GetUdpTransportOrThrow(
609 int transport_id) const {
610 UdpTransportMap::const_iterator iter = udp_transport_map_.find(
611 transport_id);
612 if (iter != udp_transport_map_.end())
613 return iter->second.get();
614 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
615 isolate->ThrowException(v8::Exception::RangeError(
616 v8::String::NewFromUtf8(isolate, kUdpTransportNotFound)));
617 return NULL;
620 bool CastStreamingNativeHandler::FrameReceiverConfigFromArg(
621 v8::Isolate* isolate,
622 const v8::Local<v8::Value>& arg,
623 media::cast::FrameReceiverConfig* config) {
625 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
626 scoped_ptr<base::Value> params_value(
627 converter->FromV8Value(arg, context()->v8_context()));
628 if (!params_value) {
629 isolate->ThrowException(v8::Exception::TypeError(
630 v8::String::NewFromUtf8(isolate, kUnableToConvertParams)));
631 return false;
633 scoped_ptr<RtpReceiverParams> params =
634 RtpReceiverParams::FromValue(*params_value);
635 if (!params) {
636 isolate->ThrowException(v8::Exception::TypeError(
637 v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
638 return false;
641 config->receiver_ssrc = params->receiver_ssrc;
642 config->sender_ssrc = params->sender_ssrc;
643 config->rtp_max_delay_ms = params->max_latency;
644 if (config->rtp_max_delay_ms < 0 || config->rtp_max_delay_ms > 1000) {
645 isolate->ThrowException(v8::Exception::TypeError(
646 v8::String::NewFromUtf8(isolate, kInvalidLatency)));
647 return false;
649 config->channels = 2;
650 if (params->codec_name == "OPUS") {
651 config->codec = media::cast::CODEC_AUDIO_OPUS;
652 config->rtp_timebase = 48000;
653 config->rtp_payload_type = 127;
654 } else if (params->codec_name == "PCM16") {
655 config->codec = media::cast::CODEC_AUDIO_PCM16;
656 config->rtp_timebase = 48000;
657 config->rtp_payload_type = 127;
658 } else if (params->codec_name == "AAC") {
659 config->codec = media::cast::CODEC_AUDIO_AAC;
660 config->rtp_timebase = 48000;
661 config->rtp_payload_type = 127;
662 } else if (params->codec_name == "VP8") {
663 config->codec = media::cast::CODEC_VIDEO_VP8;
664 config->rtp_timebase = 90000;
665 config->rtp_payload_type = 96;
666 } else if (params->codec_name == "H264") {
667 config->codec = media::cast::CODEC_VIDEO_H264;
668 config->rtp_timebase = 90000;
669 config->rtp_payload_type = 96;
671 if (params->rtp_timebase) {
672 config->rtp_timebase = *params->rtp_timebase;
673 if (config->rtp_timebase < 1000 || config->rtp_timebase > 1000000) {
674 isolate->ThrowException(v8::Exception::TypeError(
675 v8::String::NewFromUtf8(isolate, kInvalidRtpTimebase)));
676 return false;
679 if (params->aes_key &&
680 !HexDecode(*params->aes_key, &config->aes_key)) {
681 isolate->ThrowException(v8::Exception::Error(
682 v8::String::NewFromUtf8(isolate, kInvalidAesKey)));
683 return false;
685 if (params->aes_iv_mask &&
686 !HexDecode(*params->aes_iv_mask, &config->aes_iv_mask)) {
687 isolate->ThrowException(v8::Exception::Error(
688 v8::String::NewFromUtf8(isolate, kInvalidAesIvMask)));
689 return false;
691 return true;
694 bool CastStreamingNativeHandler::IPEndPointFromArg(
695 v8::Isolate* isolate,
696 const v8::Local<v8::Value>& arg,
697 net::IPEndPoint* ip_endpoint) {
698 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
699 scoped_ptr<base::Value> destination_value(
700 converter->FromV8Value(arg, context()->v8_context()));
701 if (!destination_value) {
702 isolate->ThrowException(v8::Exception::TypeError(
703 v8::String::NewFromUtf8(isolate, kInvalidAesIvMask)));
704 return false;
706 scoped_ptr<IPEndPoint> destination =
707 IPEndPoint::FromValue(*destination_value);
708 if (!destination) {
709 isolate->ThrowException(v8::Exception::TypeError(
710 v8::String::NewFromUtf8(isolate, kInvalidDestination)));
711 return false;
713 net::IPAddressNumber ip;
714 if (!net::ParseIPLiteralToNumber(destination->address, &ip)) {
715 isolate->ThrowException(v8::Exception::TypeError(
716 v8::String::NewFromUtf8(isolate, kInvalidDestination)));
717 return false;
719 *ip_endpoint = net::IPEndPoint(ip, destination->port);
720 return true;
723 void CastStreamingNativeHandler::StartCastRtpReceiver(
724 const v8::FunctionCallbackInfo<v8::Value>& args) {
725 if (args.Length() < 8 || args.Length() > 9 ||
726 !args[0]->IsObject() ||
727 !args[1]->IsObject() ||
728 !args[2]->IsObject() ||
729 !args[3]->IsInt32() ||
730 !args[4]->IsInt32() ||
731 !args[5]->IsNumber() ||
732 !args[6]->IsString()) {
733 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
734 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs)));
735 return;
738 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
740 scoped_refptr<CastReceiverSession> session(
741 new CastReceiverSession());
742 media::cast::FrameReceiverConfig audio_config;
743 media::cast::FrameReceiverConfig video_config;
744 net::IPEndPoint local_endpoint;
745 net::IPEndPoint remote_endpoint;
747 if (!FrameReceiverConfigFromArg(isolate, args[0], &audio_config) ||
748 !FrameReceiverConfigFromArg(isolate, args[1], &video_config) ||
749 !IPEndPointFromArg(isolate, args[2], &local_endpoint)) {
750 return;
753 const std::string url = *v8::String::Utf8Value(args[7]);
754 blink::WebMediaStream stream =
755 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url));
757 if (stream.isNull()) {
758 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
759 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidMediaStreamURL)));
760 return;
763 const int max_width = args[3]->ToInt32(args.GetIsolate())->Value();
764 const int max_height = args[4]->ToInt32(args.GetIsolate())->Value();
765 const double fps = args[5]->NumberValue();
767 if (fps <= 1) {
768 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
769 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidFPS)));
770 return;
773 media::VideoCaptureFormat capture_format(
774 gfx::Size(max_width, max_height),
775 fps,
776 media::PIXEL_FORMAT_I420);
778 video_config.target_frame_rate = fps;
779 audio_config.target_frame_rate = 100;
781 media::AudioParameters params(
782 media::AudioParameters::AUDIO_PCM_LINEAR,
783 media::CHANNEL_LAYOUT_STEREO,
784 audio_config.rtp_timebase, // sampling rate
786 audio_config.rtp_timebase / audio_config.target_frame_rate);
788 if (!params.IsValid()) {
789 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
790 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidAudioParams)));
791 return;
794 base::DictionaryValue* options = NULL;
795 if (args.Length() >= 10) {
796 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
797 base::Value* options_value =
798 converter->FromV8Value(args[8], context()->v8_context());
799 if (!options_value->IsType(base::Value::TYPE_NULL)) {
800 if (!options_value || !options_value->GetAsDictionary(&options)) {
801 delete options_value;
802 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
803 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs)));
804 return;
809 if (!options) {
810 options = new base::DictionaryValue();
813 v8::CopyablePersistentTraits<v8::Function>::CopyablePersistent error_callback;
814 error_callback.Reset(args.GetIsolate(),
815 v8::Local<v8::Function>(args[7].As<v8::Function>()));
817 session->Start(
818 audio_config,
819 video_config,
820 local_endpoint,
821 remote_endpoint,
822 make_scoped_ptr(options),
823 capture_format,
824 base::Bind(&CastStreamingNativeHandler::AddTracksToMediaStream,
825 weak_factory_.GetWeakPtr(),
826 url,
827 params),
828 base::Bind(&CastStreamingNativeHandler::CallReceiverErrorCallback,
829 weak_factory_.GetWeakPtr(),
830 error_callback));
833 void CastStreamingNativeHandler::CallReceiverErrorCallback(
834 v8::CopyablePersistentTraits<v8::Function>::CopyablePersistent function,
835 const std::string& error_message) {
836 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
837 v8::Local<v8::Value> arg = v8::String::NewFromUtf8(isolate,
838 error_message.data(),
839 v8::String::kNormalString,
840 error_message.size());
841 context()->CallFunction(
842 v8::Local<v8::Function>::New(isolate, function), 1, &arg);
846 void CastStreamingNativeHandler::AddTracksToMediaStream(
847 const std::string& url,
848 const media::AudioParameters& params,
849 scoped_refptr<media::AudioCapturerSource> audio,
850 scoped_ptr<media::VideoCapturerSource> video) {
851 content::AddAudioTrackToMediaStream(audio, params, true, true, url);
852 content::AddVideoTrackToMediaStream(video.Pass(), true, true, url);
855 } // namespace extensions