Android 利用V4L2 预览MJPEG格式 USB camera

介绍

上一篇文章Android 利用V4L2 调用camera介绍了使用V4L2 接口预览camera的基本方法。目前接触过的usb camera支持的图像格式基本上只包括3种:

  • YUV
  • MJPEG
  • H264
    其中YUV是原始数据,MJPEG和H264都是压缩编码的数据。所以对于MJPEG和H264需要先解码为原始图像数据,才能给到android显示。这篇文章主要介绍对MJPEG数据的处理

准备

先看一下此次修改的效果图


GIF 2022-1-18 15-35-09.gif
  • UI
    让用户自己选择对应的格式和分辨率
  • 解码
    一般可采用的有opencv,ffmpeg,libyuv。此次采用libyuv,libyuv是一个谷歌的开源项目,跨平台,处理速度很快。针对此次MJPEG处理是比较合适的。libyuv解码MJPEG需要用到libjpeg

camera使用的基本流程在文章Android 利用V4L2 调用camera已经介绍过,

正常的流程主要是以下几步

  1. SurfaceView创建
  2. SurfaceView创建成功回调
  3. 打开camera
  4. camera打开成功回调
  5. 获取camera参数
  6. 弹框用户选择对应分辨率
  7. 设置对应pixformat和分辨率
  8. 开始预览
  9. 获取到MJPEG数据后,利用libyuv解码
  10. Android nativieWindow显示

此次大体流程没有变化,着重介绍修改的地方

让用户选择预览图像格式和分辨率

