Home | History | Annotate | Download | only in source
      1 /*
      2  *  Copyright (c) 2012 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/modules/utility/source/rtp_dump_impl.h"
     12 
     13 #include <assert.h>
     14 #include <stdio.h>
     15 
     16 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
     17 #include "webrtc/system_wrappers/interface/logging.h"
     18 
     19 #if defined(_WIN32)
     20 #include <Windows.h>
     21 #include <mmsystem.h>
     22 #elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
     23 #include <string.h>
     24 #include <sys/time.h>
     25 #include <time.h>
     26 #endif
     27 
     28 #if (defined(_DEBUG) && defined(_WIN32))
     29 #define DEBUG_PRINT(expr)   OutputDebugString(##expr)
     30 #define DEBUG_PRINTP(expr, p)   \
     31 {                               \
     32     char msg[128];              \
     33     sprintf(msg, ##expr, p);    \
     34     OutputDebugString(msg);     \
     35 }
     36 #else
     37 #define DEBUG_PRINT(expr)    ((void)0)
     38 #define DEBUG_PRINTP(expr,p) ((void)0)
     39 #endif  // defined(_DEBUG) && defined(_WIN32)
     40 
     41 namespace webrtc {
     42 const char RTPFILE_VERSION[] = "1.0";
     43 const uint32_t MAX_UWORD32 = 0xffffffff;
     44 
     45 // This stucture is specified in the rtpdump documentation.
     46 // This struct corresponds to RD_packet_t in
     47 // http://www.cs.columbia.edu/irt/software/rtptools/
     48 typedef struct
     49 {
     50     // Length of packet, including this header (may be smaller than plen if not
     51     // whole packet recorded).
     52     uint16_t length;
     53     // Actual header+payload length for RTP, 0 for RTCP.
     54     uint16_t plen;
     55     // Milliseconds since the start of recording.
     56     uint32_t offset;
     57 } rtpDumpPktHdr_t;
     58 
     59 RtpDump* RtpDump::CreateRtpDump()
     60 {
     61     return new RtpDumpImpl();
     62 }
     63 
     64 void RtpDump::DestroyRtpDump(RtpDump* object)
     65 {
     66     delete object;
     67 }
     68 
     69 RtpDumpImpl::RtpDumpImpl()
     70     : _critSect(CriticalSectionWrapper::CreateCriticalSection()),
     71       _file(*FileWrapper::Create()),
     72       _startTime(0)
     73 {
     74 }
     75 
     76 RtpDump::~RtpDump()
     77 {
     78 }
     79 
     80 RtpDumpImpl::~RtpDumpImpl()
     81 {
     82     _file.Flush();
     83     _file.CloseFile();
     84     delete &_file;
     85     delete _critSect;
     86 }
     87 
     88 int32_t RtpDumpImpl::Start(const char* fileNameUTF8)
     89 {
     90 
     91     if (fileNameUTF8 == NULL)
     92     {
     93         return -1;
     94     }
     95 
     96     CriticalSectionScoped lock(_critSect);
     97     _file.Flush();
     98     _file.CloseFile();
     99     if (_file.OpenFile(fileNameUTF8, false, false, false) == -1)
    100     {
    101         LOG(LS_ERROR) << "Failed to open file.";
    102         return -1;
    103     }
    104 
    105     // Store start of RTP dump (to be used for offset calculation later).
    106     _startTime = GetTimeInMS();
    107 
    108     // All rtp dump files start with #!rtpplay.
    109     char magic[16];
    110     sprintf(magic, "#!rtpplay%s \n", RTPFILE_VERSION);
    111     if (_file.WriteText(magic) == -1)
    112     {
    113         LOG(LS_ERROR) << "Error writing to file.";
    114         return -1;
    115     }
    116 
    117     // The header according to the rtpdump documentation is sizeof(RD_hdr_t)
    118     // which is 8 + 4 + 2 = 14 bytes for 32-bit architecture (and 22 bytes on
    119     // 64-bit architecture). However, Wireshark use 16 bytes for the header
    120     // regardless of if the binary is 32-bit or 64-bit. Go by the same approach
    121     // as Wireshark since it makes more sense.
    122     // http://wiki.wireshark.org/rtpdump explains that an additional 2 bytes
    123     // of padding should be added to the header.
    124     char dummyHdr[16];
    125     memset(dummyHdr, 0, 16);
    126     if (!_file.Write(dummyHdr, sizeof(dummyHdr)))
    127     {
    128         LOG(LS_ERROR) << "Error writing to file.";
    129         return -1;
    130     }
    131     return 0;
    132 }
    133 
    134 int32_t RtpDumpImpl::Stop()
    135 {
    136     CriticalSectionScoped lock(_critSect);
    137     _file.Flush();
    138     _file.CloseFile();
    139     return 0;
    140 }
    141 
    142 bool RtpDumpImpl::IsActive() const
    143 {
    144     CriticalSectionScoped lock(_critSect);
    145     return _file.Open();
    146 }
    147 
    148 int32_t RtpDumpImpl::DumpPacket(const uint8_t* packet, uint16_t packetLength)
    149 {
    150     CriticalSectionScoped lock(_critSect);
    151     if (!IsActive())
    152     {
    153         return 0;
    154     }
    155 
    156     if (packet == NULL)
    157     {
    158         return -1;
    159     }
    160 
    161     if (packetLength < 1)
    162     {
    163         return -1;
    164     }
    165 
    166     // If the packet doesn't contain a valid RTCP header the packet will be
    167     // considered RTP (without further verification).
    168     bool isRTCP = RTCP(packet);
    169 
    170     rtpDumpPktHdr_t hdr;
    171     uint32_t offset;
    172 
    173     // Offset is relative to when recording was started.
    174     offset = GetTimeInMS();
    175     if (offset < _startTime)
    176     {
    177         // Compensate for wraparound.
    178         offset += MAX_UWORD32 - _startTime + 1;
    179     } else {
    180         offset -= _startTime;
    181     }
    182     hdr.offset = RtpDumpHtonl(offset);
    183 
    184     hdr.length = RtpDumpHtons((uint16_t)(packetLength + sizeof(hdr)));
    185     if (isRTCP)
    186     {
    187         hdr.plen = 0;
    188     }
    189     else
    190     {
    191         hdr.plen = RtpDumpHtons((uint16_t)packetLength);
    192     }
    193 
    194     if (!_file.Write(&hdr, sizeof(hdr)))
    195     {
    196         LOG(LS_ERROR) << "Error writing to file.";
    197         return -1;
    198     }
    199     if (!_file.Write(packet, packetLength))
    200     {
    201         LOG(LS_ERROR) << "Error writing to file.";
    202         return -1;
    203     }
    204 
    205     return 0;
    206 }
    207 
    208 bool RtpDumpImpl::RTCP(const uint8_t* packet) const
    209 {
    210     const uint8_t payloadType = packet[1];
    211     bool is_rtcp = false;
    212 
    213     switch(payloadType)
    214     {
    215     case 192:
    216         is_rtcp = true;
    217         break;
    218     case 193: case 195:
    219         break;
    220     case 200: case 201: case 202: case 203:
    221     case 204: case 205: case 206: case 207:
    222         is_rtcp = true;
    223         break;
    224     }
    225     return is_rtcp;
    226 }
    227 
    228 // TODO (hellner): why is TickUtil not used here?
    229 inline uint32_t RtpDumpImpl::GetTimeInMS() const
    230 {
    231 #if defined(_WIN32)
    232     return timeGetTime();
    233 #elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
    234     struct timeval tv;
    235     struct timezone tz;
    236     unsigned long val;
    237 
    238     gettimeofday(&tv, &tz);
    239     val = tv.tv_sec * 1000 + tv.tv_usec / 1000;
    240     return val;
    241 #endif
    242 }
    243 
    244 inline uint32_t RtpDumpImpl::RtpDumpHtonl(uint32_t x) const
    245 {
    246 #if defined(WEBRTC_ARCH_BIG_ENDIAN)
    247     return x;
    248 #elif defined(WEBRTC_ARCH_LITTLE_ENDIAN)
    249     return (x >> 24) + ((((x >> 16) & 0xFF) << 8) + ((((x >> 8) & 0xFF) << 16) +
    250                                                      ((x & 0xFF) << 24)));
    251 #endif
    252 }
    253 
    254 inline uint16_t RtpDumpImpl::RtpDumpHtons(uint16_t x) const
    255 {
    256 #if defined(WEBRTC_ARCH_BIG_ENDIAN)
    257     return x;
    258 #elif defined(WEBRTC_ARCH_LITTLE_ENDIAN)
    259     return (x >> 8) + ((x & 0xFF) << 8);
    260 #endif
    261 }
    262 }  // namespace webrtc
    263