Home | History | Annotate | Download | only in test
      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.
      4 
      5 // Test application that simulates a cast sender - Data can be either generated
      6 // or read from a file.
      7 
      8 #include <queue>
      9 
     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"
     39 
     40 namespace {
     41 static const int kAudioChannels = 2;
     42 static const int kAudioSamplingFrequency = 48000;
     43 
     44 // The max allowed size of serialized log.
     45 const int kMaxSerializedLogBytes = 10 * 1000 * 1000;
     46 
     47 // Flags for this program:
     48 //
     49 // --address=xx.xx.xx.xx
     50 //   IP address of receiver.
     51 //
     52 // --port=xxxx
     53 //   Port number of receiver.
     54 //
     55 // --source-file=xxx.webm
     56 //   WebM file as source of video frames.
     57 //
     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";
     64 
     65 media::cast::AudioSenderConfig GetAudioSenderConfig() {
     66   media::cast::AudioSenderConfig audio_config;
     67 
     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;
     80 }
     81 
     82 media::cast::VideoSenderConfig GetVideoSenderConfig() {
     83   media::cast::VideoSenderConfig video_config;
     84 
     85   video_config.use_external_encoder = false;
     86 
     87   // Resolution.
     88   video_config.width = 1280;
     89   video_config.height = 720;
     90   video_config.max_frame_rate = 30;
     91 
     92   // Bitrates.
     93   video_config.max_bitrate = 2500000;
     94   video_config.min_bitrate = 100000;
     95   video_config.start_bitrate = video_config.min_bitrate;
     96 
     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;
    101 
    102   // Quality options.
    103   video_config.min_qp = 4;
    104   video_config.max_qp = 40;
    105 
    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;
    114 }
    115 
    116 void UpdateCastTransportStatus(
    117     media::cast::CastTransportStatus status) {
    118   VLOG(1) << "Transport status: " << status;
    119 }
    120 
    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);
    138   }
    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);
    149   }
    150 }
    151 
    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";
    156 }
    157 
    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);
    162 }
    163 
    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();
    170 
    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;
    182   }
    183 
    184   VLOG(0) << "Events serialized length: " << event_log_bytes;
    185 
    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.";
    189 }
    190 
    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());
    201 
    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);
    208 
    209   DumpLoggingData(log_metadata,
    210                   frame_events,
    211                   packet_events,
    212                   video_log_file.Pass());
    213 
    214   VLOG(0) << "Dumping logging data for audio stream.";
    215   audio_event_subscriber->GetEventsAndReset(
    216       &log_metadata, &frame_events, &packet_events);
    217 
    218   DumpLoggingData(log_metadata,
    219                   frame_events,
    220                   packet_events,
    221                   audio_log_file.Pass());
    222 }
    223 
    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());
    234 
    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;
    240 
    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;
    246 }
    247 
    248 }  // namespace
    249 
    250 int main(int argc, char** argv) {
    251   base::AtExitManager at_exit;
    252   CommandLine::Init(argc, argv);
    253   InitLogging(logging::LoggingSettings());
    254 
    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;
    261   }
    262 
    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();
    269 
    270   base::MessageLoopForIO io_message_loop;
    271 
    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;
    281   }
    282   LOG(INFO) << "Sending to " << remote_ip_address << ":" << remote_port
    283             << ".";
    284 
    285   media::cast::AudioSenderConfig audio_config = GetAudioSenderConfig();
    286   media::cast::VideoSenderConfig video_config = GetVideoSenderConfig();
    287 
    288   // Running transport on the main thread.
    289   // Setting up transport config.
    290   net::IPEndPoint remote_endpoint =
    291       CreateUDPAddress(remote_ip_address, remote_port);
    292 
    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()));
    301 
    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));
    307 
    308   int override_fps = 0;
    309   if (!base::StringToInt(cmd->GetSwitchValueASCII(kSwitchFps),
    310                          &override_fps)){
    311     override_fps = 0;
    312   }
    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);
    317   }
    318 
    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());
    330 
    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));
    340 
    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());
    356 
    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());
    373 
    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);
    378   }
    379 
    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);
    384   }
    385 
    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));
    396 
    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));
    405 
    406   fake_media_source->Start(cast_sender->audio_frame_input(),
    407                            cast_sender->video_frame_input());
    408 
    409   io_message_loop.Run();
    410   return 0;
    411 }
    412