Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/renderer/extensions/cast_streaming_native_handler.h" | 5 #include "chrome/renderer/extensions/cast_streaming_native_handler.h" |
| 6 | 6 |
| 7 #include <functional> | 7 #include <functional> |
| 8 #include <iterator> | 8 #include <iterator> |
| 9 | 9 |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/message_loop/message_loop.h" | 11 #include "base/message_loop/message_loop.h" |
| 12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
| 13 #include "chrome/common/extensions/api/cast_streaming_receiver_session.h" | |
| 13 #include "chrome/common/extensions/api/cast_streaming_rtp_stream.h" | 14 #include "chrome/common/extensions/api/cast_streaming_rtp_stream.h" |
| 14 #include "chrome/common/extensions/api/cast_streaming_udp_transport.h" | 15 #include "chrome/common/extensions/api/cast_streaming_udp_transport.h" |
| 16 #include "chrome/renderer/media/cast_receiver_session.h" | |
| 15 #include "chrome/renderer/media/cast_rtp_stream.h" | 17 #include "chrome/renderer/media/cast_rtp_stream.h" |
| 16 #include "chrome/renderer/media/cast_session.h" | 18 #include "chrome/renderer/media/cast_session.h" |
| 17 #include "chrome/renderer/media/cast_udp_transport.h" | 19 #include "chrome/renderer/media/cast_udp_transport.h" |
| 18 #include "content/public/child/v8_value_converter.h" | 20 #include "content/public/child/v8_value_converter.h" |
| 21 #include "content/public/renderer/media_stream_api.h" | |
| 19 #include "extensions/renderer/script_context.h" | 22 #include "extensions/renderer/script_context.h" |
| 23 #include "media/audio/audio_parameters.h" | |
| 20 #include "net/base/host_port_pair.h" | 24 #include "net/base/host_port_pair.h" |
| 25 #include "third_party/WebKit/public/platform/WebMediaStream.h" | |
| 21 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" | 26 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" |
| 27 #include "third_party/WebKit/public/platform/WebURL.h" | |
| 22 #include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h" | 28 #include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h" |
| 29 #include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" | |
| 30 #include "url/gurl.h" | |
| 23 | 31 |
| 24 using content::V8ValueConverter; | 32 using content::V8ValueConverter; |
| 25 | 33 |
| 26 // Extension types. | 34 // Extension types. |
| 35 using extensions::api::cast_streaming_receiver_session::RtpReceiverParams; | |
| 27 using extensions::api::cast_streaming_rtp_stream::CodecSpecificParams; | 36 using extensions::api::cast_streaming_rtp_stream::CodecSpecificParams; |
| 28 using extensions::api::cast_streaming_rtp_stream::RtpParams; | 37 using extensions::api::cast_streaming_rtp_stream::RtpParams; |
| 29 using extensions::api::cast_streaming_rtp_stream::RtpPayloadParams; | 38 using extensions::api::cast_streaming_rtp_stream::RtpPayloadParams; |
| 30 using extensions::api::cast_streaming_udp_transport::IPEndPoint; | 39 using extensions::api::cast_streaming_udp_transport::IPEndPoint; |
| 31 | 40 |
| 32 namespace extensions { | 41 namespace extensions { |
| 33 | 42 |
| 34 namespace { | 43 namespace { |
| 44 const char kInvalidAesIvMask[] = "Invalid value for AES IV mask"; | |
| 45 const char kInvalidAesKey[] = "Invalid value for AES key"; | |
| 46 const char kInvalidAudioParams[] = "Invalid audio params"; | |
| 47 const char kInvalidDestination[] = "Invalid destination"; | |
| 48 const char kInvalidFPS[] = "Invalid FPS"; | |
| 49 const char kInvalidMediaStreamURL[] = "Invalid MediaStream URL"; | |
| 50 const char kInvalidRtpParams[] = "Invalid value for RTP params"; | |
| 51 const char kInvalidStreamArgs[] = "Invalid stream arguments"; | |
| 35 const char kRtpStreamNotFound[] = "The RTP stream cannot be found"; | 52 const char kRtpStreamNotFound[] = "The RTP stream cannot be found"; |
| 36 const char kUdpTransportNotFound[] = "The UDP transport cannot be found"; | 53 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"; | 54 const char kUnableToConvertArgs[] = "Unable to convert arguments"; |
| 43 const char kUnableToConvertParams[] = "Unable to convert params"; | 55 const char kUnableToConvertParams[] = "Unable to convert params"; |
| 44 | 56 |
| 45 // These helper methods are used to convert between Extension API | 57 // These helper methods are used to convert between Extension API |
| 46 // types and Cast types. | 58 // types and Cast types. |
| 47 void ToCastCodecSpecificParams(const CodecSpecificParams& ext_params, | 59 void ToCastCodecSpecificParams(const CodecSpecificParams& ext_params, |
| 48 CastCodecSpecificParams* cast_params) { | 60 CastCodecSpecificParams* cast_params) { |
| 49 cast_params->key = ext_params.key; | 61 cast_params->key = ext_params.key; |
| 50 cast_params->value = ext_params.value; | 62 cast_params->value = ext_params.value; |
| 51 } | 63 } |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 192 base::Unretained(this))); | 204 base::Unretained(this))); |
| 193 RouteFunction("ToggleLogging", | 205 RouteFunction("ToggleLogging", |
| 194 base::Bind(&CastStreamingNativeHandler::ToggleLogging, | 206 base::Bind(&CastStreamingNativeHandler::ToggleLogging, |
| 195 base::Unretained(this))); | 207 base::Unretained(this))); |
| 196 RouteFunction("GetRawEvents", | 208 RouteFunction("GetRawEvents", |
| 197 base::Bind(&CastStreamingNativeHandler::GetRawEvents, | 209 base::Bind(&CastStreamingNativeHandler::GetRawEvents, |
| 198 base::Unretained(this))); | 210 base::Unretained(this))); |
| 199 RouteFunction("GetStats", | 211 RouteFunction("GetStats", |
| 200 base::Bind(&CastStreamingNativeHandler::GetStats, | 212 base::Bind(&CastStreamingNativeHandler::GetStats, |
| 201 base::Unretained(this))); | 213 base::Unretained(this))); |
| 214 RouteFunction("StartCastRtpReceiver", | |
| 215 base::Bind(&CastStreamingNativeHandler::StartCastRtpReceiver, | |
| 216 base::Unretained(this))); | |
| 202 } | 217 } |
| 203 | 218 |
| 204 CastStreamingNativeHandler::~CastStreamingNativeHandler() { | 219 CastStreamingNativeHandler::~CastStreamingNativeHandler() { |
| 205 } | 220 } |
| 206 | 221 |
| 207 void CastStreamingNativeHandler::CreateCastSession( | 222 void CastStreamingNativeHandler::CreateCastSession( |
| 208 const v8::FunctionCallbackInfo<v8::Value>& args) { | 223 const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 209 CHECK_EQ(3, args.Length()); | 224 CHECK_EQ(3, args.Length()); |
| 210 CHECK(args[2]->IsFunction()); | 225 CHECK(args[2]->IsFunction()); |
| 211 | 226 |
| (...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 433 const v8::FunctionCallbackInfo<v8::Value>& args) { | 448 const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 434 CHECK_EQ(2, args.Length()); | 449 CHECK_EQ(2, args.Length()); |
| 435 CHECK(args[0]->IsInt32()); | 450 CHECK(args[0]->IsInt32()); |
| 436 CHECK(args[1]->IsObject()); | 451 CHECK(args[1]->IsObject()); |
| 437 | 452 |
| 438 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value(); | 453 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value(); |
| 439 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id); | 454 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id); |
| 440 if (!transport) | 455 if (!transport) |
| 441 return; | 456 return; |
| 442 | 457 |
| 443 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | 458 net::IPEndPoint dest; |
| 444 scoped_ptr<base::Value> destination_value( | 459 if (!IPEndPointFromArg(args.GetIsolate(), |
| 445 converter->FromV8Value(args[1], context()->v8_context())); | 460 args[1], |
| 446 if (!destination_value) { | 461 false, |
| 447 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | 462 &dest)) { |
| 448 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs))); | |
| 449 return; | 463 return; |
| 450 } | 464 } |
| 451 scoped_ptr<IPEndPoint> destination = | 465 transport->SetDestination( |
| 452 IPEndPoint::FromValue(*destination_value); | 466 dest, |
| 453 if (!destination) { | 467 base::Bind(&CastStreamingNativeHandler::CallErrorCallback, |
| 454 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | 468 weak_factory_.GetWeakPtr(), |
| 455 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination))); | 469 transport_id)); |
| 456 return; | |
| 457 } | |
| 458 net::IPAddressNumber ip; | |
| 459 if (!net::ParseIPLiteralToNumber(destination->address, &ip)) { | |
| 460 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 461 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination))); | |
| 462 return; | |
| 463 } | |
| 464 transport->SetDestination(net::IPEndPoint(ip, destination->port)); | |
| 465 } | 470 } |
| 466 | 471 |
| 467 void CastStreamingNativeHandler::SetOptionsCastUdpTransport( | 472 void CastStreamingNativeHandler::SetOptionsCastUdpTransport( |
| 468 const v8::FunctionCallbackInfo<v8::Value>& args) { | 473 const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 469 CHECK_EQ(2, args.Length()); | 474 CHECK_EQ(2, args.Length()); |
| 470 CHECK(args[0]->IsInt32()); | 475 CHECK(args[0]->IsInt32()); |
| 471 CHECK(args[1]->IsObject()); | 476 CHECK(args[1]->IsObject()); |
| 472 | 477 |
| 473 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value(); | 478 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value(); |
| 474 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id); | 479 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id); |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 609 UdpTransportMap::const_iterator iter = udp_transport_map_.find( | 614 UdpTransportMap::const_iterator iter = udp_transport_map_.find( |
| 610 transport_id); | 615 transport_id); |
| 611 if (iter != udp_transport_map_.end()) | 616 if (iter != udp_transport_map_.end()) |
| 612 return iter->second.get(); | 617 return iter->second.get(); |
| 613 v8::Isolate* isolate = context()->v8_context()->GetIsolate(); | 618 v8::Isolate* isolate = context()->v8_context()->GetIsolate(); |
| 614 isolate->ThrowException(v8::Exception::RangeError( | 619 isolate->ThrowException(v8::Exception::RangeError( |
| 615 v8::String::NewFromUtf8(isolate, kUdpTransportNotFound))); | 620 v8::String::NewFromUtf8(isolate, kUdpTransportNotFound))); |
| 616 return NULL; | 621 return NULL; |
| 617 } | 622 } |
| 618 | 623 |
| 624 bool CastStreamingNativeHandler::FrameReceiverConfigFromArg( | |
| 625 v8::Isolate* isolate, | |
| 626 const v8::Handle<v8::Value>& arg, | |
| 627 media::cast::FrameReceiverConfig* config) { | |
| 628 | |
| 629 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | |
| 630 scoped_ptr<base::Value> params_value( | |
| 631 converter->FromV8Value(arg, context()->v8_context())); | |
| 632 if (!params_value) { | |
| 633 isolate->ThrowException(v8::Exception::TypeError( | |
| 634 v8::String::NewFromUtf8(isolate, kUnableToConvertParams))); | |
| 635 return false; | |
| 636 } | |
| 637 scoped_ptr<RtpReceiverParams> params = | |
| 638 RtpReceiverParams::FromValue(*params_value); | |
| 639 if (!params) { | |
| 640 isolate->ThrowException(v8::Exception::TypeError( | |
| 641 v8::String::NewFromUtf8(isolate, kInvalidRtpParams))); | |
| 642 return false; | |
| 643 } | |
| 644 | |
| 645 config->receiver_ssrc = params->feedback_ssrc; | |
| 646 config->sender_ssrc = params->ssrc; | |
| 647 config->rtp_max_delay_ms = params->max_latency; | |
| 648 if (config->rtp_max_delay_ms < 0 || config->rtp_max_delay_ms > 1000) { | |
| 649 isolate->ThrowException(v8::Exception::TypeError( | |
| 650 v8::String::NewFromUtf8(isolate, kInvalidRtpParams))); | |
| 651 return false; | |
| 652 } | |
| 653 config->channels = 2; | |
| 654 if (params->codec_name == "OPUS") { | |
| 655 config->codec = media::cast::CODEC_AUDIO_OPUS; | |
| 656 config->rtp_timebase = 48000; | |
|
miu
2015/02/28 04:32:58
Hmm...Taking a closer look at FrameReceiver, it se
hubbe
2015/03/03 01:23:03
Made it optional.
| |
| 657 config->rtp_payload_type = 127; | |
| 658 } else if (params->codec_name == "PCM16") { | |
| 659 config->codec = media::cast::CODEC_AUDIO_PCM16; | |
| 660 config->rtp_timebase = 48000; | |
| 661 config->rtp_payload_type =127; | |
| 662 } else if (params->codec_name == "AAC") { | |
| 663 config->codec = media::cast::CODEC_AUDIO_AAC; | |
| 664 config->rtp_timebase = 48000; | |
| 665 config->rtp_payload_type = 127; | |
| 666 } else if (params->codec_name == "VP8") { | |
| 667 config->codec = media::cast::CODEC_VIDEO_VP8; | |
| 668 config->rtp_timebase = 90000; | |
| 669 config->rtp_payload_type = 96; | |
| 670 } else if (params->codec_name == "H264") { | |
| 671 config->codec = media::cast::CODEC_VIDEO_H264; | |
| 672 config->rtp_timebase = 90000; | |
| 673 config->rtp_payload_type = 96; | |
| 674 } | |
| 675 if (params->aes_key && | |
| 676 !HexDecode(*params->aes_key, &config->aes_key)) { | |
| 677 isolate->ThrowException(v8::Exception::Error( | |
| 678 v8::String::NewFromUtf8(isolate, kInvalidAesKey))); | |
| 679 return false; | |
| 680 } | |
| 681 if (params->aes_iv_mask && | |
| 682 !HexDecode(*params->aes_iv_mask, &config->aes_iv_mask)) { | |
| 683 isolate->ThrowException(v8::Exception::Error( | |
| 684 v8::String::NewFromUtf8(isolate, kInvalidAesIvMask))); | |
| 685 return false; | |
| 686 } | |
| 687 return true; | |
| 688 } | |
| 689 | |
| 690 bool CastStreamingNativeHandler::IPEndPointFromArg( | |
| 691 v8::Isolate* isolate, | |
| 692 const v8::Handle<v8::Value>& arg, | |
| 693 bool empty_ok, | |
| 694 net::IPEndPoint* ip_endpoint) { | |
| 695 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | |
| 696 scoped_ptr<base::Value> destination_value( | |
| 697 converter->FromV8Value(arg, context()->v8_context())); | |
| 698 if (!destination_value) { | |
| 699 isolate->ThrowException(v8::Exception::TypeError( | |
| 700 v8::String::NewFromUtf8(isolate, kInvalidAesIvMask))); | |
| 701 return false; | |
| 702 } | |
| 703 scoped_ptr<IPEndPoint> destination = | |
| 704 IPEndPoint::FromValue(*destination_value); | |
| 705 if (!destination) { | |
| 706 isolate->ThrowException(v8::Exception::TypeError( | |
| 707 v8::String::NewFromUtf8(isolate, kInvalidDestination))); | |
| 708 return false; | |
| 709 } | |
| 710 net::IPAddressNumber ip; | |
| 711 if (destination->address != "" || !empty_ok) { | |
| 712 if (!net::ParseIPLiteralToNumber(destination->address, &ip)) { | |
| 713 isolate->ThrowException(v8::Exception::TypeError( | |
| 714 v8::String::NewFromUtf8(isolate, kInvalidDestination))); | |
| 715 return false; | |
| 716 } | |
| 717 } | |
| 718 *ip_endpoint = net::IPEndPoint(ip, destination->port); | |
| 719 return true; | |
| 720 } | |
| 721 | |
| 722 void CastStreamingNativeHandler::StartCastRtpReceiver( | |
| 723 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
| 724 if (args.Length() < 9 || args.Length() > 10 || | |
| 725 !args[0]->IsObject() || | |
| 726 !args[1]->IsObject() || | |
| 727 !args[2]->IsObject() || | |
| 728 !args[3]->IsObject() || | |
| 729 !args[4]->IsInt32() || | |
| 730 !args[5]->IsInt32() || | |
| 731 !args[6]->IsNumber() || | |
| 732 !args[7]->IsString()) { | |
| 733 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 734 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs))); | |
| 735 return; | |
| 736 } | |
| 737 | |
| 738 v8::Isolate* isolate = context()->v8_context()->GetIsolate(); | |
| 739 | |
| 740 scoped_refptr<CastReceiverSession> session( | |
| 741 new CastReceiverSession()); | |
| 742 media::cast::FrameReceiverConfig audio_config; | |
| 743 media::cast::FrameReceiverConfig video_config; | |
| 744 net::IPEndPoint local_endpoint; | |
| 745 net::IPEndPoint remote_endpoint; | |
| 746 | |
| 747 if (!FrameReceiverConfigFromArg(isolate, args[0], &audio_config) || | |
| 748 !FrameReceiverConfigFromArg(isolate, args[1], &video_config) || | |
| 749 !IPEndPointFromArg(isolate, args[2], true, &local_endpoint) || | |
| 750 !IPEndPointFromArg(isolate, args[3], true, &remote_endpoint)) { | |
| 751 return; | |
| 752 } | |
| 753 | |
| 754 const std::string url = *v8::String::Utf8Value(args[7]); | |
| 755 blink::WebMediaStream stream = | |
| 756 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); | |
| 757 | |
| 758 if (stream.isNull()) { | |
| 759 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 760 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidMediaStreamURL))); | |
| 761 return; | |
| 762 } | |
| 763 | |
| 764 const int width = args[4]->ToInt32(args.GetIsolate())->Value(); | |
| 765 const int height = args[5]->ToInt32(args.GetIsolate())->Value(); | |
| 766 const double fps = args[6]->NumberValue(); | |
| 767 | |
| 768 if (fps <= 1) { | |
| 769 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 770 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidFPS))); | |
| 771 return; | |
| 772 } | |
| 773 | |
| 774 media::VideoCaptureFormat capture_format( | |
| 775 gfx::Size(width, height), | |
| 776 fps, | |
| 777 media::PIXEL_FORMAT_I420); | |
| 778 | |
| 779 video_config.target_frame_rate = fps; | |
| 780 audio_config.target_frame_rate = 100; | |
| 781 | |
| 782 media::AudioParameters params( | |
| 783 media::AudioParameters::AUDIO_PCM_LINEAR, | |
| 784 media::CHANNEL_LAYOUT_STEREO, | |
| 785 audio_config.rtp_timebase, // sampling rate | |
| 786 16, | |
| 787 audio_config.rtp_timebase / audio_config.target_frame_rate); | |
| 788 | |
| 789 if (!params.IsValid()) { | |
| 790 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 791 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidAudioParams))); | |
| 792 return; | |
| 793 } | |
| 794 | |
| 795 base::DictionaryValue* options = NULL; | |
| 796 if (args.Length() >= 10) { | |
| 797 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | |
| 798 base::Value* options_value = | |
| 799 converter->FromV8Value(args[9], context()->v8_context()); | |
| 800 if (!options_value->IsType(base::Value::TYPE_NULL)) { | |
| 801 if (!options_value || !options_value->GetAsDictionary(&options)) { | |
| 802 delete options_value; | |
| 803 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 804 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs))); | |
| 805 return; | |
| 806 } | |
| 807 } | |
| 808 } | |
| 809 | |
| 810 if (!options) { | |
| 811 options = new base::DictionaryValue(); | |
| 812 } | |
| 813 | |
| 814 v8::CopyablePersistentTraits<v8::Function>::CopyablePersistent error_callback; | |
| 815 error_callback.Reset(args.GetIsolate(), | |
| 816 v8::Handle<v8::Function>(args[8].As<v8::Function>())); | |
| 817 | |
| 818 session->Start( | |
| 819 audio_config, | |
| 820 video_config, | |
| 821 local_endpoint, | |
| 822 remote_endpoint, | |
| 823 make_scoped_ptr(options), | |
| 824 capture_format, | |
| 825 base::Bind(&CastStreamingNativeHandler::AddTracksToMediaStream, | |
| 826 weak_factory_.GetWeakPtr(), | |
| 827 url, | |
| 828 params), | |
| 829 base::Bind(&CastStreamingNativeHandler::CallReceiverErrorCallback, | |
| 830 weak_factory_.GetWeakPtr(), | |
| 831 error_callback)); | |
| 832 } | |
| 833 | |
| 834 void CastStreamingNativeHandler::CallReceiverErrorCallback( | |
| 835 v8::CopyablePersistentTraits<v8::Function>::CopyablePersistent function, | |
| 836 const std::string& error_message) { | |
| 837 v8::Isolate* isolate = context()->v8_context()->GetIsolate(); | |
| 838 v8::Handle<v8::Value> arg = v8::String::NewFromUtf8(isolate, | |
| 839 error_message.data(), | |
| 840 v8::String::kNormalString, | |
| 841 error_message.size()); | |
| 842 context()->CallFunction( | |
| 843 v8::Local<v8::Function>::New(isolate, function), 1, &arg); | |
| 844 } | |
| 845 | |
| 846 | |
| 847 void CastStreamingNativeHandler::AddTracksToMediaStream( | |
| 848 const std::string& url, | |
| 849 const media::AudioParameters& params, | |
| 850 scoped_refptr<media::AudioCapturerSource> audio, | |
| 851 scoped_ptr<media::VideoCapturerSource> video) { | |
| 852 content::AddAudioTrackToMediaStream(audio, params, true, true, url); | |
| 853 content::AddVideoTrackToMediaStream(video.Pass(), true, true, url); | |
| 854 } | |
| 855 | |
| 619 } // namespace extensions | 856 } // namespace extensions |
| OLD | NEW |