码迷,mamicode.com
首页 > 其他好文 > 详细

关于对H264码流的TS的封装的相关代码实现

时间:2015-08-11 23:28:27      阅读:169      评论:0      收藏:0      [点我收藏+]

标签:

1 写在开始之前

             在前段时间有分享一个H264封装ps流到相关文章的,这次和大家分享下将H264封装成TS流到相关实现,其实也是工作工作需要。依照上篇一样,分段说明每个数据头的封装情况,当然,一样也会加上rtp头,方便以后的这方面到需求,如果开发不需要的话,可   以自行屏蔽掉,当然需要主要buffer指针的移动情况


2 封装的各个头到规则要点
             整个封装过程也是和ps类似,但是最大到区别在于TS流到数据长度都是固定188大小来传输的,而PS流则是可变包结构,正因为两者在结构上到差异,导致了它们在传输误码上的不同抵抗力.TS流由于采用了固定长度的数据包,当传输误码破坏了某一个TS包的同步信息时,接收端可在固定的位置检测到下一个TS包到同步信息,从而恢复同步,避免数据丢失,PS流则长度可变到数据包,当某一个ps包同步信息丢失时,接收端就无法进行信息同步,也就无法确认下一步到同步信息,导致了严重到信息丢失。因此在环境恶劣的情况下, 传输码丢失比较严重,一般都采用TS流来进行避免,当网络环境比较稳定,传输误码概率小,这个时候采用PS流进行传送。
          关于TS流需要了解下节目映射表(PAT:Program Associate Table)以及节目映射表(PMT:Program Map Table),当发送到数据为视频数据关键帧的时候,需要在包头中添加PAT和PMT 
 
  具体结构体如下
  封装组成:(PAT +PMT) + TS + PES + H264 + (TS + H264 + TS + H264 ....)
  数据长度:PES包的长度=188字节-TS包头长度(4字节)-适应域长度(PES长度或者0)

  

注意:

   a    每次数据定长包188个字节,如果不足的则用1填充,这里填充时值每一个bit位都填充,memset就是最好选择
   b    因为我个人习惯,在封装到时候当为关键帧的时候,我直接丢了PAM+PMT+TS+PES 然后填充满188个字节,这样做提醒大家          是错误到,完全错误的,PES后必须跟H264数据。

   c    PES能表示的数据长度只有short, 两个字节,所以当数据长度超过的话,则需要考虑多个PES头

3 各个部件到头的伪代码实现

   

/*
 *@remark: 整体发送数据的抽象逻辑处理函数接口
 */
