1# 拍照(C/C++) 2<!--Kit: Camera Kit--> 3<!--Subsystem: Multimedia--> 4<!--Owner: @qano--> 5<!--Designer: @leo_ysl--> 6<!--Tester: @xchaosioda--> 7<!--Adviser: @zengyawen--> 8 9拍照是相机的最重要功能之一,拍照模块基于相机复杂的逻辑,为了保证用户拍出的照片质量,在中间步骤可以设置分辨率、闪光灯、焦距、照片质量及旋转角度等信息。 10 11## 开发步骤 12 13详细的API说明请参考[Camera API参考](../../reference/apis-camera-kit/capi-oh-camera.md)。 14 151. 导入NDK接口,接口中提供了相机相关的属性和方法,导入方法如下。 16 17 ```c++ 18 // 导入NDK接口头文件。 19 #include <cstdint> 20 #include <cstdlib> 21 #include <cstring> 22 #include <string.h> 23 #include "hilog/log.h" 24 #include "ohcamera/camera.h" 25 #include "ohcamera/camera_input.h" 26 #include "ohcamera/capture_session.h" 27 #include "ohcamera/photo_output.h" 28 #include "ohcamera/preview_output.h" 29 #include "ohcamera/video_output.h" 30 #include "ohcamera/camera_manager.h" 31 #include <multimedia/image_framework/image/image_native.h> 32 ``` 33 342. 在CMake脚本中链接相关动态库。 35 36 ```txt 37 target_link_libraries(entry PUBLIC 38 libace_napi.z.so 39 libhilog_ndk.z.so 40 libnative_buffer.so 41 libohcamera.so 42 libohimage.so 43 libohfileuri.so 44 ) 45 ``` 46 473. 创建并打开相机设备,参考[ 设备输入(C/C++)](./native-camera-device-input.md)步骤3-5。 48 494. 选择设备支持的输出流能力,创建拍照输出流。 50 51 通过[OH_CameraManager_CreatePhotoOutputWithoutSurface()](../../reference/apis-camera-kit/capi-camera-manager-h.md#oh_cameramanager_createphotooutputwithoutsurface)方法创建拍照输出流。 52 53 ```c++ 54 Camera_PhotoOutput* CreatePhotoOutput(Camera_Manager* cameraManager, const Camera_Profile* photoProfile) { 55 Camera_PhotoOutput* photoOutput = nullptr; 56 // 无需传入surfaceId,直接创建拍照流。 57 Camera_ErrorCode ret = OH_CameraManager_CreatePhotoOutputWithoutSurface(cameraManager, photoProfile, &photoOutput); 58 if (photoOutput == nullptr || ret != CAMERA_OK) { 59 OH_LOG_ERROR(LOG_APP, "OH_CameraManager_CreatePhotoOutputWithoutSurface failed."); 60 } 61 return photoOutput; 62 } 63 ``` 64 655. 注册单段式(PhotoAvailable)拍照回调,若应用希望快速得到回图,推荐使用[分段式拍照回调(PhotoAssetAvailable)](./native-camera-deferred-capture.md)。 66 67 > **注意:** 68 > 69 > 如果已经注册了PhotoAssetAvailable回调,并且在Session开始之后又注册了PhotoAvailable回调,PhotoAssetAvailable和PhotoAvailable同时注册,会导致流被重启,仅PhotoAssetAvailable生效。 70 > 71 > 不建议开发者同时注册PhotoAssetAvailable和PhotoAvailable。 72 73 **单段式拍照开发流程(PhotoAssetAvailable)**: 74 75 - 在会话commitConfig前注册单段式拍照回调。 76 - 在单段式拍照回调函数中获取图片信息,解析出buffer数据,做自定义业务处理。 77 - 将处理完的buffer通过回调传给ArkTS侧,做图片显示或通过安全控件写文件保存图片。 78 - 使用完后解注册单段式拍照回调函数。 79 80 ```c++ 81 // 保存NAPI侧注册的buffer处理回调函数。 82 static void* bufferCb = nullptr; 83 Camera_ErrorCode RegisterBufferCb(void* cb) { 84 OH_LOG_INFO(LOG_APP, " RegisterBufferCb start"); 85 if (cb == nullptr) { 86 OH_LOG_INFO(LOG_APP, " RegisterBufferCb invalid error"); 87 return CAMERA_INVALID_ARGUMENT; 88 } 89 bufferCb = cb; 90 return CAMERA_OK; 91 } 92 93 // 单段式拍照回调函数。 94 void OnPhotoAvailable(Camera_PhotoOutput* photoOutput, OH_PhotoNative* photo) { 95 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable start!"); 96 OH_ImageNative* imageNative; 97 Camera_ErrorCode errCode = OH_PhotoNative_GetMainImage(photo, &imageNative); 98 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable errCode:%{public}d imageNative:%{public}p", errCode, imageNative); 99 // 读取OH_ImageNative的 size 属性。 100 Image_Size size; 101 Image_ErrorCode imageErr = OH_ImageNative_GetImageSize(imageNative, &size); 102 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d width:%{public}d height:%{public}d", imageErr, 103 size.width, size.height); 104 // 读取OH_ImageNative的组件列表的元素个数。 105 size_t componentTypeSize = 0; 106 imageErr = OH_ImageNative_GetComponentTypes(imageNative, nullptr, &componentTypeSize); 107 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d componentTypeSize:%{public}zu", imageErr, 108 componentTypeSize); 109 // 读取OH_ImageNative的组件列表。 110 uint32_t* components = new uint32_t[componentTypeSize]; 111 imageErr = OH_ImageNative_GetComponentTypes(imageNative, &components, &componentTypeSize); 112 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable OH_ImageNative_GetComponentTypes imageErr:%{public}d", imageErr); 113 // 读取OH_ImageNative的第一个组件所对应的缓冲区对象。 114 OH_NativeBuffer* nativeBuffer = nullptr; 115 imageErr = OH_ImageNative_GetByteBuffer(imageNative, components[0], &nativeBuffer); 116 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable OH_ImageNative_GetByteBuffer imageErr:%{public}d", imageErr); 117 // 读取OH_ImageNative的第一个组件所对应的缓冲区大小。 118 size_t nativeBufferSize = 0; 119 imageErr = OH_ImageNative_GetBufferSize(imageNative, components[0], &nativeBufferSize); 120 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d nativeBufferSize:%{public}zu", imageErr, 121 nativeBufferSize); 122 // 读取OH_ImageNative的第一个组件所对应的像素行宽。 123 int32_t rowStride = 0; 124 imageErr = OH_ImageNative_GetRowStride(imageNative, components[0], &rowStride); 125 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d rowStride:%{public}d", imageErr, rowStride); 126 // 读取OH_ImageNative的第一个组件所对应的像素大小。 127 int32_t pixelStride = 0; 128 imageErr = OH_ImageNative_GetPixelStride(imageNative, components[0], &pixelStride); 129 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable imageErr:%{public}d pixelStride:%{public}d", imageErr, pixelStride); 130 // 将ION内存映射到进程空间。 131 void* virAddr = nullptr; // 指向映射内存的虚拟地址,解除映射后这个指针将不再有效。 132 int32_t ret = OH_NativeBuffer_Map(nativeBuffer, &virAddr); // 映射后通过第二个参数virAddr返回内存的首地址。 133 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable OH_NativeBuffer_Map err:%{public}d", ret); 134 // 调用NAPI层buffer回调。 135 auto cb = (void (*)(void *, size_t))(bufferCb); 136 if (!virAddr || nativeBufferSize <= 0) { 137 OH_LOG_INFO(LOG_APP, "On buffer callback failed"); 138 return; 139 } 140 cb(virAddr, nativeBufferSize); 141 // 释放资源。 142 delete[] components; 143 OH_ImageNative_Release(imageNative); 144 ret = OH_NativeBuffer_Unmap(nativeBuffer); // 在处理完之后,解除映射并释放缓冲区。 145 if (ret != 0) { 146 OH_LOG_ERROR(LOG_APP, "OnPhotoAvailable OH_NativeBuffer_Unmap error:%{public}d", ret); 147 } 148 OH_LOG_INFO(LOG_APP, "OnPhotoAvailable end"); 149 } 150 151 // 注册单段式拍照回调。 152 Camera_ErrorCode PhotoOutputRegisterPhotoAvailableCallback(Camera_PhotoOutput* photoOutput) { 153 OH_LOG_INFO(LOG_APP, "PhotoOutputRegisterPhotoAvailableCallback start!"); 154 Camera_ErrorCode ret = OH_PhotoOutput_RegisterPhotoAvailableCallback(photoOutput, OnPhotoAvailable); 155 if (ret != CAMERA_OK) { 156 OH_LOG_ERROR(LOG_APP, "PhotoOutputRegisterPhotoAvailableCallback failed."); 157 } 158 OH_LOG_INFO(LOG_APP, "PhotoOutputRegisterPhotoAvailableCallback return with ret code: %{public}d!", ret); 159 return ret; 160 } 161 162 // 解注册单段式拍照回调。 163 Camera_ErrorCode PhotoOutputUnRegisterPhotoAvailableCallback(Camera_PhotoOutput* photoOutput) { 164 OH_LOG_INFO(LOG_APP, "PhotoOutputUnRegisterPhotoAvailableCallback start!"); 165 Camera_ErrorCode ret = OH_PhotoOutput_UnregisterPhotoAvailableCallback(photoOutput, OnPhotoAvailable); 166 if (ret != CAMERA_OK) { 167 OH_LOG_ERROR(LOG_APP, "PhotoOutputUnRegisterPhotoAvailableCallback failed."); 168 } 169 OH_LOG_INFO(LOG_APP, "PhotoOutputUnRegisterPhotoAvailableCallback return with ret code: %{public}d!", ret); 170 return ret; 171 } 172 ``` 173 174 NAPI层buffer回处理参考示例代码: 175 176 ```c++ 177 static napi_ref bufferCbRef_ = nullptr; 178 static napi_env env_; 179 size_t g_size = 0; 180 181 // NAPI层buffer回调方法。 182 static void BufferCb(void* buffer, size_t size) { 183 OH_LOG_INFO(LOG_APP, "BufferCb size:%{public}zu", size); 184 g_size = size; 185 napi_value asyncResource = nullptr; 186 napi_value asyncResourceName = nullptr; 187 napi_async_work work; 188 189 void* copyBuffer = malloc(size); 190 if (copyBuffer == nullptr) { 191 return; 192 } 193 OH_LOG_INFO(LOG_APP, "BufferCb copyBuffer:%{public}p", copyBuffer); 194 // 使用 std::memcpy 复制 buffer 的内容到 copyBuffer。 195 std::memcpy(copyBuffer, buffer, size); 196 napi_create_string_utf8(env_, "BufferCb", NAPI_AUTO_LENGTH, &asyncResourceName); 197 napi_status status = napi_create_async_work( 198 env_, asyncResource, asyncResourceName, [](napi_env env, void* copyBuffer) {}, 199 [](napi_env env, napi_status status, void* copyBuffer) { 200 napi_value retVal; 201 napi_value callback = nullptr; 202 void* data = nullptr; 203 napi_value arrayBuffer = nullptr; 204 size_t bufferSize = g_size; 205 napi_create_arraybuffer(env, bufferSize, &data, &arrayBuffer); 206 std::memcpy(data, copyBuffer, bufferSize); 207 OH_LOG_INFO(LOG_APP, "BufferCb g_size: %{public}zu", g_size); 208 napi_get_reference_value(env, bufferCbRef_, &callback); 209 if (callback) { 210 OH_LOG_INFO(LOG_APP, "BufferCb callback is full"); 211 } else { 212 OH_LOG_ERROR(LOG_APP, "BufferCb callback is null"); 213 } 214 // 调用ArkTS的buffer处理回调函数,将图片arrayBuffer传给页面做显示或保存。 215 napi_call_function(env, nullptr, callback, 1, &arrayBuffer, &retVal); 216 // 清理内存。 217 free(data); // 释放在异步工作中分配的内存。 218 }, 219 copyBuffer, &work); 220 221 // 错误检查:创建异步工作失败时释放内存。 222 if (status != napi_ok) { 223 OH_LOG_ERROR(LOG_APP, "Failed to create async work"); 224 free(copyBuffer); // 释放分配的内存。 225 return; 226 } 227 napi_queue_async_work_with_qos(env_, work, napi_qos_user_initiated); 228 } 229 230 // 保存ArkTS侧传入的buffer处理回调函数。 231 static napi_value SetBufferCb(napi_env env, napi_callback_info info) { 232 OH_LOG_INFO(LOG_APP, "SetBufferCb start"); 233 napi_value result; 234 napi_get_undefined(env, &result); 235 236 napi_value argValue[1] = {nullptr}; 237 size_t argCount = 1; 238 napi_get_cb_info(env, info, &argCount, argValue, nullptr, nullptr); 239 240 env_ = env; 241 napi_create_reference(env, argValue[0], 1, &bufferCbRef_); 242 if (bufferCbRef_) { 243 OH_LOG_INFO(LOG_APP, "SetBufferCb callbackRef is full"); 244 } else { 245 OH_LOG_ERROR(LOG_APP, "SetBufferCb callbackRef is null"); 246 } 247 // 注册ArkTS侧buffer回调到NAPI层。 248 RegisterBufferCb((void *)BufferCb); 249 return result; 250 } 251 ``` 252 2536. 创建拍照类型会话,参考[会话管理(C/C++)](./native-camera-session-management.md),开启会话,准备拍照。 254 2557. 配置拍照参数(可选)。 256 配置相机的参数可以调整拍照的一些功能,包括闪光灯、变焦、焦距等。 257 258 ```c++ 259 // 判断设备是否支持闪光灯。 260 bool HasFlash(Camera_CaptureSession* captureSession) 261 { 262 bool hasFlash = false; 263 Camera_ErrorCode ret = OH_CaptureSession_HasFlash(captureSession, &hasFlash); 264 if (ret != CAMERA_OK) { 265 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_HasFlash failed."); 266 } 267 if (hasFlash) { 268 OH_LOG_INFO(LOG_APP, "hasFlash success"); 269 } else { 270 OH_LOG_ERROR(LOG_APP, "hasFlash fail"); 271 } 272 return hasFlash; 273 } 274 275 // 检测闪光灯模式是否支持。 276 bool IsFlashModeSupported(Camera_CaptureSession* captureSession, Camera_FlashMode flashMode) 277 { 278 bool isSupported = false; 279 Camera_ErrorCode ret = OH_CaptureSession_IsFlashModeSupported(captureSession, flashMode, &isSupported); 280 if (ret != CAMERA_OK) { 281 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_IsFlashModeSupported failed."); 282 } 283 return isSupported; 284 } 285 // 在支持flashMode的情况下进行调用OH_CaptureSession_SetFlashMode。 286 Camera_ErrorCode SetFocusMode(Camera_CaptureSession* captureSession, Camera_FlashMode flashMode) 287 { 288 Camera_ErrorCode ret = OH_CaptureSession_SetFlashMode(captureSession, flashMode); 289 if (ret == CAMERA_OK) { 290 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_SetFlashMode success."); 291 } else { 292 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_SetFlashMode failed. %{public}d ", ret); 293 } 294 return ret; 295 } 296 297 // 判断是否支持连续自动变焦模式。 298 bool IsFocusModeSupported(Camera_CaptureSession* captureSession, Camera_FocusMode focusMode) 299 { 300 bool isFocusModeSupported = false; 301 Camera_ErrorCode ret = OH_CaptureSession_IsFocusModeSupported(captureSession, focusMode, &isFocusModeSupported); 302 if (ret != CAMERA_OK) { 303 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_IsFocusModeSupported failed."); 304 } 305 return isFocusModeSupported; 306 } 307 // 在支持focusMode的情况下进行OH_CaptureSession_SetFocusMode。 308 Camera_ErrorCode SetFocusMode(Camera_CaptureSession* captureSession, Camera_FocusMode focusMode) 309 { 310 Camera_ErrorCode ret = OH_CaptureSession_SetFocusMode(captureSession, focusMode); 311 if (ret != CAMERA_OK) { 312 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_SetFocusMode failed. %{public}d ", ret); 313 } 314 return ret; 315 } 316 317 // 获取相机支持的可变焦距比范围。 318 Camera_ErrorCode GetZoomRatioRange(Camera_CaptureSession* captureSession, float* minZoom, float* maxZoom) 319 { 320 Camera_ErrorCode ret = OH_CaptureSession_GetZoomRatioRange(captureSession, minZoom, maxZoom); 321 if (ret != CAMERA_OK) { 322 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_GetZoomRatioRange failed."); 323 } else { 324 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_GetZoomRatioRange success. minZoom: %{public}f, maxZoom:%{public}f", 325 *minZoom, *maxZoom); 326 } 327 return ret; 328 } 329 330 // 设置变焦,zoom需要在可变焦距比范围内。 331 Camera_ErrorCode SetZoomRatio(Camera_CaptureSession* captureSession, float zoom) 332 { 333 Camera_ErrorCode ret = OH_CaptureSession_SetZoomRatio(captureSession, zoom); 334 if (ret == CAMERA_OK) { 335 OH_LOG_INFO(LOG_APP, "OH_CaptureSession_SetZoomRatio success."); 336 } else { 337 OH_LOG_ERROR(LOG_APP, "OH_CaptureSession_SetZoomRatio failed. %{public}d ", ret); 338 } 339 return ret; 340 } 341 ``` 342 3438. 触发拍照。 344 345 通过[OH_PhotoOutput_Capture()](../../reference/apis-camera-kit/capi-photo-output-h.md#oh_photooutput_capture)方法,执行拍照任务。 346 347 ```c++ 348 Camera_ErrorCode Capture(Camera_PhotoOutput* photoOutput) 349 { 350 Camera_ErrorCode ret = OH_PhotoOutput_Capture(photoOutput); 351 if (ret == CAMERA_OK) { 352 OH_LOG_INFO(LOG_APP, "OH_PhotoOutput_Capture success "); 353 } else { 354 OH_LOG_ERROR(LOG_APP, "OH_PhotoOutput_Capture failed. %d ", ret); 355 } 356 return ret; 357 } 358 ``` 359 360## 状态监听 361 362在相机应用开发过程中,可以随时监听拍照输出流状态,包括拍照流开始、拍照帧的开始与结束、拍照输出流的错误。 363 364- 通过注册固定的onFrameStart回调函数获取监听拍照开始结果,photoOutput创建成功时即可监听,拍照第一次曝光时触发。 365 ```c++ 366 void PhotoOutputOnFrameStart(Camera_PhotoOutput* photoOutput) 367 { 368 OH_LOG_INFO(LOG_APP, "PhotoOutputOnFrameStart"); 369 } 370 void PhotoOutputOnFrameShutter(Camera_PhotoOutput* photoOutput, Camera_FrameShutterInfo* info) 371 { 372 OH_LOG_INFO(LOG_APP, "PhotoOutputOnFrameShutter"); 373 } 374 ``` 375 376- 通过注册固定的onFrameEnd回调函数获取监听拍照结束结果,photoOutput创建成功时即可监听。 377 378 ```c++ 379 void PhotoOutputOnFrameEnd(Camera_PhotoOutput* photoOutput, int32_t frameCount) 380 { 381 OH_LOG_INFO(LOG_APP, "PhotoOutput frameCount = %{public}d", frameCount); 382 } 383 ``` 384 385- 通过注册固定的onError回调函数获取监听拍照输出流的错误结果。callback返回拍照输出接口使用错误时的对应错误码,错误码类型参见[Camera_ErrorCode](../../reference/apis-camera-kit/capi-camera-h.md#camera_errorcode)。 386 387 ```c++ 388 void PhotoOutputOnError(Camera_PhotoOutput* photoOutput, Camera_ErrorCode errorCode) 389 { 390 OH_LOG_INFO(LOG_APP, "PhotoOutput errorCode = %{public}d", errorCode); 391 } 392 ``` 393 ```c++ 394 PhotoOutput_Callbacks* GetPhotoOutputListener() 395 { 396 static PhotoOutput_Callbacks photoOutputListener = { 397 .onFrameStart = PhotoOutputOnFrameStart, 398 .onFrameShutter = PhotoOutputOnFrameShutter, 399 .onFrameEnd = PhotoOutputOnFrameEnd, 400 .onError = PhotoOutputOnError 401 }; 402 return &photoOutputListener; 403 } 404 Camera_ErrorCode RegisterPhotoOutputCallback(Camera_PhotoOutput* photoOutput) 405 { 406 Camera_ErrorCode ret = OH_PhotoOutput_RegisterCallback(photoOutput, GetPhotoOutputListener()); 407 if (ret != CAMERA_OK) { 408 OH_LOG_ERROR(LOG_APP, "OH_PhotoOutput_RegisterCallback failed."); 409 } 410 return ret; 411 } 412 ```