目录

ANNEXB vs. AVCC

VCL vs. NAL

signal函数响应键盘事件

收尾清理工作

x264的编码


ANNEXB vs. AVCC

今天我们继续来说一下x264结构中非常重要的属性annexb。

小贴士:编写经典的c库的大牛们都是会按照规范来进行编码,无论是ffmpeg(后面打算再开一个专栏)还是x264,他们结构体的成员还是变量,命名方式习惯采用“数据类型_变量名”,比如b_annexb,代表boolean型,即annexb是一个开关(flag)。

annexb如果为true,代表一个NALU的开始3位或者4位是start_code,所谓start_code没有逻辑意义,只是代表一个NALU的起始,或者说作为两个NALU的分隔符。那么就是是4位还是3位是由x264_nal_t的结构体成员b_long_startcode决定的:

typedef struct x264_nal_t
{... ...int b_long_startcode;... ...
} x264_nal_t;

那么代码中优势如何来进行3位和4位的处理呢?参看x264_nal_encode函数里面的实现:

void x264_nal_encode( x264_t *h, uint8_t *dst, x264_nal_t *nal )
{... ...if( h->param.b_annexb ){if( nal->b_long_startcode )*dst++ = 0x00;*dst++ = 0x00;*dst++ = 0x00;*dst++ = 0x01;}else /* save room for size later */dst += 4;... ...
}

这个b_long_startcode默认就是1,即采用4位的startcode,在源码中唯一设置地方就是在nal_start:

static void nal_start( x264_t *h, int i_type, int i_ref_idc )
{x264_nal_t *nal = &h->out.nal[h->out.i_nal];nal->i_ref_idc        = i_ref_idc;nal->i_type           = i_type;nal->b_long_startcode = 1;nal->i_payload= 0;nal->p_payload= &h->out.p_bitstream[bs_pos( &h->out.bs ) / 8];nal->i_padding= 0;
}

讲这个地方就是告诉大家在阅读源码的时候要关注核心成员的初始值,以及如果对他们进行修改,对于你们理解他们的行为是有帮助的。

如果annexb为false,即编码规则是AVCC,那么每个NALU将会用前面的4位来表示自己长度。这个处理可以在上面的x264_nal_encode函数中的else分支看到.

这里4位是byte,每个byte8位,4byte就是32位,代表能够表达的最大长度就是2^32,这里是我经常喜欢面试的时候喜欢和大家讨论的问题,但是我觉得很多人,即使是有几年C开发经验的大叔,对于byte以及取值范围无感,这个基础知识还是要敲敲黑板的。

什么是annexb?annex是附录的意思,annex b就是附录,这种startcode的语法描述就是在ITU-U发布的H264标准文档中的附录B中,进行语法描述的故得名:

很多童鞋在看到h264的语法的时候,会有困惑,这个是什么鬼?为什么一个协议还有语法,是的,开始我也不太敢相信:h264其实已经把伪代码给你写好了,为了避免描述性语言会有歧义,ITU-T那些老家伙们(是不是也有年轻小伙)直接把类C代码已经写好了,尽量介绍描述性语言的不确定性导致的歧义;所以h264具有高昂的收费也不是没有道理的,人家的工作的确实细致的很(有时间大家可以把这个文档通读一下,了解高额收费的文档都是怎么写的)。

VCL vs. NAL

上面一大堆讲完了,作为技术敏感的童鞋应该发现还有一个非常重要的概念,就是NALU。H264将整个编码解码过程抽象为了2层,大牛喜欢做的一类事情就是分层,我印象中最开始接触的分层就是TCP/IP协议(分层)以及ISO网络参考模型,为什么要分层?就是要聚焦和定位,每一个层都只是做一类事情,这样做架构设计非常清晰;而且优化以及定位问题也十分方便。

如上图所示,在H264里面将数据抽象为两层,其中VCL主要做的事情就是关注于编码/解码,NAL层则主要负责网络传输;什么编码/解码?比如对于帧的优化处理,例如宏块分割,分片等等就是在VCL中负责的,NAL层负责的则是如何将VCL层编码而成的packet进行封装序列化,保存到到本地,或者通过网络传输走,比如码率(每秒传输多少bits的数据)的控制就是NAL层要关心的事情。

