blob: 08e23cc1e278fbda3e400f22be76055a620999da [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
26#include <media/formatshaper/VQops.h>
27#include <media/formatshaper/CodecProperties.h>
28#include <media/formatshaper/VideoShaper.h>
29
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//
54constexpr double BITRATE_LEAVE_UNTOUCHED = 2.0;
55constexpr double BITRATE_QP_UNAVAILABLE = 1.20;
56// 10% didn't work so hot on bonito (with no QP support)
57// 15% is next.. still leaves a few short
58// 20% ? this is on the edge of what I want do do
59
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);
65
Ray Essick970f1c82021-03-25 13:37:45 -070066 int32_t bitRateMode = -1;
67 if (AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BITRATE_MODE, &bitRateMode)
68 && bitRateMode != BITRATE_MODE_VBR) {
69 ALOGD("minquality: applies only to VBR encoding");
Ray Essick1831f7b2021-03-15 16:10:51 -070070 return 0;
71 }
72
Ray Essick970f1c82021-03-25 13:37:45 -070073 if (codec->supportedMinimumQuality() > 0) {
74 // allow the codec provided minimum quality behavior to work at it
75 ALOGD("minquality: codec claims to implement minquality=%d",
76 codec->supportedMinimumQuality());
77 return 0;
78 }
Ray Essick1831f7b2021-03-15 16:10:51 -070079
80 //
Ray Essick244aaa52021-04-05 14:47:02 -070081 // consider any and all tools available
Ray Essick1831f7b2021-03-15 16:10:51 -070082 // -- qp
83 // -- minimum bits-per-pixel
84 //
Ray Essick244aaa52021-04-05 14:47:02 -070085 int64_t bitrateChosen = 0;
86 int32_t qpChosen = INT32_MAX;
87
88 int64_t bitrateConfigured = 0;
89 int32_t bitrateConfiguredTmp = 0;
90 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfiguredTmp);
91 bitrateConfigured = bitrateConfiguredTmp;
92 bitrateChosen = bitrateConfigured;
93
94 int32_t width = 0;
95 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
96 int32_t height = 0;
97 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
98 int64_t pixels = ((int64_t)width) * height;
99 double minimumBpp = codec->getBpp(width, height);
100
101 int64_t bitrateFloor = pixels * minimumBpp;
102 if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
103
104 // if we are far enough above the target bpp, leave it alone
105 //
106 ALOGV("bitrate: configured %" PRId64 " floor %" PRId64, bitrateConfigured, bitrateFloor);
107 if (bitrateConfigured >= BITRATE_LEAVE_UNTOUCHED * bitrateFloor) {
108 ALOGV("high enough bitrate: configured %" PRId64 " >= %f * floor %" PRId64,
109 bitrateConfigured, BITRATE_LEAVE_UNTOUCHED, bitrateFloor);
110 return 0;
111 }
112
113 // raise anything below the bitrate floor
114 if (bitrateConfigured < bitrateFloor) {
115 ALOGD("raise bitrate: configured %" PRId64 " to floor %" PRId64,
116 bitrateConfigured, bitrateFloor);
117 bitrateChosen = bitrateFloor;
118 }
119
120 bool qpPresent = hasQp(inFormat);
121
122 // add QP, if not already present
123 if (!qpPresent) {
Ray Essick970f1c82021-03-25 13:37:45 -0700124 int32_t qpmax = codec->targetQpMax();
Ray Essick244aaa52021-04-05 14:47:02 -0700125 if (qpmax != INT32_MAX) {
126 ALOGV("choosing qp=%d", qpmax);
127 qpChosen = qpmax;
Ray Essick970f1c82021-03-25 13:37:45 -0700128 }
Ray Essick1831f7b2021-03-15 16:10:51 -0700129 }
130
Ray Essick244aaa52021-04-05 14:47:02 -0700131 // if QP is desired but not supported, compensate with additional bits
132 if (!codec->supportsQp()) {
133 if (qpPresent || qpChosen != INT32_MAX) {
134 ALOGD("minquality: desired QP, but unsupported, boost bitrate %" PRId64 " to %" PRId64,
135 bitrateChosen, (int64_t)(bitrateChosen * BITRATE_QP_UNAVAILABLE));
136 bitrateChosen = bitrateChosen * BITRATE_QP_UNAVAILABLE;
137 qpChosen = INT32_MAX;
Ray Essick1831f7b2021-03-15 16:10:51 -0700138 }
139 }
140
Ray Essick244aaa52021-04-05 14:47:02 -0700141 // apply our chosen values
142 //
143 if (qpChosen != INT32_MAX) {
144 ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
145 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpChosen);
146
147 // force spreading the QP across frame types, since we are imposing a value
148 qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true);
149 }
150
151 if (bitrateChosen != bitrateConfigured) {
152 ALOGD("minquality/target bitrate raised from %" PRId64 " to %" PRId64 " bps",
153 bitrateConfigured, bitrateChosen);
154 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateChosen);
155 }
156
Ray Essick1831f7b2021-03-15 16:10:51 -0700157 return 0;
158}
159
160
161bool hasQpPerFrameType(AMediaFormat *format) {
162 int32_t value;
163
Ray Essick970f1c82021-03-25 13:37:45 -0700164 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
165 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700166 return true;
167 }
Ray Essick970f1c82021-03-25 13:37:45 -0700168 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
169 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700170 return true;
171 }
Ray Essick970f1c82021-03-25 13:37:45 -0700172 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
173 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700174 return true;
175 }
176 return false;
177}
178
179bool hasQp(AMediaFormat *format) {
180 int32_t value;
Ray Essick970f1c82021-03-25 13:37:45 -0700181 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
182 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700183 return true;
184 }
185 return hasQpPerFrameType(format);
186}
187
188void qpSpreadPerFrameType(AMediaFormat *format, int delta,
189 int qplow, int qphigh, bool override) {
190 qpSpreadMaxPerFrameType(format, delta, qphigh, override);
191 qpSpreadMinPerFrameType(format, qplow, override);
192}
193
194void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override) {
195 ALOGV("format %p delta %d hi %d override %d", format, delta, qphigh, override);
196
197 int32_t qpOffered = 0;
198 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &qpOffered)) {
199 // propagate to otherwise unspecified frame-specific keys
200 int32_t maxI;
201 if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)) {
202 int32_t value = std::min(qphigh, qpOffered);
203 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, value);
204 }
205 int32_t maxP;
206 if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)) {
207 int32_t value = std::min(qphigh, (std::min(qpOffered, INT32_MAX-delta) + delta));
208 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, value);
209 }
210 int32_t maxB;
211 if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)) {
212 int32_t value = std::min(qphigh, (std::min(qpOffered, INT32_MAX-2*delta) + 2*delta));
213 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, value);
214 }
215 }
216}
217
218void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override) {
219 ALOGV("format %p lo %d override %d", format, qplow, override);
220
221 int32_t qpOffered = 0;
222 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &qpOffered)) {
223 int value = std::max(qplow, qpOffered);
224 // propagate to otherwise unspecified frame-specific keys
225 int32_t minI;
226 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)) {
227 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, value);
228 }
229 int32_t minP;
230 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)) {
231 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, value);
232 }
233 int32_t minB;
234 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)) {
235 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, value);
236 }
237 }
238}
239
240} // namespace mediaformatshaper
241} // namespace android
242