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 |