国标PS流打包和RTP发送代码

2023-05-16

这个国标PS流打包/封包的代码来自于互联网,QuickGBLink在原先代码基础上做了一些改进。因为代码来自于别人的成果,基于互联网知识分享的精神,我们打算将其开源,希望能对更多学习和开发国标的开发者带来帮助。

这个源码集成了PS封包和RTP发送的功能,具体它支持:

1. 输入一个H264 NALU或一帧数据,对传入的数据打包成PS流,对数据进行切分,并用RTP发送。

2. 传入一个PS帧,对数据进行切分,用RTP发送。

源码文件

头文件rtpsend.h:

#ifndef RTPSEND_H
#define RTPSEND_H

//#include <stdint.h>

#define PS_HDR_LEN  14
#define SYS_HDR_LEN 18
#define PSM_HDR_LEN 24
#define PES_HDR_LEN 19
#define  PS_SYS_MAP_SIZE 30
#define PS_PES_PAYLOAD_SIZE (65535 - PES_HDR_LEN)

#define RTP_HDR_LEN 12
#define  RTP_HDR_SIZE 12 // ?
#define RTP_MAX_PACKET_BUFF 1300
#define  RTP_VERSION 2//?


struct bits_buffer_s
{
    int i_size;
    int i_data;
    int i_mask;
    unsigned char * p_data;
};

struct Data_Info_s
{
    char dstIP[32];
    int nPort;
    uint32_t sCurPts;
    int IFrame;
    unsigned short u16CSeq;
    unsigned int u32Ssrc;
    char szBuff[RTP_MAX_PACKET_BUFF];
};

#define MAX_RES_BUF 512000 //512*1024
#define MAX_RTP_CHANNEL 8

class rtpSend
{
protected:
    int makePsHeader(char *pData, uint64_t s64Scr);
    int makeSysHeader(char *pData);
    int makePsmHeader(char *pData);
    int makePesHeader(char *pData, int stream_id, int payload_len, unsigned long long pts, unsigned long long dts);

    int sendDataByUDP(int nChn,char* databuff, int nLen);
    int makeRtpHeader(char *pData, int marker_flag, unsigned short cseq, long long curpts, unsigned int ssrc);
    int sendRtpPacket(int nChn,char *databuff, int nDataLen, int mark_flag, Data_Info_s* pPacker);
    void SaveSpsPps(int nChn,char *pBuf, int nLen, int type);

public:
    rtpSend();
    ~rtpSend();

    void SetSSRC(int nChn, int nSsc);
    void SetRemoteAddr(int nChn,char* ip, int nPort); //设置目标发送地址

	int  GetOneIdleChan(); //获得一个空闲的通道

    int SendH264Stream(int nChn,char *pData, int nFrameLen, uint32_t curPts, int frameType);
	int SendPSStream(int nChn, char *pData, int nFrameLen, uint32_t sCurPts, int mark_flag);

    int rtpSendPort(int nChn) {return m_rtpSendPort[nChn];}
    int OpenStream(int nChn);
    void CloseStream(int nChn);

	bool IsStreamOpen(int nChn);

	void SetWritable(int nChn, bool bWrite);
	bool IsWritable(int nChn);

private:
    int m_sock[MAX_RTP_CHANNEL];
    int m_rtpSendPort[MAX_RTP_CHANNEL];
    Data_Info_s m_DataInfo[MAX_RTP_CHANNEL];
    char* m_pResBuf[MAX_RTP_CHANNEL];
    char* m_pPps[MAX_RTP_CHANNEL];
    char* m_pSps[MAX_RTP_CHANNEL];
    int m_nSps[MAX_RTP_CHANNEL];
    int m_nPps[MAX_RTP_CHANNEL];
    bool m_bOpenStream[MAX_RTP_CHANNEL];
    bool m_bWaitIFrame[MAX_RTP_CHANNEL];
	bool m_bCanWrite[MAX_RTP_CHANNEL];
};

#endif // RTPSEND_H

源文件rtpsend.cpp:

#include "stdafx.h"
#include "rtpsend.h"

//#include <stdlib.h>
//#include <stdio.h>
//#include <memory>
//#include <unistd.h>
//#include <arpa/inet.h>
//#include <sys/socket.h>
//#include <netinet/in.h>
#include <string.h>
#include <atlbase.h>

#define bits_write(buffer, count, bits)\
{\
    bits_buffer_s *p_buffer = (buffer);\
    int i_count = (count);\
    uint64_t i_bits = (bits);\
    while( i_count > 0 )\
{\
    i_count--;\
    if( ( i_bits >> i_count )&0x01 )\
{\
    p_buffer->p_data[p_buffer->i_data] |= p_buffer->i_mask;\
}\
        else\
{\
    p_buffer->p_data[p_buffer->i_data] &= ~p_buffer->i_mask;\
}\
    p_buffer->i_mask >>= 1;         /*操作完一个字节第一位后,操作第二位*/\
    if( p_buffer->i_mask == 0 )     /*循环完一个字节的8位后,重新开始下一位*/\
{\
    p_buffer->i_data++;\
    p_buffer->i_mask = 0x80;\
}\
}\
}

rtpSend::rtpSend()
{
    for (int i = 0; i < MAX_RTP_CHANNEL; i++)
    {
        int on = 1;
        m_sock[i] = socket(AF_INET, SOCK_DGRAM, 0);
        setsockopt(m_sock[i], SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));

        m_rtpSendPort[i] = 6666 + i;
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_port = htons(m_rtpSendPort[i]);
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        if (bind(m_sock[i], (struct sockaddr* )&addr, sizeof(addr)))
        {
            TRACE("bind error\n");
        }

        m_pResBuf[i] = new char[MAX_RES_BUF] ;
        m_pPps[i] = new char[1024];
        m_pSps[i] = new char[1024];
        m_nSps[i] = 0;
        m_nPps[i] = 0;
        m_DataInfo[i].sCurPts = 0;
		m_bOpenStream[i] = false;
		m_bCanWrite[i] = false;
    }
}


