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
24using content::V8ValueConverter;
25
26// Extension types.
27using extensions::api::cast_streaming_rtp_stream::CodecSpecificParams;
28using extensions::api::cast_streaming_rtp_stream::RtpParams;
29using extensions::api::cast_streaming_rtp_stream::RtpPayloadParams;
30using extensions::api::cast_streaming_udp_transport::IPEndPoint;
31
32namespace extensions {
33
34namespace {
35const char kRtpStreamNotFound[] = "The RTP stream cannot be found";
36const char kUdpTransportNotFound[] = "The UDP transport cannot be found";
37const char kInvalidDestination[] = "Invalid destination";
38const char kInvalidRtpParams[] = "Invalid value for RTP params";
39const char kInvalidAesKey[] = "Invalid value for AES key";
40const char kInvalidAesIvMask[] = "Invalid value for AES IV mask";
41const char kInvalidStreamArgs[] = "Invalid stream arguments";
42const char kUnableToConvertArgs[] = "Unable to convert arguments";
43const char kUnableToConvertParams[] = "Unable to convert params";
44
45// These helper methods are used to convert between Extension API
46// types and Cast types.
47void 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
53void 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
59namespace {
60bool 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
69bool 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
110void 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
140void 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
148bool 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
164CastStreamingNativeHandler::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
203CastStreamingNativeHandler::~CastStreamingNativeHandler() {
204}
205
206void 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
260void 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
293void 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
302void 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
311void 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
325void 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
336void 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
362void 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
408void 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
420void 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
431void 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
466void 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
490void 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
505void 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
535void 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
557void 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
576void 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
594CastRtpStream* 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
606CastUdpTransport* 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