1 /*
2 * Copyright (C) 2017 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 "NativeMedia"
19 #include <log/log.h>
20
21 #include <stdlib.h>
22 #include <math.h>
23 #include <string>
24 #include <algorithm>
25 #include <iterator>
26 #include "native_media_utils.h"
27
28 namespace Utils {
29
30 const char * TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
31 const char * TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
32
33 const char * TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
34
startThread()35 Status Thread::startThread() {
36 assert(mHandle == 0);
37 if (pthread_create(&mHandle, nullptr, Thread::thread_wrapper, this) != 0) {
38 ALOGE("Failed to create thread");
39 return FAIL;
40 }
41 return OK;
42 }
43
joinThread()44 Status Thread::joinThread() {
45 assert(mHandle != 0);
46 void *ret;
47 pthread_join(mHandle, &ret);
48 return OK;
49 }
50
51 //static
thread_wrapper(void * obj)52 void* Thread::thread_wrapper(void *obj) {
53 assert(obj != nullptr);
54 Thread *t = reinterpret_cast<Thread *>(obj);
55 t->run();
56 return nullptr;
57 }
58
dynamicParamsOfKind(const char * key,std::vector<DParamRef> & paramsList) const59 int32_t RunConfig::dynamicParamsOfKind(
60 const char *key, std::vector<DParamRef>& paramsList) const {
61 paramsList.clear();
62 for (const DParamRef& d : mParams) {
63 assert(d->param() != nullptr);
64
65 if (!strncmp(key, TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME,
66 strlen(TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME))) {
67 int32_t tmp;
68 if (AMediaFormat_getInt32(d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME, &tmp)) {
69 paramsList.push_back(d);
70 }
71
72 } else if (!strncmp(key, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE,
73 strlen(TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE))) {
74 int32_t tmp;
75 if (AMediaFormat_getInt32(d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, &tmp)) {
76 paramsList.push_back(d);
77 }
78 }
79 }
80 return (int32_t)paramsList.size();
81 }
82
comparePTS(const AMediaCodecBufferInfo & l,const AMediaCodecBufferInfo & r)83 static bool comparePTS(const AMediaCodecBufferInfo& l, const AMediaCodecBufferInfo& r) {
84 return l.presentationTimeUs < r.presentationTimeUs;
85 }
86
getBitrateAverage(int32_t frameNumFrom,int32_t frameNumTo) const87 int32_t Stats::getBitrateAverage(int32_t frameNumFrom, int32_t frameNumTo) const {
88 int64_t sum = 0;
89 assert(frameNumFrom >= 0 && frameNumTo < mInfos.size());
90 for (int i = frameNumFrom; i < frameNumTo; ++i) {
91 sum += mInfos[i].size;
92 }
93 sum *= 8; // kB -> kb
94
95 auto from = mInfos.begin() + frameNumFrom;
96 auto to = mInfos.begin() + frameNumTo;
97 int64_t duration = (*std::max_element(from, to, comparePTS)).presentationTimeUs
98 - (*std::min_element(from, to, comparePTS)).presentationTimeUs;
99 if (duration <= 0) {
100 return 0;
101 }
102
103 int64_t avg = (sum * 1e6) / duration;
104 return (int32_t)avg;
105 }
106
getBitratePeak(int32_t frameNumFrom,int32_t frameNumTo,int32_t windowSize) const107 int32_t Stats::getBitratePeak(
108 int32_t frameNumFrom, int32_t frameNumTo, int32_t windowSize) const {
109 int64_t sum = 0;
110 int64_t maxSum = 0;
111 assert(frameNumFrom >= 0 && frameNumTo < mInfos.size());
112 assert(windowSize < (frameNumTo - frameNumFrom));
113
114 for (int i = frameNumFrom; i < frameNumTo; ++i) {
115 sum += mInfos[i].size;
116 if (i >= windowSize) {
117 sum -= mInfos[i - windowSize].size;
118 }
119 maxSum = sum > maxSum ? sum : maxSum;
120 }
121 maxSum *= 8; // kB -> kb
122 int64_t duration = mInfos[frameNumTo].presentationTimeUs -
123 mInfos[frameNumFrom].presentationTimeUs;
124 if (duration <= 0) {
125 return 0;
126 }
127
128 int64_t peak = (maxSum * 1e6) / duration;
129 return (int32_t)peak;
130 }
131
getSyncFrameNext(int32_t frameNumWhence) const132 int32_t Stats::getSyncFrameNext(int32_t frameNumWhence) const {
133 assert(frameNumWhence >= 0 && frameNumWhence < mInfos.size());
134 int i = frameNumWhence;
135 for (; i < (int)mInfos.size(); ++i) {
136 if (mInfos[i].flags & TBD_AMEDIACODEC_BUFFER_FLAG_KEY_FRAME) {
137 return i;
138 }
139 }
140 return -1;
141 }
142
checkOverallBitrate(const Stats & stats,const RunConfig & config)143 Status Validator::checkOverallBitrate(const Stats &stats, const RunConfig& config) {
144 // skip this check if bitrate was updated dynamically
145 ALOGV("DEBUG: checkOverallBitrate");
146 std::vector<DParamRef> tmp;
147 if (config.dynamicParamsOfKind(TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, tmp) > 0) {
148 ALOGV("DEBUG: checkOverallBitrate: dynamic bitrate enabled");
149 return OK;
150 }
151
152 int32_t bitrate = 0;
153 if (!AMediaFormat_getInt32(config.format(), AMEDIAFORMAT_KEY_BIT_RATE, &bitrate)) {
154 // should not happen
155 ALOGV("DEBUG: checkOverallBitrate: bitrate was not configured !");
156 return FAIL;
157 }
158 assert(bitrate > 0);
159
160 int32_t avgBitrate = stats.getBitrateAverage(0, config.frameCount() - 1);
161 float deviation = (avgBitrate - bitrate) * 100 / bitrate;
162 ALOGI("RESULT: Bitrate expected=%d Achieved=%d Deviation=%.2g%%",
163 bitrate, avgBitrate, deviation);
164
165 if (fabs(deviation) > kBitrateDeviationPercentMax) {
166 ALOGI("RESULT: ERROR: bitrate deviation(%.2g%%) exceeds threshold (+/-%.2g%%)",
167 deviation, kBitrateDeviationPercentMax);
168 return FAIL;
169 }
170
171 // TODO
172 // if bitrate mode was set to CBR, check for peak-bitrate deviation (+/-20%?)
173 return OK;
174 }
175
checkFramerate(const Stats &,const RunConfig &)176 Status Validator::checkFramerate(const Stats&, const RunConfig&) {
177 // TODO - tricky if frames are reordered
178 return OK;
179 }
180
checkIntraPeriod(const Stats & stats,const RunConfig & config)181 Status Validator::checkIntraPeriod(const Stats& stats, const RunConfig& config) {
182 float framerate;
183 if (!AMediaFormat_getFloat(config.format(), AMEDIAFORMAT_KEY_FRAME_RATE, &framerate)) {
184 // should not happen
185 ALOGV("DEBUG: checkIntraPeriod: framerate was not configured ! : %s",
186 AMediaFormat_toString(config.format()));
187 return OK;
188 }
189
190 int32_t intraPeriod;
191 if (!AMediaFormat_getInt32(config.format(), AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &intraPeriod)) {
192 // should not happen
193 ALOGV("DEBUG: checkIntraPeriod: I-period was not configured !");
194 return OK;
195 }
196
197 // TODO: handle special cases
198 // intraPeriod = 0 => all I
199 // intraPeriod < 0 => infinite GOP
200 if (intraPeriod <= 0) {
201 return OK;
202 }
203
204 int32_t iInterval = framerate * intraPeriod;
205
206 if (iInterval >= stats.frameCount()) {
207 ALOGV("RESULT: Intra-period %d exceeds frame-count %d ..skipping",
208 iInterval, stats.frameCount());
209 return OK;
210 }
211
212 int32_t numGopFound = 0;
213 int32_t sumGopDistance = 0;
214 int32_t lastKeyLocation = stats.getSyncFrameNext(0);
215 for (;;) {
216 int32_t nextKeyLocation = stats.getSyncFrameNext(lastKeyLocation + iInterval - kSyncFrameDeviationFramesMax);
217 if (nextKeyLocation < 0) {
218 break;
219 }
220 if (abs(nextKeyLocation - lastKeyLocation - iInterval) > kSyncFrameDeviationFramesMax) {
221 ALOGE("RESULT: ERROR: Intra period at frame %d is %d (expected %d +/-%d)",
222 lastKeyLocation, nextKeyLocation - lastKeyLocation, iInterval,
223 kSyncFrameDeviationFramesMax);
224 return FAIL;
225 }
226 ++numGopFound;
227 sumGopDistance += (nextKeyLocation - lastKeyLocation);
228 lastKeyLocation = nextKeyLocation;
229 }
230
231 if (numGopFound) {
232 ALOGI("RESULT: Intra-period: configured=%d frames (%d sec). Actual=%d frames",
233 iInterval, intraPeriod, sumGopDistance / numGopFound);
234 }
235
236 return OK;
237 }
238
checkDynamicKeyFrames(const Stats & stats,const RunConfig & config)239 Status Validator::checkDynamicKeyFrames(const Stats& stats, const RunConfig& config) {
240 ALOGV("DEBUG: checkDynamicKeyFrames");
241 std::vector<DParamRef> keyRequests;
242 if (config.dynamicParamsOfKind(TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME, keyRequests) <= 0) {
243 ALOGV("DEBUG: dynamic key-frames were not requested");
244 return OK;
245 }
246
247 std::string debugStr = "";
248 bool fail = false;
249 for (DParamRef &d : keyRequests) {
250 int32_t generatedKeyLocation = stats.getSyncFrameNext(d->frameNum());
251 if (generatedKeyLocation - d->frameNum() > kSyncFrameDeviationFramesMax) {
252 ALOGI("RESULT: ERROR: Dynamic sync-frame requested at frame=%d, got at frame=%d",
253 d->frameNum(), generatedKeyLocation);
254 fail = true;
255 }
256 char tmp[128];
257 snprintf(tmp, 128, " %d/%d,", generatedKeyLocation, d->frameNum());
258 debugStr = debugStr + std::string(tmp);
259 }
260 ALOGI("RESULT: Dynamic Key-frame locations - actual/requested :");
261 ALOGI("RESULT: %s", debugStr.c_str());
262
263 return fail ? FAIL : OK;
264 }
265
checkDynamicBitrate(const Stats & stats,const RunConfig & config)266 Status Validator::checkDynamicBitrate(const Stats& stats, const RunConfig& config) {
267 // Checking bitrate convergence between two updates makes sense if requested along with CBR
268 // check if CBR mode has been set. If not, simply pass
269 int32_t bitrateMode;
270 if (!AMediaFormat_getInt32(config.format(), TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE,
271 &bitrateMode) || bitrateMode != kBitrateModeConstant) {
272 ALOGV("DEBUG: checkDynamicBitrate: skipping since CBR not requested");
273 return OK; //skip
274 }
275
276 // check if dynamic bitrates were requested
277 std::vector<DParamRef> bitrateUpdates;
278 if (config.dynamicParamsOfKind(TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrateUpdates) <= 0) {
279 ALOGV("DEBUG: checkDynamicBitrate: dynamic bitrates not requested !");
280 return OK; //skip
281 }
282 int32_t bitrate = 0;
283 if (!AMediaFormat_getInt32(config.format(), AMEDIAFORMAT_KEY_BIT_RATE, &bitrate)) {
284 // should not happen
285 ALOGV("DEBUG: checkDynamicBitrate: bitrate was not configured !");
286 return OK; //skip
287 }
288 assert(bitrate > 0);
289
290 std::string debugStr = "";
291 int32_t lastBitrateUpdateFrameNum = 0;
292 int32_t lastBitrate = bitrate;
293 bool fail = false;
294
295 for (DParamRef &d : bitrateUpdates) {
296 int32_t updatedBitrate = 0;
297 if (!AMediaFormat_getInt32(
298 d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, &updatedBitrate)) {
299 ALOGE("BUG: expected dynamic bitrate");
300 continue;
301 }
302 assert(updatedBitrate > 0);
303
304 int32_t lastAverage = stats.getBitrateAverage(lastBitrateUpdateFrameNum, d->frameNum() - 1);
305 float deviation = (lastAverage - lastBitrate) * 100 / lastBitrate;
306
307 if (fabs(deviation) > kBitrateDeviationPercentMax) {
308 ALOGI("RESULT: ERROR: dynamic bitrate deviation(%.2g%%) exceeds threshold (+/-%.2g%%)",
309 deviation, kBitrateDeviationPercentMax);
310 fail |= true;
311 }
312
313 char tmp[128];
314 snprintf(tmp, 128, " [%d - %d] %d/%d,",
315 lastBitrateUpdateFrameNum, d->frameNum() - 1, lastAverage, lastBitrate);
316 debugStr = debugStr + std::string(tmp);
317 lastBitrate = updatedBitrate;
318 lastBitrateUpdateFrameNum = d->frameNum();
319 }
320
321 ALOGI("RESULT: Dynamic Bitrates : [from-frame - to-frame] actual/expected :");
322 ALOGI("RESULT: %s", debugStr.c_str());
323
324 return fail ? FAIL : OK;
325 }
326
327
328 }; // namespace Utils
329