Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / renderer / media / cast_session_delegate.cc
blob2b073867c1cb192265c23ae92edb54fe92d0a637
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()),
37 weak_factory_(this) {
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(
64 local_endpoint,
65 remote_endpoint,
66 options.Pass(),
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;
81 switch (status) {
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.");
89 break;
90 case media::cast::TRANSPORT_SOCKET_ERROR:
91 error_callback.Run("Socket error.");
92 break;
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.");
113 return;
116 audio_frame_input_available_callback_ = callback;
117 cast_sender_->InitializeAudio(
118 config,
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.");
134 return;
137 video_frame_input_available_callback_ = callback;
139 cast_sender_->InitializeVideo(
140 config,
141 base::Bind(&CastSessionDelegate::OnOperationalStatusChange,
142 weak_factory_.GetWeakPtr(), false, error_callback),
143 create_vea_cb,
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,
155 remote_endpoint,
156 options.Pass(),
157 error_callback);
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())
167 return;
169 if (enable)
170 event_subscribers_->AddEventSubscribers(is_audio);
171 else
172 event_subscribers_->RemoveEventSubscribers(is_audio);
175 void CastSessionDelegate::GetEventLogsAndReset(
176 bool is_audio,
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());
183 return;
186 media::cast::EncodingEventSubscriber* subscriber =
187 event_subscribers_->GetEncodingEventSubscriber(is_audio);
188 if (!subscriber) {
189 callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
190 return;
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]);
209 int output_bytes;
210 bool success = media::cast::SerializeEvents(metadata,
211 frame_events,
212 packet_events,
213 true,
214 media::cast::kMaxSerializedBytes,
215 serialized_log.get(),
216 &output_bytes);
218 if (!success) {
219 DVLOG(2) << "Failed to serialize event log.";
220 callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
221 return;
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());
237 return;
240 media::cast::StatsEventSubscriber* subscriber =
241 event_subscribers_->GetStatsEventSubscriber(is_audio);
242 if (!subscriber) {
243 callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
244 return;
247 scoped_ptr<base::DictionaryValue> stats = subscriber->GetStats();
248 subscriber->Reset();
250 callback.Run(stats.Pass());
253 void CastSessionDelegate::OnOperationalStatusChange(
254 bool is_for_audio,
255 const ErrorCallback& error_callback,
256 media::cast::OperationalStatus status) {
257 DCHECK(cast_sender_);
259 switch (status) {
260 case media::cast::STATUS_UNINITIALIZED:
261 case media::cast::STATUS_CODEC_REINIT_PENDING:
262 // Not an error.
263 // TODO(miu): As an optimization, signal the client to pause sending more
264 // frames until the state becomes STATUS_INITIALIZED again.
265 break;
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.
271 if (is_for_audio) {
272 if (!audio_frame_input_available_callback_.is_null()) {
273 base::ResetAndReturn(&audio_frame_input_available_callback_).Run(
274 cast_sender_->audio_frame_input());
276 } else {
277 if (!video_frame_input_available_callback_.is_null()) {
278 base::ResetAndReturn(&video_frame_input_available_callback_).Run(
279 cast_sender_->video_frame_input());
282 break;
283 case media::cast::STATUS_INVALID_CONFIGURATION:
284 error_callback.Run(base::StringPrintf("Invalid %s configuration.",
285 is_for_audio ? "audio" : "video"));
286 break;
287 case media::cast::STATUS_UNSUPPORTED_CODEC:
288 error_callback.Run(base::StringPrintf("%s codec not supported.",
289 is_for_audio ? "Audio" : "Video"));
290 break;
291 case media::cast::STATUS_CODEC_INIT_FAILED:
292 error_callback.Run(base::StringPrintf("%s codec initialization failed.",
293 is_for_audio ? "Audio" : "Video"));
294 break;
295 case media::cast::STATUS_CODEC_RUNTIME_ERROR:
296 error_callback.Run(base::StringPrintf("%s codec runtime error.",
297 is_for_audio ? "Audio" : "Video"));
298 break;
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();
315 ++it) {
316 cast_environment_->Logging()->InsertPacketEvent(it->timestamp,
317 it->type,
318 it->media_type,
319 it->rtp_timestamp,
320 it->frame_id,
321 it->packet_id,
322 it->max_packet_id,
323 it->size);
325 for (std::vector<media::cast::FrameEvent>::const_iterator it =
326 frame_events.begin();
327 it != frame_events.end();
328 ++it) {
329 if (it->type == media::cast::FRAME_PLAYOUT) {
330 cast_environment_->Logging()->InsertFrameEventWithDelay(
331 it->timestamp,
332 it->type,
333 it->media_type,
334 it->rtp_timestamp,
335 it->frame_id,
336 it->delay_delta);
337 } else {
338 cast_environment_->Logging()->InsertFrameEvent(
339 it->timestamp,
340 it->type,
341 it->media_type,
342 it->rtp_timestamp,
343 it->frame_id);