H264码流处理详解

Wesley13
• 阅读 634

 码流(Data Rate)是指视频文件在单位时间内使用的数据流量,也叫码率,是视频编码中画面质量控制中最重要的部分。同样分辨率下,视频文件的码流越大,压缩比就越小,画面质量就越好。

一、简介 H.264的主要目标:1.高的视频压缩比 2.良好的网络亲和性 解决方案: (1)VCL video coding layer 视频编码层。VCL:核心算法引擎,块,宏块及片的语法级别的定义 (2)NAL:NAL network abstraction layer 网络提取层。片级以上的语法级别(如序列参数集和图像参数集),同时支持以下功能:独立片解码,起始码唯一保证,SEI以及流 格式编码数据传送。

注释: VCL设计目标:尽可能地独立于网络的情况下进行高效的编解码,NAL设计目标:根据不同的网络把数据打包成相应的格式,将VCL产生的比特字符串适配到各种各样的网络和多元环境中。NALU头结构:NALU类型(5bit)、重要性指示位(2bit)、禁止位(1bit)。 NALU类型:1~12由H.264使用,24~31由H.264以外的应用使用。 重要性指示:标志该NAL单元用于重建时的重要性,值越大,越重要。 禁止位:网络发现NAL单元有比特错误时可设置该比特为1,以便接收方丢掉该单元。

二.NAL语法语义

NAL层句法:在编码器输出的码流中,数据的基本单元是句法元素。句法表征句法元素的组织结构。语义阐述句法元素的具体含义。分组都有头部,解码器可以很方便的检测出NAL的分界,依次取出NAL进行解码。但为了节省码流,H.264没有另外在NAL的头部设立表示起始位置的句法元素。如果编码数据是存储在介质上的,由于NAL是依次紧密相连的,解码器就无法在数据流中分辨出每个NAL的起始位置和终止位置。解决方案:在每个NAL前添加起始码:0X000001

三、H.264的NAL层处理

结构示意图: NAL以NALU(NAL unit)为单元来支持编码数据在基于分组交换技术网络中传输。 它定义了符合传输层或存储介质要求的数据格式,同时给出头信息,从而提供了视频编码和外部世界的接口。 NALU:定义了可用于基于分组和基于比特流系统的基本格式 RTP封装:只针对基于NAL单元的本地NAL接口。

Ⅲ种不同的数据形式: SODB 数据比特串:最原始的编码数据 RBSP 原始字节序列载荷:在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit“1”)若干比特0以便字节对齐 EBSP 扩展字节序列载荷:在RBSP基础上填加了仿校验字节(0X03)它的原因是: 在NALU加到Annexb上时,需要添加每组 NALU之前的开始码StartCodePrefix,如果该NALU对应的slice为一帧的开始则用4位字节表示,ox00000001,否则用3位 字节表示ox000001.为了使NALU主体中不包括与开始码相冲突的,在编码时,每遇到两个字节连续为0,就插入一个字节的0x03。解码时将 0x03去掉。也称为脱壳操作 处理过程: 1. 将VCL层输出的SODB封装成nal_unit, Nal_unit是一个通用封装格式,可以适用于有序字节流方式和IP包交换方式。 2. 针对不同的传送网络(电路交换|包交换),将nal_unit 封装成针对不同网络的封装格式。 第一步的具体过程: VCL层输出的比特流SODB(String Of Data Bits),到nal_unit之间,经过了以下三步处理: 1.SODB字节对齐处理后封装成RBSP(Raw Byte Sequence Payload)。 2.为防止RBSP的字节流与有序字节流传送方式下的SCP(start_code_prefix_one_3bytes,0x000001)出现字节竞 争情形,循环检测RBSP前三个字节,在出现字节竞争时在第三字节前加入emulation_prevention_three_byte (0x03)。 具体方法: nal_unit( NumBytesInNALunit ) {

forbidden_zero_bit

nal_ref_idc

nal_unit_type

NumBytesInRBSP = 0

for( i = 1; i < NumBytesInNALunit; i++ ) {

if( i + 2 < NumBytesInNALunit && next_bits( 24 ) = = 0x000003 ) {

rbsp_byte[ NumBytesInRBSP++ ]

rbsp_byte[ NumBytesInRBSP++ ]

i += 2

emulation_prevention_three_byte

} else

rbsp_byte[ NumBytesInRBSP++ ]

}

}

  1. 防字节竞争处理后的RBSP再加一个字节的header(forbidden_zero_bit+ nal_ref_idc+ nal_unit_type),封装成nal_unit.

