1 /*
2  * Copyright (c) 2024-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "hcamera_preconfig.h"
17 
18 #include <cstdint>
19 #include <memory>
20 #include <queue>
21 #include <string>
22 #include <utility>
23 
24 #include "ability/camera_ability_parse_util.h"
25 #include "camera_stream_info_parse.h"
26 #include "camera_device_ability_items.h"
27 #include "camera_metadata_info.h"
28 #include "hcamera_host_manager.h"
29 #include "metadata_utils.h"
30 #include "v1_3/types.h"
31 
32 namespace OHOS {
33 namespace CameraStandard {
34 namespace {
35 
36 static constexpr float RATIO_VALUE_1_1 = 1.0f;
37 static constexpr float RATIO_VALUE_4_3 = 4.0f / 3;
38 static constexpr float RATIO_VALUE_16_9 = 16.0f / 9;
39 
40 enum PreconfigType {
41     PRECONFIG_TYPE_720P,
42     PRECONFIG_TYPE_1080P,
43     PRECONFIG_TYPE_4K,
44     PRECONFIG_TYPE_HIGH_QUALITY,
45 };
46 
47 enum PreconfigRatio {
48     RATIO_1_1,
49     RATIO_4_3,
50     RATIO_16_9,
51 };
52 
53 struct CameraInfo {
54     std::string cameraId;
55     std::shared_ptr<OHOS::Camera::CameraMetadata> ability;
56 };
57 
58 template<typename T>
GetMaxSizeDetailInfo(std::vector<T> & detailInfos,float targetRatioValue,camera_format_t format)59 std::shared_ptr<T> GetMaxSizeDetailInfo(std::vector<T>& detailInfos, float targetRatioValue, camera_format_t format)
60 {
61     CHECK_ERROR_RETURN_RET(targetRatioValue <= 0, nullptr);
62     std::shared_ptr<T> maxSizeProfile = nullptr;
63     for (auto& detailInfo : detailInfos) {
64         if (detailInfo.width == 0 || detailInfo.height == 0 || detailInfo.format != format) {
65             continue;
66         }
67         float ratio = ((float)detailInfo.width) / detailInfo.height;
68         if (abs(ratio - targetRatioValue) / targetRatioValue > 0.05f) { // 0.05f is 5% tolerance.
69             continue;
70         }
71         if (maxSizeProfile == nullptr || detailInfo.width > maxSizeProfile->width) {
72             maxSizeProfile = std::make_shared<T>(detailInfo);
73         }
74     }
75     return maxSizeProfile;
76 }
77 
78 struct PreconfigProfile {
79     std::string format;
80     int32_t width;
81     int32_t height;
82     int32_t fpsMin;
83     int32_t fpsMax;
84     int32_t fpsPrefer;
85     bool followSensorMax = false;
86     PreconfigRatio followSensorMaxRatio = RATIO_4_3;
87 
toStringOHOS::CameraStandard::__anon1ca33ed60110::PreconfigProfile88     std::string toString()
89     {
90         return "Format:" + format + "\tSize:" + std::to_string(width) + "x" + std::to_string(height) +
91                "\tFps:" + std::to_string(fpsMin) + "-" + std::to_string(fpsMax) +
92                ",prefer:" + std::to_string(fpsPrefer);
93     }
94 
GetRatioValueOHOS::CameraStandard::__anon1ca33ed60110::PreconfigProfile95     float GetRatioValue(PreconfigRatio preconfigRatio)
96     {
97         float ratioValue = RATIO_VALUE_4_3;
98         switch (preconfigRatio) {
99             case RATIO_1_1:
100                 ratioValue = RATIO_VALUE_1_1;
101                 break;
102             case RATIO_4_3:
103                 ratioValue = RATIO_VALUE_4_3;
104                 break;
105             case RATIO_16_9:
106                 ratioValue = RATIO_VALUE_16_9;
107                 break;
108             default:
109                 // Do nothing
110                 break;
111         }
112         return ratioValue;
113     }
114 
FindMaxDetailInfoFromProfileLevelOHOS::CameraStandard::__anon1ca33ed60110::PreconfigProfile115     std::shared_ptr<ProfileDetailInfo> FindMaxDetailInfoFromProfileLevel(
116         CameraInfo& cameraInfo, HDI::Camera::V1_3::OperationMode modeName)
117     {
118         camera_metadata_item_t item;
119         int ret = OHOS::Camera::CameraMetadata::FindCameraMetadataItem(
120             cameraInfo.ability->get(), OHOS_ABILITY_AVAILABLE_PROFILE_LEVEL, &item);
121         CHECK_ERROR_RETURN_RET(ret != CAM_META_SUCCESS || item.count == 0, nullptr);
122         std::vector<SpecInfo> specInfos;
123         ProfileLevelInfo modeInfo = {};
124         CameraAbilityParseUtil::GetModeInfo(modeName, item, modeInfo);
125         specInfos.insert(specInfos.end(), modeInfo.specInfos.begin(), modeInfo.specInfos.end());
126         for (SpecInfo& specInfo : specInfos) {
127             for (StreamInfo& streamInfo : specInfo.streamInfos) {
128                 if (streamInfo.streamType != HDI::Camera::V1_3::StreamType::STREAM_TYPE_STILL_CAPTURE) {
129                     continue;
130                 }
131                 float ratioValue = GetRatioValue(followSensorMaxRatio);
132                 return GetMaxSizeDetailInfo(streamInfo.detailInfos, ratioValue, OHOS_CAMERA_FORMAT_JPEG);
133             }
134         }
135         return nullptr;
136     }
137 
FindMaxDetailInfoFromExtendConfigOHOS::CameraStandard::__anon1ca33ed60110::PreconfigProfile138     std::shared_ptr<DetailInfo> FindMaxDetailInfoFromExtendConfig(
139         CameraInfo& cameraInfo, HDI::Camera::V1_3::OperationMode modeName)
140     {
141         camera_metadata_item_t item;
142         int ret = OHOS::Camera::CameraMetadata::FindCameraMetadataItem(
143             cameraInfo.ability->get(), OHOS_ABILITY_STREAM_AVAILABLE_EXTEND_CONFIGURATIONS, &item);
144         CHECK_ERROR_RETURN_RET(ret != CAM_META_SUCCESS || item.count == 0, nullptr);
145         ExtendInfo extendInfo = {};
146         std::shared_ptr<CameraStreamInfoParse> modeStreamParse = std::make_shared<CameraStreamInfoParse>();
147         modeStreamParse->getModeInfo(item.data.i32, item.count, extendInfo); // 解析tag中带的数据信息意义
148         for (auto& modeInfo : extendInfo.modeInfo) {
149             if (modeInfo.modeName != modeName) {
150                 continue;
151             }
152             for (auto& streamInfo : modeInfo.streamInfo) {
153                 if (streamInfo.streamType != HDI::Camera::V1_3::StreamType::STREAM_TYPE_STILL_CAPTURE) {
154                     continue;
155                 }
156                 float ratioValue = GetRatioValue(followSensorMaxRatio);
157                 return GetMaxSizeDetailInfo(streamInfo.detailInfo, ratioValue, OHOS_CAMERA_FORMAT_JPEG);
158             }
159         }
160         return nullptr;
161     }
162 
toStringOHOS::CameraStandard::__anon1ca33ed60110::PreconfigProfile163     std::string toString(std::vector<CameraInfo>& cameraInfos, HDI::Camera::V1_3::OperationMode modeName)
164     {
165         if (!followSensorMax) {
166             return toString();
167         }
168         std::string maxSizeInfo = "";
169         for (auto& cameraInfo : cameraInfos) {
170             camera_metadata_item_t item;
171             int ret = OHOS::Camera::CameraMetadata::FindCameraMetadataItem(
172                 cameraInfo.ability->get(), OHOS_ABILITY_CAMERA_TYPE, &item);
173             if (ret != CAM_META_SUCCESS || item.count == 0) {
174                 return "device camera type info error";
175             }
176             camera_type_enum_t cameraType = static_cast<camera_type_enum_t>(item.data.u8[0]);
177             if (cameraType != OHOS_CAMERA_TYPE_UNSPECIFIED) {
178                 continue;
179             }
180             auto maxDetail = FindMaxDetailInfoFromProfileLevel(cameraInfo, modeName);
181             if (maxDetail) {
182                 maxSizeInfo += std::to_string(maxDetail->width) + "x" + std::to_string(maxDetail->height) + "(" +
183                     cameraInfo.cameraId + ") ";
184                 continue;
185             }
186             auto maxDetailInfo = FindMaxDetailInfoFromExtendConfig(cameraInfo, modeName);
187             if (maxDetailInfo == nullptr) {
188                 continue;
189             }
190             maxSizeInfo += std::to_string(maxDetailInfo->width) + "x" + std::to_string(maxDetailInfo->height) + "(" +
191                            cameraInfo.cameraId + ") ";
192         }
193         return "Format:" + format + "\tSize:" + maxSizeInfo + "\tFps:" + std::to_string(fpsMin) + "-" +
194                std::to_string(fpsMax) + ",prefer:" + std::to_string(fpsPrefer);
195     }
196 };
197 
198 struct PhotoSessionPreconfig {
199     std::string colorSpace;
200     PreconfigProfile previewProfile;
201     PreconfigProfile photoProfile;
202 };
203 
204 struct VideoSessionPreconfig {
205     std::string colorSpace;
206     PreconfigProfile previewProfile;
207     PreconfigProfile photoProfile;
208     PreconfigProfile videoProfile;
209 };
210 
GeneratePhotoSessionPreconfigRatio1v1(PreconfigType preconfigType)211 PhotoSessionPreconfig GeneratePhotoSessionPreconfigRatio1v1(PreconfigType preconfigType)
212 {
213     PhotoSessionPreconfig sessionPreconfig { .colorSpace = "P3" };
214     switch (preconfigType) {
215         case PRECONFIG_TYPE_720P:
216             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 720, 720, 12, 30, 30 };
217             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 720, 720, 0, 0, 0 };
218             break;
219         case PRECONFIG_TYPE_1080P:
220             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1080, 1080, 12, 30, 30 };
221             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 1080, 1080, 0, 0, 0 };
222             break;
223         case PRECONFIG_TYPE_4K:
224             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1080, 1080, 12, 30, 30 };
225             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 2160, 2160, 0, 0, 0 };
226             break;
227         case PRECONFIG_TYPE_HIGH_QUALITY:
228             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1440, 1440, 12, 30, 30 };
229             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 0, 0, 0, 0, 0, true, RATIO_1_1 };
230             break;
231         default:
232             return sessionPreconfig;
233     }
234     return sessionPreconfig;
235 };
236 
GeneratePhotoSessionPreconfigRatio4v3(PreconfigType preconfigType)237 PhotoSessionPreconfig GeneratePhotoSessionPreconfigRatio4v3(PreconfigType preconfigType)
238 {
239     PhotoSessionPreconfig sessionPreconfig { .colorSpace = "P3" };
240     switch (preconfigType) {
241         case PRECONFIG_TYPE_720P:
242             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 960, 720, 12, 30, 30 };
243             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 960, 720, 0, 0, 0 };
244             break;
245         case PRECONFIG_TYPE_1080P:
246             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1440, 1080, 12, 30, 30 };
247             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 1440, 1080, 0, 0, 0 };
248             break;
249         case PRECONFIG_TYPE_4K:
250             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1440, 1080, 12, 30, 30 };
251             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 2880, 2160, 0, 0, 0 };
252             break;
253         case PRECONFIG_TYPE_HIGH_QUALITY:
254             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1920, 1440, 12, 30, 30 };
255             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 0, 0, 0, 0, 0, true, RATIO_4_3 };
256             break;
257         default:
258             return sessionPreconfig;
259     }
260     return sessionPreconfig;
261 };
262 
GeneratePhotoSessionPreconfigRatio16v9(PreconfigType preconfigType)263 PhotoSessionPreconfig GeneratePhotoSessionPreconfigRatio16v9(PreconfigType preconfigType)
264 {
265     PhotoSessionPreconfig sessionPreconfig { .colorSpace = "P3" };
266     switch (preconfigType) {
267         case PRECONFIG_TYPE_720P:
268             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1280, 720, 12, 30, 30 };
269             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 1280, 720, 0, 0, 0 };
270             break;
271         case PRECONFIG_TYPE_1080P:
272             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1920, 1080, 12, 30, 30 };
273             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 1920, 1080, 0, 0, 0 };
274             break;
275         case PRECONFIG_TYPE_4K:
276             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1920, 1080, 12, 30, 30 };
277             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 3840, 2160, 0, 0, 0 };
278             break;
279         case PRECONFIG_TYPE_HIGH_QUALITY:
280             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 2560, 1440, 12, 30, 30 };
281             sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 0, 0, 0, 0, 0, true, RATIO_16_9 };
282             break;
283         default:
284             return sessionPreconfig;
285     }
286     return sessionPreconfig;
287 };
288 
GenerateVideoSessionPreconfigRatio1v1(PreconfigType preconfigType)289 VideoSessionPreconfig GenerateVideoSessionPreconfigRatio1v1(PreconfigType preconfigType)
290 {
291     VideoSessionPreconfig sessionPreconfig { .colorSpace = "BT709" };
292     sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 2304, 2304, 0, 0, 0 };
293     switch (preconfigType) {
294         case PRECONFIG_TYPE_720P:
295             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 720, 720, 24, 30, 30 };
296             sessionPreconfig.videoProfile = sessionPreconfig.previewProfile;
297             break;
298         case PRECONFIG_TYPE_1080P:
299             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1080, 1080, 24, 30, 30 };
300             sessionPreconfig.videoProfile = sessionPreconfig.previewProfile;
301             break;
302         case PRECONFIG_TYPE_4K:
303             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1080, 1080, 24, 30, 30 };
304             sessionPreconfig.videoProfile = { "CAMERA_FORMAT_YUV_420_SP", 2160, 2160, 24, 30, 30 };
305             break;
306         case PRECONFIG_TYPE_HIGH_QUALITY:
307             sessionPreconfig.colorSpace = "BT2020_HLG_LIMIT";
308             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YCRCB_P010", 1080, 1080, 24, 30, 30 };
309             sessionPreconfig.videoProfile = { "CAMERA_FORMAT_YCRCB_P010", 2160, 2160, 24, 30, 30 };
310             break;
311         default:
312             return sessionPreconfig;
313     }
314     return sessionPreconfig;
315 };
316 
GenerateVideoSessionPreconfigRatio4v3(PreconfigType preconfigType)317 VideoSessionPreconfig GenerateVideoSessionPreconfigRatio4v3(PreconfigType preconfigType)
318 {
319     VideoSessionPreconfig sessionPreconfig { .colorSpace = "BT709" };
320     sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 3072, 2304, 0, 0, 0 };
321     switch (preconfigType) {
322         case PRECONFIG_TYPE_720P:
323             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 960, 720, 24, 30, 30 };
324             sessionPreconfig.videoProfile = sessionPreconfig.previewProfile;
325             break;
326         case PRECONFIG_TYPE_1080P:
327             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1440, 1080, 24, 30, 30 };
328             sessionPreconfig.videoProfile = sessionPreconfig.previewProfile;
329             break;
330         case PRECONFIG_TYPE_4K:
331             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1440, 1080, 24, 30, 30 };
332             sessionPreconfig.videoProfile = { "CAMERA_FORMAT_YUV_420_SP", 2880, 2160, 24, 30, 30 };
333             break;
334         case PRECONFIG_TYPE_HIGH_QUALITY:
335             sessionPreconfig.colorSpace = "BT2020_HLG_LIMIT";
336             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YCRCB_P010", 1440, 1080, 24, 30, 30 };
337             sessionPreconfig.videoProfile = { "CAMERA_FORMAT_YCRCB_P010", 2880, 2160, 24, 30, 30 };
338             break;
339         default:
340             return sessionPreconfig;
341     }
342     return sessionPreconfig;
343 };
344 
GenerateVideoSessionPreconfigRatio16v9(PreconfigType preconfigType)345 VideoSessionPreconfig GenerateVideoSessionPreconfigRatio16v9(PreconfigType preconfigType)
346 {
347     VideoSessionPreconfig sessionPreconfig { .colorSpace = "BT709" };
348     sessionPreconfig.photoProfile = { "CAMERA_FORMAT_JPEG", 4096, 2304, 0, 0, 0 };
349     switch (preconfigType) {
350         case PRECONFIG_TYPE_720P:
351             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1280, 720, 24, 30, 30 };
352             sessionPreconfig.videoProfile = sessionPreconfig.previewProfile;
353             break;
354         case PRECONFIG_TYPE_1080P:
355             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1920, 1080, 24, 30, 30 };
356             sessionPreconfig.videoProfile = sessionPreconfig.previewProfile;
357             break;
358         case PRECONFIG_TYPE_4K:
359             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YUV_420_SP", 1920, 1080, 24, 30, 30 };
360             sessionPreconfig.videoProfile = { "CAMERA_FORMAT_YUV_420_SP", 3840, 2160, 24, 30, 30 };
361             break;
362         case PRECONFIG_TYPE_HIGH_QUALITY:
363             sessionPreconfig.colorSpace = "BT2020_HLG_LIMIT";
364             sessionPreconfig.previewProfile = { "CAMERA_FORMAT_YCRCB_P010", 1920, 1080, 12, 30, 30 };
365             sessionPreconfig.videoProfile = { "CAMERA_FORMAT_YCRCB_P010", 3840, 2160, 24, 30, 30 };
366             sessionPreconfig.photoProfile.followSensorMax = true;
367             sessionPreconfig.photoProfile.followSensorMaxRatio = RATIO_16_9;
368             break;
369         default:
370             return sessionPreconfig;
371     }
372     return sessionPreconfig;
373 };
374 } // namespace
375 
DumpPreconfigInfo(CameraInfoDumper & infoDumper,sptr<HCameraHostManager> & hostManager)376 void DumpPreconfigInfo(CameraInfoDumper& infoDumper, sptr<HCameraHostManager>& hostManager)
377 {
378     std::map<PreconfigType, std::string> preconfigTypeMap = { { PRECONFIG_TYPE_720P, "PRECONFIG_720P" },
379         { PRECONFIG_TYPE_1080P, "PRECONFIG_1080P" }, { PRECONFIG_TYPE_4K, "PRECONFIG_4K" },
380         { PRECONFIG_TYPE_HIGH_QUALITY, "PRECONFIG_HIGH_QUALITY" } };
381     std::map<PreconfigRatio, std::string> preconfigRatioMap = { { RATIO_1_1, "ratio 1:1" }, { RATIO_4_3, "ratio 4:3" },
382         { RATIO_16_9, "ratio 16:9" } };
383     std::vector<std::string> cameraIds;
384     std::vector<CameraInfo> cameraInfos;
385     hostManager->GetCameras(cameraIds);
386     for (auto& cameraId : cameraIds) {
387         CameraInfo cameraInfo { .cameraId = cameraId };
388         hostManager->GetCameraAbility(cameraId, cameraInfo.ability);
389         cameraInfos.emplace_back(cameraInfo);
390     }
391     for (const auto& typePair : preconfigTypeMap) {
392         for (const auto& ratioPair : preconfigRatioMap) {
393             PhotoSessionPreconfig photoPreconfig;
394             VideoSessionPreconfig videoPreconfig;
395             switch (ratioPair.first) {
396                 case RATIO_1_1:
397                     photoPreconfig = GeneratePhotoSessionPreconfigRatio1v1(typePair.first);
398                     videoPreconfig = GenerateVideoSessionPreconfigRatio1v1(typePair.first);
399                     break;
400                 case RATIO_4_3:
401                     photoPreconfig = GeneratePhotoSessionPreconfigRatio4v3(typePair.first);
402                     videoPreconfig = GenerateVideoSessionPreconfigRatio4v3(typePair.first);
403                     break;
404                 case RATIO_16_9:
405                     photoPreconfig = GeneratePhotoSessionPreconfigRatio16v9(typePair.first);
406                     videoPreconfig = GenerateVideoSessionPreconfigRatio16v9(typePair.first);
407                     break;
408                 default:
409                     // Do nothing.
410                     break;
411             }
412             infoDumper.Title(typePair.second + " " + ratioPair.second + " :");
413             infoDumper.Push();
414             infoDumper.Title("PhotoSession:");
415             infoDumper.Msg("Colorspace:" + photoPreconfig.colorSpace);
416             infoDumper.Msg("[Preview]\t" + photoPreconfig.previewProfile.toString());
417             infoDumper.Msg("[Photo]\t" + photoPreconfig.photoProfile.toString(cameraInfos, HDI::Camera::V1_3::CAPTURE));
418             infoDumper.Title("VideoSession:");
419             infoDumper.Msg("Colorspace:" + videoPreconfig.colorSpace);
420             infoDumper.Msg("[Preview]\t" + videoPreconfig.previewProfile.toString());
421             infoDumper.Msg("[Video]\t" + videoPreconfig.videoProfile.toString());
422             infoDumper.Msg("[Photo]\t" + videoPreconfig.photoProfile.toString(cameraInfos, HDI::Camera::V1_3::VIDEO));
423             infoDumper.Pop();
424         }
425     }
426 }
427 } // namespace CameraStandard
428 } // namespace OHOS
429