openharmony 鸿蒙 native-camera-yuv-shooting

2026-08-25 浏览 (1)

YUV拍照(C/C++)

从API version 23开始,相机框架提供YUV格式图片拍照能力。与普通拍照相比,YUV拍照获取到的是未经过编码的图像数据,完整保留了传感器捕获的原始亮度和色度信息,适用于视频编码或专业处理。同时,拍摄过程会产生更高的能耗开销,保存会占用更多的存储空间。

开发步骤

详细的相机功能API说明请参考Camera模块描述OH_Camera

  1. 导入NDK接口,接口中提供了相机相关的属性和方法,导入方法如下。

    // 导入NDK接口头文件。
    #include <cstdint>
    #include <cstdlib>
    #include <cstring>
    #include <string.h>
    #include <new>
    #include "hilog/log.h"
    #include "multimedia/image_framework/image/image_source_native.h"
    #include "multimedia/image_framework/image/image_packer_native.h"
    #include "multimedia/media_library/media_access_helper_capi.h"
    #include "multimedia/media_library/media_asset_base_capi.h"
    #include "multimedia/media_library/media_asset_capi.h"
    #include "multimedia/media_library/media_asset_change_request_capi.h"
    #include "multimedia/media_library/media_asset_manager_capi.h"
    #include "ohcamera/camera.h"
    #include "ohcamera/camera_input.h"
    #include "ohcamera/camera_manager.h"
    #include "ohcamera/capture_session.h"
    #include "ohcamera/photo_native.h"
    #include "ohcamera/photo_output.h"
    #include "ohcamera/preview_output.h"
    #include "ohcamera/video_output.h"
    
  2. 在CMake脚本中链接相关动态库。

    target_link_libraries(entry PUBLIC
        libace_napi.z.so
        libhilog_ndk.z.so
        libnative_buffer.so
        libohcamera.so
        libohimage.so
        libohfileuri.so
        libmedia_asset_manager.so
        libimage_source.so
        libpixelmap.so
        libimage_packer.so
        libpicture.so
    )
    
  3. 创建并打开相机设备,参考设备输入(C/C++)中的步骤3-4。

  4. 获取相机设备完整输出能力。

    通过OH_CameraManager_GetSupportedFullCameraOutputCapabilityWithSceneMode()方法,获取当前设备支持的所有输出流的能力,包含预览流、拍照流、录像流等。输出流在CameraOutputCapability中的各个profile字段中,其中拍照流支持YUV格式。根据相机设备指定模式Camera_SceneMode的不同,需要添加不同类型的输出流。

    Camera_OutputCapability* GetSupportedFullCameraOutputCapability(Camera_Manager* cameraManager, Camera_Device &camera)
    {
         Camera_OutputCapability* cameraOutputCapability = nullptr;
         // 获取相机设备支持的输出流能力。
         const Camera_Profile* previewProfile = nullptr;
         const Camera_Profile* photoProfile = nullptr;
         Camera_ErrorCode ret = OH_CameraManager_GetSupportedFullCameraOutputCapabilityWithSceneMode(cameraManager, &camera,
             Camera_SceneMode::NORMAL_PHOTO, &cameraOutputCapability);
         if (cameraOutputCapability == nullptr||ret != CAMERA_OK) {
             OH_LOG_ERROR(LOG_APP, "OH_CameraManager_GetSupportedCameraOutputCapability failed.");
             return nullptr;
         }
         return cameraOutputCapability;
    }
    
  5. 选择设备支持的输出流能力,创建拍照输出流。

    通过OH_CameraManager_CreatePhotoOutputWithoutSurface()方法创建拍照输出流。

    可以通过OH_CameraManager_GetSupportedFullCameraOutputCapabilityWithSceneMode()获取相机在指定模式下支持的完整输出能力cameraOutputCapability,参考步骤2。在cameraOutputCapability的photoProfiles中选择支持YUV格式的profile,作为创建拍照输出流的参数photoProfile。

    Camera_PhotoOutput* CreatePhotoOutput(Camera_Manager* cameraManager, const Camera_Profile* photoProfile)
    {
        Camera_PhotoOutput* photoOutput = nullptr;
        // 无需传入surfaceId,直接创建拍照流。
        Camera_ErrorCode ret = OH_CameraManager_CreatePhotoOutputWithoutSurface(cameraManager, photoProfile, &photoOutput);
        if (photoOutput == nullptr||ret != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "OH_CameraManager_CreatePhotoOutputWithoutSurface failed.");
        }
        return photoOutput;
    }
    
  6. 注册单段式(PhotoAvailable)或分段式(PhotoAssetAvailable)拍照回调,若应用希望快速得到回图,推荐使用分段式拍照(PhotoAssetAvailable)回调。

    注意:

    如果已经注册了PhotoAssetAvailable回调,并且在Session开始之后又注册了PhotoAvailable回调,PhotoAssetAvailable和PhotoAvailable同时注册会导致流被重启,仅PhotoAssetAvailable生效。

    • 单段式拍照(PhotoAvailable)开发流程

      • 在会话OH_CaptureSession_CommitConfig前注册单段式拍照回调。
      • 在单段式拍照回调函数中获取图片信息,解析出pixelMap数据,做自定义业务处理。
      • 将处理完的pixelMap通过回调传给ArkTS侧,做图片显示或通过安全控件写文件保存图片。
      • 使用完后解注册单段式拍照回调函数。
      // 单段式拍照回调函数。
      void OnPhotoAvailable(Camera_PhotoOutput* photoOutput, OH_PhotoNative* photo)
      {
          OH_LOG_INFO(LOG_APP, "OnPhotoAvailable start!");
          OH_PictureNative* picture;
          Camera_ErrorCode errCode = OH_PhotoNative_GetUncompressedImage(photo, &picture);
          if (errCode != CAMERA_OK||picture == nullptr) {
              OH_LOG_ERROR(LOG_APP, "OH_PhotoNative_GetUncompressedImage call failed, errorCode: %{public}d", errCode);
              return;
          }
          OH_LOG_INFO(LOG_APP, "OnPhotoAvailable errCode:%{public}d picture:%{public}p", errCode, picture);
          // 读取OH_PictureNative中的主图mainPixelMap。
          OH_PixelmapNative* mainPixelmap;
          Image_ErrorCode imageErr = OH_PictureNative_GetMainPixelmap(picture, &mainPixelmap);
          if (imageErr != IMAGE_SUCCESS||mainPixelmap == nullptr) {
              OH_LOG_ERROR(LOG_APP, "OH_ImageNative_GetImageSize call failed, errorCode: %{public}d", imageErr);
              return;
          }
          OH_LOG_INFO(LOG_APP, "OH_PictureNative_GetMainPixelmap success");
          // 获取主图Pixelmap中所有像素所占用的总字节数。
          uint32_t byteCount = 0;
          imageErr = OH_PixelmapNative_GetByteCount(mainPixelmap, &byteCount);
          OH_LOG_INFO(LOG_APP, "OH_PixelmapNative_GetByteCount count:%{public}u", byteCount);
          // 获取主图的图像像素信息。
          OH_Pixelmap_ImageInfo* imageInfo;
          imageErr = OH_PixelmapNative_GetImageInfo(mainPixelmap, imageInfo);
          OH_LOG_INFO(LOG_APP, "OH_PixelmapNative_GetImageInfo errorCode:%{public}d", imageErr);
      
          uint32_t width = 0;
          uint32_t height = 0;
          uint32_t rowStride = 0;
          int32_t pixelFormat = PIXEL_FORMAT::PIXEL_FORMAT_UNKNOWN;
          int32_t alphaMode = PIXELMAP_ALPHA_TYPE::PIXELMAP_ALPHA_TYPE_UNKNOWN;
          int32_t alphaType = PIXELMAP_ALPHA_TYPE::PIXELMAP_ALPHA_TYPE_UNKNOWN;
          // 获取主图图像像素信息中的宽度。
          OH_PixelmapImageInfo_GetWidth(imageInfo, &width);
          // 获取主图图像像素信息中的高度。
          OH_PixelmapImageInfo_GetHeight(imageInfo, &height);
          // 获取主图图像像素信息中的行跨距。
          OH_PixelmapImageInfo_GetRowStride(imageInfo, &rowStride);
          // 获取主图图像像素信息中的像素格式。
          OH_PixelmapImageInfo_GetPixelFormat(imageInfo, &pixelFormat);
          // 获取主图图像像素信息中的透明通道类型。
          OH_PixelmapImageInfo_GetAlphaMode(imageInfo, &alphaMode);
          // 获取主图图像像素信息中的默认的透明通道类型。
          OH_PixelmapImageInfo_GetAlphaType(imageInfo, &alphaType);
          OH_LOG_INFO(LOG_APP, "OH_PixelmapNative_GetImageInfo width:%{public}u height:%{public}u rowStride:%{public}u pixelFormat:%{public}d alphaMode:%{public}d alphaType:%{public}d", width, height, rowStride, pixelFormat, alphaMode, alphaType);
         // 释放资源。
          OH_PixelmapNative_Release(mainPixelmap);
          OH_PictureNative_Release(picture);
      }
      
      // 注册单段式拍照回调。
      Camera_ErrorCode PhotoOutputRegisterPhotoAvailableCallback(Camera_PhotoOutput* photoOutput)
      {
          OH_LOG_INFO(LOG_APP, "PhotoOutputRegisterPhotoAvailableCallback start!");
          Camera_ErrorCode ret = OH_PhotoOutput_RegisterPhotoAvailableCallback(photoOutput, OnPhotoAvailable);
          if (ret != CAMERA_OK) {
              OH_LOG_ERROR(LOG_APP, "PhotoOutputRegisterPhotoAvailableCallback failed.");
          }
          OH_LOG_INFO(LOG_APP, "PhotoOutputRegisterPhotoAvailableCallback return with ret code: %{public}d!", ret);
          return ret;
      }
      
      // 解注册单段式拍照回调。
      Camera_ErrorCode PhotoOutputUnRegisterPhotoAvailableCallback(Camera_PhotoOutput* photoOutput)
      {
          OH_LOG_INFO(LOG_APP, "PhotoOutputUnRegisterPhotoAvailableCallback start!");
          Camera_ErrorCode ret = OH_PhotoOutput_UnregisterPhotoAvailableCallback(photoOutput, OnPhotoAvailable);
          if (ret != CAMERA_OK) {
              OH_LOG_ERROR(LOG_APP, "PhotoOutputUnRegisterPhotoAvailableCallback failed.");
          }
          OH_LOG_INFO(LOG_APP, "PhotoOutputUnRegisterPhotoAvailableCallback return with ret code: %{public}d!", ret);
          return ret;
      }
      
    • 分段式拍照(PhotoAvailable)开发流程

      // 声明快速返图回调。
      void OnRequestQuickImageDataPreparedWithDetails(MediaLibrary_ErrorCode result, MediaLibrary_RequestId requestId, MediaLibrary_MediaQuality mediaQuality, MediaLibrary_MediaContentType type, OH_ImageSourceNative* imageSourceNative, OH_PictureNative* pictureNative)
      {
          OH_LOG_INFO(LOG_APP, "OnRequestQuickImageDataPreparedWithDetails start!");
          if (!pictureNative && !imageSourceNative) {
              OH_LOG_ERROR(LOG_APP, "OnRequestQuickImageDataPreparedWithDetails, pictureNative and imageSourceNative are null.");
              return;
          } else if (!pictureNative && imageSourceNative) {
              OH_LOG_ERROR(LOG_APP, "OnRequestQuickImageDataPreparedWithDetails, pictureNative is null.");
          } else if (pictureNative && !imageSourceNative) {
              OH_LOG_ERROR(LOG_APP, "OnRequestQuickImageDataPreparedWithDetails, imageSourceNative is null.");
          } else {
              OH_LOG_INFO(LOG_APP, "OnRequestQuickImageDataPreparedWithDetails, pictureNative and imageSourceNative are not null.");
          }
      }
      
      // 分段式拍照回调函数。
      void OnPhotoAssetAvailable(Camera_PhotoOutput* photoOutput, OH_MediaAsset* mediaAsset)
      {
          OH_LOG_INFO(LOG_APP, "OnPhotoAssetAvailable start!");
          if (mediaAsset == nullptr) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable invalid error, mediaAsset is null.");
              return;
          }
          // 尝试获取mediaAsset中的uri信息。
          const char* uri = nullptr;
          MediaLibrary_ErrorCode result = OH_MediaAsset_GetUri(mediaAsset, &uri);
          if (uri == nullptr||result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to get uri.");
          }
          // 尝试获取mediaAsset中的displayName信息。
          const char* displayName = nullptr;
          result = OH_MediaAsset_GetDisplayName(mediaAsset, &displayName);
          if (displayName == nullptr||result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to get displayName.");
          }
          // 尝试获取mediaAsset中的size信息。
          uint32_t mediaAssetSize = 0;
          result = OH_MediaAsset_GetSize(mediaAsset, &mediaAssetSize);
          if (result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to get size.");
          }
          // 尝试获取mediaAsset中的修改时间信息。
          uint32_t modifiedMs = 0;
          result = OH_MediaAsset_GetDateModifiedMs(mediaAsset, &modifiedMs);
          if (result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to get modifiedMs.");
          }
          // 尝试获取mediaAsset中的图片宽度信息。
          uint32_t width = 0;
          result = OH_MediaAsset_GetWidth(mediaAsset, &width);
          if (result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to get width.");
          }
          // 尝试获取mediaAsset中的图片高度信息。
          uint32_t height = 0;
          result = OH_MediaAsset_GetHeight(mediaAsset, &height);
          if (result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to get height.");
          }
          // 尝试获取mediaAsset中的图片方向信息。
          uint32_t orientation = 0;
          result = OH_MediaAsset_GetOrientation(mediaAsset, &orientation);
          if (result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to get orientation.");
          }
          // 创建媒体资产管理对象。
          OH_MediaAssetManager* mediaAssetManager = OH_MediaAssetManager_Create();
          if (mediaAssetManager == nullptr) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to create mediaAssetManager.");
              return;
          }
      
          // 创建媒体资产变更请求。
          OH_MediaAssetChangeRequest* changeRequest = OH_MediaAssetChangeRequest_Create(mediaAsset);
          if (changeRequest == nullptr) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to create changeRequest.");
          }
          // 注册媒体资产快速请求回调。
          MediaLibrary_RequestOptions requestOptions;
          requestOptions.deliveryMode = MEDIA_LIBRARY_BALANCED_MODE;
          MediaLibrary_RequestId requestId;
          result = OH_MediaAssetManager_QuickRequestImage(mediaAssetManager, mediaAsset, requestOptions, &requestId, OnRequestQuickImageDataPreparedWithDetails);
          if (result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to quick image.");
          }
          // 创建媒体资产保存变更请求。
          result = OH_MediaAssetChangeRequest_SaveCameraPhoto(changeRequest, MediaLibrary_ImageFileType::MEDIA_LIBRARY_IMAGE_JPEG);
          if (result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to save camera photo.");
          }
          result = OH_MediaAccessHelper_ApplyChanges(changeRequest);
          if (result != MEDIA_LIBRARY_OK) {
              OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable failed to apply changes.");
          }
      }
      
      // 注册分段式拍照回调函数。
      Camera_ErrorCode RegisterPhotoAssetAvailable(Camera_PhotoOutput* photoOutput)
      {
          Camera_ErrorCode ret = OH_PhotoOutput_RegisterPhotoAssetAvailableCallback(photoOutput, OnPhotoAssetAvailable);
          if (ret != CAMERA_OK) {
              OH_LOG_ERROR(LOG_APP, "RegisterPhotoAssetAvailable failed. %d ", ret);
          }
          return ret;
      }
      // 解注册分段式拍照回调函数。
      Camera_ErrorCode UnregisterPhotoAssetAvailable(Camera_PhotoOutput* photoOutput)
      {
          Camera_ErrorCode ret = OH_PhotoOutput_UnregisterPhotoAssetAvailableCallback(photoOutput, OnPhotoAssetAvailable);
          if (ret != CAMERA_OK) {
              OH_LOG_ERROR(LOG_APP, "UnregisterPhotoAssetAvailable failed. %d ", ret);
          }
          return ret;
      }
      
  7. 创建拍照类型会话(参考会话管理(C/C++)),开启会话,准备拍照。

  8. 触发拍照。

    通过OH_PhotoOutput_Capture()方法,执行拍照任务。

    Camera_ErrorCode Capture(Camera_PhotoOutput* photoOutput)
    {
        Camera_ErrorCode ret = OH_PhotoOutput_Capture(photoOutput);
        if (ret == CAMERA_OK) {
            OH_LOG_INFO(LOG_APP, "OH_PhotoOutput_Capture success ");
        } else {
            OH_LOG_ERROR(LOG_APP, "OH_PhotoOutput_Capture failed. %d ", ret);
        }
        return ret;
    }
    