第二步的具体过程: case1:有序字节流的封装

byte_stream_nal_unit( NumBytesInNALunit ) {

while( next_bits( 24 ) != 0x000001 )

zero_byte

if( more_data_in_byte_stream( ) ) {

start_code_prefix_one_3bytes nal_unit( NumBytesInNALunit )

}

} 类似H.320和MPEG-2/H.222.0等传输系统,传输NAL作为有序连续字节或比特流,同时要依靠数据本身识别NAL单元边界。在这样的应用系 统中,H.264/AVC规范定义了字节流格式,每个NAL单元前面增加3个字节的前缀,即同步字节。在比特流应用中,每个图像需要增加一个附加字节作为 边界定位。还有一种可选特性,在字节流中增加附加数据,用做扩充发送数据量,能实现快速边界定位,恢复同步

Case2:IP网络的RTP打包封装

分组打包的规则

(1)额外开销要少,使MTU尺寸在100~64k字节范围都可以; 

(2)不用对分组内的数据解码就可以判别该分组的重要性; 

(3)载荷规范应当保证不用解码就可识别由于其他的比特丢失而造成的分组不可解码; 

(4)支持将NALU分割成多个RTP分组; 

 (5)支持将多个NALU汇集在一个RTP分组中。

RTP的头标可以是NALU的头标,并可以实现以上的打包规则。 

一个RTP分组里放入一个NALU,将NALU(包括同时作为载荷头标的NALU头)放入RTP的载荷中,设置RTP头标值。为了避免IP层对大分组的再 一次分割,片分组的大小一般都要小于MTU尺寸。由于包传送的路径不同,解码端要重新对片分组排序,RTP包含的次序信息可以用来解决这一问题。 

NALU分割

对于预先已经编码的内容,NALU可能大于MTU尺寸的限制。虽然IP层的分割可以使数据块小于64千字节,但无法在应用层实现保护,从而降低了非等重保 护方案的效果。由于UDP数据包小于64千字节,而且一个片的长度对某些应用场合来说太小,所以应用层打包是RTP打包方案的一部分。 

新的讨论方案(IETF)应当符合以下特征: 

(1)NALU的分块以按RTP次序号升序传输; 

(2)能够标记第一个和最后一个NALU分块; 

(3)可以检测丢失的分块。 

NALU合并

一些NALU如SEI、参数集等非常小,将它们合并在一起有利于减少头标开销。已有两种集合分组: 

(1)单一时间集合分组(STAP),按时间戳进行组合; 

(2)多时间集合分组(MTAP),不同时间戳也可以组合。

NAL规范视频数据的格式,主要是提供头部信息,以适合各种媒体的传输和存储。NAL支持各种网络,包括:

1.任何使用RTP/IP协议的实时有线和无线Internet 服务

2.作为MP4文件存储和多媒体信息文件服务

3.MPEG-2系统

4.其它网

NAL规定一种通用的格式,既适合面向包传输,也适合流传送。实际上,包传输和流传输的方式是相同的,不同之处是传输前面增加了一个起始码前缀

在类似Internet/RTP面向包传送协议系统中,包结构中包含包边界识别字节,在这种情况下,不需要同步字节。

NAL单元分为VCL和非VCL两种

VCL NAL单元包含视频图像采样信息,

非VCL包含各种有关的附加信息,例如参数集(头部信息,应用到大量的VCL NAL单元)、提高性能的附加信息、定时信息等

参数集:

参数集是很少变化的信息,用于大量VCL NAL单元的解码,分为两种类型:

1.序列参数集,作用于一串连续的视频图像,即视频序列。

两个IDR图像之间为序列参数集。IDR和I帧的区别见下面。

2. 图像参数集,作用于视频序列中的一个或多个个别的图像

序列和图像参数集机制,减少了重复参数的传送,每个VCL NAL单元包含一个标识,指

向有关的图像参数集,每个图像参数集包含一个标识,指向有关的序列参数集的内容

因此,只用少数的指针信息,引用大量的参数,大大减少每个VCL NAL单元重复传送的信息。

序列和图像参数集可以在发送VCL NAL单元以前发送,并且重复传送,大大提高纠错能力。序列和图像参数集可以在“带内”,也可以用更为可靠的其他“带外”通道传送。