NALU(NAL Unit)就是NAL层将VCL层输出的packet(帧frame编码/封装的形式)单元,一个帧可以是被打包到一个或者多个NALU,所以NALU是网络抽象层,视频传输数据的单元。关于一个Packet和一个NALU,他们之间其实加载了很多层(对象间)关系,从图像,片组,片...一直到像素,这个关系,我们后面将会介绍,本小节大家了解H264的VCL和NAL分层,以及NALU是网络抽象层传输的最小单元即可。另外,为什么叫网络抽象层?因为网络传输其实操作系统底层做的事情,操作系统层做的socket,socket调用的是网卡驱动,网卡驱动调用了网卡,那么在网卡之上的各层都称之为抽象层,因为并没有真是的处理网卡,对于NALU也是如此,它并不是物理层传输的数据包,只是定义了数据报的结构(成员),并为这些结构成员赋值而已。

到此main函数里面的x264_param_default函数就先介绍到这里,这里面重要的成员隐含的内容,我们在后面的源码解读中将会继续介绍。

signal函数响应键盘事件

main函数做的第二件事情就是signal( SIGINT, sigint_handler );signal是系统调用函数,用于响应信号,第一个参数SIGINT是一个终止信号,INT是理解为Interrupt比较直接一些,不过从Linux系统的注释来看INT是“INTeractive attention signal”,交互注意信号,简单讲就响应ctrl+c的键盘事件,第二个参数是sigint_handler,这个函数非常简单:

/* Ctrl-C handler */
static volatile int b_ctrl_c = 0;
static void sigint_handler( int a )
{b_ctrl_c = 1;
}

就是设置一个标志位,这个标志位将会在编码时候被处理:

static int encode( x264_param_t *param, cli_opt_t *opt )
{x264_t *h = NULL;x264_picture_t pic;... ...for( ; !b_ctrl_c && (i_frame < param->i_frame_total || !param->i_frame_total); i_frame++ ){... ...}
}

在循环对每一帧进行编码的时候,都会判断这个flag,所以当你编码过程中摁下了ctrl+c并不会马上出发,而是当一帧被编码完成后才会被处理。

所以不要想当然的觉得ctrl+c就是终止程序,系统大大都给我们做好的,不然,这些都是人为在程序中进行处理的,终止的时机,甚至是否被处理都是在程序中控制的。

收尾清理工作

第四件事情就是做了一些清理工作,包括释放一些文件的句柄以及x264参数的内存释放工作;

/* clean up handles */
if( filter.free )filter.free( opt.hin );
else if( opt.hin )cli_input.close_file( opt.hin );
if( opt.hout )cli_output.close_file( opt.hout, 0, 0 );
if( opt.tcfile_out )fclose( opt.tcfile_out );
if( opt.qpfile )fclose( opt.qpfile );
x264_param_cleanup( &param );

其中x264_param_cleanup里面展示了教科书级别的c语言缓冲区数据释放:

REALIGN_STACK void x264_param_cleanup( x264_param_t *param )
{strdup_buffer *buf = param->opaque;if( buf ){for( int i = 0; i < buf->count; i++ )free( buf->ptr[i] );free( buf );param->opaque = NULL;}
}

c语言的内存泄漏很多时候就是因为只是释放了指针(第9行),而没有释放指针的内容,因为c语言是不提供类似于Java/ Python的垃圾回收机制,不会针对变量(内存区域)的引用数量自动实现释放,所以对于通过指针来管理的内存连续区域需要手工进行逐个释放;熟记这段代码,c的内存管理的段位就上升一级咯。

x264的编码

main函数最核心的还是做得第三件事情,就是encode,上一篇文章也提到了,x264库只是实现了encode(decode部分可以使用ffmpeg自带的h264的解码器来做);那么从下节开始,我们一起解读x264的华彩乐章:编码。

参考:

https://www.itu.int/rec/T-REC-H.264-201704-S

https://www.jianshu.com/p/eeecb0eb2c6e

PowerPoint Presentation (hhi.de)

《新一代视频压缩编码标准(第二版)》 毕节厚 王建主编

