blob: 6f6f33ce03eeaa54aa97a2c8e6a62308c5a0d48e [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
47//
48// Caller retains ownership of and responsibility for inFormat
49//
50int VQApply(CodecProperties *codec, vqOps_t *info, AMediaFormat* inFormat, int flags) {
51 ALOGV("codecName %s inFormat %p flags x%x", codec->getName().c_str(), inFormat, flags);
52
53 if (codec->supportedMinimumQuality() > 0) {
54 // allow the codec provided minimum quality behavior to work at it
55 ALOGD("minquality(codec): codec says %d", codec->supportedMinimumQuality());
56 return 0;
57 }
58
59 ALOGD("considering other ways to improve quality...");
60
61 //
62 // apply any and all tools that we have.
63 // -- qp
64 // -- minimum bits-per-pixel
65 //
66 if (codec->supportsQp()) {
67 // use a (configurable) QP value to force better quality
68 //
69 // XXX: augment this so that we don't lower an existing QP setting
70 // (e.g. if user set it to 40, we don't want to set it back to 45)
71 int qpmax = codec->targetQpMax();
72 if (qpmax <= 0) {
73 qpmax = 45;
74 ALOGD("use default substitute QpMax == %d", qpmax);
75 }
76 ALOGD("minquality by QP: inject %s=%d", AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);
77 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_VIDEO_QP_MAX, qpmax);
78
79 // force spreading the QP across frame types, since we imposing a value
80 qpSpreadMaxPerFrameType(inFormat, info->qpDelta, info->qpMax, /* override */ true);
81 } else {
82 ALOGD("codec %s: no qp bounding", codec->getName().c_str());
83 }
84
85 double bpp = codec->getBpp();
86 if (bpp > 0.0) {
87 // if we've decided to use bits-per-pixel (per second) to drive the quality
88 //
89 // (properly phrased as 'bits per second per pixel' so that it's resolution
90 // and framerate agnostic
91 //
92 // all of these is structured so that a missing value cleanly gets us to a
93 // non-faulting value of '0' for the minimum bits-per-pixel.
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 int32_t bitrateConfigured = 0;
100 (void) AMediaFormat_getInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, &bitrateConfigured);
101
102 int64_t pixels = ((int64_t)width) * height;
103 int64_t bitrateFloor = pixels * bpp;
104
105 if (bitrateFloor > INT32_MAX) bitrateFloor = INT32_MAX;
106
107 ALOGD("minquality/bitrate: target %d floor %" PRId64 "(%.3f bpp * (%d w * %d h)",
108 bitrateConfigured, bitrateFloor, codec->getBpp(), height, width);
109
110 if (bitrateConfigured < bitrateFloor) {
111 ALOGD("minquality/target bitrate raised from %d to %" PRId64 " to maintain quality",
112 bitrateConfigured, bitrateFloor);
113 AMediaFormat_setInt32(inFormat, AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)bitrateFloor);
114 }
115 }
116
117 return 0;
118}
119
120
121bool hasQpPerFrameType(AMediaFormat *format) {
122 int32_t value;
123
124 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &value)
125 || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &value)) {
126 return true;
127 }
128 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &value)
129 || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &value)) {
130 return true;
131 }
132 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &value)
133 || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &value)) {
134 return true;
135 }
136 return false;
137}
138
139bool hasQp(AMediaFormat *format) {
140 int32_t value;
141 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &value)
142 || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &value)) {
143 return true;
144 }
145 return hasQpPerFrameType(format);
146}
147
148void qpSpreadPerFrameType(AMediaFormat *format, int delta,
149 int qplow, int qphigh, bool override) {
150 qpSpreadMaxPerFrameType(format, delta, qphigh, override);
151 qpSpreadMinPerFrameType(format, qplow, override);
152}
153
154void qpSpreadMaxPerFrameType(AMediaFormat *format, int delta, int qphigh, bool override) {
155 ALOGV("format %p delta %d hi %d override %d", format, delta, qphigh, override);
156
157 int32_t qpOffered = 0;
158 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MAX, &qpOffered)) {
159 // propagate to otherwise unspecified frame-specific keys
160 int32_t maxI;
161 if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, &maxI)) {
162 int32_t value = std::min(qphigh, qpOffered);
163 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MAX, value);
164 }
165 int32_t maxP;
166 if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, &maxP)) {
167 int32_t value = std::min(qphigh, (std::min(qpOffered, INT32_MAX-delta) + delta));
168 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MAX, value);
169 }
170 int32_t maxB;
171 if (override || !AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, &maxB)) {
172 int32_t value = std::min(qphigh, (std::min(qpOffered, INT32_MAX-2*delta) + 2*delta));
173 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MAX, value);
174 }
175 }
176}
177
178void qpSpreadMinPerFrameType(AMediaFormat *format, int qplow, bool override) {
179 ALOGV("format %p lo %d override %d", format, qplow, override);
180
181 int32_t qpOffered = 0;
182 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_MIN, &qpOffered)) {
183 int value = std::max(qplow, qpOffered);
184 // propagate to otherwise unspecified frame-specific keys
185 int32_t minI;
186 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, &minI)) {
187 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_I_MIN, value);
188 }
189 int32_t minP;
190 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, &minP)) {
191 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_P_MIN, value);
192 }
193 int32_t minB;
194 if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, &minB)) {
195 AMediaFormat_setInt32(format, AMEDIAFORMAT_VIDEO_QP_B_MIN, value);
196 }
197 }
198}
199
200} // namespace mediaformatshaper
201} // namespace android
202