最近在研究x264的使用,在网上没有找到满意的编译好的so库,无奈只能自己编译了,此文记录下编译的过程和简单使用。
一、x264的编译
1、下载源码
官网:x264, the best H.264/AVC encoder - VideoLAN
点击即可下载。下载完成是这样的
解压缩
2、下载ndk
下载地址:https://github.com/android/ndk/wiki/Unsupported-Downloads
我下载的是r14b版本,选择Linux平台的进行下载
,下载完成之后解压缩。
3、新建编译脚本
首先把configure文件里面的"-$API"、".$API"删除,否则编译出来的so库是带版本号的。
进入x264-master目录,新建一个脚本文件,名称随意,我用的名称是build_x264.sh,内容如下:
#!/bin/bash
# Android ndk位置
ANDROID_NDK=/home/zhangkepeng/下载/android-ndk-r14b-linux-x86_64/android-ndk-r14b
function build_x264()
{
PREFIX=$(pwd)/android/$ANDROID_ABI
SYSROOT=$ANDROID_NDK/platforms/$ANDROID_API/$ANDROID_ARCH
TOOLCHAIN=$ANDROID_NDK/toolchains/$ANDROID_EABI/prebuilt/linux-x86_64/bin
CROSS_PREFIX=$TOOLCHAIN/$CROSS_COMPILE
echo "Compiling x264 for $ANDROID_ABI"
./configure \
--prefix=$PREFIX \
--disable-asm \
--enable-static \
--enable-shared \
--enable-pic \
--host=$HOST \
--cross-prefix=$CROSS_PREFIX \
--sysroot=$SYSROOT \
make clean
make -j4
make install
echo "The Compilation of x264 for $ANDROID_ABI is completed"
}
# armeabi-v7a
ANDROID_ABI=armeabi-v7a
ANDROID_API=android-14
ANDROID_ARCH=arch-arm
ANDROID_EABI=arm-linux-androideabi-4.9
HOST=arm-linux-androideabi
CROSS_COMPILE=arm-linux-androideabi-
build_x264
# arm64-v8a
ANDROID_ABI=arm64-v8a
ANDROID_API=android-21
ANDROID_ARCH=arch-arm64
ANDROID_EABI=aarch64-linux-android-4.9
HOST=aarch64-linux-android
CROSS_COMPILE=aarch64-linux-android-
build_x264
configure各个参数的含义可以自行查阅,注意ndk的位置要正确。
脚本文件建立完成后使用命令sudo chmod +x build_x264.sh赋予可执行权限,执行./build_x264.sh开始交叉编译。
编译完成后会在当前目录生成一个android
文件夹,里面包含arm64-v8a和armeabi-v7a两个文件夹,这就是我们需要用的头文件、.a文件和.so文件了。
二、libx264.so在android上的使用
打开AS,新建一个Native C++的工程,将上面 armeabi-v7a下面的include文件夹拷贝到cpp目录下,将libx264.so分别拷贝到相应的文件夹下面,拷贝完成后工程的目录结构如下:
修改CMakeLists.txt文件内容如下:
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Declares and names the project.
project("textx264")
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(x264 SHARED IMPORTED)
#set_target_properties(x264 PROPERTIES IMPORTED_LOCATION ../jniLibs/${ANDROID_ABI}/libx264.so)
set_target_properties(x264 PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libx264.so)
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
x264
# Links the target library to the log library
# included in the NDK.
${log-lib})
修改native-lib.cpp文件的内容如下:
#include <jni.h>
#include <string>
#include <android/log.h>
#if defined ( __cplusplus)
extern "C"
{
#include "x264.h"
};
#else
#include "x264.h"
#endif
#define LIBENC_LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "libx264", __VA_ARGS__))
#define LIBENC_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO , "libx264", __VA_ARGS__))
#define LIBENC_LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN , "libx264", __VA_ARGS__))
#define LIBENC_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "libx264", __VA_ARGS__))
extern "C" JNIEXPORT jstring JNICALL
Java_com_sdses_textx264_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
FILE *fp_src = fopen("/sdcard/test_640x360_yuv420p.yuv", "rb");
FILE *fp_dst = fopen("/sdcard/test_out.h264", "wb");
if (fp_src == NULL || fp_dst == NULL) {
LIBENC_LOGE("openErr");
return env->NewStringUTF("openErr");
}
x264_picture_t *pPic_in = (x264_picture_t *) malloc(sizeof(x264_picture_t));
x264_picture_t *pPic_out = (x264_picture_t *) malloc(sizeof(x264_picture_t));
x264_param_t *pParam = (x264_param_t *) (malloc(sizeof(x264_param_t)));
x264_param_default(pParam);
pParam->i_width = 640;
pParam->i_height = 360;
int csp = X264_CSP_I420;
pParam->i_csp = csp;
x264_param_apply_profile(pParam, x264_profile_names[5]);
int iNal = 0;
x264_nal_t *pNals = NULL;
x264_t *pHandle = x264_encoder_open_164(pParam);
LIBENC_LOGE("pHandle:%d", pHandle);
x264_picture_init(pPic_out);
x264_picture_alloc(pPic_in, csp, pParam->i_width, pParam->i_height);
int y_size = pParam->i_width * pParam->i_height;
int i = 0;
for (; i < 200; ++i) {
fread(pPic_in->img.plane[0], y_size, 1, fp_src); //Y
fread(pPic_in->img.plane[1], y_size / 4, 1, fp_src); //U
fread(pPic_in->img.plane[2], y_size / 4, 1, fp_src); //V
pPic_in->i_pts = i;
int ret = x264_encoder_encode(pHandle, &pNals, &iNal, pPic_in, pPic_out);
LIBENC_LOGE("x264_encoder_encode:%d", ret);
LIBENC_LOGE("iNal:%d", iNal);
if (ret < 0) {
// printf("Error.\n");
return env->NewStringUTF("Error");
}
printf("Succeed encode frame: %5d\n", i);
for (int j = 0; j < iNal; ++j) {
fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);
}
}
//flush encoder
while (1) {
int ret = x264_encoder_encode(pHandle, &pNals, &iNal, NULL, pPic_out);
if (ret == 0) {
break;
}
printf("Flush 1 frame.\n");
for (int j = 0; j < iNal; ++j) {
fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);
}
i++;
}
x264_picture_clean(pPic_in);
x264_encoder_close(pHandle);
pHandle = NULL;
free(pPic_in);
free(pPic_out);
free(pParam);
fclose(fp_src);
fclose(fp_dst);
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
代码的功能是将/sdcard/test_640x360_yuv420p.yuv(请自己准备该文件,根据yuv视频的宽、高和像素格式调整代码中的相应参数)文件的前200帧图片使用x264编码输出为/sdcard/test_out.h264文件。
完成上述工作后,运行代码,在sdcard目录下会看到test_out.h264文件,使用ffplay播放此文件可以验证编码效果。
参考资料