x264编码过程分析(一)概述
雷神 x264编码
先看雷神的 再来看:待整理的一个栏目的博客
这里只摘录个人觉得梗概的部分,详细跳转雷神x264源代码简单分析:概述
x264编码主要分4部分:
1、x264_encoder_open()用于打开编码器,其中初始化了libx264编码所需要的各种变量。它调用了下面的函数:
x264_validate_parameters():检查输入参数(例如输入图像的宽高是否为正数)。
x264_sps_init():根据输入参数生成H.264码流的SPS信息。
x264_pps_init():根据输入参数生成H.264码流的PPS信息。
x264_predict_16x16_init():初始化Intra16x16帧内预测汇编函数。
x264_predict_4x4_init():初始化Intra4x4帧内预测汇编函数。
x264_pixel_init():初始化像素值计算相关的汇编函数(包括SAD、SATD、SSD等)。
x264_dct_init():初始化DCT变换和DCT反变换相关的汇编函数。
x264_mc_init():初始化运动补偿相关的汇编函数。
x264_quant_init():初始化量化和反量化相关的汇编函数。
x264_deblock_init():初始化去块效应滤波器相关的汇编函数。
x264_lookahead_init():初始化Lookahead相关的变量。
x264_ratecontrol_new():初始化码率控制相关的变量。
2、x264_encoder_headers()输出SPS/PPS/SEI这些H.264码流的头信息。它调用了下面的函数:
x264_sps_write():输出SPS
x264_pps_write():输出PPS
x264_sei_version_write():输出SEI//x264_param2string将参数变成string再write
//为了使之在文件中是nalu的形式 要再前后进行x264_nal_start() x264_nal_end()
3、x264_encoder_encode()编码一帧YUV为H.264码流。它调用了下面的函数:
1、x264_frame_pop_unused():获取1个x264_frame_t类型结构体fenc。
//如果frames.unused[]队列不为空,就调用x264_frame_pop()从unused[]队列取1个现成的;
//否则就调用x264_frame_new()创建一个新的编码帧or重建帧。
// 调用x264_frame_internal_csp()将多种(i_csp=)外部colorspace转换成3中内部colorspace:X264_CSP_NV12(对应YUV420),X264_CSP_NV16(对应YUV422),X264_CSP_I444(对应YUV444)
2、x264_frame_copy_picture()将外部结构体的pic_in(x264_picture_t类型)的数据拷贝给内部结构体的fenc(x264_frame_t类型)。//简单地将x264_picture_t中字段的值赋值给了x264_frame_t。
3、x264_lookahead_put_frame():将fenc放入lookahead.next.list[]队列,等待确定帧类型。
4、x264_lookahead_get_frames():分析Lookahead模块中一个帧的帧类型。分析后的帧保存在frames.current[]中。//调用x264_slicetype_decide→调用x264_slicetype_analyse 以及x264_slicetype_frame/slice/mb_cost计算开销。
//经过一些列分析之后,最终确定了帧类型信息,并且将帧放入frames.current[]队列。
//(1)如果frames[1]通过scenecut()判断为场景切换,设置为I帧,并且直接返回。
//(2)如果i_bframe为0,即不使用B帧,则将所有帧都设置为P帧。
//(3)如果i_bframe不为0,即使用B帧,则需要进行比较复杂的帧开销计算。这时候需要调用一帧图像开销的计算函数x264_slicetype_frame_cost()。
5、x264_frame_shift():从frames.current[]队列头部取出1帧list[0]用于编码。
6、x264_reference_update():更新参考帧队列frames.reference[]。//将重建帧fdec加入参考帧队列
//如果重建帧fdec是不被参考的B帧,则直接返回;如果fdec是被参考的帧,则会调用x264_frame_push()将该帧加入frames.reference[]队列的尾部。
//如果frames.reference[]已经满了,则会调用x264_frame_shift()和x264_frame_push_unused()将frames.reference[]队列头部的帧移动到frames.unused[]队列。最后函数还会调用x264_frame_pop_unused()获取一个新的重建帧fdec。
7、x264_reference_reset():如果为IDR帧,调用该函数清空参考帧列表。
//x264_reference_reset()中调用x264_frame_pop()和x264_frame_push_unused()将frames.reference[]队列中的帧移动到frames.unused[]队列中。
8、x264_reference_hierarchy_reset():如果是I(非IDR帧)、P帧、B帧(可做为参考帧),调用该函数。
9、x264_reference_build_list():创建参考帧列表list0和list1。
10、根据选项做一些配置:
a)如果b_aud不为0,输出AUD类型NALU
b)在当前帧是关键帧的情况下,如果b_repeat_headers不为0,调用x264_sps_write()和x264_pps_write()输出SPS和PPS。
c)输出一些特殊的SEI信息,用于适配各种解码器。
11、x264_ratecontrol_start():开启码率控制。
12、x264_slice_init():创建 Slice Header。//调用x264_slice_header_init()完成了Slice Header “通用”的初始化工作,然后根据帧类型的不同,做了一些特殊参数的设置
13、x264_slices_write():编码数据(最关键的步骤)。//其中调用了x264_slice_write()完成了编码的工作(注意“x264_slices_write对应帧”和“x264_slice_write对应slice”名字差了一个“s”)。
14、x264_encoder_frame_end():编码结束后做一些后续处理,//例如记录一些统计信息。其中调用了x264_frame_push_unused()将fenc重新放回frames.unused[]队列,并且调用x264_ratecontrol_end()关闭码率控制。
4、x264_encoder_close()用于关闭解码器,同时输出一些统计信息。
//输出使用了libx264中输出日志的API函数
它调用了下面的函数:
x264_lookahead_delete():释放Lookahead相关的变量。
x264_ratecontrol_summary():汇总码率控制信息。
x264_ratecontrol_delete():关闭码率控制。
上一篇: linux打包的几种方式