Home | History | Annotate | Download | only in libmediandkjni
      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