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