rtpSend::~rtpSend()
{
    for (int i = 0; i < MAX_RTP_CHANNEL; i++)
    {
        delete[] m_pResBuf[i];
        delete[] m_pPps[i];
        delete[] m_pSps[i];
        closesocket(m_sock[i]);
    }
}

int rtpSend::OpenStream(int nChn)
{
    TRACE("open stream %d\n", nChn);

    m_bOpenStream[nChn] = true;
    m_bWaitIFrame[nChn] = true;

	m_DataInfo[nChn].sCurPts = 0;
	m_DataInfo[nChn].u16CSeq = 0;

    return true;
}

void rtpSend::CloseStream(int nChn)
{
	 TRACE("close stream %d\n", nChn);
    m_bOpenStream[nChn] = false;
	m_bCanWrite[nChn] = false;
}

bool rtpSend::IsStreamOpen(int nChn)
{
	return m_bOpenStream[nChn];
}

void rtpSend::SetWritable(int nChn, bool bWrite)
{
	m_bCanWrite[nChn] = bWrite;
}

bool rtpSend::IsWritable(int nChn)
{
	return m_bCanWrite[nChn];
}

int rtpSend::makePsHeader(char *pData, uint64_t currPts)
{
    unsigned long long lScrExt = (currPts) % 100;
    unsigned long long s64Scr = currPts / 100;
    // 这里除以100是由于sdp协议返回的video的频率是90000,帧率是25帧/s,所以每次递增的量是3600,
    // 所以实际你应该根据你自己编码里的时间戳来处理以保证时间戳的增量为3600即可,
    //如果这里不对的话,就可能导致卡顿现象了
    bits_buffer_s  	bitsBuffer;
    bitsBuffer.i_size = PS_HDR_LEN;
    bitsBuffer.i_data = 0;
    bitsBuffer.i_mask = 0x80; // 二进制:10000000 这里是为了后面对一个字节的每一位进行操作,避免大小端夸字节字序错乱
    bitsBuffer.p_data = (unsigned char *)(pData);
    memset(bitsBuffer.p_data, 0, PS_HDR_LEN);
    bits_write(&bitsBuffer, 32, 0x000001BA);			/*start codes*/
    bits_write(&bitsBuffer, 2, 1);						/*marker bits '01b'*/
    bits_write(&bitsBuffer, 3, (s64Scr >> 30) & 0x07);     /*System clock [32..30]*/
    bits_write(&bitsBuffer, 1, 1);						/*marker bit*/
    bits_write(&bitsBuffer, 15, (s64Scr >> 15) & 0x7FFF);   /*System clock [29..15]*/
    bits_write(&bitsBuffer, 1, 1);						/*marker bit*/
    bits_write(&bitsBuffer, 15, s64Scr & 0x7fff);         /*System clock [29..15]*/
    bits_write(&bitsBuffer, 1, 1);						/*marker bit*/
    bits_write(&bitsBuffer, 9, lScrExt & 0x01ff);		/*System clock [14..0]*/
    bits_write(&bitsBuffer, 1, 1);						/*marker bit*/
    bits_write(&bitsBuffer, 22, (255) & 0x3fffff);		/*bit rate(n units of 50 bytes per second.)*/
    bits_write(&bitsBuffer, 2, 3);						/*marker bits '11'*/
    bits_write(&bitsBuffer, 5, 0x1f);					/*reserved(reserved for future use)*/
    bits_write(&bitsBuffer, 3, 0);						/*stuffing length*/


    return 0;
}

int rtpSend::makeRtpHeader(char *pData, int marker_flag, unsigned short cseq, long long curpts, unsigned int ssrc)
{
    bits_buffer_s  	bitsBuffer;
    if (pData == NULL)
        return -1;
    bitsBuffer.i_size = RTP_HDR_LEN;
    bitsBuffer.i_data = 0;
    bitsBuffer.i_mask = 0x80;
    bitsBuffer.p_data = (unsigned char *)(pData);
    memset(bitsBuffer.p_data, 0, RTP_HDR_SIZE);
    bits_write(&bitsBuffer, 2, RTP_VERSION);	/* rtp version 	*/
    bits_write(&bitsBuffer, 1, 0);				/* rtp padding 	*/
    bits_write(&bitsBuffer, 1, 0);				/* rtp extension 	*/
    bits_write(&bitsBuffer, 4, 0);				/* rtp CSRC count */
    bits_write(&bitsBuffer, 1, (marker_flag));			/* rtp marker  	*/
    bits_write(&bitsBuffer, 7, 96);			/* rtp payload type*/
    bits_write(&bitsBuffer, 16, (cseq));			/* rtp sequence 	 */
    bits_write(&bitsBuffer, 32, (curpts)); 		/* rtp timestamp 	 */
    bits_write(&bitsBuffer, 32, (ssrc)); 		/* rtp SSRC	 	 */
    return 0;
}


