Chinaunix首页 | 论坛 | 博客
  • 博客访问: 433166
  • 博文数量: 117
  • 博客积分: 3003
  • 博客等级: 中校
  • 技术积分: 1221
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-16 14:11
文章分类

全部博文(117)

文章存档

2011年(7)

2010年(110)

我的朋友

分类: LINUX

2010-09-13 16:16:09

rfc3984
Standards Track [Page 2] RFC 3984 RTP Payload Format for H.264 Video February 2005 1.
按照RFC3984协议实现H264视频流媒体

nalu单元 包起始 0x 00 00 00 01

H.264 NAL格式及分析器
%5Fdavy/b ... c409cc7cd92ace.html
_davy/blo ... 081312c8fc7acc.html

----------------------------------比特流信息----------------------------------------------

①NALU(Network Abstract Layer Unit):两标准中的比特流都是以NAL为单位,每个NAL单元包含一个RBSP,NALU的头信息定义了RBSP所属类型。类型一般包括序列参数集 (SPS)、图像参数集(PPS)、增强信息(SEI)、条带(Slice)等,其中,SPS和PPS属于参数集,两标准采用参数集机制是为了将一些主要 的序列、图像参数(解码图像尺寸、片组数、参考帧数、量化和滤波参数标记等)与其他参数分离,通过解码器先解码出来。此外,为了增强图像的清晰 度,AVS-M添加了图像头(Picture head)信息。读取NALU流程中,每个NALU前有一个起始码0x000001,为防止 内部0x000001序列竞争,H.264编码器在最后一字节前插入一个新的字节——0x03,所以解码器检测到该序列时,需将0x03删掉,而AVS- M只需识别出起始码0x000001。


②读取宏块类型(mb type)和宏块编码模板(cbp):编解码图像以宏块划分,一个宏块由一个16*16亮度块和相应的一个8*8cb和一个8*8cr色度块组成。


(a) 两标准的帧内、帧间预测时宏块的划分是有区别的。H.264中,I_slice亮度块有Intra_4*4和Intra_16*16两种模式,色度块只有 8*8模式;P_slice宏块分为16*16、16*8、8*16、8*8、8*4、4*8、4*4共7种模式。而AVS-M中,I_slice亮度块 有I_4*4和I_Direct两模式,P_slice时宏块的划分和H.264中的划分一致。


(b) 两标准的宏块cbp值计算也不相同。H.264中,Intra_16*16宏块的亮度(色度)cbp直接通过读mb type得到;非Intra_16*16宏块的亮度cbp=coded_block_pattern%16,色度 cbp=coded_block_pattern/16 。其中,亮度cbp最低4位有效,每位决定对应宏块的残差系数能不能为0;色度cbp为0时,对应残差系数为0,cbp为1时,DC残差系数不为0,AC 系数为0,cbp为2时,DC、AC残差系数都不为0。AVS-M中,当宏块类型不是P_skip时,直接从码流中得到cbp的索引值,并以此索引值查表 得到codenum值,再以codenum查表分别得到帧内/帧间cbp。此cbp为6位,每位代表宏块按8*8划分时能不能包含非零系数,当变换系数不 为0时,需进一步读cbp_4*4中每位值来判断一个8*8块中4个4*4块的系数能不能为0。
---------------------------------------------------------------------------------------------
总的来说H264的码流的打包方式有两种,一种为annex-b byte stream format的格式,这个是绝大部分编码器的默认输出格式,就是每个帧的开头的3~4个字节是H264的start_code,0x00000001或者0x000001。
另一种是原始的NAL打包格式,就是开始的若干字节(1,2,4字节)是NAL的长度,而不是start_code,此时必须借助某个全局的数据来获得编码器的profile,level,PPS,SPS等信息才可以解码。
----------------------------------------------------------------------------
AVC vs. H.264
AVC and H.264 are synonymous. The standard is known by the full names "ISO/IEC 14496-10" and "ITU-T Recommendation H.264". In addition, a number of alternate names are used (or have been) in reference to this standard. These include:


  • MPEG-4 part 10
  • MPEG-4 AVC
  • AVC
  • MPEG-4 (in the broadcasting world MPEG4 part 2 is ignored)
  • H.264
  • JVT (Joint Video Team, nowadays rarely used referring to actual spec)
  • H.26L (early drafts went by this name)

