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