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