最近尝试在windows系统下,交叉编译了x264源码。参考了一些网友们的经验。由于工具一直在变,网上的教程大部分比较老了,所以把我踩过的坑写下来,回馈网友们的帮助。
1. 为什么要交叉编译
x264用的c语言是c99版本的,然而VS对C99语法的支持并不好,所以没有办法直接用VS编译。x264官方代码从2009年开始放弃了支持VS。
但是可以用交叉编译工具,生成windows系统能用的exe,和VS能用的动态库。
步骤如下:
2.1 下载交叉编译工具 mingw+msys
https://sourceforge.net/projects/mingw/files/latest/download?source=files
下载后安装。
安装后会有一个 MinGW Installation Manager程序,用这个程序安装Basic Setup下的mingw-developer-toolkit、mingw32-base、mingw32-gcc-g++、msys-base工具集。
2.2 安装nasm
X264 自从2015年以后,默认的汇编器从 yasm 改成了nasm, 下一步需要下载nasm.exe
找到MinGW的安装目录,把下载后的nasm.exe 放在MinGW\bin 目录下
2.3 下载最新的x264
https://www.videolan.org/developers/x264.html
下载源代码放在 msys 的目录里面:
for example: MinGW\msys\1.0\home\yourname\
2.4 打开msys
进入MinGW\msys\1.0,执行msys.bat, 会有一个类似bash的命令行界面:
一些教程说需要编辑 msys.bat,实测并不需要。
2.5 编译源码
进入 x264源码目录,执行
./configure --enable-shared
--enable-shared 可以配置生成dll文件。
然后执行 make
在源码目录下会产生4个文件:
x264.exe:命令行程序,可以运行测试一下:
Windows 下打开一个cmd窗口, cd 到这个目录, 运行 x264.exe --version
libx264-155.dll:动态库dll文件,其中155是版本。
libx264.dll.a:将文件名字修改为libx264-155.lib,它是dll文件的引导lib。修改完名字之后在VC工程中就可以作为“附加依赖项”了。
libx264.a:这个是linux的静态库。
在VC工程使用 .lib 和 .dll ,会提示缺少 libgcc_s_dw2-1.dll,从mingw搜索拷贝过去就行了。
2.6 附简单的测试代码:
#include <iostream>
#include <string>
#include "stdint.h"
extern "C"{
#include "x264.h"
#include "x264_config.h"
};
using namespace std;
int main()
{
x264_param_t param;
x264_param_default(¶m);
getchar();
return 0;
}
2.7 库api用法例子
源码中也有一个 example.c , 演示了libx264 api的用法。
在VS新建工程,添加上述步骤生成的 .dll, .lib文件和必要的.h 文件,
然后将example.c 复制到visual studio中试运行。
由于这个文件也用了VS不支持的C99语法,需要把所有goto去掉,把所有变量声明放在代码块开始的地方。
这是我改后的一小段代码:
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#ifdef _WIN32
#include <io.h> /* _setmode() */
#include <fcntl.h> /* _O_BINARY */
#endif
#include <stdint.h>
#include <stdio.h>
extern "C" {
#include <x264.h>
}
#include <chrono>
#include <iostream>
#define FAIL_IF_ERROR( cond, ... )\
do\
{\
if( cond )\
{\
fprintf( stderr, __VA_ARGS__ );\
return -1;\
}\
} while( 0 )
int main(int argc, char **argv)
{
int width, height;
x264_param_t param;
x264_picture_t pic;
x264_picture_t pic_out;
x264_t *h;
int i_frame = 0;
int i_frame_size;
x264_nal_t *nal;
int i_nal;
FILE * i_file;
FILE * o_file;
int luma_size;
int chroma_size;
decltype(std::chrono::high_resolution_clock::now()) t_start, t_end;
FAIL_IF_ERROR(!(argc > 3), "Example usage: example 352x288 input.yuv output.h264\n");
FAIL_IF_ERROR(2 != sscanf(argv[1], "%dx%d", &width, &height), "resolution not specified or incorrect\n");
char * in_file_name = argv[2];
char * out_file_name = argv[3];
i_file = fopen(in_file_name, "rb");
FAIL_IF_ERROR(!i_file, "can't open input file");
o_file = fopen(out_file_name, "wb");
FAIL_IF_ERROR(!o_file, "can't open output file");
/* Get default params for preset/tuning */
if (x264_param_default_preset(¶m, "medium", NULL) < 0)
return -1;
/* Configure non-default params */
param.i_bitdepth = 8;
param.i_csp = X264_CSP_I420;
param.i_width = width;
param.i_height = height;
param.b_vfr_input = 0;
param.b_repeat_headers = 1;
param.b_annexb = 1;
/* Apply profile restrictions. */
if (x264_param_apply_profile(¶m, "high") < 0)
return -1;
if (x264_picture_alloc(&pic, param.i_csp, param.i_width, param.i_height) < 0)
return -1;
h = x264_encoder_open(¶m);
if (!h) {
x264_picture_clean(&pic);
return -1;
}
luma_size = width * height;
chroma_size = luma_size / 4;
t_start = std::chrono::high_resolution_clock::now();
/* Encode frames */
for (;; i_frame++)
{
/* Read input frame */
if (fread(pic.img.plane[0], 1, luma_size, i_file) != luma_size)
break;
if (fread(pic.img.plane[1], 1, chroma_size, i_file) != chroma_size)
break;
if (fread(pic.img.plane[2], 1, chroma_size, i_file) != chroma_size)
break;
pic.i_pts = i_frame;
i_frame_size = x264_encoder_encode(h, &nal, &i_nal, &pic, &pic_out);
if (i_frame_size < 0) {
x264_encoder_close(h);
x264_picture_clean(&pic);
return -1;
}
else if (i_frame_size)
{
if (!fwrite(nal->p_payload, i_frame_size, 1, o_file)) {
x264_encoder_close(h);
x264_picture_clean(&pic);
return -1;
}
}
}
/* Flush delayed frames */
while (x264_encoder_delayed_frames(h))
{
i_frame_size = x264_encoder_encode(h, &nal, &i_nal, NULL, &pic_out);
if (i_frame_size < 0) {
x264_encoder_close(h);
x264_picture_clean(&pic);
return -1;
}
else if (i_frame_size)
{
if (!fwrite(nal->p_payload, i_frame_size, 1, o_file)) {
x264_encoder_close(h);
x264_picture_clean(&pic);
return -1;
}
}
}
t_end = std::chrono::high_resolution_clock::now();
auto int_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t_end - t_start);
x264_encoder_close(h);
x264_picture_clean(&pic);
std::cout << std::endl;
std::cout << "time in ms: " << int_ms.count() << "; frames: " << i_frame << std::endl;
std::cout << "frame per seconde " << i_frame * 1000 / int_ms.count() << std::endl;
return 0;
}
Reference:
https://blog.csdn.net/aflyeaglenku/article/details/47146863
https://blog.csdn.net/chinabinlang/article/details/26452011
https://www.jianshu.com/p/fa42abce1ea7
https://my.oschina.net/u/1866382/blog/406068
3. 64位x264
默认编译出来是32位的,下一步尝试交叉编译64位的x264。
4. Visual Studio原生编译x264
有网友把x264改成兼容VS的 c语言规则,并制作了完整的VS工程。
https://github.com/ShiftMediaProject/x264
可以在VS下原生编译。
改c语言版本的过程没有想象中那么麻烦,见作者博客:
http://siliconandlithium.blogspot.ca/2014/03/building-x264-on-windows-with-visual.html
5. 性能对比
无论是交叉编译的版本(32位),还是VS编译的版本(64位),性能都比x264官方编译对应版本略逊。
哪位大神如果知道原因的话,欢迎交流。