blob: 315b3ec43f66d8bccd38348fc4ed259a4a1a7063 [file] [log] [blame]
Ray Essick1831f7b2021-03-15 16:10:51 -07001/*
2 * Copyright 2021, 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_NDEBUG 0
18#define LOG_TAG "CodecProperties"
19#include <utils/Log.h>
20
21#include <string>
Ray Essicka727ef92021-03-29 13:35:02 -070022#include <stdlib.h>
Ray Essick1831f7b2021-03-15 16:10:51 -070023
Ray Essick7b4e9d92021-04-20 16:48:00 -070024#include "CodecProperties.h"
25
26#include <media/stagefright/MediaCodecConstants.h>
Ray Essick1831f7b2021-03-15 16:10:51 -070027
Ray Essick244aaa52021-04-05 14:47:02 -070028
29// we aren't going to mess with shaping points dimensions beyond this
30static const int32_t DIMENSION_LIMIT = 16384;
31
Ray Essick1831f7b2021-03-15 16:10:51 -070032namespace android {
33namespace mediaformatshaper {
34
35CodecProperties::CodecProperties(std::string name, std::string mediaType) {
Ray Essick970f1c82021-03-25 13:37:45 -070036 ALOGV("CodecProperties(%s, %s)", name.c_str(), mediaType.c_str());
Ray Essick1831f7b2021-03-15 16:10:51 -070037 mName = name;
38 mMediaType = mediaType;
39}
40
41std::string CodecProperties::getName(){
42 return mName;
43}
44
45std::string CodecProperties::getMediaType(){
46 return mMediaType;
47}
48
49int CodecProperties::supportedMinimumQuality() {
50 return mMinimumQuality;
51}
52void CodecProperties::setSupportedMinimumQuality(int vmaf) {
53 mMinimumQuality = vmaf;
54}
55
56int CodecProperties::targetQpMax() {
57 return mTargetQpMax;
58}
59void CodecProperties::setTargetQpMax(int qpMax) {
60 mTargetQpMax = qpMax;
61}
62
63// what API is this codec set up for (e.g. API of the associated partition)
64// vendor-side (OEM) codecs may be older, due to 'vendor freeze' and treble
65int CodecProperties::supportedApi() {
66 return mApi;
67}
68
Ray Essick970f1c82021-03-25 13:37:45 -070069void CodecProperties::setFeatureValue(std::string key, int32_t value) {
70 ALOGD("setFeatureValue(%s,%d)", key.c_str(), value);
71 mFeatures.insert({key, value});
72
Ray Essick7b4e9d92021-04-20 16:48:00 -070073 if (!strcmp(key.c_str(), FEATURE_QpBounds)) {
Ray Essick970f1c82021-03-25 13:37:45 -070074 setSupportsQp(1);
Ray Essick7b4e9d92021-04-20 16:48:00 -070075 } else if (!strcmp(key.c_str(), "video-minimum-quality")) {
76 setSupportedMinimumQuality(1);
77 } else if (!strcmp(key.c_str(), "vq-minimum-quality")) { // from prototyping
Ray Essicka727ef92021-03-29 13:35:02 -070078 setSupportedMinimumQuality(1);
Ray Essick970f1c82021-03-25 13:37:45 -070079 }
80}
81
82bool CodecProperties::getFeatureValue(std::string key, int32_t *valuep) {
83 ALOGV("getFeatureValue(%s)", key.c_str());
84 if (valuep == nullptr) {
85 return false;
86 }
87 auto mapped = mFeatures.find(key);
88 if (mapped != mFeatures.end()) {
89 *valuep = mapped->second;
90 return true;
91 }
92 return false;
93}
94
Ray Essicka727ef92021-03-29 13:35:02 -070095// Tuning values (which differ from Features)
96// this is where we set up things like target bitrates and QP ranges
97// NB the tuning values arrive as a string, allowing us to convert it into an appropriate
98// format (int, float, ranges, other combinations)
99//
100void CodecProperties::setTuningValue(std::string key, std::string value) {
101 ALOGD("setTuningValue(%s,%s)", key.c_str(), value.c_str());
102 mTunings.insert({key, value});
103
104 bool legal = false;
105 // NB: old school strtol() because std::stoi() throws exceptions
106 if (!strcmp(key.c_str(), "vq-target-qpmax")) {
107 const char *p = value.c_str();
108 char *q;
109 int32_t iValue = strtol(p, &q, 0);
110 if (q != p) {
111 setTargetQpMax(iValue);
112 legal = true;
113 }
114 } else if (!strcmp(key.c_str(), "vq-target-bpp")) {
115 const char *p = value.c_str();
116 char *q;
117 double bpp = strtod(p, &q);
118 if (q != p) {
119 setBpp(bpp);
120 legal = true;
121 }
Ray Essick244aaa52021-04-05 14:47:02 -0700122 } else if (!strncmp(key.c_str(), "vq-target-bpp-", strlen("vq-target-bpp-"))) {
123 std::string resolution = key.substr(strlen("vq-target-bpp-"));
124 if (bppPoint(resolution, value)) {
125 legal = true;
126 }
Ray Essicka727ef92021-03-29 13:35:02 -0700127 } else if (!strcmp(key.c_str(), "vq-target-bppx100")) {
Ray Essick244aaa52021-04-05 14:47:02 -0700128 // legacy, prototyping
Ray Essicka727ef92021-03-29 13:35:02 -0700129 const char *p = value.c_str();
130 char *q;
131 int32_t iValue = strtol(p, &q, 0);
132 if (q != p) {
133 double bpp = iValue / 100.0;
134 setBpp(bpp);
135 legal = true;
136 }
137 } else {
138 legal = true;
139 }
140
141 if (!legal) {
142 ALOGW("setTuningValue() unable to apply tuning '%s' with value '%s'",
143 key.c_str(), value.c_str());
144 }
145 return;
146}
147
148bool CodecProperties::getTuningValue(std::string key, std::string &value) {
149 ALOGV("getTuningValue(%s)", key.c_str());
150 auto mapped = mFeatures.find(key);
151 if (mapped != mFeatures.end()) {
152 value = mapped->second;
153 return true;
154 }
155 return false;
156}
157
Ray Essick244aaa52021-04-05 14:47:02 -0700158bool CodecProperties::bppPoint(std::string resolution, std::string value) {
159
160 int32_t width = 0;
161 int32_t height = 0;
162 double bpp = -1;
163
164 // resolution is "WxH", "W*H" or a standard name like "720p"
165 if (resolution == "1080p") {
166 width = 1080; height = 1920;
167 } else if (resolution == "720p") {
168 width = 720; height = 1280;
169 } else if (resolution == "540p") {
170 width = 540; height = 960;
171 } else if (resolution == "480p") {
172 width = 480; height = 854;
173 } else {
174 size_t sep = resolution.find('x');
175 if (sep == std::string::npos) {
176 sep = resolution.find('*');
177 }
178 if (sep == std::string::npos) {
179 ALOGW("unable to parse resolution: '%s'", resolution.c_str());
180 return false;
181 }
182 std::string w = resolution.substr(0, sep);
183 std::string h = resolution.substr(sep+1);
184
185 char *q;
186 const char *p = w.c_str();
187 width = strtol(p, &q, 0);
188 if (q == p) {
189 width = -1;
190 }
191 p = h.c_str();
192 height = strtol(p, &q, 0);
193 if (q == p) {
194 height = -1;
195 }
196 if (width <= 0 || height <= 0 || width > DIMENSION_LIMIT || height > DIMENSION_LIMIT) {
197 ALOGW("unparseable: width, height '%s'", resolution.c_str());
198 return false;
199 }
200 }
201
202 const char *p = value.c_str();
203 char *q;
204 bpp = strtod(p, &q);
205 if (q == p) {
206 ALOGW("unparseable bpp '%s'", value.c_str());
207 return false;
208 }
209
210 struct bpp_point *point = (struct bpp_point*) malloc(sizeof(*point));
211 if (point == nullptr) {
212 ALOGW("unable to allocate memory for bpp point");
213 return false;
214 }
215
216 point->pixels = width * height;
217 point->width = width;
218 point->height = height;
219 point->bpp = bpp;
220
221 if (mBppPoints == nullptr) {
222 point->next = nullptr;
223 mBppPoints = point;
224 } else if (point->pixels < mBppPoints->pixels) {
225 // at the front
226 point->next = mBppPoints;
227 mBppPoints = point;
228 } else {
229 struct bpp_point *after = mBppPoints;
230 while (after->next) {
231 if (point->pixels > after->next->pixels) {
232 after = after->next;
233 continue;
234 }
235
236 // insert before after->next
237 point->next = after->next;
238 after->next = point;
239 break;
240 }
241 if (after->next == nullptr) {
242 // hasn't gone in yet
243 point->next = nullptr;
244 after->next = point;
245 }
246 }
247
248 return true;
249}
250
251double CodecProperties::getBpp(int32_t width, int32_t height) {
252 // look in the per-resolution list
253
254 int32_t pixels = width * height;
255
256 if (mBppPoints) {
257 struct bpp_point *point = mBppPoints;
258 while (point && point->pixels < pixels) {
259 point = point->next;
260 }
261 if (point) {
262 ALOGV("getBpp(w=%d,h=%d) returns %f from bpppoint w=%d h=%d",
263 width, height, point->bpp, point->width, point->height);
264 return point->bpp;
265 }
266 }
267
268 ALOGV("defaulting to %f bpp", mBpp);
269 return mBpp;
270}
Ray Essick970f1c82021-03-25 13:37:45 -0700271
Ray Essick1831f7b2021-03-15 16:10:51 -0700272std::string CodecProperties::getMapping(std::string key, std::string kind) {
273 ALOGV("getMapping(key %s, kind %s )", key.c_str(), kind.c_str());
274 //play with mMappings
275 auto mapped = mMappings.find(kind + "-" + key);
276 if (mapped != mMappings.end()) {
277 std::string result = mapped->second;
278 ALOGV("getMapping(%s, %s) -> %s", key.c_str(), kind.c_str(), result.c_str());
279 return result;
280 }
281 ALOGV("nope, return unchanged key");
282 return key;
283}
284
285
286// really a bit of debugging code here.
287void CodecProperties::showMappings() {
288 ALOGD("Mappings:");
289 int count = 0;
290 for (const auto& [key, value] : mMappings) {
291 count++;
292 ALOGD("'%s' -> '%s'", key.c_str(), value.c_str());
293 }
294 ALOGD("total %d mappings", count);
295}
296
297void CodecProperties::setMapping(std::string kind, std::string key, std::string value) {
298 ALOGV("setMapping(%s,%s,%s)", kind.c_str(), key.c_str(), value.c_str());
299 std::string metaKey = kind + "-" + key;
300 mMappings.insert({metaKey, value});
301}
302
303const char **CodecProperties::getMappings(std::string kind, bool reverse) {
304 ALOGV("getMappings(kind %s, reverse %d", kind.c_str(), reverse);
305 // how many do we need?
306 int count = mMappings.size();
307 if (count == 0) {
308 ALOGV("empty mappings");
309 return nullptr;
310 }
311 size_t size = sizeof(char *) * (2 * count + 2);
312 const char **result = (const char **)malloc(size);
313 if (result == nullptr) {
314 ALOGW("no memory to return mappings");
315 return nullptr;
316 }
317 memset(result, '\0', size);
318
319 const char **pp = result;
320 for (const auto& [key, value] : mMappings) {
321 // split out the kind/key
322 size_t pos = key.find('-');
323 if (pos == std::string::npos) {
324 ALOGD("ignoring malformed key: %s", key.c_str());
325 continue;
326 }
327 std::string actualKind = key.substr(0,pos);
328 if (kind.length() != 0 && kind != actualKind) {
329 ALOGD("kinds don't match: want '%s' got '%s'", kind.c_str(), actualKind.c_str());
330 continue;
331 }
332 if (reverse) {
333 // codec specific -> std aka 'unmapping'
334 pp[0] = strdup( value.c_str());
335 pp[1] = strdup( key.substr(pos+1).c_str());
336 } else {
337 // std -> codec specific
338 pp[0] = strdup( key.substr(pos+1).c_str());
339 pp[1] = strdup( value.c_str());
340 }
341 ALOGV(" %s -> %s", pp[0], pp[1]);
342 pp += 2;
343 }
344
345 pp[0] = nullptr;
346 pp[1] = nullptr;
347
348 return result;
349}
350
351
352} // namespace mediaformatshaper
353} // namespace android
354