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