Skip to content

Commit

Permalink
- implement fallback on dts based rate estimation in case
Browse files Browse the repository at this point in the history
codec is not supported (so far != aac)
  • Loading branch information
igorshevach committed Nov 7, 2023
1 parent 8e96bcc commit 820a605
Showing 1 changed file with 78 additions and 45 deletions.
123 changes: 78 additions & 45 deletions transcoder/utils/throttler.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
#include "throttler.h"
#include "json_parser.h"

// forward declarations
static void doThrottle(float maxDataRate,
bool useStatsDataRate,
double minThrottleWaitMs,
samples_stats_t *stats,
AVRational targetFramerate);

static
bool getFrameRateFromMediaInfo(
const transcode_mediaInfo_t *mediaInfo,
AVRational *result);

// api
int
throttler_init(samples_stats_t *stats,throttler_t *throttler) {
json_value_t *config = GetConfig();
Expand All @@ -32,66 +38,93 @@ throttler_process(throttler_t *throttler,transcode_session_t *transcode_session)
if(throttler && throttler->maxDataRate < INFINITY) {
const transcode_mediaInfo_t *mediaInfo = transcode_session ? transcode_session->currentMediaInfo : NULL;
if(mediaInfo && mediaInfo->codecParams){
const bool isVideo = mediaInfo->codecParams->codec_type == AVMEDIA_TYPE_VIDEO;
// TODO: currently only AAC 'fps' is supported, MP3 and opus etc.
// may have a different frame allocation schemes
const AVRational frameRate = isVideo ? mediaInfo->frameRate :
(AVRational){ .num = mediaInfo->codecParams->sample_rate ,
.den = 1024 };
AVRational frameRate = {0};

if(!throttler->useStatsDataRate) {
getFrameRateFromMediaInfo(mediaInfo,&frameRate);
}

doThrottle(throttler->maxDataRate,
throttler->useStatsDataRate,
throttler->minThrottleWaitMs,
throttler->stats,
frameRate);
}
}
}

// implementation:
static
bool
getFrameRateFromMediaInfo(
const transcode_mediaInfo_t *mediaInfo,
AVRational *result) {
if(AVMEDIA_TYPE_VIDEO == mediaInfo->codecParams->codec_type){
*result = mediaInfo->frameRate;
return true;
}
else if(AV_CODEC_ID_AAC == mediaInfo->codecParams->codec_id){
*result = (AVRational){
.num = mediaInfo->codecParams->sample_rate ,
.den = 960 // 1024
};
return true;
}

LOGGER(CATEGORY_THROTTLER,
AV_LOG_DEBUG,"%s. unsupported (av) codec %d",
__FUNCTION__,
mediaInfo->codecParams->codec_id);

return false;
}

static
int64_t calculateThrottleWindow(samples_stats_t *stats,AVRational targetFramerate){
if(targetFramerate.den == 0) {
if(stats->dtsPassed < 90000) {
return av_rescale(1000*1000,stats->dtsPassed , 90000);
}
} else if(stats->totalFrames * targetFramerate.den < targetFramerate.num) {
// during startup frame rate is not stable and usually is high
// due to system delays related to various factors. therefore.
// we must work with high pressures in small intervals of time.
// In order to not overshoot we take smaller intervals proportional to
// time passed since beginning.
return av_rescale_q(1000*1000,
(AVRational){stats->totalFrames,1},
targetFramerate);
}
return 1000 * 1000;
}

static
void
doThrottle(float maxDataRate,
bool useStatsDataRate,
double minThrottleWaitMs,
samples_stats_t *stats,
AVRational targetFramerate)
{
const double currentDataRate = targetFramerate.den == 0 ? stats->currentRate :
stats->currentFrameRate * targetFramerate.den / (float)targetFramerate.num;

if(targetFramerate.den > 0 && targetFramerate.num > 0) {
const double currentDataRate = useStatsDataRate ? stats->currentRate :
stats->currentFrameRate * targetFramerate.den / (float)targetFramerate.num;

samples_stats_log(CATEGORY_RECEIVER,AV_LOG_DEBUG,stats,"Throttle-Stats");

LOGGER(CATEGORY_THROTTLER,
AV_LOG_DEBUG,"%s. data rate current: %.3f max: %.3f",
__FUNCTION__,
currentDataRate,
maxDataRate);

if(currentDataRate > maxDataRate) {
// going to sleep for a period of time gained due to race
int throttleWindowUs;
if(stats->totalFrames * targetFramerate.den < targetFramerate.num) {
// during startup frame rate is not stable and usually is high
// due to system delays related to various factors. therefore.
// we must work with high pressures in small intervals of time.
// In order to not overshoot we take smaller intervals proportional to
// time passed since beginning.
throttleWindowUs = av_rescale_q(1000*1000,
(AVRational){stats->totalFrames,1},
targetFramerate);
} else {
throttleWindowUs = 1000 * 1000;
}
int throttleWaitUSec = (currentDataRate - maxDataRate) * throttleWindowUs;
if(throttleWaitUSec > minThrottleWaitMs * 1000) {
LOGGER(CATEGORY_THROTTLER,AV_LOG_INFO,"%s. throttling %.3f ms",
__FUNCTION__,
throttleWaitUSec / 1000.f);
stats->throttleWait += av_rescale_q(throttleWaitUSec, clockScale, standard_timebase);
av_usleep(throttleWaitUSec);
}
samples_stats_log(CATEGORY_RECEIVER,AV_LOG_DEBUG,stats,"Throttle-Stats");

LOGGER(CATEGORY_THROTTLER,
AV_LOG_DEBUG,"%s. data rate current: %.3f max: %.3f",
__FUNCTION__,
currentDataRate,
maxDataRate);

if(currentDataRate > maxDataRate) {
// going to sleep for a period of time gained due to race
int64_t throttleWindowUs = calculateThrottleWindow(stats,targetFramerate);
int throttleWaitUSec = (currentDataRate - maxDataRate) * throttleWindowUs;
if(throttleWaitUSec > minThrottleWaitMs * 1000) {
LOGGER(CATEGORY_THROTTLER,AV_LOG_INFO,"%s. throttling %.3f ms",
__FUNCTION__,
throttleWaitUSec / 1000.f);
stats->throttleWait += av_rescale_q(throttleWaitUSec, clockScale, standard_timebase);
av_usleep(throttleWaitUSec);
}
}
}
}

0 comments on commit 820a605

Please sign in to comment.