libyuv是Google开源的实现YUV数据格式转换,旋转,缩放和镜像等操作的库。
编译
使用cmake+ndk来编译.so文件
在新建的AS工程中建一个module--libyuv,该module下的源码目录下新建cpp目录,将下载的libyuv相关文件导入到cpp目录中:
在src目录下新建CMakeLists.txt,脚本内容为:
cmake_minimum_required(VERSION 3.4.1)
include_directories(src/main/cpp/libyuv/include)
add_subdirectory(src/main/cpp/libyuv ./build)
# 查找指定目录下的所有源文件,然后将结果存进指定变量名
aux_source_directory(src/main/cpp SRC_FILE)
add_library(yuvutil
SHARED
${SRC_FILE}
)
find_library(log-lib log)
target_link_libraries(yuvutil ${log-lib} yuv)
在libyuv这个module的build.gradle配置中指定CMakeLists.txt的路径:
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
}
}
在local.properties文件中指定ndk路径:
ndk.dir=/.../Android/sdk/ndk/21.0.6011959
然后build这个module就OK了
使用
ibyuv的旋转,缩放,镜像等操作都是通过I420格式来进行的,所以对于不是I420格式的yuv输入首先要进行格式转换。
应用层通过jni的方式来使用libyuv,此处以NV21转I420为例进行演示。
java层中提供一个native方法:
/**
* Convert NV21 to I420
* @param nv21Src NV21 input data
* @param width source width
* @param height source height
* @param i420Dst I420 output data
*/
public static native void yuvNV21ToI420(byte[] nv21Src, int width, int height, byte[] i420Dst);
在libyuv的covert.cc文件中提供了NV21转I420的api
// Convert NV12 to I420.
LIBYUV_API
int NV12ToI420(const uint8* src_y,
int src_stride_y,
const uint8* src_uv,
int src_stride_uv,
uint8* dst_y,
int dst_stride_y,
uint8* dst_u,
int dst_stride_u,
uint8* dst_v,
int dst_stride_v,
int width,
int height) {
return X420ToI420(src_y, src_stride_y, src_stride_y, src_uv, src_stride_uv,
dst_y, dst_stride_y, dst_u, dst_stride_u, dst_v,
dst_stride_v, width, height);
}
jni实现:
extern "C"
JNIEXPORT void JNICALL
Java_com_libyuv_util_YuvUtil_yuvNV21ToI420(JNIEnv *env, jclass jcls, jbyteArray nv21Src,
jint width, jint height, jbyteArray i420Dst) {
jbyte *src_nv21_data = env->GetByteArrayElements(nv21Src, NULL);
jbyte *dst_i420_data = env->GetByteArrayElements(i420Dst, NULL);
NV21ToI420(src_nv21_data, width, height, dst_i420_data);
env->ReleaseByteArrayElements(i420Dst, dst_i420_data, 0);
}
// NV21:YYYY YYYY VU VU
// I420: YYYY YYYY UU VV
void NV21ToI420(jbyte *src_nv21_data, jint width, jint height, jbyte *dst_i420_data) {
jbyte *src_y_plane = src_nv21_data;
jbyte *src_uv_plane = src_nv21_data + width * height;
jbyte *dst_y_plane = dst_i420_data;
jbyte *dst_u_plane = dst_i420_data + width * height;
jbyte *dst_v_plane = dst_u_plane + (width * height / 4);
libyuv::NV12ToI420(
(const uint8_t *) src_y_plane, width,
(const uint8_t *) src_uv_plane, width,
(uint8_t *) dst_y_plane, width,
(uint8_t *) dst_v_plane, width / 2,
(uint8_t *) dst_u_plane, width / 2,
width, height);
}
输入都统一为I420的格式后,就可以进行愉快的旋转,缩放和镜像等操作了。
常见的yuv操作都做个封装Util,代码:
https://github.com/sifutang/libyuv.git
推荐阅读:
OpenGL ES 实现实时音频的可视化
或许是迄今为止第一篇讲解 fps 计算原理的文章吧
推荐几个堪称教科书级别的 Android 音视频入门项目
OpenGL 实现视频编辑中的转场效果
觉得不错,点个在看呗~