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.
9 // WebM used as the source of video and audio frames.
11 // File path to writing out the raw event log of the simulation session.
13 // Unique simulation ID.
15 // Target playout delay to configure (integer number of milliseconds).
16 // Optional; default is 400.
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.
23 // In seconds, how long the Cast session runs for.
24 // Optional; default is 180.
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
;
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
);
96 CHECK(base::StringToInt(as_str
, &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();
120 env
->Logging()->InsertPacketEvent(it
->timestamp
,
129 for (std::vector
<media::cast::FrameEvent
>::const_iterator it
=
130 frame_events
.begin();
131 it
!= frame_events
.end();
133 if (it
->type
== FRAME_PLAYOUT
) {
134 env
->Logging()->InsertFrameEventWithDelay(
142 env
->Logging()->InsertFrameEvent(
154 CastReceiver
* cast_receiver
,
155 const scoped_refptr
<media::VideoFrame
>& video_frame
,
156 const base::TimeTicks
& render_time
,
159 cast_receiver
->RequestDecodedVideoFrame(
160 base::Bind(&GotVideoFrame
, counter
, cast_receiver
));
165 CastReceiver
* cast_receiver
,
166 scoped_ptr
<AudioBus
> audio_bus
,
167 const base::TimeTicks
& playout_time
,
168 bool is_continuous
) {
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
]);
187 bool success
= media::cast::SerializeEvents(*metadata
,
191 media::cast::kMaxSerializedBytes
,
192 serialized_log
.get(),
196 LOG(ERROR
) << "Failed to serialize log.";
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));
218 scoped_refptr
<test::FakeSingleThreadTaskRunner
> task_runner
=
219 new test::FakeSingleThreadTaskRunner(&testing_clock
);
220 base::ThreadTaskRunnerHandle
task_runner_handle(task_runner
);
223 scoped_refptr
<CastEnvironment
> sender_env
=
225 scoped_ptr
<base::TickClock
>(
226 new test::SkewedTickClock(&testing_clock
)).Pass(),
230 scoped_refptr
<CastEnvironment
> receiver_env
=
232 scoped_ptr
<base::TickClock
>(
233 new test::SkewedTickClock(&testing_clock
)).Pass(),
238 // Event subscriber. Store at most 1 hour of events.
239 EncodingEventSubscriber
audio_event_subscriber(AUDIO_EVENT
,
241 EncodingEventSubscriber
video_event_subscriber(VIDEO_EVENT
,
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
);
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(
291 make_scoped_ptr(new base::DictionaryValue
),
292 base::Bind(&UpdateCastTransportStatus
),
293 base::Bind(&LogTransportEvents
, sender_env
),
294 base::TimeDelta::FromSeconds(1),
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() << ".";
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
,
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
,
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();
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();
402 ++dropped_video_frames
;
404 if (event
.has_delay_millis() && event
.delay_millis() < 0) {
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
) /
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
) /
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"));
442 LOG(INFO
) << "Cannot write to log.";
446 AppendLogToFile(&video_metadata
, video_frame_events
, video_packet_events
,
448 AppendLogToFile(&audio_metadata
, audio_frame_events
, audio_packet_events
,
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);
479 bool IsModelValid(const NetworkSimulationModel
& model
) {
480 if (!model
.has_type())
482 NetworkSimulationModelType type
= model
.type();
483 if (type
== media::cast::proto::INTERRUPTED_POISSON_PROCESS
) {
484 if (!model
.has_ipp())
486 const IPPModel
& ipp
= model
.ipp();
487 if (ipp
.coef_burstiness() <= 0.0 || ipp
.coef_variance() <= 0.0)
489 if (ipp
.average_rate_size() == 0)
491 for (int i
= 0; i
< ipp
.average_rate_size(); i
++) {
492 if (ipp
.average_rate(i
) <= 0.0)
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();
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.";
542 if (!media::InitializeMediaLibrary(media_path
)) {
543 LOG(ERROR
) << "Failed to initialize FFmpeg.";
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
);
568 media::cast::RunSimulation(source_path
, output_path
, extra_data
, model
);