diff options
author | phintuka <phintuka> | 2012-03-26 18:49:34 +0000 |
---|---|---|
committer | phintuka <phintuka> | 2012-03-26 18:49:34 +0000 |
commit | d10cacfd239dc6bf32f2af42737c18af088644a1 (patch) | |
tree | f36a23f49bf0bd6bc8d6f6b526b9844a7717043e | |
parent | 4f3f283a752fab203448575ed5ecd019282647d6 (diff) | |
download | xineliboutput-d10cacfd239dc6bf32f2af42737c18af088644a1.tar.gz xineliboutput-d10cacfd239dc6bf32f2af42737c18af088644a1.tar.bz2 |
Timestamp-based buffering for live tv
-rw-r--r-- | xine/xvdr_metronom.c | 167 | ||||
-rw-r--r-- | xine/xvdr_metronom.h | 20 |
2 files changed, 185 insertions, 2 deletions
diff --git a/xine/xvdr_metronom.c b/xine/xvdr_metronom.c index 25f52104..1fa39e1c 100644 --- a/xine/xvdr_metronom.c +++ b/xine/xvdr_metronom.c @@ -4,7 +4,7 @@ * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * - * $Id: xvdr_metronom.c,v 1.20 2012-03-23 18:47:38 phintuka Exp $ + * $Id: xvdr_metronom.c,v 1.21 2012-03-26 18:49:34 phintuka Exp $ * */ @@ -13,15 +13,69 @@ #include <xine/xine_internal.h> #include <xine/metronom.h> +#include "../tools/time_ms.h" + #define LOG_MODULENAME "[metronom ] " #define SysLogLevel iSysLogLevel #include "../logdefs.h" +#include "adjustable_scr.h" + #define XVDR_METRONOM_COMPILE #include "xvdr_metronom.h" static int warnings = 0; +static int64_t absdiff(int64_t a, int64_t b) { int64_t diff = a-b; if (diff<0) diff = -diff; return diff; } +static int64_t min64(int64_t a, int64_t b) { return a < b ? a : b; } + +static void check_buffering_done(xvdr_metronom_t *this) +{ + /* both audio and video timestamps seen ? */ + if (this->vid_pts && this->aud_pts) { + int64_t da = this->aud_pts - this->disc_pts; + int64_t dv = this->vid_pts - this->disc_pts; + int64_t d_min = min64(da, dv); + LOGMSG(" stream A-V diff %d ms", (int)(this->vid_pts - this->aud_pts)/90); + LOGMSG(" reported stream start at pts %"PRId64, this->disc_pts); + LOGMSG(" output fifo end at: audio %"PRId64" video %"PRId64, this->aud_pts, this->vid_pts); + LOGMSG(" dA %"PRId64" dV %"PRId64, da, dv); + if (d_min < 0 && d_min > -10*90000) { + LOGMSG(" *** output is late %"PRId64" ticks (%"PRId64" ms) ***", d_min, -d_min/90); + this->scr->jump(this->scr, d_min); + } + this->buffering = 0; + this->stream_start = 0; + this->scr->set_buffering(this->scr, 0); + return; + } + + if (this->first_frame_seen_time) { + int64_t ms_since_first_frame = elapsed(this->first_frame_seen_time); + + if (ms_since_first_frame > 1000) { + + this->stream_start = 0; + + /* abort buffering if no audio */ + if (this->vid_pts && !this->aud_pts) { + LOGMSG("buffering stopped: NO AUDIO ? elapsed time %d ms", (int)ms_since_first_frame); + this->buffering = 0; + this->scr->set_buffering(this->scr, 0); + return; + } + + /* abort buffering if no video */ + if (!this->vid_pts && this->aud_pts) { + LOGMSG("buffering stopped: NO VIDEO ? elapsed time %d ms", (int)ms_since_first_frame); + this->buffering = 0; + this->scr->set_buffering(this->scr, 0); + return; + } + } + } +} + static void got_video_frame(metronom_t *metronom, vo_frame_t *frame) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; @@ -46,6 +100,40 @@ static void got_video_frame(metronom_t *metronom, vo_frame_t *frame) frame->duration *= 12; /* GOP */ } + /* initial buffering */ + pthread_mutex_lock(&this->mutex); + if (this->buffering && !frame->bad_frame) { + + /* track video pts */ + if (pts) { + if (this->vid_pts && (absdiff(this->vid_pts, pts) > 5*90000)) { + LOGMSG("buffering: video jump resetted audio pts"); + this->aud_pts = 0; + } + if (this->vid_pts && this->aud_pts && (absdiff(this->vid_pts, this->aud_pts) > 5*90000)) { + LOGMSG("buffering: A-V diff resetted audio pts"); + this->aud_pts = 0; + } + if (!this->vid_pts) { + LOGMSG("got video pts, frame type %d (@%d ms)", frame->picture_coding_type, (int)elapsed(this->buffering_start_time)); + this->first_frame_seen_time = time_ms(); + } + this->vid_pts = pts; + } + + /* some logging */ + if (!pts) { + LOGMSG("got video, pts 0, buffering, frame type %d, bad_frame %d", frame->picture_coding_type, frame->bad_frame); + } + if (pts && !frame->pts) { + LOGMSG("*** ERROR: hiding video pts while buffering ***"); + } + + check_buffering_done(this); + } + + pthread_mutex_unlock(&this->mutex); + this->orig_metronom->got_video_frame (this->orig_metronom, frame); frame->pts = pts; @@ -55,6 +143,38 @@ static int64_t got_audio_samples(metronom_t *metronom, int64_t pts, int nsamples { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; + pthread_mutex_lock(&this->mutex); + + /* initial buffering */ + if (this->buffering) { + + /* track audio pts */ + if (pts) { + if (this->aud_pts && (this->aud_pts > pts || absdiff(pts, this->aud_pts) > 5*90000)) { + LOGMSG("audio jump resetted video pts"); + this->vid_pts = 0; + } + if (this->aud_pts && this->vid_pts && (absdiff(this->vid_pts, this->aud_pts) > 5*90000)) { + LOGMSG("buffering: A-V diff resetted video pts"); + this->vid_pts = 0; + } + if (!this->aud_pts) { + LOGMSG("got audio pts (@%d ms)", (int)elapsed(this->buffering_start_time)); + this->first_frame_seen_time = time_ms(); + } + this->aud_pts = pts; + } + + /* some logging */ + if (!pts && !this->aud_pts) { + LOGMSG("got audio, pts 0, buffering"); + } + + check_buffering_done(this); + } + + pthread_mutex_unlock(&this->mutex); + return this->orig_metronom->got_audio_samples (this->orig_metronom, pts, nsamples); } @@ -68,15 +188,45 @@ static int64_t got_spu_packet(metronom_t *metronom, int64_t pts) return this->orig_metronom->got_spu_packet(this->orig_metronom, pts); } +static void start_buffering(xvdr_metronom_t *this, int64_t disc_off) +{ + if (this->live_buffering && this->stream_start && disc_off) { + if (!this->buffering) { + LOGMSG("live mode buffering started (@%d ms)", (int)elapsed(this->buffering_start_time)); + + this->aud_pts = 0; + this->vid_pts = 0; + this->disc_pts = disc_off; + + this->first_frame_seen_time = 0; + + this->buffering = 1; + this->scr->set_buffering(this->scr, 1); + } + } else { + if (this->buffering) { + LOGMSG("live mode buffering aborted (@%d ms)", (int)elapsed(this->buffering_start_time)); + this->buffering = 0; + this->scr->set_buffering(this->scr, 0); + } + } +} + static void handle_audio_discontinuity(metronom_t *metronom, int type, int64_t disc_off) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; + + start_buffering(this, disc_off); + this->orig_metronom->handle_audio_discontinuity(this->orig_metronom, type, disc_off); } static void handle_video_discontinuity(metronom_t *metronom, int type, int64_t disc_off) { xvdr_metronom_t *this = (xvdr_metronom_t *)metronom; + + start_buffering(this, disc_off); + this->orig_metronom->handle_video_discontinuity(this->orig_metronom, type, disc_off); } @@ -99,6 +249,21 @@ static void set_option(metronom_t *metronom, int option, int64_t value) return; } + if (option == XVDR_METRONOM_LIVE_BUFFERING) { + pthread_mutex_lock(&this->mutex); + this->live_buffering = value; + pthread_mutex_unlock(&this->mutex); + return; + } + + if (option == XVDR_METRONOM_STREAM_START) { + pthread_mutex_lock(&this->mutex); + this->stream_start = 1; + this->buffering_start_time = time_ms(); + pthread_mutex_unlock(&this->mutex); + return; + } + if (option == XVDR_METRONOM_TRICK_SPEED) { this->trickspeed = value; return; diff --git a/xine/xvdr_metronom.h b/xine/xvdr_metronom.h index f60299c9..d2edf8a1 100644 --- a/xine/xvdr_metronom.h +++ b/xine/xvdr_metronom.h @@ -4,7 +4,7 @@ * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * - * $Id: xvdr_metronom.h,v 1.13 2012-03-25 18:41:53 phintuka Exp $ + * $Id: xvdr_metronom.h,v 1.14 2012-03-26 18:49:34 phintuka Exp $ * */ @@ -23,9 +23,14 @@ #define XVDR_METRONOM_STILL_MODE (XVDR_METRONOM_OPTION_BASE + 2) #define XVDR_METRONOM_ID (XVDR_METRONOM_OPTION_BASE + 3) +#define XVDR_METRONOM_LIVE_BUFFERING (XVDR_METRONOM_OPTION_BASE + 4) +#define XVDR_METRONOM_STREAM_START (XVDR_METRONOM_OPTION_BASE + 5) + typedef struct xvdr_metronom_s xvdr_metronom_t; +struct adjustable_scr_s; + struct xvdr_metronom_s { /* xine-lib metronom interface */ metronom_t metronom; @@ -36,6 +41,9 @@ struct xvdr_metronom_s { void (*wire) (xvdr_metronom_t *); void (*unwire) (xvdr_metronom_t *); + /* master SCR for buffering control */ + struct adjustable_scr_s *scr; + /* private data */ #ifdef XVDR_METRONOM_COMPILE @@ -49,6 +57,16 @@ struct xvdr_metronom_s { int64_t last_vo_pts; /* last displayed video frame PTS */ int wired; /* true if currently wired to stream */ + /* initial buffering in live mode */ + uint8_t buffering; /* buffering active */ + uint8_t live_buffering; /* live buffering enabled */ + uint8_t stream_start; + int64_t vid_pts; /* last seen video pts */ + int64_t aud_pts; /* last seen audio pts */ + int64_t disc_pts; /* reported discontinuity pts */ + uint64_t buffering_start_time; + uint64_t first_frame_seen_time; + pthread_mutex_t mutex; #endif }; |