From 707862507df900ad1b38e494838a5b9d1270db34 Mon Sep 17 00:00:00 2001 From: nick black Date: Sun, 29 Dec 2019 23:18:24 -0500 Subject: [PATCH] use video pts when available #113 --- src/lib/internal.h | 12 ++++++++++++ src/lib/libav.c | 43 ++++++++++++++++++++++++++++++++----------- src/lib/render.c | 5 ----- src/view/view.cpp | 25 +++++++++++++++++++++++-- 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/src/lib/internal.h b/src/lib/internal.h index 709a575c5..b5d7878c4 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -264,6 +264,18 @@ cell* ncplane_cell_ref_yx(ncplane* n, int y, int x); #define NANOSECS_IN_SEC 1000000000 +static inline uint64_t +timespec_to_ns(const struct timespec* t){ + return t->tv_sec * NANOSECS_IN_SEC + t->tv_nsec; +} + +static inline struct timespec* +ns_to_timespec(uint64_t ns, struct timespec* ts){ + ts->tv_sec = ns / NANOSECS_IN_SEC; + ts->tv_nsec = ns % NANOSECS_IN_SEC; + return ts; +} + // no CLOCK_MONOTONIC_RAW on FreeBSD as of 12.0 :/ #ifndef CLOCK_MONOTONIC_RAW #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC diff --git a/src/lib/libav.c b/src/lib/libav.c index 8645c8e92..3cf5fb7ad 100644 --- a/src/lib/libav.c +++ b/src/lib/libav.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #endif #include "notcurses.h" @@ -364,16 +365,25 @@ int ncvisual_render(const ncvisual* ncv, int begy, int begx, int leny, int lenx) return 0; } +// iterative over the decoded frames, calling streamer() with curry for each. +// frames carry a presentation time relative to the beginning, so we get an +// initial timestamp, and check each frame against the elapsed time to sync +// up playback. int ncvisual_stream(notcurses* nc, ncvisual* ncv, int* averr, streamcb streamer, void* curry){ ncplane* n = ncv->ncp; int frame = 1; AVFrame* avf; - struct timespec start; - // FIXME should keep a start time and cumulative time; this will push things - // out on a loaded machine - while(clock_gettime(CLOCK_MONOTONIC, &start), - (avf = ncvisual_decode(ncv, averr)) ){ + struct timespec begin; // time we started + clock_gettime(CLOCK_MONOTONIC, &begin); + uint64_t nsbegin = timespec_to_ns(&begin); + struct timespec now; + bool usets = false; + while(clock_gettime(CLOCK_MONOTONIC, &now), (avf = ncvisual_decode(ncv, averr)) ){ + int64_t ts = avf->best_effort_timestamp; + if(frame == 1 && ts){ + usets = true; + } ncplane_cursor_move_yx(n, 0, 0); if(ncvisual_render(ncv, 0, 0, 0, 0)){ return -1; @@ -385,12 +395,23 @@ int ncvisual_stream(notcurses* nc, ncvisual* ncv, int* averr, } } ++frame; - uint64_t ns = avf->pkt_duration * 1000000; - struct timespec interval = { - .tv_sec = start.tv_sec + (long)(ns / 1000000000), - .tv_nsec = start.tv_nsec + (long)(ns % 1000000000), - }; - clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &interval, NULL); + struct timespec interval; + if(usets){ + double tbase = av_q2d(ncv->codecctx->time_base); + if(tbase == 0){ + tbase = avf->pkt_duration * 1000000; + } + double schedns = ts * tbase * NANOSECS_IN_SEC + nsbegin; + uint64_t nsnow = timespec_to_ns(&now); + if(nsnow < schedns){ + ns_to_timespec(schedns - nsnow, &interval); + } + nanosleep(&interval, NULL); + }else{ + uint64_t ns = avf->pkt_duration * 1000000; + ns_to_timespec(ns, &interval); + nanosleep(&interval, NULL); + } } if(*averr == AVERROR_EOF){ return 0; diff --git a/src/lib/render.c b/src/lib/render.c index 33d5dffeb..5f7068159 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -4,11 +4,6 @@ #include #include "internal.h" -static inline uint64_t -timespec_to_ns(const struct timespec* t){ - return t->tv_sec * NANOSECS_IN_SEC + t->tv_nsec; -} - static void mutex_unlock(void* vlock){ pthread_mutex_unlock(vlock); diff --git a/src/view/view.cpp b/src/view/view.cpp index 126a3ccc0..f5e63252b 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -20,14 +20,33 @@ void usage(std::ostream& o, const char* name, int exitcode){ exit(exitcode); } -// frame count is in the ncplane's user pointer +constexpr auto NANOSECS_IN_SEC = 1000000000ll; + +static inline uint64_t +timespec_to_ns(const struct timespec* ts){ + return ts->tv_sec * NANOSECS_IN_SEC + ts->tv_nsec; +} + +// frame count is in the curry. original time is in the ncplane's userptr. int perframe(struct notcurses* nc, struct ncvisual* ncv, void* vframecount){ + const struct timespec* start = static_cast(ncplane_userptr(ncvisual_plane(ncv))); struct ncplane* stdn = notcurses_stdplane(nc); int* framecount = static_cast(vframecount); ++*framecount; ncplane_set_fg(stdn, 0x80c080); ncplane_cursor_move_yx(stdn, 0, 0); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + int64_t ns = timespec_to_ns(&now) - timespec_to_ns(start); ncplane_printf(stdn, "Got frame %05d\u2026", *framecount); + const int64_t h = ns / (60 * 60 * NANOSECS_IN_SEC); + ns -= h * (60 * 60 * NANOSECS_IN_SEC); + const int64_t m = ns / (60 * NANOSECS_IN_SEC); + ns -= m * (60 * NANOSECS_IN_SEC); + const int64_t s = ns / NANOSECS_IN_SEC; + ns -= s * NANOSECS_IN_SEC; + ncplane_printf_aligned(stdn, 0, NCALIGN_RIGHT, "%02ld:%02ld:%02ld.%04ld", + h, m, s, ns / 1000000); if(ncvisual_render(ncv, 0, 0, 0, 0)){ return -1; } @@ -49,7 +68,8 @@ int main(int argc, char** argv){ } int dimy, dimx; notcurses_term_dim_yx(nc, &dimy, &dimx); - auto ncp = notcurses_newplane(nc, dimy - 1, dimx, 1, 0, nullptr); + struct timespec start; + auto ncp = notcurses_newplane(nc, dimy - 1, dimx, 1, 0, &start); if(ncp == nullptr){ notcurses_stop(nc); return EXIT_FAILURE; @@ -65,6 +85,7 @@ int main(int argc, char** argv){ std::cerr << "Error opening " << argv[i] << ": " << errbuf.data() << std::endl; return EXIT_FAILURE; } + clock_gettime(CLOCK_MONOTONIC, &start); if(ncvisual_stream(nc, ncv, &averr, perframe, &frames)){ av_make_error_string(errbuf.data(), errbuf.size(), averr); notcurses_stop(nc);