找回密码
 立即注册
首页 业界区 安全 使用 Android NDK 获取 YUV420p摄像头原始数据 ...

使用 Android NDK 获取 YUV420p摄像头原始数据

何玲 3 天前
使用 Android NDK 获取 YUV420p摄像头原始数据

首先frameworks/av/camera/Camera.cpp已经过时了不要再使用它了, 当然想要更换旧的Camera的成本也不小,一般公司也不会做.
先介绍一一些常见的数据格式,然后介绍一下使用方式即可,然后下篇文件在探索一下源码.
脉络大概如下:
CameraManager → CameraService → Camera HAL v3 → Sensor/Driver.
常见的视频原始数据格式

本质上视频就是一张一张的图片,利用人眼视觉暂留的原理,24帧率的时候人眼就会无法辨别出单幅的静态画面.
编码就是利用算法算出每张图片之间的关系然后进行压缩.
解码就是一个逆向的过程,将压缩后的数据利用逆向算法恢复成一张一张的图片,然后播放.
yuv420p

最常见得
这个是最常见的.举个例子:
4x2像素的图片存储格式如下:
首先Y分量和像素一样,如下:
YYYY
YYYY
接着是U分量,4个Y分量共用一个U分量.
UU
接着是V分量,同理
VV
最终在内存中如下:
  1. YYYY
  2. YYYY
  3. UU
  4. VV
复制代码
5x3像素的图片存储格式如下:
首先Y分量和像素一样,如下:
  1. YYYYYY
  2. YYYY
  3. UU
  4. VV
复制代码
他们一共在内存中占用15 + 2 + 2 = 19字节.
YU12
  1. YYYYYYYYYYYYYYYYYYYYYYYYYYYY
  2. YYYY
  3. UU
  4. VVUUVV
复制代码
YU21
  1. YYYY
  2. YYYY
  3. YYYY
  4. YYYY
  5. YYYY
  6. YYYY
  7. YYYY
  8. YYYY
  9. VV
  10. UU
  11. VV
  12. UU
复制代码
yuv420sp

它和yuv420p得区别在于前者UV是顺序存储,后者是交替存储.
yuv420sp分为NV12和NV21
NV12
4x8
  1. YYYY
  2. YYYY
  3. YYYY
  4. YYYY
  5. YYYY
  6. YYYY
  7. YYYY
  8. YYYY
  9. UV
  10. UV
  11. UV
  12. UV
复制代码
NV21
4x8
  1. YYYY
  2. YYYY
  3. YYYY
  4. YYYY
  5. YYYY
  6. YYYY
  7. YYYY
  8. YYYY
  9. VU
  10. VU
  11. VU
  12. VU
复制代码
源码封装

