在 Android 系统上, Camera 输出的图像一般为 NV21(YUV420SP 系列) 格式, 当我们想进行录像处理时, 会面临两个问题

问题 1

图像的旋转问题

  • 后置镜头: 需要旋转 90°
  • 前置镜头: 需要旋转 270° 然后再进行镜像处理

问题 2

处理好镜头的旋转后, 当我们尝试使用 MediaCodec 进行 H.264 的硬编时, 便会发现偏色的问题

这是因为 MediaCodec 的 COLOR_FormatYUV420SemiPlanar 格式为 NV12, 并非是 NV21, 虽然都是 YUV420SP 系列, 但他们的排列不同, 都是先存储 Y 的数据, NV21 是 vu 交替存储, NV12 是 uv 交替存储

NV21: yyyy yyyy vu vu
NV12: yyyy yyyy uv uv

为了解决这个问题, 对于这个问题网上有很多的解决思路, 我们可以在 Java 层使用进行数据操作, 不过经过测试之后发现, 在 Samsung S7 Edge 上, 录制 1080p

  • 旋转与镜像: 20mm
  • NV21 转 NV12: 16mm

消耗时长约为 40mm, 这也仅仅是勉强能够进行 25 帧的录制, 在使用 opencv 进行人脸识别或滤镜处理时, 能够感觉到明显的卡顿感

libyuv 便是 google 为了解决移动端 NV21 数据处理不便所提供的开源库, 它提供了旋转, 裁剪, 镜像, 缩放等功能

接下来看看 libyuv 的编译与使用

一. 环境

操作系统

MacOS Mojave version 10.14.5

Libyuv

git clone https://chromium.googlesource.com/libyuv/libyuv

NDK 版本

NDK16

cmake 版本

➜  ~ cmake -version
cmake version 3.14.5

二. 编译脚本

从 libyuv 的源码中, 可以看到 libyuv 已经提供了 CMakeLists.txt, 因此我们可以直接通过 cmake 生成 Makefile, 然后通过 make 对 Makefile 进行编译

ARCH=arm
ANDROID_ARCH_ABI=armeabi-v7a
NDK_PATH=/Users/sharrychoo/Library/Android/ndk/android-ndk-r16b
PREFIX=`pwd`/android/${ARCH}/${CPU}# cmake 传参
cmake -G"Unix Makefiles" \-DANDROID_NDK=${NDK_PATH} \-DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \-DANDROID_ABI=${ANDROID_ARCH_ABI} \-DANDROID_NATIVE_API_LEVE=16 \-DCMAKE_INSTALL_PREFIX=${PREFIX} \-DANDROID_ARM_NEON=TRUE \..# 生成动态库
make
make install

输出的 so 库

三. 代码编写

我们将 so 库和头文件拷贝到 AS 中, 便可以进行代码的编写了, 这里编写一个 Libyuv 的工具类, 方便后续使用

一) Java 代码

这里以 NV21 转 I420 为例

/*** 处理 YUV 的工具类** @author Sharry <a href="sharrychoochn@gmail.com">Contact me.</a>* @version 1.0* @since 2019-07-23*/
public class LibyuvUtil {static {System.loadLibrary("smedia-camera");}/*** 将 NV21 转 I420*/public static native void convertNV21ToI420(byte[] src, byte[] dst, int width, int height);......
}

二) native 实现

这里以将 NV21 转 I420 为例

