summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorphintuka <phintuka>2012-03-26 18:49:34 +0000
committerphintuka <phintuka>2012-03-26 18:49:34 +0000
commitd10cacfd239dc6bf32f2af42737c18af088644a1 (patch)
treef36a23f49bf0bd6bc8d6f6b526b9844a7717043e
parent4f3f283a752fab203448575ed5ecd019282647d6 (diff)
downloadxineliboutput-d10cacfd239dc6bf32f2af42737c18af088644a1.tar.gz
xineliboutput-d10cacfd239dc6bf32f2af42737c18af088644a1.tar.bz2
Timestamp-based buffering for live tv
-rw-r--r--xine/xvdr_metronom.c167
-rw-r--r--xine/xvdr_metronom.h20
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
};