• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  ```