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(dest); |
| 452 IPEndPoint::FromValue(*destination_value); | |
| 453 if (!destination) { | |
| 454 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 455 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination))); | |
| 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 } | 466 } |
| 466 | 467 |
| 467 void CastStreamingNativeHandler::SetOptionsCastUdpTransport( | 468 void CastStreamingNativeHandler::SetOptionsCastUdpTransport( |
| 468 const v8::FunctionCallbackInfo<v8::Value>& args) { | 469 const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 469 CHECK_EQ(2, args.Length()); | 470 CHECK_EQ(2, args.Length()); |
| 470 CHECK(args[0]->IsInt32()); | 471 CHECK(args[0]->IsInt32()); |
| 471 CHECK(args[1]->IsObject()); | 472 CHECK(args[1]->IsObject()); |
| 472 | 473 |
| 473 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value(); | 474 const int transport_id = args[0]->ToInt32(args.GetIsolate())->Value(); |
| 474 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id); | 475 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( | 610 UdpTransportMap::const_iterator iter = udp_transport_map_.find( |
| 610 transport_id); | 611 transport_id); |
| 611 if (iter != udp_transport_map_.end()) | 612 if (iter != udp_transport_map_.end()) |
| 612 return iter->second.get(); | 613 return iter->second.get(); |
| 613 v8::Isolate* isolate = context()->v8_context()->GetIsolate(); | 614 v8::Isolate* isolate = context()->v8_context()->GetIsolate(); |
| 614 isolate->ThrowException(v8::Exception::RangeError( | 615 isolate->ThrowException(v8::Exception::RangeError( |
| 615 v8::String::NewFromUtf8(isolate, kUdpTransportNotFound))); | 616 v8::String::NewFromUtf8(isolate, kUdpTransportNotFound))); |
| 616 return NULL; | 617 return NULL; |
| 617 } | 618 } |
| 618 | 619 |
| 620 bool CastStreamingNativeHandler::FrameReceiverFromArg( | |
| 621 v8::Isolate* isolate, | |
| 622 const v8::Handle<v8::Value>& arg, | |
| 623 media::cast::FrameReceiverConfig* config) { | |
| 624 | |
| 625 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | |
| 626 scoped_ptr<base::Value> params_value( | |
| 627 converter->FromV8Value(arg, context()->v8_context())); | |
| 628 if (!params_value) { | |
| 629 isolate->ThrowException(v8::Exception::TypeError( | |
| 630 v8::String::NewFromUtf8(isolate, kUnableToConvertParams))); | |
| 631 return false; | |
| 632 } | |
| 633 scoped_ptr<RtpReceiverParams> params = | |
| 634 RtpReceiverParams::FromValue(*params_value); | |
| 635 if (!params) { | |
| 636 isolate->ThrowException(v8::Exception::TypeError( | |
| 637 v8::String::NewFromUtf8(isolate, kInvalidRtpParams))); | |
| 638 return false; | |
| 639 } | |
| 640 | |
| 641 config->feedback_ssrc = params->feedback_ssrc; | |
| 642 config->incoming_ssrc = params->ssrc; | |
| 643 config->rtp_max_delay_ms = params->max_latency; | |
|
miu
2015/02/26 06:23:34
Please range-check this value, and throw a v8 exce
hubbe
2015/02/28 00:17:12
Done.
| |
| 644 config->rtp_payload_type = params->payload_type; | |
| 645 config->channels = params->channels ? *params->channels : 0; | |
| 646 if (params->codec_name == "OPUS") { | |
| 647 config->codec = media::cast::CODEC_AUDIO_OPUS; | |
| 648 config->rtp_timebase = 48000; | |
| 649 } else if (params->codec_name == "PCM16") { | |
| 650 config->codec = media::cast::CODEC_AUDIO_PCM16; | |
| 651 config->rtp_timebase = 48000; | |
| 652 } else if (params->codec_name == "AAC") { | |
| 653 config->codec = media::cast::CODEC_AUDIO_AAC; | |
| 654 config->rtp_timebase = 48000; | |
| 655 } else if (params->codec_name == "VP8") { | |
| 656 config->codec = media::cast::CODEC_VIDEO_VP8; | |
| 657 config->rtp_timebase = 90000; | |
| 658 } else if (params->codec_name == "H264") { | |
| 659 config->codec = media::cast::CODEC_VIDEO_H264; | |
| 660 config->rtp_timebase = 90000; | |
| 661 } | |
| 662 config->rtp_timebase = params->clock_rate ? *params->clock_rate : 0; | |
|
miu
2015/02/26 06:23:34
This overwrites the previous assignments of config
hubbe
2015/02/28 00:17:11
Done.
| |
| 663 if (params->aes_key && | |
| 664 !HexDecode(*params->aes_key, &config->aes_key)) { | |
| 665 isolate->ThrowException(v8::Exception::Error( | |
| 666 v8::String::NewFromUtf8(isolate, kInvalidAesKey))); | |
| 667 return false; | |
| 668 } | |
| 669 if (params->aes_iv_mask && | |
| 670 !HexDecode(*params->aes_iv_mask, &config->aes_iv_mask)) { | |
| 671 isolate->ThrowException(v8::Exception::Error( | |
| 672 v8::String::NewFromUtf8(isolate, kInvalidAesIvMask))); | |
| 673 return false; | |
| 674 } | |
| 675 return true; | |
| 676 } | |
| 677 | |
| 678 bool CastStreamingNativeHandler::IPEndPointFromArg( | |
| 679 v8::Isolate* isolate, | |
| 680 const v8::Handle<v8::Value>& arg, | |
| 681 bool empty_ok, | |
| 682 net::IPEndPoint* ip_endpoint) { | |
| 683 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | |
| 684 scoped_ptr<base::Value> destination_value( | |
| 685 converter->FromV8Value(arg, context()->v8_context())); | |
| 686 if (!destination_value) { | |
| 687 isolate->ThrowException(v8::Exception::TypeError( | |
| 688 v8::String::NewFromUtf8(isolate, kInvalidAesIvMask))); | |
| 689 return false; | |
| 690 } | |
| 691 scoped_ptr<IPEndPoint> destination = | |
| 692 IPEndPoint::FromValue(*destination_value); | |
| 693 if (!destination) { | |
| 694 isolate->ThrowException(v8::Exception::TypeError( | |
| 695 v8::String::NewFromUtf8(isolate, kInvalidDestination))); | |
| 696 return false; | |
| 697 } | |
| 698 net::IPAddressNumber ip; | |
| 699 if (destination->address != "" || !empty_ok) { | |
| 700 if (!net::ParseIPLiteralToNumber(destination->address, &ip)) { | |
| 701 isolate->ThrowException(v8::Exception::TypeError( | |
| 702 v8::String::NewFromUtf8(isolate, kInvalidDestination))); | |
| 703 return false; | |
| 704 } | |
| 705 } | |
| 706 *ip_endpoint = net::IPEndPoint(ip, destination->port); | |
| 707 return true; | |
| 708 } | |
| 709 | |
| 710 void CastStreamingNativeHandler::StartCastRtpReceiver( | |
| 711 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
| 712 if (args.Length() < 8 || args.Length() > 9 || | |
| 713 !args[0]->IsObject() || | |
| 714 !args[1]->IsObject() || | |
| 715 !args[2]->IsObject() || | |
| 716 !args[3]->IsObject() || | |
| 717 !args[4]->IsInt32() || | |
| 718 !args[5]->IsInt32() || | |
| 719 !args[6]->IsNumber() || | |
| 720 !args[7]->IsString()) { | |
| 721 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 722 v8::String::NewFromUtf8(args.GetIsolate(), "ARG!!!"))); | |
|
Alpha Left Google
2015/02/25 20:37:28
Should be a more user friendly string.
hubbe
2015/02/28 00:17:11
OOps, fixed.
| |
| 723 return; | |
| 724 } | |
| 725 | |
| 726 v8::Isolate* isolate = context()->v8_context()->GetIsolate(); | |
| 727 | |
| 728 scoped_refptr<CastReceiverSession> session( | |
| 729 new CastReceiverSession()); | |
| 730 media::cast::FrameReceiverConfig audio_config; | |
| 731 media::cast::FrameReceiverConfig video_config; | |
| 732 net::IPEndPoint local_endpoint; | |
| 733 net::IPEndPoint remote_endpoint; | |
| 734 | |
| 735 if (!FrameReceiverFromArg(isolate, args[0], &audio_config) || | |
| 736 !FrameReceiverFromArg(isolate, args[1], &video_config) || | |
| 737 !IPEndPointFromArg(isolate, args[2], true, &local_endpoint) || | |
| 738 !IPEndPointFromArg(isolate, args[3], true, &remote_endpoint)) { | |
| 739 return; | |
|
miu
2015/02/26 06:23:34
Throw error exception here?
hubbe
2015/02/28 00:17:12
An exception will have already been raised in this
| |
| 740 } | |
| 741 | |
| 742 const std::string url = *v8::String::Utf8Value(args[7]); | |
| 743 blink::WebMediaStream stream = | |
| 744 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); | |
| 745 | |
| 746 if (stream.isNull()) { | |
| 747 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 748 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidMediaStreamURL))); | |
| 749 return; | |
| 750 } | |
| 751 | |
| 752 const int width = args[4]->ToInt32(args.GetIsolate())->Value(); | |
| 753 const int height = args[5]->ToInt32(args.GetIsolate())->Value(); | |
| 754 const double fps = args[6]->NumberValue(); | |
| 755 | |
| 756 if (fps <= 1) { | |
| 757 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 758 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidFPS))); | |
| 759 return; | |
| 760 } | |
| 761 | |
| 762 media::VideoCaptureFormat capture_format( | |
| 763 gfx::Size(width, height), | |
| 764 fps, | |
| 765 media::PIXEL_FORMAT_I420); | |
| 766 | |
| 767 video_config.target_frame_rate = fps; | |
|
Alpha Left Google
2015/02/25 20:37:28
I think the fps parameter in VideoCaptureFormat ha
miu
2015/02/26 06:23:34
Doesn't a Cast Receiver have NO concept of frame r
hubbe
2015/02/28 00:17:12
That might be true, but I don't actually know how
hubbe
2015/02/28 00:17:12
Unfortunately capture_format.IsValid() must be tru
| |
| 768 audio_config.target_frame_rate = 100; | |
| 769 | |
| 770 media::AudioParameters params( | |
| 771 media::AudioParameters::AUDIO_PCM_LINEAR, | |
| 772 media::CHANNEL_LAYOUT_STEREO, | |
| 773 audio_config.rtp_timebase, // sampling rate | |
| 774 16, | |
| 775 audio_config.rtp_timebase / 100); // frames per buffer, | |
|
Alpha Left Google
2015/02/25 20:37:28
Divide by audio_config.target_frame_rate instead.
hubbe
2015/02/28 00:17:11
Done.
| |
| 776 | |
| 777 if (!params.IsValid()) { | |
| 778 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 779 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidAudioParams))); | |
| 780 return; | |
| 781 } | |
| 782 | |
| 783 base::DictionaryValue* options = NULL; | |
| 784 if (args.Length() >= 9) { | |
| 785 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); | |
| 786 base::Value* options_value = | |
| 787 converter->FromV8Value(args[8], context()->v8_context()); | |
| 788 if (!options_value->IsType(base::Value::TYPE_NULL)) { | |
| 789 if (!options_value || !options_value->GetAsDictionary(&options)) { | |
| 790 delete options_value; | |
| 791 args.GetIsolate()->ThrowException(v8::Exception::TypeError( | |
| 792 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs))); | |
| 793 return; | |
| 794 } | |
| 795 } | |
| 796 } | |
| 797 | |
| 798 if (!options) { | |
| 799 options = new base::DictionaryValue(); | |
| 800 } | |
| 801 | |
| 802 session->Start(audio_config, | |
| 803 video_config, | |
| 804 local_endpoint, | |
| 805 remote_endpoint, | |
| 806 make_scoped_ptr(options), | |
| 807 capture_format, | |
| 808 base::Bind(&CastStreamingNativeHandler::CallReceiverCB, | |
| 809 weak_factory_.GetWeakPtr(), | |
| 810 url, | |
| 811 params)); | |
| 812 } | |
| 813 | |
| 814 void CastStreamingNativeHandler::CallReceiverCB( | |
| 815 const std::string& url, | |
| 816 const media::AudioParameters& params, | |
| 817 scoped_refptr<media::AudioCapturerSource> audio, | |
| 818 scoped_ptr<media::VideoCapturerSource> video) { | |
| 819 | |
| 820 content::AddAudioTrackToMediaStream(audio, params, true, true, url); | |
| 821 content::AddVideoTrackToMediaStream(video.Pass(), true, true, url); | |
| 822 } | |
| 823 | |
| 824 | |
|
Alpha Left Google
2015/02/25 20:37:28
nit: remove this extra empty line.
hubbe
2015/02/28 00:17:12
Done.
| |
| 619 } // namespace extensions | 825 } // namespace extensions |
| OLD | NEW |