x264代码剖析(二):如何编译运行x264以及x264代码基本框架

简介: <h1 style="text-align:center">x264<span style="font-family:宋体">代码剖析(二):如何编译运行</span><span style="font-family:Calibri">x264</span><span style="font-family:宋体">以及</span><span style="font-family:Cali

x264代码剖析(二):如何编译运行x264以及x264代码基本框架

 

        x264工程在x265出现之前一直在更新,但是自x264-20091007(含)不再支持VC++平台,也就是说支持VC++平台的x264的最新版本是x264-20091006。接下来就以该版本为例简单介绍如何编译运行x264以及x264代码的基本框架。

 

        首先下载x264-20091006,地址为:http://ftp.videolan.org/pub/videolan/x264/snapshots/,找到对应版本下载后直接解压缩就可以了。

 

 

1、编译运行x264

 

        平台:win7 32bitVS2010

        直接用VS打开“...\x264-091006\build\win32”文件夹下面的对应工程项目即可,由于VS版本与x264版本不一样,所以会自动兼容,全部采用默认兼容即可。编译过程中可能会出现一系列的问题,关于这部分将在后续进行分析解决。


    已经解决:见网址:http://blog.csdn.net/frd2009041510/article/details/50822847

 

2x264代码基本框架(从源码结构方面)

 

        下面以x264-160228版本为例从源码结构方面分析其代码基本框架。

        X264源码文件夹内包括的内容如下图所示:



2.1encoder

 

        Encoder文件夹内主要包括的内容如下图:


2.2filters


        filters文件夹内主要包括的内容如下图:

        其中,video中包括:


2.3input


        input文件夹内主要包括的内容如下图:


2.4output


        output文件夹内主要包括的内容如下图:


2.5extras


       extras文件夹内主要包括的内容如下图:



2.6doc


        doc文件夹内主要包括的内容如下图:


2.7toolscommon


        toolscommon文件夹内包含内容较多,common文件夹内包含了一些共用的函数,如下图所示:


        而tools则包含了常用的工具(个人尚未使用过),如下图所示:


3x264代码基本框架(从编码结构方面)

 

        下面以x264-160228版本为例从编码结构方面分析其代码基本框架。

 

        主要参考:http://blog.csdn.net/leixiaohua1020/article/details/45536607

 

3.1x264命令行程序

 

        x264命令行程序指的是x264项目提供的控制台程序,通过这个程序可以调用libx264编码YUVH.264码流。x264命令行工具的源码调用关系图如下图所示:



        这个流程中最关键的API包括:

x264_param_default():设置参数集结构体x264_param_t的缺省值。

x264_encoder_open():打开编码器。

x264_encoder_headers():输出SPSPPSSEI等信息。

x264_encoder_encode():编码输出一帧图像。

x264_encoder_close():关闭编码器。

 

        具体分析如下:

该程序的入口函数为main()main()函数首先调用parse()解析输入的参数,然后调用encode()编码YUV数据。parse()首先调用x264_param_default()为保存参数的x264_param_t结构体赋默认值;然后在一个大循环中通过getopt_long()解析通过命令行传递来的存储在argv[]中的参数,并作相应的设置工作;最后调用select_input()select_output()完成输入文件格式(yuvy4m等)和输出文件格式(裸流,mp4mkvFLV等)的设置。encode()首先调用x264_encoder_open()打开编码器;接着在一个循环中反复调用encode_frame()一帧一帧地进行编码;最后在编码完成后调用x264_encoder_close()关闭编码器。encode_frame()则调用x264_encoder_encode()将存储YUV数据的x264_picture_t编码为存储H.264数据的x264_nal_t


3.2libx264类库的接口

 

         在一个x264编码流程中,至少需要调用如下API函数:

x264_param_default():设置参数集结构体x264_param_t的缺省值。

x264_picture_alloc():为图像结构体x264_picture_t分配内存。

x264_encoder_open():打开编码器。

x264_encoder_encode():编码一帧图像。

x264_encoder_close():关闭编码器。

