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/media/cast_session_delegate.h"
7 #include "base/callback_helpers.h"
8 #include "base/lazy_instance.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/strings/stringprintf.h"
12 #include "chrome/common/chrome_version_info.h"
13 #include "chrome/renderer/media/cast_threads.h"
14 #include "chrome/renderer/media/cast_transport_sender_ipc.h"
15 #include "content/public/renderer/render_thread.h"
16 #include "media/cast/cast_config.h"
17 #include "media/cast/cast_environment.h"
18 #include "media/cast/cast_sender.h"
19 #include "media/cast/logging/log_serializer.h"
20 #include "media/cast/logging/logging_defines.h"
21 #include "media/cast/logging/proto/raw_events.pb.h"
22 #include "media/cast/logging/raw_event_subscriber_bundle.h"
23 #include "media/cast/net/cast_transport_config.h"
24 #include "media/cast/net/cast_transport_sender.h"
26 using media::cast::AudioSenderConfig
;
27 using media::cast::CastEnvironment
;
28 using media::cast::CastSender
;
29 using media::cast::VideoSenderConfig
;
31 static base::LazyInstance
<CastThreads
> g_cast_threads
=
32 LAZY_INSTANCE_INITIALIZER
;
34 CastSessionDelegateBase::CastSessionDelegateBase()
35 : io_message_loop_proxy_(
36 content::RenderThread::Get()->GetIOMessageLoopProxy()),
38 DCHECK(io_message_loop_proxy_
.get());
41 CastSessionDelegateBase::~CastSessionDelegateBase() {
42 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
45 void CastSessionDelegateBase::StartUDP(
46 const net::IPEndPoint
& local_endpoint
,
47 const net::IPEndPoint
& remote_endpoint
,
48 scoped_ptr
<base::DictionaryValue
> options
) {
49 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
51 // CastSender uses the renderer's IO thread as the main thread. This reduces
52 // thread hopping for incoming video frames and outgoing network packets.
53 // TODO(hubbe): Create cast environment in ctor instead.
54 cast_environment_
= new CastEnvironment(
55 scoped_ptr
<base::TickClock
>(new base::DefaultTickClock()).Pass(),
56 base::MessageLoopProxy::current(),
57 g_cast_threads
.Get().GetAudioEncodeMessageLoopProxy(),
58 g_cast_threads
.Get().GetVideoEncodeMessageLoopProxy());
60 // Rationale for using unretained: The callback cannot be called after the
61 // destruction of CastTransportSenderIPC, and they both share the same thread.
62 cast_transport_
.reset(new CastTransportSenderIPC(
66 base::Bind(&CastSessionDelegateBase::ReceivePacket
,
67 base::Unretained(this)),
68 base::Bind(&CastSessionDelegateBase::StatusNotificationCB
,
69 base::Unretained(this)),
70 base::Bind(&CastSessionDelegateBase::LogRawEvents
,
71 base::Unretained(this))));
74 void CastSessionDelegateBase::StatusNotificationCB(
75 media::cast::CastTransportStatus unused_status
) {
76 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
77 // TODO(hubbe): Call javascript UDPTransport error function.
80 CastSessionDelegate::CastSessionDelegate()
81 : weak_factory_(this) {
82 DCHECK(io_message_loop_proxy_
.get());
85 CastSessionDelegate::~CastSessionDelegate() {
86 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
89 void CastSessionDelegate::StartAudio(
90 const AudioSenderConfig
& config
,
91 const AudioFrameInputAvailableCallback
& callback
,
92 const ErrorCallback
& error_callback
) {
93 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
95 if (!cast_transport_
|| !cast_sender_
) {
96 error_callback
.Run("Destination not set.");
100 audio_frame_input_available_callback_
= callback
;
101 cast_sender_
->InitializeAudio(
103 base::Bind(&CastSessionDelegate::OnOperationalStatusChange
,
104 weak_factory_
.GetWeakPtr(), true, error_callback
));
107 void CastSessionDelegate::StartVideo(
108 const VideoSenderConfig
& config
,
109 const VideoFrameInputAvailableCallback
& callback
,
110 const ErrorCallback
& error_callback
,
111 const media::cast::CreateVideoEncodeAcceleratorCallback
& create_vea_cb
,
112 const media::cast::CreateVideoEncodeMemoryCallback
&
113 create_video_encode_mem_cb
) {
114 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
116 if (!cast_transport_
|| !cast_sender_
) {
117 error_callback
.Run("Destination not set.");
121 video_frame_input_available_callback_
= callback
;
123 cast_sender_
->InitializeVideo(
125 base::Bind(&CastSessionDelegate::OnOperationalStatusChange
,
126 weak_factory_
.GetWeakPtr(), false, error_callback
),
128 create_video_encode_mem_cb
);
132 void CastSessionDelegate::StartUDP(
133 const net::IPEndPoint
& local_endpoint
,
134 const net::IPEndPoint
& remote_endpoint
,
135 scoped_ptr
<base::DictionaryValue
> options
) {
136 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
137 CastSessionDelegateBase::StartUDP(local_endpoint
,
140 event_subscribers_
.reset(
141 new media::cast::RawEventSubscriberBundle(cast_environment_
));
143 cast_sender_
= CastSender::Create(cast_environment_
, cast_transport_
.get());
146 void CastSessionDelegate::ToggleLogging(bool is_audio
, bool enable
) {
147 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
148 if (!event_subscribers_
.get())
152 event_subscribers_
->AddEventSubscribers(is_audio
);
154 event_subscribers_
->RemoveEventSubscribers(is_audio
);
157 void CastSessionDelegate::GetEventLogsAndReset(
159 const std::string
& extra_data
,
160 const EventLogsCallback
& callback
) {
161 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
163 if (!event_subscribers_
.get()) {
164 callback
.Run(make_scoped_ptr(new base::BinaryValue
).Pass());
168 media::cast::EncodingEventSubscriber
* subscriber
=
169 event_subscribers_
->GetEncodingEventSubscriber(is_audio
);
171 callback
.Run(make_scoped_ptr(new base::BinaryValue
).Pass());
175 media::cast::proto::LogMetadata metadata
;
176 media::cast::FrameEventList frame_events
;
177 media::cast::PacketEventList packet_events
;
179 subscriber
->GetEventsAndReset(&metadata
, &frame_events
, &packet_events
);
181 if (!extra_data
.empty())
182 metadata
.set_extra_data(extra_data
);
183 media::cast::proto::GeneralDescription
* gen_desc
=
184 metadata
.mutable_general_description();
185 chrome::VersionInfo version_info
;
186 gen_desc
->set_product(version_info
.Name());
187 gen_desc
->set_product_version(version_info
.Version());
188 gen_desc
->set_os(version_info
.OSType());
190 scoped_ptr
<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes
]);
192 bool success
= media::cast::SerializeEvents(metadata
,
196 media::cast::kMaxSerializedBytes
,
197 serialized_log
.get(),
201 DVLOG(2) << "Failed to serialize event log.";
202 callback
.Run(make_scoped_ptr(new base::BinaryValue
).Pass());
206 DVLOG(2) << "Serialized log length: " << output_bytes
;
208 scoped_ptr
<base::BinaryValue
> blob(
209 new base::BinaryValue(serialized_log
.Pass(), output_bytes
));
210 callback
.Run(blob
.Pass());
213 void CastSessionDelegate::GetStatsAndReset(bool is_audio
,
214 const StatsCallback
& callback
) {
215 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
217 if (!event_subscribers_
.get()) {
218 callback
.Run(make_scoped_ptr(new base::DictionaryValue
).Pass());
222 media::cast::StatsEventSubscriber
* subscriber
=
223 event_subscribers_
->GetStatsEventSubscriber(is_audio
);
225 callback
.Run(make_scoped_ptr(new base::DictionaryValue
).Pass());
229 scoped_ptr
<base::DictionaryValue
> stats
= subscriber
->GetStats();
232 callback
.Run(stats
.Pass());
235 void CastSessionDelegate::OnOperationalStatusChange(
237 const ErrorCallback
& error_callback
,
238 media::cast::OperationalStatus status
) {
239 DCHECK(cast_sender_
);
242 case media::cast::STATUS_UNINITIALIZED
:
243 case media::cast::STATUS_CODEC_REINIT_PENDING
:
245 // TODO(miu): As an optimization, signal the client to pause sending more
246 // frames until the state becomes STATUS_INITIALIZED again.
248 case media::cast::STATUS_INITIALIZED
:
249 // Once initialized, run the "frame input available" callback to allow the
250 // client to begin sending frames. If STATUS_INITIALIZED is encountered
251 // again, do nothing since this is only an indication that the codec has
252 // successfully re-initialized.
254 if (!audio_frame_input_available_callback_
.is_null()) {
255 base::ResetAndReturn(&audio_frame_input_available_callback_
).Run(
256 cast_sender_
->audio_frame_input());
259 if (!video_frame_input_available_callback_
.is_null()) {
260 base::ResetAndReturn(&video_frame_input_available_callback_
).Run(
261 cast_sender_
->video_frame_input());
265 case media::cast::STATUS_INVALID_CONFIGURATION
:
266 error_callback
.Run(base::StringPrintf("Invalid %s configuration.",
267 is_for_audio
? "audio" : "video"));
269 case media::cast::STATUS_UNSUPPORTED_CODEC
:
270 error_callback
.Run(base::StringPrintf("%s codec not supported.",
271 is_for_audio
? "Audio" : "Video"));
273 case media::cast::STATUS_CODEC_INIT_FAILED
:
274 error_callback
.Run(base::StringPrintf("%s codec initialization failed.",
275 is_for_audio
? "Audio" : "Video"));
277 case media::cast::STATUS_CODEC_RUNTIME_ERROR
:
278 error_callback
.Run(base::StringPrintf("%s codec runtime error.",
279 is_for_audio
? "Audio" : "Video"));
284 void CastSessionDelegate::ReceivePacket(
285 scoped_ptr
<media::cast::Packet
> packet
) {
286 // Do nothing (frees packet)
289 void CastSessionDelegate::LogRawEvents(
290 const std::vector
<media::cast::PacketEvent
>& packet_events
,
291 const std::vector
<media::cast::FrameEvent
>& frame_events
) {
292 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
294 for (std::vector
<media::cast::PacketEvent
>::const_iterator it
=
295 packet_events
.begin();
296 it
!= packet_events
.end();
298 cast_environment_
->Logging()->InsertPacketEvent(it
->timestamp
,
307 for (std::vector
<media::cast::FrameEvent
>::const_iterator it
=
308 frame_events
.begin();
309 it
!= frame_events
.end();
311 if (it
->type
== media::cast::FRAME_PLAYOUT
) {
312 cast_environment_
->Logging()->InsertFrameEventWithDelay(
320 cast_environment_
->Logging()->InsertFrameEvent(