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
41namespace webrtc {
42const char RTPFILE_VERSION[] = "1.0";
43const 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/
48typedef 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
59RtpDump* RtpDump::CreateRtpDump()
60{
61    return new RtpDumpImpl();
62}
63
64void RtpDump::DestroyRtpDump(RtpDump* object)
65{
66    delete object;
67}
68
69RtpDumpImpl::RtpDumpImpl()
70    : _critSect(CriticalSectionWrapper::CreateCriticalSection()),
71      _file(*FileWrapper::Create()),
72      _startTime(0)
73{
74}
75
76RtpDump::~RtpDump()
77{
78}
79
80RtpDumpImpl::~RtpDumpImpl()
81{
82    _file.Flush();
83    _file.CloseFile();
84    delete &_file;
85    delete _critSect;
86}
87
88int32_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
134int32_t RtpDumpImpl::Stop()
135{
136    CriticalSectionScoped lock(_critSect);
137    _file.Flush();
138    _file.CloseFile();
139    return 0;
140}
141
142bool RtpDumpImpl::IsActive() const
143{
144    CriticalSectionScoped lock(_critSect);
145    return _file.Open();
146}
147
148int32_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
208bool 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?
229inline 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
244inline 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
254inline 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