Give names to all utility processes.
[chromium-blink-merge.git] / chrome / renderer / extensions / cast_streaming_native_handler.cc
blob6b42643bfe2cc9f14139d24c89a53fd9fed03a32
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/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"
30 #include "url/gurl.h"
32 using content::V8ValueConverter;
34 // Extension types.
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 {
43 namespace {
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;
73 namespace {
74 bool HexDecode(const std::string& input, std::string* output) {
75 std::vector<uint8> bytes;
76 if (!base::HexStringToBytes(input, &bytes))
77 return false;
78 output->assign(reinterpret_cast<const char*>(&bytes[0]), bytes.size());
79 return true;
81 } // namespace
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)));
105 return false;
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)));
111 return false;
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],
116 &cast_codec_params);
117 cast_params->codec_specific_params.push_back(cast_codec_params);
119 return true;
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,
164 ext_params.payload,
165 &cast_params->payload)) {
166 return false;
168 return true;
171 } // namespace
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)));
228 return;
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)));
240 return;
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)));
251 return;
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(
263 FROM_HERE,
264 base::Bind(
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);
284 if (stream1) {
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());
290 if (stream2) {
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,
302 callback_args);
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));
331 event_args->Set(
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))
345 return;
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);
356 if (!transport)
357 return;
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) {
365 RtpParams params;
366 FromCastRtpParams(cast_params[i], &params);
367 scoped_ptr<base::DictionaryValue> params_value = params.ToValue();
368 result->Set(
369 static_cast<int>(i),
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);
383 if (!transport)
384 return;
386 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
387 scoped_ptr<base::Value> params_value(
388 converter->FromV8Value(args[1], context()->v8_context()));
389 if (!params_value) {
390 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
391 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertParams)));
392 return;
394 scoped_ptr<RtpParams> params = RtpParams::FromValue(*params_value);
395 if (!params) {
396 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
397 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidRtpParams)));
398 return;
401 CastRtpParams cast_params;
402 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
403 if (!ToCastRtpParamsOrThrow(isolate, *params, &cast_params))
404 return;
406 base::Closure start_callback =
407 base::Bind(&CastStreamingNativeHandler::CallStartCallback,
408 weak_factory_.GetWeakPtr(),
409 transport_id);
410 base::Closure stop_callback =
411 base::Bind(&CastStreamingNativeHandler::CallStopCallback,
412 weak_factory_.GetWeakPtr(),
413 transport_id);
414 CastRtpStream::ErrorCallback error_callback =
415 base::Bind(&CastStreamingNativeHandler::CallErrorCallback,
416 weak_factory_.GetWeakPtr(),
417 transport_id);
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);
428 if (!transport)
429 return;
430 transport->Stop();
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))
440 return;
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);
452 if (!transport)
453 return;
455 net::IPEndPoint dest;
456 if (!IPEndPointFromArg(args.GetIsolate(),
457 args[1],
458 &dest)) {
459 return;
461 transport->SetDestination(
462 dest,
463 base::Bind(&CastStreamingNativeHandler::CallErrorCallback,
464 weak_factory_.GetWeakPtr(),
465 transport_id));
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);
476 if (!transport)
477 return;
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)));
487 return;
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);
500 if (!stream)
501 return;
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);
525 if (!transport)
526 return;
528 get_raw_events_callbacks_.insert(std::make_pair(transport_id, callback));
530 transport->GetRawEvents(
531 base::Bind(&CastStreamingNativeHandler::CallGetRawEventsCallback,
532 weak_factory_.GetWeakPtr(),
533 transport_id),
534 extra_data);
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);
544 if (!transport)
545 return;
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));
553 transport->GetStats(
554 base::Bind(&CastStreamingNativeHandler::CallGetStatsCallback,
555 weak_factory_.GetWeakPtr(),
556 transport_id));
559 void CastStreamingNativeHandler::CallGetRawEventsCallback(
560 int transport_id,
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())
569 return;
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(
579 int transport_id,
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())
587 return;
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(
600 transport_id);
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)));
606 return NULL;
609 CastUdpTransport* CastStreamingNativeHandler::GetUdpTransportOrThrow(
610 int transport_id) const {
611 UdpTransportMap::const_iterator iter = udp_transport_map_.find(
612 transport_id);
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)));
618 return NULL;
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()));
629 if (!params_value) {
630 isolate->ThrowException(v8::Exception::TypeError(
631 v8::String::NewFromUtf8(isolate, kUnableToConvertParams)));
632 return false;
634 scoped_ptr<RtpReceiverParams> params =
635 RtpReceiverParams::FromValue(*params_value);
636 if (!params) {
637 isolate->ThrowException(v8::Exception::TypeError(
638 v8::String::NewFromUtf8(isolate, kInvalidRtpParams)));
639 return false;
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)));
648 return false;
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)));
677 return false;
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)));
684 return false;
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)));
690 return false;
692 return true;
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)));
705 return false;
707 scoped_ptr<IPEndPoint> destination =
708 IPEndPoint::FromValue(*destination_value);
709 if (!destination) {
710 isolate->ThrowException(v8::Exception::TypeError(
711 v8::String::NewFromUtf8(isolate, kInvalidDestination)));
712 return false;
714 net::IPAddressNumber ip;
715 if (!net::ParseIPLiteralToNumber(destination->address, &ip)) {
716 isolate->ThrowException(v8::Exception::TypeError(
717 v8::String::NewFromUtf8(isolate, kInvalidDestination)));
718 return false;
720 *ip_endpoint = net::IPEndPoint(ip, destination->port);
721 return true;
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)));
736 return;
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)) {
751 return;
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)));
761 return;
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();
768 if (fps <= 1) {
769 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
770 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidFPS)));
771 return;
774 media::VideoCaptureFormat capture_format(
775 gfx::Size(max_width, max_height),
776 fps,
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)));
792 return;
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)));
805 return;
810 if (!options) {
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>()));
818 session->Start(
819 audio_config,
820 video_config,
821 local_endpoint,
822 remote_endpoint,
823 make_scoped_ptr(options),
824 capture_format,
825 base::Bind(&CastStreamingNativeHandler::AddTracksToMediaStream,
826 weak_factory_.GetWeakPtr(),
827 url,
828 params),
829 base::Bind(&CastStreamingNativeHandler::CallReceiverErrorCallback,
830 weak_factory_.GetWeakPtr(),
831 error_callback));
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