x264_picture_clean():释放x264_picture_alloc()申请的资源。

 

3.3libx264主干函数

 

        libx264主干函数指的是编码API之后,x264_slice_write()之前的函数,如下图所示。


        从图中可以看出,x264主干部分最复杂的函数就是x264_encoder_encode(),该函数完成了编码一帧YUVH.264码流的工作。与之配合的还有打开编码器的函数x264_encoder_open(),关闭编码器的函数x264_encoder_close(),以及输出SPS/PPS/SEI这样的头信息的x264_encoder_headers()。这一部分函数较多,暂时不详细分析,仅仅举几个例子列一下它们的功能。


3.3.1x264_encoder_open()

        x264_encoder_open()用于打开编码器,其中初始化了libx264编码所需要的各种变量。它调用了下面的函数:

x264_validate_parameters():检查输入参数(例如输入图像的宽高是否为正数)。

x264_predict_16x16_init():初始化Intra16x16帧内预测汇编函数。

x264_predict_4x4_init():初始化Intra4x4帧内预测汇编函数。

x264_pixel_init():初始化像素值计算相关的汇编函数(包括SADSATDSSD等)。

x264_dct_init():初始化DCT变换和DCT反变换相关的汇编函数。

x264_mc_init():初始化运动补偿相关的汇编函数。

x264_quant_init():初始化量化和反量化相关的汇编函数。

x264_deblock_init():初始化去块效应滤波器相关的汇编函数。

x264_lookahead_init():初始化Lookahead相关的变量。

x264_ratecontrol_new():初始化码率控制模块。


3.3.2x264_encoder_headers()

 

x264_encoder_headers()输出SPS/PPS/SEI这些H.264码流的头信息。它调用了下面的函数:

x264_sps_write():输出SPS

x264_pps_write():输出PPS

x264_sei_version_write():输出SEI

如下图所示:


3.3.3x264_encoder_encode()

 

        x264_encoder_encode()编码一帧YUVH.264码流。它调用了下面的函数:

x264_frame_pop_unused():获取1x264_frame_t类型结构体fenc。如果frames.unused[]队列不为空,就调用x264_frame_pop()unused[]队列取1个现成的;否则就调用x264_frame_new()创建一个新的。

x264_frame_copy_picture():将输入的图像数据拷贝至fenc

x264_lookahead_put_frame():将fenc放入lookahead.next.list[]队列,等待确定帧类型。

x264_lookahead_get_frames():通过lookahead分析帧类型。该函数调用了x264_slicetype_decide()x264_slicetype_analyse()x264_slicetype_frame_cost()等函数。经过一些列分析之后,最终确定了帧类型信息,并且将帧放入frames.current[]队列。

x264_frame_shift():从frames.current[]队列取出一帧用于编码。

x264_reference_update():更新参考帧列表。

x264_reference_reset():如果为IDR帧,调用该函数清空参考帧列表。

x264_reference_hierarchy_reset():如果是I(非IDR帧)、P帧、B帧(可做为参考帧),调用该函数。

x264_reference_build_list():创建参考帧列表list0list1

x264_ratecontrol_start():开启码率控制。

x264_slice_init():创建 Slice Header

x264_slices_write():编码数据(最关键的步骤)。其中调用了x264_slice_write()完成了编码的工作(注意“x264_slices_write()”和“x264_slice_write()”名字差了一个“s”)。

x264_encoder_frame_end():编码结束后做一些后续处理,例如释放一些中间变量以及打印输出一些统计信息。其中调用了x264_frame_push_unused()fenc重新放回frames.unused[]队列,并且调用x264_ratecontrol_end()关闭码率控制。

 

3.3.4x264_encoder_close()

 

        x264_encoder_close()用于关闭解码器,同时输出一些统计信息。它调用了下面的函数:

x264_lookahead_delete():释放Lookahead相关的变量。

x264_ratecontrol_summary():汇总码率控制信息。

x264_ratecontrol_delete():关闭码率控制。


3.4x264_slice_write()

 