int rtsp_RTPPackage( RTP_SESSION_S *pRtpSender, int nFrameLen, StreamType_E enStreamType)
{
	int nRet = 0;
	int bVideo = 1 ;
	int nSendDataOff = 0;
	int nSendSize    = 0;	
	int nPayLoadSize = 0;
	int nHasSend     = 0;
	int IFrameFlag   = 0;
	char TSFrameHdr[1024];
	int nHead = 0;
	

	memset(TSFrameHdr, 0, 1024);
	memset(pRtpSender->stRtpPack, 0, RTP_MAX_PACKET_BUFF);
	bVideo = ((enStreamType == VIDEO_STREAM ) ? 1 : 0);
	
	// @remark: 判断数据是否为I帧,如果为I帧的话,加上PAT和PMT
	if( (bVideo == 1) && pRtpSender->stAvData.u8IFrame == 1)
	{
		if((nRet = mk_ts_pat_packet(TSFrameHdr +nSendDataOff, 
						pRtpSender->hHdlTs)) <= 0)	
		{
			DBG_INFO(" mk_ts_pat_packet failed!\n");
			return -1;
		}
		// @remark: 每次添加一个头的时候,必须注意指针到偏移量
		nSendDataOff += nRet;
		if((nRet = mk_ts_pmt_packet(TSFrameHdr + nSendDataOff, 
						pRtpSender->hHdlTs)) <= 0)	
		{
			DBG_INFO(" mk_ts_pmt_packet failed!\n");
			return -1;
		}
		nSendDataOff += nRet;
		
	}
	// @remark: 添加PS头,需要注意ps里也有一个计数的字段
	if((nRet = mk_ts_packet(TSFrameHdr + nSendDataOff, pRtpSender->hHdlTs, 
					1, bVideo, pRtpSender->stAvData.u8IFrame, pRtpSender->stAvData.u64TimeStamp)) <= 0 )
	{
		DBG_INFO(" mk_ts_packet failed!\n");
		return -1;
	}
	nSendDataOff  += nRet;
	//此字段是用来计算ts长度,因为ts包是固定188字节长度
	nHead = nRet;	
	
	// @remark: 添加PES头,后面就必须接H264数据了,不能通过1来填充
	if((nRet = mk_pes_packet(TSFrameHdr + nSendDataOff, bVideo, nFrameLen, 1, 
					pRtpSender->stAvData.u64TimeStamp, pRtpSender->stAvData.u64TimeStamp)) <= 0 )
	{
		DBG_INFO(" mk_pes_packet failed!\n");
		return -1;
	}
	nSendDataOff += nRet;	 
	nHead += nRet;
	
	// @remark: 如果第一次发送的数据长度大于剩余长度,则先发送ts包剩余长度的数据
	if( nFrameLen > (TS_LOAD_LEN - nHead))
	{
		memcpy(TSFrameHdr + nSendDataOff,  pRtpSender->stAvData.data, TS_LOAD_LEN - nHead);
		nSendDataOff += (TS_LOAD_LEN - nHead);
		nHasSend      = (TS_LOAD_LEN - nHead);
		if( rtsp_send_rtppack(TSFrameHdr, &nSendDataOff, pRtpSender->stAvData.u64TimeStamp, 0, (pRtpSender->stAvData.u8IFrame?1:0), bVideo, 1, pRtpSender) != 0 )
		{
			DBG_INFO(" rtsp_send_pack failed!\n");
			return -1;
		}
	}
	// @remark: 如果第一次发送数据长度小于ts头剩余长度,则,发送数据帧长度,剩余没有188长度的用1填充
	else 
	{
		memcpy(TSFrameHdr + nSendDataOff,  pRtpSender->stAvData.data, nFrameLen);
		nSendDataOff += nFrameLen;
		nHasSend      = nFrameLen;
		memset(TSFrameHdr +nSendDataOff, 0xFF, (TS_LOAD_LEN-nHead - nFrameLen));
		nSendDataOff += (TS_LOAD_LEN -nHead- nFrameLen);
		if( rtsp_send_rtppack(TSFrameHdr, &nSendDataOff, pRtpSender->stAvData.u64TimeStamp, 1, (pRtpSender->stAvData.u8IFrame?1:0), bVideo, 1, pRtpSender) != 0 )
		{
			DBG_INFO(" rtsp_send_rtppack failed!\n");
			return -1;
		}
	}
		

	// 对应的数据便宜长度,因为我处理的时候时固定1460到rtp包发送数据,所以这里会处理偏移,方便添加rtp头
	nPayLoadSize = RTP_MAX_PACKET_BUFF - 4 - RTP_HDR_LEN -  (4+6) * 7; // 减去rtp头,ts头 ,一个rtp包最多7个ts包
	nFrameLen -= (TS_LOAD_LEN - nHead);

	// @remark: 第二次发送数据了,此时发送数据时候,就需要外再添加ps头了
	while(nFrameLen > 0 )
	{

		nSendSize = (nFrameLen > nPayLoadSize) ? nPayLoadSize : nFrameLen;
		if( rtsp_send_rtppack(pRtpSender->stAvData.data + nHasSend, &nSendSize, pRtpSender->stAvData.u64TimeStamp, 
					((nSendSize == nFrameLen) ? 1 : 0),  IFrameFlag, bVideo, 0, pRtpSender) != 0 )
		{
			DBG_INFO(" rtsp_send_rtppack failed!\n");
			return -1;
		}
		nFrameLen -= nSendSize;
		nHasSend  += nSendSize;
		memset(pRtpSender->stRtpPack, 0, RTP_MAX_PACKET_BUFF);
		IFrameFlag = 0;
	}
	return 0;
}


/* 
 *@remark : 添加pat头 
 */
