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.
4
5 #include "chrome/renderer/media/cast_session_delegate.h"
6
7 #include "base/lazy_instance.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "chrome/common/chrome_version_info.h"
11 #include "chrome/renderer/media/cast_threads.h"
12 #include "chrome/renderer/media/cast_transport_sender_ipc.h"
13 #include "content/public/renderer/render_thread.h"
14 #include "media/cast/cast_config.h"
15 #include "media/cast/cast_environment.h"
16 #include "media/cast/cast_sender.h"
17 #include "media/cast/logging/log_serializer.h"
18 #include "media/cast/logging/logging_defines.h"
19 #include "media/cast/logging/proto/raw_events.pb.h"
20 #include "media/cast/logging/raw_event_subscriber_bundle.h"
21 #include "media/cast/net/cast_transport_config.h"
22 #include "media/cast/net/cast_transport_sender.h"
23
24 using media::cast::AudioSenderConfig;
25 using media::cast::CastEnvironment;
26 using media::cast::CastSender;
27 using media::cast::VideoSenderConfig;
28
29 static base::LazyInstance<CastThreads> g_cast_threads =
30 LAZY_INSTANCE_INITIALIZER;
31
CastSessionDelegate()32 CastSessionDelegate::CastSessionDelegate()
33 : io_message_loop_proxy_(
34 content::RenderThread::Get()->GetIOMessageLoopProxy()),
35 weak_factory_(this) {
36 DCHECK(io_message_loop_proxy_.get());
37 }
38
~CastSessionDelegate()39 CastSessionDelegate::~CastSessionDelegate() {
40 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
41 }
42
StartAudio(const AudioSenderConfig & config,const AudioFrameInputAvailableCallback & callback,const ErrorCallback & error_callback)43 void CastSessionDelegate::StartAudio(
44 const AudioSenderConfig& config,
45 const AudioFrameInputAvailableCallback& callback,
46 const ErrorCallback& error_callback) {
47 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
48
49 if (!cast_transport_ || !cast_sender_) {
50 error_callback.Run("Destination not set.");
51 return;
52 }
53
54 audio_frame_input_available_callback_ = callback;
55 cast_sender_->InitializeAudio(
56 config,
57 base::Bind(&CastSessionDelegate::InitializationResultCB,
58 weak_factory_.GetWeakPtr(), error_callback));
59 }
60
StartVideo(const VideoSenderConfig & config,const VideoFrameInputAvailableCallback & callback,const ErrorCallback & error_callback,const media::cast::CreateVideoEncodeAcceleratorCallback & create_vea_cb,const media::cast::CreateVideoEncodeMemoryCallback & create_video_encode_mem_cb)61 void CastSessionDelegate::StartVideo(
62 const VideoSenderConfig& config,
63 const VideoFrameInputAvailableCallback& callback,
64 const ErrorCallback& error_callback,
65 const media::cast::CreateVideoEncodeAcceleratorCallback& create_vea_cb,
66 const media::cast::CreateVideoEncodeMemoryCallback&
67 create_video_encode_mem_cb) {
68 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
69
70 if (!cast_transport_ || !cast_sender_) {
71 error_callback.Run("Destination not set.");
72 return;
73 }
74
75 video_frame_input_available_callback_ = callback;
76
77 cast_sender_->InitializeVideo(
78 config,
79 base::Bind(&CastSessionDelegate::InitializationResultCB,
80 weak_factory_.GetWeakPtr(), error_callback),
81 create_vea_cb,
82 create_video_encode_mem_cb);
83 }
84
StartUDP(const net::IPEndPoint & remote_endpoint,scoped_ptr<base::DictionaryValue> options)85 void CastSessionDelegate::StartUDP(const net::IPEndPoint& remote_endpoint,
86 scoped_ptr<base::DictionaryValue> options) {
87 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
88
89 // CastSender uses the renderer's IO thread as the main thread. This reduces
90 // thread hopping for incoming video frames and outgoing network packets.
91 cast_environment_ = new CastEnvironment(
92 scoped_ptr<base::TickClock>(new base::DefaultTickClock()).Pass(),
93 base::MessageLoopProxy::current(),
94 g_cast_threads.Get().GetAudioEncodeMessageLoopProxy(),
95 g_cast_threads.Get().GetVideoEncodeMessageLoopProxy());
96
97 event_subscribers_.reset(
98 new media::cast::RawEventSubscriberBundle(cast_environment_));
99
100 // Rationale for using unretained: The callback cannot be called after the
101 // destruction of CastTransportSenderIPC, and they both share the same thread.
102 cast_transport_.reset(new CastTransportSenderIPC(
103 remote_endpoint,
104 options.Pass(),
105 base::Bind(&CastSessionDelegate::StatusNotificationCB,
106 base::Unretained(this)),
107 base::Bind(&CastSessionDelegate::LogRawEvents, base::Unretained(this))));
108
109 cast_sender_ = CastSender::Create(cast_environment_, cast_transport_.get());
110 }
111
ToggleLogging(bool is_audio,bool enable)112 void CastSessionDelegate::ToggleLogging(bool is_audio, bool enable) {
113 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
114 if (!event_subscribers_.get())
115 return;
116
117 if (enable)
118 event_subscribers_->AddEventSubscribers(is_audio);
119 else
120 event_subscribers_->RemoveEventSubscribers(is_audio);
121 }
122
GetEventLogsAndReset(bool is_audio,const std::string & extra_data,const EventLogsCallback & callback)123 void CastSessionDelegate::GetEventLogsAndReset(
124 bool is_audio,
125 const std::string& extra_data,
126 const EventLogsCallback& callback) {
127 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
128
129 if (!event_subscribers_.get()) {
130 callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
131 return;
132 }
133
134 media::cast::EncodingEventSubscriber* subscriber =
135 event_subscribers_->GetEncodingEventSubscriber(is_audio);
136 if (!subscriber) {
137 callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
138 return;
139 }
140
141 media::cast::proto::LogMetadata metadata;
142 media::cast::FrameEventList frame_events;
143 media::cast::PacketEventList packet_events;
144
145 subscriber->GetEventsAndReset(&metadata, &frame_events, &packet_events);
146
147 if (!extra_data.empty())
148 metadata.set_extra_data(extra_data);
149 media::cast::proto::GeneralDescription* gen_desc =
150 metadata.mutable_general_description();
151 chrome::VersionInfo version_info;
152 gen_desc->set_product(version_info.Name());
153 gen_desc->set_product_version(version_info.Version());
154 gen_desc->set_os(version_info.OSType());
155
156 scoped_ptr<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes]);
157 int output_bytes;
158 bool success = media::cast::SerializeEvents(metadata,
159 frame_events,
160 packet_events,
161 true,
162 media::cast::kMaxSerializedBytes,
163 serialized_log.get(),
164 &output_bytes);
165
166 if (!success) {
167 VLOG(2) << "Failed to serialize event log.";
168 callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
169 return;
170 }
171
172 DVLOG(2) << "Serialized log length: " << output_bytes;
173
174 scoped_ptr<base::BinaryValue> blob(
175 new base::BinaryValue(serialized_log.Pass(), output_bytes));
176 callback.Run(blob.Pass());
177 }
178
GetStatsAndReset(bool is_audio,const StatsCallback & callback)179 void CastSessionDelegate::GetStatsAndReset(bool is_audio,
180 const StatsCallback& callback) {
181 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
182
183 if (!event_subscribers_.get()) {
184 callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
185 return;
186 }
187
188 media::cast::StatsEventSubscriber* subscriber =
189 event_subscribers_->GetStatsEventSubscriber(is_audio);
190 if (!subscriber) {
191 callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
192 return;
193 }
194
195 scoped_ptr<base::DictionaryValue> stats = subscriber->GetStats();
196 subscriber->Reset();
197
198 callback.Run(stats.Pass());
199 }
200
StatusNotificationCB(media::cast::CastTransportStatus unused_status)201 void CastSessionDelegate::StatusNotificationCB(
202 media::cast::CastTransportStatus unused_status) {
203 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
204 // TODO(hubbe): Call javascript UDPTransport error function.
205 }
206
InitializationResultCB(const ErrorCallback & error_callback,media::cast::CastInitializationStatus result) const207 void CastSessionDelegate::InitializationResultCB(
208 const ErrorCallback& error_callback,
209 media::cast::CastInitializationStatus result) const {
210 DCHECK(cast_sender_);
211
212 switch (result) {
213 case media::cast::STATUS_AUDIO_INITIALIZED:
214 audio_frame_input_available_callback_.Run(
215 cast_sender_->audio_frame_input());
216 break;
217 case media::cast::STATUS_VIDEO_INITIALIZED:
218 video_frame_input_available_callback_.Run(
219 cast_sender_->video_frame_input());
220 break;
221 case media::cast::STATUS_INVALID_CAST_ENVIRONMENT:
222 error_callback.Run("Invalid cast environment.");
223 break;
224 case media::cast::STATUS_INVALID_CRYPTO_CONFIGURATION:
225 error_callback.Run("Invalid encryption keys.");
226 break;
227 case media::cast::STATUS_UNSUPPORTED_AUDIO_CODEC:
228 error_callback.Run("Audio codec not supported.");
229 break;
230 case media::cast::STATUS_UNSUPPORTED_VIDEO_CODEC:
231 error_callback.Run("Video codec not supported.");
232 break;
233 case media::cast::STATUS_INVALID_AUDIO_CONFIGURATION:
234 error_callback.Run("Invalid audio configuration.");
235 break;
236 case media::cast::STATUS_INVALID_VIDEO_CONFIGURATION:
237 error_callback.Run("Invalid video configuration.");
238 break;
239 case media::cast::STATUS_HW_VIDEO_ENCODER_NOT_SUPPORTED:
240 error_callback.Run("Hardware video encoder not supported.");
241 break;
242 case media::cast::STATUS_AUDIO_UNINITIALIZED:
243 case media::cast::STATUS_VIDEO_UNINITIALIZED:
244 NOTREACHED() << "Not an error.";
245 break;
246 }
247 }
248
LogRawEvents(const std::vector<media::cast::PacketEvent> & packet_events,const std::vector<media::cast::FrameEvent> & frame_events)249 void CastSessionDelegate::LogRawEvents(
250 const std::vector<media::cast::PacketEvent>& packet_events,
251 const std::vector<media::cast::FrameEvent>& frame_events) {
252 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
253
254 for (std::vector<media::cast::PacketEvent>::const_iterator it =
255 packet_events.begin();
256 it != packet_events.end();
257 ++it) {
258 cast_environment_->Logging()->InsertPacketEvent(it->timestamp,
259 it->type,
260 it->media_type,
261 it->rtp_timestamp,
262 it->frame_id,
263 it->packet_id,
264 it->max_packet_id,
265 it->size);
266 }
267 for (std::vector<media::cast::FrameEvent>::const_iterator it =
268 frame_events.begin();
269 it != frame_events.end();
270 ++it) {
271 if (it->type == media::cast::FRAME_PLAYOUT) {
272 cast_environment_->Logging()->InsertFrameEventWithDelay(
273 it->timestamp,
274 it->type,
275 it->media_type,
276 it->rtp_timestamp,
277 it->frame_id,
278 it->delay_delta);
279 } else {
280 cast_environment_->Logging()->InsertFrameEvent(
281 it->timestamp,
282 it->type,
283 it->media_type,
284 it->rtp_timestamp,
285 it->frame_id);
286 }
287 }
288 }
289