# 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）开发流程**：
        
        *   在会话OH\_CaptureSession\_CommitConfig前注册分段式拍照回调。
        *   在分段式拍照回调函数中获取图片信息，解析出pixelMap数据，做自定义业务处理。
        *   将处理完的pixelMap通过回调传给ArkTS侧，做图片显示或通过安全控件写文件保存图片。
        *   调用OH\_PhotoOutput\_Capture拍照后，需要及时调用OH\_MediaAssetChangeRequest\_SaveCameraPhoto保存图片或OH\_MediaAssetChangeRequest\_DiscardCameraPhoto取消保存图片，否则会影响后续图片的拍摄。
        *   使用完后解注册分段式拍照回调函数。
        
        ```
        // 声明快速返图回调。
        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.");
            }
            // 创建媒体资产管理对象。
            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;
    }
    ```