目录

Photo Capture (C/C++)

Overview

Photo capture is one of the key functions of a camera. Based on the complex logic of the camera module, you can set information such as resolution, flash, focal length, photo quality, and rotation angle in intermediate steps to ensure the quality of captured photos.

Currently, there are two photo capture solutions: deferred photo capture and one-shot photo capture (this document uses one-shot photo capture as the basis for explanation).

How to Develop

For detailed API descriptions, see OH_Camera.

  1. Import the NDK, which provides camera-related properties and methods.

    #include <cstdint>
    #include <native_buffer/buffer_common.h>
    #include <unistd.h>
    #include <string>
    #include <thread>
    #include <cstdio>
    #include <fcntl.h>
    #include <map>
    #include <vector>
    #include <native_buffer/native_buffer.h>
    #include "iostream"
    #include "mutex"
    
    #include "hilog/log.h"
    #include "ohcamera/camera.h"
    #include "ohcamera/camera_input.h"
    #include "ohcamera/capture_session.h"
    #include "ohcamera/photo_output.h"
    #include "ohcamera/preview_output.h"
    #include "ohcamera/video_output.h"
    #include "napi/native_api.h"
    #include "ohcamera/camera_manager.h"
    #include <window_manager/oh_display_info.h>
    #include <window_manager/oh_display_manager.h>
    
    namespace OHOS_CAMERA_SAMPLE {
    class NDKCamera {
      public:
        struct CameraBuildingConfig {
            char *str;
            uint32_t focusMode;
            uint32_t cameraDeviceIndex;
            bool isVideo;
            bool isHdr;
            char *videoId;
        };
        ~NDKCamera();
        explicit NDKCamera(CameraBuildingConfig config);
        // ...
    };
    } // namespace OHOS_CAMERA_SAMPLE
  2. Link the dynamic library in the CMake script.

    target_link_libraries(entry PUBLIC
        libace_napi.z.so
        libhilog_ndk.z.so
        libnative_buffer.so
        libohcamera.so
        libohimage.so
        libohfileuri.so
    )
  3. Create and open a camera device. For details, see steps 3 to 5 in Device Input Management (C/C++).

  4. Select the output stream capability supported by the camera device and create a photo output stream.

    Call OH_CameraManager_CreatePhotoOutputWithoutSurface() to create a photo output stream.

    Camera_ErrorCode NDKCamera::CreatePhotoOutputWithoutSurfaceId()
    {
        OH_LOG_INFO(LOG_APP, "CreatePhotoOutputWithoutSurfaceId enter.");
        profile_ = cameraOutputCapability_->photoProfiles[0];
        Camera_Profile* profile = cameraOutputCapability_->photoProfiles[0];
        profile->size.width = NUM_1920;
        profile->size.height = NUM_1080;
        profile_ = profile;
        if (profile_ == nullptr) {
            OH_LOG_ERROR(LOG_APP, "Get photoProfiles failed.");
            return CAMERA_INVALID_ARGUMENT;
        }
        ret_ = OH_CameraManager_CreatePhotoOutputWithoutSurface(cameraManager_, profile_, &photoOutput_);
        if (photoOutput_ == nullptr || ret_ != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "CreatePhotoOutputWithoutSurfaceId failed.");
            return CAMERA_INVALID_ARGUMENT;
        }
    // ...
        return ret_;
    }
  5. Register a one-shot photo capture callback, which is defined as PhotoAvailable. If your application requires rapid image display, use the deferred photo delivery callback, which is defined as PhotoAssetAvailable.

    If the PhotoAssetAvailable callback has been registered and the PhotoAvailable callback is registered after the session starts, the stream will be restarted. In this case, only the PhotoAssetAvailable callback takes effect.

    Therefore, you are not advised to register both PhotoAssetAvailable and PhotoAvailable.

    One-shot photo capture development process (PhotoAvailable):

    • Register the callback before the session commits the configuration.

    • Obtain the image information from the callback, parse the buffer data, and perform custom service processing.

    • Pass the processed buffer to the ArkTS side through the callback for image display or storage (using a security component).

    • Unregister the callback when it is no longer required.

      // Save the buffer processing callback registered on the NAPI side.
      Camera_ErrorCode NDKCamera::RegisterBufferCb(void *cb)
      {
          OH_LOG_INFO(LOG_APP, " RegisterBufferCb start");
          if (cb == nullptr) {
              OH_LOG_INFO(LOG_APP, " RegisterBufferCb invalid error");
              return CAMERA_INVALID_ARGUMENT;
          }
          g_bufferCb = cb;
          return CAMERA_OK;
      }
      
      static bool ProcessImageNative(OH_ImageNative* imageNative, uint32_t** components,
                                     OH_NativeBuffer** nativeBuffer, size_t* nativeBufferSize)
      {
          if (imageNative == nullptr || components == nullptr || nativeBuffer == nullptr || nativeBufferSize == nullptr) {
              return false;
          }
      
          Image_Size size;
          Image_ErrorCode imageErr = OH_ImageNative_GetImageSize(imageNative, &size);
          if (imageErr != IMAGE_SUCCESS) {
              return false;
          }
      
          size_t componentTypeSize = 0;
          imageErr = OH_ImageNative_GetComponentTypes(imageNative, nullptr, &componentTypeSize);
          if (imageErr != IMAGE_SUCCESS || componentTypeSize == 0) {
              OH_LOG_ERROR(LOG_APP, "GetComponentTypes failed: %{public}d, size: %{public}zu",
                  imageErr, componentTypeSize);
              return false;
          }
      
          if (componentTypeSize > (SIZE_MAX / sizeof(uint32_t))) {
              OH_LOG_ERROR(LOG_APP, "componentTypeSize too large: %{public}zu", componentTypeSize);
              return false;
          }
      
          uint32_t* compArray = new (std::nothrow) uint32_t[componentTypeSize];
          if (!compArray) {
              return false;
          }
      
          size_t tempSize = componentTypeSize;
          imageErr = OH_ImageNative_GetComponentTypes(imageNative, &compArray, &tempSize);
          if (imageErr != IMAGE_SUCCESS) {
              delete[] compArray;
              return false;
          }
          *components = compArray;
      
          imageErr = OH_ImageNative_GetByteBuffer(imageNative, compArray[0], nativeBuffer);
          if (imageErr != IMAGE_SUCCESS) {
              delete[] compArray;
              return false;
          }
      
          imageErr = OH_ImageNative_GetBufferSize(imageNative, compArray[0], nativeBufferSize);
          if (imageErr != IMAGE_SUCCESS) {
              delete[] compArray;
              return false;
          }
      
          int32_t rowStride = 0;
          int32_t pixelStride = 0;
          OH_ImageNative_GetRowStride(imageNative, compArray[0], &rowStride);
          OH_ImageNative_GetPixelStride(imageNative, compArray[0], &pixelStride);
          OH_LOG_INFO(LOG_APP, "Buffer size: %{public}zu, strides: %{public}d/%{public}d",
              *nativeBufferSize, rowStride, pixelStride);
      
          return true;
      }
      
      
      static void CleanupResources(OH_ImageNative* imageNative, uint32_t* components,
                                   OH_NativeBuffer* nativeBuffer, void* virAddr)
      {
          if (components) {
              delete[] components;
          }
      
          if (imageNative) {
              int32_t ret = OH_ImageNative_Release(imageNative);
              if (ret != 0) {
                  OH_LOG_ERROR(LOG_APP, "Release image failed: %{public}d", ret);
              }
          }
      
          if (nativeBuffer && virAddr) {
              int32_t ret = OH_NativeBuffer_Unmap(nativeBuffer);
              if (ret != 0) {
                  OH_LOG_ERROR(LOG_APP, "Unmap buffer failed: %{public}d", ret);
              }
          }
      }
      
      void OnPhotoAvailable(Camera_PhotoOutput *photoOutput, OH_PhotoNative *photo)
      {
          OH_LOG_INFO(LOG_APP, "OnPhotoAvailable start!");
      
          OH_ImageNative *imageNative = nullptr;
          Camera_ErrorCode errCode = OH_PhotoNative_GetMainImage(photo, &imageNative);
          if (errCode != CAMERA_OK || !imageNative) {
              OH_LOG_ERROR(LOG_APP, "GetMainImage failed: %{public}d", errCode);
              return;
          }
      
          uint32_t* components = nullptr;
          OH_NativeBuffer* nativeBuffer = nullptr;
          size_t nativeBufferSize = 0;
      
          if (!ProcessImageNative(imageNative, &components, &nativeBuffer, &nativeBufferSize)) {
              CleanupResources(imageNative, components, nullptr, nullptr);
              return;
          }
      
          void* virAddr = nullptr;
          int32_t ret = OH_NativeBuffer_Map(nativeBuffer, &virAddr);
          if (ret != 0 || !virAddr) {
              OH_LOG_ERROR(LOG_APP, "Map buffer failed: %{public}d", ret);
              CleanupResources(imageNative, components, nativeBuffer, nullptr);
              return;
          }
      
          auto cb = (void (*)(void *, size_t))(g_bufferCb);
          if (cb && virAddr && nativeBufferSize > 0) {
              cb(virAddr, nativeBufferSize);
              OH_LOG_INFO(LOG_APP, "Buffer callback called");
          } else {
              OH_LOG_ERROR(LOG_APP, "Invalid callback parameters");
          }
      
          CleanupResources(imageNative, components, nativeBuffer, virAddr);
      
          OH_LOG_INFO(LOG_APP, "OnPhotoAvailable end");
      }
      
      Camera_ErrorCode NDKCamera::PhotoOutputRegisterPhotoAvailableCallback(void)
      {
          OH_LOG_INFO(LOG_APP, "NDKCamera::PhotoOutputRegisterPhotoAvailableCallback start!");
          Camera_ErrorCode ret = OH_PhotoOutput_RegisterPhotoAvailableCallback(photoOutput_, OnPhotoAvailable);
          if (ret != CAMERA_OK) {
              OH_LOG_INFO(LOG_APP, "NDKCamera::PhotoOutputRegisterPhotoAvailableCallback failed.");
          }
          OH_LOG_INFO(LOG_APP, "NDKCamera::PhotoOutputRegisterPhotoAvailableCallback return with ret code: %{public}d!",
              ret_);
          return ret;
      }
      
      // Unregister the PhotoAvailableCallback callback.
      Camera_ErrorCode NDKCamera::PhotoOutputUnRegisterPhotoAvailableCallback()
      {
          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;
      }

      Reference sample code for buffer callback processing at the NAPI layer:

      // Buffer callback at the NAPI layer.
      static void BufferCb(void* buffer, size_t size)
      {
          OH_LOG_INFO(LOG_APP, "BufferCb size:%{public}zu", size);
          g_size = size;
          napi_value asyncResource = nullptr;
          napi_value asyncResourceName = nullptr;
          napi_async_work work;
      
          if (size == 0 || size > SIZE_MAX) {
              OH_LOG_ERROR(LOG_APP, "BufferCb size is invalid");
              return;
          }
          void* copyBuffer = malloc(size);
          if (copyBuffer == nullptr) {
              return;
          }
          OH_LOG_INFO(LOG_APP, "BufferCb copyBuffer:%{public}p", copyBuffer);
          // Use std::memcpy to copy the content in the buffer to the copyBuffer.
          std::memcpy(copyBuffer, buffer, size);
          napi_create_string_utf8(env_, "BufferCb", NAPI_AUTO_LENGTH, &asyncResourceName);
          napi_status status = napi_create_async_work(
              env_, asyncResource, asyncResourceName, [](napi_env env, void* copyBuffer) {},
              [](napi_env env, napi_status status, void* copyBuffer) {
                  napi_value retVal;
                  napi_value callback = nullptr;
                  void* data = nullptr;
                  napi_value arrayBuffer = nullptr;
                  size_t bufferSize = g_size;
                  napi_create_arraybuffer(env, bufferSize, &data, &arrayBuffer);
                  std::memcpy(data, copyBuffer, bufferSize);
                  OH_LOG_INFO(LOG_APP, "BufferCb g_size: %{public}zu", g_size);
                  napi_get_reference_value(env, bufferCbRef_, &callback);
                  if (callback) {
                      OH_LOG_INFO(LOG_APP, "BufferCb callback is full");
                  } else {
                      OH_LOG_ERROR(LOG_APP, "BufferCb callback is null");
                  }
                  // Call the buffer processing callback function at the ArkTS side to pass the image arrayBuffer for display or storage.
                  napi_call_function(env, nullptr, callback, 1, &arrayBuffer, &retVal);
              },
              copyBuffer, &work);
          // Error check: The memory is released when the asynchronous work fails to be created.
          if (status != napi_ok) {
              OH_LOG_ERROR(LOG_APP, "Failed to create async work");
              free(copyBuffer); // Release the allocated memory.
              return;
          }
          napi_queue_async_work_with_qos(env_, work, napi_qos_user_initiated);
      }
  6. Create a photo session. For details, see Camera Session Management (C/C++).

  7. (Optional) Set photo capture parameters.

    You can set camera parameters to adjust photo capture functions, including the flash, zoom ratio, and focal length.

    Camera_ErrorCode NDKCamera::HasFlashFn(uint32_t mode)
    {
        Camera_FlashMode flashMode = static_cast<Camera_FlashMode>(mode);
        // Check the flash.
        bool hasFlash = false;
        Camera_ErrorCode ret = OH_CaptureSession_HasFlash(captureSession_, &hasFlash);
        if (captureSession_ == nullptr || ret != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_HasFlash failed.");
        }
        if (hasFlash) {
            OH_LOG_INFO(LOG_APP, "hasFlash success-----");
        } else {
            OH_LOG_ERROR(LOG_APP, "hasFlash fail-----");
        }
    
        // Query whether the flash mode is supported.
        bool isSupported = false;
        ret = OH_CaptureSession_IsFlashModeSupported(captureSession_, flashMode, &isSupported);
        if (ret != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_IsFlashModeSupported failed.");
        }
        if (isSupported) {
            OH_LOG_INFO(LOG_APP, "isFlashModeSupported success-----");
        } else {
            OH_LOG_ERROR(LOG_APP, "isFlashModeSupported fail-----");
        }
    
        // Set the flash mode.
        ret = OH_CaptureSession_SetFlashMode(captureSession_, flashMode);
        if (ret == CAMERA_OK) {
            OH_LOG_INFO(LOG_APP, "OH_CaptureSession_SetFlashMode success.");
        } else {
            OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_SetFlashMode failed. %{public}d ", ret);
        }
    
        // Obtain the flash mode of the current device.
        ret = OH_CaptureSession_GetFlashMode(captureSession_, &flashMode);
        if (ret == CAMERA_OK) {
            OH_LOG_INFO(LOG_APP, "OH_CaptureSession_GetFlashMode success. flashMode: %{public}d ", flashMode);
        } else {
            OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_GetFlashMode failed. %d ", ret);
        }
        return ret;
    }
    
    // Focus mode.
    Camera_ErrorCode NDKCamera::IsFocusModeSupported(uint32_t mode)
    {
        Camera_FocusMode focusMode = static_cast<Camera_FocusMode>(mode);
        ret_ = OH_CaptureSession_IsFocusModeSupported(captureSession_, focusMode, &isFocusModeSupported_);
        if (&isFocusModeSupported_ == nullptr || ret_ != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "IsFocusModeSupported failed.");
            return CAMERA_INVALID_ARGUMENT;
        }
        return ret_;
    }
    
    Camera_ErrorCode NDKCamera::IsFocusMode(uint32_t mode)
    {
        OH_LOG_INFO(LOG_APP, "IsFocusMode start.");
        Camera_FocusMode focusMode = static_cast<Camera_FocusMode>(mode);
        ret_ = OH_CaptureSession_IsFocusModeSupported(captureSession_, focusMode, &isFocusModeSupported_);
        if (&isFocusModeSupported_ == nullptr || ret_ != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "IsFocusModeSupported failed.");
            return CAMERA_INVALID_ARGUMENT;
        }
        ret_ = OH_CaptureSession_SetFocusMode(captureSession_, focusMode);
        if (ret_ != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "SetFocusMode failed.");
            return CAMERA_INVALID_ARGUMENT;
        }
        ret_ = OH_CaptureSession_GetFocusMode(captureSession_, &focusMode);
        if (&focusMode == nullptr || ret_ != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "GetFocusMode failed.");
            return CAMERA_INVALID_ARGUMENT;
        }
        OH_LOG_INFO(LOG_APP, "IsFocusMode end.");
        return ret_;
    }
    
    Camera_ErrorCode NDKCamera::setZoomRatioFn(uint32_t zoomRatio)
    {
        float zoom = float(zoomRatio);
        // Obtain the supported zoom range.
        float minZoom;
        float maxZoom;
        Camera_ErrorCode ret = OH_CaptureSession_GetZoomRatioRange(captureSession_, &minZoom, &maxZoom);
        if (captureSession_ == nullptr || ret != CAMERA_OK) {
            OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_GetZoomRatioRange failed.");
        } else {
            OH_LOG_INFO(LOG_APP, "OH_CaptureSession_GetZoomRatioRange success. minZoom: %{public}f, maxZoom:%{public}f",
                minZoom, maxZoom);
        }
    
        // Set the zoom ratio.
        ret = OH_CaptureSession_SetZoomRatio(captureSession_, zoom);
        if (ret == CAMERA_OK) {
            OH_LOG_INFO(LOG_APP, "OH_CaptureSession_SetZoomRatio success.");
        } else {
            OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_SetZoomRatio failed. %{public}d ", ret);
        }
    
        // Obtain the zoom ratio of the current device.
        ret = OH_CaptureSession_GetZoomRatio(captureSession_, &zoom);
        if (ret == CAMERA_OK) {
            OH_LOG_INFO(LOG_APP, "OH_CaptureSession_GetZoomRatio success. zoom: %{public}f ", zoom);
        } else {
            OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_GetZoomRatio failed. %{public}d ", ret);
        }
        return ret;
    }
  8. Trigger photo capture.

    Call OH_PhotoOutput_Capture_WithCaptureSetting() to execute the photo capture task.

    Camera_ErrorCode NDKCamera::TakePicture(int32_t degree)
    {
        Camera_ErrorCode ret = CAMERA_OK;
        Camera_ImageRotation imageRotation;
        bool isMirSupported;
        OH_PhotoOutput_IsMirrorSupported(photoOutput_, &isMirSupported);
        OH_PhotoOutput_GetPhotoRotation(photoOutput_, degree, &imageRotation);
    
        Camera_PhotoCaptureSetting curPhotoSetting = {
            quality : QUALITY_LEVEL_HIGH,
            rotation : imageRotation,
            mirror : isMirSupported
        };
        ret = OH_PhotoOutput_Capture_WithCaptureSetting(photoOutput_, curPhotoSetting);
        OH_LOG_INFO(LOG_APP, "TakePicture get quality %{public}d, rotation %{public}d, mirror %{public}d",
            curPhotoSetting.quality, curPhotoSetting.rotation, curPhotoSetting.mirror);
        OH_LOG_INFO(LOG_APP, "TakePicture ret = %{public}d.", ret);
        return ret;
    }

