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/lazy_instance.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "chrome/renderer/media/cast_threads.h"
11 #include "chrome/renderer/media/cast_transport_sender_ipc.h"
12 #include "content/public/renderer/render_thread.h"
13 #include "media/cast/cast_config.h"
14 #include "media/cast/cast_environment.h"
15 #include "media/cast/cast_sender.h"
16 #include "media/cast/logging/log_serializer.h"
17 #include "media/cast/logging/logging_defines.h"
18 #include "media/cast/logging/raw_event_subscriber_bundle.h"
19 #include "media/cast/transport/cast_transport_config.h"
20 #include "media/cast/transport/cast_transport_sender.h"
22 using media::cast::AudioSenderConfig
;
23 using media::cast::CastEnvironment
;
24 using media::cast::CastSender
;
25 using media::cast::VideoSenderConfig
;
27 static base::LazyInstance
<CastThreads
> g_cast_threads
=
28 LAZY_INSTANCE_INITIALIZER
;
30 CastSessionDelegate::CastSessionDelegate()
31 : io_message_loop_proxy_(
32 content::RenderThread::Get()->GetIOMessageLoopProxy()),
34 DCHECK(io_message_loop_proxy_
);
37 CastSessionDelegate::~CastSessionDelegate() {
38 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
41 void CastSessionDelegate::StartAudio(
42 const AudioSenderConfig
& config
,
43 const AudioFrameInputAvailableCallback
& callback
,
44 const ErrorCallback
& error_callback
) {
45 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
47 if (!cast_transport_
|| !cast_sender_
) {
48 error_callback
.Run("Destination not set.");
52 audio_frame_input_available_callback_
= callback
;
53 cast_sender_
->InitializeAudio(
55 base::Bind(&CastSessionDelegate::InitializationResultCB
,
56 weak_factory_
.GetWeakPtr()));
59 void CastSessionDelegate::StartVideo(
60 const VideoSenderConfig
& config
,
61 const VideoFrameInputAvailableCallback
& callback
,
62 const ErrorCallback
& error_callback
,
63 const media::cast::CreateVideoEncodeAcceleratorCallback
& create_vea_cb
,
64 const media::cast::CreateVideoEncodeMemoryCallback
&
65 create_video_encode_mem_cb
) {
66 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
68 if (!cast_transport_
|| !cast_sender_
) {
69 error_callback
.Run("Destination not set.");
73 video_frame_input_available_callback_
= callback
;
75 cast_sender_
->InitializeVideo(
77 base::Bind(&CastSessionDelegate::InitializationResultCB
,
78 weak_factory_
.GetWeakPtr()),
80 create_video_encode_mem_cb
);
83 void CastSessionDelegate::StartUDP(const net::IPEndPoint
& remote_endpoint
) {
84 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
86 // CastSender uses the renderer's IO thread as the main thread. This reduces
87 // thread hopping for incoming video frames and outgoing network packets.
88 cast_environment_
= new CastEnvironment(
89 scoped_ptr
<base::TickClock
>(new base::DefaultTickClock()).Pass(),
90 base::MessageLoopProxy::current(),
91 g_cast_threads
.Get().GetAudioEncodeMessageLoopProxy(),
92 g_cast_threads
.Get().GetVideoEncodeMessageLoopProxy());
94 event_subscribers_
.reset(
95 new media::cast::RawEventSubscriberBundle(cast_environment_
));
97 // Rationale for using unretained: The callback cannot be called after the
98 // destruction of CastTransportSenderIPC, and they both share the same thread.
99 cast_transport_
.reset(new CastTransportSenderIPC(
101 base::Bind(&CastSessionDelegate::StatusNotificationCB
,
102 base::Unretained(this)),
103 base::Bind(&CastSessionDelegate::LogRawEvents
, base::Unretained(this))));
105 cast_sender_
= CastSender::Create(cast_environment_
, cast_transport_
.get());
106 cast_transport_
->SetPacketReceiver(cast_sender_
->packet_receiver());
109 void CastSessionDelegate::ToggleLogging(bool is_audio
, bool enable
) {
110 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
111 if (!event_subscribers_
.get())
115 event_subscribers_
->AddEventSubscribers(is_audio
);
117 event_subscribers_
->RemoveEventSubscribers(is_audio
);
120 void CastSessionDelegate::GetEventLogsAndReset(
122 const EventLogsCallback
& callback
) {
123 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
125 if (!event_subscribers_
.get()) {
126 callback
.Run(make_scoped_ptr(new base::BinaryValue
).Pass());
130 media::cast::EncodingEventSubscriber
* subscriber
=
131 event_subscribers_
->GetEncodingEventSubscriber(is_audio
);
133 callback
.Run(make_scoped_ptr(new base::BinaryValue
).Pass());
137 media::cast::proto::LogMetadata metadata
;
138 media::cast::FrameEventList frame_events
;
139 media::cast::PacketEventList packet_events
;
141 subscriber
->GetEventsAndReset(&metadata
, &frame_events
, &packet_events
);
143 scoped_ptr
<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes
]);
145 bool success
= media::cast::SerializeEvents(metadata
,
149 media::cast::kMaxSerializedBytes
,
150 serialized_log
.get(),
154 VLOG(2) << "Failed to serialize event log.";
155 callback
.Run(make_scoped_ptr(new base::BinaryValue
).Pass());
159 DVLOG(2) << "Serialized log length: " << output_bytes
;
161 scoped_ptr
<base::BinaryValue
> blob(
162 new base::BinaryValue(serialized_log
.Pass(), output_bytes
));
163 callback
.Run(blob
.Pass());
166 void CastSessionDelegate::GetStatsAndReset(bool is_audio
,
167 const StatsCallback
& callback
) {
168 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
170 if (!event_subscribers_
.get()) {
171 callback
.Run(make_scoped_ptr(new base::DictionaryValue
).Pass());
175 media::cast::StatsEventSubscriber
* subscriber
=
176 event_subscribers_
->GetStatsEventSubscriber(is_audio
);
178 callback
.Run(make_scoped_ptr(new base::DictionaryValue
).Pass());
182 scoped_ptr
<base::DictionaryValue
> stats
= subscriber
->GetStats();
185 callback
.Run(stats
.Pass());
188 void CastSessionDelegate::StatusNotificationCB(
189 media::cast::transport::CastTransportStatus unused_status
) {
190 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
191 // TODO(hubbe): Call javascript UDPTransport error function.
194 void CastSessionDelegate::InitializationResultCB(
195 media::cast::CastInitializationStatus result
) const {
196 DCHECK(cast_sender_
);
198 // TODO(pwestin): handle the error codes.
199 if (result
== media::cast::STATUS_AUDIO_INITIALIZED
) {
200 audio_frame_input_available_callback_
.Run(
201 cast_sender_
->audio_frame_input());
202 } else if (result
== media::cast::STATUS_VIDEO_INITIALIZED
) {
203 video_frame_input_available_callback_
.Run(
204 cast_sender_
->video_frame_input());
208 void CastSessionDelegate::LogRawEvents(
209 const std::vector
<media::cast::PacketEvent
>& packet_events
) {
210 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
212 for (std::vector
<media::cast::PacketEvent
>::const_iterator it
=
213 packet_events
.begin();
214 it
!= packet_events
.end();
216 cast_environment_
->Logging()->InsertPacketEvent(it
->timestamp
,