Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / renderer / extensions / cast_streaming_native_handler.cc
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.
4
5 #include "chrome/renderer/extensions/cast_streaming_native_handler.h"
6
7 #include <functional>
8 #include <iterator>
9
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"
23
24 using content::V8ValueConverter;
25
26 // Extension types.
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;
31
32 namespace extensions {
33
34 namespace {
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";
44
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;
51 }
52
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;
57 }
58
59 namespace {
60 bool HexDecode(const std::string& input, std::string* output) {
61   std::vector<uint8> bytes;
62   if (!base::HexStringToBytes(input, &bytes))
63     return false;
64   output->assign(reinterpret_cast<const char*>(&bytes[0]), bytes.size());
65   return true;
66 }
67 }  // namespace
68
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)));
93     return false;
94   }
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)));
99     return false;
100   }
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],
104                               &cast_codec_params);
105     cast_params->codec_specific_params.push_back(cast_codec_params);
106   }
107   return true;
108 }
109
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);
137   }
138 }
139
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);
146 }
147
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,
155                                      ext_params.payload,
156                                      &cast_params->payload)) {
157     return false;
158   }
159   return true;
160 }
161
162 }  // namespace
163
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)));
201 }
202
203 CastStreamingNativeHandler::~CastStreamingNativeHandler() {
204 }
205
206 void CastStreamingNativeHandler::CreateCastSession(
207     const v8::FunctionCallbackInfo<v8::Value>& args) {
208   CHECK_EQ(3, args.Length());
209   CHECK(args[2]->IsFunction());
210
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)));
216     return;
217   }
218
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)));
228       return;
229     }
230     stream1.reset(new CastRtpStream(track.component(), session));
231   }
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)));
239       return;
240     }
241     stream2.reset(new CastRtpStream(track.component(), session));
242   }
243   scoped_ptr<CastUdpTransport> udp_transport(
244       new CastUdpTransport(session));
245
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>());
249
250   base::MessageLoop::current()->PostTask(
251       FROM_HERE,
252       base::Bind(
253           &CastStreamingNativeHandler::CallCreateCallback,
254           weak_factory_.GetWeakPtr(),
255           base::Passed(&stream1),
256           base::Passed(&stream2),
257           base::Passed(&udp_transport)));
258 }
259
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());
267
268   v8::Handle<v8::Value> callback_args[3];
269   callback_args[0] = v8::Null(isolate);
270   callback_args[1] = v8::Null(isolate);
271
272   if (stream1) {
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());
277   }
278   if (stream2) {
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());
283   }
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),
289                           3, callback_args);
290   create_callback_.reset();
291 }
292
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);
300 }
301
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);
309 }
310
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));
318   event_args->Set(
319       1,
320       v8::String::NewFromUtf8(
321           isolate, message.data(), v8::String::kNormalString, message.size()));
322   context()->DispatchEvent("cast.streaming.rtpStream.onError", event_args);
323 }
324
325 void CastStreamingNativeHandler::DestroyCastRtpStream(
326     const v8::FunctionCallbackInfo<v8::Value>& args) {
327   CHECK_EQ(1, args.Length());
328   CHECK(args[0]->IsInt32());
329
330   const int transport_id = args[0]->ToInt32()->Value();
331   if (!GetRtpStreamOrThrow(transport_id))
332     return;
333   rtp_stream_map_.erase(transport_id);
334 }
335
336 void CastStreamingNativeHandler::GetSupportedParamsCastRtpStream(
337     const v8::FunctionCallbackInfo<v8::Value>& args) {
338   CHECK_EQ(1, args.Length());
339   CHECK(args[0]->IsInt32());
340
341   const int transport_id = args[0]->ToInt32()->Value();
342   CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
343   if (!transport)
344     return;
345
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) {
352     RtpParams params;
353     FromCastRtpParams(cast_params[i], &params);
354     scoped_ptr<base::DictionaryValue> params_value = params.ToValue();
355     result->Set(
356         static_cast<int>(i),
357         converter->ToV8Value(params_value.get(), context()->v8_context()));
358   }
359   args.GetReturnValue().Set(result);
360 }
361
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());
367
368   const int transport_id = args[0]->ToInt32()->Value();
369   CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
370   if (!transport)
371     return;
372
373   scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
374   scoped_ptr<base::Value> params_value(
375       converter->FromV8Value(args[1], context()->v8_context()));
376   if (!params_value) {
377     args.GetIsolate()->ThrowException(v8::Exception::TypeError(
378         v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertParams)));
379     return;
380   }
381   scoped_ptr<RtpParams> params = RtpParams::FromValue(*params_value);
382   if (!params) {
383     args.GetIsolate()->ThrowException(v8::Exception::TypeError(
384         v8::String::NewFromUtf8(args.GetIsolate(), kInvalidRtpParams)));
385     return;
386   }
387
388   CastRtpParams cast_params;
389   v8::Isolate* isolate = context()->v8_context()->GetIsolate();
390   if (!ToCastRtpParamsOrThrow(isolate, *params, &cast_params))
391     return;
392
393   base::Closure start_callback =
394       base::Bind(&CastStreamingNativeHandler::CallStartCallback,
395                  weak_factory_.GetWeakPtr(),
396                  transport_id);
397   base::Closure stop_callback =
398       base::Bind(&CastStreamingNativeHandler::CallStopCallback,
399                  weak_factory_.GetWeakPtr(),
400                  transport_id);
401   CastRtpStream::ErrorCallback error_callback =
402       base::Bind(&CastStreamingNativeHandler::CallErrorCallback,
403                  weak_factory_.GetWeakPtr(),
404                  transport_id);
405   transport->Start(cast_params, start_callback, stop_callback, error_callback);
406 }
407
408 void CastStreamingNativeHandler::StopCastRtpStream(
409     const v8::FunctionCallbackInfo<v8::Value>& args) {
410   CHECK_EQ(1, args.Length());
411   CHECK(args[0]->IsInt32());
412
413   const int transport_id = args[0]->ToInt32()->Value();
414   CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
415   if (!transport)
416     return;
417   transport->Stop();
418 }
419
420 void CastStreamingNativeHandler::DestroyCastUdpTransport(
421     const v8::FunctionCallbackInfo<v8::Value>& args) {
422   CHECK_EQ(1, args.Length());
423   CHECK(args[0]->IsInt32());
424
425   const int transport_id = args[0]->ToInt32()->Value();
426   if (!GetUdpTransportOrThrow(transport_id))
427     return;
428   udp_transport_map_.erase(transport_id);
429 }
430
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());
436
437   const int transport_id = args[0]->ToInt32()->Value();
438   CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id);
439   if (!transport)
440     return;
441
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)));
448     return;
449   }
450   scoped_ptr<IPEndPoint> destination =
451       IPEndPoint::FromValue(*destination_value);
452   if (!destination) {
453     args.GetIsolate()->ThrowException(v8::Exception::TypeError(
454         v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination)));
455     return;
456   }
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)));
461     return;
462   }
463   transport->SetDestination(net::IPEndPoint(ip, destination->port));
464 }
465
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());
471
472   const int transport_id = args[0]->ToInt32()->Value();
473   CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id);
474   if (!transport)
475     return;
476
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)));
485     return;
486   }
487   transport->SetOptions(make_scoped_ptr(options));
488 }
489
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());
495
496   const int stream_id = args[0]->ToInt32()->Value();
497   CastRtpStream* stream = GetRtpStreamOrThrow(stream_id);
498   if (!stream)
499     return;
500
501   const bool enable = args[1]->ToBoolean()->Value();
502   stream->ToggleLogging(enable);
503 }
504
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());
511
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]);
520   }
521
522   CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
523   if (!transport)
524     return;
525
526   get_raw_events_callbacks_.insert(std::make_pair(transport_id, callback));
527
528   transport->GetRawEvents(
529       base::Bind(&CastStreamingNativeHandler::CallGetRawEventsCallback,
530                  weak_factory_.GetWeakPtr(),
531                  transport_id),
532       extra_data);
533 }
534
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);
542   if (!transport)
543     return;
544
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));
550
551   transport->GetStats(
552       base::Bind(&CastStreamingNativeHandler::CallGetStatsCallback,
553                  weak_factory_.GetWeakPtr(),
554                  transport_id));
555 }
556
557 void CastStreamingNativeHandler::CallGetRawEventsCallback(
558     int transport_id,
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());
563
564   RtpStreamCallbackMap::iterator it =
565       get_raw_events_callbacks_.find(transport_id);
566   if (it == get_raw_events_callbacks_.end())
567     return;
568   v8::Handle<v8::Value> callback_args[1];
569   scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
570   callback_args[0] =
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);
574 }
575
576 void CastStreamingNativeHandler::CallGetStatsCallback(
577     int transport_id,
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());
582
583   RtpStreamCallbackMap::iterator it = get_stats_callbacks_.find(transport_id);
584   if (it == get_stats_callbacks_.end())
585     return;
586
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);
592 }
593
594 CastRtpStream* CastStreamingNativeHandler::GetRtpStreamOrThrow(
595     int transport_id) const {
596   RtpStreamMap::const_iterator iter = rtp_stream_map_.find(
597       transport_id);
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)));
603   return NULL;
604 }
605
606 CastUdpTransport* CastStreamingNativeHandler::GetUdpTransportOrThrow(
607     int transport_id) const {
608   UdpTransportMap::const_iterator iter = udp_transport_map_.find(
609       transport_id);
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)));
615   return NULL;
616 }
617
618 }  // namespace extensions