int rtpSend::makeSysHeader(char *pData)
{

    bits_buffer_s  	bitsBuffer;
    bitsBuffer.i_size = SYS_HDR_LEN;
    bitsBuffer.i_data = 0;
    bitsBuffer.i_mask = 0x80;
    bitsBuffer.p_data = (unsigned char *)(pData);
    memset(bitsBuffer.p_data, 0, SYS_HDR_LEN);
    /*system header*/
    bits_write(&bitsBuffer, 32, 0x000001BB);	/*start code*/
    bits_write(&bitsBuffer, 16, SYS_HDR_LEN - 6);/*header_length 表示次字节后面的长度,后面的相关头也是次意思*/
    bits_write(&bitsBuffer, 1, 1);            /*marker_bit*/
    bits_write(&bitsBuffer, 22, 50000);		/*rate_bound*/
    bits_write(&bitsBuffer, 1, 1);            /*marker_bit*/
    bits_write(&bitsBuffer, 6, 1);            /*audio_bound*/
    bits_write(&bitsBuffer, 1, 0);            /*fixed_flag */
    bits_write(&bitsBuffer, 1, 1);        	/*CSPS_flag */
    bits_write(&bitsBuffer, 1, 1);        	/*system_audio_lock_flag*/
    bits_write(&bitsBuffer, 1, 1);        	/*system_video_lock_flag*/
    bits_write(&bitsBuffer, 1, 1);        	/*marker_bit*/
    bits_write(&bitsBuffer, 5, 1);        	/*video_bound*/
    bits_write(&bitsBuffer, 1, 0);        	/*dif from mpeg1*/
    bits_write(&bitsBuffer, 7, 0x7F);     	/*reserver*/
                                            /*audio stream bound*/
    bits_write(&bitsBuffer, 8, 0xC0);         /*stream_id*/
    bits_write(&bitsBuffer, 2, 3);        	/*marker_bit */
    bits_write(&bitsBuffer, 1, 0);            /*PSTD_buffer_bound_scale*/
    bits_write(&bitsBuffer, 13, 512);          /*PSTD_buffer_size_bound*/
                                               /*video stream bound*/
    bits_write(&bitsBuffer, 8, 0xE0);         /*stream_id*/
    bits_write(&bitsBuffer, 2, 3);        	/*marker_bit */
    bits_write(&bitsBuffer, 1, 1);        	/*PSTD_buffer_bound_scale*/
    bits_write(&bitsBuffer, 13, 2048);     	/*PSTD_buffer_size_bound*/
    return 0;
}

int rtpSend::sendDataByUDP(int nChn, char* databuff, int nLen)
{
//    TRACE("sendDataByUDP %s:%d\n", m_DataInfo[nChn].dstIP, m_DataInfo[nChn].nPort);
    struct sockaddr_in addrSrv;
    addrSrv.sin_addr.s_addr = inet_addr(m_DataInfo[nChn].dstIP);
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(m_DataInfo[nChn].nPort);

    int sendRet = sendto(m_sock[nChn], databuff, nLen, 0,
        (struct sockaddr*)&addrSrv, sizeof(sockaddr));

    return sendRet;
}

int rtpSend::makePsmHeader(char *pData)
{

    bits_buffer_s  	bitsBuffer;
    bitsBuffer.i_size = PSM_HDR_LEN;
    bitsBuffer.i_data = 0;
    bitsBuffer.i_mask = 0x80;
    bitsBuffer.p_data = (unsigned char *)(pData);
    memset(bitsBuffer.p_data, 0, PS_SYS_MAP_SIZE);
    bits_write(&bitsBuffer, 24, 0x000001);	/*start code*/
    bits_write(&bitsBuffer, 8, 0xBC);		/*map stream id*/
    bits_write(&bitsBuffer, 16, 18);			/*program stream map length*/
    bits_write(&bitsBuffer, 1, 1);			/*current next indicator */
    bits_write(&bitsBuffer, 2, 3);			/*reserved*/
    bits_write(&bitsBuffer, 5, 0); 			/*program stream map version*/
    bits_write(&bitsBuffer, 7, 0x7F);		/*reserved */
    bits_write(&bitsBuffer, 1, 1);			/*marker bit */
    bits_write(&bitsBuffer, 16, 0); 			/*programe stream info length*/
    bits_write(&bitsBuffer, 16, 8); 		/*elementary stream map length	is*/
                                            /*audio*/
    bits_write(&bitsBuffer, 8, 0x90);       /*stream_type*/
    bits_write(&bitsBuffer, 8, 0xC0);		/*elementary_stream_id*/
    bits_write(&bitsBuffer, 16, 0); 		/*elementary_stream_info_length is*/
                                            /*video*/
    bits_write(&bitsBuffer, 8, 0x1B);       /*stream_type*/
    bits_write(&bitsBuffer, 8, 0xE0);		/*elementary_stream_id*/
    bits_write(&bitsBuffer, 16, 0); 		/*elementary_stream_info_length */
                                            /*crc (2e b9 0f 3d)*/
    bits_write(&bitsBuffer, 8, 0x45);		/*crc (24~31) bits*/
    bits_write(&bitsBuffer, 8, 0xBD);		/*crc (16~23) bits*/
    bits_write(&bitsBuffer, 8, 0xDC);		/*crc (8~15) bits*/
    bits_write(&bitsBuffer, 8, 0xF4);		/*crc (0~7) bits*/
    return 0;
}