在camera打开成功的回调种,即 CameraStateCallback 的回调onOpened 获取usb camera支持的参数,camera参数格式可以参见文章Android 利用V4L2 调用camera中的获取camera的参数

 class CameraStateCallback implements IStateCallback {

        @Override
        public void onOpened() {
            Log.d(TAG, "onOpened");

            parameters = adCamera.getCameraParameters();

            pixformats = new String[parameters.size()];
            resolutions = new String[parameters.size()][];

            String sPixFormat;
            for (int i = 0; i < parameters.size(); i++) {
                Log.e(TAG, "format:" + parameters.get(i).pixFormat);
                switch (parameters.get(i).pixFormat) {
                    case YUYV:
                        sPixFormat = "YUYV";
                        break;
                    case MJPEG:
                        sPixFormat = "MJPEG";
                        break;
                    case H264:
                        sPixFormat = "H264";
                        break;
                    default:
                        sPixFormat = "UNKNOW";
                        break;
                }

                pixformats[i] = sPixFormat;

                int resolutionSize = parameters.get(i).frames.size();

                resolutions[i] = new String[resolutionSize];

                for (int j = 0; j < resolutionSize; j++) {
                    String resolution = parameters.get(i).frames.get(j).width + "*" + parameters.get(i).frames.get(j).height;
                    resolutions[i][j] = resolution;
                }
            }

            showDialog();
        }

将获取到的参数解析到以下两个数组中

    String[] pixformats;
    String[][] resolutions;

dialog采用加载ExpandableListView,这里就不详细介绍。
点击确认后,设置预览参数并开始预览

ret = adCamera.setPreviewParameter(previewWidth, previewHeight, parameters.get(pixClick).pixFormat);

surfaceView.setAspectRatio(previewWidth, previewHeight);
...
adCamera.setSurface(surfaceHolder);
...
cameraDataCallback = new CameraDataCallback();
adCamera.startPreview(cameraDataCallback);

libjpeg库的编译移植与使用

这里使用AS编译libJPEG-turbo源码

  • 新建Android工程libjpeg,并将libjpeg-turbo源码全部拷贝到src/main/cpp目录下


    libjpeg.png
  • 修改Android工程的build.gradle,配置libjpeg-turbo的CmakeLists.txt

defaultConfig {
        applicationId "com.test.libjpeg"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        externalNativeBuild {
            cmake {
                cppFlags ""
                // 配置编译的平台版本
                abiFilters "armeabi-v7a", "arm64-v8a"
            }
        }

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    externalNativeBuild {
        cmake {
            path "src\\main\\cpp\\CMakeLists.txt"//路径改为cpp文件夹下CMakeList的路径
        }

    }
  • 编译工程,生成对应的so和.h 文件
    编译过程很顺利,没碰到什么问题。倒是从网上下载下来的总是有编译错误,所以这里就不提供工程了。

将libjpeg 生成的so和h文件添加到AnV4L2Camera工程中

  • 在v4l2camera模块cpp文件下新建libjpeg文件夹,将libjpeg几个相关头文件拷贝到该目录下


    includelibjpeg.png

其中 jconfig.h 在工程libjpeg以下目录

./app/.cxx/cmake/debug/arm64-v8a/jconfig.h
./app/.cxx/cmake/debug/armeabi-v7a/jconfig.h

其他头文件都在在工程libjpeg以下目录

./app/src/main/cpp/
  • 在 v4l2camera模块新建文件夹src\main\jniLibs,将libjpeg.so拷贝到该文件夹下


    libjpegso.png
  • 修改v4l2camera模块 CMakeLists.txt

 # 指定libjpeg头文件的路径
include_directories(include libjpeg)

# 指定libjpeg动态库路径
set(jpeglibs "${CMAKE_SOURCE_DIR}/../jniLibs")

# 导入第三方库:libjpeg.so
add_library(libjpeg SHARED IMPORTED)
set_target_properties(libjpeg PROPERTIES
        IMPORTED_LOCATION "${jpeglibs}/${ANDROID_ABI}/libjpeg.so")

target_link_libraries( # Specifies the target library.
        v4l-android
        android
        libjpeg

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

libyuv移植

这里采用直接将libyuv源码导入到AnV4L2Camera工程中

  • 下载libyuv https://chromium.googlesource.com/libyuv/libyuv
  • 将 libyuv 源码 include 目录下的 libyuv 目录下的头文件和 libyuv.h 一起拷贝到 v4l2camera模块下的 src\main\cpp\include目录
  • 将 libyuv 源码 source 目录下的全部文件拷贝到 v4l2camera模块新建文件夹src\main\cpp\libyuv
libyuv.png
  • 修改v4l2camera模块 CMakeLists.txt
# 导入libyuv头文件路径
include_directories(include libjpeg)

# 打开宏HAVE_JPEG,libyuv才会去编译和使用libjpeg
add_definitions(-DHAVE_JPEG)

# 导入libyuv 源文件路径
file(GLOB src_files *.cpp  libyuv/source/*.cc)

libyuv解码MJPEG

        int src_width = 0;
        int src_height = 0;

        libyuv::MJPGSize(raw, rSize, &src_width, &src_height);

        //经图片保存,16进制查看保存的改格式为   64 82 35 ff    --   B G R A
        //stride 跨距, 它描述一行像素中, 该颜色分量所占的 byte 数目
        libyuv::MJPGToARGB(raw, rSize, preview, width * 4, src_width,
                                 src_height, width, height);

#ifdef SAVE_JPEG
        if (!WRITE_FILE) {
            const char *path = "/sdcard/argb.bmp"; // 路径
            bmp_write(preview, width, height, path);
            WRITE_FILE = true;
        }
#endif

        //WINDOW_FORMAT_RGBA_8888  排列顺序为 R G B A
        unsigned char temp;
        for (int i = 0; i < width * height * 4; i = i + 4) {
            temp = preview[i+2];
            preview[i+2] = preview[i];
            preview[i] = temp;
        }

raw是通过v4l2获取到的mjpeg格式数据,主要通过libyuv::MJPGToARGB将数据转换成rgba数据。 通过将转换后的数据保存成bmp,用hex格式打开发现,数据保存的格式为BGRA,这个可能windows上或bmp格式的数据就是用这种方式保存的,属于little endian。 android上WINDOW_FORMAT_RGBA_8888 排列顺序为 RGBA,所以还需要做下转换,颜色才能正常。

相关代码已经更新到demo https://github.com/yizhongliu/AnV4L2Camera

参考
https://blog.csdn.net/AndrExpert/article/details/100123845
https://blog.csdn.net/tyyj90/article/details/120294921

你可能感兴趣的:(Android 利用V4L2 预览MJPEG格式 USB camera)