1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/extensions/cast_streaming_native_handler.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/common/extensions/api/cast_streaming_rtp_stream.h"
14 #include "chrome/common/extensions/api/cast_streaming_udp_transport.h"
15 #include "chrome/renderer/media/cast_rtp_stream.h"
16 #include "chrome/renderer/media/cast_session.h"
17 #include "chrome/renderer/media/cast_udp_transport.h"
18 #include "content/public/renderer/v8_value_converter.h"
19 #include "extensions/renderer/script_context.h"
20 #include "net/base/host_port_pair.h"
21 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
22 #include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h"
24 using content::V8ValueConverter;
27 using extensions::api::cast_streaming_rtp_stream::CodecSpecificParams;
28 using extensions::api::cast_streaming_rtp_stream::RtpParams;
29 using extensions::api::cast_streaming_rtp_stream::RtpPayloadParams;
30 using extensions::api::cast_streaming_udp_transport::IPEndPoint;
32 namespace extensions {
35 const char kRtpStreamNotFound[] = "The RTP stream cannot be found";
36 const char kUdpTransportNotFound[] = "The UDP transport cannot be found";
37 const char kInvalidDestination[] = "Invalid destination";
38 const char kInvalidRtpParams[] = "Invalid value for RTP params";
39 const char kInvalidAesKey[] = "Invalid value for AES key";
40 const char kInvalidAesIvMask[] = "Invalid value for AES IV mask";
41 const char kInvalidStreamArgs[] = "Invalid stream arguments";
42 const char kUnableToConvertArgs[] = "Unable to convert arguments";
43 const char kUnableToConvertParams[] = "Unable to convert params";
45 // These helper methods are used to convert between Extension API
46 // types and Cast types.
47 void ToCastCodecSpecificParams(const CodecSpecificParams& ext_params,
48 CastCodecSpecificParams* cast_params) {
49 cast_params->key = ext_params.key;
50 cast_params->value = ext_params.value;
53 void FromCastCodecSpecificParams(const CastCodecSpecificParams& cast_params,
54 CodecSpecificParams* ext_params) {
55 ext_params->key = cast_params.key;
56 ext_params->value = cast_params.value;
60 bool HexDecode(const std::string& input, std::string* output) {
61 std::vector<uint8> bytes;
62 if (!base::HexStringToBytes(input, &bytes))
64 output->assign(reinterpret_cast<const char*>(&bytes[0]), bytes.size());
69 bool ToCastRtpPayloadParamsOrThrow(v8::Isolate* isolate,
70 const RtpPayloadParams& ext_params,
71 CastRtpPayloadParams* cast_params) {
72 cast_params->payload_type = ext_params.payload_type;
73 cast_params->max_latency_ms = ext_params.max_latency;
74 cast_params->min_latency_ms =
75 ext_params.min_latency ? *ext_params.min_latency : ext_params.max_latency;
76 cast_params->codec_name = ext_params.codec_name;
77 cast_params->ssrc = ext_params.ssrc;
78 cast_params->feedback_ssrc = ext_params.feedback_ssrc;
79 cast_params->clock_rate = ext_params.clock_rate ? *ext_params.clock_rate : 0;
80 cast_params->min_bitrate =
81 ext_params.min_bitrate ? *ext_params.min_bitrate : 0;
82 cast_params->max_bitrate =
83 ext_params.max_bitrate ? *ext_params.max_bitrate : 0;
84 cast_params->channels = ext_params.channels ? *ext_params.channels : 0;
85 cast_params->max_frame_rate =
86 ext_params.max_frame_rate ? *ext_params.max_frame_rate : 0.0;
87 cast_params->width = ext_params.width ? *ext_params.width : 0;
88 cast_params->height = ext_params.height ? *ext_params.height : 0;
89 if (ext_params.aes_key &&
90 !HexDecode(*ext_params.aes_key, &cast_params->aes_key)) {
91 isolate->ThrowException(v8::Exception::Error(
92 v8::String::NewFromUtf8(isolate, kInvalidAesKey)));
95 if (ext_params.aes_iv_mask &&
96 !HexDecode(*ext_params.aes_iv_mask, &cast_params->aes_iv_mask)) {
97 isolate->ThrowException(v8::Exception::Error(
98 v8::String::NewFromUtf8(isolate, kInvalidAesIvMask)));
101 for (size_t i = 0; i < ext_params.codec_specific_params.size(); ++i) {
102 CastCodecSpecificParams cast_codec_params;
103 ToCastCodecSpecificParams(*ext_params.codec_specific_params[i],
105 cast_params->codec_specific_params.push_back(cast_codec_params);
110 void FromCastRtpPayloadParams(const CastRtpPayloadParams& cast_params,
111 RtpPayloadParams* ext_params) {
112 ext_params->payload_type = cast_params.payload_type;
113 ext_params->max_latency = cast_params.max_latency_ms;
114 ext_params->codec_name = cast_params.codec_name;
115 ext_params->ssrc = cast_params.ssrc;
116 ext_params->feedback_ssrc = cast_params.feedback_ssrc;
117 if (cast_params.clock_rate)
118 ext_params->clock_rate.reset(new int(cast_params.clock_rate));
119 if (cast_params.min_bitrate)
120 ext_params->min_bitrate.reset(new int(cast_params.min_bitrate));
121 if (cast_params.max_bitrate)
122 ext_params->max_bitrate.reset(new int(cast_params.max_bitrate));
123 if (cast_params.channels)
124 ext_params->channels.reset(new int(cast_params.channels));
125 if (cast_params.max_frame_rate > 0.0)
126 ext_params->max_frame_rate.reset(new double(cast_params.max_frame_rate));
127 if (cast_params.width)
128 ext_params->width.reset(new int(cast_params.width));
129 if (cast_params.height)
130 ext_params->height.reset(new int(cast_params.height));
131 for (size_t i = 0; i < cast_params.codec_specific_params.size(); ++i) {
132 linked_ptr<CodecSpecificParams> ext_codec_params(
133 new CodecSpecificParams());
134 FromCastCodecSpecificParams(cast_params.codec_specific_params[i],
135 ext_codec_params.get());
136 ext_params->codec_specific_params.push_back(ext_codec_params);
140 void FromCastRtpParams(const CastRtpParams& cast_params,
141 RtpParams* ext_params) {
142 std::copy(cast_params.rtcp_features.begin(),
143 cast_params.rtcp_features.end(),
144 std::back_inserter(ext_params->rtcp_features));
145 FromCastRtpPayloadParams(cast_params.payload, &ext_params->payload);
148 bool ToCastRtpParamsOrThrow(v8::Isolate* isolate,
149 const RtpParams& ext_params,
150 CastRtpParams* cast_params) {
151 std::copy(ext_params.rtcp_features.begin(),
152 ext_params.rtcp_features.end(),
153 std::back_inserter(cast_params->rtcp_features));
154 if (!ToCastRtpPayloadParamsOrThrow(isolate,
156 &cast_params->payload)) {
164 CastStreamingNativeHandler::CastStreamingNativeHandler(ScriptContext* context)
165 : ObjectBackedNativeHandler(context),
166 last_transport_id_(1),
167 weak_factory_(this) {
168 RouteFunction("CreateSession",
169 base::Bind(&CastStreamingNativeHandler::CreateCastSession,
170 base::Unretained(this)));
171 RouteFunction("DestroyCastRtpStream",
172 base::Bind(&CastStreamingNativeHandler::DestroyCastRtpStream,
173 base::Unretained(this)));
174 RouteFunction("GetSupportedParamsCastRtpStream",
175 base::Bind(&CastStreamingNativeHandler::GetSupportedParamsCastRtpStream,
176 base::Unretained(this)));
177 RouteFunction("StartCastRtpStream",
178 base::Bind(&CastStreamingNativeHandler::StartCastRtpStream,
179 base::Unretained(this)));
180 RouteFunction("StopCastRtpStream",
181 base::Bind(&CastStreamingNativeHandler::StopCastRtpStream,
182 base::Unretained(this)));
183 RouteFunction("DestroyCastUdpTransport",
184 base::Bind(&CastStreamingNativeHandler::DestroyCastUdpTransport,
185 base::Unretained(this)));
186 RouteFunction("SetDestinationCastUdpTransport",
187 base::Bind(&CastStreamingNativeHandler::SetDestinationCastUdpTransport,
188 base::Unretained(this)));
189 RouteFunction("SetOptionsCastUdpTransport",
190 base::Bind(&CastStreamingNativeHandler::SetOptionsCastUdpTransport,
191 base::Unretained(this)));
192 RouteFunction("ToggleLogging",
193 base::Bind(&CastStreamingNativeHandler::ToggleLogging,
194 base::Unretained(this)));
195 RouteFunction("GetRawEvents",
196 base::Bind(&CastStreamingNativeHandler::GetRawEvents,
197 base::Unretained(this)));
198 RouteFunction("GetStats",
199 base::Bind(&CastStreamingNativeHandler::GetStats,
200 base::Unretained(this)));
203 CastStreamingNativeHandler::~CastStreamingNativeHandler() {
206 void CastStreamingNativeHandler::CreateCastSession(
207 const v8::FunctionCallbackInfo<v8::Value>& args) {
208 CHECK_EQ(3, args.Length());
209 CHECK(args[2]->IsFunction());
211 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
212 if ((args[0]->IsNull() || args[0]->IsUndefined()) &&
213 (args[1]->IsNull() || args[1]->IsUndefined())) {
214 isolate->ThrowException(v8::Exception::Error(
215 v8::String::NewFromUtf8(isolate, kInvalidStreamArgs)));
219 scoped_refptr<CastSession> session(new CastSession());
220 scoped_ptr<CastRtpStream> stream1, stream2;
221 if (!args[0]->IsNull() && !args[0]->IsUndefined()) {
222 CHECK(args[0]->IsObject());
223 blink::WebDOMMediaStreamTrack track =
224 blink::WebDOMMediaStreamTrack::fromV8Value(args[0]);
225 if (track.isNull()) {
226 isolate->ThrowException(v8::Exception::Error(
227 v8::String::NewFromUtf8(isolate, kInvalidStreamArgs)));
230 stream1.reset(new CastRtpStream(track.component(), session));
232 if (!args[1]->IsNull() && !args[1]->IsUndefined()) {
233 CHECK(args[1]->IsObject());
234 blink::WebDOMMediaStreamTrack track =
235 blink::WebDOMMediaStreamTrack::fromV8Value(args[1]);
236 if (track.isNull()) {
237 isolate->ThrowException(v8::Exception::Error(
238 v8::String::NewFromUtf8(isolate, kInvalidStreamArgs)));
241 stream2.reset(new CastRtpStream(track.component(), session));
243 scoped_ptr<CastUdpTransport> udp_transport(
244 new CastUdpTransport(session));
246 // TODO(imcheng): Use a weak reference to ensure we don't call into an
247 // invalid context when the callback is invoked.
248 create_callback_.reset(args[2].As<v8::Function>());
250 base::MessageLoop::current()->PostTask(
253 &CastStreamingNativeHandler::CallCreateCallback,
254 weak_factory_.GetWeakPtr(),
255 base::Passed(&stream1),
256 base::Passed(&stream2),
257 base::Passed(&udp_transport)));
260 void CastStreamingNativeHandler::CallCreateCallback(
261 scoped_ptr<CastRtpStream> stream1,
262 scoped_ptr<CastRtpStream> stream2,
263 scoped_ptr<CastUdpTransport> udp_transport) {
264 v8::Isolate* isolate = context()->isolate();
265 v8::HandleScope handle_scope(isolate);
266 v8::Context::Scope context_scope(context()->v8_context());
268 v8::Handle<v8::Value> callback_args[3];
269 callback_args[0] = v8::Null(isolate);
270 callback_args[1] = v8::Null(isolate);
273 const int stream1_id = last_transport_id_++;
274 callback_args[0] = v8::Integer::New(isolate, stream1_id);
275 rtp_stream_map_[stream1_id] =
276 linked_ptr<CastRtpStream>(stream1.release());
279 const int stream2_id = last_transport_id_++;
280 callback_args[1] = v8::Integer::New(isolate, stream2_id);
281 rtp_stream_map_[stream2_id] =
282 linked_ptr<CastRtpStream>(stream2.release());
284 const int udp_id = last_transport_id_++;
285 udp_transport_map_[udp_id] =
286 linked_ptr<CastUdpTransport>(udp_transport.release());
287 callback_args[2] = v8::Integer::New(isolate, udp_id);
288 context()->CallFunction(create_callback_.NewHandle(isolate),
290 create_callback_.reset();
293 void CastStreamingNativeHandler::CallStartCallback(int stream_id) {
294 v8::Isolate* isolate = context()->isolate();
295 v8::HandleScope handle_scope(isolate);
296 v8::Context::Scope context_scope(context()->v8_context());
297 v8::Handle<v8::Array> event_args = v8::Array::New(isolate, 1);
298 event_args->Set(0, v8::Integer::New(isolate, stream_id));
299 context()->DispatchEvent("cast.streaming.rtpStream.onStarted", event_args);
302 void CastStreamingNativeHandler::CallStopCallback(int stream_id) {
303 v8::Isolate* isolate = context()->isolate();
304 v8::HandleScope handle_scope(isolate);
305 v8::Context::Scope context_scope(context()->v8_context());
306 v8::Handle<v8::Array> event_args = v8::Array::New(isolate, 1);
307 event_args->Set(0, v8::Integer::New(isolate, stream_id));
308 context()->DispatchEvent("cast.streaming.rtpStream.onStopped", event_args);
311 void CastStreamingNativeHandler::CallErrorCallback(int stream_id,
312 const std::string& message) {
313 v8::Isolate* isolate = context()->isolate();
314 v8::HandleScope handle_scope(isolate);
315 v8::Context::Scope context_scope(context()->v8_context());
316 v8::Handle<v8::Array> event_args = v8::Array::New(isolate, 2);
317 event_args->Set(0, v8::Integer::New(isolate, stream_id));
320 v8::String::NewFromUtf8(
321 isolate, message.data(), v8::String::kNormalString, message.size()));
322 context()->DispatchEvent("cast.streaming.rtpStream.onError", event_args);
325 void CastStreamingNativeHandler::DestroyCastRtpStream(
326 const v8::FunctionCallbackInfo<v8::Value>& args) {
327 CHECK_EQ(1, args.Length());
328 CHECK(args[0]->IsInt32());
330 const int transport_id = args[0]->ToInt32()->Value();
331 if (!GetRtpStreamOrThrow(transport_id))
333 rtp_stream_map_.erase(transport_id);
336 void CastStreamingNativeHandler::GetSupportedParamsCastRtpStream(
337 const v8::FunctionCallbackInfo<v8::Value>& args) {
338 CHECK_EQ(1, args.Length());
339 CHECK(args[0]->IsInt32());
341 const int transport_id = args[0]->ToInt32()->Value();
342 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
346 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
347 std::vector<CastRtpParams> cast_params = transport->GetSupportedParams();
348 v8::Handle<v8::Array> result =
349 v8::Array::New(args.GetIsolate(),
350 static_cast<int>(cast_params.size()));
351 for (size_t i = 0; i < cast_params.size(); ++i) {
353 FromCastRtpParams(cast_params[i], ¶ms);
354 scoped_ptr<base::DictionaryValue> params_value = params.ToValue();
357 converter->ToV8Value(params_value.get(), context()->v8_context()));
359 args.GetReturnValue().Set(result);
362 void CastStreamingNativeHandler::StartCastRtpStream(
363 const v8::FunctionCallbackInfo<v8::Value>& args) {
364 CHECK_EQ(2, args.Length());
365 CHECK(args[0]->IsInt32());
366 CHECK(args[1]->IsObject());
368 const int transport_id = args[0]->ToInt32()->Value();
369 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
373 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
374 scoped_ptr<base::Value> params_value(
375 converter->FromV8Value(args[1], context()->v8_context()));
377 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
378 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertParams)));
381 scoped_ptr<RtpParams> params = RtpParams::FromValue(*params_value);
383 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
384 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidRtpParams)));
388 CastRtpParams cast_params;
389 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
390 if (!ToCastRtpParamsOrThrow(isolate, *params, &cast_params))
393 base::Closure start_callback =
394 base::Bind(&CastStreamingNativeHandler::CallStartCallback,
395 weak_factory_.GetWeakPtr(),
397 base::Closure stop_callback =
398 base::Bind(&CastStreamingNativeHandler::CallStopCallback,
399 weak_factory_.GetWeakPtr(),
401 CastRtpStream::ErrorCallback error_callback =
402 base::Bind(&CastStreamingNativeHandler::CallErrorCallback,
403 weak_factory_.GetWeakPtr(),
405 transport->Start(cast_params, start_callback, stop_callback, error_callback);
408 void CastStreamingNativeHandler::StopCastRtpStream(
409 const v8::FunctionCallbackInfo<v8::Value>& args) {
410 CHECK_EQ(1, args.Length());
411 CHECK(args[0]->IsInt32());
413 const int transport_id = args[0]->ToInt32()->Value();
414 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
420 void CastStreamingNativeHandler::DestroyCastUdpTransport(
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()->Value();
426 if (!GetUdpTransportOrThrow(transport_id))
428 udp_transport_map_.erase(transport_id);
431 void CastStreamingNativeHandler::SetDestinationCastUdpTransport(
432 const v8::FunctionCallbackInfo<v8::Value>& args) {
433 CHECK_EQ(2, args.Length());
434 CHECK(args[0]->IsInt32());
435 CHECK(args[1]->IsObject());
437 const int transport_id = args[0]->ToInt32()->Value();
438 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id);
442 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
443 scoped_ptr<base::Value> destination_value(
444 converter->FromV8Value(args[1], context()->v8_context()));
445 if (!destination_value) {
446 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
447 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs)));
450 scoped_ptr<IPEndPoint> destination =
451 IPEndPoint::FromValue(*destination_value);
453 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
454 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination)));
457 net::IPAddressNumber ip;
458 if (!net::ParseIPLiteralToNumber(destination->address, &ip)) {
459 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
460 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination)));
463 transport->SetDestination(net::IPEndPoint(ip, destination->port));
466 void CastStreamingNativeHandler::SetOptionsCastUdpTransport(
467 const v8::FunctionCallbackInfo<v8::Value>& args) {
468 CHECK_EQ(2, args.Length());
469 CHECK(args[0]->IsInt32());
470 CHECK(args[1]->IsObject());
472 const int transport_id = args[0]->ToInt32()->Value();
473 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id);
477 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
478 base::Value* options_value =
479 converter->FromV8Value(args[1], context()->v8_context());
480 base::DictionaryValue* options;
481 if (!options_value || !options_value->GetAsDictionary(&options)) {
482 delete options_value;
483 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
484 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs)));
487 transport->SetOptions(make_scoped_ptr(options));
490 void CastStreamingNativeHandler::ToggleLogging(
491 const v8::FunctionCallbackInfo<v8::Value>& args) {
492 CHECK_EQ(2, args.Length());
493 CHECK(args[0]->IsInt32());
494 CHECK(args[1]->IsBoolean());
496 const int stream_id = args[0]->ToInt32()->Value();
497 CastRtpStream* stream = GetRtpStreamOrThrow(stream_id);
501 const bool enable = args[1]->ToBoolean()->Value();
502 stream->ToggleLogging(enable);
505 void CastStreamingNativeHandler::GetRawEvents(
506 const v8::FunctionCallbackInfo<v8::Value>& args) {
507 CHECK_EQ(3, args.Length());
508 CHECK(args[0]->IsInt32());
509 CHECK(args[1]->IsNull() || args[1]->IsString());
510 CHECK(args[2]->IsFunction());
512 const int transport_id = args[0]->ToInt32()->Value();
513 // TODO(imcheng): Use a weak reference to ensure we don't call into an
514 // invalid context when the callback is invoked.
515 linked_ptr<ScopedPersistent<v8::Function> > callback(
516 new ScopedPersistent<v8::Function>(args[2].As<v8::Function>()));
517 std::string extra_data;
518 if (!args[1]->IsNull()) {
519 extra_data = *v8::String::Utf8Value(args[1]);
522 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
526 get_raw_events_callbacks_.insert(std::make_pair(transport_id, callback));
528 transport->GetRawEvents(
529 base::Bind(&CastStreamingNativeHandler::CallGetRawEventsCallback,
530 weak_factory_.GetWeakPtr(),
535 void CastStreamingNativeHandler::GetStats(
536 const v8::FunctionCallbackInfo<v8::Value>& args) {
537 CHECK_EQ(2, args.Length());
538 CHECK(args[0]->IsInt32());
539 CHECK(args[1]->IsFunction());
540 const int transport_id = args[0]->ToInt32()->Value();
541 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
545 // TODO(imcheng): Use a weak reference to ensure we don't call into an
546 // invalid context when the callback is invoked.
547 linked_ptr<ScopedPersistent<v8::Function> > callback(
548 new ScopedPersistent<v8::Function>(args[1].As<v8::Function>()));
549 get_stats_callbacks_.insert(std::make_pair(transport_id, callback));
552 base::Bind(&CastStreamingNativeHandler::CallGetStatsCallback,
553 weak_factory_.GetWeakPtr(),
557 void CastStreamingNativeHandler::CallGetRawEventsCallback(
559 scoped_ptr<base::BinaryValue> raw_events) {
560 v8::Isolate* isolate = context()->isolate();
561 v8::HandleScope handle_scope(isolate);
562 v8::Context::Scope context_scope(context()->v8_context());
564 RtpStreamCallbackMap::iterator it =
565 get_raw_events_callbacks_.find(transport_id);
566 if (it == get_raw_events_callbacks_.end())
568 v8::Handle<v8::Value> callback_args[1];
569 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
571 converter->ToV8Value(raw_events.get(), context()->v8_context());
572 context()->CallFunction(it->second->NewHandle(isolate), 1, callback_args);
573 get_raw_events_callbacks_.erase(it);
576 void CastStreamingNativeHandler::CallGetStatsCallback(
578 scoped_ptr<base::DictionaryValue> stats) {
579 v8::Isolate* isolate = context()->isolate();
580 v8::HandleScope handle_scope(isolate);
581 v8::Context::Scope context_scope(context()->v8_context());
583 RtpStreamCallbackMap::iterator it = get_stats_callbacks_.find(transport_id);
584 if (it == get_stats_callbacks_.end())
587 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
588 v8::Handle<v8::Value> callback_args[1];
589 callback_args[0] = converter->ToV8Value(stats.get(), context()->v8_context());
590 context()->CallFunction(it->second->NewHandle(isolate), 1, callback_args);
591 get_stats_callbacks_.erase(it);
594 CastRtpStream* CastStreamingNativeHandler::GetRtpStreamOrThrow(
595 int transport_id) const {
596 RtpStreamMap::const_iterator iter = rtp_stream_map_.find(
598 if (iter != rtp_stream_map_.end())
599 return iter->second.get();
600 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
601 isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8(
602 isolate, kRtpStreamNotFound)));
606 CastUdpTransport* CastStreamingNativeHandler::GetUdpTransportOrThrow(
607 int transport_id) const {
608 UdpTransportMap::const_iterator iter = udp_transport_map_.find(
610 if (iter != udp_transport_map_.end())
611 return iter->second.get();
612 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
613 isolate->ThrowException(v8::Exception::RangeError(
614 v8::String::NewFromUtf8(isolate, kUdpTransportNotFound)));
618 } // namespace extensions