Home | History | Annotate | Download | only in test
      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 "webrtc/test/rtp_file_reader.h"
     12 
     13 #include <stdio.h>
     14 
     15 #include <map>
     16 #include <string>
     17 #include <vector>
     18 
     19 #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
     20 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
     21 
     22 namespace webrtc {
     23 namespace test {
     24 
     25 static const size_t kFirstLineLength = 40;
     26 static uint16_t kPacketHeaderSize = 8;
     27 
     28 #if 1
     29 # define DEBUG_LOG(text)
     30 # define DEBUG_LOG1(text, arg)
     31 #else
     32 # define DEBUG_LOG(text) (printf(text "\n"))
     33 # define DEBUG_LOG1(text, arg) (printf(text "\n", arg))
     34 #endif
     35 
     36 #define TRY(expr)                                      \
     37   do {                                                 \
     38     if (!(expr)) {                                     \
     39       DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \
     40       return false;                                    \
     41     }                                                  \
     42   } while (0)
     43 
     44 class RtpFileReaderImpl : public RtpFileReader {
     45  public:
     46   virtual bool Init(const std::string& filename) = 0;
     47 };
     48 
     49 // Read RTP packets from file in rtpdump format, as documented at:
     50 // http://www.cs.columbia.edu/irt/software/rtptools/
     51 class RtpDumpReader : public RtpFileReaderImpl {
     52  public:
     53   RtpDumpReader() : file_(NULL) {}
     54   virtual ~RtpDumpReader() {
     55     if (file_ != NULL) {
     56       fclose(file_);
     57       file_ = NULL;
     58     }
     59   }
     60 
     61   bool Init(const std::string& filename) {
     62     file_ = fopen(filename.c_str(), "rb");
     63     if (file_ == NULL) {
     64       printf("ERROR: Can't open file: %s\n", filename.c_str());
     65       return false;
     66     }
     67 
     68     char firstline[kFirstLineLength + 1] = {0};
     69     if (fgets(firstline, kFirstLineLength, file_) == NULL) {
     70       DEBUG_LOG("ERROR: Can't read from file\n");
     71       return false;
     72     }
     73     if (strncmp(firstline, "#!rtpplay", 9) == 0) {
     74       if (strncmp(firstline, "#!rtpplay1.0", 12) != 0) {
     75         DEBUG_LOG("ERROR: wrong rtpplay version, must be 1.0\n");
     76         return false;
     77       }
     78     } else if (strncmp(firstline, "#!RTPencode", 11) == 0) {
     79       if (strncmp(firstline, "#!RTPencode1.0", 14) != 0) {
     80         DEBUG_LOG("ERROR: wrong RTPencode version, must be 1.0\n");
     81         return false;
     82       }
     83     } else {
     84       DEBUG_LOG("ERROR: wrong file format of input file\n");
     85       return false;
     86     }
     87 
     88     uint32_t start_sec;
     89     uint32_t start_usec;
     90     uint32_t source;
     91     uint16_t port;
     92     uint16_t padding;
     93     TRY(Read(&start_sec));
     94     TRY(Read(&start_usec));
     95     TRY(Read(&source));
     96     TRY(Read(&port));
     97     TRY(Read(&padding));
     98 
     99     return true;
    100   }
    101 
    102   virtual bool NextPacket(Packet* packet) OVERRIDE {
    103     uint8_t* rtp_data = packet->data;
    104     packet->length = Packet::kMaxPacketBufferSize;
    105 
    106     uint16_t len;
    107     uint16_t plen;
    108     uint32_t offset;
    109     TRY(Read(&len));
    110     TRY(Read(&plen));
    111     TRY(Read(&offset));
    112 
    113     // Use 'len' here because a 'plen' of 0 specifies rtcp.
    114     len -= kPacketHeaderSize;
    115     if (packet->length < len) {
    116       return false;
    117     }
    118     if (fread(rtp_data, 1, len, file_) != len) {
    119       return false;
    120     }
    121 
    122     packet->length = len;
    123     packet->time_ms = offset;
    124     return true;
    125   }
    126 
    127  private:
    128   bool Read(uint32_t* out) {
    129     *out = 0;
    130     for (size_t i = 0; i < 4; ++i) {
    131       *out <<= 8;
    132       uint8_t tmp;
    133       if (fread(&tmp, 1, sizeof(uint8_t), file_) != sizeof(uint8_t))
    134         return false;
    135       *out |= tmp;
    136     }
    137     return true;
    138   }
    139 
    140   bool Read(uint16_t* out) {
    141     *out = 0;
    142     for (size_t i = 0; i < 2; ++i) {
    143       *out <<= 8;
    144       uint8_t tmp;
    145       if (fread(&tmp, 1, sizeof(uint8_t), file_) != sizeof(uint8_t))
    146         return false;
    147       *out |= tmp;
    148     }
    149     return true;
    150   }
    151 
    152   FILE* file_;
    153 
    154   DISALLOW_COPY_AND_ASSIGN(RtpDumpReader);
    155 };
    156 
    157 enum {
    158   kResultFail = -1,
    159   kResultSuccess = 0,
    160   kResultSkip = 1,
    161 
    162   kPcapVersionMajor = 2,
    163   kPcapVersionMinor = 4,
    164   kLinktypeNull = 0,
    165   kLinktypeEthernet = 1,
    166   kBsdNullLoopback1 = 0x00000002,
    167   kBsdNullLoopback2 = 0x02000000,
    168   kEthernetIIHeaderMacSkip = 12,
    169   kEthertypeIp = 0x0800,
    170   kIpVersion4 = 4,
    171   kMinIpHeaderLength = 20,
    172   kFragmentOffsetClear = 0x0000,
    173   kFragmentOffsetDoNotFragment = 0x4000,
    174   kProtocolTcp = 0x06,
    175   kProtocolUdp = 0x11,
    176   kUdpHeaderLength = 8,
    177   kMaxReadBufferSize = 4096
    178 };
    179 
    180 const uint32_t kPcapBOMSwapOrder = 0xd4c3b2a1UL;
    181 const uint32_t kPcapBOMNoSwapOrder = 0xa1b2c3d4UL;
    182 
    183 #define TRY_PCAP(expr)                                 \
    184   do {                                                 \
    185     int r = (expr);                                    \
    186     if (r == kResultFail) {                            \
    187       DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \
    188       return kResultFail;                              \
    189     } else if (r == kResultSkip) {                     \
    190       return kResultSkip;                              \
    191     }                                                  \
    192   } while (0)
    193 
    194 // Read RTP packets from file in tcpdump/libpcap format, as documented at:
    195 // http://wiki.wireshark.org/Development/LibpcapFileFormat
    196 class PcapReader : public RtpFileReaderImpl {
    197  public:
    198   PcapReader()
    199     : file_(NULL),
    200       swap_pcap_byte_order_(false),
    201 #ifdef WEBRTC_ARCH_BIG_ENDIAN
    202       swap_network_byte_order_(false),
    203 #else
    204       swap_network_byte_order_(true),
    205 #endif
    206       read_buffer_(),
    207       packets_by_ssrc_(),
    208       packets_(),
    209       next_packet_it_() {
    210   }
    211 
    212   virtual ~PcapReader() {
    213     if (file_ != NULL) {
    214       fclose(file_);
    215       file_ = NULL;
    216     }
    217   }
    218 
    219   bool Init(const std::string& filename) OVERRIDE {
    220     return Initialize(filename) == kResultSuccess;
    221   }
    222 
    223   int Initialize(const std::string& filename) {
    224     file_ = fopen(filename.c_str(), "rb");
    225     if (file_ == NULL) {
    226       printf("ERROR: Can't open file: %s\n", filename.c_str());
    227       return kResultFail;
    228     }
    229 
    230     if (ReadGlobalHeader() < 0) {
    231       return kResultFail;
    232     }
    233 
    234     int total_packet_count = 0;
    235     uint32_t stream_start_ms = 0;
    236     int32_t next_packet_pos = ftell(file_);
    237     for (;;) {
    238       TRY_PCAP(fseek(file_, next_packet_pos, SEEK_SET));
    239       int result = ReadPacket(&next_packet_pos, stream_start_ms,
    240                               ++total_packet_count);
    241       if (result == kResultFail) {
    242         break;
    243       } else if (result == kResultSuccess && packets_.size() == 1) {
    244         assert(stream_start_ms == 0);
    245         PacketIterator it = packets_.begin();
    246         stream_start_ms = it->time_offset_ms;
    247         it->time_offset_ms = 0;
    248       }
    249     }
    250 
    251     if (feof(file_) == 0) {
    252       printf("Failed reading file!\n");
    253       return kResultFail;
    254     }
    255 
    256     printf("Total packets in file: %d\n", total_packet_count);
    257     printf("Total RTP/RTCP packets: %d\n", static_cast<int>(packets_.size()));
    258 
    259     for (SsrcMapIterator mit = packets_by_ssrc_.begin();
    260         mit != packets_by_ssrc_.end(); ++mit) {
    261       uint32_t ssrc = mit->first;
    262       const std::vector<uint32_t>& packet_numbers = mit->second;
    263       uint8_t pt = packets_[packet_numbers[0]].rtp_header.payloadType;
    264       printf("SSRC: %08x, %d packets, pt=%d\n", ssrc,
    265              static_cast<int>(packet_numbers.size()), pt);
    266     }
    267 
    268     // TODO(solenberg): Better validation of identified SSRC streams.
    269     //
    270     // Since we're dealing with raw network data here, we will wrongly identify
    271     // some packets as RTP. When these packets are consumed by RtpPlayer, they
    272     // are unlikely to cause issues as they will ultimately be filtered out by
    273     // the RtpRtcp module. However, we should really do better filtering here,
    274     // which we can accomplish in a number of ways, e.g.:
    275     //
    276     // - Verify that the time stamps and sequence numbers for RTP packets are
    277     //   both increasing/decreasing. If they move in different directions, the
    278     //   SSRC is likely bogus and can be dropped. (Normally they should be inc-
    279     //   reasing but we must allow packet reordering).
    280     // - If RTP sequence number is not changing, drop the stream.
    281     // - Can also use srcip:port->dstip:port pairs, assuming few SSRC collisions
    282     //   for up/down streams.
    283 
    284     next_packet_it_ = packets_.begin();
    285     return kResultSuccess;
    286   }
    287 
    288   virtual bool NextPacket(Packet* packet) OVERRIDE {
    289     uint32_t length = Packet::kMaxPacketBufferSize;
    290     if (NextPcap(packet->data, &length, &packet->time_ms) != kResultSuccess)
    291       return false;
    292     packet->length = static_cast<size_t>(length);
    293     return true;
    294   }
    295 
    296   virtual int NextPcap(uint8_t* data, uint32_t* length, uint32_t* time_ms) {
    297     assert(data);
    298     assert(length);
    299     assert(time_ms);
    300 
    301     if (next_packet_it_ == packets_.end()) {
    302       return -1;
    303     }
    304     if (*length < next_packet_it_->payload_length) {
    305       return -1;
    306     }
    307     TRY_PCAP(fseek(file_, next_packet_it_->pos_in_file, SEEK_SET));
    308     TRY_PCAP(Read(data, next_packet_it_->payload_length));
    309     *length = next_packet_it_->payload_length;
    310     *time_ms = next_packet_it_->time_offset_ms;
    311     next_packet_it_++;
    312 
    313     return 0;
    314   }
    315 
    316  private:
    317   // A marker of an RTP packet within the file.
    318   struct RtpPacketMarker {
    319     uint32_t packet_number;   // One-based index (like in WireShark)
    320     uint32_t time_offset_ms;
    321     uint32_t source_ip;
    322     uint32_t dest_ip;
    323     uint16_t source_port;
    324     uint16_t dest_port;
    325     RTPHeader rtp_header;
    326     int32_t pos_in_file;      // Byte offset of payload from start of file.
    327     uint32_t payload_length;
    328   };
    329 
    330   typedef std::vector<RtpPacketMarker>::iterator PacketIterator;
    331   typedef std::map<uint32_t, std::vector<uint32_t> > SsrcMap;
    332   typedef std::map<uint32_t, std::vector<uint32_t> >::iterator SsrcMapIterator;
    333 
    334   int ReadGlobalHeader() {
    335     uint32_t magic;
    336     TRY_PCAP(Read(&magic, false));
    337     if (magic == kPcapBOMSwapOrder) {
    338       swap_pcap_byte_order_ = true;
    339     } else if (magic == kPcapBOMNoSwapOrder) {
    340       swap_pcap_byte_order_ = false;
    341     } else {
    342       return kResultFail;
    343     }
    344 
    345     uint16_t version_major;
    346     uint16_t version_minor;
    347     TRY_PCAP(Read(&version_major, false));
    348     TRY_PCAP(Read(&version_minor, false));
    349     if (version_major != kPcapVersionMajor ||
    350         version_minor != kPcapVersionMinor) {
    351       return kResultFail;
    352     }
    353 
    354     int32_t this_zone;  // GMT to local correction.
    355     uint32_t sigfigs;   // Accuracy of timestamps.
    356     uint32_t snaplen;   // Max length of captured packets, in octets.
    357     uint32_t network;   // Data link type.
    358     TRY_PCAP(Read(&this_zone, false));
    359     TRY_PCAP(Read(&sigfigs, false));
    360     TRY_PCAP(Read(&snaplen, false));
    361     TRY_PCAP(Read(&network, false));
    362 
    363     // Accept only LINKTYPE_NULL and LINKTYPE_ETHERNET.
    364     // See: http://www.tcpdump.org/linktypes.html
    365     if (network != kLinktypeNull && network != kLinktypeEthernet) {
    366       return kResultFail;
    367     }
    368 
    369     return kResultSuccess;
    370   }
    371 
    372   int ReadPacket(int32_t* next_packet_pos, uint32_t stream_start_ms,
    373                  uint32_t number) {
    374     assert(next_packet_pos);
    375 
    376     uint32_t ts_sec;    // Timestamp seconds.
    377     uint32_t ts_usec;   // Timestamp microseconds.
    378     uint32_t incl_len;  // Number of octets of packet saved in file.
    379     uint32_t orig_len;  // Actual length of packet.
    380     TRY_PCAP(Read(&ts_sec, false));
    381     TRY_PCAP(Read(&ts_usec, false));
    382     TRY_PCAP(Read(&incl_len, false));
    383     TRY_PCAP(Read(&orig_len, false));
    384 
    385     *next_packet_pos = ftell(file_) + incl_len;
    386 
    387     RtpPacketMarker marker = {0};
    388     marker.packet_number = number;
    389     marker.time_offset_ms = CalcTimeDelta(ts_sec, ts_usec, stream_start_ms);
    390     TRY_PCAP(ReadPacketHeader(&marker));
    391     marker.pos_in_file = ftell(file_);
    392 
    393     if (marker.payload_length > sizeof(read_buffer_)) {
    394       printf("Packet too large!\n");
    395       return kResultFail;
    396     }
    397     TRY_PCAP(Read(read_buffer_, marker.payload_length));
    398 
    399     RtpUtility::RtpHeaderParser rtp_parser(read_buffer_, marker.payload_length);
    400     if (rtp_parser.RTCP()) {
    401       rtp_parser.ParseRtcp(&marker.rtp_header);
    402       packets_.push_back(marker);
    403     } else {
    404       if (!rtp_parser.Parse(marker.rtp_header, NULL)) {
    405         DEBUG_LOG("Not recognized as RTP/RTCP");
    406         return kResultSkip;
    407       }
    408 
    409       uint32_t ssrc = marker.rtp_header.ssrc;
    410       packets_by_ssrc_[ssrc].push_back(marker.packet_number);
    411       packets_.push_back(marker);
    412     }
    413 
    414     return kResultSuccess;
    415   }
    416 
    417   int ReadPacketHeader(RtpPacketMarker* marker) {
    418     int32_t file_pos = ftell(file_);
    419 
    420     // Check for BSD null/loopback frame header. The header is just 4 bytes in
    421     // native byte order, so we check for both versions as we don't care about
    422     // the header as such and will likely fail reading the IP header if this is
    423     // something else than null/loopback.
    424     uint32_t protocol;
    425     TRY_PCAP(Read(&protocol, true));
    426     if (protocol == kBsdNullLoopback1 || protocol == kBsdNullLoopback2) {
    427       int result = ReadXxpIpHeader(marker);
    428       DEBUG_LOG("Recognized loopback frame");
    429       if (result != kResultSkip) {
    430         return result;
    431       }
    432     }
    433 
    434     TRY_PCAP(fseek(file_, file_pos, SEEK_SET));
    435 
    436     // Check for Ethernet II, IP frame header.
    437     uint16_t type;
    438     TRY_PCAP(Skip(kEthernetIIHeaderMacSkip));  // Source+destination MAC.
    439     TRY_PCAP(Read(&type, true));
    440     if (type == kEthertypeIp) {
    441       int result = ReadXxpIpHeader(marker);
    442       DEBUG_LOG("Recognized ethernet 2 frame");
    443       if (result != kResultSkip) {
    444         return result;
    445       }
    446     }
    447 
    448     return kResultSkip;
    449   }
    450 
    451   uint32_t CalcTimeDelta(uint32_t ts_sec, uint32_t ts_usec, uint32_t start_ms) {
    452     // Round to nearest ms.
    453     uint64_t t2_ms = ((static_cast<uint64_t>(ts_sec) * 1000000) + ts_usec +
    454         500) / 1000;
    455     uint64_t t1_ms = static_cast<uint64_t>(start_ms);
    456     if (t2_ms < t1_ms) {
    457       return 0;
    458     } else {
    459       return t2_ms - t1_ms;
    460     }
    461   }
    462 
    463   int ReadXxpIpHeader(RtpPacketMarker* marker) {
    464     assert(marker);
    465 
    466     uint16_t version;
    467     uint16_t length;
    468     uint16_t id;
    469     uint16_t fragment;
    470     uint16_t protocol;
    471     uint16_t checksum;
    472     TRY_PCAP(Read(&version, true));
    473     TRY_PCAP(Read(&length, true));
    474     TRY_PCAP(Read(&id, true));
    475     TRY_PCAP(Read(&fragment, true));
    476     TRY_PCAP(Read(&protocol, true));
    477     TRY_PCAP(Read(&checksum, true));
    478     TRY_PCAP(Read(&marker->source_ip, true));
    479     TRY_PCAP(Read(&marker->dest_ip, true));
    480 
    481     if (((version >> 12) & 0x000f) != kIpVersion4) {
    482       DEBUG_LOG("IP header is not IPv4");
    483       return kResultSkip;
    484     }
    485 
    486     if (fragment != kFragmentOffsetClear &&
    487         fragment != kFragmentOffsetDoNotFragment) {
    488       DEBUG_LOG("IP fragments cannot be handled");
    489       return kResultSkip;
    490     }
    491 
    492     // Skip remaining fields of IP header.
    493     uint16_t header_length = (version & 0x0f00) >> (8 - 2);
    494     assert(header_length >= kMinIpHeaderLength);
    495     TRY_PCAP(Skip(header_length - kMinIpHeaderLength));
    496 
    497     protocol = protocol & 0x00ff;
    498     if (protocol == kProtocolTcp) {
    499       DEBUG_LOG("TCP packets are not handled");
    500       return kResultSkip;
    501     } else if (protocol == kProtocolUdp) {
    502       uint16_t length;
    503       uint16_t checksum;
    504       TRY_PCAP(Read(&marker->source_port, true));
    505       TRY_PCAP(Read(&marker->dest_port, true));
    506       TRY_PCAP(Read(&length, true));
    507       TRY_PCAP(Read(&checksum, true));
    508       marker->payload_length = length - kUdpHeaderLength;
    509     } else {
    510       DEBUG_LOG("Unknown transport (expected UDP or TCP)");
    511       return kResultSkip;
    512     }
    513 
    514     return kResultSuccess;
    515   }
    516 
    517   int Read(uint32_t* out, bool expect_network_order) {
    518     uint32_t tmp = 0;
    519     if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) {
    520       return kResultFail;
    521     }
    522     if ((!expect_network_order && swap_pcap_byte_order_) ||
    523         (expect_network_order && swap_network_byte_order_)) {
    524       tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) |
    525           ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000);
    526     }
    527     *out = tmp;
    528     return kResultSuccess;
    529   }
    530 
    531   int Read(uint16_t* out, bool expect_network_order) {
    532     uint16_t tmp = 0;
    533     if (fread(&tmp, 1, sizeof(uint16_t), file_) != sizeof(uint16_t)) {
    534       return kResultFail;
    535     }
    536     if ((!expect_network_order && swap_pcap_byte_order_) ||
    537         (expect_network_order && swap_network_byte_order_)) {
    538       tmp = ((tmp >> 8) & 0x00ff) | (tmp << 8);
    539     }
    540     *out = tmp;
    541     return kResultSuccess;
    542   }
    543 
    544   int Read(uint8_t* out, uint32_t count) {
    545     if (fread(out, 1, count, file_) != count) {
    546       return kResultFail;
    547     }
    548     return kResultSuccess;
    549   }
    550 
    551   int Read(int32_t* out, bool expect_network_order) {
    552     int32_t tmp = 0;
    553     if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) {
    554       return kResultFail;
    555     }
    556     if ((!expect_network_order && swap_pcap_byte_order_) ||
    557         (expect_network_order && swap_network_byte_order_)) {
    558       tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) |
    559           ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000);
    560     }
    561     *out = tmp;
    562     return kResultSuccess;
    563   }
    564 
    565   int Skip(uint32_t length) {
    566     if (fseek(file_, length, SEEK_CUR) != 0) {
    567       return kResultFail;
    568     }
    569     return kResultSuccess;
    570   }
    571 
    572   FILE* file_;
    573   bool swap_pcap_byte_order_;
    574   const bool swap_network_byte_order_;
    575   uint8_t read_buffer_[kMaxReadBufferSize];
    576 
    577   SsrcMap packets_by_ssrc_;
    578   std::vector<RtpPacketMarker> packets_;
    579   PacketIterator next_packet_it_;
    580 
    581   DISALLOW_COPY_AND_ASSIGN(PcapReader);
    582 };
    583 
    584 RtpFileReader* RtpFileReader::Create(FileFormat format,
    585                                      const std::string& filename) {
    586   RtpFileReaderImpl* reader = NULL;
    587   switch (format) {
    588     case kPcap:
    589       reader = new PcapReader();
    590       break;
    591     case kRtpDump:
    592       reader = new RtpDumpReader();
    593       break;
    594   }
    595   if (!reader->Init(filename)) {
    596     delete reader;
    597     return NULL;
    598   }
    599   return reader;
    600 }
    601 
    602 }  // namespace test
    603 }  // namespace webrtc
    604