blob: 26ff44693812ece1c2ee23c27850f638f972936c [file] [log] [blame]
Ray Essick1831f7b2021-03-15 16:10:51 -07001/*
2 * Copyright (C) 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 "VQApply"
19#include <utils/Log.h>
20
21#include <string>
22#include <inttypes.h>
23
24#include <media/NdkMediaFormat.h>
25
Ray Essick7b4e9d92021-04-20 16:48:00 -070026#include "VQops.h"
27#include "CodecProperties.h"
28#include "VideoShaper.h"
Ray Essick1831f7b2021-03-15 16:10:51 -070029
30namespace android {
31namespace mediaformatshaper {
32
33
34// these are all NDK#31 and we run as NDK#29 (to be within the module)
35// the __builtin_available(android 31, *) constructs didn't work for me.
36//
37#define AMEDIAFORMAT_VIDEO_QP_MAX "video-qp-max"
38#define AMEDIAFORMAT_VIDEO_QP_MIN "video-qp-min"
39
40#define AMEDIAFORMAT_VIDEO_QP_B_MAX "video-qp-b-max"
41#define AMEDIAFORMAT_VIDEO_QP_B_MIN "video-qp-b-min"
42#define AMEDIAFORMAT_VIDEO_QP_I_MAX "video-qp-i-max"
43#define AMEDIAFORMAT_VIDEO_QP_I_MIN "video-qp-i-min"
44#define AMEDIAFORMAT_VIDEO_QP_P_MAX "video-qp-p-max"
45#define AMEDIAFORMAT_VIDEO_QP_P_MIN "video-qp-p-min"
46
Ray Essick970f1c82021-03-25 13:37:45 -070047// defined in the SDK, but not in the NDK
48//
49static const int BITRATE_MODE_VBR = 1;
50
Ray Essick244aaa52021-04-05 14:47:02 -070051
Ray Essick1831f7b2021-03-15 16:10:51 -070052//
53// Caller retains ownership of and responsibility for inFormat
54//
55int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int flags) {
56 ALOGV("codecName %s inFormat %p flags x%x", codec->getName().c_str(), inFormat, flags);
Ray Essick7b4e9d92021-04-20 16:48:00 -070057 (void) info; // unused for now
Ray Essick1831f7b2021-03-15 16:10:51 -070058
Ray Essick970f1c82021-03-25 13:37:45 -070059 int32_t bitRateMode = -1;
60 if (AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BITRATE_MODE, &bitRateMode)
61 && bitRateMode != BITRATE_MODE_VBR) {
62 ALOGD("minquality: applies only to VBR encoding");
Ray Essick1831f7b2021-03-15 16:10:51 -070063 return 0;
64 }
65
Ray Essick1e3ca832021-06-28 15:26:47 -070066 // only proceed if we're in the handheld category.
67 // We embed this information within the codec record when we build up features
68 // and pass them in from MediaCodec; it's the easiest place to store it
69 //
70 // TODO: make a #define for ' _vq_eligible.device' here and in MediaCodec.cpp
71 //
72 int32_t isVQEligible = 0;
73 (void) codec->getFeatureValue("_vq_eligible.device", &isVQEligible);
Ray Essick1e3ca832021-06-28 15:26:47 -070074 if (!isVQEligible) {
75 ALOGD("minquality: not an eligible device class");
76 return 0;
77 }
78
Ray Essick419c1472021-07-02 13:38:32 -070079 // look at resolution to determine if we want any shaping/modification at all.
80 //
81 // we currently only shape (or ask the underlying codec to shape) for
82 // resolution range 320x240 < target <= 1920x1080
83 // NB: the < vs <=, that is deliberate.
84 //
85
86 int32_t width = 0;
87 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
88 int32_t height = 0;
89 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
90 int64_t pixels = ((int64_t)width) * height;
91
92 bool eligibleSize = true;
93 if (pixels <= 320 * 240) {
94 eligibleSize = false;
95 } else if (pixels > 1920 * 1088) {
96 eligibleSize = false;
97 }
98
99 if (!eligibleSize) {
100 // we won't shape, and ask that the codec not shape
101 ALOGD("minquality: %dx%d outside of shaping range", width, height);
102 AMediaFormat_setInt32(inFormat, "android._encoding-quality-level", 0);
103 return 0;
104 }
105
Ray Essick970f1c82021-03-25 13:37:45 -0700106 if (codec->supportedMinimumQuality() > 0) {
Ray Essick419c1472021-07-02 13:38:32 -0700107 // have the codec-provided minimum quality behavior to work at it
Ray Essick970f1c82021-03-25 13:37:45 -0700108 ALOGD("minquality: codec claims to implement minquality=%d",
109 codec->supportedMinimumQuality());
Ray Essick1e3ca832021-06-28 15:26:47 -0700110
111 // tell the underlying codec to do its thing; we won't try to second guess.
112 // default to 1, aka S_HANDHELD;
113 int32_t qualityTarget = 1;
114 (void) codec->getFeatureValue("_quality.target", &qualityTarget);
115 AMediaFormat_setInt32(inFormat, "android._encoding-quality-level", qualityTarget);
Ray Essick970f1c82021-03-25 13:37:45 -0700116 return 0;
117 }
Ray Essick1831f7b2021-03-15 16:10:51 -0700118
Ray Essick1e3ca832021-06-28 15:26:47 -0700119 // let the codec know that we'll be enforcing the minimum quality standards
120 AMediaFormat_setInt32(inFormat, "android._encoding-quality-level", 0);
121
Ray Essick1831f7b2021-03-15 16:10:51 -0700122 //
Ray Essick244aaa52021-04-05 14:47:02 -0700123 // consider any and all tools available
Ray Essick1831f7b2021-03-15 16:10:51 -0700124 // -- qp
125 // -- minimum bits-per-pixel
126 //
Ray Essick244aaa52021-04-05 14:47:02 -0700127 int64_t bitrateChosen = 0;
128 int32_t qpChosen = INT32_MAX;
129
130 int64_t bitrateConfigured = 0;
131 int32_t bitrateConfiguredTmp = 0;
132 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp);
133 bitrateConfigured = bitrateConfiguredTmp;
134 bitrateChosen = bitrateConfigured;
135
Ray Essick419c1472021-07-02 13:38:32 -0700136 // width, height, and pixels are calculated above
137
Ray Essick244aaa52021-04-05 14:47:02 -0700138 double minimumBpp = codec->getBpp(width, height);
139
140 int64_t bitrateFloor = pixels * minimumBpp;
Ray Essick614d8da2021-04-27 11:35:00 -0700141 int64_t bitrateCeiling = bitrateFloor * codec->getPhaseOut();
Ray Essick244aaa52021-04-05 14:47:02 -0700142 if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
Ray Essick7b4e9d92021-04-20 16:48:00 -0700143 if (bitrateCeiling > INT32_MAX) bitrateCeiling = INT32_MAX;
Ray Essick244aaa52021-04-05 14:47:02 -0700144
145 // if we are far enough above the target bpp, leave it alone
146 //
147 ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700148 if (bitrateConfigured >= bitrateCeiling) {
149 ALOGV("high enough bitrate: configured %" PRId64 " >= ceiling %" PRId64,
150 bitrateConfigured, bitrateCeiling);
Ray Essick244aaa52021-04-05 14:47:02 -0700151 return 0;
152 }
153
154 // raise anything below the bitrate floor
155 if (bitrateConfigured < bitrateFloor) {
156 ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64,
157 bitrateConfigured, bitrateFloor);
158 bitrateChosen = bitrateFloor;
159 }
160
Ray Essick7b4e9d92021-04-20 16:48:00 -0700161 bool qpPresent = hasQpMax(inFormat);
Ray Essick244aaa52021-04-05 14:47:02 -0700162
Ray Essick7b4e9d92021-04-20 16:48:00 -0700163 // calculate a target QP value
Ray Essick981ddf82021-05-03 13:33:09 -0700164 int32_t qpmax = codec->targetQpMax(width, height);
Ray Essick244aaa52021-04-05 14:47:02 -0700165 if (!qpPresent) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700166 // user didn't, so shaper wins
Ray Essick244aaa52021-04-05 14:47:02 -0700167 if (qpmax != INT32_MAX) {
168 ALOGV("choosing qp=%d", qpmax);
169 qpChosen = qpmax;
Ray Essick970f1c82021-03-25 13:37:45 -0700170 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700171 } else if (qpmax == INT32_MAX) {
172 // shaper didn't so user wins
173 qpChosen = INT32_MAX;
174 AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpChosen);
175 } else {
176 // both sides want it, choose most restrictive
177 int32_t value = INT32_MAX;
178 AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &value);
179 qpChosen = std::min(qpmax, value);
Ray Essick1831f7b2021-03-15 16:10:51 -0700180 }
181
Ray Essick244aaa52021-04-05 14:47:02 -0700182 // if QP is desired but not supported, compensate with additional bits
183 if (!codec->supportsQp()) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700184 if (qpChosen != INT32_MAX) {
Ray Essick614d8da2021-04-27 11:35:00 -0700185 int64_t boost = bitrateChosen * codec->getMissingQpBoost();
Ray Essick7b4e9d92021-04-20 16:48:00 -0700186 ALOGD("minquality: requested QP unsupported, boost bitrate %" PRId64 " by %" PRId64,
187 bitrateChosen, boost);
188 bitrateChosen = bitrateChosen + boost;
Ray Essick244aaa52021-04-05 14:47:02 -0700189 qpChosen = INT32_MAX;
Ray Essick1831f7b2021-03-15 16:10:51 -0700190 }
191 }
192
Ray Essick7b4e9d92021-04-20 16:48:00 -0700193 // limits
Ray Essick244aaa52021-04-05 14:47:02 -0700194 // apply our chosen values
195 //
196 if (qpChosen != INT32_MAX) {
197 ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
198 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
199
Ray Essick7b4e9d92021-04-20 16:48:00 -0700200 // caller (VideoShaper) handles spreading this across the subframes
Ray Essick244aaa52021-04-05 14:47:02 -0700201 }
202
203 if (bitrateChosen != bitrateConfigured) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700204 if (bitrateChosen > bitrateCeiling) {
Ray Essick614d8da2021-04-27 11:35:00 -0700205 ALOGD("minquality: bitrate increase clamped at ceiling %" PRId64, bitrateCeiling);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700206 bitrateChosen = bitrateCeiling;
207 }
Ray Essick244aaa52021-04-05 14:47:02 -0700208 ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps",
209 bitrateConfigured, bitrateChosen);
210 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen);
211 }
212
Ray Essick1831f7b2021-03-15 16:10:51 -0700213 return 0;
214}
215
216
Ray Essick7b4e9d92021-04-20 16:48:00 -0700217bool hasQpMaxPerFrameType(AMediaFormat *format) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700218 int32_t value;
219
Ray Essick970f1c82021-03-25 13:37:45 -0700220 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
221 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700222 return true;
223 }
Ray Essick970f1c82021-03-25 13:37:45 -0700224 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
225 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700226 return true;
227 }
Ray Essick970f1c82021-03-25 13:37:45 -0700228 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
229 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700230 return true;
231 }
232 return false;
233}
234
Ray Essick7b4e9d92021-04-20 16:48:00 -0700235bool hasQpMaxGlobal(AMediaFormat *format) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700236 int32_t value;
Ray Essick970f1c82021-03-25 13:37:45 -0700237 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
238 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700239 return true;
240 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700241 return false;
242}
243
244bool hasQpMax(AMediaFormat *format) {
245 if (hasQpMaxGlobal(format)) {
246 return true;
247 }
248 return hasQpMaxPerFrameType(format);
Ray Essick1831f7b2021-03-15 16:10:51 -0700249}
250
251void qpSpreadPerFrameType(AMediaFormat *format, int delta,
252 int qplow, int qphigh, bool override) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700253
Ray Essick1831f7b2021-03-15 16:10:51 -0700254 qpSpreadMinPerFrameType(format, qplow, override);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700255 qpSpreadMaxPerFrameType(format, delta, qphigh, override);
256 // make sure that min<max for all the QP fields.
257 qpVerifyMinMaxOrdering(format);
Ray Essick1831f7b2021-03-15 16:10:51 -0700258}
259
260void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override) {
261 ALOGV("format %p delta %d hi %d override %d", format, delta, qphigh, override);
262
263 int32_t qpOffered = 0;
264 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &qpOffered)) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700265 // propagate to frame-specific keys, choosing most restrictive
266 // ensure that we don't violate min<=max rules
267 {
268 int32_t maxI = INT32_MAX;
269 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI);
270 int32_t value = std::min({qpOffered, qphigh, maxI});
Ray Essick1831f7b2021-03-15 16:10:51 -0700271 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, value);
272 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700273 {
274 int32_t maxP = INT32_MAX;
275 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP);
276 int32_t value = std::min({(std::min(qpOffered, INT32_MAX-1*delta) + 1*delta),
277 qphigh, maxP});
Ray Essick1831f7b2021-03-15 16:10:51 -0700278 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, value);
279 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700280 {
281 int32_t maxB = INT32_MAX;
282 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB);
283 int32_t value = std::min({(std::min(qpOffered, INT32_MAX-2*delta) + 2*delta),
284 qphigh, maxB});
Ray Essick1831f7b2021-03-15 16:10:51 -0700285 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, value);
286 }
287 }
288}
289
290void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override) {
291 ALOGV("format %p lo %d override %d", format, qplow, override);
292
293 int32_t qpOffered = 0;
294 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &qpOffered)) {
295 int value = std::max(qplow, qpOffered);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700296 // propagate to frame-specific keys, use lowest of this and existing per-frame value
297 int32_t minI = INT32_MAX;
298 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI);
299 int32_t setI = std::min(value, minI);
300 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, setI);
301
302 int32_t minP = INT32_MAX;
303 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP);
304 int32_t setP = std::min(value, minP);
305 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, setP);
306
307 int32_t minB = INT32_MAX;
308 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB);
309 int32_t setB = std::min(value, minB);
310 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, setB);
311 }
312}
313
314// XXX whether we allow min==max, or if we'll insist that min<max
315void qpVerifyMinMaxOrdering(AMediaFormat *format) {
316 // ensure that we don't violate min<=max rules
317 int32_t maxI = INT32_MAX;
318 int32_t minI = INT32_MIN;
319 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)
320 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)
321 && minI > maxI) {
322 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, maxI);
323 }
324 int32_t maxP = INT32_MAX;
325 int32_t minP = INT32_MIN;
326 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)
327 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)
328 && minP > maxP) {
329 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, maxP);
330 }
331 int32_t maxB = INT32_MAX;
332 int32_t minB = INT32_MIN;
333 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)
334 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)
335 && minB > maxB) {
336 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, maxB);
Ray Essick1831f7b2021-03-15 16:10:51 -0700337 }
338}
339
340} // namespace mediaformatshaper
341} // namespace android
342