int rtpSend::makePesHeader(char *pData, int stream_id, int payload_len, unsigned long long pts, unsigned long long dts)
{

    bits_buffer_s  	bitsBuffer;
    bitsBuffer.i_size = PES_HDR_LEN;
    bitsBuffer.i_data = 0;
    bitsBuffer.i_mask = 0x80;
    bitsBuffer.p_data = (unsigned char *)(pData);
    memset(bitsBuffer.p_data, 0, PES_HDR_LEN);
    /*system header*/
    bits_write(&bitsBuffer, 24, 0x000001);	/*start code*/
    bits_write(&bitsBuffer, 8, (stream_id));	/*streamID*/
    bits_write(&bitsBuffer, 16, (payload_len)+13);	/*packet_len*/ //指出pes分组中数据长度和该字节后的长度和
    bits_write(&bitsBuffer, 2, 2);		/*'10'*/
    bits_write(&bitsBuffer, 2, 0);		/*scrambling_control*/
    bits_write(&bitsBuffer, 1, 0);		/*priority*/
    bits_write(&bitsBuffer, 1, 0);		/*data_alignment_indicator*/
    bits_write(&bitsBuffer, 1, 0);		/*copyright*/
    bits_write(&bitsBuffer, 1, 0);		/*original_or_copy*/
    bits_write(&bitsBuffer, 1, 1);		/*PTS_flag*/
    bits_write(&bitsBuffer, 1, 1);		/*DTS_flag*/
    bits_write(&bitsBuffer, 1, 0);		/*ESCR_flag*/
    bits_write(&bitsBuffer, 1, 0);		/*ES_rate_flag*/
    bits_write(&bitsBuffer, 1, 0);		/*DSM_trick_mode_flag*/
    bits_write(&bitsBuffer, 1, 0);		/*additional_copy_info_flag*/
    bits_write(&bitsBuffer, 1, 0);		/*PES_CRC_flag*/
    bits_write(&bitsBuffer, 1, 0);		/*PES_extension_flag*/
    bits_write(&bitsBuffer, 8, 10);		/*header_data_length*/
                                        // 指出包含在 PES 分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前
                                        //的字节指出了有无可选字段。

                                        /*PTS,DTS*/
    bits_write(&bitsBuffer, 4, 3);                    /*'0011'*/
    bits_write(&bitsBuffer, 3, ((pts) >> 30) & 0x07);     /*PTS[32..30]*/
    bits_write(&bitsBuffer, 1, 1);
    bits_write(&bitsBuffer, 15, ((pts) >> 15) & 0x7FFF);    /*PTS[29..15]*/
    bits_write(&bitsBuffer, 1, 1);
    bits_write(&bitsBuffer, 15, (pts) & 0x7FFF);          /*PTS[14..0]*/
    bits_write(&bitsBuffer, 1, 1);
    bits_write(&bitsBuffer, 4, 1);                    /*'0001'*/
    bits_write(&bitsBuffer, 3, ((dts) >> 30) & 0x07);     /*DTS[32..30]*/
    bits_write(&bitsBuffer, 1, 1);
    bits_write(&bitsBuffer, 15, ((dts) >> 15) & 0x7FFF);    /*DTS[29..15]*/
    bits_write(&bitsBuffer, 1, 1);
    bits_write(&bitsBuffer, 15, (dts) & 0x7FFF);          /*DTS[14..0]*/
    bits_write(&bitsBuffer, 1, 1);


    return 0;
}


int rtpSend::sendRtpPacket(int nChn, char *databuff, int nDataLen, int mark_flag, Data_Info_s* pPacker)
{
//    TRACE("sendRtpPacket %d\n", pPacker->u32Ssrc);

    int nRet = 0;
    int nPlayLoadLen = 0;
    int nSendSize = 0;
    char szRtpHdr[RTP_HDR_LEN];
    memset(szRtpHdr, 0, RTP_HDR_LEN);

    if (nDataLen + RTP_HDR_LEN <= RTP_MAX_PACKET_BUFF)
    {
        // 一帧数据发送完后,给mark标志位置1
//        makeRtpHeader(szRtpHdr, ((mark_flag == 1) ? 1 : 0), ++pPacker->u16CSeq, (pPacker->sCurPts / 300), pPacker->u32Ssrc);
                makeRtpHeader(szRtpHdr, ((mark_flag == 1) ? 1 : 0), ++pPacker->u16CSeq, (pPacker->sCurPts ), pPacker->u32Ssrc);

        memcpy(pPacker->szBuff, szRtpHdr, RTP_HDR_LEN);

        memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nDataLen);

        nRet = sendDataByUDP(nChn, pPacker->szBuff, RTP_HDR_LEN + nDataLen);

        if (nRet != (RTP_HDR_LEN + nDataLen))
        {
            TRACE(" udp send error !\n");
            return -1;
        }

    }
    else
    {
        nPlayLoadLen = RTP_MAX_PACKET_BUFF - RTP_HDR_LEN; // 每次只能发送的数据长度 除去rtp头
//        makeRtpHeader(pPacker->szBuff, 0, ++pPacker->u16CSeq, (pPacker->sCurPts / 100), pPacker->u32Ssrc);
                makeRtpHeader(pPacker->szBuff, 0, ++pPacker->u16CSeq, (pPacker->sCurPts ), pPacker->u32Ssrc);
        memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nPlayLoadLen);
        nRet = sendDataByUDP(nChn, pPacker->szBuff, RTP_HDR_LEN + nPlayLoadLen);
        if (nRet != (RTP_HDR_LEN + nPlayLoadLen))
        {
            TRACE(" udp send error !\n");
            return -1;
        }

        nDataLen -= nPlayLoadLen;
        // databuff += (nPlayLoadLen - RTP_HDR_LEN);
        databuff += nPlayLoadLen; // 表明前面到数据已经发送出去
        databuff -= RTP_HDR_LEN; // 用来存放rtp头
        while (nDataLen > 0)
        {
            if (nDataLen <= nPlayLoadLen)
            {
                //一帧数据发送完,置mark标志位
//                makeRtpHeader(databuff, mark_flag, ++pPacker->u16CSeq, (pPacker->sCurPts / 100), pPacker->u32Ssrc);
                makeRtpHeader(databuff, mark_flag, ++pPacker->u16CSeq, (pPacker->sCurPts), pPacker->u32Ssrc);
                nSendSize = nDataLen;
            }
            else
            {
//                makeRtpHeader(databuff, 0, ++pPacker->u16CSeq, (pPacker->sCurPts / 100), pPacker->u32Ssrc);
                makeRtpHeader(databuff, 0, ++pPacker->u16CSeq, (pPacker->sCurPts ), pPacker->u32Ssrc);
                nSendSize = nPlayLoadLen;
            }

            nRet = sendDataByUDP(nChn, databuff, RTP_HDR_LEN + nSendSize);
            if (nRet != (RTP_HDR_LEN + nSendSize))
            {
                TRACE(" udp send error !\n");
                return -1;
            }

            nDataLen -= nSendSize;
            databuff += nSendSize;
        }

    }
    return 0;
}


