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