• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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