编译Android使用的libx264并使用进行H(1)

交叉编译

想要使用libx264我们得编译成在Android和IOS上能够使用的二进制文件:

  • 后缀为.a格式的静态文件
  • 后缀为.so格式的动态文件

注意:这里编译是在Linux和MacOS上执行,在Windows配置实在是比较麻烦,真心不如使用虚拟机安装一个ubuntu的Linux系统。

下载

下载的方式大概有如下两种:

  • 可以直接官网的下载地址直接进行下载
  • 也可以打开Terminal,通过git将代码拷贝到本地

git clone https://code.videolan.org/videolan/x264.git

解压之后大概的目录内容如下 x264目录

编写脚本

进入上述解压之后的x264根目录,然后创建一个build_x264.sh(可以随便取名字,只要是.sh的文件就可)的可执行文件。然后贴上如下代码:

#!/bin/bash

Android ndk位置

ANDROID_NDK=/home/c2yu/developer/android/sdk/ndk/android-ndk-r14b

function build_x264()
{
PREFIX= ( p w d ) / a n d r o i d / (pwd)/android/ (pwd)/android/ANDROID_ABI
SYSROOT= A N D R O I D N D K / p l a t f o r m s / ANDROID_NDK/platforms/ ANDROIDNDK/platforms/ANDROID_API/ A N D R O I D A R C H T O O L C H A I N = ANDROID_ARCH TOOLCHAIN= ANDROIDARCHTOOLCHAIN=ANDROID_NDK/toolchains/$ANDROID_EABI/prebuilt/linux-x86_64/bin

CROSS_PREFIX= T O O L C H A I N / TOOLCHAIN/ TOOLCHAIN/CROSS_COMPILE

echo "Compiling x264 for A N D R O I D A B I " . / c o n f i g u r e   − − p r e f i x = ANDROID_ABI" ./configure \ --prefix= ANDROIDABI"./configure prefix=PREFIX
–disable-asm
–enable-static
–enable-shared
–enable-pic
–host= H O S T   − − c r o s s − p r e f i x = HOST \ --cross-prefix= HOST crossprefix=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

上述为在Linux上的脚本,需要注意

  • ANDROID_NDK 需要替换成你自己的android ndk路径。这里使用的是ndk-14b版本
  • 如果是MacOS需要将TOOLCHAIN后路径替换成$ANDROID_NDK/toolchains/$ANDROID_EABI/prebuilt/darwin-x86_64/bin

脚本编辑完成之后保存,然后在Terminal赋予可执行权限并编译:

cd xxx/x264 // 进入x264目录
sudo chmod +x build_x264.sh // 赋予可执行权限
./build_x264.sh //开始交叉编译

编译之后会在当前目录生成一个android文件夹,打开之后就会看到大概如下内容: 任意打开一个文件夹,我们可以看到如下内容:

  • include里面装的是头文件 - 后面会用到
  • bin 里面装的是x264执行文件 - 终端使用,不用考虑
  • lib 里面装的就是我们需要的.a和.so二进制文件 - 最终就是为了它

二进制文件

使用

虽然我们已经成功编译出了libx264的二进制文件,但是在Android上还是不能直接使用。因为还没有写编码程序。

在Android上使用大概有如下两种方式:

  • 通过cmake的方式直接在Android Studio上使用
  • ndk-build编译成可直接使用的动态链接文件。

下面会详细介绍这两种方式的使用方法。

Cmake

  1. 创建一个Android的原生项目(Native)。怎么创建?请参考《Android音视频开发:音频非压缩编码和压缩编码》,里面介绍了如何创建一个Native项目。

  2. 创建完成项目之后,将上述提到的include文件夹里面的头文件放入cpp这个文件夹下

请先忽略这个h264-encode的这个c++文件,在后面会介绍

  1. 然后在main目录下创建nativeLibs文件夹,将arm64-v8aarmeabi-v7a的两个文件夹的内容拷贝进去,如上图那样。

  2. 创建h264-encode.cpp的编码视频的处理程序。粘贴代码如下:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include “android/log.h”

extern “C” {
#include “x264.h”
#include “jni.h”
}

#define LOGD(…) __android_log_print(ANDROID_LOG_DEBUG , “h264-encode”, VA_ARGS)
#define LOGI(…) __android_log_print(ANDROID_LOG_INFO , “h264-encode”, VA_ARGS)
#define LOGE(…) __android_log_print(ANDROID_LOG_ERROR , “h264-encode”, VA_ARGS)

extern “C”
JNIEXPORT jint JNICALL
Java_com_coder_x264cmake_X264Encode_encode(
JNIEnv *env, jobject thiz,
jint width, jint height,
jstring yuv_path, jstring h264_path, jint yuv_csp) {
int ret = 0;
// TODO: implement encode()
if (width == 0 || height == 0) {
LOGE(“width or height cannot be zero!”);
}
const char *yuv_file_path = env->GetStringUTFChars(yuv_path, JNI_FALSE);
const char *h264_file_path = env->GetStringUTFChars(h264_path, JNI_FALSE);

if (!yuv_file_path) {
LOGE(“yuv path cannot be null”);
return -1;
}
if (!h264_file_path) {
LOGE(“h264 path cannot be null”);
return -1;
}
// 打开yuv
FILE *yuv_file = fopen(yuv_file_path, “rb”);
if (yuv_file == NULL) {
LOGE(“cannot open yuv file”);
return -1;
}
FILE *h264_file = fopen(h264_file_path, “wb”);
if (h264_file == NULL) {
LOGE(“cannot open h264 file”);
return -1;
}
// 设置x264处理的yuv格式默认为YUV420
int csp = X264_CSP_I420;
switch (yuv_csp) {
case 0:
csp = X264_CSP_I420;
break;
case 1:
csp = X264_CSP_I422;
break;
case 2:
csp = X264_CSP_I444;
break;
default:
csp = X264_CSP_I420;
}

LOGI(“the params is success:\n %dx%d %s %s:”, width, height, yuv_file_path, h264_file_path);

int frame_number = 0;
// 处理h264单元数据
int i_nal = 0;
x264_nal_t *nal = NULL;
// x264
x264_t *h = NULL;
x264_param_t *param = (x264_param_t *) malloc(sizeof(x264_param_t));
x264_picture_t *pic_in = (x264_picture_t *) (malloc(sizeof(x264_picture_t)));
x264_picture_t *pic_out = (x264_picture_t *) (malloc(sizeof(x264_picture_t)));

// 初始化编码参数
x264_param_default(param);
param->i_width = width;
param->i_height = height;
param->i_csp = csp;
// 配置处理级别
x264_param_apply_profile(param, x264_profile_names[2]);
// 通过配置的参数打开编码器
h = x264_encoder_open(param);

x264_picture_init(pic_out);
x264_picture_alloc(pic_in, param->i_csp, param->i_width, param->i_height);
// 编码前每一帧的字节大小
int size = param->i_width * param->i_height;

// 计算视频帧数
fseek(yuv_file, 0, SEEK_END);
switch (csp) {
case X264_CSP_I444:
// YUV444
frame_number = ftell(yuv_file) / (size * 3);
break;
case X264_CSP_I422:
// YUV422
frame_number = ftell(yuv_file) / (size * 2);
break;
case X264_CSP_I420:
//YUV420
frame_number = ftell(yuv_file) / (size * 3 / 2);
break;
default:
LOGE(“Colorspace Not Support.”);
return -1;
}
fseek(yuv_file, 0, SEEK_SET);
// 循环执行编码
for (int i = 0; i < frame_number; i++) {
switch (csp) {
case X264_CSP_I444:
fread(pic_in->img.plane[0], size, 1, yuv_file);
fread(pic_in->img.plane[1], size, 1, yuv_file);
fread(pic_in->img.plane[2], size, 1, yuv_file);
break;
case X264_CSP_I422:
fread(pic_in->img.plane[0], size, 1, yuv_file);
fread(pic_in->img.plane[1], size / 2, 1, yuv_file);
fread(pic_in->img.plane[2], size / 2, 1, yuv_file);
break;
case X264_CSP_I420:
fread(pic_in->img.plane[0], size, 1, yuv_file);
fread(pic_in->img.plane[1], size / 4, 1, yuv_file);
fread(pic_in->img.plane[2], size / 4, 1, yuv_file);
break;
}
pic_in->i_pts = i;
// 对每一帧执行编码
ret = x264_encoder_encode(h, &nal, &i_nal, pic_in, pic_out);
if (ret < 0) {
LOGE(“x264 encode error”);
return -1;
}
LOGI(“encode frame:%5d”, i);
// 将编码数据循环写入目标文件
for (int j = 0; j < i_nal; ++j) {
fwrite(nal[j].p_payload, 1, nal[j].i_payload, h264_file);
}
}

// 冲刷缓冲区,不执行可能造成数据不完整
int i = 0;
while (1) {
ret = x264_encoder_encode(h, &nal, &i_nal, NULL, pic_out);
if (ret == 0) {
break;
}
LOGD(“flush 1 frame”);
// 将编码数据循环写入目标文件
for (int j = 0; j < i_nal; ++j) {
fwrite(nal[j].p_payload, 1, nal[j].i_payload, h264_file);
}
i++;
}

x264_picture_clean(pic_in);
x264_encoder_close(h);
// 释放分配的空间
free(pic_in);
free(pic_out);
free(param);
// 关闭文件输入
fclose(yuv_file);
fclose(h264_file);

return ret;
}

这段代码有点长,而且还是C语言相关代码,读不懂没有关系,将上述代码直接粘贴到你的项目中即可。

注意:

  1. 当前程序只支持YUV420YUV422以及YUV444三种裸流处理。
  2. Java_com_coder_x264cmake_X264Encode_encode这个意思是指在com.coder.x264cmake的包名下的X264Encode类中的encode的方法。需要对应成你自己的完整路径。
  1. 配置CmakeLists.txt,一定要注意这个的配置,稍微一点点配置错误,就会出现如下错误

CmakeLists配置错误

为了防止大家配置错误,我将CmakeLists.txt的配置粘贴如下:

For more information about using CMake with Android Studio, read the

documentation: https://d.android.com/studio/projects/add-native-code.html

Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.10.2)

Declares and names the project.

project(“x264cmake”)

set( JNI_LIBS_DIR src/main/nativeLibs)

Creates and names a library, sets it as either STATIC

or SHARED, and provides the relative paths to its source code.

You can define multiple libraries, and CMake builds them for you.

Gradle automatically packages shared libraries with your APK.

add_library(
h264
STATIC
IMPORTED
)

set_target_properties(
h264
PROPERTIES IMPORTED_LOCATION
C M A K E S O U R C E D I R / {CMAKE_SOURCE_DIR}/ CMAKESOURCEDIR/{JNI_LIBS_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libx264.a
)

include_directories(src/main/cpp)

add_library(
h264-encode
SHARED
src/main/cpp/h264-encode.cpp
)

文末

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家

[外链图片转存中…(img-4aZ9xUbe-1715356927085)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值