diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2020-07-14 15:46:16 -0700 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2020-07-14 15:51:38 -0700 |
commit | b75da7c41ea49c78ac9362f8837d4df9469613c6 (patch) | |
tree | f8ee4f7ad88a8dbd63868cb32734b0c2c6fdb4a8 /src/rmd_timer.c | |
parent | 48dae516a3fadc46eaf3228eb519728e2a3961e6 (diff) |
timer: implement AV-sync
This is focused on keeping --on-the-fly-encoding in sync even
over long videos. The existing code inevitably would fall into a
permanently negative pdata->avd value letting things get
increasingly out of sync and never correcting.
Before removing the vestigial negative avd "don't wait" logic
from get_frame when this permanently negative avd state was
entered, get_frame would just start sampling at an unregulated
fps.
The timer thread which drives get_frame now consults avd on every
tick, Depending on which which half is ahead, the timer will
either cause get_frame to drop frames by advancing the frameno by
more than one, or it will adjust its sleep delay in proportion to
the delta.
See comments in rmd_timer.c for more details.
Note that in testing especially with a loaded system I observed
some surprisingly large deltas where multi-second sleeps occurred
to let the sound catch back up. I expect to revisit this issue
more in the future, but would just like to get things more
correct for now.
Diffstat (limited to 'src/rmd_timer.c')
-rw-r--r-- | src/rmd_timer.c | 65 |
1 files changed, 59 insertions, 6 deletions
diff --git a/src/rmd_timer.c b/src/rmd_timer.c index ff9d068..be757a6 100644 --- a/src/rmd_timer.c +++ b/src/rmd_timer.c @@ -39,15 +39,65 @@ #include <unistd.h> -void *rmdTimer(ProgData *pdata){ - struct timespec delay; +static struct timespec us_to_timespec(unsigned int us) +{ + return (struct timespec){ + .tv_sec = us / 1000000, + .tv_nsec = (us % 1000000) * 1000, + }; +} - rmdThreadsSetName("rmdTimer"); +static void sync_streams(ProgData *pdata, unsigned int *frame_step, struct timespec *delay) { + int avd; + + pthread_mutex_lock(&pdata->avd_mutex); + avd = pdata->avd; + pthread_mutex_unlock(&pdata->avd_mutex); + + /* There are two knobs available for keeping the video synchronized with the audio: + * 1. frame_step; how many frames to encode from this frame (aka dropping frames if > 1) + * 2. delay; how long to delay the next get_frame + * + * When avd is negative, we need more video relative to audio. That can be achieved + * by either sleeping less between frames, or dropping them by having the encoder + * encode a given frame multiple times. + * + * When avd is positive, we need more audio relative to video, so less video. This + * can be achieved by sleeping more between frames. + */ + + if (avd < 0) { + int frames_behind = -avd / pdata->frametime; + + if (frames_behind > 0) { + /* more than a whole frame behind, drop frames to catch up */ + *frame_step += frames_behind; + } else { + /* less than a whole frame behind, just sleep less */ + *delay = us_to_timespec(pdata->frametime + avd); + } - delay.tv_sec = 1.f / pdata->args.fps; - delay.tv_nsec = 1000000000.f / pdata->args.fps - delay.tv_sec * 1000000000.f; + } else if (avd > 0) { + /* sleep longer */ + *delay = us_to_timespec(pdata->frametime + avd); + } + +#if 0 + printf("avd: %i frame_step: %u delay: %lu,%lu\n", + avd, *frame_step, (*delay).tv_sec, (*delay).tv_nsec); +#endif +} + +void *rmdTimer(ProgData *pdata) { + + rmdThreadsSetName("rmdTimer"); while (pdata->timer_alive) { + struct timespec delay; + unsigned int frame_step = 1; + + delay.tv_sec = 1.f / pdata->args.fps; + delay.tv_nsec = 1000000000.f / pdata->args.fps - delay.tv_sec * 1000000000.f; pthread_mutex_lock(&pdata->pause_mutex); if (pdata->pause_state_changed) { @@ -71,8 +121,11 @@ void *rmdTimer(ProgData *pdata){ } else pthread_mutex_unlock(&pdata->pause_mutex); + if (!pdata->args.nosound) + sync_streams(pdata, &frame_step, &delay); + pthread_mutex_lock(&pdata->time_mutex); - pdata->time_frameno++; + pdata->time_frameno += frame_step; pthread_mutex_unlock(&pdata->time_mutex); pthread_cond_signal(&pdata->time_cond); |