All of the above (and those I've missed) include the Annex B byte-stream format. Unlike earlier MPEG1/2/4 and H.26x codecs, the H.264 specification proper does not define a full bit-stream syntax. It describes a number of NAL (Network Abstraction Layer) units, a sequence of which can be decoded into video frames. These NAL units have no boundary markers, and rely on some unspecified format to provide framing.

Annex B of of the document specifies one such format, which wraps NAL units in a format resembling a traditional MPEG video elementary stream, thus making it suitable for use with containers like MPEG PS/TS unable to provide the required framing. Other formats, such as ISO base media based formats, are able to properly separate the NAL units and do not need the Annex B wrapping.

The H.264 spec suffers from a deficiency. It defines several header-type NAL units (SPS and PPS) without specifying how to pack them into the single codec data field available in most containers. Fortunately, most containers seem to have adopted the packing used by the ISO format known as MP4.
1. H.264起始码
  在网络传输h264数据时,一个UDP包就是一个NALU,解码器可以很方便的检测出NAL分界和解码。但是如果编码数据存储为一个文件,原来的解码器将无法从数据流中分别出每个NAL的起始位置和终止位置,为此h.264用起始码来解决这一问题。

  H.264编码时,在每个NAL前添加起始码 0x000001,解码器在码流中检测到起始码,当前NAL结束。为了防止NAL内部出现0x000001的数据,h.264又提出'防止竞争 emulation prevention"机制,在编码完一个NAL时,如果检测出有连续两个0x00字节,就在后面插入一个0x03。当解码器在NAL内部检测到 0x000003的数据,就把0x03抛弃,恢复原始数据。
   0x000000  >>>>>>  0x00000300
   0x000001  >>>>>>  0x00000301
   0x000002  >>>>>>  0x00000302
   0x000003  >>>>>>  0x00000303

附上h.264解码nalu中检测起始码的算法流程  
for(;;)
{
    if next 24 bits are 0x000001
    {
        startCodeFound = true
        break;
    }
    else
    {
        flush 8 bits  
    }
}// for(;;)
if(true == startCodeFound)
{
     //startcode found
     // Flush the start code found
     flush 24 bits  
     //Now navigate up to next start code and put the in between stuff
     // in the nal structure.
     for(;;)
     {
         get next 24 bits & check if it equals to 0x000001
         if(false == (next 24 bits == 000001))
         {
             // search for pattern 0x000000
             check if next 24 bits are 0x000000
             if(false == result)
             {
                 // copy the byte into the buffer
                 copy one byte to the Nal unit              
             }
             else
             {
                 break;
             }
          }
          else
          {
              break;
          }
       }//for(;;)
   }

  2. MPEG4起始码
        MPEG4的特色是VOP,没有NALU的概念,仍使用startcode对每帧进行分界。MPEG4的起始码是0x000001. 另外MPEG4中很多起始码也很有用,比如video_object_sequence_start_code 0x000001B0 表示一个视频对象序列的开始,VO_start_code 0x000001B6 表示一个VOP的开始. 0x000001B6之后的两位,是00表示 I frame, 01 表示 P frame, 10 表示 B frame.

1.引言

H.264的主要目标:

1.高的视频压缩比

2.良好的网络亲和性

解决方案:

VCL  video coding layer       视频编码层

NAL  network abstraction layer  网络提取层

VCL:核心算法引擎,块,宏块及片的语法级别的定义

NAL:片级以上的语法级别(如序列参数集和图像参数集),同时支持以下功能:独立片解码,起始码唯一保证,SEI以及流格式编码数据传送

VCL设计目标:尽可能地独立于网络的情况下进行高效的编解码

NAL设计目标:根据不同的网络把数据打包成相应的格式,将VCL产生的比特字符串适配到各种各样的网络和多元环境中。

NALU头结构:NALU类型(5bit)、重要性指示位(2bit)、禁止位(1bit)。

NALU类型:1~12由H.264使用,24~31由H.264以外的应用使用。

重要性指示:标志该NAL单元用于重建时的重要性,值越大,越重要。

禁止位:网络发现NAL单元有比特错误时可设置该比特为1,以便接收方丢掉该单元。

2.NAL语法语义

NAL层句法:

在编码器输出的码流中,数据的基本单元是句法元素。

句法表征句法元素的组织结构。

语义阐述句法元素的具体含义。

分组都有头部,解码器可以很方便的检测出NAL的分界,依次取出NAL进行解码。

但为了节省码流,H.264没有另外在NAL的头部设立表示起始位置的句法元素。

如果编码数据是存储在介质上的,由于NAL是依次紧密相连的,解码器就无法在数据流中分辨出每个NAL的起始位置和终止位置。

解决方案:在每个NAL前添加起始码:0X000001

在某些类型的介质上,为了寻址的方便,要求数据流在长度上对齐,或某个常数的整数倍。所以在起始码前添加若干字节的0来填充。

检测NAL的开始:

0X000001和0X000000

我们必须考虑当NAL内部出现了0X000001和0X000000

解决方案:

H.264提出了“防止竞争”机制:

0X000000——0X00000300

0X000001——0X00000301

0X000002——0X00000302

0X000003——0X00000303

为此,我们可以知道:

在NAL单元中,下面的三字节序列不应在任何字节对齐的位置出现

0X000000

0X000001

0X000002

Forbidden_zero_bit =0;

Nal_ref_idc:表示NAL的优先级。0~3,取值越大,表示当前NAL越重要,需要优先受到保护。如果当前NAL是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的单位时,本句法元素必需大于0。

Nal_unit_type:当前NAL 单元的类型

3.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 /* equal to 0x03 */

} else

