Home | History | Annotate | Download | only in video
      1 /*
      2  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include <stdio.h>
     12 
     13 #include <map>
     14 #include <sstream>
     15 
     16 #include "gflags/gflags.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 #include "webrtc/call.h"
     20 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
     21 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
     22 #include "webrtc/system_wrappers/interface/clock.h"
     23 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
     24 #include "webrtc/system_wrappers/interface/sleep.h"
     25 #include "webrtc/test/encoder_settings.h"
     26 #include "webrtc/test/null_transport.h"
     27 #include "webrtc/test/rtp_file_reader.h"
     28 #include "webrtc/test/run_loop.h"
     29 #include "webrtc/test/run_test.h"
     30 #include "webrtc/test/video_capturer.h"
     31 #include "webrtc/test/video_renderer.h"
     32 #include "webrtc/typedefs.h"
     33 
     34 namespace webrtc {
     35 namespace flags {
     36 
     37 // TODO(pbos): Multiple receivers.
     38 
     39 // Flag for payload type.
     40 static bool ValidatePayloadType(const char* flagname, int32_t payload_type) {
     41   return payload_type > 0 && payload_type <= 127;
     42 }
     43 DEFINE_int32(payload_type, 0, "Payload type");
     44 static int PayloadType() { return static_cast<int>(FLAGS_payload_type); }
     45 static const bool payload_dummy =
     46     google::RegisterFlagValidator(&FLAGS_payload_type, &ValidatePayloadType);
     47 
     48 // Flag for SSRC.
     49 static bool ValidateSsrc(const char* flagname, uint64_t ssrc) {
     50   return ssrc > 0 && ssrc <= 0xFFFFFFFFu;
     51 }
     52 
     53 DEFINE_uint64(ssrc, 0, "Incoming SSRC");
     54 static uint32_t Ssrc() { return static_cast<uint32_t>(FLAGS_ssrc); }
     55 static const bool ssrc_dummy =
     56     google::RegisterFlagValidator(&FLAGS_ssrc, &ValidateSsrc);
     57 
     58 static bool ValidateOptionalPayloadType(const char* flagname,
     59                                         int32_t payload_type) {
     60   return payload_type == -1 || ValidatePayloadType(flagname, payload_type);
     61 }
     62 
     63 // Flag for RED payload type.
     64 DEFINE_int32(red_payload_type, -1, "RED payload type");
     65 static int RedPayloadType() {
     66   return static_cast<int>(FLAGS_red_payload_type);
     67 }
     68 static const bool red_dummy =
     69     google::RegisterFlagValidator(&FLAGS_red_payload_type,
     70                                   &ValidateOptionalPayloadType);
     71 
     72 // Flag for ULPFEC payload type.
     73 DEFINE_int32(fec_payload_type, -1, "ULPFEC payload type");
     74 static int FecPayloadType() {
     75   return static_cast<int>(FLAGS_fec_payload_type);
     76 }
     77 static const bool fec_dummy =
     78     google::RegisterFlagValidator(&FLAGS_fec_payload_type,
     79                                   &ValidateOptionalPayloadType);
     80 
     81 // Flag for abs-send-time id.
     82 static bool ValidateRtpHeaderExtensionId(const char* flagname,
     83                                          int32_t extension_id) {
     84   return extension_id >= -1 || extension_id < 15;
     85 }
     86 DEFINE_int32(abs_send_time_id, -1, "RTP extension ID for abs-send-time");
     87 static int AbsSendTimeId() { return static_cast<int>(FLAGS_abs_send_time_id); }
     88 static const bool abs_send_time_dummy =
     89     google::RegisterFlagValidator(&FLAGS_abs_send_time_id,
     90                                   &ValidateRtpHeaderExtensionId);
     91 
     92 // Flag for transmission-offset id.
     93 DEFINE_int32(transmission_offset_id,
     94              -1,
     95              "RTP extension ID for transmission-offset");
     96 static int TransmissionOffsetId() {
     97   return static_cast<int>(FLAGS_transmission_offset_id);
     98 }
     99 static const bool timestamp_offset_dummy =
    100     google::RegisterFlagValidator(&FLAGS_transmission_offset_id,
    101                                   &ValidateRtpHeaderExtensionId);
    102 
    103 // Flag for rtpdump input file.
    104 bool ValidateInputFilenameNotEmpty(const char* flagname,
    105                                    const std::string& string) {
    106   return string != "";
    107 }
    108 DEFINE_string(input_file, "", "input file");
    109 static std::string InputFile() {
    110   return static_cast<std::string>(FLAGS_input_file);
    111 }
    112 static const bool input_file_dummy =
    113     google::RegisterFlagValidator(&FLAGS_input_file,
    114                                   &ValidateInputFilenameNotEmpty);
    115 
    116 // Flag for raw output files.
    117 DEFINE_string(out_base, "", "Basename (excluding .yuv) for raw output");
    118 static std::string OutBase() {
    119   return static_cast<std::string>(FLAGS_out_base);
    120 }
    121 
    122 // Flag for video codec.
    123 DEFINE_string(codec, "VP8", "Video codec");
    124 static std::string Codec() { return static_cast<std::string>(FLAGS_codec); }
    125 
    126 }  // namespace flags
    127 
    128 static const uint32_t kReceiverLocalSsrc = 0x123456;
    129 
    130 class FileRenderPassthrough : public VideoRenderer {
    131  public:
    132   FileRenderPassthrough(const std::string& basename, VideoRenderer* renderer)
    133       : basename_(basename),
    134         renderer_(renderer),
    135         file_(NULL),
    136         count_(0),
    137         last_width_(0),
    138         last_height_(0) {}
    139 
    140   ~FileRenderPassthrough() {
    141     if (file_ != NULL)
    142       fclose(file_);
    143   }
    144 
    145  private:
    146   virtual void RenderFrame(const I420VideoFrame& video_frame,
    147                            int time_to_render_ms) OVERRIDE {
    148     if (renderer_ != NULL)
    149       renderer_->RenderFrame(video_frame, time_to_render_ms);
    150     if (basename_ == "")
    151       return;
    152     if (last_width_ != video_frame.width() ||
    153         last_height_ != video_frame.height()) {
    154       if (file_ != NULL)
    155         fclose(file_);
    156       std::stringstream filename;
    157       filename << basename_;
    158       if (++count_ > 1)
    159         filename << '-' << count_;
    160       filename << '_' << video_frame.width() << 'x' << video_frame.height()
    161                << ".yuv";
    162       file_ = fopen(filename.str().c_str(), "wb");
    163       if (file_ == NULL) {
    164         fprintf(stderr,
    165                 "Couldn't open file for writing: %s\n",
    166                 filename.str().c_str());
    167       }
    168     }
    169     last_width_ = video_frame.width();
    170     last_height_ = video_frame.height();
    171     if (file_ == NULL)
    172       return;
    173     PrintI420VideoFrame(video_frame, file_);
    174   }
    175 
    176   const std::string basename_;
    177   VideoRenderer* const renderer_;
    178   FILE* file_;
    179   size_t count_;
    180   int last_width_;
    181   int last_height_;
    182 };
    183 
    184 void RtpReplay() {
    185   scoped_ptr<test::VideoRenderer> playback_video(test::VideoRenderer::Create(
    186       "Playback Video", 640, 480));
    187   FileRenderPassthrough file_passthrough(flags::OutBase(),
    188                                          playback_video.get());
    189 
    190   // TODO(pbos): Might be good to have a transport that prints keyframe requests
    191   //             etc.
    192   test::NullTransport transport;
    193   Call::Config call_config(&transport);
    194   scoped_ptr<Call> call(Call::Create(call_config));
    195 
    196   VideoReceiveStream::Config receive_config;
    197   receive_config.rtp.remote_ssrc = flags::Ssrc();
    198   receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
    199   receive_config.rtp.fec.ulpfec_payload_type = flags::FecPayloadType();
    200   receive_config.rtp.fec.red_payload_type = flags::RedPayloadType();
    201   receive_config.rtp.nack.rtp_history_ms = 1000;
    202   if (flags::TransmissionOffsetId() != -1) {
    203     receive_config.rtp.extensions.push_back(
    204         RtpExtension(RtpExtension::kTOffset, flags::TransmissionOffsetId()));
    205   }
    206   if (flags::AbsSendTimeId() != -1) {
    207     receive_config.rtp.extensions.push_back(
    208         RtpExtension(RtpExtension::kAbsSendTime, flags::AbsSendTimeId()));
    209   }
    210   receive_config.renderer = &file_passthrough;
    211 
    212   VideoSendStream::Config::EncoderSettings encoder_settings;
    213   encoder_settings.payload_name = flags::Codec();
    214   encoder_settings.payload_type = flags::PayloadType();
    215   VideoCodec codec = test::CreateDecoderVideoCodec(encoder_settings);
    216   receive_config.codecs.push_back(codec);
    217 
    218   VideoReceiveStream* receive_stream =
    219       call->CreateVideoReceiveStream(receive_config);
    220 
    221   scoped_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
    222       test::RtpFileReader::kRtpDump, flags::InputFile()));
    223   if (rtp_reader.get() == NULL) {
    224     rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap,
    225                                                  flags::InputFile()));
    226     if (rtp_reader.get() == NULL) {
    227       fprintf(stderr,
    228               "Couldn't open input file as either a rtpdump or .pcap. Note "
    229               "that .pcapng is not supported.\n");
    230       return;
    231     }
    232   }
    233   receive_stream->Start();
    234 
    235   uint32_t last_time_ms = 0;
    236   int num_packets = 0;
    237   std::map<uint32_t, int> unknown_packets;
    238   while (true) {
    239     test::RtpFileReader::Packet packet;
    240     if (!rtp_reader->NextPacket(&packet))
    241       break;
    242     ++num_packets;
    243     switch (call->Receiver()->DeliverPacket(packet.data, packet.length)) {
    244       case PacketReceiver::DELIVERY_OK:
    245         break;
    246       case PacketReceiver::DELIVERY_UNKNOWN_SSRC: {
    247         RTPHeader header;
    248         scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
    249         parser->Parse(packet.data, packet.length, &header);
    250         if (unknown_packets[header.ssrc] == 0)
    251           fprintf(stderr, "Unknown SSRC: %u!\n", header.ssrc);
    252         ++unknown_packets[header.ssrc];
    253         break;
    254       }
    255       case PacketReceiver::DELIVERY_PACKET_ERROR:
    256         fprintf(stderr, "Packet error, corrupt packets or incorrect setup?\n");
    257         break;
    258     }
    259     if (last_time_ms != 0 && last_time_ms != packet.time_ms) {
    260       SleepMs(packet.time_ms - last_time_ms);
    261     }
    262     last_time_ms = packet.time_ms;
    263   }
    264   fprintf(stderr, "num_packets: %d\n", num_packets);
    265 
    266   for (std::map<uint32_t, int>::const_iterator it = unknown_packets.begin();
    267        it != unknown_packets.end();
    268        ++it) {
    269     fprintf(
    270         stderr, "Packets for unknown ssrc '%u': %d\n", it->first, it->second);
    271   }
    272 
    273   call->DestroyVideoReceiveStream(receive_stream);
    274 }
    275 }  // namespace webrtc
    276 
    277 int main(int argc, char* argv[]) {
    278   ::testing::InitGoogleTest(&argc, argv);
    279   google::ParseCommandLineFlags(&argc, &argv, true);
    280 
    281   webrtc::test::RunTest(webrtc::RtpReplay);
    282   return 0;
    283 }
    284