Bootstrap

rtp_over_udp 分析代码c++

#ifndef _PCAP_PARSE_H
#define _PCAP_PARSE_H

#include <stdio.h>
#include <map>
#include <list>
#include <memory>
//#include "payloadBase.h"

using std::map;
using std::shared_ptr;

#define MAX_FILENAME_PATH 260

enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13



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;

    // Audio Level includes both level in dBov and voiced/unvoiced bit. See:
    // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/
    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,
//     ePktRed265,
//     ePktRed264
};

class CPcapParse
{
public:
    CPcapParse();
    virtual ~CPcapParse();
    // 输入文件名,以及每个包数据起始的偏移位置,偏移位置通过wireshark界面查看获得,通常为42或44
    int init(const char *pchPcapFile);
    // 类似于fread, 顺序读取
    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;

    /*pcap packet header*/
    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;  // 时间戳wireshark 抓包
};

class CRtpManage
{
public:
    CRtpManage();
    virtual ~CRtpManage();
    // 输入已经过滤pcap 抓包文件,内部不进行ssrc 过滤
    int init(const char *inFile);

    // 发送给远端的网络
    int netPlay(const int8_t * remoteAddr, uint16_t remotePort, uint16_t localPort);
    // 获取pcap 的map 数据
    void getMap(std::map<unsigned int, shared_ptr<rtpPacket> >& omapRtp);
    // 获取pcap 的list 数据
    void getList(std::list<shared_ptr<rtpPacket> >& olistRtp);
    // 清除map list, 重新init
    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
};

// webRtc的公共库


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;
    }

    // Version
    const uint8_t V = _ptrRTPDataBegin[0] >> 6;
    // Padding
    const bool          P = ((_ptrRTPDataBegin[0] & 0x20) == 0) ? false : true;
    // eXtension
    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;

    // If in effect, MAY be omitted for those packets for which the offset
    // is zero.
    header.extension.hasTransmissionTimeOffset = false;
    header.extension.transmissionTimeOffset = 0;

    // May not be present in packet.
    header.extension.hasAbsoluteSendTime = false;
    header.extension.absoluteSendTime = 0;

    // May not be present in packet.
    header.extension.hasAudioLevel = false;
    header.extension.audioLevel = 0;

    if (X) {
        /* RTP header extension, RFC 3550.
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |      defined by profile       |           length              |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                        header extension                       |
        |                             ....                              |
        */
        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++; // in 32 bit words
        XLen *= 4; // in octs

        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)
	{
		// windows loop back
	case 0:
        m_sizePktOffset = 32;
		break;
	case 1:
        m_sizePktOffset = 42;
		break;
		// ios/mac
	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_port = htons(localPort);
	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;

            //printf("t_wait:%lld ms:%llu lastmu:%llu t_sender_use:%lld k:%d \n", t_wait, ms, lastms, t_sender_use, k );
            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 short seq = 0;
    unsigned int seq = 0;
    unsigned int seqRound = 1;
    unsigned int addCount = 0;
    bool bBegin = true;
    const unsigned int maxInOrder = 5000;

    // 将所有的pcap包读取到内存的map中,过滤ssrc的问题由wireshark完成,本程序不做处理

    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;
}




悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;