cmake
  1. # For more information about using CMake with Android Studio, read the
  2. # documentation: https://d.android.com/studio/projects/add-native-code.html.
  3. # For more examples on how to use CMake, see https://github.com/android/ndk-samples.
  4. # Sets the minimum CMake version required for this project.
  5. cmake_minimum_required(VERSION 3.22.1)
  6. # Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
  7. # Since this is the top level CMakeLists.txt, the project name is also accessible
  8. # with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
  9. # build script scope).
  10. project(openslLearn VERSION 0.1.0 LANGUAGES C CXX)
  11. # ✅ 设置 C++ 标准
  12. set(CMAKE_CXX_STANDARD 23)  # 使用 C++26 标准
  13. set(CMAKE_CXX_STANDARD_REQUIRED ON)  # 强制使用指定标准
  14. set(CMAKE_CXX_EXTENSIONS OFF)        # 禁用编译器扩展(使用纯标准)
  15. # Creates and names a library, sets it as either STATIC
  16. # or SHARED, and provides the relative paths to its source code.
  17. # You can define multiple libraries, and CMake builds them for you.
  18. # Gradle automatically packages shared libraries with your APK.
  19. #
  20. # In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
  21. # the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
  22. # is preferred for the same purpose.
  23. #
  24. # In order to load a library into your app from Java/Kotlin, you must call
  25. # System.loadLibrary() and pass the name of the library defined here;
  26. # for GameActivity/NativeActivity derived applications, the same library name must be
  27. # used in the AndroidManifest.xml file.
  28. # 第一个库
  29. # 查找源文件
  30. file(GLOB_RECURSE LEARN01_SOURCES CONFIGURE_DEPENDS
  31.         "src/learn01/*.cpp"
  32.         "src/learn01/*.c"
  33. )
  34. add_library(${CMAKE_PROJECT_NAME} SHARED ${LEARN01_SOURCES})
  35. # 设置头文件包含路径
  36. target_include_directories(${CMAKE_PROJECT_NAME}
  37.         PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/learn01
  38.         PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/logging
  39. )
  40. # Specifies libraries CMake should link to your target library. You
  41. # can link libraries from various origins, such as libraries defined in this
  42. # build script, prebuilt third-party libraries, or Android system libraries.
  43. target_link_libraries(${CMAKE_PROJECT_NAME}
  44.     # List libraries link to the target library
  45.     android
  46.     log
  47.     OpenSLES
  48. )
  49. # 新增第二个库 (openslLearn2)
  50. file(GLOB_RECURSE LEARN02_SOURCES CONFIGURE_DEPENDS
  51.         "src/learn02/*.cpp"
  52.         "src/learn02/*.c"
  53.         "src/sqlite/*.cpp"
  54.         "src/sqlite/*.c"
  55. )
  56. set(LIBRARY_NAME2 ${CMAKE_PROJECT_NAME}2)
  57. message("LIBRARY_NAME2: ${LIBRARY_NAME2}")
  58. add_library(${LIBRARY_NAME2} SHARED ${LEARN02_SOURCES})  # 使用不同源文件
  59. target_include_directories(${LIBRARY_NAME2}
  60.         PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/sqlite
  61.         PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/learn02
  62.         PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/logging
  63. )
  64. find_package (oboe REQUIRED CONFIG)
  65. target_link_libraries(${LIBRARY_NAME2}
  66.         android
  67.         log
  68.         aaudio
  69.         oboe::oboe
  70.         camera2ndk
  71.         mediandk
  72. )
复制代码
头文件
  1. //
  2. // Created by 29051 on 2025/10/25.
  3. //
  4. #ifndef OPENSL_LEARN_CAMERA_HPP
  5. #define OPENSL_LEARN_CAMERA_HPP
  6. extern "C" {
  7. #include <camera/NdkCameraManager.h>
  8. #include <media/NdkImageReader.h>
  9. }
  10. #include <string>
  11. #include <fstream>
  12. #include "logging.hpp"
  13. class NDKCamera {
  14. private:
  15.     int mWidth;
  16.     int wHeight;
  17.     ACameraManager *aCameraManager = nullptr;
  18.     ACameraDevice *device = nullptr;
  19.     ACameraCaptureSession *session = nullptr;
  20.     AImageReader *aImageReader = nullptr;
  21.     ACaptureSessionOutputContainer *aCaptureSessionOutputContainer = nullptr;
  22.     ACaptureSessionOutput *sessionOutput = nullptr;
  23.     std::string yuvPath;
  24.     std::ofstream *yuvStream = nullptr;
  25. public:
  26.     NDKCamera(int width, int height, std::string yuvPath);
  27.     ~NDKCamera();
  28.     /**
  29.      * Capabilities 功能
  30.      */
  31.     void printCameraCapabilities(const char * cameraId);
  32. };
  33. #endif //OPENSL_LEARN_CAMERA_HPP