High-Performance Photo Capture

Starting from API version 21, the high-performance photo capture feature is supported, which allows you to set an explicit photo quality prioritization strategy during one-shot photo capture.

The experience of one-shot photo capture is primarily measured by image output speed and final image quality. Therefore, to meet the differentiated requirements of developers in different scenarios, the emphasis on these two metrics varies. For example, street snapshots require quickly capturing the moment, while landscape or portrait photography pursues ultimate image quality.

Only one-shot photo capture supports setting the photo quality prioritization strategy. If the strategy is set in deferred photo capture, the setting will not take effect.

[h2]Photo Quality Prioritization Strategy

When using one-shot photo capture, you can set two types of photo quality prioritization strategies: speed priority and high quality priority, which correspond to different Camera_PhotoQualityPrioritization enumeration values.

[h2]How to Correctly Set the Photo Quality Prioritization Strategy

To correctly set the photo quality prioritization strategy in one-shot photo capture, the high-performance photo capture feature provides the following two APIs:

[h2]How to Develop

The high-performance photo capture APIs must be called during the enabling step of the Camera Session Management (C/C++) process.

The specific calling timing is as follows:

Status Listening

During camera application development, you can listen for the status of the photo output stream, including the start of the photo stream, the start and end of the photo frame, and the errors of the photo output stream.