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