• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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