x264的编译和在Android上的使用

最近在研究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播放此文件可以验证编码效果。

参考资料

1、https://cloud.tencent.com/developer/article/1832832

2、最简单的视频编码器:基于libx264(编码YUV为H.264)-CSDN博客

  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值