• 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- 在第一阶段,系统快速上报轻量处理的图片,轻量处理的图片比全质量图低,出图速度快。应用通过回调会收到一个PhotoAsset对象,通过该对象可调用媒体库接口,读取图片或落盘图片。
12- 在第二阶段,相机框架会根据应用的请求图片诉求或者在系统闲时,进行图像增强处理得到全质量图,将处理好的图片传回给媒体库,替换轻量处理的图片。
13
14## 开发步骤
15
16详细的API说明请参考[Camera API参考](../../reference/apis-camera-kit/capi-oh-camera.md)。
17
181. 导入NDK接口,接口中提供了相机相关的属性和方法,导入方法如下。
19
20   ```c++
21   // 导入NDK接口头文件。
22   #include <cstdint>
23   #include <cstdlib>
24   #include <cstring>
25   #include <string.h>
26   #include <memory>
27   #include "hilog/log.h"
28   #include "napi/native_api.h"
29   #include <ohcamera/camera.h>
30   #include <ohcamera/photo_output.h>
31   #include <ohcamera/camera_manager.h>
32   #include <multimedia/media_library/media_asset_manager_capi.h>
33   #include <multimedia/media_library/media_asset_change_request_capi.h>
34   #include <multimedia/media_library/media_access_helper_capi.h>
35   #include <multimedia/image_framework/image/image_packer_native.h>
36   ```
37
382. 在CMake脚本中链接相关动态库。
39
40   ```txt
41   target_link_libraries(entry PUBLIC
42       libace_napi.z.so
43       libhilog_ndk.z.so
44       libohcamera.so
45       libimage_source.so
46       libmedia_asset_manager.so
47       libimage_packer.so
48   )
49   ```
50
513. 相机初始化及拍照触发参考[拍照(C/C++)](./native-camera-shooting.md)。
52
534. 注册分段式(PhotoAssetAvailable)拍照回调,对比单端式拍照,仅注册的拍照回调接口不同。
54
55   > **注意:**
56   >
57   > 如果已经注册了PhotoAssetAvailable回调,并且在Session开始之后又注册了PhotoAvailable回调,PhotoAssetAvailable和PhotoAvailable同时注册,会导致流被重启,仅PhotoAssetAvailable生效。
58   >
59   > 不建议开发者同时注册PhotoAssetAvailable和PhotoAvailable。
60
61   注册PhotoAssetAvailableCallback回调,接收分段式拍照回图示例:
62
63   **分段式拍照开发流程(PhotoAssetAvailableCallback)**:
64
65   - 在会话commitConfig前注册分段式拍照回调。
66   - 通过分段式拍照回调,获取媒体库资源mediaAsset。
67   - 通过mediaAsset直接落盘图片或者通过mediaAsset配置策略模式请求图像资源,业务处理后通过buffer保存图片,或显示图片(参考[拍照(C/C++)](./native-camera-shooting.md)步骤5)。
68   - 使用完后解注册分段式拍照回调函数。
69
70   ```c++
71   // 方式一:调用媒体库落盘图片。
72   void mediaLibSavePhoto(OH_MediaAsset* mediaAsset) {
73       if (mediaAsset == nullptr) {
74           OH_LOG_ERROR(LOG_APP, "mediaAsset is nullptr!");
75           return;
76       }
77       // 创建媒体资产更改请求对象。
78       OH_MediaAssetChangeRequest* changeRequest = OH_MediaAssetChangeRequest_Create(mediaAsset);
79       if (changeRequest == nullptr) {
80           OH_LOG_ERROR(LOG_APP, "changeRequest is nullptr!");
81           return;
82       }
83       // 请求保存图片。
84       MediaLibrary_ErrorCode errCode =
85           OH_MediaAssetChangeRequest_SaveCameraPhoto(changeRequest, MEDIA_LIBRARY_IMAGE_JPEG);
86       OH_LOG_INFO(LOG_APP, "SaveCameraPhoto get errCode:%{public}d", errCode);
87       // 发起请求。
88       errCode = OH_MediaAccessHelper_ApplyChanges(changeRequest);
89       OH_LOG_INFO(LOG_APP, "ApplyChanges get errCode:%{public}d", errCode);
90   }
91
92   // 方式二:调用媒体库接口请求图像资源。
93   // 图像源准备就绪时调用。
94   OH_MediaAsset* g_mediaAsset_;
95   void OnImageDataPrepared(MediaLibrary_ErrorCode result, MediaLibrary_RequestId requestId,
96                            MediaLibrary_MediaQuality mediaQuality, MediaLibrary_MediaContentType type,
97                            OH_ImageSourceNative* imageSourceNative) {
98       if (imageSourceNative == nullptr) {
99           OH_LOG_ERROR(LOG_APP, "OnImageDataPrepared: imageSourceNative is nullptr!");
100           return;
101       }
102       if (mediaQuality == MediaLibrary_MediaQuality::MEDIA_LIBRARY_QUALITY_FAST) {
103           OH_LOG_INFO(LOG_APP, "OnImageDataPrepared: Using fast media quality.");
104       } else if (mediaQuality == MediaLibrary_MediaQuality::MEDIA_LIBRARY_QUALITY_FULL) {
105           OH_LOG_INFO(LOG_APP, "OnImageDataPrepared: Using full media quality.");
106       }
107       // 通过OH_ImageSourceNative创建OH_PixelmapNative。
108       OH_PixelmapNative* pixelmapNative = nullptr;
109       OH_DecodingOptions* decodingOptions = nullptr;
110       Image_ErrorCode imageErr = IMAGE_SUCCESS;
111       imageErr = OH_ImageSourceNative_CreatePixelmap(imageSourceNative, decodingOptions, &pixelmapNative);
112       if (imageErr != IMAGE_SUCCESS) {
113           OH_LOG_ERROR(LOG_APP, "OnImageDataPrepared: CreatePixelmap failed.");
114           return;
115       }
116       // 创建Image Packer并设置打包选项。
117       OH_ImagePackerNative* imagePacker;
118       OH_ImagePackerNative_Create(&imagePacker);
119       OH_PackingOptions* options;
120       OH_PackingOptions_Create(&options);
121       char format[] = "image/jpeg";
122       Image_MimeType image_MimeType = {format, strlen(format)};
123       OH_PackingOptions_SetMimeType(options, &image_MimeType);
124       OH_PackingOptions_SetQuality(options, 100); // 最高质量100。
125       size_t bufferSize = 3072 * 4096; // 传大于编码后的size大小,编码后会重新赋值。
126       // 解析出图片buffer。
127       auto buffer = std::make_unique<uint8_t[]>(bufferSize);
128       imageErr = OH_ImagePackerNative_PackToDataFromPixelmap(imagePacker, options, pixelmapNative, buffer.get(), &bufferSize);
129       OH_LOG_INFO(LOG_APP, "OnImageDataPrepared: packToData ret code:%{public}u outsize:%{public}zu", imageErr, bufferSize);
130       if (g_mediaAsset_ == nullptr) {
131           OH_LOG_ERROR(LOG_APP,  "OnImageDataPrepared: get current mediaAsset failed!");
132           return;
133       }
134       // 调用媒体库接口通过buffer存图。
135       OH_MediaAssetChangeRequest* changeRequest = OH_MediaAssetChangeRequest_Create(g_mediaAsset_);
136       MediaLibrary_ErrorCode mediaErr = OH_MediaAssetChangeRequest_AddResourceWithBuffer(changeRequest,
137           MEDIA_LIBRARY_IMAGE_RESOURCE, buffer.get(), bufferSize);
138       OH_LOG_INFO(LOG_APP,  "OnImageDataPrepared: AddResourceWithBuffer get errCode:%{public}d", mediaErr);
139       mediaErr = OH_MediaAssetChangeRequest_SaveCameraPhoto(changeRequest, MEDIA_LIBRARY_IMAGE_JPEG);
140       OH_LOG_INFO(LOG_APP,  "OnImageDataPrepared: SaveCameraPhoto get errCode:%{public}d", mediaErr);
141       mediaErr = OH_MediaAccessHelper_ApplyChanges(changeRequest);
142       OH_LOG_INFO(LOG_APP,  "OnImageDataPrepared: ApplyChanges get errCode:%{public}d", mediaErr);
143   }
144
145   void mediaLibRequestBuffer(OH_MediaAsset* mediaAsset) {
146       if (mediaAsset == nullptr) {
147           OH_LOG_ERROR(LOG_APP, "mediaAsset is nullptr!");
148           return;
149       }
150       // 创建媒体资产管理器。
151       OH_MediaAssetManager* mediaAssetManager = OH_MediaAssetManager_Create();
152       if (mediaAssetManager == nullptr) {
153           OH_LOG_ERROR(LOG_APP, "mediaAssetManager is nullptr!");
154           return;
155       }
156       // 配置请求媒体图片参数。
157       MediaLibrary_RequestOptions requestOptions;
158       // 按照业务需求配置策略模式请求图像资源。
159       // MEDIA_LIBRARY_FAST_MODE:仅接收一阶段低质量图回调。
160       // MEDIA_LIBRARY_HIGH_QUALITY_MODE:仅接收二阶段全质量图回调。
161       // MEDIA_LIBRARY_BALANCED_MODE:接收一阶段及二阶段图片回调。
162       requestOptions.deliveryMode = MEDIA_LIBRARY_FAST_MODE;
163       MediaLibrary_RequestId requestId;
164       // 请求图像资源,图像源准备就绪时调用onImageDataPrepared。
165       MediaLibrary_ErrorCode result = OH_MediaAssetManager_RequestImage(mediaAssetManager, mediaAsset, requestOptions, &requestId, OnImageDataPrepared);
166       if (result != MEDIA_LIBRARY_OK) {
167           OH_LOG_ERROR(LOG_APP, "OH_MediaAssetManager_RequestImage failed.");
168       }
169   }
170
171   // 分段式拍照回调函数。
172   void OnPhotoAssetAvailable(Camera_PhotoOutput* photoOutput, OH_MediaAsset* mediaAsset) {
173       OH_LOG_INFO(LOG_APP, "OnPhotoAssetAvailable start!");
174       if (mediaAsset == nullptr) {
175           OH_LOG_ERROR(LOG_APP, "OnPhotoAssetAvailable mediaAsset nullptr!");
176           return;
177       }
178       // 处理方式一:调用媒体库接口落盘图片,先保存一阶段图,二阶段图就绪后媒体库会主动帮应用替换落盘图片。
179       // mediaLibSavePhoto(mediaAsset);
180       // 处理方式二:调用媒体库接口请求图像资源,获取一阶段图或二阶段图buffer,业务处理后通过buffer存图。
181       g_mediaAsset_ = mediaAsset;
182       mediaLibRequestBuffer(mediaAsset);
183   }
184
185   // 注册分段式拍照回调。
186   Camera_ErrorCode PhotoOutputRegisterPhotoAssetAvailableCallback(Camera_PhotoOutput* photoOutput) {
187       Camera_ErrorCode ret = OH_PhotoOutput_RegisterPhotoAssetAvailableCallback(photoOutput, OnPhotoAssetAvailable);
188       if (ret != CAMERA_OK) {
189           OH_LOG_ERROR(LOG_APP, "OH_PhotoOutput_RegisterPhotoAssetAvailableCallback failed.");
190       }
191       return ret;
192   }
193   ```
194
195