void rtpSend::SaveSpsPps(int nChn, char* pBuf, int nLen, int type)
{
    if (nLen < 1024)
    {
        if (type == 7)
        {
            memcpy(m_pSps[nChn], pBuf, nLen);
            m_nSps[nChn] = nLen;
        }
        else  if (type == 8)
        {
            memcpy(m_pPps[nChn], pBuf, nLen);
            m_nPps[nChn] = nLen;
        }
    }

    return ;
}

void rtpSend::SetSSRC(int nChn, int nSsc)
{
    m_DataInfo[nChn].u32Ssrc = nSsc;
}

void rtpSend::SetRemoteAddr(int nChn, char *ip, int nPort)
{
    strcpy(m_DataInfo[nChn].dstIP, ip);
    m_DataInfo[nChn].nPort = nPort;
}



//获得一个空闲状态的通道
int  rtpSend::GetOneIdleChan()
{
	for(int i=0; i<MAX_RTP_CHANNEL;i++)
	{
		if(m_bOpenStream[i] == false)
			return i;
	}
	return -1;
}



#if 0
//SPS(frameType=7)、PPS(frameType=8)和I帧(frameType=5)分开发
int rtpSend::SendH264Stream(int nChn, char *pData, int nFrameLen, uint32_t sCurPts, int frameType)
{
    char szPsHead[256];
    int  nSizePos = 0;
    int  nSize = 0;

    if (frameType == 5)
    {
        m_bWaitIFrame[nChn] = false;
    }

     if (frameType != 1 && frameType != 5)
     {
        SaveSpsPps(nChn, pData, nFrameLen, frameType);
             return 0;
     }

     if (!m_bOpenStream[nChn])
     {
         return 0;
     }

     if (m_bWaitIFrame[nChn])
     {
         return 0;
     }

     if (m_nPps[nChn] == 0 || m_nSps[nChn] == 0)
         return 0;

    memset(szPsHead, 0, 256);

    m_DataInfo[nChn].sCurPts += 3600;

    makePsHeader(szPsHead + nSizePos,  m_DataInfo[nChn].sCurPts );

    nSizePos += PS_HDR_LEN;

    char* pPayload = m_pResBuf[nChn] + 4096;

    m_DataInfo[nChn].IFrame = 0;

    memcpy(pPayload, pData, nFrameLen);

    if (frameType == 5) //I Frame
    {
        memcpy(pPayload - m_nPps[nChn], m_pPps[nChn], m_nPps[nChn]);
        memcpy(pPayload - m_nPps[nChn] - m_nSps[nChn], m_pSps[nChn], m_nSps[nChn]);

        pPayload-= m_nPps[nChn];
        pPayload-= m_nSps[nChn];
        nFrameLen += m_nPps[nChn];
        nFrameLen += m_nSps[nChn];

        makeSysHeader(szPsHead + nSizePos);
        nSizePos += SYS_HDR_LEN;

        makePsmHeader(szPsHead + nSizePos);
        nSizePos += PSM_HDR_LEN;

        m_DataInfo[nChn].IFrame = 1;
    }

    加上rtp发送出去,这样的话,后面的数据就只要分片分包就只有加上pes头和rtp头了
    if (sendRtpPacket(nChn, szPsHead, nSizePos, 0, &m_DataInfo[nChn]) != 0)
    {
        return -1;
    }

    char* pTmp = pPayload - PES_HDR_LEN;

    while (nFrameLen > 0)
    {
        //每次帧的长度不要超过short类型,过了就得分片进循环行发送
        nSize = (nFrameLen > PS_PES_PAYLOAD_SIZE) ? PS_PES_PAYLOAD_SIZE : nFrameLen;

        // 添加pes头
        makePesHeader(pTmp, 0xE0, nSize, (m_DataInfo[nChn].sCurPts / 100), (m_DataInfo[nChn].sCurPts / 300));

        最后在添加rtp头并发送数据
        if (sendRtpPacket(nChn, pTmp, nSize + PES_HDR_LEN, ((nSize == nFrameLen) ? 1 : 0), &m_DataInfo[nChn]) != 0)
        {
            TRACE("gb28181_send_pack failed!\n");
            return -1;
        }

        nFrameLen -= nSize;
        pTmp += nSize;

    }

    return 0;
}
#else
//SPS(frameType=7)、PPS(frameType=8)和I帧(frameType=5)合在一个帧发
int rtpSend::SendH264Stream(int nChn, char *pData, int nFrameLen, uint32_t sCurPts, int frameType)
{
    char szPsHead[256];
    int  nSizePos = 0;
    int  nSize = 0;

    if (frameType == 7 || frameType == 5) //SPS或I帧
    {
        m_bWaitIFrame[nChn] = false;
    }


     if (!m_bOpenStream[nChn] || !m_bCanWrite[nChn])
     {
         return -2;
     }

     if (m_bWaitIFrame[nChn])
     {
         return -3;
     }


    memset(szPsHead, 0, 256);

    //m_DataInfo[nChn].sCurPts += 3600;
	m_DataInfo[nChn].sCurPts = sCurPts;

    makePsHeader(szPsHead + nSizePos,  m_DataInfo[nChn].sCurPts );

    nSizePos += PS_HDR_LEN;

    char* pPayload = m_pResBuf[nChn] + 4096;

    m_DataInfo[nChn].IFrame = 0;

	ASSERT(nFrameLen + 4096 < MAX_RES_BUF); //检查数据是否超过缓冲区大小
    memcpy(pPayload, pData, nFrameLen);

    if (frameType == 7 || frameType == 5) 
    {
        makeSysHeader(szPsHead + nSizePos);
        nSizePos += SYS_HDR_LEN;

        makePsmHeader(szPsHead + nSizePos);
        nSizePos += PSM_HDR_LEN;

        m_DataInfo[nChn].IFrame = 1;
    }

    加上rtp发送出去,这样的话,后面的数据就只要分片分包就只有加上pes头和rtp头了
    if (sendRtpPacket(nChn, szPsHead, nSizePos, 0, &m_DataInfo[nChn]) != 0)
    {
        return -1;
    }

    char* pTmp = pPayload - PES_HDR_LEN;

    while (nFrameLen > 0)
    {
        //每次帧的长度不要超过short类型,过了就得分片进循环行发送
        nSize = (nFrameLen > PS_PES_PAYLOAD_SIZE) ? PS_PES_PAYLOAD_SIZE : nFrameLen;

        // 添加pes头
        makePesHeader(pTmp, 0xE0, nSize, (m_DataInfo[nChn].sCurPts / 100), (m_DataInfo[nChn].sCurPts / 300));

        最后在添加rtp头并发送数据
        if (sendRtpPacket(nChn, pTmp, nSize + PES_HDR_LEN, ((nSize == nFrameLen) ? 1 : 0), &m_DataInfo[nChn]) != 0)
        {
            TRACE("sendRtpPacket failed!\n");
            return -1;
        }

        nFrameLen -= nSize;
        pTmp += nSize;

    }

    return 0;
}
#endif

