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 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 44 Status Thread::joinThread() { 45 assert(mHandle != 0); 46 void *ret; 47 pthread_join(mHandle, &ret); 48 return OK; 49 } 50 51 //static 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 59 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 83 static bool comparePTS(const AMediaCodecBufferInfo& l, const AMediaCodecBufferInfo& r) { 84 return l.presentationTimeUs < r.presentationTimeUs; 85 } 86 87 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 107 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 132 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 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 176 Status Validator::checkFramerate(const Stats&, const RunConfig&) { 177 // TODO - tricky if frames are reordered 178 return OK; 179 } 180 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 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 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