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