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.
5 #include "chrome/renderer/extensions/cast_streaming_native_handler.h"
9 #include "base/base64.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "chrome/common/extensions/api/cast_streaming_rtp_stream.h"
13 #include "chrome/common/extensions/api/cast_streaming_udp_transport.h"
14 #include "chrome/renderer/extensions/chrome_v8_context.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 "net/base/host_port_pair.h"
20 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
21 #include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h"
23 using content::V8ValueConverter;
26 using extensions::api::cast_streaming_rtp_stream::CodecSpecificParams;
27 using extensions::api::cast_streaming_rtp_stream::RtpParams;
28 using extensions::api::cast_streaming_rtp_stream::RtpPayloadParams;
29 using extensions::api::cast_streaming_udp_transport::IPEndPoint;
31 namespace extensions {
34 const char kRtpStreamNotFound[] = "The RTP stream cannot be found";
35 const char kUdpTransportNotFound[] = "The UDP transport cannot be found";
36 const char kInvalidDestination[] = "Invalid destination";
37 const char kInvalidRtpParams[] = "Invalid value for RTP params";
38 const char kInvalidAesKey[] = "Invalid value for AES key";
39 const char kInvalidAesIvMask[] = "Invalid value for AES IV mask";
40 const char kUnableToConvertArgs[] = "Unable to convert arguments";
41 const char kUnableToConvertParams[] = "Unable to convert params";
43 // These helper methods are used to convert between Extension API
44 // types and Cast types.
45 void ToCastCodecSpecificParams(const CodecSpecificParams& ext_params,
46 CastCodecSpecificParams* cast_params) {
47 cast_params->key = ext_params.key;
48 cast_params->value = ext_params.value;
51 void FromCastCodecSpecificParams(const CastCodecSpecificParams& cast_params,
52 CodecSpecificParams* ext_params) {
53 ext_params->key = cast_params.key;
54 ext_params->value = cast_params.value;
57 bool ToCastRtpPayloadParamsOrThrow(v8::Isolate* isolate,
58 const RtpPayloadParams& ext_params,
59 CastRtpPayloadParams* cast_params) {
60 cast_params->payload_type = ext_params.payload_type;
61 cast_params->codec_name = ext_params.codec_name;
62 cast_params->ssrc = ext_params.ssrc ? *ext_params.ssrc : 0;
63 cast_params->feedback_ssrc =
64 ext_params.feedback_ssrc ? *ext_params.feedback_ssrc : 0;
65 cast_params->clock_rate = ext_params.clock_rate ? *ext_params.clock_rate : 0;
66 cast_params->min_bitrate =
67 ext_params.min_bitrate ? *ext_params.min_bitrate : 0;
68 cast_params->max_bitrate =
69 ext_params.max_bitrate ? *ext_params.max_bitrate : 0;
70 cast_params->channels = ext_params.channels ? *ext_params.channels : 0;
71 cast_params->width = ext_params.width ? *ext_params.width : 0;
72 cast_params->height = ext_params.height ? *ext_params.height : 0;
73 if (ext_params.aes_key &&
74 !base::Base64Decode(*ext_params.aes_key, &cast_params->aes_key)) {
75 isolate->ThrowException(v8::Exception::Error(
76 v8::String::NewFromUtf8(isolate, kInvalidAesKey)));
79 if (ext_params.aes_iv_mask &&
80 !base::Base64Decode(*ext_params.aes_iv_mask,
81 &cast_params->aes_iv_mask)) {
82 isolate->ThrowException(v8::Exception::Error(
83 v8::String::NewFromUtf8(isolate, kInvalidAesIvMask)));
86 for (size_t i = 0; i < ext_params.codec_specific_params.size(); ++i) {
87 CastCodecSpecificParams cast_codec_params;
88 ToCastCodecSpecificParams(*ext_params.codec_specific_params[i],
90 cast_params->codec_specific_params.push_back(cast_codec_params);
95 void FromCastRtpPayloadParams(const CastRtpPayloadParams& cast_params,
96 RtpPayloadParams* ext_params) {
97 ext_params->payload_type = cast_params.payload_type;
98 ext_params->codec_name = cast_params.codec_name;
100 ext_params->ssrc.reset(new int(cast_params.ssrc));
101 if (cast_params.feedback_ssrc)
102 ext_params->feedback_ssrc.reset(new int(cast_params.feedback_ssrc));
103 if (cast_params.clock_rate)
104 ext_params->clock_rate.reset(new int(cast_params.clock_rate));
105 if (cast_params.min_bitrate)
106 ext_params->min_bitrate.reset(new int(cast_params.min_bitrate));
107 if (cast_params.max_bitrate)
108 ext_params->max_bitrate.reset(new int(cast_params.max_bitrate));
109 if (cast_params.channels)
110 ext_params->channels.reset(new int(cast_params.channels));
111 if (cast_params.width)
112 ext_params->width.reset(new int(cast_params.width));
113 if (cast_params.height)
114 ext_params->height.reset(new int(cast_params.height));
115 for (size_t i = 0; i < cast_params.codec_specific_params.size(); ++i) {
116 linked_ptr<CodecSpecificParams> ext_codec_params(
117 new CodecSpecificParams());
118 FromCastCodecSpecificParams(cast_params.codec_specific_params[i],
119 ext_codec_params.get());
120 ext_params->codec_specific_params.push_back(ext_codec_params);
124 void FromCastRtpParams(const CastRtpParams& cast_params,
125 RtpParams* ext_params) {
126 std::copy(cast_params.rtcp_features.begin(), cast_params.rtcp_features.end(),
127 ext_params->rtcp_features.begin());
128 FromCastRtpPayloadParams(cast_params.payload, &ext_params->payload);
131 bool ToCastRtpParamsOrThrow(v8::Isolate* isolate,
132 const RtpParams& ext_params,
133 CastRtpParams* cast_params) {
134 std::copy(ext_params.rtcp_features.begin(), ext_params.rtcp_features.end(),
135 cast_params->rtcp_features.begin());
136 if (!ToCastRtpPayloadParamsOrThrow(isolate,
138 &cast_params->payload)) {
146 CastStreamingNativeHandler::CastStreamingNativeHandler(ChromeV8Context* context)
147 : ObjectBackedNativeHandler(context),
148 last_transport_id_(0),
149 weak_factory_(this) {
150 RouteFunction("CreateSession",
151 base::Bind(&CastStreamingNativeHandler::CreateCastSession,
152 base::Unretained(this)));
153 RouteFunction("DestroyCastRtpStream",
154 base::Bind(&CastStreamingNativeHandler::DestroyCastRtpStream,
155 base::Unretained(this)));
156 RouteFunction("GetSupportedParamsCastRtpStream",
157 base::Bind(&CastStreamingNativeHandler::GetSupportedParamsCastRtpStream,
158 base::Unretained(this)));
159 RouteFunction("StartCastRtpStream",
160 base::Bind(&CastStreamingNativeHandler::StartCastRtpStream,
161 base::Unretained(this)));
162 RouteFunction("StopCastRtpStream",
163 base::Bind(&CastStreamingNativeHandler::StopCastRtpStream,
164 base::Unretained(this)));
165 RouteFunction("DestroyCastUdpTransport",
166 base::Bind(&CastStreamingNativeHandler::DestroyCastUdpTransport,
167 base::Unretained(this)));
168 RouteFunction("SetDestinationCastUdpTransport",
169 base::Bind(&CastStreamingNativeHandler::SetDestinationCastUdpTransport,
170 base::Unretained(this)));
173 CastStreamingNativeHandler::~CastStreamingNativeHandler() {
176 void CastStreamingNativeHandler::CreateCastSession(
177 const v8::FunctionCallbackInfo<v8::Value>& args) {
178 CHECK_EQ(3, args.Length());
179 CHECK(args[0]->IsObject());
180 CHECK(args[1]->IsObject());
181 CHECK(args[2]->IsFunction());
183 blink::WebDOMMediaStreamTrack track1 =
184 blink::WebDOMMediaStreamTrack::fromV8Value(args[0]);
187 blink::WebDOMMediaStreamTrack track2 =
188 blink::WebDOMMediaStreamTrack::fromV8Value(args[1]);
192 scoped_refptr<CastSession> session(new CastSession());
193 scoped_ptr<CastRtpStream> stream1(
194 new CastRtpStream(track1.component(), session));
195 scoped_ptr<CastRtpStream> stream2(
196 new CastRtpStream(track2.component(), session));
197 scoped_ptr<CastUdpTransport> udp_transport(
198 new CastUdpTransport(session));
200 create_callback_.reset(args[2].As<v8::Function>());
202 base::MessageLoop::current()->PostTask(
205 &CastStreamingNativeHandler::CallCreateCallback,
206 weak_factory_.GetWeakPtr(),
207 base::Passed(&stream1),
208 base::Passed(&stream2),
209 base::Passed(&udp_transport)));
212 void CastStreamingNativeHandler::CallCreateCallback(
213 scoped_ptr<CastRtpStream> stream1,
214 scoped_ptr<CastRtpStream> stream2,
215 scoped_ptr<CastUdpTransport> udp_transport) {
216 v8::Isolate* isolate = context()->isolate();
217 v8::HandleScope handle_scope(isolate);
218 v8::Context::Scope context_scope(context()->v8_context());
220 const int stream1_id = last_transport_id_++;
221 rtp_stream_map_[stream1_id] =
222 linked_ptr<CastRtpStream>(stream1.release());
223 const int stream2_id = last_transport_id_++;
224 rtp_stream_map_[stream2_id] =
225 linked_ptr<CastRtpStream>(stream2.release());
226 const int udp_id = last_transport_id_++;
227 udp_transport_map_[udp_id] =
228 linked_ptr<CastUdpTransport>(udp_transport.release());
230 v8::Handle<v8::Value> callback_args[3];
231 callback_args[0] = v8::Integer::New(isolate, stream1_id);
232 callback_args[1] = v8::Integer::New(isolate, stream2_id);
233 callback_args[2] = v8::Integer::New(isolate, udp_id);
234 context()->CallFunction(create_callback_.NewHandle(isolate),
236 create_callback_.reset();
239 void CastStreamingNativeHandler::CallStartCallback(int stream_id) {
240 v8::Isolate* isolate = context()->isolate();
241 v8::HandleScope handle_scope(isolate);
242 v8::Context::Scope context_scope(context()->v8_context());
243 v8::Handle<v8::Array> event_args = v8::Array::New(isolate, 1);
244 event_args->Set(0, v8::Integer::New(isolate, stream_id));
245 context()->DispatchEvent("cast.streaming.rtpStream.onStarted", event_args);
248 void CastStreamingNativeHandler::CallStopCallback(int stream_id) {
249 v8::Isolate* isolate = context()->isolate();
250 v8::HandleScope handle_scope(isolate);
251 v8::Context::Scope context_scope(context()->v8_context());
252 v8::Handle<v8::Array> event_args = v8::Array::New(isolate, 1);
253 event_args->Set(0, v8::Integer::New(isolate, stream_id));
254 context()->DispatchEvent("cast.streaming.rtpStream.onStopped", event_args);
257 void CastStreamingNativeHandler::CallErrorCallback(int stream_id,
258 const std::string& message) {
259 v8::Isolate* isolate = context()->isolate();
260 v8::HandleScope handle_scope(isolate);
261 v8::Context::Scope context_scope(context()->v8_context());
262 v8::Handle<v8::Array> event_args = v8::Array::New(isolate, 2);
263 event_args->Set(0, v8::Integer::New(isolate, stream_id));
266 v8::String::NewFromUtf8(
267 isolate, message.data(), v8::String::kNormalString, message.size()));
268 context()->DispatchEvent("cast.streaming.rtpStream.onError", event_args);
271 void CastStreamingNativeHandler::DestroyCastRtpStream(
272 const v8::FunctionCallbackInfo<v8::Value>& args) {
273 CHECK_EQ(1, args.Length());
274 CHECK(args[0]->IsInt32());
276 const int transport_id = args[0]->ToInt32()->Value();
277 if (!GetRtpStreamOrThrow(transport_id))
279 rtp_stream_map_.erase(transport_id);
282 void CastStreamingNativeHandler::GetSupportedParamsCastRtpStream(
283 const v8::FunctionCallbackInfo<v8::Value>& args) {
284 CHECK_EQ(1, args.Length());
285 CHECK(args[0]->IsInt32());
287 const int transport_id = args[0]->ToInt32()->Value();
288 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
292 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
293 std::vector<CastRtpParams> cast_params = transport->GetSupportedParams();
294 v8::Handle<v8::Array> result =
295 v8::Array::New(args.GetIsolate(),
296 static_cast<int>(cast_params.size()));
297 for (size_t i = 0; i < cast_params.size(); ++i) {
299 FromCastRtpParams(cast_params[i], ¶ms);
300 scoped_ptr<base::DictionaryValue> params_value = params.ToValue();
303 converter->ToV8Value(params_value.get(), context()->v8_context()));
305 args.GetReturnValue().Set(result);
308 void CastStreamingNativeHandler::StartCastRtpStream(
309 const v8::FunctionCallbackInfo<v8::Value>& args) {
310 CHECK_EQ(2, args.Length());
311 CHECK(args[0]->IsInt32());
312 CHECK(args[1]->IsObject());
314 const int transport_id = args[0]->ToInt32()->Value();
315 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
319 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
320 scoped_ptr<base::Value> params_value(
321 converter->FromV8Value(args[1], context()->v8_context()));
323 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
324 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertParams)));
327 scoped_ptr<RtpParams> params = RtpParams::FromValue(*params_value);
329 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
330 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidRtpParams)));
334 CastRtpParams cast_params;
335 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
336 if (!ToCastRtpParamsOrThrow(isolate, *params, &cast_params))
339 base::Closure start_callback =
340 base::Bind(&CastStreamingNativeHandler::CallStartCallback,
341 weak_factory_.GetWeakPtr(),
343 base::Closure stop_callback =
344 base::Bind(&CastStreamingNativeHandler::CallStopCallback,
345 weak_factory_.GetWeakPtr(),
347 CastRtpStream::ErrorCallback error_callback =
348 base::Bind(&CastStreamingNativeHandler::CallErrorCallback,
349 weak_factory_.GetWeakPtr(),
351 transport->Start(cast_params, start_callback, stop_callback, error_callback);
354 void CastStreamingNativeHandler::StopCastRtpStream(
355 const v8::FunctionCallbackInfo<v8::Value>& args) {
356 CHECK_EQ(1, args.Length());
357 CHECK(args[0]->IsInt32());
359 const int transport_id = args[0]->ToInt32()->Value();
360 CastRtpStream* transport = GetRtpStreamOrThrow(transport_id);
366 void CastStreamingNativeHandler::DestroyCastUdpTransport(
367 const v8::FunctionCallbackInfo<v8::Value>& args) {
368 CHECK_EQ(1, args.Length());
369 CHECK(args[0]->IsInt32());
371 const int transport_id = args[0]->ToInt32()->Value();
372 if (!GetUdpTransportOrThrow(transport_id))
374 udp_transport_map_.erase(transport_id);
377 void CastStreamingNativeHandler::SetDestinationCastUdpTransport(
378 const v8::FunctionCallbackInfo<v8::Value>& args) {
379 CHECK_EQ(2, args.Length());
380 CHECK(args[0]->IsInt32());
381 CHECK(args[1]->IsObject());
383 const int transport_id = args[0]->ToInt32()->Value();
384 CastUdpTransport* transport = GetUdpTransportOrThrow(transport_id);
388 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
389 scoped_ptr<base::Value> destination_value(
390 converter->FromV8Value(args[1], context()->v8_context()));
391 if (!destination_value) {
392 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
393 v8::String::NewFromUtf8(args.GetIsolate(), kUnableToConvertArgs)));
396 scoped_ptr<IPEndPoint> destination =
397 IPEndPoint::FromValue(*destination_value);
399 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
400 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination)));
403 net::IPAddressNumber ip;
404 if (!net::ParseIPLiteralToNumber(destination->address, &ip)) {
405 args.GetIsolate()->ThrowException(v8::Exception::TypeError(
406 v8::String::NewFromUtf8(args.GetIsolate(), kInvalidDestination)));
409 transport->SetDestination(net::IPEndPoint(ip, destination->port));
412 CastRtpStream* CastStreamingNativeHandler::GetRtpStreamOrThrow(
413 int transport_id) const {
414 RtpStreamMap::const_iterator iter = rtp_stream_map_.find(
416 if (iter != rtp_stream_map_.end())
417 return iter->second.get();
418 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
419 isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8(
420 isolate, kRtpStreamNotFound)));
424 CastUdpTransport* CastStreamingNativeHandler::GetUdpTransportOrThrow(
425 int transport_id) const {
426 UdpTransportMap::const_iterator iter = udp_transport_map_.find(
428 if (iter != udp_transport_map_.end())
429 return iter->second.get();
430 v8::Isolate* isolate = context()->v8_context()->GetIsolate();
431 isolate->ThrowException(v8::Exception::RangeError(
432 v8::String::NewFromUtf8(isolate, kUdpTransportNotFound)));
436 } // namespace extensions