1 /*
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>
22 #include <stdlib.h>
23
24 #include "CodecProperties.h"
25
26 #include <media/stagefright/MediaCodecConstants.h>
27
28
29 // we aren't going to mess with shaping points dimensions beyond this
30 static const int32_t DIMENSION_LIMIT = 16384;
31
32 namespace android {
33 namespace mediaformatshaper {
34
CodecProperties(std::string name,std::string mediaType)35 CodecProperties::CodecProperties(std::string name, std::string mediaType) {
36 ALOGV("CodecProperties(%s, %s)", name.c_str(), mediaType.c_str());
37 mName = name;
38 mMediaType = mediaType;
39 }
40
getName()41 std::string CodecProperties::getName(){
42 return mName;
43 }
44
getMediaType()45 std::string CodecProperties::getMediaType(){
46 return mMediaType;
47 }
48
supportedMinimumQuality()49 int CodecProperties::supportedMinimumQuality() {
50 return mMinimumQuality;
51 }
setSupportedMinimumQuality(int vmaf)52 void CodecProperties::setSupportedMinimumQuality(int vmaf) {
53 mMinimumQuality = vmaf;
54 }
55
setMissingQpBoost(double boost)56 void CodecProperties::setMissingQpBoost(double boost) {
57 mMissingQpBoost = boost;
58 }
setPhaseOut(double phaseout)59 void CodecProperties::setPhaseOut(double phaseout) {
60 mPhaseOut = phaseout;
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
supportedApi()65 int CodecProperties::supportedApi() {
66 return mApi;
67 }
68
setFeatureValue(std::string key,int32_t value)69 void CodecProperties::setFeatureValue(std::string key, int32_t value) {
70 ALOGD("setFeatureValue(%s,%d)", key.c_str(), value);
71 mFeatures.insert({key, value});
72
73 if (!strcmp(key.c_str(), FEATURE_QpBounds)) {
74 setSupportsQp(1);
75 } else if (!strcmp(key.c_str(), "video-minimum-quality")) {
76 setSupportedMinimumQuality(1);
77 } else if (!strcmp(key.c_str(), "vq-minimum-quality")) { // from prototyping
78 setSupportedMinimumQuality(1);
79 }
80 }
81
getFeatureValue(std::string key,int32_t * valuep)82 bool 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
95 // 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 //
setTuningValue(std::string key,std::string value)100 void 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 (!strncmp(key.c_str(), "vq-target-qpmax-", strlen("vq-target-qpmax-"))) {
115 std::string resolution = key.substr(strlen("vq-target-qpmax-"));
116 if (qpMaxPoint(resolution, value)) {
117 legal = true;
118 }
119 } else if (!strcmp(key.c_str(), "vq-target-bpp")) {
120 const char *p = value.c_str();
121 char *q;
122 double bpp = strtod(p, &q);
123 if (q != p) {
124 setBpp(bpp);
125 legal = true;
126 }
127 } else if (!strncmp(key.c_str(), "vq-target-bpp-", strlen("vq-target-bpp-"))) {
128 std::string resolution = key.substr(strlen("vq-target-bpp-"));
129 if (bppPoint(resolution, value)) {
130 legal = true;
131 }
132 } else if (!strcmp(key.c_str(), "vq-target-bppx100")) {
133 // legacy, prototyping
134 const char *p = value.c_str();
135 char *q;
136 int32_t iValue = strtol(p, &q, 0);
137 if (q != p) {
138 double bpp = iValue / 100.0;
139 setBpp(bpp);
140 legal = true;
141 }
142 } else if (!strcmp(key.c_str(), "vq-bitrate-phaseout")) {
143 const char *p = value.c_str();
144 char *q;
145 double phaseout = strtod(p, &q);
146 if (q != p) {
147 setPhaseOut(phaseout);
148 legal = true;
149 }
150 } else if (!strcmp(key.c_str(), "vq-boost-missing-qp")) {
151 const char *p = value.c_str();
152 char *q;
153 double boost = strtod(p, &q);
154 if (q != p) {
155 setMissingQpBoost(boost);
156 legal = true;
157 }
158 } else {
159 legal = true;
160 }
161
162 if (!legal) {
163 ALOGW("setTuningValue() unable to apply tuning '%s' with value '%s'",
164 key.c_str(), value.c_str());
165 }
166 return;
167 }
168
getTuningValue(std::string key,std::string & value)169 bool CodecProperties::getTuningValue(std::string key, std::string &value) {
170 ALOGV("getTuningValue(%s)", key.c_str());
171 auto mapped = mFeatures.find(key);
172 if (mapped != mFeatures.end()) {
173 value = mapped->second;
174 return true;
175 }
176 return false;
177 }
178
bppPoint(std::string resolution,std::string value)179 bool CodecProperties::bppPoint(std::string resolution, std::string value) {
180
181 int32_t width = 0;
182 int32_t height = 0;
183 double bpp = -1;
184
185 // resolution is "WxH", "W*H" or a standard name like "720p"
186 if (resolution == "1080p") {
187 width = 1080; height = 1920;
188 } else if (resolution == "720p") {
189 width = 720; height = 1280;
190 } else if (resolution == "540p") {
191 width = 540; height = 960;
192 } else if (resolution == "480p") {
193 width = 480; height = 854;
194 } else {
195 size_t sep = resolution.find('x');
196 if (sep == std::string::npos) {
197 sep = resolution.find('*');
198 }
199 if (sep == std::string::npos) {
200 ALOGW("unable to parse resolution: '%s'", resolution.c_str());
201 return false;
202 }
203 std::string w = resolution.substr(0, sep);
204 std::string h = resolution.substr(sep+1);
205
206 char *q;
207 const char *p = w.c_str();
208 width = strtol(p, &q, 0);
209 if (q == p) {
210 width = -1;
211 }
212 p = h.c_str();
213 height = strtol(p, &q, 0);
214 if (q == p) {
215 height = -1;
216 }
217 if (width <= 0 || height <= 0 || width > DIMENSION_LIMIT || height > DIMENSION_LIMIT) {
218 ALOGW("unparseable: width, height '%s'", resolution.c_str());
219 return false;
220 }
221 }
222
223 const char *p = value.c_str();
224 char *q;
225 bpp = strtod(p, &q);
226 if (q == p) {
227 ALOGW("unparseable bpp '%s'", value.c_str());
228 return false;
229 }
230
231 struct bpp_point *point = (struct bpp_point*) malloc(sizeof(*point));
232 if (point == nullptr) {
233 ALOGW("unable to allocate memory for bpp point");
234 return false;
235 }
236
237 point->pixels = width * height;
238 point->width = width;
239 point->height = height;
240 point->bpp = bpp;
241
242 if (mBppPoints == nullptr) {
243 point->next = nullptr;
244 mBppPoints = point;
245 } else if (point->pixels < mBppPoints->pixels) {
246 // at the front
247 point->next = mBppPoints;
248 mBppPoints = point;
249 } else {
250 struct bpp_point *after = mBppPoints;
251 while (after->next) {
252 if (point->pixels > after->next->pixels) {
253 after = after->next;
254 continue;
255 }
256
257 // insert before after->next
258 point->next = after->next;
259 after->next = point;
260 break;
261 }
262 if (after->next == nullptr) {
263 // hasn't gone in yet
264 point->next = nullptr;
265 after->next = point;
266 }
267 }
268
269 return true;
270 }
271
getBpp(int32_t width,int32_t height)272 double CodecProperties::getBpp(int32_t width, int32_t height) {
273 // look in the per-resolution list
274
275 int32_t pixels = width * height;
276
277 if (mBppPoints) {
278 struct bpp_point *point = mBppPoints;
279 while (point && point->pixels < pixels) {
280 point = point->next;
281 }
282 if (point) {
283 ALOGV("getBpp(w=%d,h=%d) returns %f from bpppoint w=%d h=%d",
284 width, height, point->bpp, point->width, point->height);
285 return point->bpp;
286 }
287 }
288
289 ALOGV("defaulting to %f bpp", mBpp);
290 return mBpp;
291 }
292
qpMaxPoint(std::string resolution,std::string value)293 bool CodecProperties::qpMaxPoint(std::string resolution, std::string value) {
294
295 int32_t width = 0;
296 int32_t height = 0;
297 int qpMax = INT32_MAX;
298
299 // resolution is "WxH", "W*H" or a standard name like "720p"
300 if (resolution == "1080p") {
301 width = 1080; height = 1920;
302 } else if (resolution == "720p") {
303 width = 720; height = 1280;
304 } else if (resolution == "540p") {
305 width = 540; height = 960;
306 } else if (resolution == "480p") {
307 width = 480; height = 854;
308 } else {
309 size_t sep = resolution.find('x');
310 if (sep == std::string::npos) {
311 sep = resolution.find('*');
312 }
313 if (sep == std::string::npos) {
314 ALOGW("unable to parse resolution: '%s'", resolution.c_str());
315 return false;
316 }
317 std::string w = resolution.substr(0, sep);
318 std::string h = resolution.substr(sep+1);
319
320 char *q;
321 const char *p = w.c_str();
322 width = strtol(p, &q, 0);
323 if (q == p) {
324 width = -1;
325 }
326 p = h.c_str();
327 height = strtol(p, &q, 0);
328 if (q == p) {
329 height = -1;
330 }
331 if (width <= 0 || height <= 0 || width > DIMENSION_LIMIT || height > DIMENSION_LIMIT) {
332 ALOGW("unparseable: width, height '%s'", resolution.c_str());
333 return false;
334 }
335 }
336
337 const char *p = value.c_str();
338 char *q;
339 qpMax = strtol(p, &q, 0);
340 if (q == p) {
341 ALOGW("unparseable qpmax '%s'", value.c_str());
342 return false;
343 }
344
345 // convert to our internal 'unspecified' notation
346 if (qpMax == -1)
347 qpMax = INT32_MAX;
348
349 struct qpmax_point *point = (struct qpmax_point*) malloc(sizeof(*point));
350 if (point == nullptr) {
351 ALOGW("unable to allocate memory for qpmax point");
352 return false;
353 }
354
355 point->pixels = width * height;
356 point->width = width;
357 point->height = height;
358 point->qpMax = qpMax;
359
360 if (mQpMaxPoints == nullptr) {
361 point->next = nullptr;
362 mQpMaxPoints = point;
363 } else if (point->pixels < mQpMaxPoints->pixels) {
364 // at the front
365 point->next = mQpMaxPoints;
366 mQpMaxPoints = point;
367 } else {
368 struct qpmax_point *after = mQpMaxPoints;
369 while (after->next) {
370 if (point->pixels > after->next->pixels) {
371 after = after->next;
372 continue;
373 }
374
375 // insert before after->next
376 point->next = after->next;
377 after->next = point;
378 break;
379 }
380 if (after->next == nullptr) {
381 // hasn't gone in yet
382 point->next = nullptr;
383 after->next = point;
384 }
385 }
386
387 return true;
388 }
389
targetQpMax(int32_t width,int32_t height)390 int CodecProperties::targetQpMax(int32_t width, int32_t height) {
391 // look in the per-resolution list
392
393 int32_t pixels = width * height;
394
395 if (mQpMaxPoints) {
396 struct qpmax_point *point = mQpMaxPoints;
397 while (point && point->pixels < pixels) {
398 point = point->next;
399 }
400 if (point) {
401 ALOGV("targetQpMax(w=%d,h=%d) returns %d from qpmax_point w=%d h=%d",
402 width, height, point->qpMax, point->width, point->height);
403 return point->qpMax;
404 }
405 }
406
407 ALOGV("defaulting to %d qpmax", mTargetQpMax);
408 return mTargetQpMax;
409 }
410
setTargetQpMax(int qpMax)411 void CodecProperties::setTargetQpMax(int qpMax) {
412 // convert to our internal 'unspecified' notation
413 if (qpMax == -1)
414 qpMax = INT32_MAX;
415 mTargetQpMax = qpMax;
416 }
417
getMapping(std::string key,std::string kind)418 std::string CodecProperties::getMapping(std::string key, std::string kind) {
419 ALOGV("getMapping(key %s, kind %s )", key.c_str(), kind.c_str());
420 //play with mMappings
421 auto mapped = mMappings.find(kind + "-" + key);
422 if (mapped != mMappings.end()) {
423 std::string result = mapped->second;
424 ALOGV("getMapping(%s, %s) -> %s", key.c_str(), kind.c_str(), result.c_str());
425 return result;
426 }
427 ALOGV("nope, return unchanged key");
428 return key;
429 }
430
431
432 // really a bit of debugging code here.
showMappings()433 void CodecProperties::showMappings() {
434 ALOGD("Mappings:");
435 int count = 0;
436 for (const auto& [key, value] : mMappings) {
437 count++;
438 ALOGD("'%s' -> '%s'", key.c_str(), value.c_str());
439 }
440 ALOGD("total %d mappings", count);
441 }
442
setMapping(std::string kind,std::string key,std::string value)443 void CodecProperties::setMapping(std::string kind, std::string key, std::string value) {
444 ALOGV("setMapping(%s,%s,%s)", kind.c_str(), key.c_str(), value.c_str());
445 std::string metaKey = kind + "-" + key;
446 mMappings.insert({metaKey, value});
447 }
448
getMappings(std::string kind,bool reverse)449 const char **CodecProperties::getMappings(std::string kind, bool reverse) {
450 ALOGV("getMappings(kind %s, reverse %d", kind.c_str(), reverse);
451 // how many do we need?
452 int count = mMappings.size();
453 if (count == 0) {
454 ALOGV("empty mappings");
455 return nullptr;
456 }
457 size_t size = sizeof(char *) * (2 * count + 2);
458 const char **result = (const char **)malloc(size);
459 if (result == nullptr) {
460 ALOGW("no memory to return mappings");
461 return nullptr;
462 }
463 memset(result, '\0', size);
464
465 const char **pp = result;
466 for (const auto& [key, value] : mMappings) {
467 // split out the kind/key
468 size_t pos = key.find('-');
469 if (pos == std::string::npos) {
470 ALOGD("ignoring malformed key: %s", key.c_str());
471 continue;
472 }
473 std::string actualKind = key.substr(0,pos);
474 if (kind.length() != 0 && kind != actualKind) {
475 ALOGD("kinds don't match: want '%s' got '%s'", kind.c_str(), actualKind.c_str());
476 continue;
477 }
478 if (reverse) {
479 // codec specific -> std aka 'unmapping'
480 pp[0] = strdup( value.c_str());
481 pp[1] = strdup( key.substr(pos+1).c_str());
482 } else {
483 // std -> codec specific
484 pp[0] = strdup( key.substr(pos+1).c_str());
485 pp[1] = strdup( value.c_str());
486 }
487 ALOGV(" %s -> %s", pp[0], pp[1]);
488 pp += 2;
489 }
490
491 pp[0] = nullptr;
492 pp[1] = nullptr;
493
494 return result;
495 }
496
497
498 } // namespace mediaformatshaper
499 } // namespace android
500
501