目录
- 参考
- 使用说明
- x264cli框架
- x264cli内部流程
- x264cli中的一些结构体
1. 参考
- [1] 雷霄骅/x264源代码简单分析:x264命令行工具(x264.exe)
- [2] github.com/FFMS/ffms2/
- [3] en.wikipedia.org/wiki/AviSynth
- [4] github.com/gpac/gpac
- [5] github.com/l-smash/l-smash
2. 使用说明
x264的使用方式可以通过帮助文档来了解,x264的help信息的一部分如下,省略了很多参数说明的部分。
x264 core:155
Syntax: x264 [options] -o outfile infile
Infile can be raw (in which case resolution is required),
or YUV4MPEG (*.y4m),
or Avisynth if compiled with support (yes).
or libav* formats if compiled with lavf support (no) or ffms support (no).
Outfile type is selected by filename:
.264 -> Raw bytestream
.mkv -> Matroska
.flv -> Flash Video
.mp4 -> MP4 if compiled with GPAC or L-SMASH support (no)
Output bit depth: 8/10
.
Options:
-h, --help List basic options
--longhelp List more options
--fullhelp List all options
Example usage:
Constant quality mode:
x264 --crf 24 -o <output> <input>
Two-pass with a bitrate of 1000kbps:
x264 --pass 1 --bitrate 1000 -o <output> <input>
x264 --pass 2 --bitrate 1000 -o <output> <input>
Lossless:
x264 --qp 0 -o <output> <input>
Maximum PSNR at the cost of speed and visual quality:
x264 --preset placebo --tune psnr -o <output> <input>
Constant bitrate at 1000kbps with a 2 second-buffer:
x264 --vbv-bufsize 2000 --bitrate 1000 -o <output> <input>
......
语法很简单:x264 [options] -o outfile infile
示例:
x264 --input-res 1280x720 -o julin_3s_x264.flv julin_3s.yuv
支持输入文件的格式:
- yuv的原始数据格式,需要指定视频的宽/高,如
--input-res 1280x720
。 - YUV4MPEG (*.y4m)格式。
- Avisynth(avs),如果编译支持。之前没听过这个软件,不太了解。
- libav*支持的format,如果编译有lavf或者ffms的支持。lavf指的是FFmpeg,ffms是一个基于FFmpeg和Avisynth/VapourSynth插件的库,方便对帧的精确访问 [2]。
支持输出文件的格式:.h264
、.mkv
、.flv
、.mp4
(需要引入外部库GPAC
[4]或L-SMASH
[5])
帮助文档中提供的几个使用的示例。各参数的使用具体看各参数的说明。
- [options]可以分为两种,一种是带参数值的,一种是不带参数值的。
3. x264命令行工具(x264_cli)框架
简要画了一下x264命令行工具(x264_cli)的框架,大致如下图。
- main:程序的主流程,主要包括参数解析、解封装、编码、封装和其他一些信息的输出流程。
- opt:解析命令行参数的工具。
- help:打印帮助信息。
- version:打印版本信息。
- demuxer:解封装器,支持的输入格式的解封装。
- muxer:封装器,编码后的H.264封装到相应的输入格式。
- filter:对输入数据应用的filter,比如crop(裁剪)等。
- libx264:进行H.264编码。
demuxer
- 内部支持yuv和y4m的输入格式。
- 其他的输入格式可编译加入外部模块如libav、libavsynth和libffms2来支持,lavf、avs、ffms为对应外部模块的一个包装。
muxer
- 内部支持h264裸流、flv、mkv的输入格式。
- mp4格式可编译加入外部模块libgpac[4]或liblsmash[5]来支持,mp4为对应外部模块的包装。
4. x264_cli内部流程
内部流程可分三个阶段:
- parse:对应x264.c中的parse()函数,解析参数和打开输入输出文件。
- encode:对应x264.c的encode()的函数,H.264编码的阶段。
- close:关闭/释放打开的一些资源。
4.1 parse阶段
parse阶段的主要流程见下面的时序图:
说明:
- 虚线箭头表示在某些情况下会执行。
- x264_param_default():设置默认参数。
- getopt_long():解析输入的参数。根据解析的参数会对
x264_param_t
、cli_input_opt_t
、cli_output_opt_t
、cli_opt_t
这些结构体进行设置。 - x264_param_default_preset():根据参数preset或tune,设置对应的编码参数,在设置其他传入的编码参数之前。
- 先根据preset设置各种调谐好的编码参数(preset分各种速度档位,用于调节编码速度和质量的平衡)。
- 再根据tune设置各种调谐好的编码参数(tune为不同视频类型和视觉优化的参数)。
- x264_param_apply_fastfirstpass():如果设置了first-pass模式(rc.b_stat_read == 0, rc.b_stat_write == 1),修改编码器设置,以禁用通常在第一趟编码无用的选项。
- x264_param_apply_profile():根据profile设置对应配置的编码参数。
- select_output():根据文件的扩展名和编译支持的三方库选择对应的封装器(使用结构体cli_output_t定义)。
- cli_output_t.openfile(): 使用选择的封装器打开输出文件。封装器的上下文信息保存在
cli_opt_t.hout
,由于不同的封装器的上下文信息不一样,这是一个(void *)类型。 - select_input():根据文件的扩展名和和编译支持的三方库选择对应的解封装器(结构体cli_input_t定义)。
- cli_input_t.openfile(): 使用选择的解封装器打开输出文件,读取header信息,获取的一些信息会保存在
video_info_t
结构体中。解封装器的上下文信息保存在cli_opt_t.hin
。 - init_vid_filters():初始化filter,其中有名为"source"的filter,里面包装了上面已经选择好的解封装器。这些filter用于对输入数据做一些处理。
4.2 encode阶段
parse阶段的主要流程见下面的时序图:
说明:
- x264_encoder_open():打开x264编码器,编码参数由x264_param_t传入,编码器内部会拷贝这些参数,编码器内部使用的参数集与打开编码器时传入的x264_param_t可能会有不同。
- x264_encoder_parameters():获得编码器内部使用的参数集x264_param_t。
- cli_output_t.set_param():封装器根据x264_param_t设定封装格式需要的一些参数。
- x264_encoder_headers():适用于mp4、flv等封装格式中的AVCC格式H.264码流。编码SPS/PPS/SEI的NALU,添加到整个H.264码流前面。
- 进入一个循环中进行一帧一帧的将YUV编码为H.264。
- cli_vid_filter_t.get_frame():从输入的获取一帧YUV数据,存放在
cli_pic_t
结构体。 - x264_picture_init():初始化
x264_picture_t
结构体。 - 设置待编码数据buffer信息和pts:把
cli_pic_t
待编码数据buffer的地址复制给x264_picture_t
,计算pts然后设置给x264_picture_t
。 - x264_encoder_encode():完成编码工作。
- cli_output_t.write_frame():封装器写入一帧数据。
- cli_vid_filter_t.release_frame():释放刚才存储YUV数据的buffer。
- cli_vid_filter_t.get_frame():从输入的获取一帧YUV数据,存放在
- fush encoder:flush编码器,获取内部缓存的编码数据。
- x264_encoder_close:关闭x264编码器。
- cli_output_t.close_file: 一些封装器会根据传入的largest_pts和second_largest_pts 来修改total_duration信息。
4.3 close阶段
close阶段很简单
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 );
- 关闭各种打开的资源。
- cli_output.close_file:当parse出现错误没有进入encode阶段时会走到这里。
5. x264cli中的一些结构体
libx264中定义的:
x264_param_t //配置编码器使用的各种参数
x264_picture_t //存放待编码的YUV数据
x264_nal_t //存放已编码的H.264的NAL数据
x264cli中定义的:
cli_opt_t //x264cli的一些上下文信息
cli_pic_t //保存从输入获取的YUV数据
cli_input_t //处理输入的解封装器
cli_output_t //处理输出的封装器
cli_vid_filter_t //对输入数据做一些处理如crop的filter
video_info_t //保存从解封装获取到的一些视频信息,filter中会使用。
5.1 cli_opt_t
typedef struct {
int b_progress; //是否显示编码进度。默认开启,使用--no-progress来关闭。
int i_seek; //第一个要编码的帧,使用--seek <integer>设置。
hnd_t hin; //输入使用的解封装器的上下文信息。
hnd_t hout; //输出使用的封装器的上下文信息。
FILE *qpfile; //对某些或所有帧强制帧类型和QP值的配置文件,使用说明参看--qpfile。
FILE *tcfile_out;//输出每一帧的pts到tcfile_out文件,使用--tcfile-out <string>指定文件。
double timebase_convert_multiplier; //timebase转换倍数,使用--timebase选项指定timebase时使用。
int i_pulldown; //使用--pulldown <string>选项来改变帧率时使用。
} cli_opt_t;
5.2 cli_pic_t
/* image data type used by x264cli */
typedef struct
{
int csp; /* colorspace */
int width; /* width of the picture */
int height; /* height of the picture */
int planes; /* number of planes */
uint8_t *plane[4]; /* pointers for each plane */
int stride[4]; /* strides for each plane */
} cli_image_t;
typedef struct
{
cli_image_t img;
int64_t pts; /* input pts */
int64_t duration; /* frame duration - used for vfr */
void *opaque; /* opaque handle */
} cli_pic_t;
5.3 cli_input_t
typedef struct
{
int (*open_file)( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt );
int (*picture_alloc)( cli_pic_t *pic, hnd_t handle, int csp, int width, int height );
int (*read_frame)( cli_pic_t *pic, hnd_t handle, int i_frame );
int (*release_frame)( cli_pic_t *pic, hnd_t handle );
void (*picture_clean)( cli_pic_t *pic, hnd_t handle );
int (*close_file)( hnd_t handle );
} cli_input_t;
- 输入使用的解装器的接口定义。
- open_file/close_file:输入文件的打开/关闭。此外,open_file中进行一些初始化的工作和获取一些header信息,close_file中进行一些销毁工作。
- picture_alloc/picture_clean:分配/释放存储待编码的视频数据的数据结构
cli_pic_t
。(有个地方没明白,为什么lavf、ffms和avs中的picture_clean函数没有去释放cli_pic_t
的内存,只是把cli_pic_t
设置为了0) - read_frame:读取一帧YUV数据。
- release_frame:在
cli_pic_t
使用mmap映射的内存和avs中需要做一些释放操作。
5.4 cli_output_t
typedef struct
{
int use_dts_compress;
} cli_output_opt_t;
typedef struct
{
int (*open_file)( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt );
int (*set_param)( hnd_t handle, x264_param_t *p_param );
int (*write_headers)( hnd_t handle, x264_nal_t *p_nal );
int (*write_frame)( hnd_t handle, uint8_t *p_nal, int i_size, x264_picture_t *p_picture );
int (*close_file)( hnd_t handle, int64_t largest_pts, int64_t second_largest_pts );
} cli_output_t;
- 输出使用的封装器的接口定义。
- open_file/close_file:输出文件的打开/关闭。此外,open_file中进行一些初始化工作,close_file中写入一些元数据和进行一些销毁工作。
- picture_alloc/picture_clean:分配/释放存储待编码的视频数据的数据结构
cli_pic_t
。(有个地方没明白,为什么lavf、ffms和avs中的picture_clean函数没有去释放cli_pic_t
的内存,只是把cli_pic_t
设置为了0) - set_param:告知封装器编码器使用的参数信息,用于设置一些视频元数据。
- write_headers:写H.264的头信息(SPS/PPS/SEI的NALU数据),适用于mp4、flv等封装格式中的AVCC格式H.264码流。
- write_frame:写一帧已编码的数据。
5.5 cli_vid_filter_t
struct cli_vid_filter_t
{
/* name of the filter */
const char *name;
/* help: a short message on what the filter does and how to use it.
* this should only be implemented by filters directly accessible by the user */
void (*help)( int longhelp );
/* init: initializes the filter given the input clip properties and parameter to adjust them as necessary
* with the given options provided by the user.
* returns 0 on success, nonzero on error. */
int (*init)( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string );
/* get_frame: given the storage for the output frame and desired frame number, generate the frame accordingly.
* the image data returned by get_frame should be treated as const and not be altered.
* returns 0 on success, nonzero on error. */
int (*get_frame)( hnd_t handle, cli_pic_t *output, int frame );
/* release_frame: frame is done being used and is signaled for cleanup.
* returns 0 on succeess, nonzero on error. */
int (*release_frame)( hnd_t handle, cli_pic_t *pic, int frame );
/* free: run filter cleanup procedures. */
void (*free)( hnd_t handle );
/* next registered filter, unused by filters themselves */
cli_vid_filter_t *next;
};
- init:使用输入的剪辑属性和参数初始化filter。
- get_frame:给定输出帧的存储空间和期望的帧序号,相应地生成帧。返回的图像数据应该作为const处理,不应该被修改。
- release_frame:帧使用完成之后出清除信号。
- free:执行filter的清理程序。
5.6 video_info_t
/* properties of the source given by the demuxer */
typedef struct
{
int csp; /* colorspace of the input */
uint32_t fps_num;
uint32_t fps_den;
int fullrange; /* has 2^bit_depth-1 instead of 219*2^(bit_depth-8) ranges (YUV only) */
int width;
int height;
int interlaced;
int num_frames;
uint32_t sar_width;
uint32_t sar_height;
int tff;
int thread_safe; /* demuxer is thread_input safe */
uint32_t timebase_num;
uint32_t timebase_den;
int vfr;
} video_info_t;