压缩mat为h264_初学H264解析

本文从初学者角度介绍H264编码格式的解析,涵盖基础概念、语法理解和实践操作。通过学习H264的基本术语,如NAL、VCL等,并结合官方文档,理解视频压缩原理。此外,提倡动手实践,通过编写代码解析H264,甚至构思了一个辅助解析工具,帮助观察和理解每个字段的变化。
摘要由CSDN通过智能技术生成

21c5c6f6cf5c4a6d941d50396809603b.png

H264是一种常用的图像编码格式。

视频图像每秒有几十帧,未压缩的情况下,每帧都有好几M,一部1小时的影片更是多达几十上百G。

对视频进行编码的主要目的就是为了压缩视频存储大小。

视频图像在时间、空间上存在冗余,这一冗余是正是各类编码的压缩依据。

下面将从初学者的角度介绍如何去学习一个完全陌生的编码格式的解析。

了解各种概念

当阅读官方文档,或是网络资料的时候,不免被排山倒海的概念淹没。

建议静下心先过一遍一些数学和信号处理的基础知识,比如什么是熵,什么是算术编码,什么是时域频域,什么是数字图像等等;推荐这位作者的H264系列文章:https://www.jianshu.com/p/9c4f51d4c3ff

然后过一遍H264中的常见术语,如NAL,VCL,Slice,Macroblock等,IDR/I/P/B,图像/场/帧等;推荐这篇文章:

https://blog.csdn.net/andywang201001/article/details/80274886

理解语法

H264官方文档(我看的是《T-REC-H.264-200503》)以表格形式定义了解析语法(在7.1节介绍了它的语法含义)

不过直接看英文的官方文档可能会有点受挫,建议先找网上写的比较全的格式解析看看,比如这篇:http://mamicode.com/info-detail-1215990.html

因为网络资料的作者一般有自己的专注领域,写的文章会有侧重,此时,就需要我们结合自己的工作内容看官方文档深入研究了。

动手实践

虽然我们有很多成熟的开源项目支持H264的解析,如官方的JM,如VLC的x264,但是如果要观察和理解每个字段的含义、变化规律,从最细粒度剖析一个视频文件,还是能自己Coding解析的比较好(何况造轮子是程序员的快乐呢!)

在自己动手”造轮子“的过程中,我突然有一个想法,是不是可以做这样一个工具:

  • 对于任意的(编码)格式,可以按官方文档的语法写几乎逐行对应的代码,完成一个解析插件
  • 这个工具可以同时展示二进制的数据、插件解析的结果、各个字段对应的解释文档
  • 还能对于多次解析的结果建立透视图,查看多次解析过程某个字段是如何变化的

于是我造了一个用于造轮子的工具:

bitinsight​github.com

比如:

我们学到H264的nal_unit中有一个nal_unit_type表示的当前nalu的类型,它的值指示了这个unit中的数据是什么。

a0f15ba238ee0662685867365d5b6d83.png
nal_unit语法表

1059284ce1805ecb75b85f795977cc1b.png
部分nal_unit_type

有了工具,就可以根据表格逐行翻译:

def nal_unit(d:Table, bs:BitStream):
    d.add_field('forbidden_zero_bit', bs.read_bits,
                count=1)
    d.add_field('nal_ref_idc', bs.read_bits, count=2)
    d.add_field('nal_unit_type', bs.read_bits, count=5)

把文档中关于nal_unit_type的解释摘抄下来,运行工具:

086767584d3071b1314e73db79c7bad2.png

就可以很有成就感地完成了nal_unit_type的解析了,而且还可以实时对照文档查看含义。(图中告诉我们0到6byte的范围是一个nal_unit_type为9的nalu,也就是一个Access unit delimeter

当然,如果只是造造轮子,还不尽兴。

在学习slice_header中的frame_num的时候有没有被绕晕?那不妨实际拿个文件,看看nal_unit_typeslice_typeframe_num的关系吧:

我们再写个slice_header的解析代码:

def slice_header(d:Table, bs:BitStream):
    d.add_field('first_mb_in_slice', bs.read_ue_golomb)
    d.add_field('slice_type', bs.read_ue_golomb)
    d.add_field('pic_parameter_set_id', bs.read_ue_golomb)
    pps = __find_pps_in_ctx(d.pic_parameter_set_id, d.context)
    if pps == None:
        raise Exception('no pps in ctx, abort!')
    sps = __find_sps_in_ctx(pps.seq_parameter_set_id, d.context)
    if pps == None:
        raise Exception('no sps in ctx, abort!')
    if sps.get_value('separate_colour_plane_flag', 0):
        d.add_field('colour_plane_id', bs.read_bits, count=2)
    try:
        nal_unit = d.context.nal_unit
    except AttributeError as e:
        raise Exception('no nal_unit in ctx, abort!')

    d.add_field('frame_num', bs.read_bits, count = (sps.log2_max_frame_num_minus4 + 4))
比之前的 nal_unit略显复杂,因为我们需要引用 sps/pps/nal_unit的header.

接下来就可以借助工具对多个nalu创建一个透视图了:

e7e5fd80b8c3e18edbacc01a5302bf0b.png

是不是一目了然了?

不知不觉从方法论写出了工具安利,收尾~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值