int mk_ts_pat_packet(char *buf, int handle)
{
	int nOffset = 0;
	int nRet = 0;
	
	if (!buf)
	{
		return 0;
	}

	if (0 >= (nRet = ts_header(buf, handle, TS_TYPE_PAT, 1)))
	{
		return 0;
	}
	nOffset += nRet;

	if (0 >= (nRet = ts_pointer_field(buf + nOffset)))
	{
		return 0;
	}
	nOffset += nRet;
	
	if (0 >= (nRet = ts_pat_header(buf + nOffset)))
	{
		return 0;
	}
	nOffset += nRet;

	// 每一个pat都会当成一个ts包来处理,所以每次剩余部分用1来充填完
	memset(buf + nOffset, 0xFF, TS_PACKET_SIZE - nOffset);
	return TS_PACKET_SIZE;
}
int ts_pat_header(char *buf)
{
	BITS_BUFFER_S bits;
	
	if (!buf)
	{
		return 0;
	}
	bits_initwrite(&bits, 32, (unsigned char *)buf);

	bits_write(&bits, 8, 0x00); 			// table id, 固定为0x00 
	
	bits_write(&bits, 1, 1);				// section syntax indicator, 固定为1
	bits_write(&bits, 1, 0);				// zero, 0
	bits_write(&bits, 2, 0x03);				// reserved1, 固定为0x03
	bits_write(&bits, 12, 0x0D);	 		// section length, 表示这个字节后面有用的字节数, 包括CRC32
	
	bits_write(&bits, 16, 0x0001);			// transport stream id, 用来区别其他的TS流
	
	bits_write(&bits, 2, 0x03);				// reserved2, 固定为0x03
	bits_write(&bits, 5, 0x00); 			// version number, 范围0-31
	bits_write(&bits, 1, 1);				// current next indicator, 0 下一个表有效, 1当前传送的PAT表可以使用
	
	bits_write(&bits, 8, 0x00);				// section number, PAT可能分为多段传输,第一段为00
	bits_write(&bits, 8, 0x00);				// last section number
	
	bits_write(&bits, 16, 0x0001);			// program number
	bits_write(&bits, 3, 0x07);				// reserved3和pmt_pid是一组,共有几个频道由program number指示
	bits_write(&bits, 13, TS_PID_PMT);		// pmt of pid in ts head

	bits_write(&bits, 8, 0x9F);				// CRC_32 先暂时写死
	bits_write(&bits, 8, 0xC7);
	bits_write(&bits, 8, 0x62);
	bits_write(&bits, 8, 0x58);

	bits_align(&bits);
	return bits.i_data;
}




/* 
 *@remaark: 添加PMT头
 */
