1)rtsp的应答少了一个\r\n,费了我一天, :(
2)h.264的rtp发送。
(access violation崩溃,又看不到调用堆栈信息时,肯定就是内存操作越界导致的,多看看memcpy操作的拷贝的字节数)
#define PACKET_BUFFER_END (unsigned int)0x00000000 // 包结束标志
#define MAX_RTP_PKT_LENGTH 1400 // 最大RTP包长度
#define DEST_IP "127.0.0.1" // VLC 播放器IP地址
#define DEST_PORT 2234 // VLC 播放器端口地址
#define H264 96 //
// 定义RTP固定头结构
typedef struct
{
/* byte 0 */
unsigned char csrc_len:4; /* CSRC 计数4位 */
unsigned char extension:1; /* 扩展1位 */
unsigned char padding:1; /* 填充1位 */
unsigned char version:2; /* 版本2位 */
/* byte 1 */
unsigned char payload:7; /* 负载类型 */
unsigned char marker:1; /* 标志1位 */
/* bytes 2, 3 */
unsigned short seq_no;
/* bytes 4-7 */
unsigned long timestamp;
/* bytes 8-11 */
unsigned long ssrc; /* 实际上它是一个随即生成的ID,表示了一个RTP连接。在应用的时候,确保这个ID唯一就可以了。*/
} RTP_FIXED_HEADER;
typedef struct {
//byte 0
unsigned char TYPE:5;
unsigned char NRI:2;
unsigned char F:1;
} NALU_HEADER; /* 1 BYTES */
typedef struct
{
int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
unsigned max_size; //! Nal Unit Buffer size
int forbidden_bit; //! should be always FALSE
int nal_reference_idc; //! NALU_PRIORITY_xxxx
int nal_unit_type; //! NALU_TYPE_xxxx
char *buf; //! contains the first byte followed by the EBSP
unsigned short lost_packets; //! true, if packet loss is detected
} NALU_t;
typedef struct {
//byte 0
unsigned char TYPE:5;
unsigned char NRI:2;
unsigned char F:1;
} FU_INDICATOR; /**//* 1 BYTES */
typedef struct {
//byte 0
unsigned char TYPE:5;
unsigned char R:1;
unsigned char E:1;
unsigned char S:1;
} FU_HEADER; /**//* 1 BYTES */
#define PACKET_BUFFER_END (unsigned int)0x00000000 // 包结束标志
#define MAX_RTP_PKT_LENGTH 1400 // 最大RTP包长度
#define DEST_IP "127.0.0.1" // VLC 播放器IP地址
#define DEST_PORT 2234 // VLC 播放器端口地址
#define H264 96 //
// 定义RTP固定头结构
typedef struct
{
/* byte 0 */
unsigned char csrc_len:4; /* CSRC 计数4位 */
unsigned char extension:1; /* 扩展1位 */
unsigned char padding:1; /* 填充1位 */
unsigned char version:2; /* 版本2位 */
/* byte 1 */
unsigned char payload:7; /* 负载类型 */
unsigned char marker:1; /* 标志1位 */
/* bytes 2, 3 */
unsigned short seq_no;
/* bytes 4-7 */
unsigned long timestamp;
/* bytes 8-11 */
unsigned long ssrc; /* 实际上它是一个随即生成的ID,表示了一个RTP连接。在应用的时候,确保这个ID唯一就可以了。*/
} RTP_FIXED_HEADER;
typedef struct {
//byte 0
unsigned char TYPE:5;
unsigned char NRI:2;
unsigned char F:1;
} NALU_HEADER; /* 1 BYTES */
typedef struct
{
int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
unsigned max_size; //! Nal Unit Buffer size
int forbidden_bit; //! should be always FALSE
int nal_reference_idc; //! NALU_PRIORITY_xxxx
int nal_unit_type; //! NALU_TYPE_xxxx
char *buf; //! contains the first byte followed by the EBSP
unsigned short lost_packets; //! true, if packet loss is detected
} NALU_t;
typedef struct {
//byte 0
unsigned char TYPE:5;
unsigned char NRI:2;
unsigned char F:1;
} FU_INDICATOR; /**//* 1 BYTES */
typedef struct {
//byte 0
unsigned char TYPE:5;
unsigned char R:1;
unsigned char E:1;
unsigned char S:1;
} FU_HEADER; /**//* 1 BYTES */
int CStreamer::SendRtpPacket(char * packet, int nLen)
{
// TRACE("send %d bytes\n", nLen);
sockaddr_in RecvAddr;
int RecvLen = sizeof(RecvAddr);
// get client address for UDP transport
getpeername(m_Client,(struct sockaddr*)&RecvAddr,&RecvLen);
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(m_RtpClientPort);
int len = 0;
len = sendto(m_RtpSocket, packet, nLen, 0, (SOCKADDR *) & RecvAddr, sizeof(RecvAddr));
return len;
}
//packet为一帧h.264数据, 不包含h.264前缀码
void CStreamer::SendH264Packet(unsigned char * packet, int nLen)
{
#define RTP_BUF_LEN 2048
if (!packet) {
return;
}
NALU_t nalu;
memset(&nalu, 0, sizeof(NALU_t));
NALU_t *pNalu = &nalu;
pNalu->buf = (char *)packet;
pNalu->len = nLen;
pNalu->forbidden_bit = pNalu->buf[0] & 0x80; //1 bit
pNalu->nal_reference_idc = pNalu->buf[0] & 0x60; // 2 bit
pNalu->nal_unit_type = (pNalu->buf[0]) & 0x1f;// 5 bit
char * nalu_payload;
RTP_FIXED_HEADER *rtp_hdr;
NALU_HEADER *nalu_hdr;
FU_INDICATOR *fu_ind;
FU_HEADER *fu_hdr;
int bytes=0;
char RtpBuf[RTP_BUF_LEN];
static unsigned int ts_current=0;
static unsigned short seq_num = 0;
unsigned int timestamp_increse=0;
float framerate = 25;
timestamp_increse=(unsigned int)(90000.0 / framerate);
memset(RtpBuf,0,RTP_BUF_LEN);//清空RtpBuf;此时会将上次的时间戳清空,因此需要ts_current来保存上次的时间戳值
// rtp固定包头,为12字节,该句将RtpBuf[0]的地址赋给rtp_hdr,以后对rtp_hdr的写入操作将直接写入RtpBuf。
// Prepare the first 4 byte of the packet. This is the Rtp over Rtsp header in case of TCP based transport
rtp_hdr =(RTP_FIXED_HEADER*)&RtpBuf[0];
//设置RTP HEADER,
rtp_hdr->payload = H264; //负载类型号,
rtp_hdr->version = 2; //版本号,此版本固定为2
rtp_hdr->marker = 0; //标志位,由具体协议规定其值。
rtp_hdr->ssrc = htonl(10); //随机指定为10,并且在本RTP会话中全局唯一
// 当一个NALU小于MAX_RTP_PKT_LENGTH字节的时候,采用一个单RTP包发送
if(pNalu->len <= MAX_RTP_PKT_LENGTH) {
//设置rtp M 位;
rtp_hdr->marker=1;
rtp_hdr->seq_no = htons(seq_num ++); //序列号,每发送一个RTP包增1
//设置NALU HEADER,并将这个HEADER填入RtpBuf[12]
nalu_hdr =(NALU_HEADER*)&RtpBuf[12]; //将RtpBuf[12]的地址赋给nalu_hdr,之后对nalu_hdr的写入就将写入RtpBuf中;
nalu_hdr->F=pNalu->forbidden_bit;
nalu_hdr->NRI=pNalu->nal_reference_idc>>5;//有效数据在n->nal_reference_idc的第6,7位,需要右移5位才能将其值赋给nalu_hdr->NRI。
nalu_hdr->TYPE=pNalu->nal_unit_type;
nalu_payload=&RtpBuf[13];//同理将RtpBuf[13]赋给nalu_payload
memcpy(nalu_payload,pNalu->buf+1,pNalu->len-1);//去掉nalu头的nalu剩余内容写入RtpBuf[13]开始的字符串。
ts_current=ts_current+timestamp_increse;
rtp_hdr->timestamp=htonl(ts_current);
bytes=pNalu->len + 12 ; //获得RtpBuf的长度,为nalu的长度(包含NALU头但除去起始前缀)加上rtp_header的固定长度12字节
SendRtpPacket(RtpBuf, bytes); //发送rtp包
} else {
//得到该nalu需要用多少长度为MAX_RTP_PKT_LENGTH字节的RTP包来发送
int k=0, l=0;
k=pNalu->len/MAX_RTP_PKT_LENGTH; //需要k个MAX_RTP_PKT_LENGTH字节的RTP包
l=pNalu->len%MAX_RTP_PKT_LENGTH; //最后一个RTP包的需要装载的字节数
int t=0;//用于指示当前发送的是第几个分片RTP包
ts_current=ts_current+timestamp_increse;
rtp_hdr->timestamp=htonl(ts_current);
while(t<=k) {
rtp_hdr->seq_no = htons(seq_num ++); //序列号,每发送一个RTP包增1
if(!t) //发送一个需要分片的NALU的第一个分片,置FU HEADER的S位
{
//设置rtp M 位;
rtp_hdr->marker=0;
//设置FU INDICATOR,并将这个HEADER填入RtpBuf[12]
fu_ind =(FU_INDICATOR*)&RtpBuf[12]; //将RtpBuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入RtpBuf中;
fu_ind->F=pNalu->forbidden_bit;
fu_ind->NRI=pNalu->nal_reference_idc>>5;
fu_ind->TYPE=28;
//设置FU HEADER,并将这个HEADER填入RtpBuf[13]
fu_hdr =(FU_HEADER*)&RtpBuf[13];
fu_hdr->E=0;
fu_hdr->R=0;
fu_hdr->S=1;
fu_hdr->TYPE=pNalu->nal_unit_type;
nalu_payload=&RtpBuf[14];//同理将RtpBuf[14]赋给nalu_payload
memcpy(nalu_payload,pNalu->buf+1,MAX_RTP_PKT_LENGTH);//去掉NALU头
bytes=MAX_RTP_PKT_LENGTH+14; //获得RtpBuf的长度,为nalu的长度(除去起始前缀和NALU头)加上rtp_header,fu_ind,fu_hdr的固定长度14字节
SendRtpPacket(RtpBuf, bytes); //发送rtp包
t++;
} else {
if(k==t) { // 发送最后一个零头,清零FU HEADER的S位,置FU HEADER的E位.注意最后一个分片的长度
// 可能超过MAX_RTP_PKT_LENGTH字节(当l>1386时)。
// 设置rtp M 位;当前传输的是最后一个分片时该位置1
rtp_hdr->marker=1;
//设置FU INDICATOR,并将这个HEADER填入RtpBuf[12]
fu_ind =(FU_INDICATOR*)&RtpBuf[12]; //将RtpBuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入RtpBuf中;
fu_ind->F=pNalu->forbidden_bit;
fu_ind->NRI=pNalu->nal_reference_idc>>5;
fu_ind->TYPE=28;
//设置FU HEADER,并将这个HEADER填入RtpBuf[13]
fu_hdr =(FU_HEADER*)&RtpBuf[13];
fu_hdr->R=0;
fu_hdr->S=0;
fu_hdr->TYPE=pNalu->nal_unit_type;
fu_hdr->E=1;
nalu_payload=&RtpBuf[14];//同理将RtpBuf[14]的地址赋给nalu_payload
if (l>1)
{
memcpy(nalu_payload,pNalu->buf+t*MAX_RTP_PKT_LENGTH+1,l-1);//将nalu最后剩余的l-1(去掉了一个字节的NALU头)字节内容写入RtpBuf[14]开始的字符串。
bytes=l-1+14; //获得RtpBuf的长度,为剩余nalu的长度l-1加上rtp_header,FU_INDICATOR,FU_HEADER三个包头共14字节
}
else
{
bytes=14;
}
SendRtpPacket(RtpBuf, bytes); //发送rtp包
t++;
}else {
if(t<k) { // 发送其他整块(MAX_RTP_PKT_LENGTH)
//设置rtp M 位;
rtp_hdr->marker=0;
//设置FU INDICATOR,并将这个HEADER填入RtpBuf[12]
fu_ind =(FU_INDICATOR*)&RtpBuf[12]; //将RtpBuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入RtpBuf中;
fu_ind->F=pNalu->forbidden_bit;
fu_ind->NRI=pNalu->nal_reference_idc>>5;
fu_ind->TYPE=28;
//设置FU HEADER,并将这个HEADER填入RtpBuf[13]
fu_hdr =(FU_HEADER*)&RtpBuf[13];
//fu_hdr->E=0;
fu_hdr->R=0;
fu_hdr->S=0;
fu_hdr->E=0;
fu_hdr->TYPE=pNalu->nal_unit_type;
nalu_payload=&RtpBuf[14];//同理将RtpBuf[14]的地址赋给nalu_payload
memcpy(nalu_payload,pNalu->buf+t*MAX_RTP_PKT_LENGTH+1,MAX_RTP_PKT_LENGTH);//去掉起始前缀的nalu剩余内容写入RtpBuf[14]开始的字符串。
bytes=MAX_RTP_PKT_LENGTH+14; //获得RtpBuf的长度,为nalu的长度(除去原NALU头)加上rtp_header,fu_ind,fu_hdr的固定长度14字节
SendRtpPacket(RtpBuf, bytes); //发送rtp包
t++;
}
}
}
}
}
}