复制代码
源文件
  1. //
  2. // Created by 29051 on 2025/10/25.
  3. //
  4. #include "NDKCamera.hpp"
  5. #include <utility>
  6. const char * const TAG = "NDKCamera";
  7. /**
  8. * CameraManager → CameraService → Camera HAL v3 → Sensor/Driver
  9. * @param width
  10. * @param height
  11. */
  12. NDKCamera::NDKCamera(int width, int height, std::string yuvPath) : mWidth(width), wHeight(height), yuvPath(std::move(yuvPath)) {
  13.     logger::info(TAG, "width: %d, height: %d, yuvPath: %s", this -> mWidth, this -> wHeight, this -> yuvPath.c_str());
  14.     this->yuvStream = new std::ofstream(this->yuvPath, std::ios::binary);
  15.     if (!this->yuvStream->is_open()){
  16.         logger::error(TAG, "文件打开失败...");
  17.         return;
  18.     }
  19.     aCameraManager = ACameraManager_create();
  20.     if (aCameraManager == nullptr){
  21.         logger::error(TAG, "aCameraManager is null");
  22.         return;
  23.     }
  24.     ACameraIdList *cameraIdList = nullptr;
  25.     camera_status_t status = ACameraManager_getCameraIdList(aCameraManager, &cameraIdList);
  26.     if (status != ACAMERA_OK){
  27.         logger::error(TAG, "开启 getCameraIdList 失败");
  28.         return;
  29.     }
  30.     if (cameraIdList->numCameras <= 0){
  31.         logger::error(TAG, "此设备没有摄像头");
  32.         return;
  33.     }
  34.     for(int i = 0; i < cameraIdList->numCameras; i ++ ){
  35.         logger::info(TAG, "index: %d, cameraId: %s", i, cameraIdList->cameraIds[i]);
  36.     }
  37.     const char* cameraId = cameraIdList->cameraIds[1];
  38.     this->printCameraCapabilities(cameraId);
  39.     ACameraDevice_StateCallbacks deviceStateCallbacks = {
  40.             .context = nullptr,
  41.             .onDisconnected = [](void*, ACameraDevice* aCameraDevice) -> void {},
  42.             .onError = [](void*, ACameraDevice* aCameraDevice, int errorCode) -> void {},
  43.     };
  44.     status = ACameraManager_openCamera(aCameraManager, cameraId, &deviceStateCallbacks, &device);
  45.     if (status != ACAMERA_OK){
  46.         logger::error(TAG, "开启 camera 失败");
  47.         return;
  48.     }
  49.     media_status_t mediaStatus = AImageReader_new(width, height, AIMAGE_FORMAT_YUV_420_888, 4, &aImageReader);
  50.     if (mediaStatus != AMEDIA_OK){
  51.         logger::error(TAG, "AImageReader_new 失败");
  52.         return;
  53.     }
  54.     AImageReader_ImageListener imageListener = {
  55.             .context = this,
  56.             .onImageAvailable = [](void* context, AImageReader* reader) -> void {
  57.                 AImage *image = nullptr;
  58.                 media_status_t mediaStatus = AImageReader_acquireNextImage(reader, &image);
  59.                 if (mediaStatus != AMEDIA_OK || image == nullptr){
  60.                     logger::error(TAG, "获取当前yuv帧失败");
  61.                     AImage_delete(image);
  62.                     return;
  63.                 }
  64.                 int32_t width = 0, height = 0;
  65.                 mediaStatus = AImage_getWidth(image, &width);
  66.                 if (mediaStatus != AMEDIA_OK || image == nullptr){
  67.                     logger::error(TAG, "获取当前yuv帧宽度失败");
  68.                     AImage_delete(image);
  69.                     return;
  70.                 }
  71.                 mediaStatus = AImage_getHeight(image, &height);
  72.                 if (mediaStatus != AMEDIA_OK || image == nullptr){
  73.                     logger::error(TAG, "获取当前yuv帧高度失败");
  74.                     AImage_delete(image);
  75.                     return;
  76.                 }
  77.                 // ==========
  78.                 const auto *ndkCamera = reinterpret_cast<NDKCamera*>(context);
  79.                 for (int plane = 0; plane < 3; ++plane) {
  80.                     uint8_t* planeData = nullptr;
  81.                     int planeDataLen = 0;
  82.                     if (AImage_getPlaneData(image, plane, &planeData, &planeDataLen) != AMEDIA_OK) {
  83.                         logger::error(TAG, "AImage_getPlaneData failed plane=%d", plane);
  84.                         AImage_delete(image);
  85.                         return;
  86.                     }
  87.                     int rowStride = 0, pixelStride = 0;
  88.                     AImage_getPlaneRowStride(image, plane, &rowStride);
  89.                     AImage_getPlanePixelStride(image, plane, &pixelStride);
  90.                     int planeWidth = (plane == 0) ? width : (width + 1) / 2;
  91.                     int planeHeight = (plane == 0) ? height : (height + 1) / 2;
  92.                     // 按行按 pixelStride 写入,确保是连续的 Y then U then V
  93.                     for (int y = 0; y < planeHeight; ++y) {
  94.                         const uint8_t* rowPtr = planeData + y * rowStride;
  95.                         if (pixelStride == 1) {
  96.                             // 直接写 planeWidth 字节
  97.                             ndkCamera->yuvStream->write(reinterpret_cast<const char*>(rowPtr), planeWidth);
  98.                         } else {
  99.                             // 需要按 pixelStride 抽取
  100.                             for (int x = 0; x < planeWidth; ++x) {
  101.                                 ndkCamera->yuvStream->put(rowPtr[x * pixelStride]);
  102.                             }
  103.                         }
  104.                     }
  105.                 }
  106.                 AImage_delete(image);
  107.                 logger::info(TAG, "yuv width: %d, height: %d", width, height);
  108.             },
  109.     };
  110.     AImageReader_setImageListener(aImageReader, &imageListener);
  111.     ANativeWindow* window = nullptr;
  112.     mediaStatus = AImageReader_getWindow(aImageReader, &window);
  113.     if (mediaStatus != AMEDIA_OK){
  114.         logger::error(TAG, "AImageReader_getWindow 失败");
  115.         return;
  116.     }
  117.     ACaptureRequest *request = nullptr;
  118.     status = ACameraDevice_createCaptureRequest(device, TEMPLATE_PREVIEW, &request);
  119.     if (status != ACAMERA_OK){
  120.         logger::error(TAG, "开启 ACameraDevice_createCaptureRequest 失败");
  121.         return;
  122.     }
  123.     // 设置帧率范围
  124.     int32_t range[2] = {30, 30}; // 固定 30fps
  125.     ACaptureRequest_setEntry_i32(request,
  126.                                  ACAMERA_CONTROL_AE_TARGET_FPS_RANGE,
  127.                                  2, range);
  128.     ACameraOutputTarget *aCameraOutputTarget = nullptr;
  129.     status = ACameraOutputTarget_create(window, &aCameraOutputTarget);
  130.     if (status != ACAMERA_OK){
  131.         logger::error(TAG, "开启 ACameraOutputTarget_create 失败");
  132.         return;
  133.     }
  134.     status = ACaptureRequest_addTarget(request, aCameraOutputTarget);
  135.     if (status != ACAMERA_OK){
  136.         logger::error(TAG, "开启 ACaptureRequest_addTarget 失败");
  137.         return;
  138.     }
  139.     ACameraCaptureSession_stateCallbacks sessionStateCallbacks = {
  140.             .context = nullptr,
  141.             .onClosed = [](void* context, ACameraCaptureSession *session) -> void {
  142.                 logger::info(TAG, "onClosed...");
  143.             },
  144.             .onReady = [](void* context, ACameraCaptureSession *session) -> void {
  145.                 logger::info(TAG, "onReady...");
  146.             },
  147.             .onActive = [](void* context, ACameraCaptureSession *session) -> void {
  148.                 logger::info(TAG, "onActive...");
  149.             },
  150.     };
  151.     ACameraCaptureSession_captureCallbacks captureCallbacks = {
  152.             .context = nullptr,
  153.             .onCaptureStarted = [](void* context, ACameraCaptureSession* session,
  154.                                    const ACaptureRequest* request, int64_t timestamp) -> void {
  155.                 logger::info(TAG, "onCaptureStarted timestamp: %d", timestamp);
  156.             },
  157.             .onCaptureProgressed = [](void* context, ACameraCaptureSession* session,
  158.                                       ACaptureRequest* request, const ACameraMetadata* result) -> void {
  159.                 logger::info(TAG, "onCaptureProgressed...");
  160.             },
  161.             .onCaptureCompleted = [](void* context, ACameraCaptureSession* session,
  162.                                      ACaptureRequest* request, const ACameraMetadata* result) -> void {
  163.                 ACameraMetadata_const_entry fpsEntry = {};
  164.                 if (ACameraMetadata_getConstEntry(result,
  165.                                                   ACAMERA_CONTROL_AE_TARGET_FPS_RANGE, &fpsEntry) == ACAMERA_OK) {
  166.                     if (fpsEntry.count >= 2) {
  167.                         int32_t minFps = fpsEntry.data.i32[0];
  168.                         int32_t maxFps = fpsEntry.data.i32[1];
  169.                         logger::info(TAG, "onCaptureCompleted 当前帧率范围: [%d, %d]", minFps, maxFps);
  170.                     }
  171.                 }
  172.             },
  173.             .onCaptureFailed = [](void* context, ACameraCaptureSession* session,
  174.                                   ACaptureRequest* request, ACameraCaptureFailure* failure) -> void {
  175.                 logger::info(TAG, "onCaptureFailed frameNumber: %d, reason: %d, sequenceId: %d, wasImageCaptured: %d", failure->frameNumber, failure->reason, failure->sequenceId, failure->wasImageCaptured);
  176.             },
  177.             .onCaptureSequenceCompleted = [](void* context, ACameraCaptureSession* session,
  178.                                              int sequenceId, int64_t frameNumber) -> void {
  179.                 logger::info(TAG, "onCaptureSequenceCompleted sequenceId: %d, frameNumber: %d", sequenceId, frameNumber);
  180.             },
  181.             .onCaptureSequenceAborted = [](void* context, ACameraCaptureSession* session,
  182.                                            int sequenceId) -> void {
  183.                 logger::info(TAG, "onCaptureSequenceAborted sequenceId: %d", sequenceId);
  184.             },
  185.             .onCaptureBufferLost = [](void* context, ACameraCaptureSession* session,
  186.                                       ACaptureRequest* request, ACameraWindowType* window, int64_t frameNumber) -> void {
  187.                 logger::info(TAG, "onCaptureBufferLost frameNumber: %d", frameNumber);
  188.             },
  189.     };
  190.     status = ACaptureSessionOutputContainer_create(&aCaptureSessionOutputContainer);
  191.     if (status != ACAMERA_OK){
  192.         logger::error(TAG, "开启 ACaptureSessionOutputContainer_create 失败");
  193.         return;
  194.     }
  195.     status = ACaptureSessionOutput_create(window, &sessionOutput);
  196.     if (status != ACAMERA_OK){
  197.         logger::error(TAG, "开启 ACaptureSessionOutput_create 失败");
  198.         return;
  199.     }
  200.     status = ACaptureSessionOutputContainer_add(aCaptureSessionOutputContainer, sessionOutput);
  201.     if (status != ACAMERA_OK){
  202.         logger::error(TAG, "开启 ACaptureSessionOutputContainer_add 失败");
  203.         return;
  204.     }
  205.     status = ACameraDevice_createCaptureSession(device, aCaptureSessionOutputContainer, &sessionStateCallbacks, &session);
  206.     if (status != ACAMERA_OK){
  207.         logger::error(TAG, "开启 ACameraDevice_createCaptureSession 失败");
  208.         return;
  209.     }
  210. #if __ANDROID_API__ >= 33
  211.     ACameraCaptureSession_captureCallbacksV2 captureCallbacksV2 = {
  212.             .context = nullptr,
  213.             .onCaptureStarted = [](void* context, ACameraCaptureSession* session,
  214.                                    const ACaptureRequest* request, int64_t timestamp, int64_t frameNumber) -> void {
  215.             },
  216.             .onCaptureProgressed = [](void* context, ACameraCaptureSession* session,
  217.                                       ACaptureRequest* request, const ACameraMetadata* result) -> void {
  218.             },
  219.             .onCaptureCompleted = [](void* context, ACameraCaptureSession* session,
  220.                                      ACaptureRequest* request, const ACameraMetadata* result) -> void {
  221.             },
  222.             .onCaptureFailed = [](void* context, ACameraCaptureSession* session,
  223.                                   ACaptureRequest* request, ACameraCaptureFailure* failure) -> void {
  224.             },
  225.             .onCaptureSequenceCompleted = [](void* context, ACameraCaptureSession* session,
  226.                                              int sequenceId, int64_t frameNumber) -> void {
  227.             },
  228.             .onCaptureSequenceAborted = [](void* context, ACameraCaptureSession* session,
  229.                                            int sequenceId) -> void {
  230.             },
  231.             .onCaptureBufferLost = [](void* context, ACameraCaptureSession* session,
  232.                                       ACaptureRequest* request, ACameraWindowType* window, int64_t frameNumber) -> void {
  233.             },
  234.     };
  235.     status = ACameraCaptureSession_setRepeatingRequestV2(session, &captureCallbacksV2, 1, &request, nullptr);
  236.     if (status != ACAMERA_OK){
  237.         logger::error(TAG, "开启 ACameraCaptureSession_setRepeatingRequestV2 失败");
  238.         return;
  239.     }
  240. #else
  241.     status = ACameraCaptureSession_setRepeatingRequest(session, &captureCallbacks, 1, &request, nullptr);
  242.     if (status != ACAMERA_OK){
  243.         logger::error(TAG, "开启 ACameraCaptureSession_setRepeatingRequest 失败");
  244.         return;
  245.     }
  246. #endif
  247. }
  248. NDKCamera::~NDKCamera() {
  249.     logger::info(TAG, "~NDKCamera...");
  250.     if (this->aImageReader != nullptr){
  251.         AImageReader_delete(this->aImageReader);
  252.     }
  253.     if (session != nullptr){
  254.         ACameraCaptureSession_close(session);
  255.     }
  256.     if (device != nullptr){
  257.         ACameraDevice_close(device);
  258.     }
  259.     if (aCameraManager != nullptr) {
  260.         ACameraManager_delete(aCameraManager);
  261.     }
  262.     if (this->yuvStream != nullptr){
  263.         this->yuvStream->close();
  264.     }
  265.     if (this->aCaptureSessionOutputContainer != nullptr){
  266.         ACaptureSessionOutputContainer_free(this->aCaptureSessionOutputContainer);
  267.     }
  268.     if (this->sessionOutput != nullptr){
  269.         ACaptureSessionOutput_free(this->sessionOutput);
  270.     }
  271. }
  272. void NDKCamera::printCameraCapabilities(const char * const cameraId){
  273.     ACameraMetadata *metadata = nullptr;
  274.     camera_status_t status = ACameraManager_getCameraCharacteristics(this->aCameraManager, cameraId, &metadata);
  275.     if(status != ACAMERA_OK){
  276.         logger::error(TAG, "获取摄像头信息失败");
  277.         return;
  278.     }
  279.     ACameraMetadata_const_entry entry = {};
  280.     if (ACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry) == ACAMERA_OK){
  281.         logger::info(TAG, "支持的分辨率:");
  282.         for(uint32_t i = 0; i + 3 < entry.count; i += 4){
  283.             int32_t format = entry.data.i32[i + 0];
  284.             int32_t width = entry.data.i32[i + 1];
  285.             int32_t height = entry.data.i32[i + 2];
  286.             int32_t isInput = entry.data.i32[i + 3];
  287.             if (isInput == 0 && format == AIMAGE_FORMAT_YUV_420_888){
  288.                 logger::info(TAG, "format: %d, width: %d, height: %d, isInput: %d", format, width, height, isInput);
  289.             }
  290.         }
  291.     }
  292.     if (ACameraMetadata_getConstEntry(metadata, ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, &entry) == ACAMERA_OK){
  293.         logger::info(TAG, "支持的帧率范围:");
  294.         for (uint32_t i = 0; i + 1 < entry.count; i += 2) {
  295.             logger::info(TAG, "帧率范围: [%d, %d]", entry.data.i32[i], entry.data.i32[i + 1]);
  296.         }
  297.     }
  298.     ACameraMetadata_free(metadata);
  299. }
