目录
- 1.概述
- 2.头文件的定义
- 2.1 基础头文件(base.h)
- 2.2 H264码流读取文件(bs.h)
- 2.2.1 单比特读取函数(bs_read_u1)
- 2.2.2 指数哥伦布无符号编码(bs_read_ue)
- 2.2.3 指数哥伦布有符号编码(bs_read_se)
- 2.3 H264码流定义头文件(h264_parser.h)
- 3.cpp文件
- 3.1 H264分析(h264_parser.cpp)
- 3.2 码流解析(parser.cpp)
- 4.程序运行结果
- 5.小结
参考:
李迟:完成一个分析H264码流的工具
雷博:视音频编解码学习工程:H.264分析器
码流介绍:H.264 入门篇 - 01 (Bitstream)
哥伦布编码:音视频从入门到精通——指数哥伦布码原理
1.概述
码流解析可以深入的剖析视频流当中语法元素存储的方式,这对于理解码流有很大的帮助作用,本文参考雷博和李迟的开源代码,结合2021年8月份新的H264视频编码标准,做了一些微调和注释,尽管有些地方还有待改进,但实现了主要功能。这个开源项目提出的另一个作用是替代那些昂贵的视频码流分析软件如VQA,目前只有打印输出分析的内容,但未来会改成可操作性界面。下面对代码进行定义和解释
2.头文件的定义
2.1 基础头文件(base.h)
base.h文件的定义,定义了ERROR打印信息,其中__FILE__是当前文件,__LINE__是当前行,__func__是当前函数,最后的error是打印的错误信息,这样方便定位出错的地方,这在代码量比较大的工程里面非常实用
#pragma once
#ifndef _BASE_H_
#define _BASE_H_
#define ERROR(error) printf("%s:%d:%s:%s\n", __FILE__, __LINE__, __func__, error)
#endif // !_BASE_H_
2.2 H264码流读取文件(bs.h)
码流头文件由h264bitstream给出,定义了码流读取的方式,作者是Alex Izvorski,这里用作开源应该没问题
/*
* h264bitstream - a library for reading and writing H.264 video
* Copyright (C) 2005-2007 Auroras Entertainment, LLC
* Copyright (C) 2008-2011 Avail-TVN
*
* Written by Alex Izvorski <aizvorski@gmail.com> and Alex Giladi <alex.giladi@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _H264_BS_H
#define _H264_BS_H 1
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
uint8_t* start;
uint8_t* p;
uint8_t* end;
int bits_left;
} bs_t;
#define _OPTIMIZE_BS_ 1
#if ( _OPTIMIZE_BS_ > 0 )
#ifndef FAST_U8
#define FAST_U8
#endif
#endif
static bs_t* bs_new(uint8_t* buf, size_t size);
static void bs_free(bs_t* b);
static bs_t* bs_clone(bs_t* dest, const bs_t* src);
static bs_t* bs_init(bs_t* b, uint8_t* buf, size_t size);
static uint32_t bs_byte_aligned(bs_t* b);
static int bs_eof(bs_t* b);
static int bs_overrun(bs_t* b);
static int bs_pos(bs_t* b);
static uint32_t bs_peek_u1(bs_t* b);
static uint32_t bs_read_u1(bs_t* b);
static uint32_t bs_read_u(bs_t* b, int n);
static uint32_t bs_read_f(bs_t* b, int n);
static uint32_t bs_read_u8(bs_t* b);
static uint32_t bs_read_ue(bs_t* b);
static int32_t bs_read_se(bs_t* b);
static void bs_write_u1(bs_t* b, uint32_t v);
static void bs_write_u(bs_t* b, int n, uint32_t v);
static void bs_write_f(bs_t* b, int n, uint32_t v);
static void bs_write_u8(bs_t* b, uint32_t v);
static void bs_write_ue(bs_t* b, uint32_t v);
static void bs_write_se(bs_t* b, int32_t v);
static int bs_read_bytes(bs_t* b, uint8_t* buf, int len);
static int bs_write_bytes(bs_t* b, uint8_t* buf, int len);
static int bs_skip_bytes(bs_t* b, int len);
static uint32_t bs_next_bits(bs_t* b, int nbits);
// IMPLEMENTATION
static inline bs_t* bs_init(bs_t* b, uint8_t* buf, size_t size)
{
b->start = buf;
b->p = buf;
b->end = buf + size;
b->bits_left = 8;
return b;
}
static inline bs_t* bs_new(uint8_t* buf, size_t size)
{
bs_t* b = (bs_t*)malloc(sizeof(bs_t));
bs_init(b, buf, size);
return b;
}
static inline void bs_free(bs_t* b)
{
free(b);
}
static inline bs_t* bs_clone(bs_t* dest, const bs_t* src)
{
dest->start = src->p;
dest->p = src->p;
dest->end = src->end;
dest->bits_left = src->bits_left;
return dest;
}
static inline uint32_t bs_byte_aligned(bs_t* b)
{
return (b->bits_left == 8);
}
static inline int bs_eof(bs_t * b) { if (b->p >= b->end) { return 1; } else { return 0; } }
static inline int bs_overrun(bs_t * b) { if (b->p > b->end) { return 1; } else { return 0; } }
static inline int bs_pos(bs_t * b) { if (b->p > b->end) { return (b->end - b->start); } else { return (b->p - b->start); } }
static inline int bs_bytes_left(bs_t * b) { return (b->end - b->p); }
static inline uint32_t bs_read_u1(bs_t * b)
{
uint32_t r = 0;
b->bits_left--;
if (!bs_eof(b))
{
r = ((*(b->p)) >> b->bits_left) & 0x01;
}
if (b->bits_left == 0) { b->p++; b->bits_left = 8; }
return r;
}
static inline void bs_skip_u1(bs_t * b)
{
b->bits_left--;
if (b->bits_left == 0) { b->p++; b->bits_left = 8; }
}
static inline uint32_t bs_peek_u1(bs_t * b)
{
uint32_t r = 0;
if (!bs_eof(b))
{
r = ((*(b->p)) >> (b->bits_left - 1)) & 0x01;
}
return r;
}
static inline uint32_t bs_read_u(bs_t * b, int n)
{
uint32_t r = 0;
int i;
for (i = 0; i < n; i++)
{
r |= (bs_read_u1(b) << (n - i - 1));
}
return r;
}
static inline void bs_skip_u(bs_t * b, int n)
{
int i;
for (i = 0; i < n; i++)
{
bs_skip_u1(b);
}
}
static inline uint32_t bs_read_f(bs_t * b, int n) { return bs_read_u(b, n); }
static inline uint32_t bs_read_u8(bs_t * b)
{
#ifdef FAST_U8
if (b->bits_left == 8 && !bs_eof(b)) // can do fast read
{
uint32_t r = b->p[0];
b->p++;
return r;
}
#endif
return bs_read_u(b, 8);
}
static inline uint32_t bs_read_ue(bs_t * b)
{
int32_t r = 0;
int i = 0;
while ((bs_read_u1(b) == 0) && (i < 32) && (!bs_eof(b)))
{
i++;
}
r = bs_read_u(b, i);
r += (1 << i) - 1;
return r;
}
static inline int32_t bs_read_se(bs_t * b)
{
int32_t r = bs_read_ue(b);
if (r & 0x01)
{
r = (r + 1) / 2;
}
else
{
r = -(r / 2);
}
return r;
}
static inline void bs_write_u1(bs_t * b, uint32_t v)
{
b->bits_left--;
if (!bs_eof(b))
{
// FIXME this is slow, but we must clear bit first
// is it better to memset(0) the whole buffer during bs_init() instead?
// if we don't do either, we introduce pretty nasty bugs
(*(b->p)) &= ~(0x01 << b->bits_left);
(*(b->p)) |= ((v & 0x01) << b->bits_left);
}
if (b->bits_left == 0) { b->p++; b->bits_left = 8; }
}
static inline void bs_write_u(bs_t * b, int n, uint32_t v)
{
int i;
for (i = 0; i < n; i++)
{
bs_write_u1(b, (v >> (n - i - 1)) & 0x01);
}
}
static inline void bs_write_f(bs_t * b, int n, uint32_t v) { bs_write_u(b, n, v); }
static inline void bs_write_u8(bs_t * b, uint32_t v)
{
#ifdef FAST_U8
if (b->bits_left == 8 && !bs_eof(b)) // can do fast write
{
b->p[0] = v;
b->p++;
return;
}
#endif
bs_write_u(b, 8, v);
}
static inline void bs_write_ue(bs_t * b, uint32_t v)
{
static const int len_table[256] =
{
1,
1,
2,2,
3,3,3,3,
4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
};
int len;
if (v == 0)
{
bs_write_u1(b, 1);
}
else
{
v++;
if (v >= 0x01000000)
{
len = 24 + len_table[v >> 24];
}
else if (v >= 0x00010000)
{
len = 16 + len_table[v >> 16];
}
else if (v >= 0x00000100)
{
len = 8 + len_table[v >> 8];
}
else
{
len = len_table[v];
}
bs_write_u(b, 2 * len - 1, v);
}
}
static inline void bs_write_se(bs_t * b, int32_t v)
{
if (v <= 0)
{
bs_write_ue(b, -v * 2);
}
else
{
bs_write_ue(b, v * 2 - 1);
}
}
static inline int bs_read_bytes(bs_t * b, uint8_t * buf, int len)
{
int actual_len = len;
if (b->end - b->p < actual_len) { actual_len = b->end - b->p; }
if (actual_len < 0) { actual_len = 0; }
memcpy(buf, b->p, actual_len);
if (len < 0) { len = 0; }
b->p += len;
return actual_len;
}
static inline int bs_write_bytes(bs_t * b, uint8_t * buf, int len)
{
int actual_len = len;
if (b->end - b->p < actual_len) { actual_len = b->end - b->p; }
if (actual_len < 0) { actual_len = 0; }
memcpy(b->p, buf, actual_len);
if (len < 0) { len = 0; }
b->p += len;
return actual_len;
}
static inline int bs_skip_bytes(bs_t * b, int len)
{
int actual_len = len;
if (b->end - b->p < actual_len) { actual_len = b->end - b->p; }
if (actual_len < 0) { actual_len = 0; }
if (len < 0) { len = 0; }
b->p += len;
return actual_len;
}
static inline uint32_t bs_next_bits(bs_t * bs, int nbits)
{
bs_t b;
bs_clone(&b, bs);
return bs_read_u(&b, nbits);
}
static inline uint64_t bs_next_bytes(bs_t * bs, int nbytes)
{
int i = 0;
uint64_t val = 0;
if ((nbytes > 8) || (nbytes < 1)) { return 0; }
if (bs->p + nbytes > bs->end) { return 0; }
for (i = 0; i < nbytes; i++) { val = (val << 8) | bs->p[i]; }
return val;
}
#define bs_print_state(b) fprintf( stderr, "%s:%d@%s: b->p=0x%02hhX, b->left = %d\n", __FILE__, __LINE__, __FUNCTION__, *b->p, b->bits_left )
#ifdef __cplusplus
}
#endif
#endif
上面定义了很多读取的函数,主要看看bs_read_u1、bs_read_ue和bs_read_se这几个函数。
2.2.1 单比特读取函数(bs_read_u1)
static inline uint32_t bs_read_u1(bs_t * b)
{
uint32_t r = 0;
// 剩余比特数减1
b->bits_left--;
if (!bs_eof(b)) // 没有到文件尾部
{
r = ((*(b->p)) >> b->bits_left) & 0x01; // 取出下一个比特
}
// 如果剩余的比特数为0,则移动到下一个字节
if (b->bits_left == 0) { b->p++; b->bits_left = 8; }
return r;
}
2.2.2 指数哥伦布无符号编码(bs_read_ue)
指数哥伦布无符号编码参考别人的记录,其编码方式为 前缀(prefix) + 1 + 后缀(suffix),其中prefix都为0,suffix的长度和prefix长度一致,codeNum的计算方式为
c
o
d
e
N
u
m
=
2
l
e
a
d
i
n
g
Z
e
r
o
B
i
t
s
−
1
+
r
e
a
d
b
i
t
s
(
l
e
a
d
i
n
g
Z
e
r
o
B
i
t
s
)
codeNum=2^{leadingZeroBits}-1+read_bits(leadingZeroBits)
codeNum=2leadingZeroBits−1+readbits(leadingZeroBits)
例如 00111,前缀是2个0,分割是中间而对1,后缀是2个1,,计算的codeNum=2^2-1+3=6
static inline uint32_t bs_read_ue(bs_t * b)
{
int32_t r = 0;
int i = 0;
// 检查前面有多少个0
while ((bs_read_u1(b) == 0) && (i < 32) && (!bs_eof(b)))
{
i++;
}
// 读取i个比特数
r = bs_read_u(b, i);
// codeNum = 2^leadingZeroBits - 1 + bs_read_u(b, i)
r += (1 << i) - 1;
return r;
}
2.2.3 指数哥伦布有符号编码(bs_read_se)
指数哥伦布有符号编码是基于无符号编码,使用指数哥伦布无符号编码计算出来的值输入到下面的公式中,计算有符号编码
C
o
d
e
N
u
m
=
(
−
1
)
k
+
1
C
e
i
l
(
k
2
)
CodeNum=(-1)^{k+1}Ceil(\frac{k}{2})
CodeNum=(−1)k+1Ceil(2k)
static inline int32_t bs_read_se(bs_t * b)
{
int32_t r = bs_read_ue(b);
if (r & 0x01)
{
r = (r + 1) / 2;
}
else
{
r = -(r / 2);
}
return r;
}
2.3 H264码流定义头文件(h264_parser.h)
h264_parser.h文件的定义,主要内容包括:
(1)定义NAL、SLICE、AUD等宏信息
(2)定义pps、sps、slice header等结构体信息,这些结构体信息存储了具体的码流信息,在解析的时候会存储在这里
(3)定义读取结构体信息的函数,如read_seq_parameter_set等
(4)定义打印输出信息的函数,如debug_pps等
这里稍微修改了一些部分包括ChromaArrayType等,做了一些注释
#pragma once
#ifndef _H264_PARSER_H_
#define _H264_PARSER_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "base.h"
#include "bs.h"
#include "parser.h"
#define BUFSIZE 32 * 1024 * 1024
#define Extended_SAR 255
//Table 7-1 NAL unit type codes
#define NAL_UNIT_TYPE_UNSPECIFIED 0 // Unspecified
#define NAL_UNIT_TYPE_CODED_SLICE_NON_IDR 1 // Coded slice of a non-IDR picture
#define NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_A 2 // Coded slice data partition A
#define NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_B 3 // Coded slice data partition B
#define NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_C 4 // Coded slice data partition C
#define NAL_UNIT_TYPE_CODED_SLICE_IDR 5 // Coded slice of an IDR picture
#define NAL_UNIT_TYPE_SEI 6 // Supplemental enhancement information (SEI)
#define NAL_UNIT_TYPE_SPS 7 // Sequence parameter set
#define NAL_UNIT_TYPE_PPS 8 // Picture parameter set
#define NAL_UNIT_TYPE_AUD 9 // Access unit delimiter
#define NAL_UNIT_TYPE_END_OF_SEQUENCE 10 // End of sequence
#define NAL_UNIT_TYPE_END_OF_STREAM 11 // End of stream
#define NAL_UNIT_TYPE_FILLER 12 // Filler data
#define NAL_UNIT_TYPE_SPS_EXT 13 // Sequence parameter set extension
// 14..18 // Reserved
#define NAL_UNIT_TYPE_CODED_SLICE_AUX 19 // Coded slice of an auxiliary coded picture without partitioning
// 20..23 // Reserved
// 24..31 // Unspecified
//7.4.3 Table 7-6. Name association to slice_type
#define SH_SLICE_TYPE_P 0 // P (P slice)
#define SH_SLICE_TYPE_B 1 // B (B slice)
#define SH_SLICE_TYPE_I 2 // I (I slice)
#define SH_SLICE_TYPE_SP 3 // SP (SP slice)
#define SH_SLICE_TYPE_SI 4 // SI (SI slice)
//as per footnote to Table 7-6, the *_ONLY slice types indicate that all other slices in that picture are of the same type
#define SH_SLICE_TYPE_P_ONLY 5 // P (P slice)
#define SH_SLICE_TYPE_B_ONLY 6 // B (B slice)
#define SH_SLICE_TYPE_I_ONLY 7 // I (I slice)
#define SH_SLICE_TYPE_SP_ONLY 8 // SP (SP slice)
#define SH_SLICE_TYPE_SI_ONLY 9 // SI (SI slice)
//7.4.2.4 Table 7-5 Meaning of primary_pic_type
#define AUD_PRIMARY_PIC_TYPE_I 0 // I
#define AUD_PRIMARY_PIC_TYPE_IP 1 // I, P
#define AUD_PRIMARY_PIC_TYPE_IPB 2 // I, P, B
#define AUD_PRIMARY_PIC_TYPE_SI 3 // SI
#define AUD_PRIMARY_PIC_TYPE_SISP 4 // SI, SP
#define AUD_PRIMARY_PIC_TYPE_ISI 5 // I, SI
#define AUD_PRIMARY_PIC_TYPE_ISIPSP 6 // I, SI, P, SP
#define AUD_PRIMARY_PIC_TYPE_ISIPSPB 7 // I, SI, P, SP, B
//D.1 SEI payload syntax
#define SEI_TYPE_BUFFERING_PERIOD 0
#define SEI_TYPE_PIC_TIMING 1
#define SEI_TYPE_PAN_SCAN_RECT 2
#define SEI_TYPE_FILLER_PAYLOAD 3
#define SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35 4
#define SEI_TYPE_USER_DATA_UNREGISTERED 5
#define SEI_TYPE_RECOVERY_POINT 6
#define SEI_TYPE_DEC_REF_PIC_MARKING_REPETITION 7
#define SEI_TYPE_SPARE_PIC 8
#define SEI_TYPE_SCENE_INFO 9
#define SEI_TYPE_SUB_SEQ_INFO 10
#define SEI_TYPE_SUB_SEQ_LAYER_CHARACTERISTICS 11
#define SEI_TYPE_SUB_SEQ_CHARACTERISTICS 12
#define SEI_TYPE_FULL_FRAME_FREEZE 13
#define SEI_TYPE_FULL_FRAME_FREEZE_RELEASE 14
#define SEI_TYPE_FULL_FRAME_SNAPSHOT 15
#define SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_START 16
#define SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_END 17
#define SEI_TYPE_MOTION_CONSTRAINED_SLICE_GROUP_SET 18
#define SEI_TYPE_FILM_GRAIN_CHARACTERISTICS 19
#define SEI_TYPE_DEBLOCKING_FILTER_DISPLAY_PREFERENCE 20
#define SEI_TYPE_STEREO_VIDEO_INFO 21
//Table 7-1 NAL unit type codes
#define NAL_UNIT_TYPE_UNSPECIFIED 0 // Unspecified
#define NAL_UNIT_TYPE_CODED_SLICE_NON_IDR 1 // Coded slice of a non-IDR picture
#define NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_A 2 // Coded slice data partition A
#define NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_B 3 // Coded slice data partition B
#define NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_C 4 // Coded slice data partition C
#define NAL_UNIT_TYPE_CODED_SLICE_IDR 5 // Coded slice of an IDR picture
#define NAL_UNIT_TYPE_SEI 6 // Supplemental enhancement information (SEI)
#define NAL_UNIT_TYPE_SPS 7 // Sequence parameter set
#define NAL_UNIT_TYPE_PPS 8 // Picture parameter set
#define NAL_UNIT_TYPE_AUD 9 // Access unit delimiter
#define NAL_UNIT_TYPE_END_OF_SEQUENCE 10 // End of sequence
#define NAL_UNIT_TYPE_END_OF_STREAM 11 // End of stream
#define NAL_UNIT_TYPE_FILLER 12 // Filler data
#define NAL_UNIT_TYPE_SPS_EXT 13 // Sequence parameter set extension
// 14..18 // Reserved
#define NAL_UNIT_TYPE_CODED_SLICE_AUX 19 // Coded slice of an auxiliary coded picture without partitioning
// 20..23 // Reserved
// 24..31 // Unspecified
typedef unsigned char uint8_t;
typedef enum {
NALU_TYPE_SLICE = 1,
NALU_TYPE_DPA = 2,
NALU_TYPE_DPB = 3,
NALU_TYPE_DPC = 4,
NALU_TYPE_IDR = 5,
NALU_TYPE_SEI = 6,
NALU_TYPE_SPS = 7,
NALU_TYPE_PPS = 8,
NALU_TYPE_AUD = 9,
NALU_TYPE_EOSEQ = 10,
NALU_TYPE_EOSTREAM = 11,
NALU_TYPE_FILL = 12,
} NALU_TYPE;
typedef enum {
NALU_PRIORITY_DISPOSABLE = 0,
NALU_PRIRITY_LOW = 1,
NALU_PRIORITY_HIGH = 2,
NALU_PRIORITY_HIGHEST = 3
} NALU_PRIORITY;
// 下面的定义来自于2021年8月的ITU英文文档,中文文档更新的比较慢
typedef struct h264_nal
{
int forbidden_zero_bit;
int nal_ref_idc;
int nal_unit_type;
void* parsed; // FIXME
int sizeof_parsed;
} h264_nal_t;
typedef struct h264_sei
{
int payload_type;
int payload_size;
uint8_t* payload;
} h264_sei_t;
typedef struct h264_sps {
int profile_idc;
int constraint_set0_flag;
int constraint_set1_flag;
int constraint_set2_flag;
int constraint_set3_flag;
int constraint_set4_flag;
int constraint_set5_flag;
int reserved_zero_2bits; // u(2), equal to 0
int level_idc;
int seq_parameter_set_id;
int chroma_format_idc;
int separate_colour_plane_flag;
int bit_depth_luma_minus8;
int bit_depth_chroma_minus8;
int qpprime_y_zero_transform_bypass_flag;
int seq_scaling_matrix_present_flag;
int seq_scaling_list_present_flag[12];
int* ScalingList4x4[16];
int UseDefaultScaningMatrix4x4Flag[6];
int* ScalingList8x8[64];
int UseDefaultScaningMatrix8x8Flag[6];
int log2_max_frame_num_minus4;
int pic_order_cnt_type;
int log2_max_pic_order_cnt_lsb_minus4;
int delta_pic_order_always_zero_flag;
int offset_for_non_ref_pic;
int offset_for_top_to_bottom_field;
int num_ref_frames_in_pic_order_cnt_cycle;
int offset_for_ref_frame[256];
int max_num_ref_frames;
int gaps_in_frame_num_value_allowed_flag;
int pic_width_in_mbs_minus1;
int pic_height_in_map_units_minus1;
int frame_mbs_only_flag;
int mb_adaptive_frame_field_flag;
int direct_8x8_inference_flag;
int frame_cropping_flag;
int frame_crop_left_offset;
int frame_crop_right_offset;
int frame_crop_top_offset;
int frame_crop_bottom_offset;
int vui_parameters_present_flag;
struct {
int aspect_ratio_info_present_flag;
int aspect_ratio_idc;
int sar_width;
int sar_height;
int overscan_info_present_flag;
int overscan_appropriate_flag;
int video_signal_type_present_flag;
int video_format;
int video_full_range_flag;
int colour_description_present_flag;
int colour_primaries;
int transfer_characteristics;
int matrix_coefficients;
int chroma_loc_info_present_flag;
int chroma_sample_loc_type_top_field;
int chroma_sample_loc_type_bottom_field;
int timing_info_present_flag;
int num_units_in_tick;
int time_scale;
int fixed_frame_rate_flag;
int nal_hrd_parameters_present_flag;
int vcl_hrd_parameters_present_flag;
int low_delay_hrd_flag;
int pic_struct_present_flag;
int bitstream_restriction_flag;
int motion_vectors_over_pic_boundaries_flag;
int max_bytes_per_pic_denom;
int max_bits_per_mb_denom;
int log2_max_mv_length_horizontal;
int log2_max_mv_length_vertical;
int max_num_reorder_frames;
int max_dec_frame_buffering;
} vui;
struct {
int cpb_cnt_minus1;
int bit_rate_scale;
int cpb_size_scale;
int bit_rate_value_minus1[32];
int cpb_size_value_minus1[32];
int cbr_flag[32];
int initial_cpb_removal_delay_length_minus1;
int cpb_removal_delay_length_minus1;
int dpb_removal_delay_length_minus1;
int time_offset_length;
} hrd;
// chroma array type
// chroma_forma_idc = chroma_format_idc
// 如果separate_colour_plane_flag为1,则ChromaArrayType为0
int ChromaArrayType= 0;
} h264_sps_t;
typedef struct h264_pps {
int pic_parameter_set_id;
int seq_parameter_set_id;
int entropy_coding_mode_flag;
int bottom_field_pic_order_in_frame_present_flag;
int num_slice_groups_minus1;
int slice_group_map_type;
int run_length_minus1[8];
int top_left[8];
int bottom_right[8];
int slice_group_change_direction_flag;
int slice_group_change_rate_minus1;
int pic_size_in_map_units_minus1;
int slice_group_id[256]; // 这里为什么是256?
int num_ref_idx_l0_default_active_minus1;
int num_ref_idx_l1_default_active_minus1;
int weighted_pred_flag;
int weighted_bipred_idc;
int pic_init_qp_minus26;
int pic_init_qs_minus26;
int chroma_qp_index_offset;
int deblocking_filter_control_present_flag;
int constrained_intra_pred_flag;
int redundant_pic_cnt_present_flag;
// 是否有更多的数据
int more_rbsp_data;
int transform_8x8_mode_flag;
int pic_scaling_matrix_present_flag;
int pic_scaling_list_present_flag[12];
int* ScalingList4x4[16];
int UseDefaultScalingMatrix4x4[6];
int* ScalingList8x8[64];
int UseDefaultScalingMatrix8x8[6];
int second_chroma_qp_index_offset;
} h264_pps_t;
typedef struct h264_slice_header
{
int first_mb_in_slice;
int slice_type;
int pic_parameter_set_id;
int colour_plane_id; // 雷博没有写这个字段
int frame_num;
int field_pic_flag;
int bottom_field_flag;
int idr_pic_id;
int pic_order_cnt_lsb;
int delta_pic_order_cnt_bottom;
int delta_pic_order_cnt[2];
int redundant_pic_cnt;
int direct_spatial_mv_pred_flag;
int num_ref_idx_active_override_flag;
int num_ref_idx_l0_active_minus1;
int num_ref_idx_l1_active_minus1;
// ...
// ref_pic_list_mvc_modification(),如果nal_type == 20或21时使用这个语法元素
// 这里先不记录
// ref pic list modification
// 在雷博的文章中,记录的是 ref pic list reorder,但是新标准文档中似乎记录的是modification
struct {
int ref_pic_list_modification_flag_l0;
int ref_pic_list_modification_flag_l1;
// set array, what size?
int modification_of_pic_nums_idc;
int abs_diff_pic_num_minus1;
int long_term_pic_num;
} rplm;
// predictive weight table
struct {
int luma_log2_weight_denom;
int chroma_log2_weight_denom;
int luma_weight_l0_flag[64];
int luma_weight_l0[64];
int luma_offset_l0[64];
int chroma_weight_l0_flag[64];
int chroma_weight_l0[64][2];
int chroma_offset_l0[64][2];
int luma_weight_l1_flag[64];
int luma_weight_l1[64];
int luma_offset_l1[64];
int chroma_weight_l1_flag[64];
int chroma_weight_l1[64][2];
int chroma_offset_l1[64][2];
} pwt;
// dec ref pic marking
struct {
int no_output_of_prior_pics_flag;
int long_term_reference_flag;
int adaptive_ref_pic_marking_mode_flag;
// set array
int memory_management_control_operation;
int difference_of_pic_nums_minus1;
int long_term_pic_num;
int long_term_frame_idx;
int max_long_term_frame_idx_plus1;
} drpm;
int cabac_init_idc;
int slice_qp_delta;
int sp_for_switch_flag;
int slice_qs_delta;
int disable_deblocking_filter_idc;
int slice_alpha_c0_offset_div2;
int slice_beta_offset_div2;
int slice_group_change_cycle;
} h264_slice_header_t;
typedef struct h264_aud
{
int primary_pic_type;
} h264_aud_t;
typedef struct h264_slice_data_rbsp
{
int rbsp_size;
uint8_t* rbsp_buf;
} h264_slice_data_rbsp_t;
typedef struct
{
h264_nal_t* nal;
h264_sps_t* sps;
h264_pps_t* pps;
h264_aud_t* aud;
h264_sei_t* sei; //This is a TEMP pointer at whats in h->seis...
int num_seis;
h264_slice_header_t* sh;
h264_slice_data_rbsp_t* slice_data;
h264_sps_t* sps_table[32];
h264_pps_t* pps_table[256];
h264_sei_t** seis;
} h264_stream_t;
h264_stream_t* h264_new();
void h264_free(h264_stream_t* h);
int find_nal_unit(uint8_t* buf, int size, int* nal_start, int* nal_end);
int nal_to_rbsp(const uint8_t* nal_buf, int* nal_size, uint8_t* rbsp_buf, int* rbsp_size);
// 7.3.1 NAL unit syntax
void read_nal_unit(h264_stream_t* h, uint8_t* buf, int size);
// 7.3.2.1 Sequence parameter set data syntax
void read_sequence_parameter_set_rbsp(h264_stream_t* h, bs_t* b);
// 7.3.2.1.1 Scaling list syntax syntax
void read_scaling_list(bs_t* b, int* scaling_list, int size, int use_default_scaling_matrix_flag);
// 7.3.2.2 Picture parameter set RBSP syntax
void read_picture_parameter_set_rbsp(h264_stream_t* h, bs_t* b);
// 7.3.2.3 Supplemental enhancement information RBSP syntax
void read_sei_rbsp(h264_stream_t* h, bs_t* b);
// 7.3.2.3.1 Supplemental enhancement information message syntax
void read_sei_message(h264_stream_t* h, bs_t* b);
// 7.3.2.8 Slice layer without partitioning RBSP syntax
void read_slice_layer_rbsp(h264_stream_t* h, bs_t* b);
// 7.3.2.10 RBSP slice trailing bits syntax
void read_rbsp_slice_trailing_bits(h264_stream_t* h, bs_t* b);
// 7.3.2.11 RBSP trailing bits bits syntax
void read_rbsp_trailing_bits(h264_stream_t* h, bs_t* b);
// 7.3.3 Slice header syntax
void read_slice_header(h264_stream_t* h, bs_t* b);
// 7.3.3.1 Reference picture list modification syntax
void read_ref_pic_list_modification(h264_stream_t* h, bs_t* b);
// 7.3.3.2 Prediction weight table syntax
void read_pred_weight_table(h264_stream_t* h, bs_t* b);
// 7.3.3.3 Decoded refernece picture marking syntax
void read_dec_ref_pic_marking(h264_stream_t* h, bs_t* b);
// D.1 SEI payload syntax
void read_sei_payload(h264_stream_t* h, bs_t* b, int payloadType, int payloadSize);
// E.1.1 VUI parameters syntax
void read_vui_parameters(h264_stream_t* h, bs_t* b);
// E.1.2 HRD parameters syntax
void read_hrd_parameters(h264_stream_t* h, bs_t* b);
// debug info
void debug_nal(h264_stream_t* h, h264_nal_t* nal);
void debug_sps(h264_stream_t* h);
void debug_pps(h264_stream_t* h);
void debug_slice_header(h264_stream_t* h);
void debug_aud(h264_stream_t* h);
void debug_seis(h264_stream_t* h);
void debug_bytes(uint8_t* buf, int len);
#endif // !_H264_PARSER_H_
3.cpp文件
3.1 H264分析(h264_parser.cpp)
函数定义了具体如何读取sps、pps和slice header当中的信息,可以参考2021年8月的H264标准中定义的语法元素
#include "h264_parser.h"
/**
Calculate the log base 2 of the argument, rounded up.
Zero or negative arguments return zero
Idea from http://www.southwindsgames.com/blog/2009/01/19/fast-integer-log2-function-in-cc/
*/
int intlog2(int x)
{
int log = 0;
if (x < 0) { x = 0; }
while ((x >> log) > 0)
{
log++;
}
if (log > 0 && x == 1 << (log - 1)) { log--; }
return log;
}
h264_stream_t* h264_new()
{
h264_stream_t* h = (h264_stream_t*)calloc(1, sizeof(h264_stream_t));
h->nal = (h264_nal_t*)calloc(1, sizeof(h264_nal_t));
// initialize tables
for (int i = 0; i < 32; i++) { h->sps_table[i] = (h264_sps_t*)calloc(1, sizeof(h264_sps_t)); }
for (int i = 0; i < 256; i++) { h->pps_table[i] = (h264_pps_t*)calloc(1, sizeof(h264_pps_t)); }
h->sps = h->sps_table[0];
h->pps = h->pps_table[0];
h->aud = (h264_aud_t*)calloc(1, sizeof(h264_aud_t));
h->num_seis = 0;
h->seis = NULL;
h->sei = NULL; //This is a TEMP pointer at whats in h->seis...
h->sh = (h264_slice_header_t*)calloc(1, sizeof(h264_slice_header_t));
return h;
}
void sei_free(h264_sei_t* s)
{
if (s->payload != NULL) free(s->payload);
free(s);
}
void h264_free(h264_stream_t * h)
{
free(h->nal);
for (int i = 0; i < 32; i++) { free(h->sps_table[i]); }
for (int i = 0; i < 256; i++) { free(h->pps_table[i]); }
free(h->aud);
if (h->seis != NULL)
{
for (int i = 0; i < h->num_seis; i++)
{
h264_sei_t* sei = h->seis[i];
sei_free(sei);
}
free(h->seis);
}
free(h->sh);
free(h);
}
int _read_ff_coded_number(bs_t* b)
{
int n1 = 0;
int n2;
do
{
n2 = bs_read_u8(b);
n1 += n2;
} while (n2 == 0xff);
return n1;
}
void read_sei_end_bits(h264_stream_t * h, bs_t * b)
{
// if the message doesn't end at a byte border
if (!bs_byte_aligned(b))
{
if (!bs_read_u1(b))
//fprintf(stderr, "WARNING: bit_equal_to_one is 0!!!!\n");
ERROR("WARNING: bit_equal_to_one is 0");
while (!bs_byte_aligned(b))
{
if (bs_read_u1(b))
// fprintf(stderr, "WARNING: bit_equal_to_zero is 1!!!!\n");
ERROR("WARNING: bit_equal_to_zero is 1");
}
}
read_rbsp_trailing_bits(h, b);
}
// 这里减去5,是为了将format对齐。在ffmpeg中,使用的是%5
int is_slice_type(int slice_type, int cmp_type)
{
if (slice_type >= 5) { slice_type -= 5; }
if (cmp_type >= 5) { cmp_type -= 5; }
if (slice_type == cmp_type) { return 1; }
else { return 0; }
}
// 7.3.1 Nal unit syntax
void read_nal_unit(h264_stream_t* h, uint8_t* buf, int size)
{
h264_nal_t* nal = h->nal;
bs_t* b = bs_new(buf, size);
nal->forbidden_zero_bit = bs_read_f(b, 1);
nal->nal_ref_idc = bs_read_u(b, 2);
nal->nal_unit_type = bs_read_u(b, 5);
nal->parsed = NULL;
nal->sizeof_parsed = 0;
bs_free(b);
int nal_size = size;
int rbsp_size = size;
uint8_t* rbsp_buf = (uint8_t*)malloc(rbsp_size);
// 在rbsp中,存在0x000003这样的分段码,函数会去掉这个分段码,将数据放到rbsp_buf中
int rc = nal_to_rbsp(buf, &nal_size, rbsp_buf, &rbsp_size);
if (rc < 0) {
ERROR("transfer nal to rbsp failed");
free(rbsp_buf);
return;
} // handle conversion error
b = bs_new(rbsp_buf, rbsp_size);
switch (nal->nal_unit_type)
{
/*
if nal_unit_type == 14 || nal_unit_type == 20 || nal_unit_type == 21
{
if (nal_unit_type != 21)
{
svc_extension_flag;
}
else
{
avc_3d_extension_flag; // specified in Annex J
}
if (svc_extension_flag)
{
nal_unit_header_svc_extension(); // specified in Annex G
}
else if (avc_3d_extension_flag)
{
nal_unit_header_3davc_extension(); // specified in Annex J
}
else
{
nal_unit_header_mvc_extension(); // specified in Annex H
}
}
*/
case NAL_UNIT_TYPE_CODED_SLICE_IDR:
case NAL_UNIT_TYPE_CODED_SLICE_NON_IDR:
case NAL_UNIT_TYPE_CODED_SLICE_AUX:
//printf("find slice\n");
read_slice_layer_rbsp(h, b);
break;
case NAL_UNIT_TYPE_SPS:
//printf("find sps\n");
read_sequence_parameter_set_rbsp(h, b);
break;
case NAL_UNIT_TYPE_PPS:
//printf("find pps\n");
read_picture_parameter_set_rbsp(h, b);
break;
case NAL_UNIT_TYPE_SEI:
//printf("find sei\n");
read_sei_rbsp(h, b);
break;
};
}
// 7.3.2.1 Sequence parameter set RBSP syntax
void read_sequence_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
{
int profile_idc = bs_read_u(b, 8);
int constraint_set0_flag = bs_read_u1(b);
int constraint_set1_flag = bs_read_u1(b);
int constraint_set2_flag = bs_read_u1(b);
int constraint_set3_flag = bs_read_u1(b);
int constraint_set4_flag = bs_read_u1(b);
int constraint_set5_flag = bs_read_u1(b);
int reserved_zero_2bits = bs_read_u(b, 2);
int level_idc = bs_read_u8(b);
int seq_parameter_set_id = bs_read_ue(b);
// 当编码器产生新的序列集时,应该使用新的seq_parameter_set_id
// 即使用新的序列参数集,而不是改变原有sps的信息,这在视频源切换的情况下会出现
h->sps = h->sps_table[seq_parameter_set_id];
h264_sps_t* sps = h->sps;
memset(sps, 0, sizeof(h264_sps_t));
sps->profile_idc = profile_idc;
sps->constraint_set0_flag = constraint_set0_flag;
sps->constraint_set1_flag = constraint_set1_flag;
sps->constraint_set2_flag = constraint_set2_flag;
sps->constraint_set3_flag = constraint_set3_flag;
sps->constraint_set4_flag = constraint_set4_flag;
sps->constraint_set5_flag = constraint_set5_flag;
sps->reserved_zero_2bits = reserved_zero_2bits;
sps->level_idc = level_idc;
sps->seq_parameter_set_id = seq_parameter_set_id;
// 根据profile idc确定sps的一些参数
if (profile_idc == 100 || // High profile
profile_idc == 110 || // High10 profile
profile_idc == 122 || // High422 profile
profile_idc == 244 || // High444 Predictive profile
profile_idc == 44 || // Cavlc444 profile
profile_idc == 83 || // Scalable Constrained High profile (SVC)
profile_idc == 86 || // Scalable High Intra profile (SVC)
profile_idc == 118 || // Stereo High profile (MVC)
profile_idc == 128 || // Multiview High profile (MVC)
profile_idc == 138 || // Multiview Depth High profile (MVCD)
profile_idc == 139 || // High444 Intra
profile_idc == 134 || // High10 Intra
profile_idc == 135) // High422 Intra
{
sps->chroma_format_idc = bs_read_ue(b);
sps->ChromaArrayType = sps->chroma_format_idc;
if (sps->chroma_format_idc == 3)
{
sps->separate_colour_plane_flag = bs_read_u1(b);
if (sps->separate_colour_plane_flag)
{
sps->ChromaArrayType = 0;
}
}
sps->bit_depth_luma_minus8 = bs_read_ue(b);
sps->bit_depth_chroma_minus8 = bs_read_ue(b);
sps->qpprime_y_zero_transform_bypass_flag = bs_read_u1(b);
sps->seq_scaling_matrix_present_flag = bs_read_u1(b);
if (sps->seq_scaling_matrix_present_flag)
{
for (int i = 0; i < ((sps->chroma_format_idc != 3) ? 8 : 12); i++)
{
sps->seq_scaling_list_present_flag[i] = bs_read_u1(b);
if (sps->seq_scaling_list_present_flag[i])
{
if (i < 6)
{
read_scaling_list(b, sps->ScalingList4x4[i], 16, sps->UseDefaultScaningMatrix4x4Flag[i]);
}
else
{
read_scaling_list(b, sps->ScalingList8x8[i - 6], 64, sps->UseDefaultScaningMatrix8x8Flag[i]);
}
}
}
}
}
sps->log2_max_frame_num_minus4 = bs_read_ue(b);
sps->pic_order_cnt_type = bs_read_ue(b);
if (sps->pic_order_cnt_type == 0)
{
sps->log2_max_pic_order_cnt_lsb_minus4 = bs_read_ue(b);
}
else if (sps->pic_order_cnt_type == 1)
{
sps->delta_pic_order_always_zero_flag = bs_read_u1(b);
sps->offset_for_non_ref_pic = bs_read_se(b);
sps->offset_for_top_to_bottom_field = bs_read_se(b);
sps->num_ref_frames_in_pic_order_cnt_cycle = bs_read_ue(b);
for (int i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++)
{
sps->offset_for_ref_frame[i] = bs_read_se(b);
}
}
sps->max_num_ref_frames = bs_read_ue(b);
sps->gaps_in_frame_num_value_allowed_flag = bs_read_u1(b);
sps->pic_width_in_mbs_minus1 = bs_read_ue(b);
sps->pic_height_in_map_units_minus1 = bs_read_ue(b);
sps->frame_mbs_only_flag = bs_read_u1(b);
if (!sps->frame_mbs_only_flag)
{
sps->mb_adaptive_frame_field_flag = bs_read_u1(b);
}
sps->direct_8x8_inference_flag = bs_read_u1(b);
sps->frame_cropping_flag = bs_read_u1(b);
if (sps->frame_cropping_flag)
{
sps->frame_crop_left_offset = bs_read_ue(b);
sps->frame_crop_right_offset = bs_read_ue(b);
sps->frame_crop_top_offset = bs_read_ue(b);
sps->frame_crop_bottom_offset = bs_read_ue(b);
}
sps->vui_parameters_present_flag = bs_read_u1(b);
// vui info
if (sps->vui_parameters_present_flag)
{
read_vui_parameters(h, b);
}
// trailing bits
read_rbsp_trailing_bits(h, b);
}
// 7.3.2.1.1 Scaling list syntax syntax
void read_scaling_list(bs_t* b, int* scaling_list, int size, int use_default_scaling_matrix_flag)
{
int last_scale = 8;
int next_scale = 8;
int delta_scale = 0;
for (int j = 0; j < size; j++)
{
if (next_scale != 0)
{
delta_scale = bs_read_se(b);
next_scale = (last_scale + delta_scale + 256) % 256;
use_default_scaling_matrix_flag = (j == 0 && next_scale == 0);
}
scaling_list[j] = (next_scale == 0) ? last_scale : next_scale;
last_scale = scaling_list[j];
}
}
// E.1.1 VUI parameters syntax
void read_vui_parameters(h264_stream_t* h, bs_t* b)
{
h264_sps* sps = h->sps;
sps->vui.aspect_ratio_info_present_flag = bs_read_u1(b);
if (sps->vui.aspect_ratio_info_present_flag)
{
sps->vui.aspect_ratio_idc = bs_read_u8(b);
if (sps->vui.aspect_ratio_idc == Extended_SAR)
{
sps->vui.sar_width = bs_read_u(b, 16);
sps->vui.sar_height = bs_read_u(b, 16);
}
}
sps->vui.overscan_info_present_flag = bs_read_u1(b);
if (sps->vui.overscan_info_present_flag)
{
sps->vui.overscan_appropriate_flag = bs_read_u1(b);
}
sps->vui.video_signal_type_present_flag = bs_read_u1(b);
if (sps->vui.video_signal_type_present_flag)
{
sps->vui.video_format = bs_read_u(b, 3);
sps->vui.video_full_range_flag = bs_read_u1(b);
sps->vui.colour_description_present_flag = bs_read_u1(b);
if (sps->vui.colour_description_present_flag)
{
sps->vui.colour_primaries = bs_read_u8(b);
sps->vui.transfer_characteristics = bs_read_u8(b);
sps->vui.matrix_coefficients = bs_read_u8(b);
}
}
sps->vui.chroma_loc_info_present_flag = bs_read_u1(b);
if (sps->vui.chroma_loc_info_present_flag)
{
sps->vui.chroma_sample_loc_type_top_field = bs_read_ue(b);
sps->vui.chroma_sample_loc_type_bottom_field = bs_read_ue(b);
}
sps->vui.timing_info_present_flag = bs_read_u1(b);
if (sps->vui.timing_info_present_flag)
{
sps->vui.num_units_in_tick = bs_read_u(b, 32);
sps->vui.time_scale = bs_read_u(b, 32);
sps->vui.fixed_frame_rate_flag = bs_read_u(b, 1);
}
// hrd info
sps->vui.nal_hrd_parameters_present_flag = bs_read_u1(b);
if (sps->vui.nal_hrd_parameters_present_flag)
{
read_hrd_parameters(h, b);
}
// vcl info
sps->vui.vcl_hrd_parameters_present_flag = bs_read_u1(b);
if (sps->vui.vcl_hrd_parameters_present_flag)
{
read_hrd_parameters(h, b); // why hrd?
}
if (sps->vui.nal_hrd_parameters_present_flag || sps->vui.vcl_hrd_parameters_present_flag)
{
sps->vui.low_delay_hrd_flag = bs_read_u1(b);
}
sps->vui.pic_struct_present_flag = bs_read_u1(b);
sps->vui.bitstream_restriction_flag = bs_read_u1(b);
if (sps->vui.bitstream_restriction_flag)
{
sps->vui.motion_vectors_over_pic_boundaries_flag = bs_read_u1(b);
sps->vui.max_bytes_per_pic_denom = bs_read_ue(b);
sps->vui.max_bits_per_mb_denom = bs_read_ue(b);
sps->vui.log2_max_mv_length_horizontal = bs_read_ue(b);
sps->vui.log2_max_mv_length_vertical = bs_read_ue(b);
sps->vui.max_num_reorder_frames = bs_read_ue(b);
sps->vui.max_dec_frame_buffering = bs_read_ue(b);
}
}
// E.1.2 HRD parameters syntax
void read_hrd_parameters(h264_stream_t* h, bs_t* b)
{
h264_sps* sps = h->sps;
sps->hrd.cpb_cnt_minus1 = bs_read_ue(b);
sps->hrd.bit_rate_scale = bs_read_u(b, 4);
sps->hrd.cpb_size_scale = bs_read_u(b, 4);
for (int sched_sel_idx = 0; sched_sel_idx <= sps->hrd.cpb_cnt_minus1; sched_sel_idx++)
{
sps->hrd.bit_rate_value_minus1[sched_sel_idx] = bs_read_ue(b);
sps->hrd.cpb_size_value_minus1[sched_sel_idx] = bs_read_ue(b);
sps->hrd.cbr_flag[sched_sel_idx] = bs_read_u1(b);
}
sps->hrd.initial_cpb_removal_delay_length_minus1 = bs_read_u(b, 5);
sps->hrd.cpb_removal_delay_length_minus1 = bs_read_u(b, 5);
sps->hrd.dpb_removal_delay_length_minus1 = bs_read_u(b, 5);
sps->hrd.time_offset_length = bs_read_u(b, 5);
}
int more_rbsp_data(h264_stream_t * h, bs_t * b)
{
// 雷博原有的代码有bug,这里无法正确解析后面到来的data
//if (bs_eof(b)) { return 0; }
//if (bs_peek_u1(b) == 1) { return 0; } // if next bit is 1, we've reached the stop bit
//return 1;
// see FFmpeg h264_ps.c
int profile_idc = h->sps->profile_idc;
int constraint_set_flags = 0;
constraint_set_flags |= h->sps->constraint_set0_flag << 0; // constraint_set0_flag
constraint_set_flags |= h->sps->constraint_set1_flag << 1; // constraint_set1_flag
constraint_set_flags |= h->sps->constraint_set2_flag << 2; // constraint_set2_flag
constraint_set_flags |= h->sps->constraint_set3_flag << 3; // constraint_set3_flag
constraint_set_flags |= h->sps->constraint_set4_flag << 4; // constraint_set4_flag
constraint_set_flags |= h->sps->constraint_set5_flag << 5; // constraint_set5_flag
if ((profile_idc == 66 || profile_idc == 77 ||
profile_idc == 88) && (constraint_set_flags & 7))
{
ERROR("Current profile doesn't provide more RBSP data in PPS, skipping");
return 0;
}
return 1;
}
// 7.3.2.2 Picture parameter set RBSP syntax
void read_picture_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
{
int pps_id = bs_read_ue(b);
h264_pps_t* pps = h->pps = h->pps_table[pps_id];
memset(pps, 0, sizeof(h264_pps_t));
pps->pic_parameter_set_id = pps_id;
pps->seq_parameter_set_id = bs_read_ue(b);
pps->entropy_coding_mode_flag = bs_read_u1(b);
pps->bottom_field_pic_order_in_frame_present_flag = bs_read_u1(b);
pps->num_slice_groups_minus1 = bs_read_ue(b);
if (pps->num_slice_groups_minus1 > 0)
{
pps->slice_group_map_type = bs_read_ue(b);
if (pps->slice_group_map_type == 0)
{
for (int i_group = 0; i_group <= pps->num_slice_groups_minus1; i_group++)
{
pps->run_length_minus1[i_group] = bs_read_ue(b);
}
}
else if (pps->slice_group_map_type == 2)
{
for (int i_group = 0; i_group <= pps->num_slice_groups_minus1; i_group++)
{
pps->top_left[i_group] = bs_read_ue(b);
pps->bottom_right[i_group] = bs_read_ue(b);
}
}
else if (pps->slice_group_map_type == 3 || pps->slice_group_map_type == 4 || pps->slice_group_map_type == 5)
{
pps->slice_group_change_direction_flag = bs_read_u1(b);
pps->slice_group_change_rate_minus1 = bs_read_ue(b);
}
else if (pps->slice_group_map_type == 6)
{
pps->pic_size_in_map_units_minus1 = bs_read_ue(b);
for (int i = 0; i <= pps->pic_size_in_map_units_minus1; i++)
{
pps->slice_group_id[i] = bs_read_u(b, intlog2(pps->num_slice_groups_minus1 + 1)); // reaplace u(v)
}
}
}
pps->num_ref_idx_l0_default_active_minus1 = bs_read_ue(b);
pps->num_ref_idx_l1_default_active_minus1 = bs_read_ue(b);
pps->weighted_pred_flag = bs_read_u1(b);
pps->weighted_bipred_idc = bs_read_u(b, 2);
pps->pic_init_qp_minus26 = bs_read_se(b);
pps->pic_init_qs_minus26 = bs_read_se(b);
pps->chroma_qp_index_offset = bs_read_se(b);
pps->deblocking_filter_control_present_flag = bs_read_u1(b);
pps->constrained_intra_pred_flag = bs_read_u1(b);
pps->redundant_pic_cnt_present_flag = bs_read_u1(b);
pps->more_rbsp_data = more_rbsp_data(h, b);
if (pps->more_rbsp_data)
{
pps->transform_8x8_mode_flag = bs_read_u1(b);
pps->pic_scaling_matrix_present_flag = bs_read_u1(b);
if (pps->pic_scaling_matrix_present_flag)
{
for (int i = 0; i < 6 + ((h->sps->chroma_format_idc != 3) ? 2 : 6) * pps->transform_8x8_mode_flag; i++)
{
pps->pic_scaling_list_present_flag[i] = bs_read_u1(b);
if (pps->pic_scaling_list_present_flag)
{
if (i < 6)
{
read_scaling_list(b, pps->ScalingList4x4[i], 16, pps->UseDefaultScalingMatrix4x4[i]);
}
else
{
read_scaling_list(b, pps->ScalingList8x8[i - 6], 64, pps->UseDefaultScalingMatrix8x8[i - 6]);
}
}
}
}
pps->second_chroma_qp_index_offset = bs_read_se(b);
}
read_rbsp_trailing_bits(h, b);
}
// 7.3.2.3 Supplemental enhancement information RBSP syntax
void read_sei_rbsp(h264_stream_t * h, bs_t * b)
{
int i;
for (i = 0; i < h->num_seis; i++)
{
sei_free(h->seis[i]);
}
h->num_seis = 0;
do {
h->num_seis++;
h->seis = (h264_sei_t * *)realloc(h->seis, h->num_seis * sizeof(h264_sei_t*));
// sei new
h264_sei_t* s = (h264_sei_t*)malloc(sizeof(h264_sei_t));
memset(s, 0, sizeof(h264_sei_t));
s->payload = NULL;
h->seis[h->num_seis - 1] = s;
h->sei = h->seis[h->num_seis - 1];
read_sei_message(h, b);
} while (!(bs_eof(b) || bs_peek_u1(b) == 1)); // 检查是否有更多数据
read_rbsp_trailing_bits(h, b);
}
// 7.3.2.3.1 Supplemental enhancement information message syntax
void read_sei_message(h264_stream_t* h, bs_t* b)
{
h->sei->payload_type = _read_ff_coded_number(b);
h->sei->payload_size = _read_ff_coded_number(b);
read_sei_payload(h, b, h->sei->payload_type, h->sei->payload_size);
}
// D.1 SEI payload syntax
void read_sei_payload(h264_stream_t* h, bs_t* b, int payloadType, int payloadSize)
{
h264_sei_t* s = h->sei;
s->payload = (uint8_t*)malloc(payloadSize);
int i;
for (i = 0; i < payloadSize; i++)
s->payload[i] = bs_read_u(b, 8);
read_sei_end_bits(h, b);
}
// 7.3.2.8 Slice layer without partitioning RBSP syntax
void read_slice_layer_rbsp(h264_stream_t* h, bs_t* b)
{
read_slice_header(h, b);
h264_slice_data_rbsp_t* slice_data = h->slice_data;
if (slice_data != NULL)
{
if (slice_data->rbsp_buf != NULL) free(slice_data->rbsp_buf);
uint8_t * sptr = b->p + (!!b->bits_left); // CABAC-specific: skip alignment bits, if there are any
slice_data->rbsp_size = b->end - sptr;
slice_data->rbsp_buf = (uint8_t*)malloc(slice_data->rbsp_size);
memcpy(slice_data->rbsp_buf, sptr, slice_data->rbsp_size);
// ugly hack: since next NALU starts at byte border, we are going to be padded by trailing_bits;
return;
}
// FIXME should read or skip data
//slice_data( ); /* all categories of slice_data( ) syntax */
read_rbsp_slice_trailing_bits(h, b);
}
// 7.3.2.10 RBSP slice trailing bits syntax
void read_rbsp_slice_trailing_bits(h264_stream_t* h, bs_t* b)
{
read_rbsp_trailing_bits(h, b);
int cabac_zero_word;
if (h->pps->entropy_coding_mode_flag)
{
while (!bs_eof(b))
{
cabac_zero_word = bs_read_f(b, 16); // equal to 0x0000
}
}
}
// 7.3.2.11 RBSP trailing bits syntax
void read_rbsp_trailing_bits(h264_stream_t * h, bs_t * b)
{
int rbsp_stop_one_bit = bs_read_f(b, 1); // equal to 1
while (!bs_byte_aligned(b))
{
int rbsp_alignment_zero_bit = bs_read_f(b, 1); // equal to 0
}
}
// 7.3.3 Slice header syntax
void read_slice_header(h264_stream_t* h, bs_t* b)
{
h264_slice_header_t* sh = h->sh;
memset(sh, 0, sizeof(h264_slice_header_t));
h264_sps_t* sps = h->sps; // h->sps;
h264_pps_t* pps = h->pps;//h->pps;
h264_nal_t* nal = h->nal;
sh->first_mb_in_slice = bs_read_ue(b);
sh->slice_type = bs_read_ue(b);
sh->pic_parameter_set_id = bs_read_ue(b);
if (h->sps->separate_colour_plane_flag == 1)
{
sh->colour_plane_id = bs_read_u(b, 2);
}
sh->frame_num = bs_read_u(b, sps->log2_max_frame_num_minus4 + 4); // was u(v)
if (!h->sps->frame_mbs_only_flag)
{
sh->field_pic_flag = bs_read_u1(b);
if (sh->field_pic_flag)
{
sh->bottom_field_flag = bs_read_u1(b);
}
}
if (nal->nal_unit_type == 5)
{
sh->idr_pic_id = bs_read_ue(b);
}
if (h->sps->pic_order_cnt_type == 0)
{
// sh->pic_order_cnt_lsb_bytes = sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
// sh->pic_order_cnt_lsb = bs_read_u(b, sh->pic_order_cnt_lsb_bytes); // was u(v)
// sh->pic_order_cnt_lsb = bs_read_u(b, sps->log2_max_frame_num_minus4 + 4);
sh->pic_order_cnt_lsb = bs_read_u(b, sps->log2_max_pic_order_cnt_lsb_minus4 + 4); // was u(v)
if (h->pps->bottom_field_pic_order_in_frame_present_flag && !sh->field_pic_flag)
{
sh->delta_pic_order_cnt_bottom = bs_read_se(b);
}
}
if (h->sps->pic_order_cnt_type == 1 && !h->sps->delta_pic_order_always_zero_flag)
{
sh->delta_pic_order_cnt[0] = bs_read_se(b);
if (h->pps->bottom_field_pic_order_in_frame_present_flag && !sh->field_pic_flag)
{
sh->delta_pic_order_cnt[1] = bs_read_se(b);
}
}
if (h->pps->redundant_pic_cnt_present_flag)
{
sh->redundant_pic_cnt = bs_read_ue(b);
}
if (is_slice_type(h->sh->slice_type, SH_SLICE_TYPE_B))
{
sh->direct_spatial_mv_pred_flag = bs_read_u1(b);
}
if (is_slice_type(h->sh->slice_type, SH_SLICE_TYPE_P) || is_slice_type(h->sh->slice_type, SH_SLICE_TYPE_SP) || is_slice_type(h->sh->slice_type, SH_SLICE_TYPE_B))
{
sh->num_ref_idx_active_override_flag = bs_read_u1(b);
if (sh->num_ref_idx_active_override_flag)
{
sh->num_ref_idx_l0_active_minus1 = bs_read_ue(b);
if (is_slice_type(h->sh->slice_type, SH_SLICE_TYPE_B))
{
sh->num_ref_idx_l1_active_minus1 = bs_read_ue(b);
}
}
}
if (h->nal->nal_unit_type == 20 || h->nal->nal_unit_type == 21)
{
// ref_pic_list_mvc_modification
}
else // rplm
{
read_ref_pic_list_modification(h, b);
}
// pwt
if ((pps->weighted_pred_flag && (is_slice_type(sh->slice_type, SH_SLICE_TYPE_P) || is_slice_type(sh->slice_type, SH_SLICE_TYPE_SP))) ||
(pps->weighted_bipred_idc == 1 && is_slice_type(sh->slice_type, SH_SLICE_TYPE_B)))
{
read_pred_weight_table(h, b);
}
if (h->nal->nal_ref_idc != 0)
{
read_dec_ref_pic_marking(h, b);
}
if (pps->entropy_coding_mode_flag && !is_slice_type(sh->slice_type, SH_SLICE_TYPE_I) && !is_slice_type(sh->slice_type, SH_SLICE_TYPE_SI))
{
sh->cabac_init_idc = bs_read_ue(b);
}
sh->slice_qp_delta = bs_read_se(b);
if (is_slice_type(sh->slice_type, SH_SLICE_TYPE_SP) || is_slice_type(sh->slice_type, SH_SLICE_TYPE_SI))
{
if (is_slice_type(sh->slice_type, SH_SLICE_TYPE_SP))
{
sh->sp_for_switch_flag = bs_read_u1(b);
}
sh->slice_qs_delta = bs_read_se(b);
}
if (pps->deblocking_filter_control_present_flag)
{
sh->disable_deblocking_filter_idc = bs_read_ue(b);
if (sh->disable_deblocking_filter_idc != 1)
{
sh->slice_alpha_c0_offset_div2 = bs_read_se(b);
sh->slice_beta_offset_div2 = bs_read_se(b);
}
}
if (pps->num_slice_groups_minus1 > 0 &&
pps->slice_group_map_type >= 3 && pps->slice_group_map_type <= 5)
{
sh->slice_group_change_cycle = bs_read_u(b, intlog2(pps->pic_size_in_map_units_minus1 +
pps->slice_group_change_rate_minus1 + 1));
}
}
// 7.3.3.1 Reference picture list modification syntax
void read_ref_pic_list_modification(h264_stream_t * h, bs_t * b)
{
h264_slice_header* sh = h->sh;
int i = 0;
if (!is_slice_type(sh->slice_type, SH_SLICE_TYPE_I) && !is_slice_type(sh->slice_type, SH_SLICE_TYPE_SI))
{
sh->rplm.ref_pic_list_modification_flag_l0 = bs_read_u1(b);
if (sh->rplm.ref_pic_list_modification_flag_l0)
{
do
{
sh->rplm.modification_of_pic_nums_idc = bs_read_ue(b);
if (sh->rplm.modification_of_pic_nums_idc == 0 ||
sh->rplm.modification_of_pic_nums_idc == 1)
{
sh->rplm.abs_diff_pic_num_minus1 = bs_read_ue(b);
}
else if (sh->rplm.modification_of_pic_nums_idc == 2)
{
sh->rplm.long_term_pic_num = bs_read_ue(b);
}
} while (sh->rplm.modification_of_pic_nums_idc != 3 && ! bs_eof(b));
}
}
i = 0;
if (is_slice_type(sh->slice_type, SH_SLICE_TYPE_B))
{
sh->rplm.ref_pic_list_modification_flag_l1 = bs_read_u1(b);
if (sh->rplm.ref_pic_list_modification_flag_l1)
{
do
{
sh->rplm.modification_of_pic_nums_idc = bs_read_ue(b);
if (sh->rplm.modification_of_pic_nums_idc == 0 ||
sh->rplm.modification_of_pic_nums_idc == 1)
{
sh->rplm.abs_diff_pic_num_minus1 = bs_read_ue(b);
}
else if (sh->rplm.modification_of_pic_nums_idc == 2)
{
sh->rplm.long_term_pic_num = bs_read_ue(b);
}
} while (sh->rplm.modification_of_pic_nums_idc != 3 && !bs_eof(b));
}
}
}
// 7.3.3.2 Prediction weight table syntax
void read_pred_weight_table(h264_stream_t * h, bs_t * b)
{
h264_slice_header_t* sh = h->sh;
h264_sps_t* sps = h->sps;
h264_pps_t* pps = h->pps;
int i, j;
sh->pwt.luma_log2_weight_denom = bs_read_ue(b);
if (sps->ChromaArrayType != 0)
{
sh->pwt.chroma_log2_weight_denom = bs_read_ue(b);
}
for (i = 0; i <= sh->num_ref_idx_l0_active_minus1; i++)
{
sh->pwt.luma_weight_l0_flag[i] = bs_read_u1(b);
if (sh->pwt.luma_weight_l0_flag[i])
{
sh->pwt.luma_weight_l0[i] = bs_read_se(b);
sh->pwt.luma_offset_l0[i] = bs_read_se(b);
}
if (sps->ChromaArrayType != 0)
{
sh->pwt.chroma_weight_l0_flag[i] = bs_read_u1(b);
if (sh->pwt.chroma_weight_l0_flag[i])
{
for (j = 0; j < 2; j++)
{
sh->pwt.chroma_weight_l0[i][j] = bs_read_se(b);
sh->pwt.chroma_offset_l0[i][j] = bs_read_se(b);
}
}
}
}
if (is_slice_type(sh->slice_type, SH_SLICE_TYPE_B))
{
for (i = 0; i <= sh->num_ref_idx_l1_active_minus1; i++)
{
sh->pwt.luma_weight_l1_flag[i] = bs_read_u1(b);
if (sh->pwt.luma_weight_l1_flag[i])
{
sh->pwt.luma_weight_l1[i] = bs_read_se(b);
sh->pwt.luma_offset_l1[i] = bs_read_se(b);
}
if (sps->ChromaArrayType != 0)
{
sh->pwt.chroma_weight_l1_flag[i] = bs_read_u1(b);
if (sh->pwt.chroma_weight_l1_flag[i])
{
for (j = 0; j < 2; j++)
{
sh->pwt.chroma_weight_l1[i][j] = bs_read_se(b);
sh->pwt.chroma_offset_l1[i][j] = bs_read_se(b);
}
}
}
}
}
}
// 7.3.3.3 Decoded reference picture marking syntax
void read_dec_ref_pic_marking(h264_stream_t * h, bs_t * b)
{
h264_slice_header_t* sh = h->sh;
h264_sps_t* sps = h->sps;
h264_pps_t* pps = h->pps;
int i = 0;
if (h->nal->nal_unit_type == 5)
{
sh->drpm.no_output_of_prior_pics_flag = bs_read_u1(b);
sh->drpm.long_term_reference_flag = bs_read_u1(b);
}
else
{
sh->drpm.adaptive_ref_pic_marking_mode_flag = bs_read_u1(b);
if (sh->drpm.adaptive_ref_pic_marking_mode_flag)
{
do
{
sh->drpm.memory_management_control_operation = bs_read_ue(b);
if (sh->drpm.memory_management_control_operation == 1 ||
sh->drpm.memory_management_control_operation == 3)
{
sh->drpm.difference_of_pic_nums_minus1 = bs_read_ue(b);
}
if (sh->drpm.memory_management_control_operation == 2)
{
sh->drpm.long_term_pic_num = bs_read_ue(b);
}
if (sh->drpm.memory_management_control_operation == 3 ||
sh->drpm.memory_management_control_operation == 6)
{
sh->drpm.long_term_frame_idx = bs_read_ue(b);
}
if (sh->drpm.memory_management_control_operation == 4)
{
sh->drpm.max_long_term_frame_idx_plus1 = bs_read_ue(b);
}
} while (sh->drpm.memory_management_control_operation != 0 && !bs_eof(b));
}
}
}
// skip 0x000003 in EBSP (Encapsulated Byte Sequence Payload),
// then we have RBSP(Raw Byte Squence Payload)
int nal_to_rbsp(const uint8_t * nal_buf, int* nal_size, uint8_t * rbsp_buf, int* rbsp_size)
{
int i;
int j = 0;
int count = 0;
for (i = 1; i < *nal_size; i++)
{
// in NAL unit, 0x000000, 0x000001 or 0x000002 shall not occur at any byte-aligned position
if ((count == 2) && (nal_buf[i] < 0x03))
{
return -1;
}
if ((count == 2) && (nal_buf[i] == 0x03))
{
// check the 4th byte after 0x000003, except when cabac_zero_word is used, in which case the last three bytes of this NAL unit must be 0x000003
if ((i < *nal_size - 1) && (nal_buf[i + 1] > 0x03))
{
return -1;
}
// if cabac_zero_word is used, the final byte of this NAL unit(0x03) is discarded, and the last two bytes of RBSP must be 0x0000
if (i == *nal_size - 1)
{
break;
}
i++;
count = 0;
}
if (j >= *rbsp_size)
{
// error, not enough space
return -1;
}
rbsp_buf[j] = nal_buf[i];
if (nal_buf[i] == 0x00)
{
count++;
}
else
{
count = 0;
}
j++;
}
*nal_size = i;
*rbsp_size = j;
return j;
}
/**
Find the beginning and end of a NAL (Network Abstraction Layer) unit in a byte buffer containing H264 bitstream data.
@param[in] buf the buffer
@param[in] size the size of the buffer
@param[out] nal_start the beginning offset of the nal
@param[out] nal_end the end offset of the nal
@return the length of the nal, or 0 if did not find start of nal, or -1 if did not find end of nal
*/
int find_nal_unit(uint8_t * buf, int size, int* nal_start, int* nal_end)
{
int i;
// find start
*nal_start = 0;
*nal_end = 0;
i = 0;
while ( //( next_bits( 24 ) != 0x000001 && next_bits( 32 ) != 0x00000001 )
(buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0x01) &&
(buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0 || buf[i + 3] != 0x01)
)
{
i++; // skip leading zero
if (i + 4 >= size) { return 0; } // did not find nal start
}
if (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0x01) // ( next_bits( 24 ) != 0x000001 )
{
i++;
}
if (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0x01) { /* error, should never happen */ return 0; }
i += 3;
*nal_start = i;
while ( //( next_bits( 24 ) != 0x000000 && next_bits( 24 ) != 0x000001 )
(buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0) &&
(buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0x01)
)
{
i++;
// FIXME the next line fails when reading a nal that ends exactly at the end of the data
if (i + 3 >= size) { *nal_end = size; return -1; } // did not find nal end, stream ended first
}
*nal_end = i;
return (*nal_end - *nal_start);
}
// debug info
void debug_nal(h264_stream_t* h, h264_nal_t* nal)
{
printf("==================== NAL ====================\n");
printf(" forbidden_zero_bit : %d \n", nal->forbidden_zero_bit);
printf(" nal_ref_idc : %d \n", nal->nal_ref_idc);
// TODO make into subroutine
const char* nal_unit_type_name;
switch (nal->nal_unit_type)
{
case NAL_UNIT_TYPE_UNSPECIFIED: nal_unit_type_name = "Unspecified"; break;
case NAL_UNIT_TYPE_CODED_SLICE_NON_IDR: nal_unit_type_name = "Coded slice of a non-IDR picture"; break;
case NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_A: nal_unit_type_name = "Coded slice data partition A"; break;
case NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_B: nal_unit_type_name = "Coded slice data partition B"; break;
case NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_C: nal_unit_type_name = "Coded slice data partition C"; break;
case NAL_UNIT_TYPE_CODED_SLICE_IDR: nal_unit_type_name = "Coded slice of an IDR picture"; break;
case NAL_UNIT_TYPE_SEI: nal_unit_type_name = "Supplemental enhancement information (SEI)"; break;
case NAL_UNIT_TYPE_SPS: nal_unit_type_name = "Sequence parameter set"; break;
case NAL_UNIT_TYPE_PPS: nal_unit_type_name = "Picture parameter set"; break;
case NAL_UNIT_TYPE_AUD: nal_unit_type_name = "Access unit delimiter"; break;
case NAL_UNIT_TYPE_END_OF_SEQUENCE: nal_unit_type_name = "End of sequence"; break;
case NAL_UNIT_TYPE_END_OF_STREAM: nal_unit_type_name = "End of stream"; break;
case NAL_UNIT_TYPE_FILLER: nal_unit_type_name = "Filler data"; break;
case NAL_UNIT_TYPE_SPS_EXT: nal_unit_type_name = "Sequence parameter set extension"; break;
// 14..18 // Reserved
case NAL_UNIT_TYPE_CODED_SLICE_AUX: nal_unit_type_name = "Coded slice of an auxiliary coded picture without partitioning"; break;
// 20..23 // Reserved
// 24..31 // Unspecified
default: nal_unit_type_name = "Unknown"; break;
}
printf(" nal_unit_type : %d ( %s ) \n", nal->nal_unit_type, nal_unit_type_name);
if (nal->nal_unit_type == NAL_UNIT_TYPE_CODED_SLICE_NON_IDR) { debug_slice_header(h); }
else if (nal->nal_unit_type == NAL_UNIT_TYPE_CODED_SLICE_IDR) { debug_slice_header(h); }
else if (nal->nal_unit_type == NAL_UNIT_TYPE_SPS) { debug_sps(h); }
else if (nal->nal_unit_type == NAL_UNIT_TYPE_PPS) { debug_pps(h); }
else if (nal->nal_unit_type == NAL_UNIT_TYPE_AUD) { debug_aud(h); }
else if (nal->nal_unit_type == NAL_UNIT_TYPE_SEI) { debug_seis(h); }
}
void debug_sps(h264_stream_t* h)
{
h264_sps_t* sps = h->sps;
printf("======= SPS =======\n");
printf(" profile_idc : %d \n", sps->profile_idc);
printf(" constraint_set0_flag : %d \n", sps->constraint_set0_flag);
printf(" constraint_set1_flag : %d \n", sps->constraint_set1_flag);
printf(" constraint_set2_flag : %d \n", sps->constraint_set2_flag);
printf(" constraint_set3_flag : %d \n", sps->constraint_set3_flag);
printf(" constraint_set4_flag : %d \n", sps->constraint_set4_flag);
printf(" constraint_set5_flag : %d \n", sps->constraint_set5_flag);
printf(" reserved_zero_2bits : %d \n", sps->reserved_zero_2bits);
printf(" level_idc : %d \n", sps->level_idc);
printf(" seq_parameter_set_id : %d \n", sps->seq_parameter_set_id);
int profile_idc = sps->profile_idc;
if (profile_idc == 100 || profile_idc == 110 ||
profile_idc == 122 || profile_idc == 244 || profile_idc == 44 ||
profile_idc == 83 || profile_idc == 86 || profile_idc == 118 ||
profile_idc == 128 || profile_idc == 138 || profile_idc == 139 ||
profile_idc == 134 || profile_idc == 135)
{
printf(" chroma_format_idc : %d \n", sps->chroma_format_idc);
if (sps->chroma_format_idc == 3)
{
printf(" separate_colour_plane_flag : %d \n", sps->separate_colour_plane_flag);
}
printf(" bit_depth_luma_minus8 : %d \n", sps->bit_depth_luma_minus8);
printf(" bit_depth_chroma_minus8 : %d \n", sps->bit_depth_chroma_minus8);
printf(" qpprime_y_zero_transform_bypass_flag : %d \n", sps->qpprime_y_zero_transform_bypass_flag);
printf(" seq_scaling_matrix_present_flag : %d \n", sps->seq_scaling_matrix_present_flag);
}
// int seq_scaling_list_present_flag[8];
// void* ScalingList4x4[6];
// int UseDefaultScalingMatrix4x4Flag[6];
// void* ScalingList8x8[2];
// int UseDefaultScalingMatrix8x8Flag[2];
printf(" log2_max_frame_num_minus4 : %d \n", sps->log2_max_frame_num_minus4);
printf(" pic_order_cnt_type : %d \n", sps->pic_order_cnt_type);
if (sps->pic_order_cnt_type == 0)
{
printf(" log2_max_pic_order_cnt_lsb_minus4 : %d \n", sps->log2_max_pic_order_cnt_lsb_minus4);
}
else if (sps->pic_order_cnt_type == 1)
{
printf(" delta_pic_order_always_zero_flag : %d \n", sps->delta_pic_order_always_zero_flag);
printf(" offset_for_non_ref_pic : %d \n", sps->offset_for_non_ref_pic);
printf(" offset_for_top_to_bottom_field : %d \n", sps->offset_for_top_to_bottom_field);
printf(" num_ref_frames_in_pic_order_cnt_cycle : %d \n", sps->num_ref_frames_in_pic_order_cnt_cycle);
for (int i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++)
{
// int offset_for_ref_frame[256];
}
}
printf(" max_num_ref_frames : %d \n", sps->max_num_ref_frames);
printf(" gaps_in_frame_num_value_allowed_flag : %d \n", sps->gaps_in_frame_num_value_allowed_flag);
printf(" pic_width_in_mbs_minus1 : %d \n", sps->pic_width_in_mbs_minus1);
printf(" pic_height_in_map_units_minus1 : %d \n", sps->pic_height_in_map_units_minus1);
printf(" frame_mbs_only_flag : %d \n", sps->frame_mbs_only_flag);
if (!sps->frame_mbs_only_flag)
{
printf(" mb_adaptive_frame_field_flag : %d \n", sps->mb_adaptive_frame_field_flag);
}
printf(" direct_8x8_inference_flag : %d \n", sps->direct_8x8_inference_flag);
printf(" frame_cropping_flag : %d \n", sps->frame_cropping_flag);
if (sps->frame_cropping_flag)
{
printf(" frame_crop_left_offset : %d \n", sps->frame_crop_left_offset);
printf(" frame_crop_right_offset : %d \n", sps->frame_crop_right_offset);
printf(" frame_crop_top_offset : %d \n", sps->frame_crop_top_offset);
printf(" frame_crop_bottom_offset : %d \n", sps->frame_crop_bottom_offset);
}
printf(" vui_parameters_present_flag : %d \n", sps->vui_parameters_present_flag);
if (sps->vui_parameters_present_flag)
{
printf("=== VUI ===\n");
printf(" aspect_ratio_info_present_flag : %d \n", sps->vui.aspect_ratio_info_present_flag);
if (sps->vui.aspect_ratio_info_present_flag)
{
printf(" aspect_ratio_idc : %d \n", sps->vui.aspect_ratio_idc);
if (sps->vui.aspect_ratio_idc == Extended_SAR)
{
printf(" sar_width : %d \n", sps->vui.sar_width);
printf(" sar_height : %d \n", sps->vui.sar_height);
}
}
printf(" overscan_info_present_flag : %d \n", sps->vui.overscan_info_present_flag);
if (sps->vui.overscan_info_present_flag)
{
printf(" overscan_appropriate_flag : %d \n", sps->vui.overscan_appropriate_flag);
}
printf(" video_signal_type_present_flag : %d \n", sps->vui.video_signal_type_present_flag);
if (sps->vui.video_signal_type_present_flag)
{
printf(" video_format : %d \n", sps->vui.video_format);
printf(" video_full_range_flag : %d \n", sps->vui.video_full_range_flag);
printf(" colour_description_present_flag : %d \n", sps->vui.colour_description_present_flag);
if (sps->vui.colour_description_present_flag)
{
printf(" colour_primaries : %d \n", sps->vui.colour_primaries);
printf(" transfer_characteristics : %d \n", sps->vui.transfer_characteristics);
printf(" matrix_coefficients : %d \n", sps->vui.matrix_coefficients);
}
}
printf(" chroma_loc_info_present_flag : %d \n", sps->vui.chroma_loc_info_present_flag);
if (sps->vui.chroma_loc_info_present_flag)
{
printf(" chroma_sample_loc_type_top_field : %d \n", sps->vui.chroma_sample_loc_type_top_field);
printf(" chroma_sample_loc_type_bottom_field : %d \n", sps->vui.chroma_sample_loc_type_bottom_field);
}
printf(" timing_info_present_flag : %d \n", sps->vui.timing_info_present_flag);
if (sps->vui.timing_info_present_flag)
{
printf(" num_units_in_tick : %d \n", sps->vui.num_units_in_tick);
printf(" time_scale : %d \n", sps->vui.time_scale);
printf(" fixed_frame_rate_flag : %d \n", sps->vui.fixed_frame_rate_flag);
}
printf(" nal_hrd_parameters_present_flag : %d \n", sps->vui.nal_hrd_parameters_present_flag);
printf(" vcl_hrd_parameters_present_flag : %d \n", sps->vui.vcl_hrd_parameters_present_flag);
if (sps->vui.nal_hrd_parameters_present_flag || sps->vui.vcl_hrd_parameters_present_flag)
{
printf(" low_delay_hrd_flag : %d \n", sps->vui.low_delay_hrd_flag);
}
printf(" pic_struct_present_flag : %d \n", sps->vui.pic_struct_present_flag);
printf(" bitstream_restriction_flag : %d \n", sps->vui.bitstream_restriction_flag);
if (sps->vui.bitstream_restriction_flag)
{
printf(" motion_vectors_over_pic_boundaries_flag : %d \n", sps->vui.motion_vectors_over_pic_boundaries_flag);
printf(" max_bytes_per_pic_denom : %d \n", sps->vui.max_bytes_per_pic_denom);
printf(" max_bits_per_mb_denom : %d \n", sps->vui.max_bits_per_mb_denom);
printf(" log2_max_mv_length_horizontal : %d \n", sps->vui.log2_max_mv_length_horizontal);
printf(" log2_max_mv_length_vertical : %d \n", sps->vui.log2_max_mv_length_vertical);
printf(" max_num_reorder_frames : %d \n", sps->vui.max_num_reorder_frames);
printf(" max_dec_frame_buffering : %d \n", sps->vui.max_dec_frame_buffering);
}
if (sps->vui.nal_hrd_parameters_present_flag || sps->vui.vcl_hrd_parameters_present_flag)
{
printf("=== HRD ===\n");
printf(" cpb_cnt_minus1 : %d \n", sps->hrd.cpb_cnt_minus1);
printf(" bit_rate_scale : %d \n", sps->hrd.bit_rate_scale);
printf(" cpb_size_scale : %d \n", sps->hrd.cpb_size_scale);
int SchedSelIdx;
for (SchedSelIdx = 0; SchedSelIdx <= sps->hrd.cpb_cnt_minus1; SchedSelIdx++)
{
printf(" bit_rate_value_minus1[%d] : %d \n", SchedSelIdx, sps->hrd.bit_rate_value_minus1[SchedSelIdx]); // up to cpb_cnt_minus1, which is <= 31
printf(" cpb_size_value_minus1[%d] : %d \n", SchedSelIdx, sps->hrd.cpb_size_value_minus1[SchedSelIdx]);
printf(" cbr_flag[%d] : %d \n", SchedSelIdx, sps->hrd.cbr_flag[SchedSelIdx]);
}
printf(" initial_cpb_removal_delay_length_minus1 : %d \n", sps->hrd.initial_cpb_removal_delay_length_minus1);
printf(" cpb_removal_delay_length_minus1 : %d \n", sps->hrd.cpb_removal_delay_length_minus1);
printf(" dpb_removal_delay_length_minus1 : %d \n", sps->hrd.dpb_removal_delay_length_minus1);
printf(" time_offset_length : %d \n", sps->hrd.time_offset_length);
}
}
}
void debug_pps(h264_stream_t* h)
{
h264_pps_t* pps = h->pps;
printf("======= PPS =======\n");
printf(" pic_parameter_set_id : %d \n", pps->pic_parameter_set_id);
printf(" seq_parameter_set_id : %d \n", pps->seq_parameter_set_id);
printf(" entropy_coding_mode_flag : %d \n", pps->entropy_coding_mode_flag);
printf(" bottom_field_pic_order_in_frame_present_flag : %d \n", pps->bottom_field_pic_order_in_frame_present_flag);
printf(" num_slice_groups_minus1 : %d \n", pps->num_slice_groups_minus1);
if (pps->num_slice_groups_minus1 > 0)
{
printf(" slice_group_map_type : %d \n", pps->slice_group_map_type);
int slice_group_map_type = pps->slice_group_map_type;
if (slice_group_map_type == 0)
{
for (int i = 0; i <= pps->num_slice_groups_minus1; i++)
{
printf(" run_length_minus1[%d] : %d \n", i, pps->run_length_minus1[i]);
}
}
else if (slice_group_map_type == 2)
{
for (int i = 0; i < pps->num_slice_groups_minus1; i++)
{
printf(" top_left[%d] : %d \n", i, pps->top_left[i]);
printf(" bottom_right[%d] : %d \n", i, pps->bottom_right[i]);
}
}
else if (slice_group_map_type == 3 || slice_group_map_type == 4 || slice_group_map_type == 5)
{
printf(" slice_group_change_direction_flag : %d \n", pps->slice_group_change_direction_flag);
printf(" slice_group_change_rate_minus1 : %d \n", pps->slice_group_change_rate_minus1);
}
else if (slice_group_map_type == 6)
{
printf(" pic_size_in_map_units_minus1 : %d \n", pps->pic_size_in_map_units_minus1);
for (int i = 0; i <= pps->pic_size_in_map_units_minus1; i++)
{
printf(" slice_group_id[%d] : %d \n", pps->slice_group_id[i]);
}
}
}
// int run_length_minus1[8]; // up to num_slice_groups_minus1, which is <= 7 in Baseline and Extended, 0 otheriwse
// int top_left[8];
// int bottom_right[8];
// int slice_group_change_direction_flag;
// int slice_group_change_rate_minus1;
// int pic_size_in_map_units_minus1;
// int slice_group_id[256]; // FIXME what size?
printf(" num_ref_idx_l0_default_active_minus1 : %d \n", pps->num_ref_idx_l0_default_active_minus1);
printf(" num_ref_idx_l1_default_active_minus1 : %d \n", pps->num_ref_idx_l1_default_active_minus1);
printf(" weighted_pred_flag : %d \n", pps->weighted_pred_flag);
printf(" weighted_bipred_idc : %d \n", pps->weighted_bipred_idc);
printf(" pic_init_qp_minus26 : %d \n", pps->pic_init_qp_minus26);
printf(" pic_init_qs_minus26 : %d \n", pps->pic_init_qs_minus26);
printf(" chroma_qp_index_offset : %d \n", pps->chroma_qp_index_offset);
printf(" deblocking_filter_control_present_flag : %d \n", pps->deblocking_filter_control_present_flag);
printf(" constrained_intra_pred_flag : %d \n", pps->constrained_intra_pred_flag);
printf(" redundant_pic_cnt_present_flag : %d \n", pps->redundant_pic_cnt_present_flag);
if (pps->more_rbsp_data)
{
printf(" transform_8x8_mode_flag : %d \n", pps->transform_8x8_mode_flag);
printf(" pic_scaling_matrix_present_flag : %d \n", pps->pic_scaling_matrix_present_flag);
// int pic_scaling_list_present_flag[8];
// void* ScalingList4x4[6];
// int UseDefaultScalingMatrix4x4Flag[6];
// void* ScalingList8x8[2];
// int UseDefaultScalingMatrix8x8Flag[2];
printf(" second_chroma_qp_index_offset : %d \n", pps->second_chroma_qp_index_offset);
}
}
void debug_slice_header(h264_stream_t* h)
{
h264_slice_header_t* sh = h->sh;
printf("======= Slice Header =======\n");
printf(" first_mb_in_slice : %d \n", sh->first_mb_in_slice);
const char* slice_type_name;
switch (sh->slice_type)
{
case SH_SLICE_TYPE_P: slice_type_name = "P slice"; break;
case SH_SLICE_TYPE_B: slice_type_name = "B slice"; break;
case SH_SLICE_TYPE_I: slice_type_name = "I slice"; break;
case SH_SLICE_TYPE_SP: slice_type_name = "SP slice"; break;
case SH_SLICE_TYPE_SI: slice_type_name = "SI slice"; break;
case SH_SLICE_TYPE_P_ONLY: slice_type_name = "P slice only"; break;
case SH_SLICE_TYPE_B_ONLY: slice_type_name = "B slice only"; break;
case SH_SLICE_TYPE_I_ONLY: slice_type_name = "I slice only"; break;
case SH_SLICE_TYPE_SP_ONLY: slice_type_name = "SP slice only"; break;
case SH_SLICE_TYPE_SI_ONLY: slice_type_name = "SI slice only"; break;
default: slice_type_name = "Unknown"; break;
}
printf(" slice_type : %d ( %s ) \n", sh->slice_type, slice_type_name);
printf(" pic_parameter_set_id : %d \n", sh->pic_parameter_set_id);
if (h->sps->separate_colour_plane_flag == 1)
{
printf(" colour_plane_id : %d \n", sh->colour_plane_id);
}
printf(" frame_num : %d \n", sh->frame_num);
if (!h->sps->frame_mbs_only_flag)
{
printf(" field_pic_flag : %d \n", sh->field_pic_flag);
if (sh->field_pic_flag)
{
printf(" bottom_field_flag : %d \n", sh->bottom_field_flag);
}
}
if (h->nal->nal_unit_type == 5)
{
printf(" idr_pic_id : %d \n", sh->idr_pic_id);
}
if (h->sps->pic_order_cnt_type == 0)
{
printf(" pic_order_cnt_lsb : %d \n", sh->pic_order_cnt_lsb);
if (h->pps->bottom_field_pic_order_in_frame_present_flag && !sh->field_pic_flag)
{
printf(" delta_pic_order_cnt_bottom : %d \n", sh->delta_pic_order_cnt_bottom);
}
}
if (h->sps->pic_order_cnt_type == 1 && !h->sps->delta_pic_order_always_zero_flag)
{
// int delta_pic_order_cnt[ 2 ];
printf(" delta_pic_order_cnt[0] : %d \n", sh->delta_pic_order_cnt[0]);
if (h->pps->bottom_field_pic_order_in_frame_present_flag && !sh->field_pic_flag)
{
printf(" delta_pic_order_cnt[1] : %d \n", sh->delta_pic_order_cnt[1]);
}
}
if (h->pps->redundant_pic_cnt_present_flag)
{
printf(" redundant_pic_cnt : %d \n", sh->redundant_pic_cnt);
}
if (is_slice_type(h->sh->slice_type, SH_SLICE_TYPE_B))
{
printf(" direct_spatial_mv_pred_flag : %d \n", sh->direct_spatial_mv_pred_flag);
}
if (is_slice_type(h->sh->slice_type, SH_SLICE_TYPE_P) || is_slice_type(h->sh->slice_type, SH_SLICE_TYPE_SP) || is_slice_type(h->sh->slice_type, SH_SLICE_TYPE_B))
{
printf(" num_ref_idx_active_override_flag : %d \n", sh->num_ref_idx_active_override_flag);
if (sh->num_ref_idx_active_override_flag)
{
printf(" num_ref_idx_l0_active_minus1 : %d \n", sh->num_ref_idx_l0_active_minus1);
if (is_slice_type(sh->slice_type, SH_SLICE_TYPE_B))
{
printf(" num_ref_idx_l1_active_minus1 : %d \n", sh->num_ref_idx_l1_active_minus1);
}
}
}
//if (h->nal->nal_unit_type == 20 || h->nal->nal_unit_type == 21)
//{
// // ref_pic_list_mvc_modification; // speficied in Annex H
//}
//else
//{
// // ref_pic_list_modification()
//}
if (h->pps->entropy_coding_mode_flag && !is_slice_type(sh->slice_type, SH_SLICE_TYPE_I) && !is_slice_type(sh->slice_type, SH_SLICE_TYPE_SI))
{
printf(" cabac_init_idc : %d \n", sh->cabac_init_idc);
}
printf(" slice_qp_delta : %d \n", sh->slice_qp_delta);
if (is_slice_type(sh->slice_type, SH_SLICE_TYPE_SP) || is_slice_type(sh->slice_type, SH_SLICE_TYPE_SI))
{
if (is_slice_type(sh->slice_type, SH_SLICE_TYPE_SP))
{
printf(" sp_for_switch_flag : %d \n", sh->sp_for_switch_flag);
}
printf(" slice_qs_delta : %d \n", sh->slice_qs_delta);
}
if (h->pps->deblocking_filter_control_present_flag)
{
printf(" disable_deblocking_filter_idc : %d \n", sh->disable_deblocking_filter_idc);
if (sh->disable_deblocking_filter_idc != 1)
{
printf(" slice_alpha_c0_offset_div2 : %d \n", sh->slice_alpha_c0_offset_div2);
printf(" slice_beta_offset_div2 : %d \n", sh->slice_beta_offset_div2);
}
}
if (h->pps->num_slice_groups_minus1 > 0 && h->pps->slice_group_map_type >= 3 && h->pps->slice_group_map_type <= 5)
{
printf(" slice_group_change_cycle : %d \n", sh->slice_group_change_cycle);
}
// debug rplm info
if (h->nal->nal_unit_type == 20 || h->nal->nal_unit_type == 21)
{
// ref_pic_list_mvc_modification; // speficied in Annex H
}
else
{
// ref_pic_list_modification()
printf("=== Ref Pic List Modification ===\n");
if (!is_slice_type(sh->slice_type, SH_SLICE_TYPE_I) && !is_slice_type(sh->slice_type, SH_SLICE_TYPE_SI))
{
printf(" ref_pic_list_modification_flag_l0 : %d \n", sh->rplm.ref_pic_list_modification_flag_l0);
if (sh->rplm.ref_pic_list_modification_flag_l0)
{
printf(" modification_of_pic_nums_idc : %d \n", sh->rplm.modification_of_pic_nums_idc);
printf(" abs_diff_pic_num_minus1 : %d \n", sh->rplm.abs_diff_pic_num_minus1);
printf(" long_term_pic_num : %d \n", sh->rplm.long_term_pic_num);
}
}
if (is_slice_type(sh->slice_type, SH_SLICE_TYPE_B))
{
printf(" ref_pic_list_modification_flag_l1 : %d \n", sh->rplm.ref_pic_list_modification_flag_l1);
if (sh->rplm.ref_pic_list_modification_flag_l1)
{
printf(" modification_of_pic_nums_idc : %d \n", sh->rplm.modification_of_pic_nums_idc);
printf(" abs_diff_pic_num_minus1 : %d \n", sh->rplm.abs_diff_pic_num_minus1);
printf(" long_term_pic_num : %d \n", sh->rplm.long_term_pic_num);
}
}
}
// debug pwt info
if ((h->pps->weighted_pred_flag && (is_slice_type(sh->slice_type, SH_SLICE_TYPE_P) || is_slice_type(sh->slice_type, SH_SLICE_TYPE_SP))) ||
h->pps->weighted_bipred_idc == 1 && is_slice_type(sh->slice_type, SH_SLICE_TYPE_B))
{
printf("=== Prediction Weight Table ===\n");
printf(" luma_log2_weight_denom : %d \n", sh->pwt.luma_log2_weight_denom);
printf(" chroma_log2_weight_denom : %d \n", sh->pwt.chroma_log2_weight_denom);
for (int i = 0; i <= sh->num_ref_idx_l0_active_minus1; i++)
{
if (sh->pwt.luma_weight_l0_flag[i])
{
printf(" luma_weight_l0_flag[%d] : %d \n", i, sh->pwt.luma_weight_l0_flag[i]);
printf(" luma_weight_l0[%d] : %d \n", i, sh->pwt.luma_weight_l0[i]);
printf(" luma_offset_l0[%d] : %d \n", i, sh->pwt.luma_offset_l0[i]);
}
if (h->sps->ChromaArrayType != 0)
{
printf(" chroma_weight_l0_flag[%d] : %d \n", i, sh->pwt.chroma_weight_l0_flag[i]);
if (sh->pwt.chroma_weight_l0_flag[i])
{
for (int j = 0; j < 2; j++)
{
printf(" chroma_weight_l0[%d][%d] : %d \n", i, j, sh->pwt.chroma_weight_l0[i][j]);
printf(" chroma_offset_l0[%d][%d] : %d \n", i, j, sh->pwt.chroma_offset_l0[i][j]);
}
}
}
}
for (int i = 0; i <= sh->num_ref_idx_l1_active_minus1; i++)
{
if (sh->pwt.luma_weight_l1_flag[i])
{
printf(" luma_weight_l1_flag[%d] : %d \n", i, sh->pwt.luma_weight_l1_flag[i]);
printf(" luma_weight_l1[%d] : %d \n", i, sh->pwt.luma_weight_l1[i]);
printf(" luma_offset_l1[%d] : %d \n", i, sh->pwt.luma_offset_l1[i]);
}
if (h->sps->ChromaArrayType != 0)
{
printf(" chroma_weight_l1_flag[%d] : %d \n", i, sh->pwt.chroma_weight_l1_flag[i]);
if (sh->pwt.chroma_weight_l1_flag[i])
{
for (int j = 0; j < 2; j++)
{
printf(" chroma_weight_l1[%d][%d] : %d \n", i, j, sh->pwt.chroma_weight_l1[i][j]);
printf(" chroma_offset_l1[%d][%d] : %d \n", i, j, sh->pwt.chroma_offset_l1[i][j]);
}
}
}
}
// int luma_weight_l0[64];
// int luma_offset_l0[64];
// printf(" chroma_weight_l0_flag : %d \n", sh->pwt.chroma_weight_l0_flag );
// int chroma_weight_l0[64][2];
// int chroma_offset_l0[64][2];
// int luma_weight_l1[64];
// int luma_offset_l1[64];
// printf(" chroma_weight_l1_flag : %d \n", sh->pwt.chroma_weight_l1_flag );
// int chroma_weight_l1[64][2];
// int chroma_offset_l1[64][2];
}
// debug drpm info
if (h->nal->nal_ref_idc != 0)
{
printf("=== Decoded Ref Pic Marking ===\n");
if (h->nal->nal_unit_type == 5)
{
printf(" no_output_of_prior_pics_flag : %d \n", sh->drpm.no_output_of_prior_pics_flag);
printf(" long_term_reference_flag : %d \n", sh->drpm.long_term_reference_flag);
}
else
{
printf(" adaptive_ref_pic_marking_mode_flag : %d \n", sh->drpm.adaptive_ref_pic_marking_mode_flag);
if (sh->drpm.adaptive_ref_pic_marking_mode_flag)
{
printf(" memory_management_control_operation : %d \n", sh->drpm.memory_management_control_operation);
printf(" difference_of_pic_nums_minus1 : %d \n", sh->drpm.difference_of_pic_nums_minus1);
printf(" long_term_pic_num : %d \n", sh->drpm.long_term_pic_num);
printf(" max_long_term_frame_idx_plus1 : %d \n", sh->drpm.max_long_term_frame_idx_plus1);
}
}
}
}
void debug_aud(h264_stream_t* h)
{
h264_aud_t* aud = h->aud;
printf("======= Access Unit Delimiter =======\n");
const char* primary_pic_type_name;
switch (aud->primary_pic_type)
{
case AUD_PRIMARY_PIC_TYPE_I: primary_pic_type_name = "I"; break;
case AUD_PRIMARY_PIC_TYPE_IP: primary_pic_type_name = "I, P"; break;
case AUD_PRIMARY_PIC_TYPE_IPB: primary_pic_type_name = "I, P, B"; break;
case AUD_PRIMARY_PIC_TYPE_SI: primary_pic_type_name = "SI"; break;
case AUD_PRIMARY_PIC_TYPE_SISP: primary_pic_type_name = "SI, SP"; break;
case AUD_PRIMARY_PIC_TYPE_ISI: primary_pic_type_name = "I, SI"; break;
case AUD_PRIMARY_PIC_TYPE_ISIPSP: primary_pic_type_name = "I, SI, P, SP"; break;
case AUD_PRIMARY_PIC_TYPE_ISIPSPB: primary_pic_type_name = "I, SI, P, SP, B"; break;
default: primary_pic_type_name = "Unknown"; break;
}
printf(" primary_pic_type : %d ( %s ) \n", aud->primary_pic_type, primary_pic_type_name);
}
void debug_seis(h264_stream_t* h)
{
h264_sei_t** seis = h->seis;
int num_seis = h->num_seis;
printf("======= SEI =======\n");
const char* sei_type_name;
int i;
for (i = 0; i < num_seis; i++)
{
h264_sei_t* s = seis[i];
switch (s->payload_type)
{
case SEI_TYPE_BUFFERING_PERIOD: sei_type_name = "Buffering period"; break;
case SEI_TYPE_PIC_TIMING: sei_type_name = "Pic timing"; break;
case SEI_TYPE_PAN_SCAN_RECT: sei_type_name = "Pan scan rect"; break;
case SEI_TYPE_FILLER_PAYLOAD: sei_type_name = "Filler payload"; break;
case SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35: sei_type_name = "User data registered ITU-T T35"; break;
case SEI_TYPE_USER_DATA_UNREGISTERED: sei_type_name = "User data unregistered"; break;
case SEI_TYPE_RECOVERY_POINT: sei_type_name = "Recovery point"; break;
case SEI_TYPE_DEC_REF_PIC_MARKING_REPETITION: sei_type_name = "Dec ref pic marking repetition"; break;
case SEI_TYPE_SPARE_PIC: sei_type_name = "Spare pic"; break;
case SEI_TYPE_SCENE_INFO: sei_type_name = "Scene info"; break;
case SEI_TYPE_SUB_SEQ_INFO: sei_type_name = "Sub seq info"; break;
case SEI_TYPE_SUB_SEQ_LAYER_CHARACTERISTICS: sei_type_name = "Sub seq layer characteristics"; break;
case SEI_TYPE_SUB_SEQ_CHARACTERISTICS: sei_type_name = "Sub seq characteristics"; break;
case SEI_TYPE_FULL_FRAME_FREEZE: sei_type_name = "Full frame freeze"; break;
case SEI_TYPE_FULL_FRAME_FREEZE_RELEASE: sei_type_name = "Full frame freeze release"; break;
case SEI_TYPE_FULL_FRAME_SNAPSHOT: sei_type_name = "Full frame snapshot"; break;
case SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_START: sei_type_name = "Progressive refinement segment start"; break;
case SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_END: sei_type_name = "Progressive refinement segment end"; break;
case SEI_TYPE_MOTION_CONSTRAINED_SLICE_GROUP_SET: sei_type_name = "Motion constrained slice group set"; break;
case SEI_TYPE_FILM_GRAIN_CHARACTERISTICS: sei_type_name = "Film grain characteristics"; break;
case SEI_TYPE_DEBLOCKING_FILTER_DISPLAY_PREFERENCE: sei_type_name = "Deblocking filter display preference"; break;
case SEI_TYPE_STEREO_VIDEO_INFO: sei_type_name = "Stereo video info"; break;
default: sei_type_name = "Unknown"; break;
}
printf("=== %s ===\n", sei_type_name);
printf(" payloadType : %d \n", s->payload_type);
printf(" payloadSize : %d \n", s->payload_size);
printf(" payload : ");
debug_bytes(s->payload, s->payload_size);
}
}
void debug_bytes(uint8_t* buf, int len)
{
int i;
for (i = 0; i < len; i++)
{
printf("%02X ", buf[i]);
if ((i + 1) % 16 == 0) { printf("\n"); }
}
printf("\n");
}
3.2 码流解析(parser.cpp)
码流解析参考了雷博的videoEye代码,改了一下原有代码中的bug,最后的地方需要多读取一个NALU
#pragma pack(1)
#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "streamer.h"
#include "bs.h"
#include "h264_parser.h"
int h264_parser(const char* h264_name)
{
FILE* h264_bitstream = NULL;
h264_nal_t*n;
h264_bitstream = fopen(h264_name, "rb");
if (!h264_bitstream) {
ERROR("failed to open input file");
return -1;
}
n = (h264_nal_t*)calloc(1, sizeof(h264_nal_t));
if (n == NULL) {
ERROR("Alloc NALU Error");
return 0;
}
uint8_t *p = (uint8_t*)calloc(BUFSIZE, sizeof(uint8_t));
if (p == NULL) {
free(n);
ERROR("failed to alloc nalu buffer");
return 0;
}
h264_stream_t* h = h264_new();
int data_offset = 0;
int nal_num = 0;
//printf("-----+-------- NALU Table ------+---------+\n");
//printf(" NUM | POS | IDC | TYPE | LEN |\n");
//printf("-----+---------+--------+-------+---------+\n");
size_t rsz = 0;
size_t sz = 0;
int nal_start, nal_end;
int len = 0;
int debug = 1;
int nal_cnt = 0;
while (1) {
rsz = fread(p + sz, 1, BUFSIZE - sz, h264_bitstream);
if (!rsz) {
ERROR("read stram failed");
break;
}
sz += rsz;
while (find_nal_unit(p, sz, &nal_start, &nal_end) > 0) {
p += nal_start;
read_nal_unit(h, p, nal_end - nal_start);
if (debug)
{
debug_bytes(p - 4, nal_end - nal_start + 4 >= 16 ? 16 : nal_end - nal_start + 4);
debug_nal(h, h->nal);
}
p += (nal_end - nal_start);
sz -= nal_end;
}
// 这里需要多执行一次,对于最后一个NALU而言,确定其大小必须依靠更后一个NALU,但更后一个NALU是不存在的
find_nal_unit(p, sz, &nal_start, &nal_end);
}
}
int parser()
{
const char* h264_name = "output.h264";
h264_parser(h264_name);
return 0;
}
4.程序运行结果
这里和H264/H264Analyzer进行码流的对比一致,不过还需要和一些大的码流分析器进行对比,看看有没有细节的错误,不过后续再改善
==================== NAL ====================
forbidden_zero_bit : 0
nal_ref_idc : 3
nal_unit_type : 7 ( Sequence parameter set )
======= SPS =======
profile_idc : 100
constraint_set0_flag : 0
constraint_set1_flag : 0
constraint_set2_flag : 0
constraint_set3_flag : 0
constraint_set4_flag : 0
constraint_set5_flag : 0
reserved_zero_2bits : 0
level_idc : 31
seq_parameter_set_id : 0
chroma_format_idc : 1
bit_depth_luma_minus8 : 0
bit_depth_chroma_minus8 : 0
qpprime_y_zero_transform_bypass_flag : 0
seq_scaling_matrix_present_flag : 0
log2_max_frame_num_minus4 : 0
pic_order_cnt_type : 2
max_num_ref_frames : 3
gaps_in_frame_num_value_allowed_flag : 0
pic_width_in_mbs_minus1 : 79
pic_height_in_map_units_minus1 : 44
frame_mbs_only_flag : 1
direct_8x8_inference_flag : 1
frame_cropping_flag : 0
vui_parameters_present_flag : 1
=== VUI ===
aspect_ratio_info_present_flag : 0
overscan_info_present_flag : 0
video_signal_type_present_flag : 0
chroma_loc_info_present_flag : 0
timing_info_present_flag : 1
num_units_in_tick : 1
time_scale : 50
fixed_frame_rate_flag : 0
nal_hrd_parameters_present_flag : 0
vcl_hrd_parameters_present_flag : 0
pic_struct_present_flag : 0
bitstream_restriction_flag : 1
motion_vectors_over_pic_boundaries_flag : 1
max_bytes_per_pic_denom : 0
max_bits_per_mb_denom : 0
log2_max_mv_length_horizontal : 11
log2_max_mv_length_vertical : 11
max_num_reorder_frames : 0
max_dec_frame_buffering : 3
==================== NAL ====================
forbidden_zero_bit : 0
nal_ref_idc : 3
nal_unit_type : 8 ( Picture parameter set )
======= PPS =======
pic_parameter_set_id : 0
seq_parameter_set_id : 0
entropy_coding_mode_flag : 1
bottom_field_pic_order_in_frame_present_flag : 0
num_slice_groups_minus1 : 0
num_ref_idx_l0_default_active_minus1 : 2
num_ref_idx_l1_default_active_minus1 : 0
weighted_pred_flag : 1
weighted_bipred_idc : 0
pic_init_qp_minus26 : -3
pic_init_qs_minus26 : 0
chroma_qp_index_offset : -2
deblocking_filter_control_present_flag : 1
constrained_intra_pred_flag : 0
redundant_pic_cnt_present_flag : 0
transform_8x8_mode_flag : 1
pic_scaling_matrix_present_flag : 0
second_chroma_qp_index_offset : -2
==================== NAL ====================
forbidden_zero_bit : 0
nal_ref_idc : 0
nal_unit_type : 6 ( Supplemental enhancement information (SEI) )
======= SEI =======
=== User data unregistered ===
payloadType : 5
payloadSize : 620
payload : // ... 这里数据太多不显示
==================== NAL ====================
forbidden_zero_bit : 0
nal_ref_idc : 3
nal_unit_type : 5 ( Coded slice of an IDR picture )
======= Slice Header =======
first_mb_in_slice : 0
slice_type : 7 ( I slice only )
pic_parameter_set_id : 0
frame_num : 0
idr_pic_id : 0
slice_qp_delta : -4
disable_deblocking_filter_idc : 0
slice_alpha_c0_offset_div2 : 0
slice_beta_offset_div2 : 0
=== Ref Pic List Modification ===
=== Decoded Ref Pic Marking ===
no_output_of_prior_pics_flag : 0
long_term_reference_flag : 0
==================== NAL ====================
forbidden_zero_bit : 0
nal_ref_idc : 2
nal_unit_type : 1 ( Coded slice of a non-IDR picture )
======= Slice Header =======
first_mb_in_slice : 0
slice_type : 5 ( P slice only )
pic_parameter_set_id : 0
frame_num : 1
num_ref_idx_active_override_flag : 1
num_ref_idx_l0_active_minus1 : 0
cabac_init_idc : 0
slice_qp_delta : -1
disable_deblocking_filter_idc : 0
slice_alpha_c0_offset_div2 : 0
slice_beta_offset_div2 : 0
=== Ref Pic List Modification ===
ref_pic_list_modification_flag_l0 : 0
=== Prediction Weight Table ===
luma_log2_weight_denom : 0
chroma_log2_weight_denom : 0
chroma_weight_l0_flag[0] : 0
chroma_weight_l1_flag[0] : 0
=== Decoded Ref Pic Marking ===
adaptive_ref_pic_marking_mode_flag : 0
5.小结
在写这个代码过程中,为了避免中间的语法元素错误,按照2021年8月的H264标准语法元素逐句写的,发现确实有些地方和雷博不太一样,例如separate_colour_plane_flag,rplm的名称等等。另外,还有一些细节的bug例如读取nal没有完全读取到。通过这样的一个工程,能够对码流有一个比较全面的理解。
后续应该可以增加一些可操作性界面,增加h265的分析,还可以增加packet解码,进行图像展示
CSDN : https://blog.csdn.net/weixin_42877471
Github : https://github.com/DoFulangChen