Simplify web_view.js
[chromium-blink-merge.git] / media / cast / test / sender.cc
blob38aef308fe2faa30f430ba59fd2ef902bf445c37
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 {
41 static const int kAudioChannels = 2;
42 static const int kAudioSamplingFrequency = 48000;
44 // The max allowed size of serialized log.
45 const int kMaxSerializedLogBytes = 10 * 1000 * 1000;
47 // Flags for this program:
49 // --address=xx.xx.xx.xx
50 // IP address of receiver.
52 // --port=xxxx
53 // Port number of receiver.
55 // --source-file=xxx.webm
56 // WebM file as source of video frames.
58 // --fps=xx
59 // Override framerate of the video stream.
60 const char kSwitchAddress[] = "address";
61 const char kSwitchPort[] = "port";
62 const char kSwitchSourceFile[] = "source-file";
63 const char kSwitchFps[] = "fps";
65 media::cast::AudioSenderConfig GetAudioSenderConfig() {
66 media::cast::AudioSenderConfig audio_config;
68 audio_config.use_external_encoder = false;
69 audio_config.frequency = kAudioSamplingFrequency;
70 audio_config.channels = kAudioChannels;
71 audio_config.bitrate = 0; // Use Opus auto-VBR mode.
72 audio_config.codec = media::cast::CODEC_AUDIO_OPUS;
73 audio_config.ssrc = 1;
74 audio_config.incoming_feedback_ssrc = 2;
75 audio_config.rtp_payload_type = 127;
76 // TODO(miu): The default in cast_defines.h is 100. Should this be 100, and
77 // should receiver.cc's config also be 100?
78 audio_config.max_playout_delay = base::TimeDelta::FromMilliseconds(300);
79 return audio_config;
82 media::cast::VideoSenderConfig GetVideoSenderConfig() {
83 media::cast::VideoSenderConfig video_config;
85 video_config.use_external_encoder = false;
87 // Resolution.
88 video_config.width = 1280;
89 video_config.height = 720;
90 video_config.max_frame_rate = 30;
92 // Bitrates.
93 video_config.max_bitrate = 2500000;
94 video_config.min_bitrate = 100000;
95 video_config.start_bitrate = video_config.min_bitrate;
97 // Codec.
98 video_config.codec = media::cast::CODEC_VIDEO_VP8;
99 video_config.max_number_of_video_buffers_used = 1;
100 video_config.number_of_encode_threads = 2;
102 // Quality options.
103 video_config.min_qp = 4;
104 video_config.max_qp = 40;
106 // SSRCs and payload type. Don't change them.
107 video_config.ssrc = 11;
108 video_config.incoming_feedback_ssrc = 12;
109 video_config.rtp_payload_type = 96;
110 // TODO(miu): The default in cast_defines.h is 100. Should this be 100, and
111 // should receiver.cc's config also be 100?
112 video_config.max_playout_delay = base::TimeDelta::FromMilliseconds(300);
113 return video_config;
116 void UpdateCastTransportStatus(
117 media::cast::CastTransportStatus status) {
118 VLOG(1) << "Transport status: " << status;
121 void LogRawEvents(
122 const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
123 const std::vector<media::cast::PacketEvent>& packet_events,
124 const std::vector<media::cast::FrameEvent>& frame_events) {
125 VLOG(1) << "Got packet events from transport, size: " << packet_events.size();
126 for (std::vector<media::cast::PacketEvent>::const_iterator it =
127 packet_events.begin();
128 it != packet_events.end();
129 ++it) {
130 cast_environment->Logging()->InsertPacketEvent(it->timestamp,
131 it->type,
132 it->media_type,
133 it->rtp_timestamp,
134 it->frame_id,
135 it->packet_id,
136 it->max_packet_id,
137 it->size);
139 VLOG(1) << "Got frame events from transport, size: " << frame_events.size();
140 for (std::vector<media::cast::FrameEvent>::const_iterator it =
141 frame_events.begin();
142 it != frame_events.end();
143 ++it) {
144 cast_environment->Logging()->InsertFrameEvent(it->timestamp,
145 it->type,
146 it->media_type,
147 it->rtp_timestamp,
148 it->frame_id);
152 void InitializationResult(media::cast::CastInitializationStatus result) {
153 bool end_result = result == media::cast::STATUS_AUDIO_INITIALIZED ||
154 result == media::cast::STATUS_VIDEO_INITIALIZED;
155 CHECK(end_result) << "Cast sender uninitialized";
158 net::IPEndPoint CreateUDPAddress(std::string ip_str, int port) {
159 net::IPAddressNumber ip_number;
160 CHECK(net::ParseIPLiteralToNumber(ip_str, &ip_number));
161 return net::IPEndPoint(ip_number, port);
164 void DumpLoggingData(const media::cast::proto::LogMetadata& log_metadata,
165 const media::cast::FrameEventList& frame_events,
166 const media::cast::PacketEventList& packet_events,
167 base::ScopedFILE log_file) {
168 VLOG(0) << "Frame map size: " << frame_events.size();
169 VLOG(0) << "Packet map size: " << packet_events.size();
171 scoped_ptr<char[]> event_log(new char[kMaxSerializedLogBytes]);
172 int event_log_bytes;
173 if (!media::cast::SerializeEvents(log_metadata,
174 frame_events,
175 packet_events,
176 true,
177 kMaxSerializedLogBytes,
178 event_log.get(),
179 &event_log_bytes)) {
180 VLOG(0) << "Failed to serialize events.";
181 return;
184 VLOG(0) << "Events serialized length: " << event_log_bytes;
186 int ret = fwrite(event_log.get(), 1, event_log_bytes, log_file.get());
187 if (ret != event_log_bytes)
188 VLOG(0) << "Failed to write logs to file.";
191 void WriteLogsToFileAndDestroySubscribers(
192 const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
193 scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber,
194 scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber,
195 base::ScopedFILE video_log_file,
196 base::ScopedFILE audio_log_file) {
197 cast_environment->Logging()->RemoveRawEventSubscriber(
198 video_event_subscriber.get());
199 cast_environment->Logging()->RemoveRawEventSubscriber(
200 audio_event_subscriber.get());
202 VLOG(0) << "Dumping logging data for video stream.";
203 media::cast::proto::LogMetadata log_metadata;
204 media::cast::FrameEventList frame_events;
205 media::cast::PacketEventList packet_events;
206 video_event_subscriber->GetEventsAndReset(
207 &log_metadata, &frame_events, &packet_events);
209 DumpLoggingData(log_metadata,
210 frame_events,
211 packet_events,
212 video_log_file.Pass());
214 VLOG(0) << "Dumping logging data for audio stream.";
215 audio_event_subscriber->GetEventsAndReset(
216 &log_metadata, &frame_events, &packet_events);
218 DumpLoggingData(log_metadata,
219 frame_events,
220 packet_events,
221 audio_log_file.Pass());
224 void WriteStatsAndDestroySubscribers(
225 const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
226 scoped_ptr<media::cast::StatsEventSubscriber> video_event_subscriber,
227 scoped_ptr<media::cast::StatsEventSubscriber> audio_event_subscriber,
228 scoped_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl> estimator) {
229 cast_environment->Logging()->RemoveRawEventSubscriber(
230 video_event_subscriber.get());
231 cast_environment->Logging()->RemoveRawEventSubscriber(
232 audio_event_subscriber.get());
233 cast_environment->Logging()->RemoveRawEventSubscriber(estimator.get());
235 scoped_ptr<base::DictionaryValue> stats = video_event_subscriber->GetStats();
236 std::string json;
237 base::JSONWriter::WriteWithOptions(
238 stats.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
239 VLOG(0) << "Video stats: " << json;
241 stats = audio_event_subscriber->GetStats();
242 json.clear();
243 base::JSONWriter::WriteWithOptions(
244 stats.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
245 VLOG(0) << "Audio stats: " << json;
248 } // namespace
250 int main(int argc, char** argv) {
251 base::AtExitManager at_exit;
252 CommandLine::Init(argc, argv);
253 InitLogging(logging::LoggingSettings());
255 // Load the media module for FFmpeg decoding.
256 base::FilePath path;
257 PathService::Get(base::DIR_MODULE, &path);
258 if (!media::InitializeMediaLibrary(path)) {
259 LOG(ERROR) << "Could not initialize media library.";
260 return 1;
263 base::Thread test_thread("Cast sender test app thread");
264 base::Thread audio_thread("Cast audio encoder thread");
265 base::Thread video_thread("Cast video encoder thread");
266 test_thread.Start();
267 audio_thread.Start();
268 video_thread.Start();
270 base::MessageLoopForIO io_message_loop;
272 // Default parameters.
273 CommandLine* cmd = CommandLine::ForCurrentProcess();
274 std::string remote_ip_address = cmd->GetSwitchValueASCII(kSwitchAddress);
275 if (remote_ip_address.empty())
276 remote_ip_address = "127.0.0.1";
277 int remote_port = 0;
278 if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchPort),
279 &remote_port)) {
280 remote_port = 2344;
282 LOG(INFO) << "Sending to " << remote_ip_address << ":" << remote_port
283 << ".";
285 media::cast::AudioSenderConfig audio_config = GetAudioSenderConfig();
286 media::cast::VideoSenderConfig video_config = GetVideoSenderConfig();
288 // Running transport on the main thread.
289 // Setting up transport config.
290 net::IPEndPoint remote_endpoint =
291 CreateUDPAddress(remote_ip_address, remote_port);
293 // Enable raw event and stats logging.
294 // Running transport on the main thread.
295 scoped_refptr<media::cast::CastEnvironment> cast_environment(
296 new media::cast::CastEnvironment(
297 make_scoped_ptr<base::TickClock>(new base::DefaultTickClock()),
298 io_message_loop.message_loop_proxy(),
299 audio_thread.message_loop_proxy(),
300 video_thread.message_loop_proxy()));
302 // SendProcess initialization.
303 scoped_ptr<media::cast::FakeMediaSource> fake_media_source(
304 new media::cast::FakeMediaSource(test_thread.message_loop_proxy(),
305 cast_environment->Clock(),
306 video_config));
308 int override_fps = 0;
309 if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchFps),
310 &override_fps)){
311 override_fps = 0;
313 base::FilePath source_path = cmd->GetSwitchValuePath(kSwitchSourceFile);
314 if (!source_path.empty()) {
315 LOG(INFO) << "Source: " << source_path.value();
316 fake_media_source->SetSourceFile(source_path, override_fps);
319 // CastTransportSender initialization.
320 scoped_ptr<media::cast::CastTransportSender> transport_sender =
321 media::cast::CastTransportSender::Create(
322 NULL, // net log.
323 cast_environment->Clock(),
324 remote_endpoint,
325 make_scoped_ptr(new base::DictionaryValue), // options
326 base::Bind(&UpdateCastTransportStatus),
327 base::Bind(&LogRawEvents, cast_environment),
328 base::TimeDelta::FromSeconds(1),
329 io_message_loop.message_loop_proxy());
331 // CastSender initialization.
332 scoped_ptr<media::cast::CastSender> cast_sender =
333 media::cast::CastSender::Create(cast_environment, transport_sender.get());
334 cast_sender->InitializeVideo(
335 fake_media_source->get_video_config(),
336 base::Bind(&InitializationResult),
337 media::cast::CreateDefaultVideoEncodeAcceleratorCallback(),
338 media::cast::CreateDefaultVideoEncodeMemoryCallback());
339 cast_sender->InitializeAudio(audio_config, base::Bind(&InitializationResult));
341 // Set up event subscribers.
342 scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber;
343 scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber;
344 std::string video_log_file_name("/tmp/video_events.log.gz");
345 std::string audio_log_file_name("/tmp/audio_events.log.gz");
346 LOG(INFO) << "Logging audio events to: " << audio_log_file_name;
347 LOG(INFO) << "Logging video events to: " << video_log_file_name;
348 video_event_subscriber.reset(new media::cast::EncodingEventSubscriber(
349 media::cast::VIDEO_EVENT, 10000));
350 audio_event_subscriber.reset(new media::cast::EncodingEventSubscriber(
351 media::cast::AUDIO_EVENT, 10000));
352 cast_environment->Logging()->AddRawEventSubscriber(
353 video_event_subscriber.get());
354 cast_environment->Logging()->AddRawEventSubscriber(
355 audio_event_subscriber.get());
357 // Subscribers for stats.
358 scoped_ptr<media::cast::ReceiverTimeOffsetEstimatorImpl> offset_estimator(
359 new media::cast::ReceiverTimeOffsetEstimatorImpl());
360 cast_environment->Logging()->AddRawEventSubscriber(offset_estimator.get());
361 scoped_ptr<media::cast::StatsEventSubscriber> video_stats_subscriber(
362 new media::cast::StatsEventSubscriber(media::cast::VIDEO_EVENT,
363 cast_environment->Clock(),
364 offset_estimator.get()));
365 scoped_ptr<media::cast::StatsEventSubscriber> audio_stats_subscriber(
366 new media::cast::StatsEventSubscriber(media::cast::AUDIO_EVENT,
367 cast_environment->Clock(),
368 offset_estimator.get()));
369 cast_environment->Logging()->AddRawEventSubscriber(
370 video_stats_subscriber.get());
371 cast_environment->Logging()->AddRawEventSubscriber(
372 audio_stats_subscriber.get());
374 base::ScopedFILE video_log_file(fopen(video_log_file_name.c_str(), "w"));
375 if (!video_log_file) {
376 VLOG(1) << "Failed to open video log file for writing.";
377 exit(-1);
380 base::ScopedFILE audio_log_file(fopen(audio_log_file_name.c_str(), "w"));
381 if (!audio_log_file) {
382 VLOG(1) << "Failed to open audio log file for writing.";
383 exit(-1);
386 const int logging_duration_seconds = 10;
387 io_message_loop.message_loop_proxy()->PostDelayedTask(
388 FROM_HERE,
389 base::Bind(&WriteLogsToFileAndDestroySubscribers,
390 cast_environment,
391 base::Passed(&video_event_subscriber),
392 base::Passed(&audio_event_subscriber),
393 base::Passed(&video_log_file),
394 base::Passed(&audio_log_file)),
395 base::TimeDelta::FromSeconds(logging_duration_seconds));
397 io_message_loop.message_loop_proxy()->PostDelayedTask(
398 FROM_HERE,
399 base::Bind(&WriteStatsAndDestroySubscribers,
400 cast_environment,
401 base::Passed(&video_stats_subscriber),
402 base::Passed(&audio_stats_subscriber),
403 base::Passed(&offset_estimator)),
404 base::TimeDelta::FromSeconds(logging_duration_seconds));
406 fake_media_source->Start(cast_sender->audio_frame_input(),
407 cast_sender->video_frame_input());
409 io_message_loop.Run();
410 return 0;