首页 > 其他分享 >使用Android NDK Camera2经验总结

使用Android NDK Camera2经验总结

时间:2023-04-04 21:11:11浏览次数:52  
标签:NDK res Camera2 AImage len AImageReader Android data image

2023年03月30日 NDK Camera

参考文章:https://blog.csdn.net/daihuimaozideren/article/details/101235393

项目链接:https://github.com/qi-xmu/Android-ndk-camera-zh.git

项目基于官方NDK Camera2 texture sample,添加了详细的中午注释,点个Star!!!谢谢!

第一部分 程序入口逻辑

首先需要做相机权限检查和相机的类型检查,这里使用的相机必须满足Camera2的最低要求。

然后程序的启动流程如下:

img

红色部分需要通过NDK实现对应的功能。


第二部分 获取相机设置信息

img

以上图像中存在几个重要的变量:

  • ACameraManager:使用ACameraManager_create构建,使用完成需要ACameraManager_delete删除释放内存。
  • ACameraList:通过ACameraManage_getCameraIdList使用ACameraManager获取手机所有的相机设备(包括前置镜头,后置镜头,外置镜头)使用完成之后需要ACameraManager_deleteCameraList释放内存。
  • CameraIDACameraIdList类型中存在cameraIds,作为相机的唯一标识符。
  • ACameraMetadata:通过ACameraManager_getCameraCharacteristics使用CameraIDACameraManager获取相机的元数据。使用完成之后需要通过ACameraMetadata_free释放内存。
  • 通过ACameraMetadata_getAllTags可以获取所有的相机的标签数量和标签值。这个Tags是一个键值对系统,可以根据标签获取相应的数据,也可以通过设置标签纸去更改相机的参数。
  • 通过ACameraMetadata_getConstEntry使用ACameraMetadata和标签名可以获取对应的标签值。
  • 通过ACaptureRequest_setEntry_xx使用request和标签名可以设置对应的标签值。

第三部分 启动相机会话

img

各个对象之间的关系:

img

最终得到的是ACameraRequestACaptureSessionOutputContainer两个对象,通过ACameraCaptureSession_setRepeatingRequest可以实现不断地相同地相机请求,达到相机预览的效果。使用ACameraCaptureSession_stopRepeating函数停止预览。

以上所有变量地申请都需要最后通过xxx_delete方法释放内存。防止内存泄露造成错误。

第四部分 获取图像数据

img

图像读取思路

设置图像格式和图像回调的AImageReader_new获取一个AImageReader对象。该对象再通过第三部分的内容设置为相机的数据输出。

成功获取图像数据之后,数据处理代码示例如下:

void onImageAvailable(void *context, AImageReader *reader) {
    media_status_t res;
    // 获取图像的格式
    int32_t img_fmt;
    int32_t width, height;
    res = AImageReader_getFormat(reader, &img_fmt);
    if (res) LOG_ERR("AImageReader_getFormat error");
    res = AImageReader_getWidth(reader, &width);
    if (res) LOG_ERR("AImageReader_getFormat error");
    res = AImageReader_getHeight(reader, &height);
    if (res) LOG_ERR("AImageReader_getWidth error");

    AImage *image;
    res = AImageReader_acquireNextImage(reader, &image);
    if (res) LOG_ERR("AImageReader_acquireNextImage  error");

    // 获取图像的时间戳
    int64_t image_timestamp;
    AImage_getTimestamp(image, &image_timestamp);
    uint8_t *y_data, *u_data, *v_data;
    int32_t y_len = 0, u_len = 0, v_len = 0;
    if (AIMAGE_FORMAT_YUV_420_888 == img_fmt) {
        // 获取各个分量的指针,这个地方存在一个问题,这里的数据结构如下
        // YY ... YYYY (repeat width * height) U V U V ..... (total width * height /2);
        // 数据总长度为 width * height * 3 / 2
        res = AImage_getPlaneData(image, 0, &y_data, &y_len);
        if (res) LOG_ERR("AImage_getPlaneData 0 error");
        res = AImage_getPlaneData(image, 1, &u_data, &u_len);
        if (res) LOG_ERR("AImage_getPlaneData 1 error");
        res = AImage_getPlaneData(image, 2, &v_data, &v_len);
        if (res) LOG_ERR("AImage_getPlaneData 2 error");
//        LOG_WARN("0bit %x %x %x %x", y_data[0], y_data[1], y_data[2], y_data[3]);
    } else {
        // 其他格式
        int32_t image_buffer_len = 0;
        uint8_t *image_raw_buffer;
        res = AImage_getPlaneData(image, 0, &image_raw_buffer, &image_buffer_len);
        if (res) LOG_ERR("AImage_getPlaneData 0 error");
    }

    // 获取一行的像素长度 >= width (内存对齐的原因)
    int32_t rowStride;
    AImage_getPlaneRowStride(image, 0, &rowStride);

    // 这里传入的上下文 为 CameraEngine对象
    auto *cam_eng = reinterpret_cast<CameraEngine *>(context);
    //  获取 surface 生成的 NativeWindow对象 用于前端显示
    ANativeWindow *window = cam_eng->GetSurfaceNativeWindow();

    // 获取图像图像的格式
    ANativeWindow_setBuffersGeometry(window, width, height, img_fmt);


    ANativeWindow_Buffer aw_buffer;
    ANativeWindow_acquire(window);
    ANativeWindow_lock(window, &aw_buffer, nullptr);
    auto *bits = reinterpret_cast<uint8_t *>(aw_buffer.bits);
    if (AIMAGE_FORMAT_YUV_420_888 == img_fmt) {
        memcpy(bits, y_data, y_len + u_len + 1);
    } else if (AIMAGE_FORMAT_JPEG == img_fmt) {
//        memcpy(bits, image_raw_buffer, image_buffer_len);
        LOG_WARN("Can not directly show jpeg.");
    } else if (AIMAGE_FORMAT_RGBA_8888 == img_fmt) {

    }

    ANativeWindow_unlockAndPost(window);
    ANativeWindow_release(window);
    AImage_delete(image);
}