namespace libyuv_util {void convertI420ToNV12(JNIEnv *env, jclass, jbyteArray i420_src, jbyteArray nv12_dst, int width,int height) {jbyte *src = env->GetByteArrayElements(i420_src, NULL);jbyte *dst = env->GetByteArrayElements(nv12_dst, NULL);// 执行转换 I420 -> NV12 的转换LibyuvUtil::I420ToNV12(src, dst, width, height);// 释放资源env->ReleaseByteArrayElements(i420_src, src, 0);env->ReleaseByteArrayElements(nv12_dst, dst, 0);}}void LibyuvUtil::NV21ToI420(jbyte *src, jbyte *dst, int width, int height) {// NV21 参数jint src_y_size = width * height;jbyte *src_y = src;jbyte *src_vu = src + src_y_size;// I420 参数jint dst_y_size = width * height;jint dst_u_size = dst_y_size >> 2;jbyte *dst_y = dst;jbyte *dst_u = dst + dst_y_size;jbyte *dst_v = dst + dst_y_size + dst_u_size;/*** <pre>* int NV21ToI420(const uint8_t* src_y,*          int src_stride_y,*          const uint8_t* src_vu,*          int src_stride_vu,*          uint8_t* dst_y,*          int dst_stride_y,*          uint8_t* dst_u,*          int dst_stride_u,*          uint8_t* dst_v,*          int dst_stride_v,*          int width,*          int height);* </pre>* <p>* stride 为颜色分量的跨距: 它描述一行像素中, 该颜色分量所占的 byte 数目, YUV 每个通道均为 1byte(8bit)* <p>* stride_y: Y 是最全的, 一行中有 width 个像素, 也就有 width 个 Y* stride_u: YUV420 的采样为 Y:U:V = 4:1:1, 从整体的存储来看, 一个 Y 分量的数目为 U/V 的四倍* 但从一行上来看, width 个 Y, 它会用到 width/2 个 U* stride_v: 同 stride_u 的分析方式*/libyuv::NV21ToI420((uint8_t *) src_y, width,(uint8_t *) src_vu, width,(uint8_t *) dst_y, width,(uint8_t *) dst_u, width >> 1,(uint8_t *) dst_v, width >> 1,width, height);
}

可以看到方法的调用也非常的简单, 只需要传入相关参数即可, 其中有个非常重要的参数, stride 跨距, 它描述一行像素中, 该颜色分量所占的 byte 数目

  • YUV420 系列

    • NV21

      • Y: 跨距为 width
      • VU: 跨距为 width
    • I420P(YU12):
      • Y: 跨距为 width
      • U: 跨距为 width/2
      • V: 跨距为 width/2
  • ABGR: 跨距为 4 *width

总结

通过 libyuv 进行旋转镜像转码等操作, 其时长如下

  • 旋转镜像: 5~8mm
  • NV21 转 NV12: 0~3mm

可以看到比起 java 代码, 几乎快了 3 倍, 这已经能够满足流畅录制的需求了

Android 端处理 YUV 数据 - Libyuv 的编译与使用相关推荐

  1. Unity 渲染YUV数据 ---- 以Unity渲染Android Camera数据为例子

    1 背景 一般Unity都是RGB直接渲染的,但是总有特殊情况下,需要渲染YUV数据.比如,Unity读取Android的Camera YUV数据,并渲染.本文就基于这种情况,来展开讨论. Unity ...

  2. 【Flutter】Flutter 混合开发 ( Flutter 与 Native 通信 | Android 端实现 EventChannel 通信 )

    文章目录 前言 一.Android 端 EventChannel 构造函数 二.Android 端 setStreamHandler 方法 三.Android 端实现 EventChannel 通信步 ...

  3. 【错误记录】Flutter 混合开发报错 ( Android 端与 Flutter 端 EventChannel 初始化顺序错误导致无法通信 | EventChannel 通信流程 )

    文章目录 一. 报错信息 二. Android 端与 Flutter 端 EventChannel 注册与监听流程 三. 解决方案 一. 报错信息 在 Android 端初始化 EventChanne ...

  4. Android音视频学习系列(六) — 掌握视频基础知识并使用OpenGL ES 2.0渲染YUV数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  5. 【Android 逆向】Android 逆向通用工具开发 ( Android 端远程命令工具 | Android 端可执行程序的 main 函数操作 | TCP 协议服务器建立 | 接收客户端数据 )

