blob: cab4065c929bd0d37e6eef95ddc6f2a4e0e6259c [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
pbos@webrtc.org8b062002013-07-12 08:28:10 +000011#include "webrtc/modules/utility/source/rtp_dump_impl.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
pbos@webrtc.org12dc1a32013-08-05 16:22:53 +000013#include <assert.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000014#include <stdio.h>
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000015#include <limits>
niklase@google.com470e71d2011-07-07 08:21:25 +000016
pbos@webrtc.org8b062002013-07-12 08:28:10 +000017#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
asapersson@webrtc.org8b2ec152014-04-11 07:59:43 +000018#include "webrtc/system_wrappers/interface/logging.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000019
20#if defined(_WIN32)
21#include <Windows.h>
22#include <mmsystem.h>
23#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
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
42namespace webrtc {
leozwang@webrtc.org2559cbf2012-02-27 19:18:25 +000043const char RTPFILE_VERSION[] = "1.0";
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +000044const uint32_t MAX_UWORD32 = 0xffffffff;
niklase@google.com470e71d2011-07-07 08:21:25 +000045
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/
49typedef struct
50{
51 // Length of packet, including this header (may be smaller than plen if not
52 // whole packet recorded).
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +000053 uint16_t length;
niklase@google.com470e71d2011-07-07 08:21:25 +000054 // Actual header+payload length for RTP, 0 for RTCP.
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +000055 uint16_t plen;
niklase@google.com470e71d2011-07-07 08:21:25 +000056 // Milliseconds since the start of recording.
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +000057 uint32_t offset;
niklase@google.com470e71d2011-07-07 08:21:25 +000058} rtpDumpPktHdr_t;
59
60RtpDump* RtpDump::CreateRtpDump()
61{
niklase@google.com470e71d2011-07-07 08:21:25 +000062 return new RtpDumpImpl();
63}
64
65void RtpDump::DestroyRtpDump(RtpDump* object)
66{
niklase@google.com470e71d2011-07-07 08:21:25 +000067 delete object;
68}
69
70RtpDumpImpl::RtpDumpImpl()
henrike@webrtc.org105e0712011-12-16 19:53:46 +000071 : _critSect(CriticalSectionWrapper::CreateCriticalSection()),
niklase@google.com470e71d2011-07-07 08:21:25 +000072 _file(*FileWrapper::Create()),
73 _startTime(0)
74{
niklase@google.com470e71d2011-07-07 08:21:25 +000075}
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}
88
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +000089int32_t RtpDumpImpl::Start(const char* fileNameUTF8)
niklase@google.com470e71d2011-07-07 08:21:25 +000090{
niklase@google.com470e71d2011-07-07 08:21:25 +000091
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 {
asapersson@webrtc.org8b2ec152014-04-11 07:59:43 +0000102 LOG(LS_ERROR) << "Failed to open file.";
niklase@google.com470e71d2011-07-07 08:21:25 +0000103 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.
leozwang@webrtc.org2559cbf2012-02-27 19:18:25 +0000110 char magic[16];
niklase@google.com470e71d2011-07-07 08:21:25 +0000111 sprintf(magic, "#!rtpplay%s \n", RTPFILE_VERSION);
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000112 if (_file.WriteText(magic) == -1)
113 {
asapersson@webrtc.org8b2ec152014-04-11 07:59:43 +0000114 LOG(LS_ERROR) << "Error writing to file.";
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000115 return -1;
116 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000117
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.
leozwang@webrtc.org2559cbf2012-02-27 19:18:25 +0000125 char dummyHdr[16];
niklase@google.com470e71d2011-07-07 08:21:25 +0000126 memset(dummyHdr, 0, 16);
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000127 if (!_file.Write(dummyHdr, sizeof(dummyHdr)))
128 {
asapersson@webrtc.org8b2ec152014-04-11 07:59:43 +0000129 LOG(LS_ERROR) << "Error writing to file.";
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000130 return -1;
131 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000132 return 0;
133}
134
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +0000135int32_t RtpDumpImpl::Stop()
niklase@google.com470e71d2011-07-07 08:21:25 +0000136{
niklase@google.com470e71d2011-07-07 08:21:25 +0000137 CriticalSectionScoped lock(_critSect);
138 _file.Flush();
139 _file.CloseFile();
140 return 0;
141}
142
143bool RtpDumpImpl::IsActive() const
144{
145 CriticalSectionScoped lock(_critSect);
146 return _file.Open();
147}
148
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000149int32_t RtpDumpImpl::DumpPacket(const uint8_t* packet, size_t packetLength)
niklase@google.com470e71d2011-07-07 08:21:25 +0000150{
151 CriticalSectionScoped lock(_critSect);
152 if (!IsActive())
153 {
154 return 0;
155 }
156
157 if (packet == NULL)
158 {
159 return -1;
160 }
161
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000162 rtpDumpPktHdr_t hdr;
163 size_t total_size = packetLength + sizeof hdr;
164 if (packetLength < 1 || total_size > std::numeric_limits<uint16_t>::max())
niklase@google.com470e71d2011-07-07 08:21:25 +0000165 {
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
niklase@google.com470e71d2011-07-07 08:21:25 +0000173 // Offset is relative to when recording was started.
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000174 uint32_t offset = GetTimeInMS();
niklase@google.com470e71d2011-07-07 08:21:25 +0000175 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
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000184 hdr.length = RtpDumpHtons((uint16_t)(total_size));
niklase@google.com470e71d2011-07-07 08:21:25 +0000185 if (isRTCP)
186 {
187 hdr.plen = 0;
188 }
189 else
190 {
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +0000191 hdr.plen = RtpDumpHtons((uint16_t)packetLength);
niklase@google.com470e71d2011-07-07 08:21:25 +0000192 }
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000193
194 if (!_file.Write(&hdr, sizeof(hdr)))
195 {
asapersson@webrtc.org8b2ec152014-04-11 07:59:43 +0000196 LOG(LS_ERROR) << "Error writing to file.";
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000197 return -1;
198 }
199 if (!_file.Write(packet, packetLength))
200 {
asapersson@webrtc.org8b2ec152014-04-11 07:59:43 +0000201 LOG(LS_ERROR) << "Error writing to file.";
andrew@webrtc.org5ae19de2011-12-13 22:59:33 +0000202 return -1;
203 }
204
niklase@google.com470e71d2011-07-07 08:21:25 +0000205 return 0;
206}
207
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +0000208bool RtpDumpImpl::RTCP(const uint8_t* packet) const
niklase@google.com470e71d2011-07-07 08:21:25 +0000209{
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +0000210 const uint8_t payloadType = packet[1];
niklase@google.com470e71d2011-07-07 08:21:25 +0000211 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?
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +0000229inline uint32_t RtpDumpImpl::GetTimeInMS() const
niklase@google.com470e71d2011-07-07 08:21:25 +0000230{
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;
niklase@google.com470e71d2011-07-07 08:21:25 +0000241#endif
242}
243
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +0000244inline uint32_t RtpDumpImpl::RtpDumpHtonl(uint32_t x) const
niklase@google.com470e71d2011-07-07 08:21:25 +0000245{
andrew@webrtc.org621df672013-10-22 10:27:23 +0000246#if defined(WEBRTC_ARCH_BIG_ENDIAN)
niklase@google.com470e71d2011-07-07 08:21:25 +0000247 return x;
andrew@webrtc.org621df672013-10-22 10:27:23 +0000248#elif defined(WEBRTC_ARCH_LITTLE_ENDIAN)
niklase@google.com470e71d2011-07-07 08:21:25 +0000249 return (x >> 24) + ((((x >> 16) & 0xFF) << 8) + ((((x >> 8) & 0xFF) << 16) +
250 ((x & 0xFF) << 24)));
niklase@google.com470e71d2011-07-07 08:21:25 +0000251#endif
252}
253
pbos@webrtc.orgc75102e2013-04-09 13:32:55 +0000254inline uint16_t RtpDumpImpl::RtpDumpHtons(uint16_t x) const
niklase@google.com470e71d2011-07-07 08:21:25 +0000255{
andrew@webrtc.org621df672013-10-22 10:27:23 +0000256#if defined(WEBRTC_ARCH_BIG_ENDIAN)
niklase@google.com470e71d2011-07-07 08:21:25 +0000257 return x;
andrew@webrtc.org621df672013-10-22 10:27:23 +0000258#elif defined(WEBRTC_ARCH_LITTLE_ENDIAN)
niklase@google.com470e71d2011-07-07 08:21:25 +0000259 return (x >> 8) + ((x & 0xFF) << 8);
niklase@google.com470e71d2011-07-07 08:21:25 +0000260#endif
261}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000262} // namespace webrtc