复制代码
暴露给Kotlin
  1. extern "C"
  2. JNIEXPORT jlong JNICALL
  3. Java_io_github_opensllearn_utils_Utils_initCamera(JNIEnv *env, jobject, jint width, jint height, jstring pcmPath) {
  4.     NDKCamera *ndkCamera = nullptr;
  5.     try {
  6.         jboolean isCopy = false;
  7.         const char * const pcmPathStr = env->GetStringUTFChars(pcmPath, &isCopy);
  8.         ndkCamera = new NDKCamera(width, height, pcmPathStr);
  9.         if (isCopy){
  10.             env->ReleaseStringUTFChars(pcmPath, pcmPathStr);
  11.         }
  12.     } catch (const std::exception &e) {
  13.         delete ndkCamera;
  14.         ndkCamera = nullptr;
  15.         env->ThrowNew(env->FindClass("java/lang/RuntimeException"), e.what());
  16.     }
  17.     return reinterpret_cast<jlong>(ndkCamera);
  18. }
  19. extern "C"
  20. JNIEXPORT void JNICALL
  21. Java_io_github_opensllearn_utils_Utils_releaseCamera(JNIEnv*, jobject, jlong ptr) {
  22.     const auto* const ndkKCamera = reinterpret_cast<NDKCamera*>(ptr);
  23.     delete ndkKCamera;
  24. }