状态监听

在相机应用开发过程中,可以随时监听拍照输出流状态,包括拍照流开始、拍照帧的开始与结束、拍摄下一张图片是否就绪、拍照输出流的错误。

  • 通过注册固定的onFrameStart回调函数获取监听拍照开始结果,当photoOutput创建成功时,即可监听。拍照第一次曝光时触发。

    void PhotoOutputOnFrameStart(Camera_PhotoOutput* photoOutput)
    {
        OH_LOG_INFO(LOG_APP, "PhotoOutputOnFrameStart");
    }
    void PhotoOutputOnFrameShutter(Camera_PhotoOutput* photoOutput, Camera_FrameShutterInfo* info)
    {
        OH_LOG_INFO(LOG_APP, "PhotoOutputOnFrameShutter");
    }
    
  • 通过注册固定的onFrameEnd回调函数监听拍照结束结果,当photoOutput创建成功时,即可监听。

    void PhotoOutputOnFrameEnd(Camera_PhotoOutput* photoOutput, int32_t frameCount)
    {
        OH_LOG_INFO(LOG_APP, "PhotoOutput frameCount = %{public}d", frameCount);
    }
    
  • 通过注册固定的captureReady回调函数监听能否继续拍摄下一张的结果,当photoOutput创建成功时,即可监听。当下一张可拍时触发,该事件返回结果为下一张可拍的相关信息。

    static bool g_captureReadyFlag = false;
    void CaptureReadyCb(Camera_PhotoOutput* photoOutput) {
      g_captureReadyFlag = true;
      OH_LOG_INFO(LOG_APP, "PhotoOutputOnCaptureReady captureReadyFlag = %{public}d", g_captureReadyFlag);
    }
    
    void OnPhotoOutputOnCaptureReady(Camera_PhotoOutput* photoOutput)
    {
        OH_LOG_INFO(LOG_APP, "PhotoOutputOnCaptureReady");
        Camera_ErrorCode ret = OH_PhotoOutput_RegisterCaptureReadyCallback(photoOutput, CaptureReadyCb);
    }
    
  • 通过注册固定的onError回调函数获取监听拍照输出流的错误结果。callback返回拍照输出接口使用错误时的对应错误码,错误码类型参见Camera_ErrorCode

    void PhotoOutputOnError(Camera_PhotoOutput* photoOutput, Camera_ErrorCode errorCode)
    {
        OH_LOG_INFO(LOG_APP, "PhotoOutput errorCode = %{public}d", errorCode);
    }
    
    PhotoOutput_Callbacks* GetPhotoOutputListener()
    {
        static PhotoOutput_Callbacks photoOutputListener = {
            .onFrameStart = PhotoOutputOnFrameStart,
            .onFrameShutter = PhotoOutputOnFrameShutter,
            .onFrameEnd = PhotoOutputOnFrameEnd,
            .onError = PhotoOutputOnError
        };
        return &photoOutputListener;
    }
    Camera_ErrorCode RegisterPhotoOutputCallback(Camera_PhotoOutput* photoOutput)
    {
        Camera_ErrorCode ret = OH_PhotoOutput_RegisterCallback(photoOutput, GetPhotoOutputListener());
        if (ret != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "OH_PhotoOutput_RegisterCallback failed.");
        }
        return ret;
    }
    

你可能感兴趣的鸿蒙文章

openharmony 鸿蒙 camera-whitebalance

openharmony 鸿蒙 native-camera-preview

openharmony 鸿蒙 camera-worker

openharmony 鸿蒙 camera-background-recovery

openharmony 鸿蒙 camera-preparation

openharmony 鸿蒙 camera-shooting

openharmony 鸿蒙 camera-moving-photo

openharmony 鸿蒙 camera-system-pressure

openharmony 鸿蒙 native-camera-preview-imageReceiver

openharmony 鸿蒙 native-camera-shooting

  • 所属分类: 后端技术
  • 本文标签: 鸿蒙 软件
  • 版权声明: 本文链接 https://seaxiang.com/blog/sHW06rLO