blob: 694182d4d1e136329ff76e945e17ecc3adb36325 [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);
74 ALOGD("minquality: are we eligible: %d", isVQEligible);
75 if (!isVQEligible) {
76 ALOGD("minquality: not an eligible device class");
77 return 0;
78 }
79
Ray Essick970f1c82021-03-25 13:37:45 -070080 if (codec->supportedMinimumQuality() > 0) {
81 // allow the codec provided minimum quality behavior to work at it
82 ALOGD("minquality: codec claims to implement minquality=%d",
83 codec->supportedMinimumQuality());
Ray Essick1e3ca832021-06-28 15:26:47 -070084
85 // tell the underlying codec to do its thing; we won't try to second guess.
86 // default to 1, aka S_HANDHELD;
87 int32_t qualityTarget = 1;
88 (void) codec->getFeatureValue("_quality.target", &qualityTarget);
89 AMediaFormat_setInt32(inFormat, "android._encoding-quality-level", qualityTarget);
Ray Essick970f1c82021-03-25 13:37:45 -070090 return 0;
91 }
Ray Essick1831f7b2021-03-15 16:10:51 -070092
Ray Essick1e3ca832021-06-28 15:26:47 -070093 // let the codec know that we'll be enforcing the minimum quality standards
94 AMediaFormat_setInt32(inFormat, "android._encoding-quality-level", 0);
95
Ray Essick1831f7b2021-03-15 16:10:51 -070096 //
Ray Essick244aaa52021-04-05 14:47:02 -070097 // consider any and all tools available
Ray Essick1831f7b2021-03-15 16:10:51 -070098 // -- qp
99 // -- minimum bits-per-pixel
100 //
Ray Essick244aaa52021-04-05 14:47:02 -0700101 int64_t bitrateChosen = 0;
102 int32_t qpChosen = INT32_MAX;
103
104 int64_t bitrateConfigured = 0;
105 int32_t bitrateConfiguredTmp = 0;
106 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp);
107 bitrateConfigured = bitrateConfiguredTmp;
108 bitrateChosen = bitrateConfigured;
109
110 int32_t width = 0;
111 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
112 int32_t height = 0;
113 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
114 int64_t pixels = ((int64_t)width) * height;
115 double minimumBpp = codec->getBpp(width, height);
116
117 int64_t bitrateFloor = pixels * minimumBpp;
Ray Essick614d8da2021-04-27 11:35:00 -0700118 int64_t bitrateCeiling = bitrateFloor * codec->getPhaseOut();
Ray Essick244aaa52021-04-05 14:47:02 -0700119 if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
Ray Essick7b4e9d92021-04-20 16:48:00 -0700120 if (bitrateCeiling > INT32_MAX) bitrateCeiling = INT32_MAX;
Ray Essick244aaa52021-04-05 14:47:02 -0700121
122 // if we are far enough above the target bpp, leave it alone
123 //
124 ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700125 if (bitrateConfigured >= bitrateCeiling) {
126 ALOGV("high enough bitrate: configured %" PRId64 " >= ceiling %" PRId64,
127 bitrateConfigured, bitrateCeiling);
Ray Essick244aaa52021-04-05 14:47:02 -0700128 return 0;
129 }
130
131 // raise anything below the bitrate floor
132 if (bitrateConfigured < bitrateFloor) {
133 ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64,
134 bitrateConfigured, bitrateFloor);
135 bitrateChosen = bitrateFloor;
136 }
137
Ray Essick7b4e9d92021-04-20 16:48:00 -0700138 bool qpPresent = hasQpMax(inFormat);
Ray Essick244aaa52021-04-05 14:47:02 -0700139
Ray Essick7b4e9d92021-04-20 16:48:00 -0700140 // calculate a target QP value
Ray Essick981ddf82021-05-03 13:33:09 -0700141 int32_t qpmax = codec->targetQpMax(width, height);
Ray Essick244aaa52021-04-05 14:47:02 -0700142 if (!qpPresent) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700143 // user didn't, so shaper wins
Ray Essick244aaa52021-04-05 14:47:02 -0700144 if (qpmax != INT32_MAX) {
145 ALOGV("choosing qp=%d", qpmax);
146 qpChosen = qpmax;
Ray Essick970f1c82021-03-25 13:37:45 -0700147 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700148 } else if (qpmax == INT32_MAX) {
149 // shaper didn't so user wins
150 qpChosen = INT32_MAX;
151 AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpChosen);
152 } else {
153 // both sides want it, choose most restrictive
154 int32_t value = INT32_MAX;
155 AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &value);
156 qpChosen = std::min(qpmax, value);
Ray Essick1831f7b2021-03-15 16:10:51 -0700157 }
158
Ray Essick244aaa52021-04-05 14:47:02 -0700159 // if QP is desired but not supported, compensate with additional bits
160 if (!codec->supportsQp()) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700161 if (qpChosen != INT32_MAX) {
Ray Essick614d8da2021-04-27 11:35:00 -0700162 int64_t boost = bitrateChosen * codec->getMissingQpBoost();
Ray Essick7b4e9d92021-04-20 16:48:00 -0700163 ALOGD("minquality: requested QP unsupported, boost bitrate %" PRId64 " by %" PRId64,
164 bitrateChosen, boost);
165 bitrateChosen = bitrateChosen + boost;
Ray Essick244aaa52021-04-05 14:47:02 -0700166 qpChosen = INT32_MAX;
Ray Essick1831f7b2021-03-15 16:10:51 -0700167 }
168 }
169
Ray Essick7b4e9d92021-04-20 16:48:00 -0700170 // limits
Ray Essick244aaa52021-04-05 14:47:02 -0700171 // apply our chosen values
172 //
173 if (qpChosen != INT32_MAX) {
174 ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
175 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
176
Ray Essick7b4e9d92021-04-20 16:48:00 -0700177 // caller (VideoShaper) handles spreading this across the subframes
Ray Essick244aaa52021-04-05 14:47:02 -0700178 }
179
180 if (bitrateChosen != bitrateConfigured) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700181 if (bitrateChosen > bitrateCeiling) {
Ray Essick614d8da2021-04-27 11:35:00 -0700182 ALOGD("minquality: bitrate increase clamped at ceiling %" PRId64, bitrateCeiling);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700183 bitrateChosen = bitrateCeiling;
184 }
Ray Essick244aaa52021-04-05 14:47:02 -0700185 ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps",
186 bitrateConfigured, bitrateChosen);
187 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen);
188 }
189
Ray Essick1831f7b2021-03-15 16:10:51 -0700190 return 0;
191}
192
193
Ray Essick7b4e9d92021-04-20 16:48:00 -0700194bool hasQpMaxPerFrameType(AMediaFormat *format) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700195 int32_t value;
196
Ray Essick970f1c82021-03-25 13:37:45 -0700197 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
198 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700199 return true;
200 }
Ray Essick970f1c82021-03-25 13:37:45 -0700201 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
202 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700203 return true;
204 }
Ray Essick970f1c82021-03-25 13:37:45 -0700205 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
206 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700207 return true;
208 }
209 return false;
210}
211
Ray Essick7b4e9d92021-04-20 16:48:00 -0700212bool hasQpMaxGlobal(AMediaFormat *format) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700213 int32_t value;
Ray Essick970f1c82021-03-25 13:37:45 -0700214 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
215 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700216 return true;
217 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700218 return false;
219}
220
221bool hasQpMax(AMediaFormat *format) {
222 if (hasQpMaxGlobal(format)) {
223 return true;
224 }
225 return hasQpMaxPerFrameType(format);
Ray Essick1831f7b2021-03-15 16:10:51 -0700226}
227
228void qpSpreadPerFrameType(AMediaFormat *format, int delta,
229 int qplow, int qphigh, bool override) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700230
Ray Essick1831f7b2021-03-15 16:10:51 -0700231 qpSpreadMinPerFrameType(format, qplow, override);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700232 qpSpreadMaxPerFrameType(format, delta, qphigh, override);
233 // make sure that min<max for all the QP fields.
234 qpVerifyMinMaxOrdering(format);
Ray Essick1831f7b2021-03-15 16:10:51 -0700235}
236
237void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override) {
238 ALOGV("format %p delta %d hi %d override %d", format, delta, qphigh, override);
239
240 int32_t qpOffered = 0;
241 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &qpOffered)) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700242 // propagate to frame-specific keys, choosing most restrictive
243 // ensure that we don't violate min<=max rules
244 {
245 int32_t maxI = INT32_MAX;
246 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI);
247 int32_t value = std::min({qpOffered, qphigh, maxI});
Ray Essick1831f7b2021-03-15 16:10:51 -0700248 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, value);
249 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700250 {
251 int32_t maxP = INT32_MAX;
252 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP);
253 int32_t value = std::min({(std::min(qpOffered, INT32_MAX-1*delta) + 1*delta),
254 qphigh, maxP});
Ray Essick1831f7b2021-03-15 16:10:51 -0700255 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, value);
256 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700257 {
258 int32_t maxB = INT32_MAX;
259 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB);
260 int32_t value = std::min({(std::min(qpOffered, INT32_MAX-2*delta) + 2*delta),
261 qphigh, maxB});
Ray Essick1831f7b2021-03-15 16:10:51 -0700262 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, value);
263 }
264 }
265}
266
267void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override) {
268 ALOGV("format %p lo %d override %d", format, qplow, override);
269
270 int32_t qpOffered = 0;
271 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &qpOffered)) {
272 int value = std::max(qplow, qpOffered);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700273 // propagate to frame-specific keys, use lowest of this and existing per-frame value
274 int32_t minI = INT32_MAX;
275 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI);
276 int32_t setI = std::min(value, minI);
277 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, setI);
278
279 int32_t minP = INT32_MAX;
280 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP);
281 int32_t setP = std::min(value, minP);
282 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, setP);
283
284 int32_t minB = INT32_MAX;
285 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB);
286 int32_t setB = std::min(value, minB);
287 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, setB);
288 }
289}
290
291// XXX whether we allow min==max, or if we'll insist that min<max
292void qpVerifyMinMaxOrdering(AMediaFormat *format) {
293 // ensure that we don't violate min<=max rules
294 int32_t maxI = INT32_MAX;
295 int32_t minI = INT32_MIN;
296 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)
297 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)
298 && minI > maxI) {
299 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, maxI);
300 }
301 int32_t maxP = INT32_MAX;
302 int32_t minP = INT32_MIN;
303 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)
304 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)
305 && minP > maxP) {
306 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, maxP);
307 }
308 int32_t maxB = INT32_MAX;
309 int32_t minB = INT32_MIN;
310 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)
311 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)
312 && minB > maxB) {
313 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, maxB);
Ray Essick1831f7b2021-03-15 16:10:51 -0700314 }
315}
316
317} // namespace mediaformatshaper
318} // namespace android
319