存储单元: 一组指定格式的NAL单元称为存储单元,每个存储单元对应一个图像。每个存储单元包含一组VCL NAL单元,组成一个主编码图像,VCL NAL单元由表示视频图像采样的像条所组成。存储单元前面可以加一个前缀,分界存储单元,附加增强信息(SEI)(如图像定时信息)也可以放在主编码图像 的前面。主编码图像后附加的VCL NAL单元,包含同一图像的冗余表示,称为冗余编码图像,当主编码图像数据丢失或损坏时,可用冗余编码图像解码。

编码视频序列 一个编码视频序列由一串连续的存储单元组成,使用同一序列参数集。每个视频序列可独立解码。编码序列的开始是即时刷新存储单元(IDR)。IDR是一个I帧图像,表示后面的图像不用参考以前的图像。一个NAL单元流可包含一个或更多的编码视频序列。

RTP协议: 实时传输协议(Real-time Transport Protocol,RTP)是在Internet上处理多媒体数据流的一种网络协议,利用它能够在一对一(单播)或者一对多(multicast,多播) 的网络环境中实现传流媒体数据的实时传输。RTP通常使用UDP来进行多媒体数据的传输,但如果需要的话可以使用TCP或者ATM等其它协议,整个RTP 协议由两个密切相关的部分组成:RTP数据协议和RTP控制协议。实时流协议(Real Time Streaming Protocol, RTSP)最早由Real Networks和Netscape公司共同提出,它位于RTP和RTCP之上,其目的是希望通过IP网络有效地传输多媒体数据。

RTP数据协议 RTP数据协议负责对流媒体数据进行封包并实现媒体流的实时传输,每一个RTP数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前12个字节的含义是固定的,而负载则可以是音频或者视频数据。RTP数据报的头部格式如图1所示:

其中比较重要的几个域及其意义如下: CSRC记数(CC)  表示CSRC标识的数目。CSRC标识紧跟在RTP固定头部之后,用来表示RTP数据报的来源,RTP协议允许在同一个会话中存 在多个数据源,它们可以通过RTP混合器合并为一个数据源。例如,可以产生一个CSRC列表来表示一个电话会议,该会议通过一个RTP混合器将所有讲话者 的语音数据组合为一个RTP数据源。

负载类型(PT)  标明RTP负载的格式,包括所采用的编码算法、采样频率、承载通道等。例如,类型2表明该RTP数据包中承载的是用ITU G.721算法编码的语音数据,采样频率为8000Hz,并且采用单声道。

序列号  用来为接收方提供探测数据丢失的方法,但如何处理丢失的数据则是应用程序自己的事情,RTP协议本身并不负责数据的重传。

时间戳  记录了负载中第一个字节的采样时间,接收方能够时间戳能够确定数据的到达是否受到了延迟抖动的影响,但具体如何来补偿延迟抖动则是应用程序自 己的事情。从RTP数据报的格式不难看出,它包含了传输媒体的类型、格式、序列号、时间戳以及是否有附加数据等信息,这些都为实时的流媒体传输提供了相应 的基础。RTP协议的目的是提供实时数据(如交互式的音频和视频)的端到端传输服务,因此在RTP中没有连接的概念,它可以建立在底层的面向连接或面向非 连接的传输协议之上;RTP也不依赖于特别的网络地址格式,而仅仅只需要底层传输协议支持组帧(Framing)和分段(Segmentation)就足 够了;另外RTP本身还不提供任何可靠性机制,这些都要由传输协议或者应用程序自己来保证。在典型的应用场合下,RTP一般是在传输协议之上作为应用程序 的一部分加以实现的,如图2所示:

RTCP控制协议 RTCP控制协议需要与RTP数据协议一起配合使用,当应用程序启动一个RTP会话时将同时占用两个端口,分别供RTP和RTCP使用。RTP本身并不能 为按序传输数据包提供可靠的保证,也不提供流量控制和拥塞控制,这些都由RTCP来负责完成。通常RTCP会采用与RTP相同的分发机制,向会话中的所有 成员周期性地发送控制信息,应用程序通过接收这些数据,从中获取会话参与者的相关资料,以及网络状况、分组丢失概率等反馈信息,从而能够对服务质量进行控 制或者对网络状况进行诊断。