int rtpSend::SendPSStream(int nChn, char *pData, int nFrameLen, uint32_t sCurPts,  int mark_flag)
{
	//ATLASSERT(nFrameLen < 65535);
     if (!m_bOpenStream[nChn] || !m_bCanWrite[nChn])
     {
         return -2;
     }

	m_DataInfo[nChn].sCurPts = sCurPts;

    最后在添加rtp头并发送数据
    if (sendRtpPacket(nChn, pData, nFrameLen, mark_flag, &m_DataInfo[nChn]) != 0)
    {
        TRACE("sendRtpPacket failed!\n");
        return -1;
    }

    return 0;
}

怎么使用这个类

这个类支持8个通道的RTP发送,即支持发送给8个终端,每个通道创建了一个UDP Socket,传输为一对一,即一个通道发送到一个目标IP地址(当然读者也可以改成一对多发送)。

使用这个类的步骤:

1. 首先,创建对象,并初始化参数:

m_pRtpSend = new rtpSend;
m_pRtpSend->SetRemoteAddr(nChn, dest_client_ip, atoi(dest_client_port));
m_pRtpSend->SetSSRC(nChn, nSSRC);
m_pRtpSend->OpenStream(nChn);

m_pRtpSend->SetWritable(nChn, true); //打开发送开关

上面代码的作用是创建一个rtpSend的对象,设置目标接收IP地址和目标端口号,设置RTP的SSRC标识,调用OpenStream初始化内部变量,并设置Writable状态为true,即启动发送状态开关。

2. 发送数据。假如用户自己实现读取H264或PS文件,要调用这个类发送可实现一个回调,每得到一帧数据就在回调函数里通过m_pRtpSend对象进行发送。

回调函数原型如下:

enum mediasourcetype
{
	_None_media_format = 0,
	_PS_FILE = 1,
	_H264_FILE
};

typedef LRESULT  (CALLBACK *READ_FRAME_DATA_CALLBACK)(int nChanNo, mediasourcetype media_type, BYTE * data, int size, int frame_type, unsigned int pts, int mark_flag);

回调函数实现例子如下:

//媒体文件读取线程回调的每一帧图像数据(可能是H264或PS格式)
LRESULT CALLBACK FileSourceReadFrameCB(int nChanNo, mediasourcetype media_type,  BYTE * pFrameBuffer, int nFrameSize, int frame_type, unsigned int nPts, int mark_flag)
{
	if(media_type == _H264_FILE)
	{
		if (m_pRtpSend)
		{
			int nalu_type = (pFrameBuffer[4] & 0x1f);
			ATLTRACE("frameType: %d \n", nalu_type);

			if(nChanNo == -1) //nChanNo为-1表示发送给所有通道
			{
				for(int i=0; i<MAX_RTP_CHANNEL; i++)
				{
					if(m_pRtpSend->IsWritable(i))
					{
						m_pRtpSend->SendH264Stream(i, (char*)pFrameBuffer, nFrameSize, nPts, nalu_type);
					}
				}
			}
			else
			{
				m_pRtpSend->SendH264Stream(nChanNo, (char*)pFrameBuffer, nFrameSize, nPts, nalu_type);
			}

		}
	}
	else if(media_type == _PS_FILE)
	{
		if (m_pRtpSend)
		{
			if(nChanNo == -1) //nChanNo为-1表示发送给所有通道
			{
				for(int i=0; i<MAX_RTP_CHANNEL; i++)
				{
					if(m_pRtpSend->IsWritable(i))
					{
						m_pRtpSend->SendPSStream(i, (char*)pFrameBuffer, nFrameSize, nPts,  mark_flag);
					}
				}
			}
			else
			{
				m_pRtpSend->SendPSStream(nChanNo, (char*)pFrameBuffer, nFrameSize, nPts,  mark_flag);
			}

		}
	}

	return 0;
}

关于PS流格式的一些学习资料:

https://www.cnblogs.com/yuweifeng/p/9717354.html

https://blog.csdn.net/y601500359/article/details/97649112

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

