use video pts when available #113

pull/239/head
nick black 5 years ago committed by Nick Black
parent c21a04137c
commit 707862507d

@ -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

@ -3,6 +3,7 @@
#include <libavutil/error.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavutil/rational.h>
#include <libavformat/avformat.h>
#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;

@ -4,11 +4,6 @@
#include <sys/poll.h>
#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);

@ -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<struct timespec*>(ncplane_userptr(ncvisual_plane(ncv)));
struct ncplane* stdn = notcurses_stdplane(nc);
int* framecount = static_cast<int*>(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);

Loading…
Cancel
Save