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
CreateRtpDump()59 RtpDump* RtpDump::CreateRtpDump()
60 {
61 return new RtpDumpImpl();
62 }
63
DestroyRtpDump(RtpDump * object)64 void RtpDump::DestroyRtpDump(RtpDump* object)
65 {
66 delete object;
67 }
68
RtpDumpImpl()69 RtpDumpImpl::RtpDumpImpl()
70 : _critSect(CriticalSectionWrapper::CreateCriticalSection()),
71 _file(*FileWrapper::Create()),
72 _startTime(0)
73 {
74 }
75
~RtpDump()76 RtpDump::~RtpDump()
77 {
78 }
79
~RtpDumpImpl()80 RtpDumpImpl::~RtpDumpImpl()
81 {
82 _file.Flush();
83 _file.CloseFile();
84 delete &_file;
85 delete _critSect;
86 }
87
Start(const char * fileNameUTF8)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
Stop()134 int32_t RtpDumpImpl::Stop()
135 {
136 CriticalSectionScoped lock(_critSect);
137 _file.Flush();
138 _file.CloseFile();
139 return 0;
140 }
141
IsActive() const142 bool RtpDumpImpl::IsActive() const
143 {
144 CriticalSectionScoped lock(_critSect);
145 return _file.Open();
146 }
147
DumpPacket(const uint8_t * packet,uint16_t packetLength)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
RTCP(const uint8_t * packet) const208 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?
GetTimeInMS() const229 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
RtpDumpHtonl(uint32_t x) const244 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
RtpDumpHtons(uint16_t x) const254 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