blob: 4f6a6c370ba90d7602cd25c509b12c48773ad908 [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
52// constants we use within the calculations
53//
Ray Essick7b4e9d92021-04-20 16:48:00 -070054constexpr double BITRATE_LEAVE_UNTOUCHED = 1.75;
55
56// 20% bump if QP is configured but it is unavailable
57constexpr double BITRATE_QP_UNAVAILABLE_BOOST = 0.20;
58
Ray Essick244aaa52021-04-05 14:47:02 -070059
Ray Essick1831f7b2021-03-15 16:10:51 -070060//
61// Caller retains ownership of and responsibility for inFormat
62//
63int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int flags) {
64 ALOGV("codecName %s inFormat %p flags x%x", codec->getName().c_str(), inFormat, flags);
Ray Essick7b4e9d92021-04-20 16:48:00 -070065 (void) info; // unused for now
Ray Essick1831f7b2021-03-15 16:10:51 -070066
Ray Essick970f1c82021-03-25 13:37:45 -070067 int32_t bitRateMode = -1;
68 if (AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BITRATE_MODE, &bitRateMode)
69 && bitRateMode != BITRATE_MODE_VBR) {
70 ALOGD("minquality: applies only to VBR encoding");
Ray Essick1831f7b2021-03-15 16:10:51 -070071 return 0;
72 }
73
Ray Essick970f1c82021-03-25 13:37:45 -070074 if (codec->supportedMinimumQuality() > 0) {
75 // allow the codec provided minimum quality behavior to work at it
76 ALOGD("minquality: codec claims to implement minquality=%d",
77 codec->supportedMinimumQuality());
78 return 0;
79 }
Ray Essick1831f7b2021-03-15 16:10:51 -070080
81 //
Ray Essick244aaa52021-04-05 14:47:02 -070082 // consider any and all tools available
Ray Essick1831f7b2021-03-15 16:10:51 -070083 // -- qp
84 // -- minimum bits-per-pixel
85 //
Ray Essick244aaa52021-04-05 14:47:02 -070086 int64_t bitrateChosen = 0;
87 int32_t qpChosen = INT32_MAX;
88
89 int64_t bitrateConfigured = 0;
90 int32_t bitrateConfiguredTmp = 0;
91 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp);
92 bitrateConfigured = bitrateConfiguredTmp;
93 bitrateChosen = bitrateConfigured;
94
95 int32_t width = 0;
96 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
97 int32_t height = 0;
98 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
99 int64_t pixels = ((int64_t)width) * height;
100 double minimumBpp = codec->getBpp(width, height);
101
102 int64_t bitrateFloor = pixels * minimumBpp;
Ray Essick7b4e9d92021-04-20 16:48:00 -0700103 int64_t bitrateCeiling = bitrateFloor * BITRATE_LEAVE_UNTOUCHED;
Ray Essick244aaa52021-04-05 14:47:02 -0700104 if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
Ray Essick7b4e9d92021-04-20 16:48:00 -0700105 if (bitrateCeiling > INT32_MAX) bitrateCeiling = INT32_MAX;
Ray Essick244aaa52021-04-05 14:47:02 -0700106
107 // if we are far enough above the target bpp, leave it alone
108 //
109 ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700110 if (bitrateConfigured >= bitrateCeiling) {
111 ALOGV("high enough bitrate: configured %" PRId64 " >= ceiling %" PRId64,
112 bitrateConfigured, bitrateCeiling);
Ray Essick244aaa52021-04-05 14:47:02 -0700113 return 0;
114 }
115
116 // raise anything below the bitrate floor
117 if (bitrateConfigured < bitrateFloor) {
118 ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64,
119 bitrateConfigured, bitrateFloor);
120 bitrateChosen = bitrateFloor;
121 }
122
Ray Essick7b4e9d92021-04-20 16:48:00 -0700123 bool qpPresent = hasQpMax(inFormat);
Ray Essick244aaa52021-04-05 14:47:02 -0700124
Ray Essick7b4e9d92021-04-20 16:48:00 -0700125 // calculate a target QP value
126 int32_t qpmax = codec->targetQpMax();
Ray Essick244aaa52021-04-05 14:47:02 -0700127 if (!qpPresent) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700128 // user didn't, so shaper wins
Ray Essick244aaa52021-04-05 14:47:02 -0700129 if (qpmax != INT32_MAX) {
130 ALOGV("choosing qp=%d", qpmax);
131 qpChosen = qpmax;
Ray Essick970f1c82021-03-25 13:37:45 -0700132 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700133 } else if (qpmax == INT32_MAX) {
134 // shaper didn't so user wins
135 qpChosen = INT32_MAX;
136 AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpChosen);
137 } else {
138 // both sides want it, choose most restrictive
139 int32_t value = INT32_MAX;
140 AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &value);
141 qpChosen = std::min(qpmax, value);
Ray Essick1831f7b2021-03-15 16:10:51 -0700142 }
143
Ray Essick244aaa52021-04-05 14:47:02 -0700144 // if QP is desired but not supported, compensate with additional bits
145 if (!codec->supportsQp()) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700146 if (qpChosen != INT32_MAX) {
147 int64_t boost = 0;
148 boost = bitrateChosen * BITRATE_QP_UNAVAILABLE_BOOST;
149 ALOGD("minquality: requested QP unsupported, boost bitrate %" PRId64 " by %" PRId64,
150 bitrateChosen, boost);
151 bitrateChosen = bitrateChosen + boost;
Ray Essick244aaa52021-04-05 14:47:02 -0700152 qpChosen = INT32_MAX;
Ray Essick1831f7b2021-03-15 16:10:51 -0700153 }
154 }
155
Ray Essick7b4e9d92021-04-20 16:48:00 -0700156 // limits
Ray Essick244aaa52021-04-05 14:47:02 -0700157 // apply our chosen values
158 //
159 if (qpChosen != INT32_MAX) {
160 ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
161 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
162
Ray Essick7b4e9d92021-04-20 16:48:00 -0700163 // caller (VideoShaper) handles spreading this across the subframes
Ray Essick244aaa52021-04-05 14:47:02 -0700164 }
165
166 if (bitrateChosen != bitrateConfigured) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700167 if (bitrateChosen > bitrateCeiling) {
168 ALOGD("minquality: bitrate clamped at ceiling %" PRId64, bitrateCeiling);
169 bitrateChosen = bitrateCeiling;
170 }
Ray Essick244aaa52021-04-05 14:47:02 -0700171 ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps",
172 bitrateConfigured, bitrateChosen);
173 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen);
174 }
175
Ray Essick1831f7b2021-03-15 16:10:51 -0700176 return 0;
177}
178
179
Ray Essick7b4e9d92021-04-20 16:48:00 -0700180bool hasQpMaxPerFrameType(AMediaFormat *format) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700181 int32_t value;
182
Ray Essick970f1c82021-03-25 13:37:45 -0700183 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
184 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700185 return true;
186 }
Ray Essick970f1c82021-03-25 13:37:45 -0700187 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
188 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700189 return true;
190 }
Ray Essick970f1c82021-03-25 13:37:45 -0700191 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
192 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700193 return true;
194 }
195 return false;
196}
197
Ray Essick7b4e9d92021-04-20 16:48:00 -0700198bool hasQpMaxGlobal(AMediaFormat *format) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700199 int32_t value;
Ray Essick970f1c82021-03-25 13:37:45 -0700200 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
201 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700202 return true;
203 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700204 return false;
205}
206
207bool hasQpMax(AMediaFormat *format) {
208 if (hasQpMaxGlobal(format)) {
209 return true;
210 }
211 return hasQpMaxPerFrameType(format);
Ray Essick1831f7b2021-03-15 16:10:51 -0700212}
213
214void qpSpreadPerFrameType(AMediaFormat *format, int delta,
215 int qplow, int qphigh, bool override) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700216
Ray Essick1831f7b2021-03-15 16:10:51 -0700217 qpSpreadMinPerFrameType(format, qplow, override);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700218 qpSpreadMaxPerFrameType(format, delta, qphigh, override);
219 // make sure that min<max for all the QP fields.
220 qpVerifyMinMaxOrdering(format);
Ray Essick1831f7b2021-03-15 16:10:51 -0700221}
222
223void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override) {
224 ALOGV("format %p delta %d hi %d override %d", format, delta, qphigh, override);
225
226 int32_t qpOffered = 0;
227 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &qpOffered)) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700228 // propagate to frame-specific keys, choosing most restrictive
229 // ensure that we don't violate min<=max rules
230 {
231 int32_t maxI = INT32_MAX;
232 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI);
233 int32_t value = std::min({qpOffered, qphigh, maxI});
Ray Essick1831f7b2021-03-15 16:10:51 -0700234 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, value);
235 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700236 {
237 int32_t maxP = INT32_MAX;
238 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP);
239 int32_t value = std::min({(std::min(qpOffered, INT32_MAX-1*delta) + 1*delta),
240 qphigh, maxP});
Ray Essick1831f7b2021-03-15 16:10:51 -0700241 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, value);
242 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700243 {
244 int32_t maxB = INT32_MAX;
245 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB);
246 int32_t value = std::min({(std::min(qpOffered, INT32_MAX-2*delta) + 2*delta),
247 qphigh, maxB});
Ray Essick1831f7b2021-03-15 16:10:51 -0700248 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, value);
249 }
250 }
251}
252
253void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override) {
254 ALOGV("format %p lo %d override %d", format, qplow, override);
255
256 int32_t qpOffered = 0;
257 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &qpOffered)) {
258 int value = std::max(qplow, qpOffered);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700259 // propagate to frame-specific keys, use lowest of this and existing per-frame value
260 int32_t minI = INT32_MAX;
261 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI);
262 int32_t setI = std::min(value, minI);
263 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, setI);
264
265 int32_t minP = INT32_MAX;
266 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP);
267 int32_t setP = std::min(value, minP);
268 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, setP);
269
270 int32_t minB = INT32_MAX;
271 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB);
272 int32_t setB = std::min(value, minB);
273 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, setB);
274 }
275}
276
277// XXX whether we allow min==max, or if we'll insist that min<max
278void qpVerifyMinMaxOrdering(AMediaFormat *format) {
279 // ensure that we don't violate min<=max rules
280 int32_t maxI = INT32_MAX;
281 int32_t minI = INT32_MIN;
282 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)
283 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)
284 && minI > maxI) {
285 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, maxI);
286 }
287 int32_t maxP = INT32_MAX;
288 int32_t minP = INT32_MIN;
289 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)
290 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)
291 && minP > maxP) {
292 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, maxP);
293 }
294 int32_t maxB = INT32_MAX;
295 int32_t minB = INT32_MIN;
296 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)
297 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)
298 && minB > maxB) {
299 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, maxB);
Ray Essick1831f7b2021-03-15 16:10:51 -0700300 }
301}
302
303} // namespace mediaformatshaper
304} // namespace android
305