RTCP协议的功能是通过不同的RTCP数据报来实现的,主要有如下几种类型:

SR  发送端报告,所谓发送端是指发出RTP数据报的应用程序或者终端,发送端同时也可以是接收端。

RR  接收端报告,所谓接收端是指仅接收但不发送RTP数据报的应用程序或者终端。

SDES  源描述,主要功能是作为会话成员有关标识信息的载体,如用户名、邮件地址、电话号码等,此外还具有向会话成员传达会话控制信息的功能。

BYE  通知离开,主要功能是指示某一个或者几个源不再有效,即通知会话中的其他成员自己将退出会话。

APP  由应用程序自己定义,解决了RTCP的扩展性问题,并且为协议的实现者提供了很大的灵活性。

RTCP数据报携带有服务质量监控的必要信息,能够对服务质量进行动态的调整,并能够对网络拥塞进行有效的控制。由于RTCP数据报采用的是多播方式,因此会话中的所有成员都可以通过RTCP数据报返回的控制信息,来了解其他参与者的当前情况。

在一个典型的应用场合下,发送媒体流的应用程序将周期性地产生发送端报告SR,该RTCP数据报含有不同媒体流间的同步信息,以及已经发送的数据报和字节 的计数,接收端根据这些信息可以估计出实际的数据传输速率。另一方面,接收端会向所有已知的发送端发送接收端报告RR,该RTCP数据报含有已接收数据报 的最大序列号、丢失的数据报数目、延时抖动和时间戳等重要信息,发送端应用根据这些信息可以估计出往返时延,并且可以根据数据报丢失概率和时延抖动情况动 态调整发送速率,以改善网络拥塞状况,或者根据网络状况平滑地调整应用程序的服务质量。

RTSP实时流协议

作为一个应用层协议,RTSP提供了一个可供扩展的框架,它的意义在于使得实时流媒体数据的受控和点播变得可能。总的说来,RTSP是一个流媒体表示协 议,主要用来控制具有实时特性的数据发送,但它本身并不传输数据,而是必须依赖于下层传输协议所提供的某些服务。RTSP可以对流媒体提供诸如播放、暂 停、快进等操作,它负责定义具体的控制消息、操作方法、状态码等,此外还描述了与RTP间的交互操作。

RTSP在制定时较多地参考了HTTP/1.1协议,甚至许多描述与HTTP/1.1完全相同。RTSP之所以特意使用与HTTP/1.1类似的语法和操 作,在很大程度上是为了兼容现有的Web基础结构,正因如此,HTTP/1.1的扩展机制大都可以直接引入到RTSP中。

由RTSP控制的媒体流集合可以用表示描述(Presentation Description)来定义,所谓表示是指流媒体服务器提供给客户机的一个或者多个媒体流的集合,而表示描述则包含了一个表示中各个媒体流的相关信 息,如数据编码/解码算法、网络地址、媒体流的内容等。

虽然RTSP服务器同样也使用标识符来区别每一流连接会话(Session),但RTSP连接并没有被绑定到传输层连接(如TCP等),也就是说在整个 RTSP连接期间,RTSP用户可打开或者关闭多个对RTSP服务器的可靠传输连接以发出RTSP 请求。此外,RTSP连接也可以基于面向无连接的传输协议(如UDP等)。

RTSP协议目前支持以下操作:

检索媒体  允许用户通过HTTP或者其它方法向媒体服务器提交一个表示描述。如表示是组播的,则表示描述就包含用于该媒体流的组播地址和端口号;如果表示是单播的, 为了安全在表示描述中应该只提供目的地址。

邀请加入  媒体服务器可以被邀请参加正在进行的会议,或者在表示中回放媒体,或者在表示中录制全部媒体或其子集,非常适合于分布式教学。

添加媒体  通知用户新加入的可利用媒体流,这对现场讲座来讲显得尤其有用。与HTTP/1.1类似,RTSP请求也可以交由代理、通道或者缓存来进行处理。

3. JM86中的处理

涉及的函数:

流程图:

I帧和IDR帧的区别:

1. 在 H.264 中 I 帧并不具有随机访问的能力,这个功能由 IDR 承担。以前的标准中由 I 帧承担。

2. IDR 会导致 DPB (参考帧列表——这是关键所在)清空,而 I 不会。

3. I和IDR帧其实都是I帧,都是使用帧内预测的。但是IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始,重新算一个新的序列开始编码。

