• 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/transport/cast_transport_config.h"
22 #include "media/cast/transport/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_);
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()));
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()),
81       create_vea_cb,
82       create_video_encode_mem_cb);
83 }
84 
StartUDP(const net::IPEndPoint & remote_endpoint)85 void CastSessionDelegate::StartUDP(const net::IPEndPoint& remote_endpoint) {
86   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
87 
88   // CastSender uses the renderer's IO thread as the main thread. This reduces
89   // thread hopping for incoming video frames and outgoing network packets.
90   cast_environment_ = new CastEnvironment(
91       scoped_ptr<base::TickClock>(new base::DefaultTickClock()).Pass(),
92       base::MessageLoopProxy::current(),
93       g_cast_threads.Get().GetAudioEncodeMessageLoopProxy(),
94       g_cast_threads.Get().GetVideoEncodeMessageLoopProxy());
95 
96   event_subscribers_.reset(
97       new media::cast::RawEventSubscriberBundle(cast_environment_));
98 
99   // Rationale for using unretained: The callback cannot be called after the
100   // destruction of CastTransportSenderIPC, and they both share the same thread.
101   cast_transport_.reset(new CastTransportSenderIPC(
102       remote_endpoint,
103       base::Bind(&CastSessionDelegate::StatusNotificationCB,
104                  base::Unretained(this)),
105       base::Bind(&CastSessionDelegate::LogRawEvents, base::Unretained(this))));
106 
107   cast_sender_ = CastSender::Create(cast_environment_, cast_transport_.get());
108   cast_transport_->SetPacketReceiver(cast_sender_->packet_receiver());
109 }
110 
ToggleLogging(bool is_audio,bool enable)111 void CastSessionDelegate::ToggleLogging(bool is_audio, bool enable) {
112   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
113   if (!event_subscribers_.get())
114     return;
115 
116   if (enable)
117     event_subscribers_->AddEventSubscribers(is_audio);
118   else
119     event_subscribers_->RemoveEventSubscribers(is_audio);
120 }
121 
GetEventLogsAndReset(bool is_audio,const std::string & extra_data,const EventLogsCallback & callback)122 void CastSessionDelegate::GetEventLogsAndReset(
123     bool is_audio,
124     const std::string& extra_data,
125     const EventLogsCallback& callback) {
126   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
127 
128   if (!event_subscribers_.get()) {
129     callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
130     return;
131   }
132 
133   media::cast::EncodingEventSubscriber* subscriber =
134       event_subscribers_->GetEncodingEventSubscriber(is_audio);
135   if (!subscriber) {
136     callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
137     return;
138   }
139 
140   media::cast::proto::LogMetadata metadata;
141   media::cast::FrameEventList frame_events;
142   media::cast::PacketEventList packet_events;
143 
144   subscriber->GetEventsAndReset(&metadata, &frame_events, &packet_events);
145 
146   if (!extra_data.empty())
147     metadata.set_extra_data(extra_data);
148   media::cast::proto::GeneralDescription* gen_desc =
149       metadata.mutable_general_description();
150   chrome::VersionInfo version_info;
151   gen_desc->set_product(version_info.Name());
152   gen_desc->set_product_version(version_info.Version());
153   gen_desc->set_os(version_info.OSType());
154 
155   scoped_ptr<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes]);
156   int output_bytes;
157   bool success = media::cast::SerializeEvents(metadata,
158                                               frame_events,
159                                               packet_events,
160                                               true,
161                                               media::cast::kMaxSerializedBytes,
162                                               serialized_log.get(),
163                                               &output_bytes);
164 
165   if (!success) {
166     VLOG(2) << "Failed to serialize event log.";
167     callback.Run(make_scoped_ptr(new base::BinaryValue).Pass());
168     return;
169   }
170 
171   DVLOG(2) << "Serialized log length: " << output_bytes;
172 
173   scoped_ptr<base::BinaryValue> blob(
174       new base::BinaryValue(serialized_log.Pass(), output_bytes));
175   callback.Run(blob.Pass());
176 }
177 
GetStatsAndReset(bool is_audio,const StatsCallback & callback)178 void CastSessionDelegate::GetStatsAndReset(bool is_audio,
179                                            const StatsCallback& callback) {
180   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
181 
182   if (!event_subscribers_.get()) {
183     callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
184     return;
185   }
186 
187   media::cast::StatsEventSubscriber* subscriber =
188       event_subscribers_->GetStatsEventSubscriber(is_audio);
189   if (!subscriber) {
190     callback.Run(make_scoped_ptr(new base::DictionaryValue).Pass());
191     return;
192   }
193 
194   scoped_ptr<base::DictionaryValue> stats = subscriber->GetStats();
195   subscriber->Reset();
196 
197   callback.Run(stats.Pass());
198 }
199 
StatusNotificationCB(media::cast::transport::CastTransportStatus unused_status)200 void CastSessionDelegate::StatusNotificationCB(
201     media::cast::transport::CastTransportStatus unused_status) {
202   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
203   // TODO(hubbe): Call javascript UDPTransport error function.
204 }
205 
InitializationResultCB(media::cast::CastInitializationStatus result) const206 void CastSessionDelegate::InitializationResultCB(
207     media::cast::CastInitializationStatus result) const {
208   DCHECK(cast_sender_);
209 
210   // TODO(pwestin): handle the error codes.
211   if (result == media::cast::STATUS_AUDIO_INITIALIZED) {
212     audio_frame_input_available_callback_.Run(
213         cast_sender_->audio_frame_input());
214   } else if (result == media::cast::STATUS_VIDEO_INITIALIZED) {
215     video_frame_input_available_callback_.Run(
216         cast_sender_->video_frame_input());
217   }
218 }
219 
LogRawEvents(const std::vector<media::cast::PacketEvent> & packet_events)220 void CastSessionDelegate::LogRawEvents(
221     const std::vector<media::cast::PacketEvent>& packet_events) {
222   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
223 
224   for (std::vector<media::cast::PacketEvent>::const_iterator it =
225            packet_events.begin();
226        it != packet_events.end();
227        ++it) {
228     cast_environment_->Logging()->InsertPacketEvent(it->timestamp,
229                                                     it->type,
230                                                     it->media_type,
231                                                     it->rtp_timestamp,
232                                                     it->frame_id,
233                                                     it->packet_id,
234                                                     it->max_packet_id,
235                                                     it->size);
236   }
237 }
238