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 59 RtpDump* RtpDump::CreateRtpDump() 60 { 61 return new RtpDumpImpl(); 62 } 63 64 void RtpDump::DestroyRtpDump(RtpDump* object) 65 { 66 delete object; 67 } 68 69 RtpDumpImpl::RtpDumpImpl() 70 : _critSect(CriticalSectionWrapper::CreateCriticalSection()), 71 _file(*FileWrapper::Create()), 72 _startTime(0) 73 { 74 } 75 76 RtpDump::~RtpDump() 77 { 78 } 79 80 RtpDumpImpl::~RtpDumpImpl() 81 { 82 _file.Flush(); 83 _file.CloseFile(); 84 delete &_file; 85 delete _critSect; 86 } 87 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 134 int32_t RtpDumpImpl::Stop() 135 { 136 CriticalSectionScoped lock(_critSect); 137 _file.Flush(); 138 _file.CloseFile(); 139 return 0; 140 } 141 142 bool RtpDumpImpl::IsActive() const 143 { 144 CriticalSectionScoped lock(_critSect); 145 return _file.Open(); 146 } 147 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 208 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? 229 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 244 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 254 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