blob: 28ce43fffa49649501f0d3e16123a4f936ba03e4 [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 Essick970f1c82021-03-25 13:37:45 -070066 if (codec->supportedMinimumQuality() > 0) {
67 // allow the codec provided minimum quality behavior to work at it
68 ALOGD("minquality: codec claims to implement minquality=%d",
69 codec->supportedMinimumQuality());
70 return 0;
71 }
Ray Essick1831f7b2021-03-15 16:10:51 -070072
73 //
Ray Essick244aaa52021-04-05 14:47:02 -070074 // consider any and all tools available
Ray Essick1831f7b2021-03-15 16:10:51 -070075 // -- qp
76 // -- minimum bits-per-pixel
77 //
Ray Essick244aaa52021-04-05 14:47:02 -070078 int64_t bitrateChosen = 0;
79 int32_t qpChosen = INT32_MAX;
80
81 int64_t bitrateConfigured = 0;
82 int32_t bitrateConfiguredTmp = 0;
83 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp);
84 bitrateConfigured = bitrateConfiguredTmp;
85 bitrateChosen = bitrateConfigured;
86
87 int32_t width = 0;
88 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
89 int32_t height = 0;
90 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
91 int64_t pixels = ((int64_t)width) * height;
92 double minimumBpp = codec->getBpp(width, height);
93
94 int64_t bitrateFloor = pixels * minimumBpp;
Ray Essick614d8da2021-04-27 11:35:00 -070095 int64_t bitrateCeiling = bitrateFloor * codec->getPhaseOut();
Ray Essick244aaa52021-04-05 14:47:02 -070096 if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
Ray Essick7b4e9d92021-04-20 16:48:00 -070097 if (bitrateCeiling > INT32_MAX) bitrateCeiling = INT32_MAX;
Ray Essick244aaa52021-04-05 14:47:02 -070098
99 // if we are far enough above the target bpp, leave it alone
100 //
101 ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700102 if (bitrateConfigured >= bitrateCeiling) {
103 ALOGV("high enough bitrate: configured %" PRId64 " >= ceiling %" PRId64,
104 bitrateConfigured, bitrateCeiling);
Ray Essick244aaa52021-04-05 14:47:02 -0700105 return 0;
106 }
107
108 // raise anything below the bitrate floor
109 if (bitrateConfigured < bitrateFloor) {
110 ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64,
111 bitrateConfigured, bitrateFloor);
112 bitrateChosen = bitrateFloor;
113 }
114
Ray Essick7b4e9d92021-04-20 16:48:00 -0700115 bool qpPresent = hasQpMax(inFormat);
Ray Essick244aaa52021-04-05 14:47:02 -0700116
Ray Essick7b4e9d92021-04-20 16:48:00 -0700117 // calculate a target QP value
118 int32_t qpmax = codec->targetQpMax();
Ray Essick244aaa52021-04-05 14:47:02 -0700119 if (!qpPresent) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700120 // user didn't, so shaper wins
Ray Essick244aaa52021-04-05 14:47:02 -0700121 if (qpmax != INT32_MAX) {
122 ALOGV("choosing qp=%d", qpmax);
123 qpChosen = qpmax;
Ray Essick970f1c82021-03-25 13:37:45 -0700124 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700125 } else if (qpmax == INT32_MAX) {
126 // shaper didn't so user wins
127 qpChosen = INT32_MAX;
128 AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpChosen);
129 } else {
130 // both sides want it, choose most restrictive
131 int32_t value = INT32_MAX;
132 AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &value);
133 qpChosen = std::min(qpmax, value);
Ray Essick1831f7b2021-03-15 16:10:51 -0700134 }
135
Ray Essick244aaa52021-04-05 14:47:02 -0700136 // if QP is desired but not supported, compensate with additional bits
137 if (!codec->supportsQp()) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700138 if (qpChosen != INT32_MAX) {
Ray Essick614d8da2021-04-27 11:35:00 -0700139 int64_t boost = bitrateChosen * codec->getMissingQpBoost();
Ray Essick7b4e9d92021-04-20 16:48:00 -0700140 ALOGD("minquality: requested QP unsupported, boost bitrate %" PRId64 " by %" PRId64,
141 bitrateChosen, boost);
142 bitrateChosen = bitrateChosen + boost;
Ray Essick244aaa52021-04-05 14:47:02 -0700143 qpChosen = INT32_MAX;
Ray Essick1831f7b2021-03-15 16:10:51 -0700144 }
145 }
146
Ray Essick7b4e9d92021-04-20 16:48:00 -0700147 // limits
Ray Essick244aaa52021-04-05 14:47:02 -0700148 // apply our chosen values
149 //
150 if (qpChosen != INT32_MAX) {
151 ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
152 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
153
Ray Essick7b4e9d92021-04-20 16:48:00 -0700154 // caller (VideoShaper) handles spreading this across the subframes
Ray Essick244aaa52021-04-05 14:47:02 -0700155 }
156
157 if (bitrateChosen != bitrateConfigured) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700158 if (bitrateChosen > bitrateCeiling) {
Ray Essick614d8da2021-04-27 11:35:00 -0700159 ALOGD("minquality: bitrate increase clamped at ceiling %" PRId64, bitrateCeiling);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700160 bitrateChosen = bitrateCeiling;
161 }
Ray Essick244aaa52021-04-05 14:47:02 -0700162 ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps",
163 bitrateConfigured, bitrateChosen);
164 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen);
165 }
166
Ray Essick1831f7b2021-03-15 16:10:51 -0700167 return 0;
168}
169
170
Ray Essick7b4e9d92021-04-20 16:48:00 -0700171bool hasQpMaxPerFrameType(AMediaFormat *format) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700172 int32_t value;
173
Ray Essick970f1c82021-03-25 13:37:45 -0700174 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
175 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700176 return true;
177 }
Ray Essick970f1c82021-03-25 13:37:45 -0700178 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
179 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700180 return true;
181 }
Ray Essick970f1c82021-03-25 13:37:45 -0700182 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
183 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700184 return true;
185 }
186 return false;
187}
188
Ray Essick7b4e9d92021-04-20 16:48:00 -0700189bool hasQpMaxGlobal(AMediaFormat *format) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700190 int32_t value;
Ray Essick970f1c82021-03-25 13:37:45 -0700191 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
192 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700193 return true;
194 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700195 return false;
196}
197
198bool hasQpMax(AMediaFormat *format) {
199 if (hasQpMaxGlobal(format)) {
200 return true;
201 }
202 return hasQpMaxPerFrameType(format);
Ray Essick1831f7b2021-03-15 16:10:51 -0700203}
204
205void qpSpreadPerFrameType(AMediaFormat *format, int delta,
206 int qplow, int qphigh, bool override) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700207
Ray Essick1831f7b2021-03-15 16:10:51 -0700208 qpSpreadMinPerFrameType(format, qplow, override);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700209 qpSpreadMaxPerFrameType(format, delta, qphigh, override);
210 // make sure that min<max for all the QP fields.
211 qpVerifyMinMaxOrdering(format);
Ray Essick1831f7b2021-03-15 16:10:51 -0700212}
213
214void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override) {
215 ALOGV("format %p delta %d hi %d override %d", format, delta, qphigh, override);
216
217 int32_t qpOffered = 0;
218 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &qpOffered)) {
Ray Essick7b4e9d92021-04-20 16:48:00 -0700219 // propagate to frame-specific keys, choosing most restrictive
220 // ensure that we don't violate min<=max rules
221 {
222 int32_t maxI = INT32_MAX;
223 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI);
224 int32_t value = std::min({qpOffered, qphigh, maxI});
Ray Essick1831f7b2021-03-15 16:10:51 -0700225 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, value);
226 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700227 {
228 int32_t maxP = INT32_MAX;
229 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP);
230 int32_t value = std::min({(std::min(qpOffered, INT32_MAX-1*delta) + 1*delta),
231 qphigh, maxP});
Ray Essick1831f7b2021-03-15 16:10:51 -0700232 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, value);
233 }
Ray Essick7b4e9d92021-04-20 16:48:00 -0700234 {
235 int32_t maxB = INT32_MAX;
236 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB);
237 int32_t value = std::min({(std::min(qpOffered, INT32_MAX-2*delta) + 2*delta),
238 qphigh, maxB});
Ray Essick1831f7b2021-03-15 16:10:51 -0700239 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, value);
240 }
241 }
242}
243
244void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override) {
245 ALOGV("format %p lo %d override %d", format, qplow, override);
246
247 int32_t qpOffered = 0;
248 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &qpOffered)) {
249 int value = std::max(qplow, qpOffered);
Ray Essick7b4e9d92021-04-20 16:48:00 -0700250 // propagate to frame-specific keys, use lowest of this and existing per-frame value
251 int32_t minI = INT32_MAX;
252 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI);
253 int32_t setI = std::min(value, minI);
254 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, setI);
255
256 int32_t minP = INT32_MAX;
257 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP);
258 int32_t setP = std::min(value, minP);
259 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, setP);
260
261 int32_t minB = INT32_MAX;
262 AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB);
263 int32_t setB = std::min(value, minB);
264 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, setB);
265 }
266}
267
268// XXX whether we allow min==max, or if we'll insist that min<max
269void qpVerifyMinMaxOrdering(AMediaFormat *format) {
270 // ensure that we don't violate min<=max rules
271 int32_t maxI = INT32_MAX;
272 int32_t minI = INT32_MIN;
273 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)
274 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)
275 && minI > maxI) {
276 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, maxI);
277 }
278 int32_t maxP = INT32_MAX;
279 int32_t minP = INT32_MIN;
280 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)
281 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)
282 && minP > maxP) {
283 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, maxP);
284 }
285 int32_t maxB = INT32_MAX;
286 int32_t minB = INT32_MIN;
287 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)
288 && AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)
289 && minB > maxB) {
290 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, maxB);
Ray Essick1831f7b2021-03-15 16:10:51 -0700291 }
292}
293
294} // namespace mediaformatshaper
295} // namespace android
296