• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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