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/base/checks.h"
     20 #include "webrtc/base/scoped_ptr.h"
     21 #include "webrtc/call.h"
     22 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
     23 #include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h"
     24 #include "webrtc/system_wrappers/include/clock.h"
     25 #include "webrtc/system_wrappers/include/sleep.h"
     26 #include "webrtc/test/encoder_settings.h"
     27 #include "webrtc/test/null_transport.h"
     28 #include "webrtc/test/fake_decoder.h"
     29 #include "webrtc/test/rtp_file_reader.h"
     30 #include "webrtc/test/run_loop.h"
     31 #include "webrtc/test/run_test.h"
     32 #include "webrtc/test/video_capturer.h"
     33 #include "webrtc/test/video_renderer.h"
     34 #include "webrtc/typedefs.h"
     35 #include "webrtc/video_decoder.h"
     36 
     37 namespace webrtc {
     38 namespace flags {
     39 
     40 // TODO(pbos): Multiple receivers.
     41 
     42 // Flag for payload type.
     43 static bool ValidatePayloadType(const char* flagname, int32_t payload_type) {
     44   return payload_type > 0 && payload_type <= 127;
     45 }
     46 DEFINE_int32(payload_type, 0, "Payload type");
     47 static int PayloadType() { return static_cast<int>(FLAGS_payload_type); }
     48 static const bool payload_dummy =
     49     google::RegisterFlagValidator(&FLAGS_payload_type, &ValidatePayloadType);
     50 
     51 // Flag for SSRC.
     52 static bool ValidateSsrc(const char* flagname, uint64_t ssrc) {
     53   return ssrc > 0 && ssrc <= 0xFFFFFFFFu;
     54 }
     55 
     56 DEFINE_uint64(ssrc, 0, "Incoming SSRC");
     57 static uint32_t Ssrc() { return static_cast<uint32_t>(FLAGS_ssrc); }
     58 static const bool ssrc_dummy =
     59     google::RegisterFlagValidator(&FLAGS_ssrc, &ValidateSsrc);
     60 
     61 static bool ValidateOptionalPayloadType(const char* flagname,
     62                                         int32_t payload_type) {
     63   return payload_type == -1 || ValidatePayloadType(flagname, payload_type);
     64 }
     65 
     66 // Flag for RED payload type.
     67 DEFINE_int32(red_payload_type, -1, "RED payload type");
     68 static int RedPayloadType() {
     69   return static_cast<int>(FLAGS_red_payload_type);
     70 }
     71 static const bool red_dummy =
     72     google::RegisterFlagValidator(&FLAGS_red_payload_type,
     73                                   &ValidateOptionalPayloadType);
     74 
     75 // Flag for ULPFEC payload type.
     76 DEFINE_int32(fec_payload_type, -1, "ULPFEC payload type");
     77 static int FecPayloadType() {
     78   return static_cast<int>(FLAGS_fec_payload_type);
     79 }
     80 static const bool fec_dummy =
     81     google::RegisterFlagValidator(&FLAGS_fec_payload_type,
     82                                   &ValidateOptionalPayloadType);
     83 
     84 // Flag for abs-send-time id.
     85 static bool ValidateRtpHeaderExtensionId(const char* flagname,
     86                                          int32_t extension_id) {
     87   return extension_id >= -1 || extension_id < 15;
     88 }
     89 DEFINE_int32(abs_send_time_id, -1, "RTP extension ID for abs-send-time");
     90 static int AbsSendTimeId() { return static_cast<int>(FLAGS_abs_send_time_id); }
     91 static const bool abs_send_time_dummy =
     92     google::RegisterFlagValidator(&FLAGS_abs_send_time_id,
     93                                   &ValidateRtpHeaderExtensionId);
     94 
     95 // Flag for transmission-offset id.
     96 DEFINE_int32(transmission_offset_id,
     97              -1,
     98              "RTP extension ID for transmission-offset");
     99 static int TransmissionOffsetId() {
    100   return static_cast<int>(FLAGS_transmission_offset_id);
    101 }
    102 static const bool timestamp_offset_dummy =
    103     google::RegisterFlagValidator(&FLAGS_transmission_offset_id,
    104                                   &ValidateRtpHeaderExtensionId);
    105 
    106 // Flag for rtpdump input file.
    107 bool ValidateInputFilenameNotEmpty(const char* flagname,
    108                                    const std::string& string) {
    109   return !string.empty();
    110 }
    111 
    112 DEFINE_string(input_file, "", "input file");
    113 static std::string InputFile() {
    114   return static_cast<std::string>(FLAGS_input_file);
    115 }
    116 static const bool input_file_dummy =
    117     google::RegisterFlagValidator(&FLAGS_input_file,
    118                                   &ValidateInputFilenameNotEmpty);
    119 
    120 // Flag for raw output files.
    121 DEFINE_string(out_base, "", "Basename (excluding .yuv) for raw output");
    122 static std::string OutBase() {
    123   return static_cast<std::string>(FLAGS_out_base);
    124 }
    125 
    126 DEFINE_string(decoder_bitstream_filename, "", "Decoder bitstream output file");
    127 static std::string DecoderBitstreamFilename() {
    128   return static_cast<std::string>(FLAGS_decoder_bitstream_filename);
    129 }
    130 
    131 // Flag for video codec.
    132 DEFINE_string(codec, "VP8", "Video codec");
    133 static std::string Codec() { return static_cast<std::string>(FLAGS_codec); }
    134 
    135 }  // namespace flags
    136 
    137 static const uint32_t kReceiverLocalSsrc = 0x123456;
    138 
    139 class FileRenderPassthrough : public VideoRenderer {
    140  public:
    141   FileRenderPassthrough(const std::string& basename, VideoRenderer* renderer)
    142       : basename_(basename),
    143         renderer_(renderer),
    144         file_(nullptr),
    145         count_(0),
    146         last_width_(0),
    147         last_height_(0) {}
    148 
    149   ~FileRenderPassthrough() {
    150     if (file_ != nullptr)
    151       fclose(file_);
    152   }
    153 
    154  private:
    155   void RenderFrame(const VideoFrame& video_frame,
    156                    int time_to_render_ms) override {
    157     if (renderer_ != nullptr)
    158       renderer_->RenderFrame(video_frame, time_to_render_ms);
    159     if (basename_.empty())
    160       return;
    161     if (last_width_ != video_frame.width() ||
    162         last_height_ != video_frame.height()) {
    163       if (file_ != nullptr)
    164         fclose(file_);
    165       std::stringstream filename;
    166       filename << basename_;
    167       if (++count_ > 1)
    168         filename << '-' << count_;
    169       filename << '_' << video_frame.width() << 'x' << video_frame.height()
    170                << ".yuv";
    171       file_ = fopen(filename.str().c_str(), "wb");
    172       if (file_ == nullptr) {
    173         fprintf(stderr,
    174                 "Couldn't open file for writing: %s\n",
    175                 filename.str().c_str());
    176       }
    177     }
    178     last_width_ = video_frame.width();
    179     last_height_ = video_frame.height();
    180     if (file_ == nullptr)
    181       return;
    182     PrintVideoFrame(video_frame, file_);
    183   }
    184 
    185   bool IsTextureSupported() const override { return false; }
    186 
    187   const std::string basename_;
    188   VideoRenderer* const renderer_;
    189   FILE* file_;
    190   size_t count_;
    191   int last_width_;
    192   int last_height_;
    193 };
    194 
    195 class DecoderBitstreamFileWriter : public EncodedFrameObserver {
    196  public:
    197   explicit DecoderBitstreamFileWriter(const char* filename)
    198       : file_(fopen(filename, "wb")) {
    199     RTC_DCHECK(file_ != nullptr);
    200   }
    201   ~DecoderBitstreamFileWriter() { fclose(file_); }
    202 
    203   virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) {
    204     fwrite(encoded_frame.data_, 1, encoded_frame.length_, file_);
    205   }
    206 
    207  private:
    208   FILE* file_;
    209 };
    210 
    211 void RtpReplay() {
    212   rtc::scoped_ptr<test::VideoRenderer> playback_video(
    213       test::VideoRenderer::Create("Playback Video", 640, 480));
    214   FileRenderPassthrough file_passthrough(flags::OutBase(),
    215                                          playback_video.get());
    216 
    217   rtc::scoped_ptr<Call> call(Call::Create(Call::Config()));
    218 
    219   test::NullTransport transport;
    220   VideoReceiveStream::Config receive_config(&transport);
    221   receive_config.rtp.remote_ssrc = flags::Ssrc();
    222   receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
    223   receive_config.rtp.fec.ulpfec_payload_type = flags::FecPayloadType();
    224   receive_config.rtp.fec.red_payload_type = flags::RedPayloadType();
    225   receive_config.rtp.nack.rtp_history_ms = 1000;
    226   if (flags::TransmissionOffsetId() != -1) {
    227     receive_config.rtp.extensions.push_back(
    228         RtpExtension(RtpExtension::kTOffset, flags::TransmissionOffsetId()));
    229   }
    230   if (flags::AbsSendTimeId() != -1) {
    231     receive_config.rtp.extensions.push_back(
    232         RtpExtension(RtpExtension::kAbsSendTime, flags::AbsSendTimeId()));
    233   }
    234   receive_config.renderer = &file_passthrough;
    235 
    236   VideoSendStream::Config::EncoderSettings encoder_settings;
    237   encoder_settings.payload_name = flags::Codec();
    238   encoder_settings.payload_type = flags::PayloadType();
    239   VideoReceiveStream::Decoder decoder;
    240   rtc::scoped_ptr<DecoderBitstreamFileWriter> bitstream_writer;
    241   if (!flags::DecoderBitstreamFilename().empty()) {
    242     bitstream_writer.reset(new DecoderBitstreamFileWriter(
    243         flags::DecoderBitstreamFilename().c_str()));
    244     receive_config.pre_decode_callback = bitstream_writer.get();
    245   }
    246   decoder = test::CreateMatchingDecoder(encoder_settings);
    247   if (!flags::DecoderBitstreamFilename().empty()) {
    248     // Replace with a null decoder if we're writing the bitstream to a file
    249     // instead.
    250     delete decoder.decoder;
    251     decoder.decoder = new test::FakeNullDecoder();
    252   }
    253   receive_config.decoders.push_back(decoder);
    254 
    255   VideoReceiveStream* receive_stream =
    256       call->CreateVideoReceiveStream(receive_config);
    257 
    258   rtc::scoped_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
    259       test::RtpFileReader::kRtpDump, flags::InputFile()));
    260   if (rtp_reader.get() == nullptr) {
    261     rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap,
    262                                                  flags::InputFile()));
    263     if (rtp_reader.get() == nullptr) {
    264       fprintf(stderr,
    265               "Couldn't open input file as either a rtpdump or .pcap. Note "
    266               "that .pcapng is not supported.\nTrying to interpret the file as "
    267               "length/packet interleaved.\n");
    268       rtp_reader.reset(test::RtpFileReader::Create(
    269           test::RtpFileReader::kLengthPacketInterleaved, flags::InputFile()));
    270       if (rtp_reader.get() == nullptr) {
    271         fprintf(stderr,
    272                 "Unable to open input file with any supported format\n");
    273         return;
    274       }
    275     }
    276   }
    277   receive_stream->Start();
    278 
    279   uint32_t last_time_ms = 0;
    280   int num_packets = 0;
    281   std::map<uint32_t, int> unknown_packets;
    282   while (true) {
    283     test::RtpPacket packet;
    284     if (!rtp_reader->NextPacket(&packet))
    285       break;
    286     ++num_packets;
    287     switch (call->Receiver()->DeliverPacket(webrtc::MediaType::ANY, packet.data,
    288                                             packet.length, PacketTime())) {
    289       case PacketReceiver::DELIVERY_OK:
    290         break;
    291       case PacketReceiver::DELIVERY_UNKNOWN_SSRC: {
    292         RTPHeader header;
    293         rtc::scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
    294         parser->Parse(packet.data, packet.length, &header);
    295         if (unknown_packets[header.ssrc] == 0)
    296           fprintf(stderr, "Unknown SSRC: %u!\n", header.ssrc);
    297         ++unknown_packets[header.ssrc];
    298         break;
    299       }
    300       case PacketReceiver::DELIVERY_PACKET_ERROR:
    301         fprintf(stderr, "Packet error, corrupt packets or incorrect setup?\n");
    302         break;
    303     }
    304     if (last_time_ms != 0 && last_time_ms != packet.time_ms) {
    305       SleepMs(packet.time_ms - last_time_ms);
    306     }
    307     last_time_ms = packet.time_ms;
    308   }
    309   fprintf(stderr, "num_packets: %d\n", num_packets);
    310 
    311   for (std::map<uint32_t, int>::const_iterator it = unknown_packets.begin();
    312        it != unknown_packets.end();
    313        ++it) {
    314     fprintf(
    315         stderr, "Packets for unknown ssrc '%u': %d\n", it->first, it->second);
    316   }
    317 
    318   call->DestroyVideoReceiveStream(receive_stream);
    319 
    320   delete decoder.decoder;
    321 }
    322 }  // namespace webrtc
    323 
    324 int main(int argc, char* argv[]) {
    325   ::testing::InitGoogleTest(&argc, argv);
    326   google::ParseCommandLineFlags(&argc, &argv, true);
    327 
    328   webrtc::test::RunTest(webrtc::RtpReplay);
    329   return 0;
    330 }
    331