int mk_ts_pmt_packet(char *buf, int handle)
{
	int nOffset = 0;
	int nRet = 0;
	
	if (!buf)
	{
		return 0;
	}

	if (0 >= (nRet = ts_header(buf, handle, TS_TYPE_PMT, 1)))
	{
		return 0;
	}
	nOffset += nRet;

	if (0 >= (nRet = ts_pointer_field(buf + nOffset)))
	{
		return 0;
	}
	nOffset += nRet;

	if (0 >= (nRet = ts_pmt_header(buf + nOffset)))
	{
		return 0;
	}
	nOffset += nRet;

	// 每一个pmt都会当成一个ts包来处理,所以每次剩余部分用1来充填完
	memset(buf + nOffset, 0xFF, TS_PACKET_SIZE - nOffset);
	return TS_PACKET_SIZE;
}
int ts_pmt_header(char *buf)
{
	BITS_BUFFER_S bits;

	if (!buf)
	{
		return 0;
	}

	bits_initwrite(&bits, 32, (unsigned char *)buf);

	bits_write(&bits, 8, 0x02);				// table id, 固定为0x02
	
	bits_write(&bits, 1, 1);				// section syntax indicator, 固定为1
	bits_write(&bits, 1, 0);				// zero, 0
	bits_write(&bits, 2, 0x03);				// reserved1, 固定为0x03
	bits_write(&bits, 12, 0x1C);	 		// section length, 表示这个字节后面有用的字节数, 包括CRC32
	
	bits_write(&bits, 16, 0x0001);			// program number, 表示当前的PMT关联到的频道号码
	
	bits_write(&bits, 2, 0x03);				// reserved2, 固定为0x03
	bits_write(&bits, 5, 0x00); 			// version number, 范围0-31
	bits_write(&bits, 1, 1);				// current next indicator, 0 下一个表有效, 1当前传送的PAT表可以使用
	
	bits_write(&bits, 8, 0x00);				// section number, PAT可能分为多段传输,第一段为00
	bits_write(&bits, 8, 0x00);				// last section number

	bits_write(&bits, 3, 0x07);				// reserved3, 固定为0x07
	bits_write(&bits, 13, TS_PID_VIDEO);	// pcr of pid in ts head, 如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF
	bits_write(&bits, 4, 0x0F);				// reserved4, 固定为0x0F
	bits_write(&bits, 12, 0x00);			// program info length, 前两位bit为00

	bits_write(&bits, 8, TS_PMT_STREAMTYPE_H264_VIDEO);		// stream type, 标志是Video还是Audio还是其他数据
	bits_write(&bits, 3, 0x07);				// reserved, 固定为0x07
	bits_write(&bits, 13, TS_PID_VIDEO);	// elementary of pid in ts head
	bits_write(&bits, 4, 0x0F);				// reserved, 固定为0x0F
	bits_write(&bits, 12, 0x00);			// elementary stream info length, 前两位bit为00

	bits_write(&bits, 8, TS_PMT_STREAMTYPE_11172_AUDIO);		// stream type, 标志是Video还是Audio还是其他数据
	bits_write(&bits, 3, 0x07);				// reserved, 固定为0x07
	bits_write(&bits, 13, TS_PID_AUDIO);	// elementary of pid in ts head
	bits_write(&bits, 4, 0x0F);				// reserved, 固定为0x0F
	bits_write(&bits, 12, 0x00);			// elementary stream info length, 前两位bit为00

	bits_write(&bits, 8, 0xA4);				// stream type, 标志是Video还是Audio还是其他数据
	bits_write(&bits, 3, 0x07);				// reserved, 固定为0x07
	bits_write(&bits, 13, 0x00A4);			// elementary of pid in ts head
	bits_write(&bits, 4, 0x0F);				// reserved, 固定为0x0F
	bits_write(&bits, 12, 0x00);			// elementary stream info length, 前两位bit为00

	bits_write(&bits, 8, 0x34);				//CRC_32	先暂时写死
	bits_write(&bits, 8, 0x12);
	bits_write(&bits, 8, 0xA3);
	bits_write(&bits, 8, 0x72);
	
	bits_align(&bits);
	return bits.i_data;
}


/* 
 *@remark: ts头的封装
 */
int mk_ts_packet(char *buf, int handle, int bStart, int bVideo, int bIFrame, unsigned long long timestamp)
{
	int nOffset = 0;
	int nRet = 0;
	
	if (!buf)
	{
		return 0;
	}

	if (0 >= (nRet = ts_header(buf, handle, bVideo ? TS_TYPE_VIDEO : TS_TYPE_AUDIO, bStart)))
	{
		return 0;
	}
	nOffset += nRet;

	if (0 >= (nRet = ts_adaptation_field(buf + nOffset, bStart, bVideo && (bIFrame), timestamp)))
	{
		return 0;
	}
	nOffset += nRet;

	return nOffset;
}

/* *@remark: ts头相关封装
 *  PSI 包括了PAT、PMT、NIT、CAT
 *  PSI--Program Specific Information, PAT--program association table, PMT--program map table
 *  NIT--network information table, CAT--Conditional Access Table
 *  一个网络中可以有多个TS流(用PAT中的ts_id区分)
 *  一个TS流中可以有多个频道(用PAT中的pnumber、pmt_pid区分)
 *  一个频道中可以有多个PES流(用PMT中的mpt_stream区分)
 */