国标PS流打包和RTP发送代码 的相关文章

  • 【Java杂谈】Iterator(迭代器)的使用

    x1f3b8 Iterator是什么 xff1f 迭代器是Java提供的一种访问集合的方法 xff0c Iterator 是 Java 迭代器最简单的实现 xff0c 常用来访问ArrayList HashMap等类的对象 Iterator
  • orb 纯背景物体识别

    include lt chrono gt include lt iostream gt include lt opencv2 core core hpp gt include lt opencv2 features2d features2d
  • Windows逆向安全(一)之基础知识(九)

    汇编比较三种循环 众所周知 xff0c 在C语言可以使用可以使用三种循环 xff0c 分别是 xff1a while do while和for 本文从汇编的角度出发 xff0c 观察这三种循环的差异 范例代码 先贴出三种循环的代码 xff0
  • c++读取yolov5模型进行目标检测(读取摄像头实时监测)

    文章介绍 本文是篇基于yolov5模型的一个工程 xff0c 主要是利用c 43 43 将yolov5模型进行调用并测试 xff0c 从而实现目标检测任务 任务过程中主要重点有两个 xff0c 第一 版本问题 xff0c 第二配置问题 一
  • ROS初学(二):ROS的三种基本通信机制(来自赵虚左老师)

    一 综述 ROS引入通信机制 xff0c 是为了实现ROS节点 xff08 进程 xff09 之间的通信 利用ROS进程的分布式框架 xff0c 可以使得每个进程独立的工作 xff0c 甚至分布于不同的主机工作 机器人上的各种传感器 xff
  • webpack 5.5.1 compiled with 1 error in 63 ms

    新建项目空白目录 xff0c 并运行 npm init y 命令 xff0c 初始化包管理配置文件 package json 新建 src 源代码目录 新建 src gt index html 首页和 src gt index js 脚本文
  • TypeError: Class constructor ServeCommand cannot be invoked without ‘new‘

    安装webpack插件 webpack dev server配置时将package json里面webpack改为webpack serve后 xff0c 运行npm run 脚本名 时出现 TypeError Class construc
  • Field ‘browser‘ doesn‘t contain a valid alias configuration

    意思是字段 39 browser 39 不包含有效的别名配置 意思就是说你打包的css路径不对 这个是我的代码 这里js里面导入的css路径不对 xff0c css文件夹不是和index js平级 xff0c 应该是上级所以正确的代码应该是
  • Failed to load resource: the server responded with a status of 404 (Not Found)

    问题场景 xff1a 编写路由模块 解决方法 xff1a 1 先检测路径是否有写错 2 将vscode中开的插件vetur关闭 3 上面方法不管用时 xff0c 将浏览器插件关闭 xff0c 例如油猴 有什么错误望大佬指出 xff01 xf
  • Vue3.js【未完成】

    Vue3 js 如何关闭烦人的vscode的提示框 https blog csdn net liuyuemozhu article details 101056556 ES6模块化与异步编程高级用法 ES6模块化 1 回顾 xff1a no
  • npm ERR! code ERESOLVEnpm ERR! ERESOLVE unable to resolve dependency tree

    npm下载报错 xff0c 这个是因为npm版本问题 xff0c 例如我下载element ui运行npm i element ui S 因为版本问题我就无法下载 xff0c 解决办法就是在末尾加上 legacy peer deps 即np
  • 前端面试题

    浏览器 输入一个URL到页面过程中发生了什么 xff08 高频 xff09 首先在浏览器中输入URL 查找缓存 xff1a 浏览器先查看浏览器缓存 系统缓存 路由缓存中是否有该地址页面 xff0c 如果有则显示页面内容 如果没有则进行下一步
  • MATALAB动态爱心代码

    第一步 xff1a 先下载MATLAB软件 xff08 该代码只适用于此软件 xff0c 要下载软件才可运行 xff09 第二步 xff1a 点击主页 新建脚本 xff08 可自己给脚本命名 xff0c 我命名的是aixin xff09 第
  • Linux相关博文中使用的头文件

    收录博文中看到的已经封装好的文件时 xff0c 方便各位查看 Log hpp 日志信息 pragma once include lt iostream gt include lt cstdio gt include lt cstdarg g
  • 构造函数和析构函数执行顺序

    析构函数运行顺序 xff1a 1 主函数结束 xff1b 2 函数调用结束 xff1b 3 用delete删除 xff08 new创建的 xff09 对象时 注释代码运行结果 输入坐标6 xff0c 3 xff0c 6 xff0c 2 xf
  • 27.【C/C++ 最全vector数组的用法 (详解)】

    vector动态数组 xff08 一 xff09 什么是vector xff1f xff08 二 xff09 vector的作用是什么 xff08 三 xff09 经典用法 1 vector函数的定义 代码展示 效果展示 2 vector的
  • 析构函数运行顺序

    全局变量 xff0c 静态局部变量 xff0c 局部变量空间的堆分配和栈分配 其中全局变量和静态局部变量时从 静态存储区中划分的空间 xff0c 二者的区别在于作用域的不同 xff0c 全局变量作用域大于静态局部变量 xff08 只用于声明
  • 03_临界段的保护

    一 临界段的概念 所谓的临界段就是在执行时不能被中断的代码段 在FreeRTOS中 xff0c 临界段最常出现的地方就是对全局变量的操作 那么什么情况下临界段会被打断 xff1f 一个是系统调度 xff0c 还有一个是外部中断 在FreeR
  • Qt自定义窗口继承基类

    设计一个窗口基类模板 xff0c 方便子类的统一风格使用 xff0c 省略重写事件函数 xff0c 代码思路
  • ROS学习笔记4:创建工作空间和功能包

    一 linux命令行使用基础 xff1a 1 cd命令 xff08 change directory xff09 xff1a xff08 1 xff09 语法 xff1a cd dirName xff08 dirName xff1a 要切换

