blob: 39a5e1943f05e0f744962948d93d24e3edc6af3d [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 Essick1831f7b2021-03-15 16:10:51 -070051//
52// Caller retains ownership of and responsibility for inFormat
53//
54int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int flags) {
55 ALOGV("codecName %s inFormat %p flags x%x", codec->getName().c_str(), inFormat, flags);
56
Ray Essick970f1c82021-03-25 13:37:45 -070057 int32_t bitRateMode = -1;
58 if (AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BITRATE_MODE, &bitRateMode)
59 && bitRateMode != BITRATE_MODE_VBR) {
60 ALOGD("minquality: applies only to VBR encoding");
Ray Essick1831f7b2021-03-15 16:10:51 -070061 return 0;
62 }
63
Ray Essick970f1c82021-03-25 13:37:45 -070064 if (codec->supportedMinimumQuality() > 0) {
65 // allow the codec provided minimum quality behavior to work at it
66 ALOGD("minquality: codec claims to implement minquality=%d",
67 codec->supportedMinimumQuality());
68 return 0;
69 }
Ray Essick1831f7b2021-03-15 16:10:51 -070070
71 //
72 // apply any and all tools that we have.
73 // -- qp
74 // -- minimum bits-per-pixel
75 //
Ray Essick970f1c82021-03-25 13:37:45 -070076 if (!codec->supportsQp()) {
77 ALOGD("minquality: no qp bounding in codec %s", codec->getName().c_str());
78 } else {
Ray Essick1831f7b2021-03-15 16:10:51 -070079 // use a (configurable) QP value to force better quality
80 //
Ray Essick970f1c82021-03-25 13:37:45 -070081 int32_t qpmax = codec->targetQpMax();
82 int32_t qpmaxUser = INT32_MAX;
83 if (hasQp(inFormat)) {
84 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, &qpmaxUser);
85 ALOGD("minquality by QP: format already sets QP");
Ray Essick1831f7b2021-03-15 16:10:51 -070086 }
Ray Essick1831f7b2021-03-15 16:10:51 -070087
Ray Essick970f1c82021-03-25 13:37:45 -070088 // if the system didn't do one, use what the user provided
89 if (qpmax == 0 && qpmaxUser != INT32_MAX) {
90 qpmax = qpmaxUser;
91 }
92 // XXX: if both said something, how do we want to reconcile that
93
94 if (qpmax > 0) {
95 ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);
96 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);
97
98 // force spreading the QP across frame types, since we imposing a value
99 qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true);
100 }
Ray Essick1831f7b2021-03-15 16:10:51 -0700101 }
102
103 double bpp = codec->getBpp();
104 if (bpp > 0.0) {
105 // if we've decided to use bits-per-pixel (per second) to drive the quality
106 //
107 // (properly phrased as 'bits per second per pixel' so that it's resolution
108 // and framerate agnostic
109 //
110 // all of these is structured so that a missing value cleanly gets us to a
111 // non-faulting value of '0' for the minimum bits-per-pixel.
112 //
113 int32_t width = 0;
114 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
115 int32_t height = 0;
116 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
117 int32_t bitrateConfigured = 0;
118 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfigured);
119
120 int64_t pixels = ((int64_t)width) * height;
121 int64_t bitrateFloor = pixels * bpp;
122
123 if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
124
125 ALOGD("minquality/bitrate: target %d floor %" PRId64 "(%.3f bpp * (%d w * %d h)",
126 bitrateConfigured, bitrateFloor, codec->getBpp(), height, width);
127
128 if (bitrateConfigured < bitrateFloor) {
Ray Essick970f1c82021-03-25 13:37:45 -0700129 ALOGD("minquality/target bitrate raised from %d to %" PRId64 " bps",
Ray Essick1831f7b2021-03-15 16:10:51 -0700130 bitrateConfigured, bitrateFloor);
131 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateFloor);
132 }
133 }
134
135 return 0;
136}
137
138
139bool hasQpPerFrameType(AMediaFormat *format) {
140 int32_t value;
141
Ray Essick970f1c82021-03-25 13:37:45 -0700142 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
143 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700144 return true;
145 }
Ray Essick970f1c82021-03-25 13:37:45 -0700146 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
147 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700148 return true;
149 }
Ray Essick970f1c82021-03-25 13:37:45 -0700150 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
151 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700152 return true;
153 }
154 return false;
155}
156
157bool hasQp(AMediaFormat *format) {
158 int32_t value;
Ray Essick970f1c82021-03-25 13:37:45 -0700159 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
160 || AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
Ray Essick1831f7b2021-03-15 16:10:51 -0700161 return true;
162 }
163 return hasQpPerFrameType(format);
164}
165
166void qpSpreadPerFrameType(AMediaFormat *format, int delta,
167 int qplow, int qphigh, bool override) {
168 qpSpreadMaxPerFrameType(format, delta, qphigh, override);
169 qpSpreadMinPerFrameType(format, qplow, override);
170}
171
172void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override) {
173 ALOGV("format %p delta %d hi %d override %d", format, delta, qphigh, override);
174
175 int32_t qpOffered = 0;
176 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &qpOffered)) {
177 // propagate to otherwise unspecified frame-specific keys
178 int32_t maxI;
179 if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)) {
180 int32_t value = std::min(qphigh, qpOffered);
181 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, value);
182 }
183 int32_t maxP;
184 if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)) {
185 int32_t value = std::min(qphigh, (std::min(qpOffered, INT32_MAX-delta) + delta));
186 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, value);
187 }
188 int32_t maxB;
189 if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)) {
190 int32_t value = std::min(qphigh, (std::min(qpOffered, INT32_MAX-2*delta) + 2*delta));
191 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, value);
192 }
193 }
194}
195
196void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override) {
197 ALOGV("format %p lo %d override %d", format, qplow, override);
198
199 int32_t qpOffered = 0;
200 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &qpOffered)) {
201 int value = std::max(qplow, qpOffered);
202 // propagate to otherwise unspecified frame-specific keys
203 int32_t minI;
204 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)) {
205 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, value);
206 }
207 int32_t minP;
208 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)) {
209 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, value);
210 }
211 int32_t minB;
212 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)) {
213 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, value);
214 }
215 }
216}
217
218} // namespace mediaformatshaper
219} // namespace android
220