int ts_header(char *buf, int handle, TS_TYPE_E type, int bStart)
{
	BITS_BUFFER_S bits;
	TS_MNG_S *pMng = (TS_MNG_S *)handle;

	if (!buf || !handle || TS_TYPE_BEGIN >= type || TS_TYPE_END <= type)
	{
		return 0;
	}

	bits_initwrite(&bits, 32, (unsigned char *)buf);

	bits_write(&bits, 8, 0x47);			// sync_byte, 固定为0x47,表示后面的是一个TS分组

	// payload unit start indicator根据TS packet究竟是包含PES packet还是包含PSI data而设置不同值
	// 1. 若包含的是PES packet header, 设为1,  如果是PES packet余下内容, 则设为0
	// 2. 若包含的是PSI data, 设为1, 则payload的第一个byte将是point_field, 0则表示payload中没有point_field
	// 3. 若此TS packet为null packet, 此flag设为0
	bits_write(&bits, 1, 0);			// transport error indicator
	bits_write(&bits, 1, bStart);		// payload unit start indicator
	bits_write(&bits, 1, 0);			// transport priority, 1表示高优先级
	if (TS_TYPE_PAT == type)
	{
		bits_write(&bits, 13, 0x00);	// pid, 0x00 PAT, 0x01 CAT
	}
	else if (TS_TYPE_PMT == type)
	{
		bits_write(&bits, 13, TS_PID_PMT);
	}
	else if (TS_TYPE_VIDEO == type)
	{
		bits_write(&bits, 13, TS_PID_VIDEO);
	}
	else if (TS_TYPE_AUDIO == type)
	{
		bits_write(&bits, 13, TS_PID_AUDIO);
	}

	bits_write(&bits, 2, 0);			// transport scrambling control, 传输加扰控制
	if (TS_TYPE_PAT == type || TS_TYPE_PMT == type)
	{
		// continuity counter, 是具有同一PID值的TS包之间的连续计数值
		// 当分组的adaption_field_control字段为00话10时,该字段不递增
		bits_write(&bits, 2, 0x01);		// adaptation field control, 00 forbid, 01 have payload, 10 have adaptation, 11 have payload and adaptation
		bits_write(&bits, 4, pMng->nPatCounter);	// continuity counter, 0~15
		
		if (TS_TYPE_PAT != type)
		{
			pMng->nPatCounter++;
			pMng->nPatCounter &= 0x0F;
		}
	}
	else
	{
		bits_write(&bits, 2, 0x03);		// 第一位表示有无调整字段,第二位表示有无有效负载
		bits_write(&bits, 4, pMng->nContinuityCounter);
		pMng->nContinuityCounter++;
		pMng->nContinuityCounter &= 0x0F;
	}
	
	bits_align(&bits);
	return bits.i_data;
}


/* 
 *remark:添加pes头
 */
int mk_pes_packet(char *buf, int bVideo, int length, int bDtsEn, unsigned long long pts, unsigned long long dts)
{
	PES_HEAD_S pesHead;
	PES_OPTION_S pesOption;
	PES_PTS_S pesPts;
	PES_PTS_S pesDts;

	if (!buf)
	{
		return 0;
	}

	if( bVideo == 1)
	{
		// 视频的采样频率为90kHZ,则增量为3600
		pts = pts * 9 / 100;	//  90000Hz
		dts = dts * 9 / 100;	//  90000Hz 
	}	
	else 
	{
		// 音频的话,则需要按照8000HZ来计算增量[需要的话]
		pts = pts * 8 / 1000;	// 8000Hz
		dts = dts * 8 / 1000;	// 8000Hz 
		
	}

	memset(&pesHead, 0, sizeof(pesHead));
	memset(&pesOption, 0, sizeof(pesOption));
	memset(&pesPts, 0, sizeof(pesPts));
	memset(&pesDts, 0, sizeof(pesDts));

	pesHead.startcode = htonl(0x000001) >> 8;
	pesHead.stream_id = bVideo ? 0xE0 : 0xC0;
	if (PES_MAX_SIZE < length)
	{
		pesHead.pack_len = 0;
	}
	else
	{
		pesHead.pack_len = htons(length + sizeof(pesOption) + sizeof(pesPts) + (bDtsEn ? sizeof(pesDts) : 0));
	}

	pesOption.fixed = 0x02;
	pesOption.pts_dts = bDtsEn ? 0x03 : 0x02;
	pesOption.head_len = sizeof(pesPts) + (bDtsEn ? sizeof(pesDts) : 0);

	pesPts.fixed2 = pesPts.fixed3 = pesPts.fixed4 = 0x01;
	pesPts.fixed1 = bDtsEn ? 0x03 : 0x02;
	pesPts.ts1 = (pts >> 30) & 0x07;
	pesPts.ts2 = (pts >> 22) & 0xFF;
	pesPts.ts3 = (pts >> 15) & 0x7F;
	pesPts.ts4 = (pts >> 7) & 0xFF;
	pesPts.ts5 = pts & 0x7F;
	
	pesDts.fixed1 = pesDts.fixed2 = pesDts.fixed3 = pesDts.fixed4 = 0x01;
	pesDts.ts1 = (dts >> 30) & 0x07;
	pesDts.ts2 = (dts >> 22) & 0xFF;
	pesDts.ts3 = (dts >> 15) & 0x7F;
	pesDts.ts4 = (dts >> 7) & 0xFF;
	pesDts.ts5 = dts & 0x7F;

	char *head = buf;
	memcpy(head, &pesHead, sizeof(pesHead));
	head += sizeof(pesHead);
	memcpy(head, &pesOption, sizeof(pesOption));
	head += sizeof(pesOption);
	memcpy(head, &pesPts, sizeof(pesPts));
	head += sizeof(pesPts);
	if (bDtsEn)
	{
		memcpy(head, &pesDts, sizeof(pesDts));
		head += sizeof(pesPts);
	}
	
	return (head - buf);
}