复制代码
结束.后续如果想渲染得话可以使用Surface,然后传入Native,使用OpenGL,先将yuv420p转为RGB然后交给OpenGL.不是很复杂.
核心逻辑
  1. for (int plane = 0; plane < 3; ++plane) {
  2.         uint8_t* planeData = nullptr;
  3.         int planeDataLen = 0;
  4.         if (AImage_getPlaneData(image, plane, &planeData, &planeDataLen) != AMEDIA_OK) {
  5.                 logger::error(TAG, "AImage_getPlaneData failed plane=%d", plane);
  6.                 AImage_delete(image);
  7.                 return;
  8.         }
  9.         int rowStride = 0, pixelStride = 0;
  10.         AImage_getPlaneRowStride(image, plane, &rowStride);
  11.         AImage_getPlanePixelStride(image, plane, &pixelStride);
  12.         int planeWidth = (plane == 0) ? width : (width + 1) / 2;
  13.         int planeHeight = (plane == 0) ? height : (height + 1) / 2;
  14.         // 按行按 pixelStride 写入,确保是连续的 Y then U then V
  15.         for (int y = 0; y < planeHeight; ++y) {
  16.                 const uint8_t* rowPtr = planeData + y * rowStride;
  17.                 if (pixelStride == 1) {
  18.                         // 直接写 planeWidth 字节
  19.                         ndkCamera->yuvStream->write(reinterpret_cast<const char*>(rowPtr), planeWidth);
  20.                 } else {
  21.                         // 需要按 pixelStride 抽取
  22.                         for (int x = 0; x < planeWidth; ++x) {
  23.                                 ndkCamera->yuvStream->put(rowPtr[x * pixelStride]);
  24.                         }
  25.                 }
  26.         }
  27. }