4. IDR图像一定是I图像,但I图像不一定是IDR图像。一个序列中可以有很多的I图像,I图像之后的图像可以引用I图像之间的图像做运动参考。

H.264 NAL格式及分析器程序源代码

NAL分析器程序源代码

#include "stdio.h" #include "stdlib.h"

#include "nalucommon.h"

//##################################### //#本程序检查一个缓冲区里面有几个NALU # //#输出每个NALU的类型到输出文件 # //#输入为一个带有标记/NALU+标记/的文件# //#####################################

void main(void) { FILE *pSrc; FILE *pRep;

if((pSrc=fopen("H264.rec","r+b"))==NULL) exit(-1); else printf("open src file succeed\n"); if((pRep=fopen("H264.rep","w+t"))==NULL) exit(-1); else printf("open report file succeed\n"); ReportNalu(pSrc,pRep); fclose(pSrc); fclose(pRep); }

#include <memory.h>

#ifndef NALUCOMMON_H #define NALUCOMMON_H

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 nal_unit_type; //! NALU_TYPE_xxxx int nal_reference_idc; //! NALU_PRIORITY_xxxx int forbidden_bit; //! should be always FALSE unsigned char *buf; //! conjtains the first byte followed by the EBSP } NALU_t;

#define MAXRBSPSIZE 64000

#define NALU_TYPE_SLICE 1 #define NALU_TYPE_DPA 2 #define NALU_TYPE_DPB 3 #define NALU_TYPE_DPC 4 #define NALU_TYPE_IDR 5 #define NALU_TYPE_SEI 6 #define NALU_TYPE_SPS 7 #define NALU_TYPE_PPS 8 #define NALU_TYPE_AUD 9 #define NALU_TYPE_EOSEQ 10 #define NALU_TYPE_EOSTREAM 11 #define NALU_TYPE_FILL 12

#define NALU_PRIORITY_HIGHEST 3 #define NALU_PRIORITY_HIGH 2 #define NALU_PRIRITY_LOW 1 #define NALU_PRIORITY_DISPOSABLE 0

#ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif

#ifndef BOOL #define BOOL unsigned int #endif

void ReportNalu(FILE* src,FILE* rep); void ParseRecBuf(unsigned char p,int bufsize,FILE pReport); int ShowBits (unsigned char buffer[],int totbitoffset,int bytecount, int numbits); BOOL next_bits(unsigned char *p,int bufpos,int bs,int id ); static int FindStartCode (unsigned char *Buf, int bufpos, int bufsize, int next_start_pos, BOOL fIRFD); BOOL gETNALandPROC(unsigned char *p,int bufpos,int bufsize, BOOL fisrtfd,NALU_t *mNal,int inc_SIZE,FILE

*pReport); #endif

//############################################################ //# src 文件格式如下
//# #-#-#-#-#-B-INT32LEN-T264DST - #-#-#-#-B-INT32LEN # //# 1 1 1 1 1 1 4 INT32LEN # //############################################################

void ReportNalu(FILE* src,FILE* rep) { unsigned char tempbuf[1]; unsigned char *pBuf; int *dstlen; int fpos=0; int fstart=0; int FileSize=0; int BufSize; int dstpacketid=0; unsigned int SymState=0; fseek(src,0,SEEK_END); FileSize=ftell(src); printf("the src file size is %d \n",FileSize); fseek(src,0,SEEK_SET); do {

while (fread(tempbuf,sizeof(unsigned char),1,src)) { if(tempbuf=="#") SymState++; else { fpos=fstart+1; SymState=0; fseek(src,fpos,SEEK_SET); } if(SymState==4) {fpos+=4;break;} }

fread(tempbuf,sizeof(unsigned char),1,src);

if(tempbuf=="B") { SymState++; fpos+=1; } else { fpos=fstart+1; fseek(src,fpos,SEEK_SET); }

if(SymState==5)
{
dstpacketid++; fprintf(rep,"the %d dstpacket\n",dstpacketid); dstlen=(int*)malloc(sizeof(int)); fread(dstlen,sizeof(unsigned char),4,src); BufSize=(int*)dstlen; pBuf=(unsigned char*)malloc(sizeof(unsigned char)*BufSize); fpos+=4; fread(pBuf,sizeof(unsigned char),BufSize,src); ParseRecBuf(pBuf,BufSize,rep); fstart=fpos; free(dstlen); free(pBuf); }

} while(fpos!=FileSize);//

}

