Home | History | Annotate | Download | only in extensions
      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
    619