这个国标PS流打包/封包的代码来自于互联网,QuickGBLink在原先代码基础上做了一些改进。因为代码来自于别人的成果,基于互联网知识分享的精神,我们打算将其开源,希望能对更多学习和开发国标的开发者带来帮助。
这个源码集成了PS封包和RTP发送的功能,具体它支持:
1. 输入一个H264 NALU或一帧数据,对传入的数据打包成PS流,对数据进行切分,并用RTP发送。
2. 传入一个PS帧,对数据进行切分,用RTP发送。
头文件rtpsend.h:
#ifndef RTPSEND_H
#define RTPSEND_H
//#include
#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
//#include
//#include
//#include
//#include
//#include
//#include
#include
#include
#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 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; iIsWritable(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; iIsWritable(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