x264源码解读(二)- VCL和NAL那些事相关推荐

  1. redis源码解读二

    上一篇解读了一下SDS,本来觉得完了,但之后想想感觉少点什么,现在我们从使用的角度去梳理一下,大家想想对于字符串, 我们经常使用的有哪几个方法呢?这些方法又是怎么实现的? 在研究上面的几个方法之前我们 ...

  2. 【mmdetection源码解读(二)】RPN网络

    以下仅为个人理解,若有不正之处还请指出,欢迎交流! 在two-stage目标检测方法中,通过骨干网络获得的特征图需要送进RPN网络产生区域建议候选框,下面就结合mmdetection中的源码详细解释这 ...

  3. ECharts 源码解读 二

    2021SC@SDUSC 源码结构和打包 源码使用webpack打包,查看文件webpack.config.js可知,将echarts源码编译成三个版本,分别为常用版本,精简版本,完整版本,分别对应w ...

  4. SEDA源码解读(二)

    接着上一篇的话题,本篇继续探讨SEDA的实践项目--sandstorm. 首先,看看package里面的类文件: ResponseTimeControllerIF:该接口代表一个响应时间的控制器,通常 ...

  5. 启动eureka(源码解读二)

    从github上把eureka的源码下载下来,按照readme进行build. eureka是依赖tomcat的,server启动之后的界面是这样的: 这和我们用springboot集成eureka看 ...

  6. JStorm/Storm源码解读(二)--启动篇

    为了在解读分析时有个统一的思路,本文将从启动一个集群开始分析. (说明:为了测试方便,采用的是本地模式). 1.参数设置 Config conf = new Config(); //设置Topolog ...

  7. PyTorch faster_rcnn之一源码解读二 model_util

    整个model/util文件夹下主要将了三个Creator函数: AnchorTargetCreator() : 用每张图中bbox的真实标签为所有任务分配ground truth![RPN网络] P ...

  8. jQuery源码解读二(apply和call)

    一.apply方法和call方法的用法: apply方法: 语法:apply(thisObj,[,argArray]) 定义:应用某一对象的一个方法,用另一个对象替换当前对象. 说明:如果argArr ...

  9. 「 Flutter 项目实战 」设计企业级项目入口 main.dart 设计与实现 ( GSYGithubApp 源码解读·二 )

最新文章

  1. 52 个深度学习目标检测模型汇总,论文、源码一应俱全
  2. “TNS-03505:无法解析名称”问题解决一例
  3. Numpy.array矩阵百分制化(比例化)
  4. D1net阅闻:IBM宣布推出全新存储技术 存储速度快70倍
  5. 【转载】IIS网站配置不带www域名直接跳转带www的域名
  6. 微信的充值页面为啥长这样?(多图)
  7. 西南交通大学计算机程序设计实验13,西南交通大学C++实验报告.doc
  8. zynq中mgtx应用_基于ZYNQ的UCOS移植(TCP通讯)
  9. php让代码重新运行一次,脚本运行时是否可以动态重新加载PHP代码?
  10. jenkins-系统管理-节点管理进去报错
  11. 深度 | 伯克利教授Stuart Russell:人工智能基础概念与34个误区
  12. mysql sql语句 datediff_SQL语句中DateDiff函数说明
  13. 测试开发之缺陷报告下篇
  14. Shell 的概述,操作命令
  15. C# StringBuilder 和 String 的区别?(简单易懂不抽象)
  16. js版算24点小游戏
  17. 循环冗余校验码(CRC)详解
  18. 推荐我看过的几本好书给大家
  19. linux内网穿透(内外网服务器端口映射)
  20. 内存超频trfc_P55平台内存超频实战

热门文章

  1. 剑指offer----C语言版----第十四天
  2. 基于本人多年工作经验谈谈对于BIM的理解
  3. Android Support Annotation介绍
  4. 怎么让照片变年轻_PS照片易容术:让年轻人像照片变老的方法
  5. 概率论 3-1 二维随机向量及其联合分布
  6. Dr.COM防BT下载技术的原理和实现的方式
  7. 财付通基础支付平台高可用保障体系演进之路
  8. Android如何在onCreate()方法中获取控件的高度和宽度
  9. 天钰原装正品FR9608供应,同步降压DC/DC提供4.5V至28V宽输入的转换器
  10. 联合国健康产业基金会与亚非欧多国达成合作意向,共同在健康领域开展多边合作