rbsp_byte[ NumBytesInRBSP++ ]

}

}

3. 防字节竞争处理后的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 /* equal to 0x00 */

if( more_data_in_byte_stream( ) ) {

start_code_prefix_one_3bytes /* equal to 0x000001 */ 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格式及分析器程序源代码
          email:zsw_davy@hotmail.com

NAL分析器程序源代码
/*!
**************************************************************************************
* \file
*   nalchek.c
* \brief
*    NALU handling common to encoder and decoder
l
l Aug.18.2006 ***************************************************************************************
*/

#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);
}


/*! **************************************************************************************
* \file
*    nalucommon.h.h
* \brief
*    NALU handling common to encoder and decoder
*  
*   Aug.18.2006 ***************************************************************************************
*/
#include


#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 {
  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 /* equal to 0x00 */ //f(8)
  else{ isfirst=FALSE; }
  
  if(!next_bits( p,bufpos,bufsize,24 )  &&   
   next_bits( p,bufpos,bufsize,8 )
   )  
  {   
   bufpos+=8;//zero_byte /* equal to 0x00 */ f(8)
   zero_ex=TRUE;
   if( bufpos     next_bits(p,bufpos,bufsize, 24 ) )
   {      
    bufpos+=24; // start_code_prefix_one_3bytes /* equal to 0x000001 */ 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      (!next_bits(p,bufpos,bufsize, 24 )) &&  
     (!next_bits(p,bufpos,bufsize, 32 ) ) &&
     (next_bits(p,bufpos, bufsize,8 )))
    {
     bufpos+=8; trail_ex=TRUE ;/*  trailing_zero_8bitsequal to 0x00 */
    }
    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;
  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   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;
}
阅读(4267) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~