/* 
 *@remark: 最后封装rtp头并发送最终封装好到完整的数据包
 */
int rtsp_send_rtppack(char *Databuf, int *datalen, unsigned long curtimestamp, int mark_flag, int IFrameFlag, int bVideo, int nFrameStart, RTP_SESSION_S *pRtpSender)
{
	int nHasSend     = 0;
	int nRet		 = 0;	
	int nTsHeadNum   = 0;
	int nHadDataLen  = 0;
	int nTcpSendLen  = 0;
	static unsigned short cSeqnum;


	// @remark:表示为数据的第一次发送,所以不需要额外再添加ts头
	if( nFrameStart == 1 )
	{
		nRet = mk_rtp_packet(pRtpSender->stRtpPack + nHasSend, mark_flag, IFrameFlag, bVideo, ++cSeqnum, (curtimestamp * 9/100)); 
		nHasSend += nRet;
		memcpy(pRtpSender->stRtpPack + nHasSend, Databuf, *datalen);
		nHasSend += *datalen;
	}
	else  // 不是第一次发送此帧数据的话,则需要添加封装新的ts包,并添加ts头
	{
		// rtp+ rtp_ext + ts  +data 
		nRet = mk_rtp_packet(pRtpSender->stRtpPack + nHasSend, mark_flag, IFrameFlag, bVideo, ++cSeqnum, (curtimestamp * 9/100)); 
		nHasSend += nRet;
		while(*datalen > 0 && nTsHeadNum < 7)
		{
			nRet = mk_ts_packet(pRtpSender->stRtpPack + nHasSend , pRtpSender->hHdlTs, 0, bVideo, (IFrameFlag > 0 ? 1:0), curtimestamp);
			nHasSend += nRet;
			if(*datalen < (TS_LOAD_LEN- nRet))
			{
				memcpy(pRtpSender->stRtpPack + nHasSend, Databuf + nHadDataLen, *datalen);
				nHasSend	+= *datalen;
				nHadDataLen += *datalen;	
			
				//不够Ts188用1补充	
				memset(pRtpSender->stRtpPack + nHasSend, 0xFF, TS_LOAD_LEN- nRet - (*datalen));
				nHasSend += (TS_LOAD_LEN - nRet - *datalen);
			}	
			else 
			{
				memcpy(pRtpSender->stRtpPack + nHasSend, Databuf + nHadDataLen, TS_LOAD_LEN - nRet);
				nHasSend    += (TS_LOAD_LEN - nRet);
				*datalen    -= (TS_LOAD_LEN - nRet);
				nHadDataLen += (TS_LOAD_LEN - nRet);	
			}
			nTsHeadNum ++; 
		}
		*datalen = nHadDataLen; //实际发送裸数据到长度
	}


	if(pRtpSender->RtspsockFd <= 0 )
	{
		DBG_INFO("send rtp packet socket error\n");	
		return -1;
	}
	
	nTcpSendLen = hi_tcp_noblock_send(pRtpSender->RtspsockFd, pRtpSender->stRtpPack, nHasSend, NULL,1500); 
	if(nTcpSendLen != nHasSend )
	{
		DBG_INFO("send rtp packet failed:%s\n",strerror(errno));	
		return -1;
	}
	return 0;
}


