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 const ErrorCallback
& error_callback
) {
50 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
52 // CastSender uses the renderer's IO thread as the main thread. This reduces
53 // thread hopping for incoming video frames and outgoing network packets.
54 // TODO(hubbe): Create cast environment in ctor instead.
55 cast_environment_
= new CastEnvironment(
56 scoped_ptr
<base::TickClock
>(new base::DefaultTickClock()).Pass(),
57 base::MessageLoopProxy::current(),
58 g_cast_threads
.Get().GetAudioEncodeMessageLoopProxy(),
59 g_cast_threads
.Get().GetVideoEncodeMessageLoopProxy());
61 // Rationale for using unretained: The callback cannot be called after the
62 // destruction of CastTransportSenderIPC, and they both share the same thread.
63 cast_transport_
.reset(new CastTransportSenderIPC(
67 base::Bind(&CastSessionDelegateBase::ReceivePacket
,
68 base::Unretained(this)),
69 base::Bind(&CastSessionDelegateBase::StatusNotificationCB
,
70 base::Unretained(this), error_callback
),
71 base::Bind(&CastSessionDelegateBase::LogRawEvents
,
72 base::Unretained(this))));
75 void CastSessionDelegateBase::StatusNotificationCB(
76 const ErrorCallback
& error_callback
,
77 media::cast::CastTransportStatus status
) {
78 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
79 std::string error_message
;
82 case media::cast::TRANSPORT_AUDIO_UNINITIALIZED
:
83 case media::cast::TRANSPORT_VIDEO_UNINITIALIZED
:
84 case media::cast::TRANSPORT_AUDIO_INITIALIZED
:
85 case media::cast::TRANSPORT_VIDEO_INITIALIZED
:
86 return; // Not errors, do nothing.
87 case media::cast::TRANSPORT_INVALID_CRYPTO_CONFIG
:
88 error_callback
.Run("Invalid encrypt/decrypt configuration.");
90 case media::cast::TRANSPORT_SOCKET_ERROR
:
91 error_callback
.Run("Socket error.");
96 CastSessionDelegate::CastSessionDelegate()
97 : weak_factory_(this) {
98 DCHECK(io_message_loop_proxy_
.get());
101 CastSessionDelegate::~CastSessionDelegate() {
102 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
105 void CastSessionDelegate::StartAudio(
106 const AudioSenderConfig
& config
,
107 const AudioFrameInputAvailableCallback
& callback
,
108 const ErrorCallback
& error_callback
) {
109 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
111 if (!cast_transport_
|| !cast_sender_
) {
112 error_callback
.Run("Destination not set.");
116 audio_frame_input_available_callback_
= callback
;
117 cast_sender_
->InitializeAudio(
119 base::Bind(&CastSessionDelegate::OnOperationalStatusChange
,
120 weak_factory_
.GetWeakPtr(), true, error_callback
));
123 void CastSessionDelegate::StartVideo(
124 const VideoSenderConfig
& config
,
125 const VideoFrameInputAvailableCallback
& callback
,
126 const ErrorCallback
& error_callback
,
127 const media::cast::CreateVideoEncodeAcceleratorCallback
& create_vea_cb
,
128 const media::cast::CreateVideoEncodeMemoryCallback
&
129 create_video_encode_mem_cb
) {
130 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
132 if (!cast_transport_
|| !cast_sender_
) {
133 error_callback
.Run("Destination not set.");
137 video_frame_input_available_callback_
= callback
;
139 cast_sender_
->InitializeVideo(
141 base::Bind(&CastSessionDelegate::OnOperationalStatusChange
,
142 weak_factory_
.GetWeakPtr(), false, error_callback
),
144 create_video_encode_mem_cb
);
148 void CastSessionDelegate::StartUDP(
149 const net::IPEndPoint
& local_endpoint
,
150 const net::IPEndPoint
& remote_endpoint
,
151 scoped_ptr
<base::DictionaryValue
> options
,
152 const ErrorCallback
& error_callback
) {
153 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
154 CastSessionDelegateBase::StartUDP(local_endpoint
,
158 event_subscribers_
.reset(
159 new media::cast::RawEventSubscriberBundle(cast_environment_
));
161 cast_sender_
= CastSender::Create(cast_environment_
, cast_transport_
.get());
164 void CastSessionDelegate::ToggleLogging(bool is_audio
, bool enable
) {
165 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
166 if (!event_subscribers_
.get())
170 event_subscribers_
->AddEventSubscribers(is_audio
);
172 event_subscribers_
->RemoveEventSubscribers(is_audio
);
175 void CastSessionDelegate::GetEventLogsAndReset(
177 const std::string
& extra_data
,
178 const EventLogsCallback
& callback
) {
179 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
181 if (!event_subscribers_
.get()) {
182 callback
.Run(make_scoped_ptr(new base::BinaryValue
).Pass());
186 media::cast::EncodingEventSubscriber
* subscriber
=
187 event_subscribers_
->GetEncodingEventSubscriber(is_audio
);
189 callback
.Run(make_scoped_ptr(new base::BinaryValue
).Pass());
193 media::cast::proto::LogMetadata metadata
;
194 media::cast::FrameEventList frame_events
;
195 media::cast::PacketEventList packet_events
;
197 subscriber
->GetEventsAndReset(&metadata
, &frame_events
, &packet_events
);
199 if (!extra_data
.empty())
200 metadata
.set_extra_data(extra_data
);
201 media::cast::proto::GeneralDescription
* gen_desc
=
202 metadata
.mutable_general_description();
203 chrome::VersionInfo version_info
;
204 gen_desc
->set_product(version_info
.Name());
205 gen_desc
->set_product_version(version_info
.Version());
206 gen_desc
->set_os(version_info
.OSType());
208 scoped_ptr
<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes
]);
210 bool success
= media::cast::SerializeEvents(metadata
,
214 media::cast::kMaxSerializedBytes
,
215 serialized_log
.get(),
219 DVLOG(2) << "Failed to serialize event log.";
220 callback
.Run(make_scoped_ptr(new base::BinaryValue
).Pass());
224 DVLOG(2) << "Serialized log length: " << output_bytes
;
226 scoped_ptr
<base::BinaryValue
> blob(
227 new base::BinaryValue(serialized_log
.Pass(), output_bytes
));
228 callback
.Run(blob
.Pass());
231 void CastSessionDelegate::GetStatsAndReset(bool is_audio
,
232 const StatsCallback
& callback
) {
233 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
235 if (!event_subscribers_
.get()) {
236 callback
.Run(make_scoped_ptr(new base::DictionaryValue
).Pass());
240 media::cast::StatsEventSubscriber
* subscriber
=
241 event_subscribers_
->GetStatsEventSubscriber(is_audio
);
243 callback
.Run(make_scoped_ptr(new base::DictionaryValue
).Pass());
247 scoped_ptr
<base::DictionaryValue
> stats
= subscriber
->GetStats();
250 callback
.Run(stats
.Pass());
253 void CastSessionDelegate::OnOperationalStatusChange(
255 const ErrorCallback
& error_callback
,
256 media::cast::OperationalStatus status
) {
257 DCHECK(cast_sender_
);
260 case media::cast::STATUS_UNINITIALIZED
:
261 case media::cast::STATUS_CODEC_REINIT_PENDING
:
263 // TODO(miu): As an optimization, signal the client to pause sending more
264 // frames until the state becomes STATUS_INITIALIZED again.
266 case media::cast::STATUS_INITIALIZED
:
267 // Once initialized, run the "frame input available" callback to allow the
268 // client to begin sending frames. If STATUS_INITIALIZED is encountered
269 // again, do nothing since this is only an indication that the codec has
270 // successfully re-initialized.
272 if (!audio_frame_input_available_callback_
.is_null()) {
273 base::ResetAndReturn(&audio_frame_input_available_callback_
).Run(
274 cast_sender_
->audio_frame_input());
277 if (!video_frame_input_available_callback_
.is_null()) {
278 base::ResetAndReturn(&video_frame_input_available_callback_
).Run(
279 cast_sender_
->video_frame_input());
283 case media::cast::STATUS_INVALID_CONFIGURATION
:
284 error_callback
.Run(base::StringPrintf("Invalid %s configuration.",
285 is_for_audio
? "audio" : "video"));
287 case media::cast::STATUS_UNSUPPORTED_CODEC
:
288 error_callback
.Run(base::StringPrintf("%s codec not supported.",
289 is_for_audio
? "Audio" : "Video"));
291 case media::cast::STATUS_CODEC_INIT_FAILED
:
292 error_callback
.Run(base::StringPrintf("%s codec initialization failed.",
293 is_for_audio
? "Audio" : "Video"));
295 case media::cast::STATUS_CODEC_RUNTIME_ERROR
:
296 error_callback
.Run(base::StringPrintf("%s codec runtime error.",
297 is_for_audio
? "Audio" : "Video"));
302 void CastSessionDelegate::ReceivePacket(
303 scoped_ptr
<media::cast::Packet
> packet
) {
304 // Do nothing (frees packet)
307 void CastSessionDelegate::LogRawEvents(
308 const std::vector
<media::cast::PacketEvent
>& packet_events
,
309 const std::vector
<media::cast::FrameEvent
>& frame_events
) {
310 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
312 for (std::vector
<media::cast::PacketEvent
>::const_iterator it
=
313 packet_events
.begin();
314 it
!= packet_events
.end();
316 cast_environment_
->Logging()->InsertPacketEvent(it
->timestamp
,
325 for (std::vector
<media::cast::FrameEvent
>::const_iterator it
=
326 frame_events
.begin();
327 it
!= frame_events
.end();
329 if (it
->type
== media::cast::FRAME_PLAYOUT
) {
330 cast_environment_
->Logging()->InsertFrameEventWithDelay(
338 cast_environment_
->Logging()->InsertFrameEvent(