【开源项目】H264码流格式解析

目录

  • 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=2leadingZeroBits1+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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/790063.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

这类工作要被大语言模型取代?

春节前&#xff0c;哈佛的一篇文章带来了思考。 美国哈佛大学网站上有一篇文章探讨了ChatGPT对写作工作的代替可能性。 文章内容是围绕哈佛大学的一位校聘作家和一位有名望的心理学教授之间的对话展开&#xff0c;主要讲了一个基本的观点&#xff1a; 类似ChatGPT这样的人工…

解决Invalid or unsupported by client SCRAM mechanisms(dbeaver)

在用工具&#xff08;dbeaver&#xff09;链接Opengauss数据库的时候&#xff0c;报出标题的错误。原因为驱动不正确。 驱动下载地址&#xff1a;https://opengauss.org/zh/download/ 下载完的包 &#xff0c;解压后&#xff0c;里面应该有两个jar 包,使用postgresql.jar dbe…

HTTP 概况

Web的应用层协议是超文本传输协议(HyperTextTransferProtocol&#xff0c;HTTP)&#xff0c;它是 Web的核心。HTTP由两个程序实现:一个客户程序和一个服务器程序。客户程序和服务器程序运行在不同的端系统中&#xff0c;通过交换HTTP报文进行会话。HTTP定义了这些报文的结构以及…

C++入门基础篇(下)

目录 6.引用 6.1 引用的特性 6.2 const引用 7.指针和引用的关系 8.内联函数 9.nullptr 6.引用 引⽤不是新定义⼀个变量&#xff0c;⽽是给已存在变量取了⼀个别名&#xff0c;编译器不会为引⽤变量开辟内存空间&#xff0c; 它和它引⽤的变量共⽤同⼀块内存空间。比如&a…

深入了解代理IP常见协议:区别与选择

代理服务器在网络使用中扮演着重要的角色&#xff0c;是您设备和互联网之间的中间层。它不仅可以增强网络访问的安全性和隐私保护&#xff0c;还可以提供许多灵活的应用。使用代理时&#xff0c;不同的协议类型对数据交换具有不同的规则和特征。常见的代理协议包括HTTP代理、HT…

高并发内存池联调问题

断言报错1 在这里插入图片描述 排查原因 实际 actualNum 值并没有大于一 断点报错。 经过排查&#xff0c;此处assert&#xff08;actualNum > 0) 判断条件应该是大于零&#xff0c;否则或许if判断条件无法执行。 跑通结果: 2

Java语言程序设计——篇三(1)

选择结构 概述选择单分支if语句例题讲解 双分支if-else语句例题讲解 条件运算符多分支的if-else语句例题讲解 嵌套的if语句例题讲解 switch语句结构例题讲解代码演示运行结果 概述 Java中的控制结构&#xff0c;包括&#xff1a; 1、选择结构( if、if-else、switch ) 2、循环结…

空间自回归模型及 Stata 具体操作步骤

目录 一、理论原理 二、数据准备 三、程序代码及解释 四、代码运行结果 一、理论原理 空间自回归模型&#xff08;Spatial Autoregressive Model&#xff0c;SAR&#xff09;是一种用于分析具有空间相关性的数据的统计模型。它假设观测值之间的相关性不仅取决于传统的时间或…

深入理解 go map

什么是 map 维基百科里这样定义 map: In computer science, an associative array, map, symbol table, or dictionary is an abstract data type composed of a collection of (key, value) pairs, such that each possible key appears at most once in the collection. 简单…

镭速Raysync vs MASV:哪个才最合适企业大文件传输

在当前信息爆炸的时代&#xff0c;企业面临的一个关键挑战是如何高效、安全地传输日益增长的大量文件。选择正确的文件传输工具对于企业的日常运作至关重要。本文旨在对比分析两款备受瞩目的企业级大文件传输解决方案——镭速Raysync和MASV&#xff0c;以助企业决策者挑选出最适…

【代码随想录】【算法训练营】【第64天】 [卡码117]软件构建 [卡码47]参加科学大会

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 64&#xff0c;周三&#xff0c;继续ding~ 题目详情 [卡码117] 软件构建 题目描述 卡码117 软件构建 解题思路 前提&#xff1a; 思路&#xff1a; 重点&#xff1a; 代码实现 C语言 [卡码…

python的集合

定义 集合&#xff08;是一个无序的、不包含重复元素的集合。集合对象支持数学上的标准集合操作&#xff0c;如并集、交集、差集等。&#xff09; 创建集合 添加元素 删除元素 遍历 其他 union() 或 |&#xff1a;返回两个集合的 并集intersection() 或 &&#xff1a;返回…

CLion学习笔记-cmake编译和多main函数编译

这里就不讲怎么配置clion了 项目名字 pcl_kdtree_search 1.新建一个工程名字自己取&#xff0c;我这里用自己学习pcl的&#xff0c;加一个main函数&#xff0c;这个时候Cmake里边就是这样的。 #声明要求的cmake最低版本 cmake_minimum_required(VERSION 3.19) #声明一个工程…

leetcode:1332. 删除回文子序列(python3解法)

难度&#xff1a;简单 给你一个字符串 s&#xff0c;它仅由字母 a 和 b 组成。每一次删除操作都可以从 s 中删除一个回文 子序列。 返回删除给定字符串中所有字符&#xff08;字符串为空&#xff09;的最小删除次数。 「子序列」定义&#xff1a;如果一个字符串可以通过删除原字…

力扣 160相聚链表

注意 判断是否有交点 用while(A! B) 其中A A nullptr? headb:A->next;B同理 注意&#xff0c;while循环的退出条件是AB指针指向同一个&#xff0c;如果没有相交&#xff0c;仍然可以退出 当AB都为NULLPTR时退出

怎么样的主食冻干算好冻干?避坑私藏宝藏主食冻干分享

主食冻干市场现状复杂多变&#xff0c;产品品质差异显著。部分品牌过分追求营养指标的堆砌与利润的最大化&#xff0c;却忽略了猫咪健康饮食的根本所在&#xff0c;导致市场上充斥着以次充好、虚假标注等不法行为。更有甚者&#xff0c;一些产品未经严格第三方检测便流入市场&a…

亚马逊erp跟卖采集之关键词采集

大家好&#xff0c;今天讲这款erp的跟卖采集关键词采集。 打开erp跟卖功能采集任务&#xff0c;点新增任务站点美国&#xff0c;有5种采集方式&#xff1a;关键词、店铺链接、类目ASIN。 选择关键词采集&#xff0c;这里我选择女童装&#xff0c;选择女童板鞋复制粘贴。页数我…

[Spring] SpringBoot基本配置与快速上手

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

第四门课 第一周 卷积神经网络

第四门课 第一周 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09; 文章目录 第四门课 第一周 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;1.1 计算机视觉遇到的问题&#xff08;Computer vision&#xff09;1.2 卷积运算示例&…

[C++] 模拟实现list(一)

标题&#xff1a;[C] 模拟实现list 水墨不写bug 目录 一、list简介 二、铺垫 I&#xff0c;分离编译模式的选择 II&#xff0c;数据结构分析 &#xff08;1&#xff09;命名空间 &#xff08;2&#xff09;类的设计分析 正文开始&#xff1a; 一、list简介 C STL&#xf…