blob: 69a52ecc5789f04acb06f580a28d2646acc3caf8 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
xians@webrtc.org6bde7a82012-02-20 08:39:25 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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 "rtp_dump_impl.h"
12
13#include <cassert>
14#include <stdio.h>
15
16#include "critical_section_wrapper.h"
17#include "trace.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 {
leozwang@webrtc.org2559cbf2012-02-27 19:18:25 +000042const char RTPFILE_VERSION[] = "1.0";
niklase@google.com470e71d2011-07-07 08:21:25 +000043const WebRtc_UWord32 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 WebRtc_UWord16 length;
53 // Actual header+payload length for RTP, 0 for RTCP.
54 WebRtc_UWord16 plen;
55 // Milliseconds since the start of recording.
56 WebRtc_UWord32 offset;
57} rtpDumpPktHdr_t;
58
59RtpDump* RtpDump::CreateRtpDump()
60{
niklase@google.com470e71d2011-07-07 08:21:25 +000061 return new RtpDumpImpl();
62}
63
64void RtpDump::DestroyRtpDump(RtpDump* object)
65{
niklase@google.com470e71d2011-07-07 08:21:25 +000066 delete object;
67}
68
69RtpDumpImpl::RtpDumpImpl()
henrike@webrtc.org105e0712011-12-16 19:53:46 +000070 : _critSect(CriticalSectionWrapper::CreateCriticalSection()),
niklase@google.com470e71d2011-07-07 08:21:25 +000071 _file(*FileWrapper::Create()),
72 _startTime(0)
73{
74 WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s created", __FUNCTION__);
75}
76
77RtpDump::~RtpDump()
78{
79}
80
81RtpDumpImpl::~RtpDumpImpl()
82{
83 _file.Flush();
84 _file.CloseFile();
85 delete &_file;
henrike@webrtc.org105e0712011-12-16 19:53:46 +000086 delete _critSect;
niklase@google.com470e71d2011-07-07 08:21:25 +000087 WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s deleted", __FUNCTION__);
88}
89
leozwang@webrtc.org2559cbf2012-02-27 19:18:25 +000090WebRtc_Word32 RtpDumpImpl::Start(const char* fileNameUTF8)
niklase@google.com470e71d2011-07-07 08:21:25 +000091{
niklase@google.com470e71d2011-07-07 08:21:25 +000092
93 if (fileNameUTF8 == NULL)
94 {
95 return -1;
96 }
97
98 CriticalSectionScoped lock(_critSect);
99 _file.Flush();
100 _file.CloseFile();
101 if (_file.OpenFile(fileNameUTF8, false, false, false) == -1)
102 {
103 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
104 "failed to open the specified file");
105 return -1;
106 }
107
108 // Store start of RTP dump (to be used for offset calculation later).
109 _startTime = GetTimeInMS();
110
111 // All rtp dump files start with #!rtpplay.
leozwang@webrtc.org2559cbf2012-02-27 19:18:25 +0000112 char magic[16];
niklase@google.com470e71d2011-07-07 08:21:25 +0000113 sprintf(magic, "#!rtpplay%s \n", RTPFILE_VERSION);
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000114 if (_file.WriteText(magic) == -1)
115 {
116 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
117 "error writing to file");
118 return -1;
119 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000120
121 // The header according to the rtpdump documentation is sizeof(RD_hdr_t)
122 // which is 8 + 4 + 2 = 14 bytes for 32-bit architecture (and 22 bytes on
123 // 64-bit architecture). However, Wireshark use 16 bytes for the header
124 // regardless of if the binary is 32-bit or 64-bit. Go by the same approach
125 // as Wireshark since it makes more sense.
126 // http://wiki.wireshark.org/rtpdump explains that an additional 2 bytes
127 // of padding should be added to the header.
leozwang@webrtc.org2559cbf2012-02-27 19:18:25 +0000128 char dummyHdr[16];
niklase@google.com470e71d2011-07-07 08:21:25 +0000129 memset(dummyHdr, 0, 16);
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000130 if (!_file.Write(dummyHdr, sizeof(dummyHdr)))
131 {
132 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
133 "error writing to file");
134 return -1;
135 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000136 return 0;
137}
138
139WebRtc_Word32 RtpDumpImpl::Stop()
140{
niklase@google.com470e71d2011-07-07 08:21:25 +0000141 CriticalSectionScoped lock(_critSect);
142 _file.Flush();
143 _file.CloseFile();
144 return 0;
145}
146
147bool RtpDumpImpl::IsActive() const
148{
149 CriticalSectionScoped lock(_critSect);
150 return _file.Open();
151}
152
153WebRtc_Word32 RtpDumpImpl::DumpPacket(const WebRtc_UWord8* packet,
154 WebRtc_UWord16 packetLength)
155{
156 CriticalSectionScoped lock(_critSect);
157 if (!IsActive())
158 {
159 return 0;
160 }
161
162 if (packet == NULL)
163 {
164 return -1;
165 }
166
167 if (packetLength < 1)
168 {
169 return -1;
170 }
171
172 // If the packet doesn't contain a valid RTCP header the packet will be
173 // considered RTP (without further verification).
174 bool isRTCP = RTCP(packet);
175
176 rtpDumpPktHdr_t hdr;
177 WebRtc_UWord32 offset;
178
179 // Offset is relative to when recording was started.
180 offset = GetTimeInMS();
181 if (offset < _startTime)
182 {
183 // Compensate for wraparound.
184 offset += MAX_UWORD32 - _startTime + 1;
185 } else {
186 offset -= _startTime;
187 }
188 hdr.offset = RtpDumpHtonl(offset);
189
190 hdr.length = RtpDumpHtons((WebRtc_UWord16)(packetLength + sizeof(hdr)));
191 if (isRTCP)
192 {
193 hdr.plen = 0;
194 }
195 else
196 {
197 hdr.plen = RtpDumpHtons((WebRtc_UWord16)packetLength);
198 }
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000199
200 if (!_file.Write(&hdr, sizeof(hdr)))
201 {
202 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
203 "error writing to file");
204 return -1;
205 }
206 if (!_file.Write(packet, packetLength))
207 {
208 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
209 "error writing to file");
210 return -1;
211 }
212
niklase@google.com470e71d2011-07-07 08:21:25 +0000213 return 0;
214}
215
216bool RtpDumpImpl::RTCP(const WebRtc_UWord8* packet) const
217{
218 const WebRtc_UWord8 payloadType = packet[1];
219 bool is_rtcp = false;
220
221 switch(payloadType)
222 {
223 case 192:
224 is_rtcp = true;
225 break;
226 case 193: case 195:
227 break;
228 case 200: case 201: case 202: case 203:
229 case 204: case 205: case 206: case 207:
230 is_rtcp = true;
231 break;
232 }
233 return is_rtcp;
234}
235
236// TODO (hellner): why is TickUtil not used here?
237inline WebRtc_UWord32 RtpDumpImpl::GetTimeInMS() const
238{
239#if defined(_WIN32)
240 return timeGetTime();
241#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
242 struct timeval tv;
243 struct timezone tz;
244 unsigned long val;
245
246 gettimeofday(&tv, &tz);
247 val = tv.tv_sec * 1000 + tv.tv_usec / 1000;
248 return val;
249#else
250 #error Either _WIN32 or LINUX or WEBRTC_MAC has to be defined!
251 assert(false);
252 return 0;
253#endif
254}
255
256inline WebRtc_UWord32 RtpDumpImpl::RtpDumpHtonl(WebRtc_UWord32 x) const
257{
258#if defined(WEBRTC_BIG_ENDIAN)
259 return x;
260#elif defined(WEBRTC_LITTLE_ENDIAN)
261 return (x >> 24) + ((((x >> 16) & 0xFF) << 8) + ((((x >> 8) & 0xFF) << 16) +
262 ((x & 0xFF) << 24)));
263#else
264#error Either WEBRTC_BIG_ENDIAN or WEBRTC_LITTLE_ENDIAN has to be defined!
265 assert(false);
266 return 0;
267#endif
268}
269
270inline WebRtc_UWord16 RtpDumpImpl::RtpDumpHtons(WebRtc_UWord16 x) const
271{
272#if defined(WEBRTC_BIG_ENDIAN)
273 return x;
274#elif defined(WEBRTC_LITTLE_ENDIAN)
275 return (x >> 8) + ((x & 0xFF) << 8);
276#else
277 #error Either WEBRTC_BIG_ENDIAN or WEBRTC_LITTLE_ENDIAN has to be defined!
278 assert(false);
279 return 0;
280#endif
281}
282} // namespace webrtc