x264_slice_write()x264项目的核心,它完成了编码了一个Slice的工作。根据功能的不同,该函数可以分为滤波(Filter),分析(Analysis),宏块编码(Encode)和熵编码(Entropy Encoding)几个子模块,如下图所示。


        x264_slice_write()用于编码Slice。该函数中包含了一个很长的for()循环。该循环每执行一遍编码一个宏块。x264_slice_write()中以下几个函数比较重要:

x264_nal_start():开始写一个NALU

x264_macroblock_thread_init():初始化存储宏块的重建数据缓存fdec_buf[]和编码数据缓存fenc_buf[]

x264_slice_header_write():输出 Slice Header

x264_fdec_filter_row():滤波模块。该模块包含了环路滤波,半像素插值,SSIM/PSNR的计算。

x264_macroblock_cache_load():将要编码的宏块的周围的宏块的信息读进来。

x264_macroblock_analyse():分析模块。该模块包含了帧内预测模式分析以及帧间运动估计等。

x264_macroblock_encode():宏块编码模块。该模块通过对残差的DCT变换、量化等方式对宏块进行编码。

x264_macroblock_write_cabac()CABAC熵编码模块。

x264_macroblock_write_cavlc()CAVLC熵编码模块。

x264_macroblock_cache_save():保存当前宏块的信息。

x264_ratecontrol_mb():码率控制。

x264_nal_end():结束写一个NALU

 

3.5、滤波模块

 

滤波模块对应的函数是x264_fdec_filter_row()。该函数完成了环路滤波,半像素插值,SSIM/PSNR的计算的功能,如下图所示。


        该函数调用了以下几个比较重要的函数:

x264_frame_deblock_row():去块效应滤波器。

x264_frame_filter():半像素插值。

x264_pixel_ssd_wxh()PSNR计算。

x264_pixel_ssim_wxh()SSIM计算。

 

3.6、分析模块

 

        分析模块对应的函数是x264_macroblock_analyse()。该函数包含了帧内预测模式分析以及帧间运动估计等,如下图所示。



        该函数调用了以下比较重要的函数(只列举了几个有代表性的函数):

x264_mb_analyse_init()Analysis模块初始化。

x264_mb_analyse_intra()I宏块帧内预测模式分析。

x264_macroblock_probe_pskip():分析是否是skip模式。

x264_mb_analyse_inter_p16x16()P16x16宏块帧间预测模式分析。

x264_mb_analyse_inter_p8x8()P8x8宏块帧间预测模式分析。

x264_mb_analyse_inter_p16x8()P16x8宏块帧间预测模式分析。

x264_mb_analyse_inter_b16x16()B16x16宏块帧间预测模式分析。

x264_mb_analyse_inter_b8x8()B8x8宏块帧间预测模式分析。

x264_mb_analyse_inter_b16x8()B16x8宏块帧间预测模式分析。

 

3.7、宏块编码模块

 

        宏块编码模块对应的函数是x264_macroblock_encode()。该模块通过对残差的DCT变换、量化等方式对宏块进行编码。对于Intra16x16宏块,调用x264_mb_encode_i16x16()进行编码,对于Intra4x4,调用x264_mb_encode_i4x4()进行编码。对于Inter类型的宏块则直接在函数体里面编码。


        从图中可以看出,宏块编码模块的x264_macroblock_encode()调用了x264_macroblock_encode_internal(),而x264_macroblock_encode_internal()完成了如下功能:

x264_macroblock_encode_skip():编码Skip类型宏块。

x264_mb_encode_i16x16():编码Intra16x16类型的宏块。该函数除了进行DCT变换之外,还对16个小块的DC系数进行了Hadamard变换。

x264_mb_encode_i4x4():编码Intra4x4类型的宏块。

帧间宏块编码:这一部分代码直接写在了函数体里面。

x264_mb_encode_chroma():编码色度块。

 

3.8、熵编码模块

 

        CABAC熵编码对应的函数是x264_macroblock_write_cabac()CAVLC熵编码对应的函数是x264_macroblock_write_cavlc(),如下图所示。


        x264_macroblock_write_cavlc()调用了以下几个比较重要的函数:

