H.264 (AVC) 和 H.265 (HEVC) 是两种常见的视频编码标准,它们都有不同的封装格式,即Annex B 和 AVCC(H264)/HVCC(H265) 封装格式。
1、Annex B 封装格式
Annex B 是 H264/H265中适合流式传输的一种封装格式,在这种封装格式下,视频码流被分割成称为 NALU的小单元,每个 NALU 都以特定的起始码(start code)开头。Annex B 封装格式的特点包括:
- 起始码:起始码指示了每个 NALU 的开始,是 3 或 4 个字节的特定模式(0x00 00 01/0x00 00 00 01),用于标识 NALU 的开始位置。
- 容易进行流分割:由于起始码的存在,可以轻松地对码流进行分割和重新组合。
视频编码参数vps(H265)、sps(H264/H265)、pps(H264/H265)均以Annex B即用起始码分隔NALU的方式包含在视频流中,H264/H265裸流文件就是以Annex B的方式进行存储的。
Annex B格式如下:
| 起始码 | vps(H265) | 起始码 | sps(H264/H265) | 起始码 | pps(H264/H265) | 起始码 | NALU(H264/H265) | ... | 起始码 | vps(H265) | 起始码 | sps(H264/H265) | 起始码 | pps(H264/H265) | 起始码 | NALU(H264/H265) | ... |
Annex B中视频编码参数vps、sps、pps和视频码流混合到一起,并且每个GOP的开始都会包含,所以可以从任意位置切入进去,只要找到vps(H265)、sps(H264/H265)、pps(H264/H265)即可开始视频解码。因此Annex B更适合流式传输场景。使用Annex B的封装格式有:TS、PS等。
2、AVCC/HVCC 封装格式
2.1、AVCC
AVCC是H264的第二种封装格式,主要应用在视频存储中,AVCC不使用起始码分隔NALU,而是在NALU前面加上一个前缀,前缀使用大端模式,一般为4个字节表示当前NALU的长度。sps、pps也不会和视频码流混合到一起,而是把sps、pps按照一定的格式保存下来,通常称为extradata或者sequence header,并且在文件头部包含extradata/sequence header,视频码流就不会再包含sps、pps了。
格式如下:
| extradata/sequence header | 前缀 | NALU | 前缀 | NALU | ... |
由于sps、pps不会在每个GOP前重复出现,也因此节约了存储空间,更适合存储,解码的时候先从extradata/sequence header中解析出sps、pps,然后读取NALU解码出视频帧。使用AVCC的封装格式有:MP4、FLV等。
AVCC的extradata/sequence header定义如下:
NALULengthSizeMinusOne表示前缀的字节数,一般为4;number of SPS NALUs usually表示sps的数量,之后2个字节表示sps长度,然后接sps NALU data,如果number of SPS不等于1,后面继续接sps长度、sps NALU,以此类推,pps也同理。
AVCC的extradata/sequence header封装代码为:
void Muxer::H264WriteExtra(unsigned char *extra_data, int &extra_data_size)
{
unsigned char *extra_data_start = extra_data;
extra_data[0] = 0x01;
extra_data[1] = sps_buf_[0][1];
extra_data[2] = sps_buf_[0][2];
extra_data[3] = sps_buf_[0][3];
extra_data += 4;
*extra_data++ = 0xff;
// sps
*extra_data++ = 0xe0 | (sps_number_ & 0x1f); // sps 个数
for (int i = 0; i < sps_number_; i++) {
// 两个字节表示sps
extra_data[0] = (sps_len_[i]) >> 8;
extra_data[1] = sps_len_[i];
extra_data += 2;
memcpy(extra_data, sps_buf_[i], sps_len_[i]);
extra_data += sps_len_[i];
}
// pps
*extra_data++ = pps_number_; // 1个sps
for (int i = 0; i < pps_number_; i++) {
extra_data[0] = (pps_len_[i]) >> 8;
extra_data[1] = pps_len_[i];
extra_data += 2;
memcpy(extra_data, pps_buf_[i], pps_len_[i]);
extra_data += pps_len_[i];
}
extra_data_size = extra_data - extra_data_start;
}
sps_number_为sps总个数、sps_len_[]存储了每个sps NALU的长度、sps_buf_[]保存了每个sps NALU data,pps同理。
2.2、HVCC
HVCC是H265的第二种封装格式,除了extradata/sequence header定义,其他和H264的AVCC格式都是一样的。
HVCC的extradata/sequence header定义如下:
numOfArrays表示数组的个数,一般为0x03,表示包含VPS、SPS、PPS,数组每个元素的数据格式如下:
NAL unit type用以区分VPS、SPS、PPS。
HVCC的extradata/sequence header的封装代码如下:
void Muxer::H265WriteExtra(unsigned char *extra_data, int &extra_data_size)
{
int i = 0;
unsigned char *buffer = extra_data;
buffer[i++] = 0x01;
// general_profile_idc 8bit
buffer[i++] = 0x00;
// general_profile_compatibility_flags 32 bit
buffer[i++] = 0x00;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
// 48 bit NUll nothing deal in rtmp
buffer[i++] = 0x00;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
// general_level_idc
buffer[i++] = 0x00;
// 48 bit NUll nothing deal in rtmp
buffer[i++] = 0xf0;
buffer[i++] = 0x00;
buffer[i++] = 0xfc;
buffer[i++] = 0xfc;
buffer[i++] = 0xf8;
buffer[i++] = 0xf8;
// bit(16) avgFrameRate;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
/* bit(2) constantFrameRate; */
/* bit(3) numTemporalLayers; */
/* bit(1) temporalIdNested; */
buffer[i++] = 0x03;
/* unsigned int(8) numOfArrays; 03 */
buffer[i++] = 3;
// printf("HEVCDecoderConfigurationRecord data = %s\n", buffer);
buffer[i++] = 0xa0; // vps 32
buffer[i++] = (vps_number_ >> 8) & 0xff;
buffer[i++] = vps_number_ & 0xff;
for (int j = 0; j < vps_number_; j++) {
buffer[i++] = (vps_len_[j] >> 8) & 0xff;
buffer[i++] = (vps_len_[j]) & 0xff;
memcpy(&buffer[i], vps_buf_[j], vps_len_[j]);
i += vps_len_[j];
}
// sps
buffer[i++] = 0xa1; // sps 33
buffer[i++] = (sps_number_ >> 8) & 0xff;
buffer[i++] = sps_number_ & 0xff;
for (int j = 0; j < sps_number_; j++) {
buffer[i++] = (sps_len_[j] >> 8) & 0xff;
buffer[i++] = sps_len_[j] & 0xff;
memcpy(&buffer[i], sps_buf_[j], sps_len_[j]);
i += sps_len_[j];
}
// pps
buffer[i++] = 0xa2; // pps 34
buffer[i++] = (pps_number_ >> 8) & 0xff;
buffer[i++] = pps_number_ & 0xff;
for (int j = 0; j < pps_number_; j++) {
buffer[i++] = (pps_len_[j] >> 8) & 0xff;
buffer[i++] = pps_len_[j] & 0xff;
memcpy(&buffer[i], pps_buf_[j], pps_len_[j]);
i += pps_len_[j];
}
extra_data_size = i;
return;
}
3、项目地址:
完整代码:https://github.com/BreakingY/FFmpeg-Media-Codec-Pipeline里面的MediaMuxer/MediaMuxer.cpp,是用ffmpeg实现的MP4封装C++类。关于FFmpeg-Media-Codec-Pipeline:用ffmpeg实现音视频(H264/H265/AAC)封装、解封装、编解码pipeline,支持NVIDIA硬编解码。