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