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 |