//#################################################### //# unsigned char p --T264 encode的一个dst长度为len# //# int bufsize --buffer的大小 # //##FILE pReport --文件指针,存放NAL的分析结果 # //####################################################

void ParseRecBuf(unsigned char p,int bufsize,FILE pReport) {

int bufpos=0; int bUFSTART=0; struct NALU_t *mNal; BOOL isfirst; BOOL starcode_ex; BOOL zero_ex; BOOL trail_ex; int nALSIZE=0; int nal_id=0; BOOL firstfd=FALSE;

while (bufpos<bufsize) { if( !next_bits(p,bufpos,bufsize, 24 ) && !next_bits(p,bufpos,bufsize,32 ) && next_bits( p,bufpos,bufsize,8 )) {bufpos+=8;isfirst=TRUE;firstfd=TRUE;}// leading_zero_8bits //f(8) else{ isfirst=FALSE; }

if(!next_bits( p,bufpos,bufsize,24 ) &&
next_bits( p,bufpos,bufsize,8 ) )
{
bufpos+=8;//zero_byte f(8) zero_ex=TRUE; if( bufpos<bufsize && next_bits(p,bufpos,bufsize, 24 ) ) {
bufpos+=24; // start_code_prefix_one_3bytes f(24) starcode_ex=TRUE; mNal=(struct NALU_t* )malloc(sizeof( NALU_t)); if(gETNALandPROC(p,bufpos,bufsize,firstfd,mNal,nALSIZE,pReport))

{

nal_id++;
   fprintf(pReport," id of the nal packet above is %d\n",nal_id);
bufpos+=(nALSIZE>>3);
if(( bufpos<bufsize)  &&
 (!next_bits(p,bufpos,bufsize, 24 )) &&  
 (!next_bits(p,bufpos,bufsize, 32 ) ) &&
 (next_bits(p,bufpos, bufsize,8 )))
{
 bufpos+=8; trail_ex=TRUE ;
}
else trail_ex=FALSE;
free(mNal);

} else printf("cannot get any nal units\n"); } else{starcode_ex=FALSE;}
} else { zero_ex=FALSE; }

if (!zero_ex || !starcode_ex) bufpos=bUFSTART+1; else bUFSTART=bufpos; } }

//############################################ //#unsigned char *p =--buffer containing NALU# //#int bufpos--current buffer position # //#int bs---buffersize # //#int id # //# id=24 ----0x000001----FALSE # //# id=32 ----0x00000001---FALSE # //# id=8 ----0x00------TRUE # //############################################ BOOL next_bits(unsigned char *p,int bufpos,int bs,int id ) { BOOL rERULT; switch(id) { case 8:
if(ShowBits(p,bufpos,bs,8)==0x00) rERULT=TRUE ; else rERULT=FALSE; break; case 24: if(ShowBits(p,bufpos,bs,24)!=0x000001) rERULT=FALSE; else rERULT=TRUE; break; case 32 : if(ShowBits(p,bufpos,bs,32)!=0x00000001) rERULT=FALSE; else rERULT=TRUE; break; default: break; } return rERULT; }

//############################################## // # \brief # // # Reads bits from the bitstream buffer # // # \param buffer # // # buffer containing VLC-coded data bits # // # \param totbitoffset # // # bit offset from start of partition # // # \param bytecount # // # total bytes in bitstream # // # \param numbits # // # number of bits to read # // ############################################# int ShowBits (unsigned char buffer[],int totbitoffset,int bytecount, int numbits) {

register int inf; long byteoffset; // byte from start of buffer int bitoffset; // bit from start of byte

byteoffset= totbitoffset/8; bitoffset= 7-(totbitoffset%8);

inf=0; while (numbits) { inf <<=1; inf |= (buffer[byteoffset] & (0x01<<bitoffset))>>bitoffset; numbits--; bitoffset--; if (bitoffset < 0) { byteoffset++; bitoffset += 8; if (byteoffset > bytecount) { return -1; } } }

return inf; // return absolute offset in bit from start of frame }

//######################################################################### // # \brief

#

// # returns if new start code is found at byte aligned position buf. # // # new-startcode is of form N 0x00 bytes, followed by a 0x01 byte. # // # \return

#