/* 
 *remark: 上面用到的一些宏定义和一些关于字节操作的函数,很多一些开源到视频处理的库都能看到,
          为了方便也都将贴出来分享,当然也可以参考下vlc里面的源码
 */

/*@remark: 常量定义 */
#define TS_PID_PMT		(0x62)
#define TS_PID_VIDEO	(0x65)
#define TS_PID_AUDIO	(0x84)
#define TS_PMT_STREAMTYPE_11172_AUDIO	(0x03)
#define TS_PMT_STREAMTYPE_13818_AUDIO	(0x04)
#define TS_PMT_STREAMTYPE_AAC_AUDIO		(0x0F)
#define TS_PMT_STREAMTYPE_H264_VIDEO	(0x1B)

/* @remark: 结构体定义 */
typedef struct
{
	int i_size;				// p_data字节数
	int i_data;				// 当前操作字节的位置
	unsigned char i_mask;	// 当前操作位的掩码
	unsigned char *p_data;	// bits buffer
} BITS_BUFFER_S;

typedef struct
{
	unsigned int startcode		: 24;	// 固定为00 00 01
	unsigned int stream_id		: 8;	// 0xC0-0xDF audio stream, 0xE0-0xEF video stream, 0xBD Private stream 1, 0xBE Padding stream, 0xBF Private stream 2
	unsigned short pack_len;			// PES packet length
} __attribute__ ((packed)) PES_HEAD_S;

typedef struct
{
#if (BYTE_ORDER == LITTLE_ENDIAN)
	unsigned char original		: 1;	// original or copy, 原版或拷贝
	unsigned char copyright		: 1;	// copyright flag
	unsigned char align			: 1;	// data alignment indicator, 数据定位指示符
	unsigned char priority		: 1;	// PES priority
	unsigned char scramb		: 2;	// PES Scrambling control, 加扰控制
	unsigned char fixed			: 2;	// 固定为10

	unsigned char exten			: 1;	// PES extension flag
	unsigned char crc			: 1;	// PES CRC flag
	unsigned char acopy			: 1;	// additional copy info flag
	unsigned char trick			: 1;	// DSM(Digital Storage Media) trick mode flag
	unsigned char rate			: 1;	// ES rate flag, ES流速率标志
	unsigned char escr			: 1;	// ESCR(Elementary Stream Clock Reference) flag, ES流时钟基准标志
	unsigned char pts_dts		: 2;	// PTS DTS flags, 00 no PTS and DTS, 01 forbid, 10 have PTS, 11 have PTS and DTS
#elif (BYTE_ORDER == BIG_ENDIAN)
	unsigned char fixed			: 2;	// 固定为10
	unsigned char scramb		: 2;	// PES Scrambling control, 加扰控制
	unsigned char priority		: 1;	// PES priority
	unsigned char align			: 1;	// data alignment indicator, 数据定位指示符
	unsigned char copyright		: 1;	// copyright flag
	unsigned char original		: 1;	// original or copy, 原版或拷贝
	
	unsigned char pts_dts		: 2;	// PTS DTS flags, 00 no PTS and DTS, 01 forbid, 10 have PTS, 11 have PTS and DTS
	unsigned char escr			: 1;	// ESCR(Elementary Stream Clock Reference) flag, ES流时钟基准标志
	unsigned char rate			: 1;	// ES rate flag, ES流速率标志
	unsigned char trick			: 1;	// DSM(Digital Storage Media) trick mode flag
	unsigned char acopy			: 1;	// additional copy info flag
	unsigned char crc			: 1;	// PES CRC flag
	unsigned char exten			: 1;	// PES extension flag
#endif

	unsigned char head_len;				// PES header data length
} __attribute__ ((packed)) PES_OPTION_S;

