265 lines
6.8 KiB
C++
265 lines
6.8 KiB
C++
|
/*
|
||
|
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||
|
*
|
||
|
* Use of this source code is governed by a BSD-style license
|
||
|
* that can be found in the LICENSE file in the root of the source
|
||
|
* tree. An additional intellectual property rights grant can be found
|
||
|
* in the file PATENTS. All contributing project authors may
|
||
|
* be found in the AUTHORS file in the root of the source tree.
|
||
|
*/
|
||
|
|
||
|
#include "rtp_dump_impl.h"
|
||
|
|
||
|
#include <cassert>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#include "critical_section_wrapper.h"
|
||
|
#include "trace.h"
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
#include <Windows.h>
|
||
|
#include <mmsystem.h>
|
||
|
#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
|
||
|
#include <string.h>
|
||
|
#include <sys/time.h>
|
||
|
#include <time.h>
|
||
|
#endif
|
||
|
|
||
|
#if (defined(_DEBUG) && defined(_WIN32))
|
||
|
#define DEBUG_PRINT(expr) OutputDebugString(##expr)
|
||
|
#define DEBUG_PRINTP(expr, p) \
|
||
|
{ \
|
||
|
char msg[128]; \
|
||
|
sprintf(msg, ##expr, p); \
|
||
|
OutputDebugString(msg); \
|
||
|
}
|
||
|
#else
|
||
|
#define DEBUG_PRINT(expr) ((void)0)
|
||
|
#define DEBUG_PRINTP(expr,p) ((void)0)
|
||
|
#endif // defined(_DEBUG) && defined(_WIN32)
|
||
|
|
||
|
namespace webrtc {
|
||
|
const WebRtc_Word8* RTPFILE_VERSION = "1.0";
|
||
|
const WebRtc_UWord32 MAX_UWORD32 = 0xffffffff;
|
||
|
|
||
|
// This stucture is specified in the rtpdump documentation.
|
||
|
// This struct corresponds to RD_packet_t in
|
||
|
// http://www.cs.columbia.edu/irt/software/rtptools/
|
||
|
typedef struct
|
||
|
{
|
||
|
// Length of packet, including this header (may be smaller than plen if not
|
||
|
// whole packet recorded).
|
||
|
WebRtc_UWord16 length;
|
||
|
// Actual header+payload length for RTP, 0 for RTCP.
|
||
|
WebRtc_UWord16 plen;
|
||
|
// Milliseconds since the start of recording.
|
||
|
WebRtc_UWord32 offset;
|
||
|
} rtpDumpPktHdr_t;
|
||
|
|
||
|
RtpDump* RtpDump::CreateRtpDump()
|
||
|
{
|
||
|
WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "CreateRtpDump()");
|
||
|
return new RtpDumpImpl();
|
||
|
}
|
||
|
|
||
|
void RtpDump::DestroyRtpDump(RtpDump* object)
|
||
|
{
|
||
|
WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "DestroyRtpDump()");
|
||
|
delete object;
|
||
|
}
|
||
|
|
||
|
RtpDumpImpl::RtpDumpImpl()
|
||
|
: _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
|
||
|
_file(*FileWrapper::Create()),
|
||
|
_startTime(0)
|
||
|
{
|
||
|
WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s created", __FUNCTION__);
|
||
|
}
|
||
|
|
||
|
RtpDump::~RtpDump()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
RtpDumpImpl::~RtpDumpImpl()
|
||
|
{
|
||
|
_file.Flush();
|
||
|
_file.CloseFile();
|
||
|
delete &_file;
|
||
|
delete &_critSect;
|
||
|
WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s deleted", __FUNCTION__);
|
||
|
}
|
||
|
|
||
|
WebRtc_Word32 RtpDumpImpl::Start(const WebRtc_Word8* fileNameUTF8)
|
||
|
{
|
||
|
WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "Start()");
|
||
|
|
||
|
if (fileNameUTF8 == NULL)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
CriticalSectionScoped lock(_critSect);
|
||
|
_file.Flush();
|
||
|
_file.CloseFile();
|
||
|
if (_file.OpenFile(fileNameUTF8, false, false, false) == -1)
|
||
|
{
|
||
|
WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
|
||
|
"failed to open the specified file");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// Store start of RTP dump (to be used for offset calculation later).
|
||
|
_startTime = GetTimeInMS();
|
||
|
|
||
|
// All rtp dump files start with #!rtpplay.
|
||
|
WebRtc_Word8 magic[16];
|
||
|
sprintf(magic, "#!rtpplay%s \n", RTPFILE_VERSION);
|
||
|
_file.WriteText(magic);
|
||
|
|
||
|
// The header according to the rtpdump documentation is sizeof(RD_hdr_t)
|
||
|
// which is 8 + 4 + 2 = 14 bytes for 32-bit architecture (and 22 bytes on
|
||
|
// 64-bit architecture). However, Wireshark use 16 bytes for the header
|
||
|
// regardless of if the binary is 32-bit or 64-bit. Go by the same approach
|
||
|
// as Wireshark since it makes more sense.
|
||
|
// http://wiki.wireshark.org/rtpdump explains that an additional 2 bytes
|
||
|
// of padding should be added to the header.
|
||
|
WebRtc_Word8 dummyHdr[16];
|
||
|
memset(dummyHdr, 0, 16);
|
||
|
_file.Write(dummyHdr, sizeof(dummyHdr));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
WebRtc_Word32 RtpDumpImpl::Stop()
|
||
|
{
|
||
|
WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "Stop()");
|
||
|
CriticalSectionScoped lock(_critSect);
|
||
|
_file.Flush();
|
||
|
_file.CloseFile();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool RtpDumpImpl::IsActive() const
|
||
|
{
|
||
|
CriticalSectionScoped lock(_critSect);
|
||
|
return _file.Open();
|
||
|
}
|
||
|
|
||
|
WebRtc_Word32 RtpDumpImpl::DumpPacket(const WebRtc_UWord8* packet,
|
||
|
WebRtc_UWord16 packetLength)
|
||
|
{
|
||
|
CriticalSectionScoped lock(_critSect);
|
||
|
if (!IsActive())
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (packet == NULL)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (packetLength < 1)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// If the packet doesn't contain a valid RTCP header the packet will be
|
||
|
// considered RTP (without further verification).
|
||
|
bool isRTCP = RTCP(packet);
|
||
|
|
||
|
rtpDumpPktHdr_t hdr;
|
||
|
WebRtc_UWord32 offset;
|
||
|
|
||
|
// Offset is relative to when recording was started.
|
||
|
offset = GetTimeInMS();
|
||
|
if (offset < _startTime)
|
||
|
{
|
||
|
// Compensate for wraparound.
|
||
|
offset += MAX_UWORD32 - _startTime + 1;
|
||
|
} else {
|
||
|
offset -= _startTime;
|
||
|
}
|
||
|
hdr.offset = RtpDumpHtonl(offset);
|
||
|
|
||
|
hdr.length = RtpDumpHtons((WebRtc_UWord16)(packetLength + sizeof(hdr)));
|
||
|
if (isRTCP)
|
||
|
{
|
||
|
hdr.plen = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hdr.plen = RtpDumpHtons((WebRtc_UWord16)packetLength);
|
||
|
}
|
||
|
_file.Write(&hdr, sizeof(hdr));
|
||
|
_file.Write(packet, packetLength);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool RtpDumpImpl::RTCP(const WebRtc_UWord8* packet) const
|
||
|
{
|
||
|
const WebRtc_UWord8 payloadType = packet[1];
|
||
|
bool is_rtcp = false;
|
||
|
|
||
|
switch(payloadType)
|
||
|
{
|
||
|
case 192:
|
||
|
is_rtcp = true;
|
||
|
break;
|
||
|
case 193: case 195:
|
||
|
break;
|
||
|
case 200: case 201: case 202: case 203:
|
||
|
case 204: case 205: case 206: case 207:
|
||
|
is_rtcp = true;
|
||
|
break;
|
||
|
}
|
||
|
return is_rtcp;
|
||
|
}
|
||
|
|
||
|
// TODO (hellner): why is TickUtil not used here?
|
||
|
inline WebRtc_UWord32 RtpDumpImpl::GetTimeInMS() const
|
||
|
{
|
||
|
#if defined(_WIN32)
|
||
|
return timeGetTime();
|
||
|
#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
|
||
|
struct timeval tv;
|
||
|
struct timezone tz;
|
||
|
unsigned long val;
|
||
|
|
||
|
gettimeofday(&tv, &tz);
|
||
|
val = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||
|
return val;
|
||
|
#else
|
||
|
#error Either _WIN32 or LINUX or WEBRTC_MAC has to be defined!
|
||
|
assert(false);
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline WebRtc_UWord32 RtpDumpImpl::RtpDumpHtonl(WebRtc_UWord32 x) const
|
||
|
{
|
||
|
#if defined(WEBRTC_BIG_ENDIAN)
|
||
|
return x;
|
||
|
#elif defined(WEBRTC_LITTLE_ENDIAN)
|
||
|
return (x >> 24) + ((((x >> 16) & 0xFF) << 8) + ((((x >> 8) & 0xFF) << 16) +
|
||
|
((x & 0xFF) << 24)));
|
||
|
#else
|
||
|
#error Either WEBRTC_BIG_ENDIAN or WEBRTC_LITTLE_ENDIAN has to be defined!
|
||
|
assert(false);
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline WebRtc_UWord16 RtpDumpImpl::RtpDumpHtons(WebRtc_UWord16 x) const
|
||
|
{
|
||
|
#if defined(WEBRTC_BIG_ENDIAN)
|
||
|
return x;
|
||
|
#elif defined(WEBRTC_LITTLE_ENDIAN)
|
||
|
return (x >> 8) + ((x & 0xFF) << 8);
|
||
|
#else
|
||
|
#error Either WEBRTC_BIG_ENDIAN or WEBRTC_LITTLE_ENDIAN has to be defined!
|
||
|
assert(false);
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
} // namespace webrtc
|