Unregister from GCM when the only GCM app is removed
[chromium-blink-merge.git] / media / cast / test / sender.cc
blob686efa8b3f049a0561394198c4890e6974258644
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 // Test application that simulates a cast sender - Data can be either generated
6 // or read from a file.
8 #include <queue>
10 #include "base/at_exit.h"
11 #include "base/base_paths.h"
12 #include "base/command_line.h"
13 #include "base/files/file_path.h"
14 #include "base/json/json_writer.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/path_service.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/threading/thread.h"
20 #include "base/time/default_tick_clock.h"
21 #include "base/values.h"
22 #include "media/base/media.h"
23 #include "media/base/video_frame.h"
24 #include "media/cast/cast_config.h"
25 #include "media/cast/cast_environment.h"
26 #include "media/cast/cast_sender.h"
27 #include "media/cast/logging/encoding_event_subscriber.h"
28 #include "media/cast/logging/log_serializer.h"
29 #include "media/cast/logging/logging_defines.h"
30 #include "media/cast/logging/proto/raw_events.pb.h"
31 #include "media/cast/logging/receiver_time_offset_estimator_impl.h"
32 #include "media/cast/logging/stats_event_subscriber.h"
33 #include "media/cast/net/cast_transport_defines.h"
34 #include "media/cast/net/cast_transport_sender.h"
35 #include "media/cast/net/udp_transport.h"
36 #include "media/cast/test/fake_media_source.h"
37 #include "media/cast/test/utility/default_config.h"
38 #include "media/cast/test/utility/input_builder.h"
40 namespace {
42 // The max allowed size of serialized log.
43 const int kMaxSerializedLogBytes = 10 * 1000 * 1000;
45 // Flags for this program:
47 // --address=xx.xx.xx.xx
48 // IP address of receiver.
50 // --port=xxxx
51 // Port number of receiver.
53 // --source-file=xxx.webm
54 // WebM file as source of video frames.
56 // --fps=xx
57 // Override framerate of the video stream.
59 // --vary-frame-sizes
60 // Randomly vary the video frame sizes at random points in time. Has no
61 // effect if --source-file is being used.
62 const char kSwitchAddress[] = "address";
63 const char kSwitchPort[] = "port";
64 const char kSwitchSourceFile[] = "source-file";
65 const char kSwitchFps[] = "fps";
66 const char kSwitchVaryFrameSizes[] = "vary-frame-sizes";
68 void UpdateCastTransportStatus(
69 media::cast::CastTransportStatus status) {
70 VLOG(1) << "Transport status: " << status;
73 void LogRawEvents(
74 const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
75 const std::vector<media::cast::PacketEvent>& packet_events,
76 const std::vector<media::cast::FrameEvent>& frame_events) {
77 VLOG(1) << "Got packet events from transport, size: " << packet_events.size();
78 for (std::vector<media::cast::PacketEvent>::const_iterator it =
79 packet_events.begin();
80 it != packet_events.end();
81 ++it) {
82 cast_environment->Logging()->InsertPacketEvent(it->timestamp,
83 it->type,
84 it->media_type,
85 it->rtp_timestamp,
86 it->frame_id,
87 it->packet_id,
88 it->max_packet_id,
89 it->size);
91 VLOG(1) << "Got frame events from transport, size: " << frame_events.size();
92 for (std::vector<media::cast::FrameEvent>::const_iterator it =
93 frame_events.begin();
94 it != frame_events.end();
95 ++it) {
96 cast_environment->Logging()->InsertFrameEvent(it->timestamp,
97 it->type,
98 it->media_type,
99 it->rtp_timestamp,
100 it->frame_id);
104 void QuitLoopOnInitializationResult(media::cast::OperationalStatus result) {
105 CHECK(result == media::cast::STATUS_INITIALIZED)
106 << "Cast sender uninitialized";
107 base::MessageLoop::current()->Quit();
110 net::IPEndPoint CreateUDPAddress(std::string ip_str, uint16 port) {
111 net::IPAddressNumber ip_number;
112 CHECK(net::ParseIPLiteralToNumber(ip_str, &ip_number));
113 return net::IPEndPoint(ip_number, port);
116 void DumpLoggingData(const media::cast::proto::LogMetadata& log_metadata,
117 const media::cast::FrameEventList& frame_events,
118 const media::cast::PacketEventList& packet_events,
119 base::ScopedFILE log_file) {
120 VLOG(0) << "Frame map size: " << frame_events.size();
121 VLOG(0) << "Packet map size: " << packet_events.size();
123 scoped_ptr<char[]> event_log(new char[kMaxSerializedLogBytes]);
124 int event_log_bytes;
125 if (!media::cast::SerializeEvents(log_metadata,
126 frame_events,
127 packet_events,
128 true,
129 kMaxSerializedLogBytes,
130 event_log.get(),
131 &event_log_bytes)) {
132 VLOG(0) << "Failed to serialize events.";
133 return;
136 VLOG(0) << "Events serialized length: " << event_log_bytes;
138 int ret = fwrite(event_log.get(), 1, event_log_bytes, log_file.get());
139 if (ret != event_log_bytes)
140 VLOG(0) << "Failed to write logs to file.";
143 void WriteLogsToFileAndDestroySubscribers(
144 const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
145 scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber,
146 scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber,
147 base::ScopedFILE video_log_file,
148 base::ScopedFILE audio_log_file) {
149 cast_environment->Logging()->RemoveRawEventSubscriber(
150 video_event_subscriber.get());
151 cast_environment->Logging()->RemoveRawEventSubscriber(
152 audio_event_subscriber.get());
154 VLOG(0) << "Dumping logging data for video stream.";
155 media::cast::proto::LogMetadata log_metadata;
156 media::cast::FrameEventList frame_events;
157 media::cast::PacketEventList packet_events;
158 video_event_subscriber->GetEventsAndReset(
159 &log_metadata, &frame_events, &packet_events);
161 DumpLoggingData(log_metadata,
162 frame_events,
163 packet_events,
164 video_log_file.Pass());
166 VLOG(0) << "Dumping logging data for audio stream.";
167 audio_event_subscriber->GetEventsAndReset(
168 &log_metadata, &frame_events, &packet_events);
170 DumpLoggingData(log_metadata,
171 frame_events,
172 packet_events,
173 audio_log_file.Pass());
176 void WriteStatsAndDestroySubscribers(
177 const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
178 scoped_ptr<media::cast::StatsEventSubscriber> video_event_subscriber,
179 scoped_ptr<media::cast::StatsEventSubscriber> audio_event_subscriber,
180 scoped_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl> estimator) {
181 cast_environment->Logging()->RemoveRawEventSubscriber(
182 video_event_subscriber.get());
183 cast_environment->Logging()->RemoveRawEventSubscriber(
184 audio_event_subscriber.get());
185 cast_environment->Logging()->RemoveRawEventSubscriber(estimator.get());
187 scoped_ptr<base::DictionaryValue> stats = video_event_subscriber->GetStats();
188 std::string json;
189 base::JSONWriter::WriteWithOptions(
190 stats.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
191 VLOG(0) << "Video stats: " << json;
193 stats = audio_event_subscriber->GetStats();
194 json.clear();
195 base::JSONWriter::WriteWithOptions(
196 stats.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
197 VLOG(0) << "Audio stats: " << json;
200 } // namespace
202 int main(int argc, char** argv) {
203 base::AtExitManager at_exit;
204 base::CommandLine::Init(argc, argv);
205 InitLogging(logging::LoggingSettings());
207 // Load the media module for FFmpeg decoding.
208 base::FilePath path;
209 PathService::Get(base::DIR_MODULE, &path);
210 if (!media::InitializeMediaLibrary(path)) {
211 LOG(ERROR) << "Could not initialize media library.";
212 return 1;
215 base::Thread test_thread("Cast sender test app thread");
216 base::Thread audio_thread("Cast audio encoder thread");
217 base::Thread video_thread("Cast video encoder thread");
218 test_thread.Start();
219 audio_thread.Start();
220 video_thread.Start();
222 base::MessageLoopForIO io_message_loop;
224 // Default parameters.
225 base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
226 std::string remote_ip_address = cmd->GetSwitchValueASCII(kSwitchAddress);
227 if (remote_ip_address.empty())
228 remote_ip_address = "127.0.0.1";
229 int remote_port = 0;
230 if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchPort), &remote_port) ||
231 remote_port < 0 || remote_port > 65535) {
232 remote_port = 2344;
234 LOG(INFO) << "Sending to " << remote_ip_address << ":" << remote_port
235 << ".";
237 media::cast::AudioSenderConfig audio_config =
238 media::cast::GetDefaultAudioSenderConfig();
239 media::cast::VideoSenderConfig video_config =
240 media::cast::GetDefaultVideoSenderConfig();
242 // Running transport on the main thread.
243 // Setting up transport config.
244 net::IPEndPoint remote_endpoint =
245 CreateUDPAddress(remote_ip_address, static_cast<uint16>(remote_port));
247 // Enable raw event and stats logging.
248 // Running transport on the main thread.
249 scoped_refptr<media::cast::CastEnvironment> cast_environment(
250 new media::cast::CastEnvironment(
251 make_scoped_ptr<base::TickClock>(new base::DefaultTickClock()),
252 io_message_loop.message_loop_proxy(),
253 audio_thread.message_loop_proxy(),
254 video_thread.message_loop_proxy()));
256 // SendProcess initialization.
257 scoped_ptr<media::cast::FakeMediaSource> fake_media_source(
258 new media::cast::FakeMediaSource(test_thread.message_loop_proxy(),
259 cast_environment->Clock(),
260 audio_config,
261 video_config,
262 false));
264 int override_fps = 0;
265 if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchFps),
266 &override_fps)){
267 override_fps = 0;
269 base::FilePath source_path = cmd->GetSwitchValuePath(kSwitchSourceFile);
270 if (!source_path.empty()) {
271 LOG(INFO) << "Source: " << source_path.value();
272 fake_media_source->SetSourceFile(source_path, override_fps);
274 if (cmd->HasSwitch(kSwitchVaryFrameSizes))
275 fake_media_source->SetVariableFrameSizeMode(true);
277 // CastTransportSender initialization.
278 scoped_ptr<media::cast::CastTransportSender> transport_sender =
279 media::cast::CastTransportSender::Create(
280 NULL, // net log.
281 cast_environment->Clock(),
282 net::IPEndPoint(),
283 remote_endpoint,
284 make_scoped_ptr(new base::DictionaryValue), // options
285 base::Bind(&UpdateCastTransportStatus),
286 base::Bind(&LogRawEvents, cast_environment),
287 base::TimeDelta::FromSeconds(1),
288 media::cast::PacketReceiverCallback(),
289 io_message_loop.message_loop_proxy());
291 // Set up event subscribers.
292 scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber;
293 scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber;
294 std::string video_log_file_name("/tmp/video_events.log.gz");
295 std::string audio_log_file_name("/tmp/audio_events.log.gz");
296 LOG(INFO) << "Logging audio events to: " << audio_log_file_name;
297 LOG(INFO) << "Logging video events to: " << video_log_file_name;
298 video_event_subscriber.reset(new media::cast::EncodingEventSubscriber(
299 media::cast::VIDEO_EVENT, 10000));
300 audio_event_subscriber.reset(new media::cast::EncodingEventSubscriber(
301 media::cast::AUDIO_EVENT, 10000));
302 cast_environment->Logging()->AddRawEventSubscriber(
303 video_event_subscriber.get());
304 cast_environment->Logging()->AddRawEventSubscriber(
305 audio_event_subscriber.get());
307 // Subscribers for stats.
308 scoped_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl> offset_estimator(
309 new media::cast::ReceiverTimeOffsetEstimatorImpl());
310 cast_environment->Logging()->AddRawEventSubscriber(offset_estimator.get());
311 scoped_ptr<media::cast::StatsEventSubscriber> video_stats_subscriber(
312 new media::cast::StatsEventSubscriber(media::cast::VIDEO_EVENT,
313 cast_environment->Clock(),
314 offset_estimator.get()));
315 scoped_ptr<media::cast::StatsEventSubscriber> audio_stats_subscriber(
316 new media::cast::StatsEventSubscriber(media::cast::AUDIO_EVENT,
317 cast_environment->Clock(),
318 offset_estimator.get()));
319 cast_environment->Logging()->AddRawEventSubscriber(
320 video_stats_subscriber.get());
321 cast_environment->Logging()->AddRawEventSubscriber(
322 audio_stats_subscriber.get());
324 base::ScopedFILE video_log_file(fopen(video_log_file_name.c_str(), "w"));
325 if (!video_log_file) {
326 VLOG(1) << "Failed to open video log file for writing.";
327 exit(-1);
330 base::ScopedFILE audio_log_file(fopen(audio_log_file_name.c_str(), "w"));
331 if (!audio_log_file) {
332 VLOG(1) << "Failed to open audio log file for writing.";
333 exit(-1);
336 const int logging_duration_seconds = 10;
337 io_message_loop.message_loop_proxy()->PostDelayedTask(
338 FROM_HERE,
339 base::Bind(&WriteLogsToFileAndDestroySubscribers,
340 cast_environment,
341 base::Passed(&video_event_subscriber),
342 base::Passed(&audio_event_subscriber),
343 base::Passed(&video_log_file),
344 base::Passed(&audio_log_file)),
345 base::TimeDelta::FromSeconds(logging_duration_seconds));
347 io_message_loop.message_loop_proxy()->PostDelayedTask(
348 FROM_HERE,
349 base::Bind(&WriteStatsAndDestroySubscribers,
350 cast_environment,
351 base::Passed(&video_stats_subscriber),
352 base::Passed(&audio_stats_subscriber),
353 base::Passed(&offset_estimator)),
354 base::TimeDelta::FromSeconds(logging_duration_seconds));
356 // CastSender initialization.
357 scoped_ptr<media::cast::CastSender> cast_sender =
358 media::cast::CastSender::Create(cast_environment, transport_sender.get());
359 io_message_loop.PostTask(
360 FROM_HERE,
361 base::Bind(&media::cast::CastSender::InitializeVideo,
362 base::Unretained(cast_sender.get()),
363 fake_media_source->get_video_config(),
364 base::Bind(&QuitLoopOnInitializationResult),
365 media::cast::CreateDefaultVideoEncodeAcceleratorCallback(),
366 media::cast::CreateDefaultVideoEncodeMemoryCallback()));
367 io_message_loop.Run(); // Wait for video initialization.
368 io_message_loop.PostTask(
369 FROM_HERE,
370 base::Bind(&media::cast::CastSender::InitializeAudio,
371 base::Unretained(cast_sender.get()),
372 audio_config,
373 base::Bind(&QuitLoopOnInitializationResult)));
374 io_message_loop.Run(); // Wait for audio initialization.
376 fake_media_source->Start(cast_sender->audio_frame_input(),
377 cast_sender->video_frame_input());
378 io_message_loop.Run();
379 return 0;