H264有两种封装方式:字节流AnnexB格式 AVCC格式。
1. AnnexB格式 ---- 用于实时播放
开始前缀(00000001或000001)+NALU数据 绝大部分编码器的默认输出格式
一共有两种起始码start_code
①3字节0x000001 单帧多slice(即单帧多个NALU)之间间隔
②4字节0x00000001 帧之间,或者SPS等之前
4字节类型的开始码在在连续的数据传输中非常有用,因为用字节来对齐、分割流数据,比如:用连续的31个bit0后接一个bit1来分割流数据,是很容易的。
AnnexB格式每个NALU都包含起始码,且通常会周期性的在关键帧之前重复SPS和PPS
所以解码器可以从视频流随机点开始进行解码,实时的流格式
2. AVCC格式 ---- 用于存储
AVCC格式不使用起始码作为NALU的分界,这种格式在每个NALU前都加上一个大端格式的前缀(1、2、4字节,代表NALU长度)
所以在解析AVCC格式的时候需要将指定的前缀字节数的值保存在一个头部对象中,这个都通常称为extradata或者sequence header。同时,SPS和PPS数据也需要保存在extradata或者叫’sequence header’中。
3.AVPacket数据格式
AVPacket中的data格式是:size(4个字节)+ nalu + size(4个字节)+ nalu,一个packet可能会存在多个nalu,前面size表示nalu的大小(不包含自身)
图1.
如图1所示,前4个字节表示:第一个nalu大小是0x52d = 1325, 总的pkt的大小是66736。表示存在多个nalu。下一个nalu位置:0x7dc0 + 0x025d + 4 = 0x8021,如图2所示
图2.
4.AVPacket转换为 AnnexB,使用h264_mp4toannexb
编码:
// 1 获取相应的比特流过滤器
// FLV/MP4/MKV等结构中,h264需要h264_mp4toannexb处理。添加SPS/PPS等信息。
// FLV封装时,可以把多个NALU放在一个VIDEO TAG中,结构为4B NALU长度+NALU1+4B NALU长度+NALU2+...,
// 需要做的处理把4B长度换成00000001或者000001
// annexb模式: startcode 00000001 AVCC模式: 无startcode (mp4 flv mkv)
const AVBitStreamFilter *bsfilter = av_bsf_get_by_name("h264_mp4toannexb");
AVBSFContext *bsf_ctx = NULL;
// 2 初始化过滤器上下文
av_bsf_alloc(bsfilter, &bsf_ctx); //AVBSFContext;
// 3 添加解码器属性
avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[videoindex]->codecpar);
av_bsf_init(bsf_ctx);
// 4 发送和接受if (av_bsf_send_packet(bsf_ctx, pkt) != 0) // bitstreamfilter内部去维护内存空间
{
av_packet_unref(pkt); // 你不用了就把资源释放掉
continue; // 继续送
}
av_packet_unref(pkt); // 释放资源
while(av_bsf_receive_packet(bsf_ctx, pkt) == 0)
{
out_pkt_count++;
// printf("fwrite size:%d\n", pkt->size);
av_packet_unref(pkt);
}
我们看看filter之后的数据:
图3.
我们可以看到,和图1相比size变为00 00 00 01.下面我们看下一帧
图4.
我们可以看到,下一个并不是简单变为00 00 00 01,而是插入了一个nalu,再下一个nalu(f885位置),才是之前的nalu。我们分析一下中间插入的nalu,看第一下第一个字节表示nalu header.
NALU header格式:
0x67:
F:0 默认为0
R :11 表示这个帧很重要
T:7 表示SPS,到这里可以看出,插入了一个filter