    文章目录 前言 一.Android 端可执行程序的 main 函数操作 二.Android 端 TCP 协议服务器建立 三.Android 端接收 PC 端传来的数据 四.博客资源 前言 本篇博客重点 ...

  6. 【Android RTMP】x264 图像数据编码 ( NV21 格式中的 YUV 数据排列 | Y 灰度数据拷贝 | U 色彩值数据拷贝 | V 饱和度数据拷贝 | 图像编码操作 )

    文章目录 安卓直播推流专栏博客总结 一. NV21 图像数据中的 YUV 数据简介 二.向 x264 编码图片 三. 提取 NV21 数据中的灰度数据 Y 四. 提取 NV21 数据中的饱和度数据 U ...

  7. android yuv旋转,针对移动端摄像头yuv旋转、裁剪、镜像、格式转换算法的实现

    存在问题 移动端录像在yuv数据上存在如下问题: 无论android还是ios都不能直接从摄像头取出颜色空间为i420的数据,所以在编码前需要进行格式转换. 而且由于所取图像得分辨率必须是摄像头所提供 ...

  8. Android用surface直接显示yuv数据(三)

    本文用Java创建UI并联合JNI层操作surface来直接显示yuv数据(yv12),开发环境为Android 4.4,全志A23平台. package com.example.myyuvviewe ...

  9. Android解码输出yuv,Android OpenGLES2.0 直接导出YUV420数据

    Android OpenGLES2.0中提供的glReadPixels方法提供的格式只有RGB的几种格式,但是这并不妨碍我们导出YUV格式的数据,因为不管是RGBA还是YUV,都不是glReadPix ...

最新文章

  1. mysql取消外码约束_MySQL 关闭子表的外键约束检察方法
  2. php libxml 宝塔,宝塔编译安装各个版本php无法安装
  3. fisher线性判别算法python_干货|机器学习算法之线性判别分析
  4. 如何自建appender扩展Log4j框架
  5. Linux系统分区及LVM使用(一)
  6. MySQL手册chm格式文档
  7. android 模拟点击屏幕,按键精灵后台简明教程(后台找色,后台鼠标点击等)
  8. 黑客知识之7种DoS攻击方法简述
  9. 2018 CTCS第五届“智能出行”中国企业差旅合规高峰论坛即将开幕
  10. 三年级语文计算机之父教学反思,三年级语文教学反思15篇
  11. 固态硬盘安装--系统迁移--设置引导启动项
  12. Docker 相关配置文件路径
  13. 【路径规划】基于蚁群算法求解运钞车路径规划VRPSD问题matlab代码
  14. ES 7.6.2集群迁移(从一套ES集群迁移数据到另一套集群)
  15. SALOME软件下载
  16. 基于 javaagent + javassist 一步步实现调用链系统 (2)
  17. 使计算机热机重启的键应该是,电脑正常启动后在重启就不能启动了断电在开又...
  18. 谷歌翻译不能用了,怎么办
  19. 一个简单的PE感染病毒
  20. Pytorch实战2:ResNet-18实现Cifar-10图像分类(测试集分类准确率95.170%)

热门文章

  1. BUAA(2021春)实验:树的构造与遍历——根据提示循序渐进(可惜提示有问题Ծ‸Ծ)
  2. 微信公众平台测试帐号申请最新地址
  3. 用html画布做扇形,jquery canvas 画扇形
  4. Linux C/C++编程:netstat分析tcp状态转移(socket通信)
  5. 推荐几个无需注册免费的PPT模板下载网站
  6. Python:根据身高、体重计算BMI指数
  7. 安卓玩机搞机技巧综合资源-----卸载内置软件 获取root权限 刷写第三方ROM【六】
  8. 原始socket实现局域网ARP欺骗
  9. 迷你世界计算机原理,迷你世界先遣服免费账号永久2020
  10. java, c 亦或用法