复制代码
AIMAGE_FORMAT_YUV_420_888: 后面得888表示Y,U,V占一字节.
这个特殊得结果兼任了上文所说得yuv420p与yuv420sp.
  1. int32_t planes = 0;
  2. AImage_getNumberOfPlanes(image, &planes);
复制代码
AImage_getNumberOfPlanes可以获得planes得分量,一般是3(RGB,YUV)或者4(ARGB).
AImage_getPlaneData(image, plane, &planeData, &planeDataLen)获取得是对于得分量的Plane.
planeData是个char类型的二维数组指针,planeDataLen就是把二维数组看成一维数组以后的长度.
比如:
  1. planeData
  2. |
  3. YYYY
  4. YYYY
复制代码
又比如
  1. planeData
  2. |
  3. UPUP
复制代码
高潮时刻到了,打起精神! 先整一个AI笑话.
「对着代码改到凌晨,突然灵魂拷问:我费这劲学 YUV 格式、调 AImage 有啥卵用啊?」「要是此刻能冲进来个富婆,啪给我一巴掌说‘别卷这些破玩意了’,再扔张黑卡‘姐带你环球旅行’,我当场能把编译器删了!」
「调试 YUV420P 转码又卡了两小时,盯着屏幕发呆:会这些到底能换几毛钱啊?」「突然脑补一个场景:富婆推门进来,反手给我一巴掌,特霸气地说‘别跟像素较劲了’,然后拽着我就走‘现在就去马尔代夫晒太阳’—— 唉,梦该醒了,继续改 bug 吧。」
「写 AImage 提取数据的代码时,突然摆烂:学这些冷门技术,除了掉头发还有啥用?」「要是有富婆能过来,轻轻扇我一下说‘别学了没用’,再补一句‘我带你去环游世界’,我现在就把项目文件夹拖进回收站,绝不犹豫!」
1.jpeg

梦醒了!
AImage_getPlaneRowStride会返回每行的数据量,且会包含无效数据
如下
  1. planeData
  2. |
  3. UPUP
复制代码
P就是无效数据,所以就需要下一个函数登场.
AImage_getPlanePixelStride代表每行有效像素的距离.
这时候你就需要一个char一个char的写了.
结束.

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册