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/proto/raw_events.pb.h"
19 #include "media/cast/logging/raw_event_subscriber_bundle.h"
20 #include "media/cast/transport/cast_transport_config.h"
21 #include "media/cast/transport/cast_transport_sender.h"
23 using media::cast::AudioSenderConfig;
24 using media::cast::CastEnvironment;
25 using media::cast::CastSender;
26 using media::cast::VideoSenderConfig;
28 static base::LazyInstance<CastThreads> g_cast_threads =
29 LAZY_INSTANCE_INITIALIZER;
31 CastSessionDelegate::CastSessionDelegate()
32 : io_message_loop_proxy_(
33 content::RenderThread::Get()->GetIOMessageLoopProxy()),
35 DCHECK(io_message_loop_proxy_);
38 CastSessionDelegate::~CastSessionDelegate() {
39 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
42 void CastSessionDelegate::StartAudio(
43 const AudioSenderConfig& config,
44 const AudioFrameInputAvailableCallback& callback,
45 const ErrorCallback& error_callback) {
46 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
48 if (!cast_transport_ || !cast_sender_) {
49 error_callback.Run("Destination not set.");
53 audio_frame_input_available_callback_ = callback;
54 cast_sender_->InitializeAudio(
56 base::Bind(&CastSessionDelegate::InitializationResultCB,
57 weak_factory_.GetWeakPtr()));
60 void CastSessionDelegate::StartVideo(
61 const VideoSenderConfig& config,
62 const VideoFrameInputAvailableCallback& callback,
63 const ErrorCallback& error_callback,
64 const media::cast::CreateVideoEncodeAcceleratorCallback& create_vea_cb,
65 const media::cast::CreateVideoEncodeMemoryCallback&
66 create_video_encode_mem_cb) {
67 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
69 if (!cast_transport_ || !cast_sender_) {
70 error_callback.Run("Destination not set.");
74 video_frame_input_available_callback_ = callback;
76 cast_sender_->InitializeVideo(
78 base::Bind(&CastSessionDelegate::InitializationResultCB,
79 weak_factory_.GetWeakPtr()),
81 create_video_encode_mem_cb);
84 void CastSessionDelegate::StartUDP(const net::IPEndPoint& remote_endpoint) {
85 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
87 // CastSender uses the renderer's IO thread as the main thread. This reduces
88 // thread hopping for incoming video frames and outgoing network packets.
89 cast_environment_ = new CastEnvironment(
90 scoped_ptr<base::TickClock>(new base::DefaultTickClock()).Pass(),
91 base::MessageLoopProxy::current(),
92 g_cast_threads.Get().GetAudioEncodeMessageLoopProxy(),
93 g_cast_threads.Get().GetVideoEncodeMessageLoopProxy());
95 event_subscribers_.reset(
96 new media::cast::RawEventSubscriberBundle(cast_environment_));
98 // Rationale for using unretained: The callback cannot be called after the
99 // destruction of CastTransportSenderIPC, and they both share the same thread.
100 cast_transport_.reset(new CastTransportSenderIPC(
102 base::Bind(&CastSessionDelegate::StatusNotificationCB,
103 base::Unretained(this)),
104 base::Bind(&CastSessionDelegate::LogRawEvents, base::Unretained(this))));
106 cast_sender_ = CastSender::Create(cast_environment_, cast_transport_.get());
107 cast_transport_->SetPacketReceiver(cast_sender_->packet_receiver());
110 void CastSessionDelegate::ToggleLogging(bool is_audio, bool enable) {
111 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
112 if (!event_subscribers_.get())
116 event_subscribers_->AddEventSubscribers(is_audio);
118 event_subscribers_->RemoveEventSubscribers(is_audio);
121 void CastSessionDelegate::GetEventLogsAndReset(
123 const EventLogsCallback& callback) {
124 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
126 if (!event_subscribers_.get()) {
127 callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
131 media::cast::EncodingEventSubscriber* subscriber =
132 event_subscribers_->GetEncodingEventSubscriber(is_audio);
134 callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
138 media::cast::proto::LogMetadata metadata;
139 media::cast::FrameEventList frame_events;
140 media::cast::PacketEventList packet_events;
142 subscriber->GetEventsAndReset(&metadata, &frame_events, &packet_events);
144 scoped_ptr<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes]);
146 bool success = media::cast::SerializeEvents(metadata,
150 media::cast::kMaxSerializedBytes,
151 serialized_log.get(),
155 VLOG(2) << "Failed to serialize event log.";
156 callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
160 DVLOG(2) << "Serialized log length: " << output_bytes;
162 scoped_ptr<base::BinaryValue> blob(
163 new base::BinaryValue(serialized_log.Pass(), output_bytes));
164 callback.Run(blob.Pass());
167 void CastSessionDelegate::GetStatsAndReset(bool is_audio,
168 const StatsCallback& callback) {
169 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
171 if (!event_subscribers_.get()) {
172 callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
176 media::cast::StatsEventSubscriber* subscriber =
177 event_subscribers_->GetStatsEventSubscriber(is_audio);
179 callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
183 scoped_ptr<base::DictionaryValue> stats = subscriber->GetStats();
186 callback.Run(stats.Pass());
189 void CastSessionDelegate::StatusNotificationCB(
190 media::cast::transport::CastTransportStatus unused_status) {
191 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
192 // TODO(hubbe): Call javascript UDPTransport error function.
195 void CastSessionDelegate::InitializationResultCB(
196 media::cast::CastInitializationStatus result) const {
197 DCHECK(cast_sender_);
199 // TODO(pwestin): handle the error codes.
200 if (result == media::cast::STATUS_AUDIO_INITIALIZED) {
201 audio_frame_input_available_callback_.Run(
202 cast_sender_->audio_frame_input());
203 } else if (result == media::cast::STATUS_VIDEO_INITIALIZED) {
204 video_frame_input_available_callback_.Run(
205 cast_sender_->video_frame_input());
209 void CastSessionDelegate::LogRawEvents(
210 const std::vector<media::cast::PacketEvent>& packet_events) {
211 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
213 for (std::vector<media::cast::PacketEvent>::const_iterator it =
214 packet_events.begin();
215 it != packet_events.end();
217 cast_environment_->Logging()->InsertPacketEvent(it->timestamp,