随机推荐

  • 一段代码给你讲清楚【链式存储结构】

    1 基础定义 链式存储结构 xff08 linked storage structure xff09 xff0c 是逻辑存储结构的一种 xff0c 链式存储结构中每一个逻辑元素用一个内存结点存储 xff0c 每一个结点都是单独分配的 xff
  • Flask之before_request与after_request

    Flask之before request与after request 更多的用法或说明请查看文档 xff1a https dormousehole readthedocs io en latest api html flask Flask
  • C++教程之迭代器Iterator

    前言 之前的两篇文章我们主要了解了vector和string的相关知识 xff0c 从中我们知道可以通过下标来访问vector的元素或者string的字符 xff0c 但是除了这种方式还有一种更为通用的方式获取元素 xff0c 那就是迭代器
  • Qt 绘制图表 - Qt Charts版

    一 前言 自从 Qt 发布以来 xff0c 给广大跨平台界面研发人员带来了无数的福利 但是Qt自己却一直没有提供自带的图表库 xff0c 这就使得 QWT QCustomPlot 等第三方图表库有了巨大的生存空间 xff0c 为了降低开发成
  • Qt 多线程使用moveToThread

    Qt有两种多线程的方法 xff0c 其中一种是继承QThread的run函数 xff0c 另外一种是把一个继承于QObject的类用moveToThread函数转移到一个Thread里 Qt4 8之前都是使用继承QThread的run这种方
  • Qt 之http学习

    在Qt网络编程中 xff0c 需要用到协议 xff0c 即HTTP 它是超文本传输协议 xff0c 它是一种文件传输协议 新建工程名为 http xff0c 然后选中QtNetwork模块 xff0c 最后Base class选择QWidg
  • QT之QString

    字符串类 QString xff1a 在Qt官方文档中是这样描述QString的 xff1a The QString class provides a Unicode character string 我们可以将做C 43 43 中的str
  • 什么是栈?什么是任务栈?

    在裸机系统中 xff0c 它们统统放在一个叫栈的地方 栈式单片机RAM里一段连续的内存空间 栈的大小一般在启动文件或者链接脚本里里面指定 最后由C库函数 main进行初始化 每个任务都是独立的 xff0c 互不干扰的 所以要为每个任务都分配
  • OPENMV上的目标检测,目标定位模型

    目标板定位模型 代码地址 前言 在17届省赛结束后 xff0c 因为担心国赛场地光照影响较大 xff0c 写了这个目标检测算法 但因为时间以及实力不足 xff0c 算法仅有了个雏形 xff0c 没能成功部署 xff0c 非常遗憾 今年寒假终
  • select函数当对端关闭后的状态

    struct timeval timeout 61 5 0 rset 61 allset nready 61 select maxfd 43 1 amp rset NULL NULL amp timeout 当使用select等待客户端发送
  • web渗透之攻击 Authentication-1-

    可利用常见漏洞 认证http明文传输 xff0c 未加密 xff0c 攻击者可通过中间人攻击获取默认密码弱口令 xff0c 简单的credentials xff0c 可以通过暴力破解获取通过其他漏洞绕过认证不正确的重置密码功能密码在本地存储
  • UART——学习总结

    UART 一 含义 xff1a 通用异步收发传输器 xff08 Universal Asynchronous Receiver Transmitter xff0c 通常称作UART xff09 是一种串行异步收发协议 xff0c 二 工作原
  • 海康威视web3.0+vue项目使用(踩坑之路)ps:附webVideoCtrl.js文件

    文章目录 1 海康威视web3 02 vue项目应用3 运行环境 最近公司开发了一版监控视频的vue项目 我作为实现需求的前端真的是难为我了 各种调查后知道海康威视有web开发包 跟海康威视的SDK联系后发给我两个web二次开发包 CH W
  • GB28181国标平台软件(包含服务器和客户端)

    GB28181是国家针对安防领域制定的一个通信协议标准 xff0c 目的是解决不同平台 不同设备间通信协议不统一的问题 GB28181标准从2011制定的第一个版本开始 xff0c 到最近的2016版本 xff0c 制定的协议内容越来越细
  • 国标服务器GBServerPlatform对接海康NVR的操作说明

    这一篇文章介绍QuickGBLink开发的国标服务器如何对接海康NVR xff08 网络硬盘录像机 xff09 xff0c 主要讲解相关的操作步骤 xff08 GBT28181平台软件下载地址 xff1a https github com
  • 国标服务器GBServerPlatform对接海康摄像头的操作说明

    这篇文章讲解一下QuickGBLink开发的国标服务器如何对接海康IPC xff08 GBT28181国标平台软件下载地址 xff1a https github com QuickGBLink88 GB28181 ServerPlatfor
  • 开源工具:ONVIF协议实现搜索局域网摄像机(IPC)+RTSP地址获取

    Onvif协议做安防监控的人应该都很熟悉 xff0c 它广泛被用在各种安防设备中 xff0c 比如IPC DVR NVR 在局域网中发现IPC设备很多就是基于Onvif协议实现的 本文章给大家介绍QuickGBLink开源的一个工具 xff
  • 开源C++ RTSP客户端(支持RTP Over TCP)

    QuickGBLink开源的这个RTSP客户端是用C 43 43 写的 xff0c 目前仅实现了RTP Over TCP传输 支持接收多种视音频格式的流 xff0c 视频格式支持MPEG4 H264 H265 xff0c 音频格式支持AAC
  • 开源工具:实现用FFmpeg接收RTSP/RTMP/HLS流和录制文件

    上一篇文章介绍了QuickGBLink开发的一个RTSP客户端 开源C 43 43 RTSP客户端 xff0c 实现了通过TCP接收RTP流的功能 xff0c 但是在局域网监控中 xff0c 很多时候RTSP服务器和客户端都是用UDP模式来
  • 国标PS流打包和RTP发送代码

    这个国标PS流打包 封包的代码来自于互联网 xff0c QuickGBLink在原先代码基础上做了一些改进 因为代码来自于别人的成果 xff0c 基于互联网知识分享的精神 xff0c 我们打算将其开源 xff0c 希望能对更多学习和开发国标