#ifndef _PCAP_PARSE_H
#define _PCAP_PARSE_H
#include <stdio.h>
#include <map>
#include <list>
#include <memory>
using std::map;
using std::shared_ptr;
#define MAX_FILENAME_PATH 260
enum { kRtpCsrcSize = 15 };
struct RTPHeaderExtension {
RTPHeaderExtension()
: hasTransmissionTimeOffset(false),
transmissionTimeOffset(0),
hasAbsoluteSendTime(false),
absoluteSendTime(0),
hasAudioLevel(false),
audioLevel(0) {}
bool hasTransmissionTimeOffset;
int32_t transmissionTimeOffset;
bool hasAbsoluteSendTime;
uint32_t absoluteSendTime;
bool hasAudioLevel;
uint8_t audioLevel;
};
struct RTPHeader {
RTPHeader()
: markerBit(false),
payloadType(0),
sequenceNumber(0),
timestamp(0),
ssrc(0),
numCSRCs(0),
paddingLength(0),
headerLength(0),
payload_type_frequency(0),
extension() {
memset(&arrOfCSRCs, 0, sizeof(arrOfCSRCs));
}
bool markerBit;
uint8_t payloadType;
uint16_t sequenceNumber;
uint32_t timestamp;
uint32_t ssrc;
uint8_t numCSRCs;
uint32_t arrOfCSRCs[kRtpCsrcSize];
uint8_t paddingLength;
uint16_t headerLength;
int payload_type_frequency;
RTPHeaderExtension extension;
};
enum ePktType
{
ePktNaked,
ePkt264,
ePkt265,
ePktPsVideo,
ePktPsAudio,
};
class CPcapParse
{
public:
CPcapParse();
virtual ~CPcapParse();
int init(const char *pchPcapFile);
int copyPktData(char *pchData, size_t bufLen, size_t &dataLen, uint64_t *pMs);
void reset();
private:
typedef struct pcapFileHeader
{
unsigned char magic[4];
unsigned short version_major;
unsigned short version_minor;
int thiszone;
unsigned int sigfigs;
unsigned int snaplen;
unsigned int linktype;
} pcapFileHeader_t;
pcapFileHeader_t m_FileHeader;
typedef struct pcapPkthdr
{
unsigned int seconds;
unsigned int u_seconds;
unsigned int caplen;
unsigned int len;
} pcapPkthdr_t;
FILE *m_fp;
size_t m_sizePktOffset;
};
bool rtpParse(RTPHeader& header, const void *p, size_t sizeLen);
class rtpPacket
{
public:
rtpPacket();
virtual ~rtpPacket();
int init(const char *pchBuf, size_t sizeLen, uint64_t ts);
const char *getHeader() const { return &m_buf[0]; }
size_t getPktLen() const { return m_len; }
uint64_t getTs(){ return m_ts; }
private:
char m_buf[2000];
size_t m_len;
uint64_t m_ts;
};
class CRtpManage
{
public:
CRtpManage();
virtual ~CRtpManage();
int init(const char *inFile);
int netPlay(const int8_t * remoteAddr, uint16_t remotePort, uint16_t localPort);
void getMap(std::map<unsigned int, shared_ptr<rtpPacket> >& omapRtp);
void getList(std::list<shared_ptr<rtpPacket> >& olistRtp);
void reset();
private:
int proc();
typedef map<unsigned int, shared_ptr<rtpPacket> > mapRtp;
char m_inFile[MAX_FILENAME_PATH];
CPcapParse m_pcap;
mapRtp m_mapRtp;
std::list<shared_ptr<rtpPacket>> m_listRtp;
};
#endif
#include <stdio.h>
#ifdef WIN32
#include <winsock2.h>
#endif
#include <cstring>
#include "pcapParse.h"
#ifdef WIN32
#pragma comment(lib,"ws2_32.lib")
typedef int socklen_t;
#else
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#define SOCKET int32_t
#define SOCKET_ERROR (-1)
#define INVALID_SOCKET (-1)
#endif
#ifndef WIN32
int closesocket(int sock)
{
(void)shutdown(sock, SHUT_RDWR);
return close(sock);
}
#endif
using std::shared_ptr;
using std::make_shared;
enum {
kRtcpExpectedVersion = 2,
kRtcpMinHeaderLength = 4,
kRtcpMinParseLength = 8,
kRtpExpectedVersion = 2,
kRtpMinParseLength = 12
};
bool rtpParse(RTPHeader& header, const void *p, size_t sizeLen)
{
const uint8_t* const _ptrRTPDataBegin = (const uint8_t* const)p;
const uint8_t* const _ptrRTPDataEnd = _ptrRTPDataBegin + sizeLen;
const ptrdiff_t length = _ptrRTPDataEnd - _ptrRTPDataBegin;
if (length < kRtpMinParseLength) {
return false;
}
const uint8_t V = _ptrRTPDataBegin[0] >> 6;
const bool P = ((_ptrRTPDataBegin[0] & 0x20) == 0) ? false : true;
const bool X = ((_ptrRTPDataBegin[0] & 0x10) == 0) ? false : true;
const uint8_t CC = _ptrRTPDataBegin[0] & 0x0f;
const bool M = ((_ptrRTPDataBegin[1] & 0x80) == 0) ? false : true;
const uint8_t PT = _ptrRTPDataBegin[1] & 0x7f;
const uint16_t sequenceNumber = (_ptrRTPDataBegin[2] << 8) +
_ptrRTPDataBegin[3];
const uint8_t* ptr = &_ptrRTPDataBegin[4];
uint32_t RTPTimestamp = *ptr++ << 24;
RTPTimestamp += *ptr++ << 16;
RTPTimestamp += *ptr++ << 8;
RTPTimestamp += *ptr++;
uint32_t SSRC = *ptr++ << 24;
SSRC += *ptr++ << 16;
SSRC += *ptr++ << 8;
SSRC += *ptr++;
if (V != kRtpExpectedVersion) {
printf("V:%d is invalid.\n", V);
return false;
}
const uint8_t CSRCocts = CC * 4;
if ((ptr + CSRCocts) > _ptrRTPDataEnd) {
printf("!!!! ptr:%p, CSRCocts:%d, _ptrRTPDataEnd:%p is invalid.\n", ptr, CSRCocts, _ptrRTPDataEnd);
return false;
}
header.markerBit = M;
header.payloadType = PT;
header.sequenceNumber = sequenceNumber;
header.timestamp = RTPTimestamp;
header.ssrc = SSRC;
header.numCSRCs = CC;
header.paddingLength = P ? *(_ptrRTPDataEnd - 1) : 0;
for (unsigned int i = 0; i < CC; ++i) {
uint32_t CSRC = *ptr++ << 24;
CSRC += *ptr++ << 16;
CSRC += *ptr++ << 8;
CSRC += *ptr++;
header.arrOfCSRCs[i] = CSRC;
}
header.headerLength = 12 + CSRCocts;
header.extension.hasTransmissionTimeOffset = false;
header.extension.transmissionTimeOffset = 0;
header.extension.hasAbsoluteSendTime = false;
header.extension.absoluteSendTime = 0;
header.extension.hasAudioLevel = false;
header.extension.audioLevel = 0;
if (X) {
const ptrdiff_t remain = _ptrRTPDataEnd - ptr;
if (remain < 4) {
printf("remain:%d is invalid.\n", remain);
return false;
}
header.headerLength += 4;
uint16_t definedByProfile = *ptr++ << 8;
definedByProfile += *ptr++;
uint16_t XLen = *ptr++ << 8;
XLen += *ptr++;
XLen *= 4;
if (remain < (4 + XLen)) {
printf("remain:%d XLen:%d is definedByProfile:%d, invalid.\n", remain, XLen, definedByProfile);
return false;
}
header.headerLength += XLen;
}
return true;
}
CPcapParse::CPcapParse()
{
m_fp = NULL;
}
CPcapParse::~CPcapParse()
{
reset();
}
#define PCAP_FILE_MAGIC_1 0Xd4
#define PCAP_FILE_MAGIC_2 0Xc3
#define PCAP_FILE_MAGIC_3 0Xb2
#define PCAP_FILE_MAGIC_4 0Xa1
int CPcapParse::init(const char *pchPcapFile)
{
int ret = 0;
if (!pchPcapFile)
{
return -1;
}
m_fp = fopen(pchPcapFile, "rb+");
if (!m_fp)
{
return -2;
}
fread(&m_FileHeader, 1, sizeof(m_FileHeader), m_fp);
if (m_FileHeader.magic[0] != PCAP_FILE_MAGIC_1 || m_FileHeader.magic[1] != PCAP_FILE_MAGIC_2 ||
m_FileHeader.magic[2] != PCAP_FILE_MAGIC_3 || m_FileHeader.magic[3] != PCAP_FILE_MAGIC_4)
{
printf("The file is not a pcap file.\n");
return -3;
}
switch (m_FileHeader.linktype)
{
case 0:
m_sizePktOffset = 32;
break;
case 1:
m_sizePktOffset = 42;
break;
case 101:
m_sizePktOffset = 28;
break;
default:
m_sizePktOffset = 44;
break;
}
printf("CPcapParse::init m_sizePktOffset:%d \n", m_sizePktOffset);
return ret;
}
int CPcapParse::copyPktData(char *pchData, size_t bufLen, size_t &dataLen, uint64_t *pMs)
{
size_t ret;
pcapPkthdr_t packetHeader;
size_t readLen;
char buf[1500];
ret = fread(&packetHeader, sizeof(packetHeader), 1, m_fp);
if (pMs)
{
*pMs = packetHeader.u_seconds / 1000;
*pMs = *pMs + (packetHeader.seconds * 1000);
}
fseek(m_fp, m_sizePktOffset, SEEK_CUR);
readLen = packetHeader.caplen - m_sizePktOffset;
dataLen = fread(pchData, 1, readLen, m_fp);
if (dataLen != readLen)
{
return -1;
}
return 0;
}
void CPcapParse::reset()
{
if (m_fp)
{
fclose(m_fp);
m_fp = NULL;
}
m_sizePktOffset = 0;
}
rtpPacket::rtpPacket()
{
m_len = 0;
m_ts = 0;
}
rtpPacket::~rtpPacket()
{
}
int rtpPacket::init(const char *pchBuf, size_t sizeLen, uint64_t ts)
{
if (sizeLen > sizeof(m_buf))
{
return -1;
}
else
{
memcpy(m_buf, pchBuf, sizeLen);
m_len = sizeLen;
m_ts = ts;
}
return 0;
}
CRtpManage::CRtpManage()
{
memset(m_inFile, 0, sizeof(m_inFile));
#ifdef _WIN32
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
#endif
}
CRtpManage::~CRtpManage()
{
#ifdef _WIN32
WSACleanup();
#endif
}
int CRtpManage::init(const char *inFile)
{
int ret = 0;
sprintf(m_inFile, "%s", inFile);
if (m_pcap.init(inFile))
{
return -1;
}
ret = proc();
return ret;
}
#include <chrono>
static long long getCurrentMs()
{
auto time_now = std::chrono::system_clock::now();
auto duration_in_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_now.time_since_epoch());
return duration_in_ms.count();
}
int CRtpManage::netPlay(const int8_t * remoteAddr, uint16_t remotePort, uint16_t localPort)
{
struct sockaddr_in m_remoteAddr;
SOCKET m_socket;
memset(&m_remoteAddr, 0, sizeof(struct sockaddr_in));
m_remoteAddr.sin_family = AF_INET;
m_remoteAddr.sin_port = htons(remotePort);
m_remoteAddr.sin_addr.s_addr = inet_addr((const char*)remoteAddr);
struct sockaddr_in locateAddr;
locateAddr.sin_family = AF_INET;
locateAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if ((m_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
return -1;
}
if (bind(m_socket, (struct sockaddr*)&locateAddr, sizeof(sockaddr_in)) < 0)
{
return -1;
}
int iRet = 0;
uint64_t ms = 0;
uint64_t lastms = 0;
long long t_sender_use = 0;
std::list<shared_ptr<rtpPacket>>::iterator it = m_listRtp.begin();
int k = 0;
do
{
shared_ptr<rtpPacket> pkt = *it;
ms = pkt->getTs();
int nServAddLen = sizeof(m_remoteAddr);
if (lastms != 0)
{
int64_t t_wait = ms - lastms - t_sender_use;
k++;
if (t_wait > 0)
{
#ifdef WIN32
Sleep(t_wait);
#else
usleep((t_wait) * 1000);
#endif
lastms = ms;
}
}
else
{
lastms = ms;
}
long long t_1 = getCurrentMs();
iRet = sendto(m_socket, pkt->getHeader(), pkt->getPktLen(), 0, (sockaddr*)&m_remoteAddr, nServAddLen);
if (iRet == SOCKET_ERROR) {
printf("sendto() failed:\n");
break;
}
long long t_2 = getCurrentMs();
t_sender_use = t_2 - t_1;
it++;
} while (it != m_listRtp.end());
closesocket(m_socket);
return 0;
}
int CRtpManage::proc()
{
char buf[2000];
size_t sizeLen;
RTPHeader rtpHeader;
unsigned int seq = 0;
unsigned int seqRound = 1;
unsigned int addCount = 0;
bool bBegin = true;
const unsigned int maxInOrder = 5000;
uint64_t t_ts = 0;
while (!m_pcap.copyPktData(buf, sizeof(buf), sizeLen, &t_ts))
{
shared_ptr<rtpPacket> pPkt = make_shared<rtpPacket>();
if (pPkt->init(buf, sizeLen, t_ts))
{
continue;
}
m_listRtp.push_back(pPkt);
if (!rtpParse(rtpHeader, buf, sizeLen))
{
printf("Parse failed.\n");
continue;
}
if (bBegin)
{
seq = rtpHeader.sequenceNumber;
seqRound = 0;
bBegin = false;
}
else
{
if (seq > (unsigned int)(rtpHeader.sequenceNumber) + maxInOrder + seqRound * 65536)
{
addCount++;
}
if (addCount > 1000)
{
addCount = 0;
seqRound++;
seq = seqRound * 65536 + rtpHeader.sequenceNumber;
}
if (seq + 1 == seqRound * 65536 + rtpHeader.sequenceNumber)
{
seq = seqRound * 65536 + rtpHeader.sequenceNumber;
}
else if (seq + maxInOrder < seqRound * 65536 + rtpHeader.sequenceNumber)
{
seq = seqRound * 65536 + rtpHeader.sequenceNumber;
}
}
if (rtpHeader.sequenceNumber < maxInOrder && addCount > 0)
{
m_mapRtp[(seqRound + 1) * 65536 + rtpHeader.sequenceNumber] = pPkt;
}
else
{
m_mapRtp[seqRound * 65536 + rtpHeader.sequenceNumber] = pPkt;
}
}
return 0;
}
void CRtpManage::reset()
{
m_pcap.reset();
m_mapRtp.clear();
m_listRtp.clear();
}
void CRtpManage::getMap(map<unsigned int, shared_ptr<rtpPacket> >& omapRtp)
{
omapRtp = m_mapRtp;
}
void CRtpManage::getList(std::list<shared_ptr<rtpPacket> >& olistRtp)
{
olistRtp = m_listRtp;
}