x264_cavlc_mb_header_i():写入I宏块MB Header数据。包含帧内预测模式等。

x264_cavlc_mb_header_p():写入P宏块MB Header数据。包含MVD、参考帧序号等。

x264_cavlc_mb_header_b():写入B宏块MB Header数据。包含MVD、参考帧序号等。

x264_cavlc_qp_delta():写入QP

x264_cavlc_block_residual():写入残差数据。

 

3.9、码率控制模块

 

        码率控制模块函数分布在x264源代码不同的地方,包含了以下几个比较重要的函数:

x264_encoder_open()中的x264_ratecontrol_new():创建码率控制。

x264_encoder_encode()中的x264_ratecontrol_start():开始码率控制。

x264_slice_write()中的x264_ratecontrol_mb():码率控制算法。

x264_encoder_encode()中的x264_ratecontrol_end():结束码率控制。

x264_encoder_close()中的x264_ratecontrol_summary():码率控制信息。

x264_encoder_close()中的x264_ratecontrol_delete():释放码率控制。

 

截止到这儿,x264的基本框架就差不多完全介绍完了,具体的细节性问题以及代码分析将在后续进行不断更新。

(此处要多谢雷神提供众多的资料) 大笑
目录
相关文章
|
3月前
|
存储 缓存 Java
hyengine编译实现问题之复用 quickjs 原代码如何解决
hyengine编译实现问题之复用 quickjs 原代码如何解决
|
6月前
|
编译器 Linux C语言
程序环境和预处理(含C语言程序的编译+链接)--2
程序环境和预处理(含C语言程序的编译+链接)--2
50 5
|
6月前
|
存储 编译器 程序员
程序环境和预处理(含C语言程序的编译+链接)--1
程序环境和预处理(含C语言程序的编译+链接)--1
44 0
|
Swift iOS开发
iOS 逆向编程(二十一)代码编译过程(以及什么阶段可反编译)
iOS 逆向编程(二十一)代码编译过程(以及什么阶段可反编译)
153 0
|
存储 自然语言处理 编译器
程序的编译与链接(C语言为例) #代码写好后到运行期间要经过怎样的过程呢?# 粗略版 #
程序的编译与链接(C语言为例) #代码写好后到运行期间要经过怎样的过程呢?# 粗略版 #
进阶C语言 第七章-------《程序的编译(预处理操作)+链接》 (预编译、编译、汇编、#define、条件编译,#include的包含)知识点+完整思维导图+基本练习题+深入细节+通俗易懂建议收藏(三)
进阶C语言 第七章-------《程序的编译(预处理操作)+链接》 (预编译、编译、汇编、#define、条件编译,#include的包含)知识点+完整思维导图+基本练习题+深入细节+通俗易懂建议收藏(三)
|
编译器 C语言
进阶C语言 第七章-------《程序的编译(预处理操作)+链接》 (预编译、编译、汇编、#define、条件编译,#include的包含)知识点+完整思维导图+基本练习题+深入细节+通俗易懂建议收藏(二)
进阶C语言 第七章-------《程序的编译(预处理操作)+链接》 (预编译、编译、汇编、#define、条件编译,#include的包含)知识点+完整思维导图+基本练习题+深入细节+通俗易懂建议收藏(二)
|
算法 计算机视觉 C++
Kalman算法C++实现代码(编译运行通过)
Kalman算法C++实现代码(编译运行通过)
186 0
|
存储 .NET C#
C#基础拾遗系列之一:先看懂IL代码
原文:C#基础拾遗系列之一:先看懂IL代码 一、前言 首先,想说说为什么要写这样系列的文章,有时候在和同事朋友聊天的时候,经常会听到这样的话题: (1)在这家公司没什么长进,代码太烂,学不到东西。(你有没有想想框架为什么这样写,代码还可以怎么去优化,比如公司使用Dapper,源码研究过没以及这样...
2029 0