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], ¶ms); 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