Make |track_| in MediaStreamTrack const. and a couple of other cosmetic changes.
[chromium-blink-merge.git] / media / cast / test / simulator.cc
blob61084386c35d12ad3139b0807f949e02eb7ced26
1 // Copyright 2014 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 // Simulate end to end streaming.
6 //
7 // Input:
8 // --source=
9 // WebM used as the source of video and audio frames.
10 // --output=
11 // File path to writing out the raw event log of the simulation session.
12 // --sim-id=
13 // Unique simulation ID.
14 // --target-delay-ms=
15 // Target playout delay to configure (integer number of milliseconds).
16 // Optional; default is 400.
17 // --max-frame-rate=
18 // The maximum frame rate allowed at any time during the Cast session.
19 // Optional; default is 30.
20 // --source-frame-rate=
21 // Overrides the playback rate; the source video will play faster/slower.
22 // --run-time=
23 // In seconds, how long the Cast session runs for.
24 // Optional; default is 180.
26 // Output:
27 // - Raw event log of the simulation session tagged with the unique test ID,
28 // written out to the specified file path.
30 #include "base/at_exit.h"
31 #include "base/base_paths.h"
32 #include "base/command_line.h"
33 #include "base/files/file_path.h"
34 #include "base/files/file_util.h"
35 #include "base/files/memory_mapped_file.h"
36 #include "base/files/scoped_file.h"
37 #include "base/json/json_writer.h"
38 #include "base/logging.h"
39 #include "base/path_service.h"
40 #include "base/strings/string_number_conversions.h"
41 #include "base/test/simple_test_tick_clock.h"
42 #include "base/thread_task_runner_handle.h"
43 #include "base/time/tick_clock.h"
44 #include "base/values.h"
45 #include "media/base/audio_bus.h"
46 #include "media/base/media.h"
47 #include "media/base/video_frame.h"
48 #include "media/cast/cast_config.h"
49 #include "media/cast/cast_environment.h"
50 #include "media/cast/cast_receiver.h"
51 #include "media/cast/cast_sender.h"
52 #include "media/cast/logging/encoding_event_subscriber.h"
53 #include "media/cast/logging/log_serializer.h"
54 #include "media/cast/logging/logging_defines.h"
55 #include "media/cast/logging/proto/raw_events.pb.h"
56 #include "media/cast/logging/raw_event_subscriber_bundle.h"
57 #include "media/cast/logging/simple_event_subscriber.h"
58 #include "media/cast/net/cast_transport_config.h"
59 #include "media/cast/net/cast_transport_defines.h"
60 #include "media/cast/net/cast_transport_sender.h"
61 #include "media/cast/net/cast_transport_sender_impl.h"
62 #include "media/cast/test/fake_media_source.h"
63 #include "media/cast/test/fake_single_thread_task_runner.h"
64 #include "media/cast/test/loopback_transport.h"
65 #include "media/cast/test/proto/network_simulation_model.pb.h"
66 #include "media/cast/test/skewed_tick_clock.h"
67 #include "media/cast/test/utility/audio_utility.h"
68 #include "media/cast/test/utility/default_config.h"
69 #include "media/cast/test/utility/test_util.h"
70 #include "media/cast/test/utility/udp_proxy.h"
71 #include "media/cast/test/utility/video_utility.h"
73 using media::cast::proto::IPPModel;
74 using media::cast::proto::NetworkSimulationModel;
75 using media::cast::proto::NetworkSimulationModelType;
77 namespace media {
78 namespace cast {
79 namespace {
80 const char kSourcePath[] = "source";
81 const char kModelPath[] = "model";
82 const char kOutputPath[] = "output";
83 const char kSimulationId[] = "sim-id";
84 const char kLibDir[] = "lib-dir";
85 const char kTargetDelay[] = "target-delay-ms";
86 const char kMaxFrameRate[] = "max-frame-rate";
87 const char kSourceFrameRate[] = "source-frame-rate";
88 const char kRunTime[] = "run-time";
90 int GetIntegerSwitchValue(const char* switch_name, int default_value) {
91 const std::string as_str =
92 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switch_name);
93 if (as_str.empty())
94 return default_value;
95 int as_int;
96 CHECK(base::StringToInt(as_str, &as_int));
97 CHECK_GT(as_int, 0);
98 return as_int;
101 void UpdateCastTransportStatus(CastTransportStatus status) {
102 LOG(INFO) << "Cast transport status: " << status;
105 void AudioInitializationStatus(CastInitializationStatus status) {
106 LOG(INFO) << "Audio status: " << status;
109 void VideoInitializationStatus(CastInitializationStatus status) {
110 LOG(INFO) << "Video status: " << status;
113 void LogTransportEvents(const scoped_refptr<CastEnvironment>& env,
114 const std::vector<PacketEvent>& packet_events,
115 const std::vector<FrameEvent>& frame_events) {
116 for (std::vector<media::cast::PacketEvent>::const_iterator it =
117 packet_events.begin();
118 it != packet_events.end();
119 ++it) {
120 env->Logging()->InsertPacketEvent(it->timestamp,
121 it->type,
122 it->media_type,
123 it->rtp_timestamp,
124 it->frame_id,
125 it->packet_id,
126 it->max_packet_id,
127 it->size);
129 for (std::vector<media::cast::FrameEvent>::const_iterator it =
130 frame_events.begin();
131 it != frame_events.end();
132 ++it) {
133 if (it->type == FRAME_PLAYOUT) {
134 env->Logging()->InsertFrameEventWithDelay(
135 it->timestamp,
136 it->type,
137 it->media_type,
138 it->rtp_timestamp,
139 it->frame_id,
140 it->delay_delta);
141 } else {
142 env->Logging()->InsertFrameEvent(
143 it->timestamp,
144 it->type,
145 it->media_type,
146 it->rtp_timestamp,
147 it->frame_id);
152 void GotVideoFrame(
153 int* counter,
154 CastReceiver* cast_receiver,
155 const scoped_refptr<media::VideoFrame>& video_frame,
156 const base::TimeTicks& render_time,
157 bool continuous) {
158 ++*counter;
159 cast_receiver->RequestDecodedVideoFrame(
160 base::Bind(&GotVideoFrame, counter, cast_receiver));
163 void GotAudioFrame(
164 int* counter,
165 CastReceiver* cast_receiver,
166 scoped_ptr<AudioBus> audio_bus,
167 const base::TimeTicks& playout_time,
168 bool is_continuous) {
169 ++*counter;
170 cast_receiver->RequestDecodedAudioFrame(
171 base::Bind(&GotAudioFrame, counter, cast_receiver));
174 // Serialize |frame_events| and |packet_events| and append to the file
175 // located at |output_path|.
176 void AppendLogToFile(media::cast::proto::LogMetadata* metadata,
177 const media::cast::FrameEventList& frame_events,
178 const media::cast::PacketEventList& packet_events,
179 const base::FilePath& output_path) {
180 media::cast::proto::GeneralDescription* gen_desc =
181 metadata->mutable_general_description();
182 gen_desc->set_product("Cast Simulator");
183 gen_desc->set_product_version("0.1");
185 scoped_ptr<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes]);
186 int output_bytes;
187 bool success = media::cast::SerializeEvents(*metadata,
188 frame_events,
189 packet_events,
190 true,
191 media::cast::kMaxSerializedBytes,
192 serialized_log.get(),
193 &output_bytes);
195 if (!success) {
196 LOG(ERROR) << "Failed to serialize log.";
197 return;
200 if (!AppendToFile(output_path, serialized_log.get(), output_bytes)) {
201 LOG(ERROR) << "Failed to append to log.";
205 // Run simulation once.
207 // |output_path| is the path to write serialized log.
208 // |extra_data| is extra tagging information to write to log.
209 void RunSimulation(const base::FilePath& source_path,
210 const base::FilePath& output_path,
211 const std::string& extra_data,
212 const NetworkSimulationModel& model) {
213 // Fake clock. Make sure start time is non zero.
214 base::SimpleTestTickClock testing_clock;
215 testing_clock.Advance(base::TimeDelta::FromSeconds(1));
217 // Task runner.
218 scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner =
219 new test::FakeSingleThreadTaskRunner(&testing_clock);
220 base::ThreadTaskRunnerHandle task_runner_handle(task_runner);
222 // CastEnvironments.
223 scoped_refptr<CastEnvironment> sender_env =
224 new CastEnvironment(
225 scoped_ptr<base::TickClock>(
226 new test::SkewedTickClock(&testing_clock)).Pass(),
227 task_runner,
228 task_runner,
229 task_runner);
230 scoped_refptr<CastEnvironment> receiver_env =
231 new CastEnvironment(
232 scoped_ptr<base::TickClock>(
233 new test::SkewedTickClock(&testing_clock)).Pass(),
234 task_runner,
235 task_runner,
236 task_runner);
238 // Event subscriber. Store at most 1 hour of events.
239 EncodingEventSubscriber audio_event_subscriber(AUDIO_EVENT,
240 100 * 60 * 60);
241 EncodingEventSubscriber video_event_subscriber(VIDEO_EVENT,
242 30 * 60 * 60);
243 sender_env->Logging()->AddRawEventSubscriber(&audio_event_subscriber);
244 sender_env->Logging()->AddRawEventSubscriber(&video_event_subscriber);
246 // Audio sender config.
247 AudioSenderConfig audio_sender_config = GetDefaultAudioSenderConfig();
248 audio_sender_config.min_playout_delay =
249 audio_sender_config.max_playout_delay = base::TimeDelta::FromMilliseconds(
250 GetIntegerSwitchValue(kTargetDelay, 400));
252 // Audio receiver config.
253 FrameReceiverConfig audio_receiver_config =
254 GetDefaultAudioReceiverConfig();
255 audio_receiver_config.rtp_max_delay_ms =
256 audio_sender_config.max_playout_delay.InMilliseconds();
258 // Video sender config.
259 VideoSenderConfig video_sender_config = GetDefaultVideoSenderConfig();
260 video_sender_config.max_bitrate = 2500000;
261 video_sender_config.min_bitrate = 2000000;
262 video_sender_config.start_bitrate = 2000000;
263 video_sender_config.min_playout_delay =
264 video_sender_config.max_playout_delay =
265 audio_sender_config.max_playout_delay;
266 video_sender_config.max_frame_rate = GetIntegerSwitchValue(kMaxFrameRate, 30);
268 // Video receiver config.
269 FrameReceiverConfig video_receiver_config =
270 GetDefaultVideoReceiverConfig();
271 video_receiver_config.rtp_max_delay_ms =
272 video_sender_config.max_playout_delay.InMilliseconds();
274 // Loopback transport.
275 LoopBackTransport receiver_to_sender(receiver_env);
276 LoopBackTransport sender_to_receiver(sender_env);
278 // Cast receiver.
279 scoped_ptr<CastReceiver> cast_receiver(
280 CastReceiver::Create(receiver_env,
281 audio_receiver_config,
282 video_receiver_config,
283 &receiver_to_sender));
285 // Cast sender and transport sender.
286 scoped_ptr<CastTransportSender> transport_sender(
287 new CastTransportSenderImpl(
288 NULL,
289 &testing_clock,
290 net::IPEndPoint(),
291 make_scoped_ptr(new base::DictionaryValue),
292 base::Bind(&UpdateCastTransportStatus),
293 base::Bind(&LogTransportEvents, sender_env),
294 base::TimeDelta::FromSeconds(1),
295 task_runner,
296 &sender_to_receiver));
297 scoped_ptr<CastSender> cast_sender(
298 CastSender::Create(sender_env, transport_sender.get()));
300 // Build packet pipe.
301 if (model.type() != media::cast::proto::INTERRUPTED_POISSON_PROCESS) {
302 LOG(ERROR) << "Unknown model type " << model.type() << ".";
303 return;
306 const IPPModel& ipp_model = model.ipp();
308 std::vector<double> average_rates(ipp_model.average_rate_size());
309 std::copy(ipp_model.average_rate().begin(), ipp_model.average_rate().end(),
310 average_rates.begin());
311 test::InterruptedPoissonProcess ipp(average_rates,
312 ipp_model.coef_burstiness(), ipp_model.coef_variance(), 0);
314 // Connect sender to receiver. This initializes the pipe.
315 receiver_to_sender.Initialize(
316 ipp.NewBuffer(128 * 1024).Pass(),
317 transport_sender->PacketReceiverForTesting(),
318 task_runner, &testing_clock);
319 sender_to_receiver.Initialize(
320 ipp.NewBuffer(128 * 1024).Pass(),
321 cast_receiver->packet_receiver(), task_runner,
322 &testing_clock);
324 // Start receiver.
325 int audio_frame_count = 0;
326 int video_frame_count = 0;
327 cast_receiver->RequestDecodedVideoFrame(
328 base::Bind(&GotVideoFrame, &video_frame_count, cast_receiver.get()));
329 cast_receiver->RequestDecodedAudioFrame(
330 base::Bind(&GotAudioFrame, &audio_frame_count, cast_receiver.get()));
332 FakeMediaSource media_source(task_runner,
333 &testing_clock,
334 video_sender_config);
336 // Initializing audio and video senders.
337 cast_sender->InitializeAudio(audio_sender_config,
338 base::Bind(&AudioInitializationStatus));
339 cast_sender->InitializeVideo(media_source.get_video_config(),
340 base::Bind(&VideoInitializationStatus),
341 CreateDefaultVideoEncodeAcceleratorCallback(),
342 CreateDefaultVideoEncodeMemoryCallback());
343 task_runner->RunTasks();
345 // Start sending.
346 if (!source_path.empty()) {
347 // 0 means using the FPS from the file.
348 media_source.SetSourceFile(source_path,
349 GetIntegerSwitchValue(kSourceFrameRate, 0));
351 media_source.Start(cast_sender->audio_frame_input(),
352 cast_sender->video_frame_input());
354 // Run for 3 minutes.
355 base::TimeDelta elapsed_time;
356 const base::TimeDelta desired_run_time =
357 base::TimeDelta::FromSeconds(GetIntegerSwitchValue(kRunTime, 180));
358 while (elapsed_time < desired_run_time) {
359 // Each step is 100us.
360 base::TimeDelta step = base::TimeDelta::FromMicroseconds(100);
361 task_runner->Sleep(step);
362 elapsed_time += step;
365 // Get event logs for audio and video.
366 media::cast::proto::LogMetadata audio_metadata, video_metadata;
367 media::cast::FrameEventList audio_frame_events, video_frame_events;
368 media::cast::PacketEventList audio_packet_events, video_packet_events;
369 audio_metadata.set_extra_data(extra_data);
370 video_metadata.set_extra_data(extra_data);
371 audio_event_subscriber.GetEventsAndReset(
372 &audio_metadata, &audio_frame_events, &audio_packet_events);
373 video_event_subscriber.GetEventsAndReset(
374 &video_metadata, &video_frame_events, &video_packet_events);
376 // Print simulation results.
378 // Compute and print statistics for video:
380 // * Total video frames captured.
381 // * Total video frames encoded.
382 // * Total video frames dropped.
383 // * Total video frames received late.
384 // * Average target bitrate.
385 // * Average encoded bitrate.
386 int total_video_frames = 0;
387 int encoded_video_frames = 0;
388 int dropped_video_frames = 0;
389 int late_video_frames = 0;
390 int64 total_delay_of_late_frames_ms = 0;
391 int64 encoded_size = 0;
392 int64 target_bitrate = 0;
393 for (size_t i = 0; i < video_frame_events.size(); ++i) {
394 const media::cast::proto::AggregatedFrameEvent& event =
395 *video_frame_events[i];
396 ++total_video_frames;
397 if (event.has_encoded_frame_size()) {
398 ++encoded_video_frames;
399 encoded_size += event.encoded_frame_size();
400 target_bitrate += event.target_bitrate();
401 } else {
402 ++dropped_video_frames;
404 if (event.has_delay_millis() && event.delay_millis() < 0) {
405 ++late_video_frames;
406 total_delay_of_late_frames_ms += -event.delay_millis();
410 // Subtract fraction of dropped frames from |elapsed_time| before estimating
411 // the average encoded bitrate.
412 const base::TimeDelta elapsed_time_undropped =
413 total_video_frames <= 0 ? base::TimeDelta() :
414 (elapsed_time * (total_video_frames - dropped_video_frames) /
415 total_video_frames);
416 const double avg_encoded_bitrate =
417 elapsed_time_undropped <= base::TimeDelta() ? 0 :
418 8.0 * encoded_size / elapsed_time_undropped.InSecondsF() / 1000;
419 double avg_target_bitrate =
420 !encoded_video_frames ? 0 : target_bitrate / encoded_video_frames / 1000;
422 LOG(INFO) << "Configured target playout delay (ms): "
423 << video_receiver_config.rtp_max_delay_ms;
424 LOG(INFO) << "Audio frame count: " << audio_frame_count;
425 LOG(INFO) << "Total video frames: " << total_video_frames;
426 LOG(INFO) << "Dropped video frames " << dropped_video_frames;
427 LOG(INFO) << "Late video frames: " << late_video_frames
428 << " (average lateness: "
429 << (late_video_frames > 0 ?
430 static_cast<double>(total_delay_of_late_frames_ms) /
431 late_video_frames :
433 << " ms)";
434 LOG(INFO) << "Average encoded bitrate (kbps): " << avg_encoded_bitrate;
435 LOG(INFO) << "Average target bitrate (kbps): " << avg_target_bitrate;
436 LOG(INFO) << "Writing log: " << output_path.value();
438 // Truncate file and then write serialized log.
440 base::ScopedFILE file(base::OpenFile(output_path, "wb"));
441 if (!file.get()) {
442 LOG(INFO) << "Cannot write to log.";
443 return;
446 AppendLogToFile(&video_metadata, video_frame_events, video_packet_events,
447 output_path);
448 AppendLogToFile(&audio_metadata, audio_frame_events, audio_packet_events,
449 output_path);
452 NetworkSimulationModel DefaultModel() {
453 NetworkSimulationModel model;
454 model.set_type(cast::proto::INTERRUPTED_POISSON_PROCESS);
455 IPPModel* ipp = model.mutable_ipp();
456 ipp->set_coef_burstiness(0.609);
457 ipp->set_coef_variance(4.1);
459 ipp->add_average_rate(0.609);
460 ipp->add_average_rate(0.495);
461 ipp->add_average_rate(0.561);
462 ipp->add_average_rate(0.458);
463 ipp->add_average_rate(0.538);
464 ipp->add_average_rate(0.513);
465 ipp->add_average_rate(0.585);
466 ipp->add_average_rate(0.592);
467 ipp->add_average_rate(0.658);
468 ipp->add_average_rate(0.556);
469 ipp->add_average_rate(0.371);
470 ipp->add_average_rate(0.595);
471 ipp->add_average_rate(0.490);
472 ipp->add_average_rate(0.980);
473 ipp->add_average_rate(0.781);
474 ipp->add_average_rate(0.463);
476 return model;
479 bool IsModelValid(const NetworkSimulationModel& model) {
480 if (!model.has_type())
481 return false;
482 NetworkSimulationModelType type = model.type();
483 if (type == media::cast::proto::INTERRUPTED_POISSON_PROCESS) {
484 if (!model.has_ipp())
485 return false;
486 const IPPModel& ipp = model.ipp();
487 if (ipp.coef_burstiness() <= 0.0 || ipp.coef_variance() <= 0.0)
488 return false;
489 if (ipp.average_rate_size() == 0)
490 return false;
491 for (int i = 0; i < ipp.average_rate_size(); i++) {
492 if (ipp.average_rate(i) <= 0.0)
493 return false;
497 return true;
500 NetworkSimulationModel LoadModel(const base::FilePath& model_path) {
501 if (model_path.empty()) {
502 LOG(ERROR) << "Model path not set.";
503 return DefaultModel();
505 std::string model_str;
506 if (!base::ReadFileToString(model_path, &model_str)) {
507 LOG(ERROR) << "Failed to read model file.";
508 return DefaultModel();
511 NetworkSimulationModel model;
512 if (!model.ParseFromString(model_str)) {
513 LOG(ERROR) << "Failed to parse model.";
514 return DefaultModel();
516 if (!IsModelValid(model)) {
517 LOG(ERROR) << "Invalid model.";
518 return DefaultModel();
521 return model;
524 } // namespace
525 } // namespace cast
526 } // namespace media
528 int main(int argc, char** argv) {
529 base::AtExitManager at_exit;
530 CommandLine::Init(argc, argv);
531 InitLogging(logging::LoggingSettings());
533 const CommandLine* cmd = CommandLine::ForCurrentProcess();
534 base::FilePath media_path = cmd->GetSwitchValuePath(media::cast::kLibDir);
535 if (media_path.empty()) {
536 if (!PathService::Get(base::DIR_MODULE, &media_path)) {
537 LOG(ERROR) << "Failed to load FFmpeg.";
538 return 1;
542 if (!media::InitializeMediaLibrary(media_path)) {
543 LOG(ERROR) << "Failed to initialize FFmpeg.";
544 return 1;
547 base::FilePath source_path = cmd->GetSwitchValuePath(
548 media::cast::kSourcePath);
549 base::FilePath output_path = cmd->GetSwitchValuePath(
550 media::cast::kOutputPath);
551 if (output_path.empty()) {
552 base::GetTempDir(&output_path);
553 output_path = output_path.AppendASCII("sim-events.gz");
555 std::string sim_id = cmd->GetSwitchValueASCII(media::cast::kSimulationId);
557 NetworkSimulationModel model = media::cast::LoadModel(
558 cmd->GetSwitchValuePath(media::cast::kModelPath));
560 base::DictionaryValue values;
561 values.SetBoolean("sim", true);
562 values.SetString("sim-id", sim_id);
564 std::string extra_data;
565 base::JSONWriter::Write(&values, &extra_data);
567 // Run.
568 media::cast::RunSimulation(source_path, output_path, extra_data, model);
569 return 0;