/Teh264

simple decoder for h.264, for the purpose of learning.

Primary LanguageC++GNU General Public License v3.0GPL-3.0

Te-h264

非个人学习和研究用途请遵守 LICENSE,
个人研究和学习无使用限制要求,
不对因为使用本项目代码中的全部或者部分所带来的问题负责。

前言

本项目基于之前自己写过的一个项目重写而来,
其中 cabac.cpp 有小部分代码参考了 JM19.0 中的部分代码
所有代码都是由自己手写完成,只是因为之前的代码逻辑比较混乱所以进行了重写

适用

YUV: 4:2:0 并且不包含luma之外的分量的解码
CABAC编码

缺陷

  • 解码速度还是好慢,,,,好慢
  • Direct 的时间空间预测全部简化为了空间预测随周围宏块一起运动(时域还是没有写,,)
  • 还只能输出亮度,色度还没有写解码方式(但是读取了解码元素)
  • 没做去方块滤波,没有做裁剪
  • PCM 只读没解

改进

  • 加入了一些注释,对于重写的一些具有原理代表性的函数进行了保留用于自己参考
  • 一些逻辑的分开和提取,
  • 解码对象都采用了 decode() 函数作为下一层对象的入口
  • 解码函数都大概都分成了 head 和 data 两个主要函数

错误处理

删除了之前使用的 Debug 对象,改用全局的 terror 对象(terr)来控制 debug 信息的输出
这个对象需要同时包含 terror.h 和 gvars.h
terr会读取配置文件 conf.lua 如果没有 lua 可能需要改动
配置文件主要是针对是否打印某些变量来设计的,都进行了注释

数据区

Bitsbuf 移到了 reader 对象中,缓冲区的比特指针改成了 字节指针 加 比特偏移的形式
目前主要是针对于 FILE* 来写的,

帧内预测

采用面向过程的形式把帧内预测模式提取分开写在了不同的cpp文件中
其中帧内4x4预测方式进行大量的重写,已经不具备可读性,因为改成了基于16个单独像素的统计
帧内 8x8 还没有开始写
帧内16x16 也改正了之前一些错误理解的地方

帧间预测

只能说有部分地方变量写的好看吧。表达形式上比较直观。

主要对象

parser

用来和做读取算子的统一表达
ae 算子单独交给 parser 中的 cabac
其他普通算子交给 reader
ce 算子还没写

同时还包含 SPS 和 PPS 的参数
更新时还会计算一些参数

解析对象,把之前位于 deocder 的两个矩阵移动到这里了

cabac

句法元素统一采用 uint32 (unsigned int),部分句法元素有额外的参数值时采用位运算
统一使用16进制数,
0x000FF000 FF所在的位置是句法元素的种类值(按16进制读),
比如 mb_type (21号) 就使用 0x00021000U 作为参数

上下文状态改成了int** 的形式,也不使用array2d了

一元二值化和UEGK二值化写成了固定的函数并且其ctx的变化值固定了
截断二值化直接按照逻辑写在代码中了,不再单独开函数判断
关于mb_type
重新修改了枚举类型的排布,使得可以直接使用枚举类型来访问表
块大小和部分信息之前写的索引很混乱,这部分重写在了predchart.h 中了

reader

用来读取文件中的数据,
去除”防止竞争位“
完成bit位的读取

matrix

按照 opencv 中 Mat 的**重写了数据指针的使用方式
并且不再采用 array2d 作为基类(因为不会用,,,),而是直接写 int 类型作为运算
重载的[]操作符号中用行号,得到这一行的起始指针,再来一次就是真实的数据

pixmap

重建图像值每一个像素只有一个字节
预测和残差都是采用matrix
所以最后写入的时候只能一个个像素强制转换后写入
没有自己的像素数据空间,只有指针指向picture中的重建图像值
所以操作的数据也是 一个字节
[]方法和matrix保持一致

解码对象

每个解码对象都包含一个 decode 函数
从这个函数一层一层往下就是整个解码的流程

nal

完成 nal 层的三个句法元素的读取之后就不再使用了
三个句法元素分别会在解码时就传给 picture 从而完成对应操作
数据也不是由 nal 管理

picture

管理 slice 和 宏块
管理像素数据 (只有重建图像像素值)
图像的参考属性的管理

slice

用来管理slice内的宏块
同时完成slice的header和data的处理
完成macroblock的解码

macroblock

帧内、帧间、Skip、PCM、Direct 都分开了,
处理这些的主函数是 DecodeData();

residual

残差的处理对象, 采用block来存储相应的数据
需要注意的是 luma[0] 才是 AC
(这是为了在cabac中解码索引和luma4x4保持一致)