typedef struct
{// ts total 33 bits
#if (BYTE_ORDER == LITTLE_ENDIAN)
	unsigned char fixed2		: 1;	// 固定为1
	unsigned char ts1			: 3;	// bit30-32
	unsigned char fixed1		: 4;	// DTS为0x01, PTS为0x02, PTS+DTS则PTS为0x03
	
	unsigned char ts2;					// bit22-29
	unsigned char fixed3		: 1;	// 固定为1
	unsigned char ts3			: 7;	// bit15-21

	unsigned char ts4;					// bit7-14
	unsigned char fixed4		: 1;	// 固定为1
	unsigned char ts5			: 7;	// bit0-6
#elif (BYTE_ORDER == BIG_ENDIAN)
	unsigned char fixed1		: 4;	// DTS为0x01, PTS为0x02, PTS+DTS则PTS为0x03
	unsigned char ts1			: 3;	// bit30-32
	unsigned char fixed2		: 1;	// 固定为1

	unsigned char ts2;					// bit22-29
	unsigned char ts3			: 7;	// bit15-21
	unsigned char fixed3		: 1;	// 固定为1

	unsigned char ts4;					// bit7-14
	unsigned char ts5			: 7;	// bit0-6
	unsigned char fixed4		: 1;	// 固定为1
#endif
} __attribute__ ((packed)) PES_PTS_S;


/* remark:接口函数定义 */
int bits_initwrite(BITS_BUFFER_S *p_buffer, int i_size, unsigned char *p_data)
{
	if (!p_data)
	{
		return -1;
	}
	p_buffer->i_size = i_size;
	p_buffer->i_data = 0;
	p_buffer->i_mask = 0x80;
	p_buffer->p_data = p_data;
	p_buffer->p_data[0] = 0;
	return 0;
}

void bits_align(BITS_BUFFER_S *p_buffer)
{
	if (p_buffer->i_mask != 0x80 && p_buffer->i_data < p_buffer->i_size)
	{
		p_buffer->i_mask = 0x80;
		p_buffer->i_data++;
		p_buffer->p_data[p_buffer->i_data] = 0x00;
	}
}

inline void bits_write(BITS_BUFFER_S *p_buffer, int i_count, unsigned long i_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)
		{
			p_buffer->i_data++;
			p_buffer->i_mask = 0x80;
		}
	}
}


int bits_initread(BITS_BUFFER_S *p_buffer, int i_size, unsigned char *p_data)
{
	if (!p_data)
	{
		return -1;
	}
	p_buffer->i_size = i_size;
	p_buffer->i_data = 0;
	p_buffer->i_mask = 0x80;
	p_buffer->p_data = p_data;
	return 0;
}

inline int bits_read(BITS_BUFFER_S *p_buffer, int i_count, unsigned long *i_bits)
{
	if (!i_bits)
	{
		return -1;
	}
	*i_bits = 0;
	
	while (i_count > 0)
	{
		i_count--;

		if (p_buffer->p_data[p_buffer->i_data] & p_buffer->i_mask)
		{
			*i_bits |= 0x01;
		}

		if (i_count)
		{
			*i_bits = *i_bits << 1;
		}
		
		p_buffer->i_mask >>= 1;
		if(p_buffer->i_mask == 0)
		{
			p_buffer->i_data++;
			p_buffer->i_mask = 0x80;
		}
	}

	return 0;
}

5 写在最后
   看过我上一篇的关于ps封装的可能会注意的,关于压字节的处理,两篇博文到处理方式有些差异。关于我这个我简单说两点
   第一次是这个ts的处理里面封装是另外一个同事实现的,我因为用到所以拿来使用,但是上次调用封装都是自己完成。第二个就是
   ps和ts的处理方式不一样。一个定长,一个不定长。所以这样处理,也挺好的,我也有点懒,所以没有改过来。
   


版权声明:本文为博主原创文章,未经博主允许不得转载。

关于对H264码流的TS的封装的相关代码实现

标签:

原文地址:http://blog.csdn.net/max_min_go/article/details/39463675

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!