标签:NDK,res,Camera2,AImage,len,AImageReader,Android,data,image
From: https://www.cnblogs.com/qi-xmu/p/17287888.html

相关文章

  • Android 原生 SQLite 数据库的一次封装实践
    作者:LiBingyan本文主要讲述原生SQLite数据库的一次ORM封装实践,给使用原生数据库操作的业务场景(如:本身是一个SDK)带来一些启示和参考意义,以及跟随框架的实现思路对数据库操作、APT、泛型等概念更深一层的理解。实现思路:通过动态代理获取请求接口参数进行SQL拼凑,并以接口返回值(泛型)......
  • Android 加载图片占用内存分析
    作者:XuJie不同Android版本,对一张图片的内存处理方式是不一样的,使用不正确会导致OOM的发生,这篇文章带你梳理内存占用情况,选择适合你的图片加载模式,解决OOM问题。一、背景你知道吗一张5.48MB,宽高像素为4896*6528的24位的静态图片,放在Android工程目录下面的res/drawable-[density]/......
  • android四大组件
    Android开发的四大组件,本文主要分为一、Activity详解二、Service详解三、BroadcastReceiver详解四、ContentProvider详解外加一个重要组件intent的详解。一、Activity详解Activty的生命周期的也就是它所在进程的生命周期。 一个Activity的启动顺序:onCreate()——>onStart()——......
  • 【GiraKoo】重置Android Studio环境的几个方案
    【GiraKoo】重置AndroidStudio环境的几个方案AndroidStudio经常在编译时,发现一些奇奇怪怪的编译/运行问题。明明是很小的改动,但是出现了一些不相关的错误。搞不清楚究竟是什么原因导致的。这时候,就需要考虑重置AndroidStudio环境的几个方案。InvalidateCaches在"File"菜......
  • Android 手把手教您自定义ViewGroup(一)
    本文出自:【张鸿洋的博客】最近由于工作的变动,导致的博客的更新计划有点被打乱,希望可以尽快脉动回来~今天给大家带来一篇自定义ViewGroup的教程,说白了,就是教大家如何自定义ViewGroup,如果你对自定义ViewGroup还不是很了解,或者正想学习如何自定义,那么你可以好好看看这篇博客。1、......
  • 一手遮天 Android - view(媒体类): 截图
    项目地址https://github.com/webabcd/AndroidDemo作者webabcd一手遮天Android-view(媒体类):截图示例如下:/view/media/ScreenshotDemo1.kt/***截图*/packagecom.webabcd.androiddemo.view.mediaimportandroid.graphics.Bitmapimportandroid.graphics.Can......
  • 一手遮天 Android - view(媒体类): MediaPlayer(在 SurfaceView 上播放)
    项目地址https://github.com/webabcd/AndroidDemo作者webabcd一手遮天Android-view(媒体类):MediaPlayer(在SurfaceView上播放)示例如下:/view/media/MediaPlayerDemo1.kt/***MediaPlayer(在SurfaceView上播放)**注:无法对SurfaceView截图,如果需要对视频截图......
  • 高效的Android布局
    EfficientAndroidLayouts每一个视图最大的效率!查看/下载幻灯片介绍我已经做了大约七年的Android开发,首先在旅游App公司,然后Expedia,现在目前在Trello。这个演讲是关于高效的Android布局,当我写它,我发现是我真正感兴趣的不是那么多的性能方面的效率,但有作为一个开发者的抛砖......
  • android 权限
    1.AIDandroid系统沿用了Linux的UID/GID权限模型,但并没有使用传统的passws和group文件来存储用户和用户组的认证凭据,作为代替,Android定义了从名称到AndroidID(AID)的映射表。system/core/include/private/android_filesystem_config.h#defineAID_ROOT0/*traditional......
  • Android如何为某个APK开启代码混淆机制
    1.修改该模块的Android.mk文件,添加如下内容:LOCAL_PROGUARD_ENABLED:=customLOCAL_PROGUARD_FLAG_FILES:=proguard.flags2.编写一个文本文件,将其命名为proguard.flags,并将该文件放到与该模块的Android.mk相同的目录下;该文件开头部分内容需要填写:......