xref: /aosp_15_r20/frameworks/av/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "HeicEncoderInfoManager"
18 //#define LOG_NDEBUG 0
19 
20 #include <cstdint>
21 #include <regex>
22 
23 #include <com_android_internal_camera_flags.h>
24 #include <cutils/properties.h>
25 #include <log/log_main.h>
26 #include <system/graphics.h>
27 
28 #include <media/stagefright/MediaCodecList.h>
29 #include <media/stagefright/foundation/MediaDefs.h>
30 #include <media/stagefright/foundation/ABuffer.h>
31 
32 #include "HeicEncoderInfoManager.h"
33 
34 namespace android {
35 namespace camera3 {
36 
37 namespace flags = com::android::internal::camera::flags;
38 
HeicEncoderInfoManager(bool useSWCodec)39 HeicEncoderInfoManager::HeicEncoderInfoManager(bool useSWCodec) :
40         mIsInited(false),
41         mMinSizeHeic(0, 0),
42         mMaxSizeHeic(INT32_MAX, INT32_MAX),
43         mHasHEVC(false),
44         mHasHEIC(false),
45         mDisableGrid(false) {
46     if (initialize(useSWCodec) == OK) {
47         mIsInited = true;
48     }
49 }
50 
~HeicEncoderInfoManager()51 HeicEncoderInfoManager::~HeicEncoderInfoManager() {
52 }
53 
isSizeSupported(int32_t width,int32_t height,bool * useHeic,bool * useGrid,int64_t * stall,AString * hevcName) const54 bool HeicEncoderInfoManager::isSizeSupported(int32_t width, int32_t height, bool* useHeic,
55         bool* useGrid, int64_t* stall, AString* hevcName) const {
56     if (useHeic == nullptr || useGrid == nullptr) {
57         ALOGE("%s: invalid parameters: useHeic %p, useGrid %p",
58                 __FUNCTION__, useHeic, useGrid);
59         return false;
60     }
61     if (!mIsInited) return false;
62 
63     bool chooseHeic = false, enableGrid = true;
64     if (mHasHEIC && width >= mMinSizeHeic.first &&
65             height >= mMinSizeHeic.second && width <= mMaxSizeHeic.first &&
66             height <= mMaxSizeHeic.second) {
67         chooseHeic = true;
68         enableGrid = false;
69     } else if (mHasHEVC) {
70         bool fullSizeSupportedByHevc = (width >= mMinSizeHevc.first &&
71                 height >= mMinSizeHevc.second &&
72                 width <= mMaxSizeHevc.first &&
73                 height <= mMaxSizeHevc.second);
74         if (fullSizeSupportedByHevc && (mDisableGrid ||
75                 (width <= 1920 && height <= 1080))) {
76             enableGrid = false;
77         }
78     } else {
79         // No encoder available for the requested size.
80         return false;
81     }
82 
83     if (hevcName != nullptr) {
84         *hevcName = mHevcName;
85     }
86 
87     if (stall != nullptr) {
88         // Find preferred encoder which advertise
89         // "measured-frame-rate-WIDTHxHEIGHT-range" key.
90         const FrameRateMaps& maps =
91                 (chooseHeic && mHeicFrameRateMaps.size() > 0) ?
92                 mHeicFrameRateMaps : mHevcFrameRateMaps;
93         const auto& closestSize = findClosestSize(maps, width, height);
94         if (closestSize == maps.end()) {
95             // The "measured-frame-rate-WIDTHxHEIGHT-range" key is optional.
96             // Hardcode to some default value (3.33ms * tile count) based on resolution.
97             *stall = 3333333LL * width * height / (kGridWidth * kGridHeight);
98             *useHeic = chooseHeic;
99             *useGrid = enableGrid;
100             return true;
101         }
102 
103         // Derive stall durations based on average fps of the closest size.
104         constexpr int64_t NSEC_PER_SEC = 1000000000LL;
105         int32_t avgFps = (closestSize->second.first + closestSize->second.second)/2;
106         float ratio = 1.0f * width * height /
107                 (closestSize->first.first * closestSize->first.second);
108         *stall = ratio * NSEC_PER_SEC / avgFps;
109     }
110 
111     *useHeic = chooseHeic;
112     *useGrid = enableGrid;
113     return true;
114 }
115 
initialize(bool allowSWCodec)116 status_t HeicEncoderInfoManager::initialize(bool allowSWCodec) {
117     mDisableGrid = property_get_bool("camera.heic.disable_grid", false);
118     sp<IMediaCodecList> codecsList = MediaCodecList::getInstance();
119     if (codecsList == nullptr) {
120         // No media codec available.
121         return OK;
122     }
123 
124     sp<AMessage> heicDetails = getCodecDetails(codecsList, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
125 
126     if (!getHevcCodecDetails(codecsList, MEDIA_MIMETYPE_VIDEO_HEVC, allowSWCodec)) {
127         if (heicDetails != nullptr) {
128             ALOGE("%s: Device must support HEVC codec if HEIC codec is available!",
129                     __FUNCTION__);
130             return BAD_VALUE;
131         }
132         return OK;
133     }
134     mHasHEVC = true;
135 
136     // HEIC size range
137     if (heicDetails != nullptr) {
138         auto res = getCodecSizeRange(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC,
139                 heicDetails, &mMinSizeHeic, &mMaxSizeHeic, &mHeicFrameRateMaps);
140         if (res != OK) {
141             ALOGE("%s: Failed to get HEIC codec size range: %s (%d)", __FUNCTION__,
142                     strerror(-res), res);
143             return BAD_VALUE;
144         }
145         mHasHEIC = true;
146     }
147 
148     return OK;
149 }
150 
getFrameRateMaps(sp<AMessage> details,FrameRateMaps * maps)151 status_t HeicEncoderInfoManager::getFrameRateMaps(sp<AMessage> details, FrameRateMaps* maps) {
152     if (details == nullptr || maps == nullptr) {
153         ALOGE("%s: Invalid input: details: %p, maps: %p", __FUNCTION__, details.get(), maps);
154         return BAD_VALUE;
155     }
156 
157     for (size_t i = 0; i < details->countEntries(); i++) {
158         AMessage::Type type;
159         const char* entryName = details->getEntryNameAt(i, &type);
160         if (type != AMessage::kTypeString) continue;
161         std::regex frameRateNamePattern("measured-frame-rate-([0-9]+)[*x]([0-9]+)-range",
162                 std::regex_constants::icase);
163         std::cmatch sizeMatch;
164         if (std::regex_match(entryName, sizeMatch, frameRateNamePattern) &&
165                 sizeMatch.size() == 3) {
166             AMessage::ItemData item = details->getEntryAt(i);
167             AString fpsRangeStr;
168             if (item.find(&fpsRangeStr)) {
169                 ALOGV("%s: %s", entryName, fpsRangeStr.c_str());
170                 std::regex frameRatePattern("([0-9]+)-([0-9]+)");
171                 std::cmatch fpsMatch;
172                 if (std::regex_match(fpsRangeStr.c_str(), fpsMatch, frameRatePattern) &&
173                         fpsMatch.size() == 3) {
174                     maps->emplace(
175                             std::make_pair(stoi(sizeMatch[1]), stoi(sizeMatch[2])),
176                             std::make_pair(stoi(fpsMatch[1]), stoi(fpsMatch[2])));
177                 } else {
178                     return BAD_VALUE;
179                 }
180             }
181         }
182     }
183     return OK;
184 }
185 
getCodecSizeRange(const char * codecName,sp<AMessage> details,std::pair<int32_t,int32_t> * minSize,std::pair<int32_t,int32_t> * maxSize,FrameRateMaps * frameRateMaps)186 status_t HeicEncoderInfoManager::getCodecSizeRange(
187         const char* codecName,
188         sp<AMessage> details,
189         std::pair<int32_t, int32_t>* minSize,
190         std::pair<int32_t, int32_t>* maxSize,
191         FrameRateMaps* frameRateMaps) {
192     if (codecName == nullptr || minSize == nullptr || maxSize == nullptr ||
193             details == nullptr || frameRateMaps == nullptr) {
194         return BAD_VALUE;
195     }
196 
197     AString sizeRange;
198     auto hasItem = details->findString("size-range", &sizeRange);
199     if (!hasItem) {
200         ALOGE("%s: Failed to query size range for codec %s", __FUNCTION__, codecName);
201         return BAD_VALUE;
202     }
203     ALOGV("%s: %s codec's size range is %s", __FUNCTION__, codecName, sizeRange.c_str());
204     std::regex pattern("([0-9]+)[*x]([0-9]+)-([0-9]+)[*x]([0-9]+)");
205     std::cmatch match;
206     if (std::regex_match(sizeRange.c_str(), match, pattern)) {
207         if (match.size() == 5) {
208             minSize->first = stoi(match[1]);
209             minSize->second = stoi(match[2]);
210             maxSize->first = stoi(match[3]);
211             maxSize->second = stoi(match[4]);
212             if (minSize->first > maxSize->first ||
213                     minSize->second > maxSize->second) {
214                 ALOGE("%s: Invalid %s code size range: %s",
215                         __FUNCTION__, codecName, sizeRange.c_str());
216                 return BAD_VALUE;
217             }
218         } else {
219             return BAD_VALUE;
220         }
221     }
222 
223     auto res = getFrameRateMaps(details, frameRateMaps);
224     if (res != OK) {
225         return res;
226     }
227 
228     return OK;
229 }
230 
findClosestSize(const FrameRateMaps & maps,int32_t width,int32_t height) const231 HeicEncoderInfoManager::FrameRateMaps::const_iterator HeicEncoderInfoManager::findClosestSize(
232         const FrameRateMaps& maps, int32_t width, int32_t height) const {
233     int32_t minDiff = INT32_MAX;
234     FrameRateMaps::const_iterator closestIter = maps.begin();
235     for (auto iter = maps.begin(); iter != maps.end(); iter++) {
236         // Use area difference between the sizes to approximate size
237         // difference.
238         int32_t diff = abs(iter->first.first * iter->first.second - width * height);
239         if (diff < minDiff) {
240             closestIter = iter;
241             minDiff = diff;
242         }
243     }
244     return closestIter;
245 }
246 
getCodecDetails(sp<IMediaCodecList> codecsList,const char * name)247 sp<AMessage> HeicEncoderInfoManager::getCodecDetails(
248         sp<IMediaCodecList> codecsList, const char* name) {
249     ssize_t idx = codecsList->findCodecByType(name, true /*encoder*/);
250     if (idx < 0) {
251         return nullptr;
252     }
253 
254     const sp<MediaCodecInfo> info = codecsList->getCodecInfo(idx);
255     if (info == nullptr) {
256         ALOGE("%s: Failed to get codec info for %s", __FUNCTION__, name);
257         return nullptr;
258     }
259     const sp<MediaCodecInfo::Capabilities> caps =
260             info->getCapabilitiesFor(name);
261     if (caps == nullptr) {
262         ALOGE("%s: Failed to get capabilities for codec %s", __FUNCTION__, name);
263         return nullptr;
264     }
265     const sp<AMessage> details = caps->getDetails();
266     if (details == nullptr) {
267         ALOGE("%s: Failed to get details for codec %s", __FUNCTION__, name);
268         return nullptr;
269     }
270 
271     return details;
272 }
273 
getHevcCodecDetails(sp<IMediaCodecList> codecsList,const char * mime,bool allowSWCodec)274 bool HeicEncoderInfoManager::getHevcCodecDetails(
275         sp<IMediaCodecList> codecsList, const char* mime, bool allowSWCodec) {
276     bool found = false;
277     ssize_t idx = 0;
278     while ((idx = codecsList->findCodecByType(mime, true /*encoder*/, idx)) >= 0) {
279         const sp<MediaCodecInfo> info = codecsList->getCodecInfo(idx++);
280         if (info == nullptr) {
281             ALOGE("%s: Failed to get codec info for %s", __FUNCTION__, mime);
282             break;
283         }
284         ALOGV("%s: [%s] codec found", __FUNCTION__,
285                 info->getCodecName());
286 
287         if (!allowSWCodec) {
288             // Filter out software ones as they may be too slow
289             if (!(info->getAttributes() & MediaCodecInfo::kFlagIsHardwareAccelerated)) {
290                 ALOGV("%s: [%s] Filter out software ones as they may be too slow", __FUNCTION__,
291                         info->getCodecName());
292                 continue;
293             }
294         }
295 
296         const sp<MediaCodecInfo::Capabilities> caps =
297                 info->getCapabilitiesFor(mime);
298         if (caps == nullptr) {
299             ALOGE("%s: [%s] Failed to get capabilities", __FUNCTION__,
300                     info->getCodecName());
301             break;
302         }
303         const sp<AMessage> details = caps->getDetails();
304         if (details == nullptr) {
305             ALOGE("%s: [%s] Failed to get details", __FUNCTION__,
306                     info->getCodecName());
307             break;
308         }
309 
310         // Check CQ mode
311         AString bitrateModes;
312         auto hasItem = details->findString("feature-bitrate-modes", &bitrateModes);
313         if (!hasItem) {
314             ALOGE("%s: [%s] Failed to query bitrate modes", __FUNCTION__,
315                     info->getCodecName());
316             break;
317         }
318         ALOGV("%s: [%s] feature-bitrate-modes value is %d, %s",
319                 __FUNCTION__, info->getCodecName(), hasItem, bitrateModes.c_str());
320         std::regex pattern("(^|,)CQ($|,)", std::regex_constants::icase);
321         if (!std::regex_search(bitrateModes.c_str(), pattern)) {
322             continue; // move on to next encoder
323         }
324 
325         std::pair<int32_t, int32_t> minSizeHevc, maxSizeHevc;
326         FrameRateMaps hevcFrameRateMaps;
327         auto res = getCodecSizeRange(MEDIA_MIMETYPE_VIDEO_HEVC,
328                 details, &minSizeHevc, &maxSizeHevc, &hevcFrameRateMaps);
329         if (res != OK) {
330             ALOGE("%s: [%s] Failed to get size range: %s (%d)", __FUNCTION__,
331                     info->getCodecName(), strerror(-res), res);
332             break;
333         }
334         if (kGridWidth < minSizeHevc.first
335                 || kGridWidth > maxSizeHevc.first
336                 || kGridHeight < minSizeHevc.second
337                 || kGridHeight > maxSizeHevc.second) {
338             continue; // move on to next encoder
339         }
340 
341         // Found: save name, size, frame rate
342         mHevcName = info->getCodecName();
343         mMinSizeHevc = minSizeHevc;
344         mMaxSizeHevc = maxSizeHevc;
345         mHevcFrameRateMaps = hevcFrameRateMaps;
346 
347         found = true;
348         break;
349     }
350 
351     return found;
352 }
353 
354 } //namespace camera3
355 } // namespace android
356