// # 1 if start-code is found or \n # // # 0, indicating that there is no start code # // # # // # \param Buf # // # pointer to byte-stream # // # \param bufpos # // # indicates current bufpos. //# \bufsize // # indicates total buffer size # // # \param next_star_pos # // # indicates the next_start_code pos # // # \param fIRFD // # is firs nal already found // ######################################################################## static int FindStartCode (unsigned char *Buf, int bufpos, int bufsize, int next_start_pos, BOOL fIRFD) { int info; int tPOS; int tSTART=bufpos; BOOL sTARFOUND=FALSE; info = 1; while (!sTARFOUND && tSTART<bufsize) { for (tPOS = 0; tPOS < 3; tPOS++) if(Buf[tSTART+tPOS] != 0) info = 0;
if(Buf[tSTART+tPOS] != 1) info = 0; if (info==0) tSTART++;
else sTARFOUND=TRUE; } if (fIRFD && sTARFOUND) {
if(Buf[tSTART-1]==0 && Buf[tSTART-2]==0) { info=2;//trailing zero found next_start_pos=tSTART-2;} else {info=1; next_start_pos=tSTART;}
} return info; }

//##################################### //# unsigned char *p # //# int bufpos # //# NALU_t *mNal # //# int n_SIZE # //# # //# # //##################################### BOOL gETNALandPROC(unsigned char *p, int bufpos, int bufsize, BOOL fisrtfd, NALU_t *mNal, int inc_SIZE, FILE *pReport) {
BOOL getNfailed=FALSE; int nal_BUFSIZE=0; int next_start_point=0; int mNal_size=0; int b_bufpos=bufpos/8; unsigned int finresult; BOOL info=TRUE; finresult=FindStartCode (p,b_bufpos,bufsize,next_start_point,fisrtfd); if (finresult==1 || finresult==2) {
nal_BUFSIZE=next_start_point-b_bufpos-1; inc_SIZE=nal_BUFSIZE; mNal->buf=(unsigned char *)malloc(sizeof(unsigned char)*nal_BUFSIZE); mNal->len=nal_BUFSIZE; memcpy (mNal->buf, &p[b_bufpos+1], mNal->len); mNal->forbidden_bit = (mNal->buf[0]>>7) & 1; mNal->nal_reference_idc = (mNal->buf[0]>>5) & 3; mNal->nal_unit_type = (mNal->buf[0]) & 0x1f; fprintf(pReport,"#########################################################################\n"); fprintf(pReport,"nal len is %d\n",mNal->len); fprintf(pReport,"fobidden bit is % d\n",mNal->forbidden_bit); fprintf(pReport,"nal_reference_idc is d%\n",mNal->nal_reference_idc); switch(mNal->nal_unit_type) { case NALU_TYPE_SLICE : fprintf(pReport," nal type is * NALU_TYPE_SLICE\n"); break; case NALU_TYPE_DPA: fprintf(pReport,"nal type is * NALU_TYPE_DPA\n"); break; case NALU_TYPE_DPB : fprintf(pReport,"nal type is * NALU_TYPE_DPB\n"); break; case NALU_TYPE_DPC: fprintf(pReport,"nal type is * NALU_TYPE_DPC\n"); break; case NALU_TYPE_IDR: fprintf(pReport,"nal type is * NALU_TYPE_IDR\n"); break; case NALU_TYPE_SEI: fprintf(pReport,"nal type is * NALU_TYPE_SEI\n"); break; case NALU_TYPE_SPS: fprintf(pReport,"nal type is * NALU_TYPE_SPS\n"); break; case NALU_TYPE_PPS: fprintf(pReport,"nal type is * NALU_TYPE_PPS\n"); break; case NALU_TYPE_AUD: fprintf(pReport,"nal type is * NALU_TYPE_AUD\n"); break; case NALU_TYPE_EOSEQ: fprintf(pReport,"nal type is * NALU_TYPE_EOSEQ\n"); break; case NALU_TYPE_EOSTREAM: fprintf(pReport,"nal type is * NALU_TYPE_EOSTREAM\n"); break; case NALU_TYPE_FILL : fprintf(pReport,"nal type is * NALU_TYPE_FILL\n"); break; default: break; } fprintf(pReport,"nal start code len is %d\n",mNal->startcodeprefix_len); fprintf(pReport,"#########################################################################\n"); free(mNal->buf); mNal->buf=NULL; } else info=FALSE; return info; }

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这