summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
Diffstat (limited to 'libhb')
-rw-r--r--libhb/decavcodec.c743
-rw-r--r--libhb/deccc608sub.c14
-rw-r--r--libhb/deccc608sub.h2
-rw-r--r--libhb/declpcm.c20
-rw-r--r--libhb/decpgssub.c2
-rw-r--r--libhb/decssasub.c47
-rw-r--r--libhb/dectx3gsub.c7
-rw-r--r--libhb/decvobsub.c46
-rw-r--r--libhb/fifo.c16
-rw-r--r--libhb/internal.h2
-rw-r--r--libhb/reader.c514
-rw-r--r--libhb/sync.c501
12 files changed, 1022 insertions, 892 deletions
diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c
index c35ec6343..eef1059df 100644
--- a/libhb/decavcodec.c
+++ b/libhb/decavcodec.c
@@ -47,7 +47,6 @@
#endif
static void compute_frame_duration( hb_work_private_t *pv );
-static void flushDelayQueue( hb_work_private_t *pv );
static int decavcodecaInit( hb_work_object_t *, hb_job_t * );
static int decavcodecaWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
static void decavcodecClose( hb_work_object_t * );
@@ -65,14 +64,29 @@ hb_work_object_t hb_decavcodeca =
.bsinfo = decavcodecaBSInfo
};
-#define HEAP_SIZE 8
-typedef struct {
- // there are nheap items on the heap indexed 1..nheap (i.e., top of
- // heap is 1). The 0th slot is unused - a marker is put there to check
- // for overwrite errs.
- int64_t h[HEAP_SIZE+1];
- int nheap;
-} pts_heap_t;
+typedef struct
+{
+ uint8_t * data;
+ int size;
+ int64_t pts;
+ int64_t dts;
+ int frametype;
+ int scr_sequence;
+ int new_chap;
+} packet_info_t;
+
+typedef struct reordered_data_s reordered_data_t;
+
+struct reordered_data_s
+{
+ int64_t sequence;
+ int64_t pts;
+ int scr_sequence;
+ int new_chap;
+};
+
+#define REORDERED_HASH_SZ (2 << 7)
+#define REORDERED_HASH_MASK (REORDERED_HASH_SZ - 1)
struct hb_work_private_s
{
@@ -87,24 +101,26 @@ struct hb_work_private_s
hb_buffer_list_t list;
double duration; // frame duration (for video)
double field_duration; // field duration (for video)
- double pts_next; // next pts we expect to generate
- int64_t chap_time; // time of next chap mark (if new_chap != 0)
+ int64_t chap_time; // time of next chap mark
+ int chap_scr;
int new_chap; // output chapter mark pending
+ int64_t last_pts;
+ double next_pts;
uint32_t nframes;
- uint32_t ndrops;
uint32_t decode_errors;
- int64_t prev_pts;
- int brokenTS; // video stream may contain packed b-frames
- hb_buffer_t* delayq[HEAP_SIZE];
- int queue_primed;
- pts_heap_t pts_heap;
- void* buffer;
+ packet_info_t packet_info;
+ uint8_t unfinished;
+ reordered_data_t * reordered_hash[REORDERED_HASH_SZ];
+ int64_t sequence;
+ int last_scr_sequence;
+ int last_chapter;
struct SwsContext * sws_context; // if we have to rescale or convert color space
+
int sws_width;
int sws_height;
int sws_pix_fmt;
- int cadence[12];
- int wait_for_keyframe;
+
+ hb_audio_t * audio;
hb_audio_resample_t * resample;
#ifdef USE_QSV
@@ -114,129 +130,13 @@ struct hb_work_private_s
int decode;
av_qsv_config config;
const char * codec_name;
-#define USE_QSV_PTS_WORKAROUND // work around out-of-order output timestamps
-#ifdef USE_QSV_PTS_WORKAROUND
- hb_list_t * pts_list;
-#endif
} qsv;
#endif
hb_list_t * list_subtitle;
};
-#ifdef USE_QSV_PTS_WORKAROUND
-// save/restore PTS if the decoder may not attach the right PTS to the frame
-static void hb_av_add_new_pts(hb_list_t *list, int64_t new_pts)
-{
- int index = 0;
- int64_t *cur_item, *new_item;
- if (list != NULL && new_pts != AV_NOPTS_VALUE)
- {
- new_item = malloc(sizeof(int64_t));
- if (new_item != NULL)
- {
- *new_item = new_pts;
- // sort chronologically
- for (index = 0; index < hb_list_count(list); index++)
- {
- cur_item = hb_list_item(list, index);
- if (cur_item != NULL)
- {
- if (*cur_item == *new_item)
- {
- // no duplicates
- free(new_item);
- return;
- }
- if (*cur_item > *new_item)
- {
- // insert here
- break;
- }
- }
- }
- hb_list_insert(list, index, new_item);
- }
- }
-}
-static int64_t hb_av_pop_next_pts(hb_list_t *list)
-{
- int64_t *item, next_pts = AV_NOPTS_VALUE;
- if (list != NULL && hb_list_count(list) > 0)
- {
- item = hb_list_item(list, 0);
- if (item != NULL)
- {
- next_pts = *item;
- hb_list_rem(list, item);
- free(item);
- }
- }
- return next_pts;
-}
-#endif
-
-static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *data, int size, int64_t pts );
-
-
-static int64_t heap_pop( pts_heap_t *heap )
-{
- int64_t result;
-
- if ( heap->nheap <= 0 )
- {
- return AV_NOPTS_VALUE;
- }
-
- // return the top of the heap then put the bottom element on top,
- // decrease the heap size by one & rebalence the heap.
- result = heap->h[1];
-
- int64_t v = heap->h[heap->nheap--];
- int parent = 1;
- int child = parent << 1;
- while ( child <= heap->nheap )
- {
- // find the smallest of the two children of parent
- if (child < heap->nheap && heap->h[child] > heap->h[child+1] )
- ++child;
-
- if (v <= heap->h[child])
- // new item is smaller than either child so it's the new parent.
- break;
-
- // smallest child is smaller than new item so move it up then
- // check its children.
- int64_t hp = heap->h[child];
- heap->h[parent] = hp;
- parent = child;
- child = parent << 1;
- }
- heap->h[parent] = v;
- return result;
-}
-
-static void heap_push( pts_heap_t *heap, int64_t v )
-{
- if ( heap->nheap < HEAP_SIZE )
- {
- ++heap->nheap;
- }
-
- // stick the new value on the bottom of the heap then bubble it
- // up to its correct spot.
- int child = heap->nheap;
- while (child > 1) {
- int parent = child >> 1;
- if (heap->h[parent] <= v)
- break;
- // move parent down
- int64_t hp = heap->h[parent];
- heap->h[child] = hp;
- child = parent;
- }
- heap->h[child] = v;
-}
+static void decodeAudio( hb_work_private_t *pv, packet_info_t * packet_info );
/***********************************************************************
* hb_work_decavcodec_init
@@ -250,7 +150,9 @@ static int decavcodecaInit( hb_work_object_t * w, hb_job_t * job )
hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
w->private_data = pv;
- pv->job = job;
+ pv->job = job;
+ pv->audio = w->audio;
+ pv->next_pts = (int64_t)AV_NOPTS_VALUE;
if (job)
pv->title = job->title;
else
@@ -418,14 +320,12 @@ static void closePrivData( hb_work_private_t ** ppv )
if ( pv )
{
- flushDelayQueue( pv );
hb_buffer_list_close(&pv->list);
if ( pv->job && pv->context && pv->context->codec )
{
- hb_log( "%s-decoder done: %u frames, %u decoder errors, %u drops",
- pv->context->codec->name, pv->nframes, pv->decode_errors,
- pv->ndrops );
+ hb_log( "%s-decoder done: %u frames, %u decoder errors",
+ pv->context->codec->name, pv->nframes, pv->decode_errors);
}
av_frame_free(&pv->frame);
if ( pv->sws_context )
@@ -460,20 +360,11 @@ static void closePrivData( hb_work_private_t ** ppv )
}
hb_audio_resample_free(pv->resample);
-#ifdef USE_QSV_PTS_WORKAROUND
- if (pv->qsv.decode && pv->qsv.pts_list != NULL)
-
+ int ii;
+ for (ii = 0; ii < REORDERED_HASH_SZ; ii++)
{
- while (hb_list_count(pv->qsv.pts_list) > 0)
- {
- int64_t *item = hb_list_item(pv->qsv.pts_list, 0);
- hb_list_rem(pv->qsv.pts_list, item);
- free(item);
- }
- hb_list_close(&pv->qsv.pts_list);
+ free(pv->reordered_hash[ii]);
}
-#endif
-
free(pv);
}
*ppv = NULL;
@@ -496,7 +387,7 @@ static void decavcodecClose( hb_work_object_t * w )
*
**********************************************************************/
static int decavcodecaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
- hb_buffer_t ** buf_out )
+ hb_buffer_t ** buf_out )
{
hb_work_private_t * pv = w->private_data;
hb_buffer_t * in = *buf_in;
@@ -519,35 +410,65 @@ static int decavcodecaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
*buf_out = NULL;
- if ( in->s.start < 0 && pv->pts_next <= 0 )
- {
- // discard buffers that start before video time 0
- return HB_WORK_OK;
- }
-
- int pos, len;
- for ( pos = 0; pos < in->size; pos += len )
- {
- uint8_t *pout;
- int pout_len;
- int64_t cur;
-
- cur = in->s.start;
+ int pos, len;
+ int64_t pts = in->s.start;
+
+ // There are a 3 scenarios that can happen here.
+ // 1. The buffer contains exactly one frame of data
+ // 2. The buffer contains multiple frames of data
+ // 3. The buffer contains a partial frame of data
+ //
+ // In scenario 2, we want to be sure that the timestamps are only
+ // applied to the first frame in the buffer. Additional frames
+ // in the buffer will have their timestamps computed in sync.
+ //
+ // In scenario 3, we need to save the ancillary buffer info of an
+ // unfinished frame so it can be applied when we receive the last
+ // buffer of that frame.
+ if (!pv->unfinished)
+ {
+ // New packet, and no previous data pending
+ pv->packet_info.scr_sequence = in->s.scr_sequence;
+ pv->packet_info.new_chap = in->s.new_chap;
+ pv->packet_info.frametype = in->s.frametype;
+ }
+ for (pos = 0; pos < in->size; pos += len)
+ {
+ uint8_t * pout;
+ int pout_len;
+ int64_t parser_pts;
if ( pv->parser != NULL )
{
- len = av_parser_parse2( pv->parser, pv->context, &pout, &pout_len,
- in->data + pos, in->size - pos, cur, cur, 0 );
- cur = pv->parser->pts;
+ len = av_parser_parse2(pv->parser, pv->context, &pout, &pout_len,
+ in->data + pos, in->size - pos,
+ pts, pts, 0 );
+ parser_pts = pv->parser->pts;
+ pts = AV_NOPTS_VALUE;
}
else
{
pout = in->data;
len = pout_len = in->size;
+ parser_pts = in->s.start;
}
if (pout != NULL && pout_len > 0)
{
- decodeAudio( w->audio, pv, pout, pout_len, cur );
+ pv->packet_info.data = pout;
+ pv->packet_info.size = pout_len;
+ pv->packet_info.pts = parser_pts;
+
+ decodeAudio(pv, &pv->packet_info);
+
+ // There could have been an unfinished packet when we entered
+ // decodeVideo that is now finished. The next packet is associated
+ // with the input buffer, so set it's chapter and scr info.
+ pv->packet_info.scr_sequence = in->s.scr_sequence;
+ pv->unfinished = 0;
+ }
+ if (len > 0 && pout_len <= 0)
+ {
+ pv->unfinished = 1;
}
}
*buf_out = hb_buffer_list_clear(&pv->list);
@@ -804,6 +725,37 @@ static int decavcodecaBSInfo( hb_work_object_t *w, const hb_buffer_t *buf,
return ret;
}
+reordered_data_t *
+reordered_hash_rem(hb_work_private_t * pv, int64_t sequence)
+{
+ reordered_data_t * reordered;
+ int slot = sequence & REORDERED_HASH_MASK;
+
+ reordered = pv->reordered_hash[slot];
+ if (reordered == NULL)
+ {
+ // This shouldn't happen...
+ // But, this happens sometimes when libav outputs exactly the same
+ // frame twice for some reason.
+ hb_deep_log(3, "decavcodec: missing sequence %"PRId64"", sequence);
+ }
+ pv->reordered_hash[slot] = NULL;
+ return reordered;
+}
+
+void
+reordered_hash_add(hb_work_private_t * pv, reordered_data_t * reordered)
+{
+ int slot = reordered->sequence & REORDERED_HASH_MASK;
+
+ // Free any unused previous entries.
+ // This can happen due to libav parser feeding partial
+ // frames data to the decoder.
+ // It can also happen due to decoding errors.
+ free(pv->reordered_hash[slot]);
+ pv->reordered_hash[slot] = reordered;
+}
+
/* -------------------------------------------------------------
* General purpose video decoder using libavcodec
*/
@@ -847,7 +799,41 @@ static hb_buffer_t *copy_frame( hb_work_private_t *pv )
h = pv->job->title->geometry.height;
}
- hb_buffer_t *out = hb_video_buffer_init( w, h );
+ reordered_data_t * reordered = NULL;
+ hb_buffer_t * out = hb_video_buffer_init( w, h );
+
+ if (pv->frame->pkt_pts != AV_NOPTS_VALUE)
+ {
+ reordered = reordered_hash_rem(pv, pv->frame->pkt_pts);
+ }
+ if (reordered != NULL)
+ {
+ out->s.scr_sequence = reordered->scr_sequence;
+ out->s.start = reordered->pts;
+ out->s.new_chap = reordered->new_chap;
+ pv->last_scr_sequence = reordered->scr_sequence;
+ pv->last_chapter = reordered->new_chap;
+ free(reordered);
+ }
+ else
+ {
+ out->s.scr_sequence = pv->last_scr_sequence;
+ out->s.start = AV_NOPTS_VALUE;
+ }
+ if (out->s.new_chap > 0 && out->s.new_chap == pv->new_chap)
+ {
+ pv->new_chap = 0;
+ }
+ // It is possible that the buffer with new_chap gets dropped
+ // by the decoder. So also check if the output buffer is after
+ // the new_chap in the timeline.
+ if (pv->new_chap > 0 &&
+ (out->s.scr_sequence > pv->chap_scr ||
+ (out->s.scr_sequence == pv->chap_scr && out->s.start > pv->chap_time)))
+ {
+ out->s.new_chap = pv->new_chap;
+ pv->new_chap = 0;
+ }
#ifdef USE_QSV
// no need to copy the frame data when decoding with QSV to opaque memory
@@ -909,21 +895,6 @@ static hb_buffer_t *copy_frame( hb_work_private_t *pv )
return out;
}
-static void flushDelayQueue( hb_work_private_t *pv )
-{
- hb_buffer_t *buf;
- int slot = pv->queue_primed ? pv->nframes & (HEAP_SIZE-1) : 0;
-
- // flush all the video packets left on our timestamp-reordering delay q
- while ((buf = pv->delayq[slot]) != NULL)
- {
- buf->s.start = heap_pop(&pv->pts_heap);
- hb_buffer_list_append(&pv->list, buf);
- pv->delayq[slot] = NULL;
- slot = ( slot + 1 ) & (HEAP_SIZE-1);
- }
-}
-
// send cc_buf to the CC decoder(s)
static void cc_send_to_decoder(hb_work_private_t *pv, hb_buffer_t *buf)
{
@@ -946,7 +917,7 @@ static void cc_send_to_decoder(hb_work_private_t *pv, hb_buffer_t *buf)
hb_fifo_push( subtitle->fifo_in, buf );
}
-static hb_buffer_t * cc_fill_buffer(hb_work_private_t *pv, uint8_t *cc, int size, int64_t pts)
+static hb_buffer_t * cc_fill_buffer(hb_work_private_t *pv, uint8_t *cc, int size)
{
int cc_count[4] = {0,};
int ii;
@@ -966,7 +937,6 @@ static hb_buffer_t * cc_fill_buffer(hb_work_private_t *pv, uint8_t *cc, int size
if (cc_count[0] > 0)
{
buf = hb_buffer_init(cc_count[0] * 2);
- buf->s.start = pts;
int jj = 0;
for (ii = 0; ii < size; ii += 3)
{
@@ -1004,20 +974,13 @@ static int get_frame_type(int type)
* ('data', 'size').
* The output of this function is stored in 'pv->list', which contains a list
* of zero or more decoded packets.
- *
- * The returned packets are guaranteed to have their timestamps in the correct
- * order, even if the original packets decoded by libavcodec have misordered
- * timestamps, due to the use of 'packed B-frames'.
- *
- * Internally the set of decoded packets may be buffered in 'pv->delayq'
- * until enough packets have been decoded so that the timestamps can be
- * correctly rewritten, if this is necessary.
*/
-static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int64_t pts, int64_t dts, uint8_t frametype )
+static int decodeFrame( hb_work_object_t *w, packet_info_t * packet_info )
{
hb_work_private_t *pv = w->private_data;
int got_picture, oldlevel = 0;
AVPacket avp;
+ reordered_data_t * reordered;
if ( global_verbosity_level <= 1 )
{
@@ -1026,10 +989,35 @@ static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int64_t pt
}
av_init_packet(&avp);
- avp.data = data;
- avp.size = size;
- avp.pts = pts;
- avp.dts = dts;
+ if (packet_info != NULL)
+ {
+ avp.data = packet_info->data;
+ avp.size = packet_info->size;
+ avp.pts = pv->sequence;
+ avp.dts = pv->sequence;
+ reordered = malloc(sizeof(*reordered));
+ if (reordered != NULL)
+ {
+ reordered->sequence = pv->sequence++;
+ reordered->pts = packet_info->pts;
+ reordered->scr_sequence = packet_info->scr_sequence;
+ reordered->new_chap = packet_info->new_chap;
+ }
+ reordered_hash_add(pv, reordered);
+
+ // libav avcodec_decode_video2() needs AVPacket flagged with
+ // AV_PKT_FLAG_KEY for some codecs. For example, sequence of
+ // PNG in a mov container.
+ if (packet_info->frametype & HB_FRAME_KEY)
+ {
+ avp.flags |= AV_PKT_FLAG_KEY;
+ }
+ }
+ else
+ {
+ avp.data = NULL;
+ avp.size = 0;
+ }
if (pv->palette != NULL)
{
@@ -1042,30 +1030,7 @@ static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int64_t pt
hb_buffer_close(&pv->palette);
}
- /*
- * libav avcodec_decode_video2() needs AVPacket flagged with AV_PKT_FLAG_KEY
- * for some codecs. For example, sequence of PNG in a mov container.
- */
- if ( frametype & HB_FRAME_KEY )
- {
- avp.flags |= AV_PKT_FLAG_KEY;
- }
-
-#ifdef USE_QSV_PTS_WORKAROUND
- /*
- * The MediaSDK decoder will return decoded frames in the correct order,
- * but *sometimes* with the incorrect timestamp assigned to them.
- *
- * We work around it by saving the input timestamps (in chronological order)
- * and restoring them after decoding.
- */
- if (pv->qsv.decode && avp.data != NULL)
- {
- hb_av_add_new_pts(pv->qsv.pts_list, avp.pts);
- }
-#endif
-
- if ( avcodec_decode_video2( pv->context, pv->frame, &got_picture, &avp ) < 0 )
+ if (avcodec_decode_video2(pv->context, pv->frame, &got_picture, &avp) < 0)
{
++pv->decode_errors;
}
@@ -1079,19 +1044,11 @@ static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int64_t pt
}
#endif
-#ifdef USE_QSV_PTS_WORKAROUND
- if (pv->qsv.decode && got_picture)
- {
- // we got a decoded frame, restore the lowest available PTS
- pv->frame->pkt_pts = hb_av_pop_next_pts(pv->qsv.pts_list);
- }
-#endif
-
if ( global_verbosity_level <= 1 )
{
av_log_set_level( oldlevel );
}
- if( got_picture )
+ if (got_picture)
{
uint16_t flags = 0;
@@ -1112,32 +1069,25 @@ static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int64_t pt
// recompute the frame/field duration, because sometimes it changes
compute_frame_duration( pv );
- double pts;
- double frame_dur = pv->duration;
+ double frame_dur = pv->duration;
if ( pv->frame->repeat_pict )
{
frame_dur += pv->frame->repeat_pict * pv->field_duration;
}
-
- // If there was no pts for this frame, assume constant frame rate
- // video & estimate the next frame time from the last & duration.
- if (pv->frame->pkt_pts == AV_NOPTS_VALUE)
+ hb_buffer_t * out = copy_frame( pv );
+ if (out->s.start == AV_NOPTS_VALUE)
{
- pts = pv->pts_next;
+ out->s.start = pv->next_pts;
}
else
{
- pts = pv->frame->pkt_pts;
- // Detect streams with broken out of order timestamps
- if (!pv->brokenTS && pv->frame->pkt_pts < pv->prev_pts)
- {
- hb_log("Broken timestamps detected. Reordering.");
- pv->brokenTS = 1;
- }
- pv->prev_pts = pv->frame->pkt_pts;
+ pv->next_pts = out->s.start;
+ }
+ if (pv->next_pts != (int64_t)AV_NOPTS_VALUE)
+ {
+ pv->next_pts += frame_dur;
+ out->s.stop = pv->next_pts;
}
-
- pv->pts_next = pts + frame_dur;
if ( pv->frame->top_field_first )
{
@@ -1209,86 +1159,30 @@ static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int64_t pt
if (pv->list_subtitle != NULL && sd->size > 0)
{
hb_buffer_t *cc_buf;
- cc_buf = cc_fill_buffer(pv, sd->data, sd->size, pts);
+ cc_buf = cc_fill_buffer(pv, sd->data, sd->size);
+ if (cc_buf != NULL)
+ {
+ cc_buf->s.start = out->s.start;
+ cc_buf->s.scr_sequence = out->s.scr_sequence;
+ }
cc_send_to_decoder(pv, cc_buf);
}
}
- hb_buffer_t *buf;
-
- // if we're doing a scan or this content couldn't have been broken
- // by Microsoft we don't worry about timestamp reordering
- if ( ! pv->job || ! pv->brokenTS )
- {
- buf = copy_frame( pv );
- av_frame_unref(pv->frame);
- buf->s.start = pts;
-
- buf->s.flags = flags;
- buf->s.frametype = frametype;
-
- if ( pv->new_chap && buf->s.start >= pv->chap_time )
- {
- buf->s.new_chap = pv->new_chap;
- pv->new_chap = 0;
- pv->chap_time = 0;
- }
- hb_buffer_list_append(&pv->list, buf);
- ++pv->nframes;
- return got_picture;
- }
-
- // XXX This following probably addresses a libavcodec bug but I don't
- // see an easy fix so we workaround it here.
- //
- // The M$ 'packed B-frames' atrocity results in decoded frames with
- // the wrong timestamp. E.g., if there are 2 b-frames the timestamps
- // we see here will be "2 3 1 5 6 4 ..." instead of "1 2 3 4 5 6".
- // The frames are actually delivered in the right order but with
- // the wrong timestamp. To get the correct timestamp attached to
- // each frame we have a delay queue (longer than the max number of
- // b-frames) & a sorting heap for the timestamps. As each frame
- // comes out of the decoder the oldest frame in the queue is removed
- // and associated with the smallest timestamp. Then the new frame is
- // added to the queue & its timestamp is pushed on the heap.
- // This does nothing if the timestamps are correct (i.e., the video
- // uses a codec that Micro$oft hasn't broken yet) but the frames
- // get timestamped correctly even when M$ has munged them.
-
- // remove the oldest picture from the frame queue (if any) &
- // give it the smallest timestamp from our heap. The queue size
- // is a power of two so we get the slot of the oldest by masking
- // the frame count & this will become the slot of the newest
- // once we've removed & processed the oldest.
- int slot = pv->nframes & (HEAP_SIZE-1);
- if ( ( buf = pv->delayq[slot] ) != NULL )
- {
- pv->queue_primed = 1;
- buf->s.start = heap_pop( &pv->pts_heap );
- if ( pv->new_chap && buf->s.start >= pv->chap_time )
- {
- buf->s.new_chap = pv->new_chap;
- pv->new_chap = 0;
- pv->chap_time = 0;
- }
- hb_buffer_list_append(&pv->list, buf);
- }
-
- // add the new frame to the delayq & push its timestamp on the heap
- buf = copy_frame( pv );
av_frame_unref(pv->frame);
- /* Store picture flags for later use by filters */
- buf->s.flags = flags;
- buf->s.frametype = frametype;
- pv->delayq[slot] = buf;
- heap_push( &pv->pts_heap, pts );
+ out->s.duration = frame_dur;
+ out->s.flags = flags;
+ out->s.frametype = frametype;
+
+ hb_buffer_list_append(&pv->list, out);
++pv->nframes;
}
return got_picture;
}
-static void decodeVideo( hb_work_object_t *w, uint8_t *data, int size, int64_t pts, int64_t dts, uint8_t frametype )
+
+static void decodeVideo( hb_work_object_t *w, hb_buffer_t * in)
{
hb_work_private_t *pv = w->private_data;
@@ -1298,37 +1192,98 @@ static void decodeVideo( hb_work_object_t *w, uint8_t *data, int size, int64_t p
* generally a frame in the parser & one or more frames in the decoder
* (depending on the bframes setting).
*/
- int pos = 0;
- do {
- uint8_t *pout;
- int pout_len, len;
- int64_t parser_pts, parser_dts;
- if ( pv->parser )
+ int pos, len;
+ int64_t pts = in->s.start;
+ int64_t dts = in->s.renderOffset;
+
+ if (in->s.new_chap > 0)
+ {
+ pv->new_chap = in->s.new_chap;
+ pv->chap_scr = in->s.scr_sequence;
+ if (in->s.start != AV_NOPTS_VALUE)
+ {
+ pv->chap_time = in->s.start;
+ }
+ else
+ {
+ pv->chap_time = pv->last_pts + 1;
+ }
+ }
+ if (in->s.start != AV_NOPTS_VALUE)
+ {
+ pv->last_pts = in->s.start;
+ }
+
+ // There are a 3 scenarios that can happen here.
+ // 1. The buffer contains exactly one frame of data
+ // 2. The buffer contains multiple frames of data
+ // 3. The buffer contains a partial frame of data
+ //
+ // In scenario 2, we want to be sure that the timestamps are only
+ // applied to the first frame in the buffer. Additional frames
+ // in the buffer will have their timestamps computed in sync.
+ //
+ // In scenario 3, we need to save the ancillary buffer info of an
+ // unfinished frame so it can be applied when we receive the last
+ // buffer of that frame.
+ if (!pv->unfinished)
+ {
+ // New packet, and no previous data pending
+ pv->packet_info.scr_sequence = in->s.scr_sequence;
+ pv->packet_info.new_chap = in->s.new_chap;
+ pv->packet_info.frametype = in->s.frametype;
+ }
+ for (pos = 0; pos < in->size; pos += len)
+ {
+ uint8_t * pout;
+ int pout_len;
+ int64_t parser_pts, parser_dts;
+
+ if (pv->parser)
{
- len = av_parser_parse2( pv->parser, pv->context, &pout, &pout_len,
- data + pos, size - pos, pts, dts, 0 );
+ len = av_parser_parse2(pv->parser, pv->context, &pout, &pout_len,
+ in->data + pos, in->size - pos,
+ pts, dts, 0 );
parser_pts = pv->parser->pts;
parser_dts = pv->parser->dts;
+ pts = AV_NOPTS_VALUE;
+ dts = AV_NOPTS_VALUE;
}
else
{
- pout = data;
- len = pout_len = size;
+ pout = in->data;
+ len = pout_len = in->size;
parser_pts = pts;
parser_dts = dts;
}
- pos += len;
- if ( pout_len > 0 )
+ if (pout != NULL && pout_len > 0)
{
- decodeFrame( w, pout, pout_len, parser_pts, parser_dts, frametype );
+ pv->packet_info.data = pout;
+ pv->packet_info.size = pout_len;
+ pv->packet_info.pts = parser_pts;
+ pv->packet_info.dts = parser_dts;
+
+ decodeFrame(w, &pv->packet_info);
+
+ // There could have been an unfinished packet when we entered
+ // decodeVideo that is now finished. The next packet is associated
+ // with the input buffer, so set it's chapter and scr info.
+ pv->packet_info.scr_sequence = in->s.scr_sequence;
+ pv->packet_info.new_chap = in->s.new_chap;
+ pv->packet_info.frametype = in->s.frametype;
+ pv->unfinished = 0;
}
- } while ( pos < size );
+ if (len > 0 && pout_len <= 0)
+ {
+ pv->unfinished = 1;
+ }
+ }
/* the stuff above flushed the parser, now flush the decoder */
- if (size <= 0)
+ if (in->s.flags & HB_BUF_FLAG_EOF)
{
- while (decodeFrame(w, NULL, 0, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0))
+ while (decodeFrame(w, NULL))
{
continue;
}
@@ -1336,13 +1291,12 @@ static void decodeVideo( hb_work_object_t *w, uint8_t *data, int size, int64_t p
if (pv->qsv.decode)
{
// flush a second time
- while (decodeFrame(w, NULL, 0, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0))
+ while (decodeFrame(w, NULL))
{
continue;
}
}
#endif
- flushDelayQueue(pv);
if (pv->list_subtitle != NULL)
cc_send_to_decoder(pv, hb_buffer_eof_init());
}
@@ -1354,8 +1308,8 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
hb_work_private_t *pv = calloc( 1, sizeof( hb_work_private_t ) );
w->private_data = pv;
- pv->wait_for_keyframe = 60;
- pv->job = job;
+ pv->job = job;
+ pv->next_pts = (int64_t)AV_NOPTS_VALUE;
if ( job )
pv->title = job->title;
else
@@ -1427,9 +1381,6 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
#ifdef USE_QSV
if (pv->qsv.decode)
{
-#ifdef USE_QSV_PTS_WORKAROUND
- pv->qsv.pts_list = hb_list_init();
-#endif
// set the QSV configuration before opening the decoder
pv->context->hwaccel_context = &pv->qsv.config;
}
@@ -1452,13 +1403,6 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
av_dict_free( &av_opts );
pv->video_codec_opened = 1;
- // avi, mkv and possibly mp4 containers can contain the M$ VFW packed
- // b-frames abortion that messes up frame ordering and timestamps.
- // XXX ffmpeg knows which streams are broken but doesn't expose the
- // info externally. We should patch ffmpeg to add a flag to the
- // codec context for this but until then we mark all ffmpeg streams
- // as suspicious.
- pv->brokenTS = 1;
}
else
{
@@ -1538,8 +1482,6 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
{
hb_work_private_t *pv = w->private_data;
hb_buffer_t *in = *buf_in;
- int64_t pts = AV_NOPTS_VALUE;
- int64_t dts = pts;
*buf_in = NULL;
*buf_out = NULL;
@@ -1557,7 +1499,7 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
{
if (pv->context != NULL && pv->context->codec != NULL)
{
- decodeVideo(w, in->data, 0, pts, dts, 0);
+ decodeVideo(w, in);
}
hb_buffer_list_append(&pv->list, in);
*buf_out = hb_buffer_list_clear(&pv->list);
@@ -1605,9 +1547,6 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
#ifdef USE_QSV
if (pv->qsv.decode)
{
-#ifdef USE_QSV_PTS_WORKAROUND
- pv->qsv.pts_list = hb_list_init();
-#endif
// set the QSV configuration before opening the decoder
pv->context->hwaccel_context = &pv->qsv.config;
}
@@ -1632,22 +1571,12 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
pv->video_codec_opened = 1;
}
- if( in->s.start >= 0 )
- {
- pts = in->s.start;
- dts = in->s.renderOffset;
- }
- if ( in->s.new_chap )
- {
- pv->new_chap = in->s.new_chap;
- pv->chap_time = pts >= 0? pts : pv->pts_next;
- }
if (in->palette != NULL)
{
pv->palette = in->palette;
in->palette = NULL;
}
- decodeVideo( w, in->data, in->size, pts, dts, in->s.frametype );
+ decodeVideo(w, in);
hb_buffer_close( &in );
*buf_out = hb_buffer_list_clear(&pv->list);
return HB_WORK_OK;
@@ -1871,7 +1800,6 @@ static void decavcodecvFlush( hb_work_object_t *w )
if (pv->context != NULL && pv->context->codec != NULL)
{
- flushDelayQueue( pv );
hb_buffer_list_close(&pv->list);
if ( pv->title->opaque_priv == NULL )
{
@@ -1890,7 +1818,6 @@ static void decavcodecvFlush( hb_work_object_t *w )
avcodec_flush_buffers( pv->context );
}
}
- pv->wait_for_keyframe = 60;
}
hb_work_object_t hb_decavcodecv =
@@ -1904,30 +1831,34 @@ hb_work_object_t hb_decavcodecv =
.info = decavcodecvInfo,
.bsinfo = decavcodecvBSInfo
};
-static void decodeAudio(hb_audio_t *audio, hb_work_private_t *pv, uint8_t *data,
- int size, int64_t pts)
+
+static void decodeAudio(hb_work_private_t *pv, packet_info_t * packet_info)
{
- AVCodecContext *context = pv->context;
- int loop_limit = 256;
- int pos = 0;
+ AVCodecContext * context = pv->context;
+ int loop_limit = 256;
+ int pos = 0;
+ int64_t pts = packet_info->pts;
- // If we are given a pts, use it; but don't lose partial ticks.
- if (pts != AV_NOPTS_VALUE && (int64_t)pv->pts_next != pts)
- pv->pts_next = pts;
- while (pos < size)
+ while (pos < packet_info->size)
{
int got_frame;
AVPacket avp;
av_init_packet(&avp);
- avp.data = data + pos;
- avp.size = size - pos;
- avp.pts = pv->pts_next;
+ avp.data = packet_info->data + pos;
+ avp.size = packet_info->size - pos;
+ avp.pts = pts;
avp.dts = AV_NOPTS_VALUE;
int len = avcodec_decode_audio4(context, pv->frame, &got_frame, &avp);
if (len < 0)
{
+ if (pts != AV_NOPTS_VALUE)
+ {
+ // Update next_pts since subsequent packets may have no
+ // pts and depend on next_pts being up to date
+ pv->next_pts = pts + pv->duration;
+ }
++pv->decode_errors;
}
if ((len < 0) || (!got_frame && !(loop_limit--)))
@@ -1943,8 +1874,9 @@ static void decodeAudio(hb_audio_t *audio, hb_work_private_t *pv, uint8_t *data,
if (got_frame)
{
- hb_buffer_t *out;
- int samplerate;
+ hb_buffer_t * out;
+ int samplerate;
+
// libavcoded doesn't yet consistently set frame->sample_rate
if (pv->frame->sample_rate != 0)
{
@@ -1954,10 +1886,9 @@ static void decodeAudio(hb_audio_t *audio, hb_work_private_t *pv, uint8_t *data,
{
samplerate = context->sample_rate;
}
- double duration = (90000. * pv->frame->nb_samples /
- (double)samplerate);
+ pv->duration = (90000. * pv->frame->nb_samples / samplerate);
- if (audio->config.out.codec & HB_ACODEC_PASS_FLAG)
+ if (pv->audio->config.out.codec & HB_ACODEC_PASS_FLAG)
{
// Note that even though we are doing passthru, we had to decode
// so that we know the stop time and the pts of the next audio
@@ -1972,10 +1903,12 @@ static void decodeAudio(hb_audio_t *audio, hb_work_private_t *pv, uint8_t *data,
av_frame_get_side_data(pv->frame,
AV_FRAME_DATA_DOWNMIX_INFO)) != NULL)
{
- double surround_mix_level, center_mix_level;
- AVDownmixInfo *downmix_info = (AVDownmixInfo*)side_data->data;
- if (audio->config.out.mixdown == HB_AMIXDOWN_DOLBY ||
- audio->config.out.mixdown == HB_AMIXDOWN_DOLBYPLII)
+ double surround_mix_level, center_mix_level;
+ AVDownmixInfo * downmix_info;
+
+ downmix_info = (AVDownmixInfo*)side_data->data;
+ if (pv->audio->config.out.mixdown == HB_AMIXDOWN_DOLBY ||
+ pv->audio->config.out.mixdown == HB_AMIXDOWN_DOLBYPLII)
{
surround_mix_level = downmix_info->surround_mix_level_ltrt;
center_mix_level = downmix_info->center_mix_level_ltrt;
@@ -2003,16 +1936,30 @@ static void decodeAudio(hb_audio_t *audio, hb_work_private_t *pv, uint8_t *data,
out = hb_audio_resample(pv->resample, pv->frame->extended_data,
pv->frame->nb_samples);
}
- av_frame_unref(pv->frame);
if (out != NULL)
{
- out->s.start = pv->pts_next;
- out->s.duration = duration;
- out->s.stop = duration + pv->pts_next;
- pv->pts_next = duration + pv->pts_next;
+ out->s.scr_sequence = packet_info->scr_sequence;
+ out->s.start = pv->frame->pkt_pts;
+ out->s.duration = pv->duration;
+ if (out->s.start == AV_NOPTS_VALUE)
+ {
+ out->s.start = pv->next_pts;
+ }
+ else
+ {
+ pv->next_pts = out->s.start;
+ }
+ if (pv->next_pts != (int64_t)AV_NOPTS_VALUE)
+ {
+ pv->next_pts += pv->duration;
+ out->s.stop = pv->next_pts;
+ }
hb_buffer_list_append(&pv->list, out);
+
+ pts = AV_NOPTS_VALUE;
}
+ av_frame_unref(pv->frame);
++pv->nframes;
}
}
diff --git a/libhb/deccc608sub.c b/libhb/deccc608sub.c
index 8ac6642da..03c544f20 100644
--- a/libhb/deccc608sub.c
+++ b/libhb/deccc608sub.c
@@ -135,6 +135,7 @@ static int general_608_init (struct s_write *wb)
hb_buffer_list_clear(&wb->list);
wb->last_pts = 0;
+ wb->last_scr_sequence = 0;
return 0;
}
@@ -944,6 +945,7 @@ static int write_cc_buffer_as_ssa(struct eia608_screen *data,
buffer->s.frametype = HB_FRAME_SUBTITLE;
buffer->s.start = ms_start;
buffer->s.stop = AV_NOPTS_VALUE;
+ buffer->s.scr_sequence = wb->data608->current_visible_scr_sequence;
sprintf((char*)buffer->data, "%d,,Default,,0,0,0,,", ++wb->line);
len = strlen((char*)buffer->data);
memcpy(buffer->data + len, wb->enc_buffer, wb->enc_buffer_used);
@@ -958,6 +960,7 @@ static int write_cc_buffer_as_ssa(struct eia608_screen *data,
buffer->s.flags = HB_BUF_FLAG_EOS;
buffer->s.start = ms_start;
buffer->s.stop = ms_start;
+ buffer->s.scr_sequence = wb->data608->current_visible_scr_sequence;
hb_buffer_list_append(&wb->list, buffer);
wb->clear_sub_needed = 0;
}
@@ -1232,10 +1235,12 @@ static void handle_command(unsigned char c1, const unsigned char c2,
case COM_RESUMECAPTIONLOADING:
wb->data608->mode=MODE_POPUP;
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
break;
case COM_RESUMETEXTDISPLAY:
wb->data608->mode=MODE_TEXT;
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
break;
case COM_ROLLUP2:
if (wb->data608->rollup_base_row + 1 < 2)
@@ -1260,6 +1265,7 @@ static void handle_command(unsigned char c1, const unsigned char c2,
wb->rollup_cr = 1;
}
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
wb->data608->mode=MODE_ROLLUP_2;
erase_memory (wb, 0);
wb->data608->cursor_column = 0;
@@ -1287,6 +1293,7 @@ static void handle_command(unsigned char c1, const unsigned char c2,
wb->rollup_cr = 1;
}
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
wb->data608->mode=MODE_ROLLUP_3;
erase_memory (wb, 0);
wb->data608->cursor_column = 0;
@@ -1314,6 +1321,7 @@ static void handle_command(unsigned char c1, const unsigned char c2,
wb->rollup_cr = 1;
}
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
wb->data608->mode = MODE_ROLLUP_4;
wb->data608->cursor_column = 0;
wb->data608->cursor_row = wb->data608->rollup_base_row;
@@ -1328,6 +1336,7 @@ static void handle_command(unsigned char c1, const unsigned char c2,
{
wb->rollup_cr = 0;
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
break;
}
if (write_cc_buffer(wb))
@@ -1335,6 +1344,7 @@ static void handle_command(unsigned char c1, const unsigned char c2,
roll_up(wb);
wb->data608->cursor_column = 0;
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
break;
case COM_ERASENONDISPLAYEDMEMORY:
erase_memory (wb,0);
@@ -1353,6 +1363,7 @@ static void handle_command(unsigned char c1, const unsigned char c2,
// the last pts is the time to remove the previously
// displayed CC from the display
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
// Write "clear" subtitle if necessary
struct eia608_screen *data;
@@ -1367,6 +1378,7 @@ static void handle_command(unsigned char c1, const unsigned char c2,
{
swap_visible_buffer(wb);
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
}
if (write_cc_buffer(wb))
wb->data608->screenfuls_counter++;
@@ -1732,6 +1744,7 @@ static void process608(const unsigned char *data, int length,
// write a buffer now
write_cc_buffer(wb);
wb->data608->current_visible_start_ms = wb->last_pts;
+ wb->data608->current_visible_scr_sequence = wb->last_scr_sequence;
}
}
}
@@ -1811,6 +1824,7 @@ static int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
}
pv->cc608->last_pts = in->s.start;
+ pv->cc608->last_scr_sequence = in->s.scr_sequence;
process608(in->data, in->size, pv->cc608);
/*
diff --git a/libhb/deccc608sub.h b/libhb/deccc608sub.h
index 6dcdf4690..202890dc0 100644
--- a/libhb/deccc608sub.h
+++ b/libhb/deccc608sub.h
@@ -70,6 +70,7 @@ struct eia608
int ssa_counter; // Number of subs currently written
int screenfuls_counter; // Number of meaningful screenfuls written
int64_t current_visible_start_ms; // At what time did the current visible buffer became so?
+ int64_t current_visible_scr_sequence; // At what SCR did the current visible buffer became so?
enum cc_modes mode;
unsigned char last_c1, last_c2;
int channel; // Currently selected channel
@@ -89,6 +90,7 @@ struct s_write {
hb_buffer_t *hb_buffer;
hb_buffer_t *hb_last_buffer;
int64_t last_pts;
+ int last_scr_sequence;
unsigned char *enc_buffer; // Generic general purpose buffer
unsigned enc_buffer_used;
unsigned enc_buffer_capacity;
diff --git a/libhb/declpcm.c b/libhb/declpcm.c
index a75c506d9..e7d009375 100644
--- a/libhb/declpcm.c
+++ b/libhb/declpcm.c
@@ -20,6 +20,7 @@ struct hb_work_private_s
uint32_t pos; /* buffer offset for next input data */
int64_t next_pts; /* pts for next output frame */
+ int scr_sequence;
/* the following is frame info for the frame we're currently accumulating */
uint64_t duration; /* frame duratin (in 90KHz ticks) */
@@ -151,7 +152,11 @@ static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in )
pv->nsamples = ( pv->duration * pv->samplerate ) / 90000;
pv->size = pv->nchunks * chunk_size;
- pv->next_pts = in->s.start;
+ if (in->s.start != AV_NOPTS_VALUE)
+ {
+ pv->next_pts = in->s.start;
+ }
+ pv->scr_sequence = in->s.scr_sequence;
}
static int declpcmInit( hb_work_object_t * w, hb_job_t * job )
@@ -160,6 +165,7 @@ static int declpcmInit( hb_work_object_t * w, hb_job_t * job )
w->private_data = pv;
pv->job = job;
+ pv->next_pts = (int64_t)AV_NOPTS_VALUE;
pv->resample =
hb_audio_resample_init(AV_SAMPLE_FMT_FLT,
w->audio->config.out.mixdown,
@@ -336,10 +342,14 @@ static hb_buffer_t *Decode( hb_work_object_t *w )
if (out != NULL)
{
- out->s.start = pv->next_pts;
- out->s.duration = pv->duration;
- pv->next_pts += pv->duration;
- out->s.stop = pv->next_pts;
+ out->s.start = pv->next_pts;
+ out->s.duration = pv->duration;
+ if (pv->next_pts != (int64_t)AV_NOPTS_VALUE)
+ {
+ pv->next_pts += pv->duration;
+ out->s.stop = pv->next_pts;
+ }
+ out->s.scr_sequence = pv->scr_sequence;
}
return out;
}
diff --git a/libhb/decpgssub.c b/libhb/decpgssub.c
index 2ddd76f45..9efb83009 100644
--- a/libhb/decpgssub.c
+++ b/libhb/decpgssub.c
@@ -410,6 +410,7 @@ static int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
out->s.start = pts;
out->s.stop = AV_NOPTS_VALUE;
out->s.renderOffset = AV_NOPTS_VALUE;
+ out->s.scr_sequence = in->s.scr_sequence;
out->f.x = x0;
out->f.y = y0;
out->f.window_width = pv->context->width;
@@ -474,6 +475,7 @@ static int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
out->s.start = pts;
out->s.stop = pts;
out->s.renderOffset = AV_NOPTS_VALUE;
+ out->s.scr_sequence = in->s.scr_sequence;
out->f.x = 0;
out->f.y = 0;
out->f.width = 0;
diff --git a/libhb/decssasub.c b/libhb/decssasub.c
index 7027926e8..98f62d4fe 100644
--- a/libhb/decssasub.c
+++ b/libhb/decssasub.c
@@ -226,7 +226,9 @@ void hb_ssa_style_init(hb_subtitle_style_t *style)
style->bg_alpha = 0xFF;
}
-static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *in_data, int in_size );
+static hb_buffer_t *
+ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, int scr_sequence,
+ uint8_t *in_data, int in_size );
/*
* Decodes a single SSA packet to one or more TEXTSUB or PICTURESUB subtitle packets.
@@ -256,37 +258,11 @@ static hb_buffer_t *ssa_decode_packet( hb_work_object_t * w, hb_buffer_t *in )
continue;
// Decode an individual SSA line
- buf = ssa_decode_line_to_mkv_ssa(w, (uint8_t *)curLine,
- strlen(curLine));
+ buf = ssa_decode_line_to_mkv_ssa(w, in->s.scr_sequence,
+ (uint8_t *)curLine, strlen(curLine));
hb_buffer_list_append(&list, buf);
}
- // For point-to-point encoding, when the start time of the stream
- // may be offset, the timestamps of the subtitles must be offset as well.
- //
- // HACK: Here we are making the assumption that, under normal circumstances,
- // the output display time of the first output packet is equal to the
- // display time of the input packet.
- //
- // During point-to-point encoding, the display time of the input
- // packet will be offset to compensate.
- //
- // Therefore we offset all of the output packets by a slip amount
- // such that first output packet's display time aligns with the
- // input packet's display time. This should give the correct time
- // when point-to-point encoding is in effect.
- buf = hb_buffer_list_head(&list);
- if (buf && buf->s.start > in->s.start)
- {
- int64_t slip = buf->s.start - in->s.start;
- while (buf != NULL)
- {
- buf->s.start -= slip;
- buf->s.stop -= slip;
- buf = buf->next;
- }
- }
-
return hb_buffer_list_clear(&list);
}
@@ -346,7 +322,9 @@ static uint8_t *find_field( uint8_t *pos, uint8_t *end, int fieldNum )
* ReadOrder,Marked, Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
* 1 2 3 4 5 6 7 8 9
*/
-static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *in_data, int in_size )
+static hb_buffer_t *
+ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, int scr_sequence,
+ uint8_t *in_data, int in_size )
{
hb_work_private_t * pv = w->private_data;
hb_buffer_t * out;
@@ -393,10 +371,11 @@ static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *i
strcat( mkvIn, "," );
strcat( mkvIn, (char *)styleToTextFields );
- out->size = strlen(mkvIn) + 1;
- out->s.frametype = HB_FRAME_SUBTITLE;
- out->s.start = in_start;
- out->s.stop = in_stop;
+ out->size = strlen(mkvIn) + 1;
+ out->s.frametype = HB_FRAME_SUBTITLE;
+ out->s.start = in_start;
+ out->s.stop = in_stop;
+ out->s.scr_sequence = scr_sequence;
if( out->size == 0 )
{
diff --git a/libhb/dectx3gsub.c b/libhb/dectx3gsub.c
index 3baec1d41..591fd48f3 100644
--- a/libhb/dectx3gsub.c
+++ b/libhb/dectx3gsub.c
@@ -216,9 +216,10 @@ static hb_buffer_t *tx3g_decode_to_ssa(hb_work_private_t *pv, hb_buffer_t *in)
out->size = dst - out->data;
// Copy metadata from the input packet to the output packet
- out->s.frametype = HB_FRAME_SUBTITLE;
- out->s.start = in->s.start;
- out->s.stop = in->s.stop;
+ out->s.frametype = HB_FRAME_SUBTITLE;
+ out->s.start = in->s.start;
+ out->s.stop = in->s.stop;
+ out->s.scr_sequence = in->s.scr_sequence;
fail:
free(styleRecords);
diff --git a/libhb/decvobsub.c b/libhb/decvobsub.c
index 85644c47d..4fb98685b 100644
--- a/libhb/decvobsub.c
+++ b/libhb/decvobsub.c
@@ -38,8 +38,10 @@ struct hb_work_private_s
int size_got;
int size_rle;
int64_t pts;
+ int current_scr_sequence;
int64_t pts_start;
int64_t pts_stop;
+ int scr_sequence;
int pts_forced;
int x;
int y;
@@ -127,7 +129,8 @@ int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
pv->size_got = in->size;
if( in->s.start >= 0 )
{
- pv->pts = in->s.start;
+ pv->pts = in->s.start;
+ pv->current_scr_sequence = in->s.scr_sequence;
}
}
}
@@ -141,7 +144,8 @@ int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
pv->size_got += in->size;
if( in->s.start >= 0 )
{
- pv->pts = in->s.start;
+ pv->pts = in->s.start;
+ pv->current_scr_sequence = in->s.scr_sequence;
}
}
else
@@ -180,7 +184,8 @@ int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
// of the current sub as the start of the next.
// This can happen if reader invalidates timestamps while
// waiting for an audio to update the SCR.
- pv->pts = pv->pts_stop;
+ pv->pts = pv->pts_stop;
+ pv->current_scr_sequence = pv->scr_sequence;
}
}
@@ -255,21 +260,26 @@ static void ParseControls( hb_work_object_t * w )
switch( command )
{
case 0x00: // 0x00 - FSTA_DSP - Forced Start Display, no arguments
- pv->pts_start = pv->pts + date * 1024;
- pv->pts_forced = 1;
+ pv->pts_start = pv->pts + date * 1024;
+ pv->scr_sequence = pv->current_scr_sequence;
+ pv->pts_forced = 1;
w->subtitle->hits++;
w->subtitle->forced_hits++;
break;
case 0x01: // 0x01 - STA_DSP - Start Display, no arguments
- pv->pts_start = pv->pts + date * 1024;
- pv->pts_forced = 0;
+ pv->pts_start = pv->pts + date * 1024;
+ pv->scr_sequence = pv->current_scr_sequence;
+ pv->pts_forced = 0;
w->subtitle->hits++;
break;
case 0x02: // 0x02 - STP_DSP - Stop Display, no arguments
- if(pv->pts_stop == AV_NOPTS_VALUE)
- pv->pts_stop = pv->pts + date * 1024;
+ if (pv->pts_stop == AV_NOPTS_VALUE)
+ {
+ pv->pts_stop = pv->pts + date * 1024;
+ pv->scr_sequence = pv->current_scr_sequence;
+ }
break;
case 0x03: // 0x03 - SET_COLOR - Set Colour indices
@@ -346,7 +356,8 @@ static void ParseControls( hb_work_object_t * w )
// fading-out
if (currAlpha < lastAlpha && pv->pts_stop == AV_NOPTS_VALUE)
{
- pv->pts_stop = pv->pts + date * 1024;
+ pv->pts_stop = pv->pts + date * 1024;
+ pv->scr_sequence = pv->current_scr_sequence;
}
i += 2;
@@ -382,7 +393,8 @@ static void ParseControls( hb_work_object_t * w )
if( pv->pts_start == AV_NOPTS_VALUE )
{
// Set pts to end of last sub if the start time is unknown.
- pv->pts_start = pv->pts;
+ pv->pts_start = pv->pts;
+ pv->scr_sequence = pv->current_scr_sequence;
}
}
@@ -535,9 +547,10 @@ static hb_buffer_t * CropSubtitle( hb_work_object_t * w, uint8_t * raw )
realheight = crop[1] - crop[0] + 1;
buf = hb_frame_buffer_init( AV_PIX_FMT_YUVA420P, realwidth, realheight );
- buf->s.frametype = HB_FRAME_SUBTITLE;
- buf->s.start = pv->pts_start;
- buf->s.stop = pv->pts_stop;
+ buf->s.frametype = HB_FRAME_SUBTITLE;
+ buf->s.start = pv->pts_start;
+ buf->s.stop = pv->pts_stop;
+ buf->s.scr_sequence = pv->scr_sequence;
buf->f.x = pv->x + crop[2];
buf->f.y = pv->y + crop[0];
@@ -606,8 +619,9 @@ static hb_buffer_t * Decode( hb_work_object_t * w )
if (w->subtitle->config.dest == PASSTHRUSUB)
{
- pv->buf->s.start = pv->pts_start;
- pv->buf->s.stop = pv->pts_stop;
+ pv->buf->s.start = pv->pts_start;
+ pv->buf->s.stop = pv->pts_stop;
+ pv->buf->s.scr_sequence = pv->scr_sequence;
buf = pv->buf;
pv->buf = NULL;
return buf;
diff --git a/libhb/fifo.c b/libhb/fifo.c
index 0795619ce..42894c7a0 100644
--- a/libhb/fifo.c
+++ b/libhb/fifo.c
@@ -393,12 +393,13 @@ hb_buffer_t * hb_buffer_init_internal( int size , int needsMapped )
int loc = b->cl.buffer_location;
memset( b, 0, sizeof(hb_buffer_t) );
- b->alloc = buffer_pool->buffer_size;
- b->size = size;
- b->data = data;
- b->s.start = AV_NOPTS_VALUE;
- b->s.stop = AV_NOPTS_VALUE;
+ b->alloc = buffer_pool->buffer_size;
+ b->size = size;
+ b->data = data;
+ b->s.start = AV_NOPTS_VALUE;
+ b->s.stop = AV_NOPTS_VALUE;
b->s.renderOffset = AV_NOPTS_VALUE;
+ b->s.scr_sequence = -1;
/* OpenCL */
b->cl.buffer = buffer;
@@ -470,9 +471,10 @@ hb_buffer_t * hb_buffer_init_internal( int size , int needsMapped )
buffers.allocated += b->alloc;
hb_unlock(buffers.lock);
}
- b->s.start = AV_NOPTS_VALUE;
- b->s.stop = AV_NOPTS_VALUE;
+ b->s.start = AV_NOPTS_VALUE;
+ b->s.stop = AV_NOPTS_VALUE;
b->s.renderOffset = AV_NOPTS_VALUE;
+ b->s.scr_sequence = -1;
#if defined(HB_BUFFER_DEBUG)
hb_lock(buffers.lock);
hb_list_add(buffers.alloc_list, b);
diff --git a/libhb/internal.h b/libhb/internal.h
index f4ad2b3c9..d1f92ca8d 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -66,6 +66,8 @@ struct hb_buffer_settings_s
int64_t stop; // stop time of frame
int64_t renderOffset; // DTS used by b-frame offsets in muxmp4
int64_t pcr;
+ int scr_sequence; // The SCR sequence that this buffer's
+ // timestamps are referenced to
int split;
uint8_t discontinuity;
int new_chap; // Video packets: if non-zero, is the index of the chapter whose boundary was crossed
diff --git a/libhb/reader.c b/libhb/reader.c
index 84de0856a..45da42ca9 100644
--- a/libhb/reader.c
+++ b/libhb/reader.c
@@ -27,17 +27,6 @@ hb_work_object_t hb_reader =
typedef struct
{
- int startup;
- double average; // average time between packets
- double filtered_average; // average time between packets
- int64_t last; // last timestamp seen on this stream
- int id; // stream id
- int is_audio; // != 0 if this is an audio stream
- int valid; // Stream timing is not valid until next scr.
-} stream_timing_t;
-
-typedef struct
-{
int id;
hb_buffer_list_t list;
} buffer_splice_list_t;
@@ -53,20 +42,18 @@ struct hb_work_private_s
hb_dvd_t * dvd;
hb_stream_t * stream;
- stream_timing_t *stream_timing;
- int64_t scr_offset;
- int sub_scr_set;
hb_psdemux_t demux;
int scr_changes;
- uint8_t st_slots; // size (in slots) of stream_timing array
- uint8_t saw_video; // != 0 if we've seen video
- uint8_t saw_audio; // != 0 if we've seen audio
+ int64_t scr_offset;
+ int64_t last_pts;
int start_found; // found pts_to_start point
int64_t pts_to_start;
int chapter_end;
+
uint64_t st_first;
- uint64_t duration;
+ int64_t duration;
+
hb_fifo_t ** fifos;
buffer_splice_list_t * splice_list;
@@ -77,14 +64,29 @@ struct hb_work_private_s
* Local prototypes
**********************************************************************/
static hb_fifo_t ** GetFifoForId( hb_work_private_t * r, int id );
-static void UpdateState( hb_work_private_t * r, int64_t start);
static hb_buffer_list_t * get_splice_list(hb_work_private_t * r, int id);
+static void UpdateState( hb_work_private_t * r );
/***********************************************************************
* reader_init
***********************************************************************
*
**********************************************************************/
+static int64_t chapter_end_pts(hb_title_t * title, int chapter_end )
+{
+ hb_chapter_t * chapter;
+ int64_t duration;
+ int ii;
+
+ duration = 0;
+ for (ii = 0; ii < chapter_end; ii++)
+ {
+ chapter = hb_list_item(title->list_chapter, ii);
+ duration += chapter->duration;
+ }
+ return duration;
+}
+
static int hb_reader_open( hb_work_private_t * r )
{
if ( r->title->type == HB_BD_TYPE )
@@ -119,6 +121,8 @@ static int hb_reader_open( hb_work_private_t * r )
else
{
hb_bd_seek_chapter(r->bd, r->job->chapter_start);
+ r->duration -= chapter_end_pts(r->job->title,
+ r->job->chapter_start - 1);
}
}
else if (r->title->type == HB_DVD_TYPE)
@@ -130,6 +134,8 @@ static int hb_reader_open( hb_work_private_t * r )
hb_dvd_close(&r->dvd);
return 1;
}
+ r->duration -= chapter_end_pts(r->job->title,
+ r->job->chapter_start - 1);
if (r->job->angle)
{
hb_dvd_set_angle(r->dvd, r->job->angle);
@@ -141,6 +147,10 @@ static int hb_reader_open( hb_work_private_t * r )
(r->job->seek_points ? (r->job->seek_points + 1.0)
: 11.0));
}
+ // libdvdnav doesn't have a seek to timestamp function.
+ // So we will have to decode frames until we find the correct time
+ // in sync.c
+ r->start_found = 1;
}
else if (r->title->type == HB_STREAM_TYPE ||
r->title->type == HB_FF_STREAM_TYPE)
@@ -161,11 +171,17 @@ static int hb_reader_open( hb_work_private_t * r )
// that we want. So we will retrieve the start time of the
// first packet we get, subtract that from pts_to_start, and
// inspect the reset of the frames in sync.
- r->start_found = 2;
r->duration -= r->job->pts_to_start;
}
- // hb_stream_seek_ts does nothing for TS streams and will return
- // an error.
+ else
+ {
+ // hb_stream_seek_ts does nothing for TS streams and will
+ // return an error.
+ //
+ // So we will decode frames until we find the correct time
+ // in sync.c
+ r->start_found = 1;
+ }
}
else
{
@@ -187,6 +203,8 @@ static int hb_reader_open( hb_work_private_t * r )
* Seek to the start chapter.
*/
hb_stream_seek_chapter(r->stream, start);
+ r->duration -= chapter_end_pts(r->job->title,
+ r->job->chapter_start - 1);
}
}
else
@@ -210,22 +228,14 @@ static int reader_init( hb_work_object_t * w, hb_job_t * job )
r->title = job->title;
r->die = job->die;
- r->st_slots = 4;
- r->stream_timing = calloc( sizeof(stream_timing_t), r->st_slots );
- r->stream_timing[0].id = r->title->video_id;
- r->stream_timing[0].average = 90000. * (double)job->vrate.den /
- job->vrate.num;
- r->stream_timing[0].filtered_average = r->stream_timing[0].average;
- r->stream_timing[0].last = -r->stream_timing[0].average;
- r->stream_timing[0].valid = 1;
- r->stream_timing[0].startup = 10;
- r->stream_timing[1].id = -1;
-
r->demux.last_scr = AV_NOPTS_VALUE;
+ r->last_pts = AV_NOPTS_VALUE;
r->chapter_end = job->chapter_end;
- if ( !job->pts_to_start )
+ if (!job->pts_to_start)
+ {
r->start_found = 1;
+ }
else
{
// The frame at the actual start time may not be an i-frame
@@ -235,7 +245,7 @@ static int reader_init( hb_work_object_t * w, hb_job_t * job )
r->pts_to_start = MAX(0, job->pts_to_start - 1000000);
}
- if (job->pts_to_stop)
+ if (job->pts_to_stop > 0)
{
r->duration = job->pts_to_start + job->pts_to_stop;
}
@@ -243,18 +253,18 @@ static int reader_init( hb_work_object_t * w, hb_job_t * job )
{
int frames = job->frame_to_start + job->frame_to_stop;
r->duration = (int64_t)frames * job->title->vrate.den * 90000 /
- job->title->vrate.num;
+ job->title->vrate.num;
}
else
{
- hb_chapter_t *chapter;
- int ii;
-
- r->duration = 0;
- for (ii = job->chapter_start; ii < job->chapter_end; ii++)
+ int count = hb_list_count(job->title->list_chapter);
+ if (count == 0 || count <= job->chapter_end)
{
- chapter = hb_list_item( job->title->list_chapter, ii - 1);
- r->duration += chapter->duration;
+ r->duration = job->title->duration;
+ }
+ else
+ {
+ r->duration = chapter_end_pts(job->title, job->chapter_end);
}
}
@@ -289,7 +299,6 @@ static int reader_init( hb_work_object_t * w, hb_job_t * job )
// with the reader. Specifically avcodec needs this.
if ( hb_reader_open( r ) )
{
- free( r->stream_timing );
free( r );
return 1;
}
@@ -320,11 +329,6 @@ static void reader_close( hb_work_object_t * w )
hb_stream_close(&r->stream);
}
- if ( r->stream_timing )
- {
- free( r->stream_timing );
- }
-
int ii;
for (ii = 0; ii < r->splice_list_size; ii++)
{
@@ -391,150 +395,6 @@ static void push_buf( hb_work_private_t *r, hb_fifo_t *fifo, hb_buffer_t *buf )
}
}
-static int is_audio( hb_work_private_t *r, int id )
-{
- int i;
- hb_audio_t *audio;
-
- for( i = 0; ( audio = hb_list_item( r->title->list_audio, i ) ); ++i )
- {
- if ( audio->id == id )
- {
- return 1;
- }
- }
- return 0;
-}
-
-static int is_subtitle( hb_work_private_t *r, int id )
-{
- int i;
- hb_subtitle_t *sub;
-
- for( i = 0; ( sub = hb_list_item( r->title->list_subtitle, i ) ); ++i )
- {
- if ( sub->id == id )
- {
- return 1;
- }
- }
- return 0;
-}
-
-// The MPEG STD (Standard Target Decoder) essentially requires that we keep
-// per-stream timing so that when there's a timing discontinuity we can
-// seemlessly join packets on either side of the discontinuity. This join
-// requires that we know the timestamp of the previous packet and the
-// average inter-packet time (since we position the new packet at the end
-// of the previous packet). The next four routines keep track of this
-// per-stream timing.
-
-// find or create the per-stream timing state for 'buf'
-
-static stream_timing_t *id_to_st( hb_work_private_t *r, const hb_buffer_t *buf, int valid )
-{
- stream_timing_t *st = r->stream_timing;
- while ( st->id != buf->s.id && st->id != -1)
- {
- ++st;
- }
- // if we haven't seen this stream add it.
- if ( st->id == -1 )
- {
- // we keep the steam timing info in an array with some power-of-two
- // number of slots. If we don't have two slots left (one for our new
- // entry plus one for the "-1" eol) we need to expand the array.
- int slot = st - r->stream_timing;
- if ( slot + 1 >= r->st_slots )
- {
- r->st_slots *= 2;
- r->stream_timing = realloc( r->stream_timing, r->st_slots *
- sizeof(*r->stream_timing) );
- st = r->stream_timing + slot;
- }
- st->id = buf->s.id;
- st->average = 30.*90.;
- st->filtered_average = st->average;
- st->startup = 10;
- st->last = -st->average;
- if ( ( st->is_audio = is_audio( r, buf->s.id ) ) != 0 )
- {
- r->saw_audio = 1;
- }
- st[1].id = -1;
- st->valid = valid;
- }
- return st;
-}
-
-// update the average inter-packet time of the stream associated with 'buf'
-// using a recursive low-pass filter with a 16 packet time constant.
-
-static void update_ipt( hb_work_private_t *r, const hb_buffer_t *buf )
-{
- stream_timing_t *st = id_to_st( r, buf, 1 );
-
- if (buf->s.renderOffset == AV_NOPTS_VALUE)
- {
- st->last += st->filtered_average;
- return;
- }
-
- double dt = buf->s.renderOffset - st->last;
-
- // Protect against spurious bad timestamps
- // timestamps should only move forward and by reasonable increments
- if ( dt > 0 && dt < 5 * 90000LL )
- {
- if( st->startup )
- {
- st->average += ( dt - st->average ) * (1./4.);
- st->startup--;
- }
- else
- {
- st->average += ( dt - st->average ) * (1./32.);
- }
- // Ignore outliers
- if (dt < 1.5 * st->average)
- {
- st->filtered_average += ( dt - st->filtered_average ) * (1./32.);
- }
- }
- st->last = buf->s.renderOffset;
- st->valid = 1;
-}
-
-// use the per-stream state associated with 'buf' to compute a new scr_offset
-// such that 'buf' will follow the previous packet of this stream separated
-// by the average packet time of the stream.
-
-static void new_scr_offset( hb_work_private_t *r, hb_buffer_t *buf )
-{
- stream_timing_t *st = id_to_st( r, buf, 1 );
- int64_t last;
- if ( !st->valid )
- {
- // !valid means we've not received any previous data
- // for this stream. There is no 'last' packet time.
- // So approximate it with video's last time.
- last = r->stream_timing[0].last;
- st->valid = 1;
- }
- else
- {
- last = st->last;
- }
- int64_t nxt = last + st->filtered_average;
- r->scr_offset = buf->s.renderOffset - nxt;
- // This log is handy when you need to debug timing problems...
- //hb_log("id %x last %"PRId64" avg %g nxt %"PRId64" renderOffset %"PRId64
- // " scr_offset %"PRId64"",
- // buf->s.id, last, st->filtered_average, nxt,
- // buf->s.renderOffset, r->scr_offset);
- r->scr_changes = r->demux.scr_changes;
-}
-
static void reader_send_eof( hb_work_private_t * r )
{
int ii;
@@ -619,10 +479,57 @@ static int reader_work( hb_work_object_t * w, hb_buffer_t ** buf_in,
while ((buf = hb_buffer_list_rem_head(&list)) != NULL)
{
- fifos = GetFifoForId( r, buf->s.id );
+ if (buf->s.start != AV_NOPTS_VALUE &&
+ r->scr_changes != r->demux.scr_changes)
+ {
+ // First valid timestamp after an SCR change. Update
+ // the per-stream scr sequence number
+ r->scr_changes = r->demux.scr_changes;
+
+ // libav tries to be too smart with timestamps and
+ // enforces unnecessary conditions. One such condition
+ // is that subtitle timestamps must be monotonically
+ // increasing. To encure this is the case, we calculate
+ // an offset upon each SCR change that will guarantee this.
+ // This is just a very rough SCR offset. A fine grained
+ // offset that maintains proper sync is calculated in sync.c
+ if (r->last_pts != AV_NOPTS_VALUE)
+ {
+ r->scr_offset = r->last_pts + 90000 - buf->s.start;
+ }
+ else
+ {
+ r->scr_offset = -buf->s.start;
+ }
+ }
+ // Set the scr sequence that this buffer's timestamps are
+ // referenced to.
+ buf->s.scr_sequence = r->scr_changes;
+ if (buf->s.start != AV_NOPTS_VALUE)
+ {
+ buf->s.start += r->scr_offset;
+ }
+ if (buf->s.renderOffset != AV_NOPTS_VALUE)
+ {
+ buf->s.renderOffset += r->scr_offset;
+ }
+ if (buf->s.start > r->last_pts)
+ {
+ r->last_pts = buf->s.start;
+ UpdateState(r);
+ }
- if (fifos && r->stream && r->start_found == 2 )
+ fifos = GetFifoForId( r, buf->s.id );
+ if (fifos && r->stream && !r->start_found)
{
+ // libav is allowing SSA subtitles to leak through that are
+ // prior to the seek point. So only make the adjustment to
+ // pts_to_start after we see the next video buffer.
+ if (buf->s.id != r->job->title->video_id)
+ {
+ hb_buffer_close(&buf);
+ continue;
+ }
// We will inspect the timestamps of each frame in sync
// to skip from this seek point to the timestamp we
// want to start at.
@@ -638,176 +545,9 @@ static int reader_work( hb_work_object_t * w, hb_buffer_t ** buf_in,
r->start_found = 1;
}
- if ( fifos && ! r->saw_video && !r->job->indepth_scan )
- {
- // The first data packet with a PTS from an audio or video stream
- // that we're decoding defines 'time zero'. Discard packets until
- // we get one.
- if (buf->s.start != AV_NOPTS_VALUE &&
- buf->s.renderOffset != AV_NOPTS_VALUE &&
- (buf->s.id == r->title->video_id ||
- is_audio( r, buf->s.id)))
- {
- // force a new scr offset computation
- r->scr_changes = r->demux.scr_changes - 1;
- // create a stream state if we don't have one so the
- // offset will get computed correctly.
- id_to_st( r, buf, 1 );
- r->saw_video = 1;
- hb_log( "reader: first SCR %"PRId64" id 0x%x DTS %"PRId64,
- r->demux.last_scr, buf->s.id, buf->s.renderOffset );
- }
- else
- {
- fifos = NULL;
- }
- }
-
- if ( r->job->indepth_scan || fifos )
- {
- if ( buf->s.renderOffset != AV_NOPTS_VALUE )
- {
- if ( r->scr_changes != r->demux.scr_changes )
- {
- // This is the first audio or video packet after an SCR
- // change. Compute a new scr offset that would make this
- // packet follow the last of this stream with the
- // correct average spacing.
- stream_timing_t *st = id_to_st( r, buf, 0 );
-
- // if this is the video stream and we don't have
- // audio yet or this is an audio stream
- // generate a new scr
- if ( st->is_audio ||
- ( st == r->stream_timing && !r->saw_audio ) )
- {
- new_scr_offset( r, buf );
- r->sub_scr_set = 0;
- }
- else
- {
- // defer the scr change until we get some
- // audio since audio has a timestamp per
- // frame but video & subtitles don't. Clear
- // the timestamps so the decoder will generate
- // them from the frame durations.
- if (is_subtitle(r, buf->s.id) &&
- buf->s.start != AV_NOPTS_VALUE)
- {
- if (!r->sub_scr_set)
- {
- // We can't generate timestamps in the
- // subtitle decoder as we can for
- // audio & video. So we need to make
- // the closest guess that we can
- // for the subtitles start time here.
- int64_t last = r->stream_timing[0].last;
- r->scr_offset = buf->s.start - last;
- r->sub_scr_set = 1;
- }
- }
- else
- {
- buf->s.start = AV_NOPTS_VALUE;
- buf->s.renderOffset = AV_NOPTS_VALUE;
- }
- }
- }
- }
- if ( buf->s.start != AV_NOPTS_VALUE )
- {
- int64_t start = buf->s.start - r->scr_offset;
-
- if (!r->start_found || r->job->indepth_scan)
- {
- UpdateState( r, start );
- }
-
- if (r->job->indepth_scan && r->job->pts_to_stop &&
- start >= r->pts_to_start + r->job->pts_to_stop)
- {
- // sync normally would terminate p-to-p
- // but sync doesn't run during indepth scan
- hb_log("reader: reached pts %"PRId64", exiting early", start);
- reader_send_eof(r);
- hb_buffer_list_close(&list);
- return HB_WORK_DONE;
- }
-
- if (!r->start_found && start >= r->pts_to_start)
- {
- // pts_to_start point found
- // Note that this code path only gets executed for
- // medai where we have not performed an initial seek
- // to get close to the start time. So the 'start' time
- // is the time since the first frame.
-
- if (r->stream)
- {
- // libav multi-threaded decoders can get into
- // a bad state if the initial data is not
- // decodable. So try to improve the chances of
- // a good start by waiting for an initial iframe
- hb_stream_set_need_keyframe(r->stream, 1);
- hb_buffer_close( &buf );
- continue;
- }
- r->start_found = 1;
- // sync.c also pays attention to job->pts_to_start
- // It eats up the 10 second slack that we build in
- // to the start time here in reader (so that video
- // decode is clean at the start time).
- // sync.c expects pts_to_start to be relative to the
- // first timestamp it sees.
- if (r->job->pts_to_start > start)
- {
- r->job->pts_to_start -= start;
- }
- else
- {
- r->job->pts_to_start = 0;
- }
- }
- // This log is handy when you need to debug timing problems
- //hb_log("id %x scr_offset %"PRId64
- // " start %"PRId64" --> %"PRId64"",
- // buf->s.id, r->scr_offset, buf->s.start,
- // buf->s.start - r->scr_offset);
- buf->s.start -= r->scr_offset;
- if ( buf->s.stop != AV_NOPTS_VALUE )
- {
- buf->s.stop -= r->scr_offset;
- }
- }
- if ( buf->s.renderOffset != AV_NOPTS_VALUE )
- {
- // This packet is referenced to the same SCR as the last.
- // Adjust timestamp to remove the System Clock Reference
- // offset then update the average inter-packet time
- // for this stream.
- buf->s.renderOffset -= r->scr_offset;
- update_ipt( r, buf );
- }
-#if 0
- // JAS: This was added to fix a rare "audio time went backward"
- // sync error I found in one sample. But it has a bad side
- // effect on DVDs, causing frequent "adding silence" sync
- // errors. So I am disabling it.
- else
- {
- update_ipt( r, buf );
- }
-#endif
- }
buf = splice_discontinuity(r, buf);
- if( fifos && buf != NULL )
+ if (fifos && buf != NULL)
{
- if ( !r->start_found )
- {
- hb_buffer_close( &buf );
- continue;
- }
-
/* if there are mutiple output fifos, send a copy of the
* buffer down all but the first (we have to not ship the
* original buffer or we'll race with the thread that's
@@ -832,12 +572,18 @@ static int reader_work( hb_work_object_t * w, hb_buffer_t ** buf_in,
return HB_WORK_OK;
}
-static void UpdateState( hb_work_private_t * r, int64_t start)
+static void UpdateState( hb_work_private_t * r )
{
hb_state_t state;
uint64_t now;
double avg;
+ if (!r->job->indepth_scan || !r->start_found)
+ {
+ // Only update state when sync.c is not handling state updates
+ return;
+ }
+
now = hb_get_date();
if( !r->st_first )
{
@@ -846,16 +592,8 @@ static void UpdateState( hb_work_private_t * r, int64_t start)
hb_get_state2(r->job->h, &state);
#define p state.param.working
- if ( !r->job->indepth_scan )
- {
- state.state = HB_STATE_SEARCHING;
- p.progress = (float) start / (float) r->job->pts_to_start;
- }
- else
- {
- state.state = HB_STATE_WORKING;
- p.progress = (float) start / (float) r->duration;
- }
+ state.state = HB_STATE_WORKING;
+ p.progress = (float) r->last_pts / (float) r->duration;
if( p.progress > 1.0 )
{
p.progress = 1.0;
@@ -866,11 +604,12 @@ static void UpdateState( hb_work_private_t * r, int64_t start)
{
int eta;
- avg = 1000.0 * (double)start / (now - r->st_first);
- if ( !r->job->indepth_scan )
- eta = ( r->job->pts_to_start - start ) / avg;
- else
- eta = ( r->duration - start ) / avg;
+ avg = 1000.0 * (double)r->last_pts / (now - r->st_first);
+ eta = (r->duration - r->last_pts) / avg;
+ if (eta < 0)
+ {
+ eta = 0;
+ }
p.hours = eta / 3600;
p.minutes = ( eta % 3600 ) / 60;
p.seconds = eta % 60;
@@ -885,6 +624,7 @@ static void UpdateState( hb_work_private_t * r, int64_t start)
hb_set_state( r->job->h, &state );
}
+
/***********************************************************************
* GetFifoForId
***********************************************************************
@@ -898,9 +638,9 @@ static hb_fifo_t ** GetFifoForId( hb_work_private_t * r, int id )
hb_subtitle_t * subtitle;
int i, n;
- if( id == title->video_id )
+ if (id == title->video_id)
{
- if (job->indepth_scan && !job->frame_to_stop)
+ if (job->indepth_scan && r->start_found)
{
/*
* Ditch the video here during the indepth scan until
@@ -919,7 +659,7 @@ static hb_fifo_t ** GetFifoForId( hb_work_private_t * r, int id )
}
}
- for( i = n = 0; i < hb_list_count( job->list_subtitle ); i++ )
+ for (i = n = 0; i < hb_list_count( job->list_subtitle ); i++)
{
subtitle = hb_list_item( job->list_subtitle, i );
if (id == subtitle->id)
@@ -928,24 +668,24 @@ static hb_fifo_t ** GetFifoForId( hb_work_private_t * r, int id )
r->fifos[n++] = subtitle->fifo_in;
}
}
- if ( n != 0 )
+ if (n != 0)
{
r->fifos[n] = NULL;
return r->fifos;
}
- if( !job->indepth_scan )
+ if (!job->indepth_scan)
{
- for( i = n = 0; i < hb_list_count( job->list_audio ); i++ )
+ for (i = n = 0; i < hb_list_count( job->list_audio ); i++)
{
audio = hb_list_item( job->list_audio, i );
- if( id == audio->id )
+ if (id == audio->id)
{
r->fifos[n++] = audio->priv.fifo_in;
}
}
- if( n != 0 )
+ if (n != 0)
{
r->fifos[n] = NULL;
return r->fifos;
diff --git a/libhb/sync.c b/libhb/sync.c
index 48464d834..6fe00c206 100644
--- a/libhb/sync.c
+++ b/libhb/sync.c
@@ -17,7 +17,7 @@
// Audio is small, buffer a lot. It helps to ensure that we see
// the initial PTS from all input streams before setting the 'zero' point.
-#define SYNC_MAX_AUDIO_QUEUE_LEN 100
+#define SYNC_MAX_AUDIO_QUEUE_LEN 200
#define SYNC_MIN_AUDIO_QUEUE_LEN 30
// We do not place a limit on the number of subtitle frames
@@ -57,12 +57,22 @@ typedef struct
typedef struct sync_common_s sync_common_t;
+#define SCR_HASH_SZ (2 << 3)
+#define SCR_HASH_MASK (SCR_HASH_SZ - 1)
+
+typedef struct
+{
+ int scr_sequence;
+ int64_t scr_offset;
+} scr_t;
+
typedef struct
{
sync_common_t * common;
// Stream I/O control
hb_list_t * in_queue;
+ hb_list_t * scr_delay_queue;
int max_len;
int min_len;
hb_cond_t * cond_full;
@@ -74,6 +84,11 @@ typedef struct
double next_pts;
double last_pts;
+ // SCR recovery
+ int last_scr_sequence;
+ double last_scr_pts;
+ double last_duration;
+
// frame statistics
int64_t first_pts;
int64_t min_frame_duration;
@@ -127,7 +142,7 @@ typedef struct
struct sync_common_s
{
- /* Audio/Video sync thread synchronization */
+ // Audio/Video sync thread synchronization
hb_job_t * job;
hb_lock_t * mutex;
int stream_count;
@@ -135,6 +150,10 @@ struct sync_common_s
int found_first_pts;
int done;
+ // SCR adjustments
+ scr_t scr[SCR_HASH_SZ];
+ int first_scr;
+
// point-to-point support
int start_found;
int64_t start_pts;
@@ -163,8 +182,10 @@ struct hb_work_private_s
static void UpdateState( sync_common_t * common, int frame_count );
static void UpdateSearchState( sync_common_t * common, int64_t start,
int frame_count );
+static int UpdateSCR( sync_stream_t * stream, hb_buffer_t * buf );
static hb_buffer_t * FilterAudioFrame( sync_stream_t * stream,
hb_buffer_t *buf );
+static void SortedQueueBuffer( sync_stream_t * stream, hb_buffer_t * buf );
static hb_buffer_t * sanitizeSubtitle(sync_stream_t * stream,
hb_buffer_t * sub);
@@ -223,16 +244,12 @@ static void signalBuffer( sync_stream_t * stream )
}
}
-static void allSlip( sync_common_t * common, int64_t delta )
+static void scrSlip( sync_common_t * common, int64_t delta )
{
int ii;
- for (ii = 0; ii < common->stream_count; ii++)
+ for (ii = 0; ii < SCR_HASH_SZ; ii++)
{
- common->streams[ii].pts_slip += delta;
- if (common->streams[ii].next_pts != (int64_t)AV_NOPTS_VALUE)
- {
- common->streams[ii].next_pts -= delta;
- }
+ common->scr[ii].scr_offset += delta;
}
}
@@ -240,27 +257,105 @@ static void shiftTS( sync_common_t * common, int64_t delta )
{
int ii, jj;
- allSlip(common, delta);
+ scrSlip(common, delta);
for (ii = 0; ii < common->stream_count; ii++)
{
+ hb_buffer_t * buf = NULL;
sync_stream_t * stream = &common->streams[ii];
- int count = hb_list_count(stream->in_queue);
+ int count = hb_list_count(stream->in_queue);
+
for (jj = 0; jj < count; jj++)
{
- hb_buffer_t * buf = hb_list_item(stream->in_queue, jj);
- buf->s.start -= delta;
+ buf = hb_list_item(stream->in_queue, jj);
+ if (buf->s.start != AV_NOPTS_VALUE)
+ {
+ buf->s.start -= delta;
+ }
if (buf->s.stop != AV_NOPTS_VALUE)
{
buf->s.stop -= delta;
}
}
+ if (buf != NULL && buf->s.start != AV_NOPTS_VALUE)
+ {
+ stream->last_scr_pts = buf->s.start + buf->s.duration;
+ }
+ else
+ {
+ stream->last_scr_pts = (int64_t)AV_NOPTS_VALUE;
+ }
+ }
+}
+
+static void computeInitialTS( sync_common_t * common,
+ sync_stream_t * first_stream )
+{
+ int ii, count;
+ hb_buffer_t * prev;
+
+ // Process first_stream first since it has the initial PTS
+ prev = NULL;
+ count = hb_list_count(first_stream->in_queue);
+ for (ii = 0; ii < count; ii++)
+ {
+ hb_buffer_t * buf = hb_list_item(first_stream->in_queue, ii);
+ if (UpdateSCR(first_stream, buf))
+ {
+ if (first_stream->type == SYNC_TYPE_VIDEO && prev != NULL)
+ {
+ double duration = buf->s.start - prev->s.start;
+ if (duration > 0)
+ {
+ prev->s.duration = duration;
+ prev->s.stop = buf->s.start;
+ }
+ }
+ }
+ prev = buf;
+ }
+ for (ii = 0; ii < common->stream_count; ii++)
+ {
+ sync_stream_t * stream = &common->streams[ii];
+
+ if (stream == first_stream)
+ {
+ // skip first_stream, already done
+ continue;
+ }
+
+ int jj;
+ prev = NULL;
+ for (jj = 0; jj < hb_list_count(stream->in_queue);)
+ {
+ hb_buffer_t * buf = hb_list_item(stream->in_queue, jj);
+ if (!UpdateSCR(stream, buf))
+ {
+ // Subtitle put into delay queue, remove it from in_queue
+ hb_list_rem(stream->in_queue, buf);
+ }
+ else
+ {
+ jj++;
+ if (stream->type == SYNC_TYPE_VIDEO && prev != NULL)
+ {
+ double duration = buf->s.start - prev->s.start;
+ if (duration > 0)
+ {
+ prev->s.duration = duration;
+ prev->s.stop = buf->s.start;
+ }
+ }
+ }
+ prev = buf;
+ }
}
}
static void checkFirstPts( sync_common_t * common )
{
- int ii;
- int64_t first_pts = INT64_MAX;
+ int ii;
+ int64_t first_pts = INT64_MAX;
+ sync_stream_t * first_stream = NULL;
for (ii = 0; ii < common->stream_count; ii++)
{
@@ -275,22 +370,30 @@ static void checkFirstPts( sync_common_t * common )
if (hb_list_count(stream->in_queue) > 0)
{
hb_buffer_t * buf = hb_list_item(stream->in_queue, 0);
- if (first_pts > buf->s.start)
+ if (buf->s.start != AV_NOPTS_VALUE && buf->s.start < first_pts)
{
first_pts = buf->s.start;
+ first_stream = stream;
}
}
}
+ // We should *always* find a first pts because we let the queues
+ // fill before performing this test.
if (first_pts != INT64_MAX)
{
- // Add a fudge factor to first pts to prevent negative
- // timestamps from leaking through. The pipeline can
- // handle a positive offset, but some things choke on
- // negative offsets
- //first_pts -= 500000;
- shiftTS(common, first_pts);
+ common->found_first_pts = 1;
+ // We may have buffers that have no timestamps (i.e. AV_NOPTS_VALUE).
+ // Compute these timestamps based on previous buffer's timestamp
+ // and duration.
+ computeInitialTS(common, first_stream);
+ // After this initialization, all AV_NOPTS_VALUE timestamps
+ // will be filled in by UpdateSCR()
+ }
+ else
+ {
+ // This should never happen
+ hb_error("checkFirstPts: No initial PTS found!\n");
}
- common->found_first_pts = 1;
}
static void addDelta( sync_common_t * common, int64_t start, int64_t delta)
@@ -586,6 +689,56 @@ static void dejitterAudio( sync_stream_t * stream )
}
}
+static hb_buffer_t * CreateSilenceBuf( sync_stream_t * stream, int64_t dur )
+{
+ double frame_dur, next_pts;
+ int size;
+ hb_buffer_list_t list;
+ hb_buffer_t * buf;
+
+ if (stream->audio.audio->config.out.codec & HB_ACODEC_PASS_FLAG)
+ {
+ return NULL;
+ }
+ frame_dur = (90000. * stream->audio.audio->config.out.samples_per_frame) /
+ stream->audio.audio->config.in.samplerate;
+ size = sizeof(float) * stream->audio.audio->config.out.samples_per_frame *
+ hb_mixdown_get_discrete_channel_count(
+ stream->audio.audio->config.out.mixdown );
+
+ hb_buffer_list_clear(&list);
+ next_pts = stream->next_pts;
+ while (dur >= frame_dur)
+ {
+ buf = hb_buffer_init(size);
+ memset(buf->data, 0, buf->size);
+ buf->s.start = next_pts;
+ buf->s.duration = frame_dur;
+ next_pts += frame_dur;
+ buf->s.stop = next_pts;
+ dur -= frame_dur;
+ hb_buffer_list_append(&list, buf);
+ }
+ if (dur > 0)
+ {
+ size = sizeof(float) *
+ (dur * stream->audio.audio->config.in.samplerate / 90000) *
+ hb_mixdown_get_discrete_channel_count(
+ stream->audio.audio->config.out.mixdown );
+ if (size > 0)
+ {
+ buf = hb_buffer_init(size);
+ memset(buf->data, 0, buf->size);
+ buf->s.start = next_pts;
+ buf->s.duration = frame_dur;
+ next_pts += frame_dur;
+ buf->s.stop = next_pts;
+ hb_buffer_list_append(&list, buf);
+ }
+ }
+ return hb_buffer_list_clear(&list);
+}
+
// Fix audio gaps that could not be corrected with dejitter
static void fixAudioGap( sync_stream_t * stream )
{
@@ -609,8 +762,23 @@ static void fixAudioGap( sync_stream_t * stream )
{
stream->gap_pts = buf->s.start;
}
- addDelta(stream->common, stream->next_pts, gap);
- applyDeltas(stream->common);
+ buf = CreateSilenceBuf(stream, gap);
+ if (buf != NULL)
+ {
+ hb_buffer_t * next;
+ int pos;
+ for (pos = 0; buf != NULL; buf = next, pos++)
+ {
+ next = buf->next;
+ buf->next = NULL;
+ hb_list_insert(stream->in_queue, pos, buf);
+ }
+ }
+ else
+ {
+ addDelta(stream->common, stream->next_pts, gap);
+ applyDeltas(stream->common);
+ }
stream->gap_duration += gap;
}
else
@@ -1305,26 +1473,70 @@ static void Synchronize( sync_stream_t * stream )
hb_unlock(common->mutex);
}
-static void updateDuration( sync_stream_t * stream, int64_t start )
+static void updateDuration( sync_stream_t * stream )
{
- // The video decoder does not set an initial duration for frames.
- // So set it here.
+ // The video decoder sets a nominal duration for frames. But the
+ // actual duration needs to be computed from timestamps.
if (stream->type == SYNC_TYPE_VIDEO)
{
int count = hb_list_count(stream->in_queue);
- if (count > 0)
+ if (count >= 2)
{
- hb_buffer_t * buf = hb_list_item(stream->in_queue, count - 1);
- double duration = start - buf->s.start;
+ hb_buffer_t * buf1 = hb_list_item(stream->in_queue, count - 1);
+ hb_buffer_t * buf2 = hb_list_item(stream->in_queue, count - 2);
+ double duration = buf1->s.start - buf2->s.start;
if (duration > 0)
{
- buf->s.duration = duration;
- buf->s.stop = start;
+ buf2->s.duration = duration;
+ buf2->s.stop = buf1->s.start;
+ }
+ else
+ {
+ buf2->s.duration = 0.;
+ buf2->s.start = buf2->s.stop = buf1->s.start;
+ }
+ }
+ }
+}
+
+static void ProcessSCRDelayQueue( sync_common_t * common )
+{
+ int ii, jj;
+
+ for (ii = 0; ii < common->stream_count; ii++)
+ {
+ sync_stream_t * stream = &common->streams[ii];
+ for (jj = 0; jj < hb_list_count(stream->scr_delay_queue);)
+ {
+ hb_buffer_t * buf = hb_list_item(stream->scr_delay_queue, jj);
+ int hash = buf->s.scr_sequence & SCR_HASH_MASK;
+ if (buf->s.scr_sequence < 0)
+ {
+ // Unset scr_sequence inidicates an external stream
+ // (e.g. SRT subtitle) that is not on the same timebase
+ // as the source tracks. Do not adjust timestamps for
+ // scr_offset in this case.
+ hb_list_rem(stream->scr_delay_queue, buf);
+ SortedQueueBuffer(stream, buf);
+ }
+ else if (buf->s.scr_sequence == common->scr[hash].scr_sequence)
+ {
+ if (buf->s.start != AV_NOPTS_VALUE)
+ {
+ buf->s.start -= common->scr[hash].scr_offset;
+ buf->s.start -= stream->pts_slip;
+ }
+ if (buf->s.stop != AV_NOPTS_VALUE)
+ {
+ buf->s.stop -= common->scr[hash].scr_offset;
+ buf->s.stop -= stream->pts_slip;
+ }
+ hb_list_rem(stream->scr_delay_queue, buf);
+ SortedQueueBuffer(stream, buf);
}
else
{
- buf->s.duration = 0.;
- buf->s.stop = buf->s.start;
+ jj++;
}
}
}
@@ -1360,6 +1572,174 @@ static int getStreamId( sync_stream_t * stream )
}
}
+static int UpdateSCR( sync_stream_t * stream, hb_buffer_t * buf )
+{
+ int hash = buf->s.scr_sequence & SCR_HASH_MASK;
+ sync_common_t * common = stream->common;
+ double last_scr_pts, last_duration;
+ int64_t scr_offset = 0;
+
+ if (buf->s.scr_sequence < stream->last_scr_sequence)
+ {
+ // In decoder error conditions, the decoder can send us out of
+ // order frames. Often the stream error will also trigger a
+ // discontinuity detection. An out of order frame will
+ // cause an incorrect SCR offset, so drop such frames.
+ hb_deep_log(3, "SCR sequence went backwards %d -> %d",
+ stream->last_scr_sequence, buf->s.scr_sequence);
+ hb_buffer_close(&buf);
+ return 0;
+ }
+ if (buf->s.scr_sequence >= 0)
+ {
+ if (buf->s.scr_sequence != common->scr[hash].scr_sequence)
+ {
+ if (stream->type == SYNC_TYPE_SUBTITLE ||
+ (stream->last_scr_pts == (int64_t)AV_NOPTS_VALUE &&
+ common->first_scr))
+ {
+ // We got a new scr, but we have no last_scr_pts to base it
+ // off of. Delay till we can compute the scr offset from a
+ // different stream.
+ hb_list_add(stream->scr_delay_queue, buf);
+ return 0;
+ }
+ if (buf->s.start != AV_NOPTS_VALUE)
+ {
+ last_scr_pts = stream->last_scr_pts;
+ last_duration = stream->last_duration;
+ if (last_scr_pts == (int64_t)AV_NOPTS_VALUE)
+ {
+ last_scr_pts = 0.;
+ last_duration = 0.;
+ common->first_scr = 1;
+ }
+ // New SCR. Compute SCR offset
+ common->scr[hash].scr_sequence = buf->s.scr_sequence;
+ common->scr[hash].scr_offset = buf->s.start -
+ (last_scr_pts + last_duration);
+ hb_deep_log(4,
+ "New SCR: type %8s id %x scr seq %d scr offset %ld "
+ "start %"PRId64" last %f dur %f",
+ getStreamType(stream), getStreamId(stream),
+ buf->s.scr_sequence, common->scr[hash].scr_offset,
+ buf->s.start, last_scr_pts, last_duration);
+ ProcessSCRDelayQueue(common);
+ }
+ }
+ scr_offset = common->scr[hash].scr_offset;
+ }
+
+ // Adjust buffer timestamps for SCR offset
+ if (buf->s.start != AV_NOPTS_VALUE)
+ {
+ buf->s.start -= scr_offset;
+ last_scr_pts = buf->s.start;
+ }
+ else if (stream->last_scr_pts != (int64_t)AV_NOPTS_VALUE)
+ {
+ last_scr_pts = stream->last_scr_pts + stream->last_duration;
+ buf->s.start = last_scr_pts;
+ }
+ else
+ {
+ // This should happen extremely rarely if ever.
+ // But if we get here, this is the first buffer received for
+ // this stream. Normally, some buffers will be queued for
+ // every stream before we ever call UpdateSCR.
+ // We don't really know what it's timestamp should be,
+ // but 0 is a good guess.
+ buf->s.start = 0;
+ last_scr_pts = buf->s.start;
+ }
+ if (buf->s.stop != AV_NOPTS_VALUE)
+ {
+ buf->s.stop -= scr_offset;
+ }
+ if (last_scr_pts > stream->last_scr_pts)
+ {
+ stream->last_scr_pts = last_scr_pts;
+ }
+ if (buf->s.scr_sequence > stream->last_scr_sequence)
+ {
+ stream->last_scr_sequence = buf->s.scr_sequence;
+ }
+ stream->last_duration = buf->s.duration;
+
+ return 1;
+}
+
+// Handle broken timestamps that are out of order
+// These are usually due to a broken decoder (e.g. QSV and libav AVI packed
+// b-frame support). But sometimes can come from a severely broken or
+// corrupted source file.
+//
+// We can pretty reliably fix out of order timestamps *if* every frame
+// has a timestamp. But some container formats allow frames with no
+// timestamp (e.g. TS and PS). When there is no timestamp, we will
+// compute one based on the last frames timestamp. If the missing
+// timestamp is out of order and really belonged on an earlier frame (A),
+// this will result in the frame before (A) being long and the frame
+// after the current will overlap current.
+//
+// The condition above of one long frame and one overlap will most likely
+// get fixed by dejitterVideo. dejitterVideo finds sequences where the
+// sum of the durations of frames 1..N == (1/fps) * N. When it finds such
+// a sequence, it adjusts the frame durations to all be 1/fps. Since the
+// vast majority of video is constant framerate, this will fix the above
+// problem most of the time.
+static void SortedQueueBuffer( sync_stream_t * stream, hb_buffer_t * buf )
+{
+ int64_t start;
+ int ii, count;
+
+ start = buf->s.start;
+ hb_list_add(stream->in_queue, buf);
+
+ // Search for the first earlier timestamp that is < this one.
+ // Under normal circumstances where the timestamps are not broken,
+ // this will only check the next to last buffer in the queue
+ // before aborting.
+ count = hb_list_count(stream->in_queue);
+ for (ii = count - 2; ii >= 0; ii--)
+ {
+ buf = hb_list_item(stream->in_queue, ii);
+ if (buf->s.start < start || start == AV_NOPTS_VALUE)
+ {
+ break;
+ }
+ }
+ if (ii < count - 2)
+ {
+ hb_buffer_t * prev = NULL;
+ int jj;
+
+ // The timestamp was out of order.
+ // The timestamp belongs at position ii + 1
+ // Every timestamp from ii + 2 to count - 1 needs to be shifted up.
+ if (ii >= 0)
+ {
+ prev = hb_list_item(stream->in_queue, ii);
+ }
+ for (jj = ii + 1; jj < count; jj++)
+ {
+ int64_t tmp_start;
+
+ buf = hb_list_item(stream->in_queue, jj);
+ tmp_start = buf->s.start;
+ buf->s.start = start;
+ start = tmp_start;
+ if (stream->type == SYNC_TYPE_VIDEO && prev != NULL)
+ {
+ // recompute video buffer duration
+ prev->s.duration = buf->s.start - prev->s.start;
+ prev->s.stop = buf->s.start;
+ }
+ prev = buf;
+ }
+ }
+}
+
static void QueueBuffer( sync_stream_t * stream, hb_buffer_t * buf )
{
hb_lock(stream->common->mutex);
@@ -1374,17 +1754,42 @@ static void QueueBuffer( sync_stream_t * stream, hb_buffer_t * buf )
buf->s.renderOffset = AV_NOPTS_VALUE;
hb_deep_log(11,
- "type %8s id %x start %"PRId64" stop %"PRId64" dur %f",
- getStreamType(stream), getStreamId(stream),
+ "type %8s id %x scr seq %d start %"PRId64" stop %"PRId64" dur %f",
+ getStreamType(stream), getStreamId(stream), buf->s.scr_sequence,
buf->s.start, buf->s.stop, buf->s.duration);
- buf->s.start -= stream->pts_slip;
- if (buf->s.stop != AV_NOPTS_VALUE)
+ if (stream->common->found_first_pts)
+ {
+ if (UpdateSCR(stream, buf))
+ {
+ // Apply any stream slips.
+ // Stream slips will only temporarily differ between
+ // the streams. The slips get updated in applyDeltas. When
+ // all the deltas are absorbed, the stream slips will all
+ // be equal.
+ buf->s.start -= stream->pts_slip;
+ if (buf->s.stop != AV_NOPTS_VALUE)
+ {
+ buf->s.stop -= stream->pts_slip;
+ }
+
+ SortedQueueBuffer(stream, buf);
+ updateDuration(stream);
+ }
+ }
+ else
{
- buf->s.stop -= stream->pts_slip;
+ if (buf->s.start == AV_NOPTS_VALUE &&
+ hb_list_count(stream->in_queue) == 0)
+ {
+ // We require an initial pts to start synchronization
+ saveChap(stream, buf);
+ hb_buffer_close(&buf);
+ hb_unlock(stream->common->mutex);
+ return;
+ }
+ SortedQueueBuffer(stream, buf);
}
- updateDuration(stream, buf->s.start);
- hb_list_add(stream->in_queue, buf);
// Make adjustments for gaps found in other streams
applyDeltas(stream->common);
@@ -1426,6 +1831,7 @@ static int InitAudio( sync_common_t * common, int index )
pv->stream->cond_full = hb_cond_init();
if (pv->stream->cond_full == NULL) goto fail;
pv->stream->in_queue = hb_list_init();
+ pv->stream->scr_delay_queue = hb_list_init();
pv->stream->max_len = SYNC_MAX_AUDIO_QUEUE_LEN;
pv->stream->min_len = SYNC_MIN_AUDIO_QUEUE_LEN;
if (pv->stream->in_queue == NULL) goto fail;
@@ -1435,6 +1841,9 @@ static int InitAudio( sync_common_t * common, int index )
pv->stream->first_pts = AV_NOPTS_VALUE;
pv->stream->next_pts = (int64_t)AV_NOPTS_VALUE;
pv->stream->last_pts = (int64_t)AV_NOPTS_VALUE;
+ pv->stream->last_scr_pts = (int64_t)AV_NOPTS_VALUE;
+ pv->stream->last_scr_sequence = -1;
+ pv->stream->last_duration = (int64_t)AV_NOPTS_VALUE;
pv->stream->audio.audio = audio;
pv->stream->fifo_out = w->fifo_out;
@@ -1498,6 +1907,7 @@ static int InitSubtitle( sync_common_t * common, int index )
pv->stream->cond_full = hb_cond_init();
if (pv->stream->cond_full == NULL) goto fail;
pv->stream->in_queue = hb_list_init();
+ pv->stream->scr_delay_queue = hb_list_init();
pv->stream->max_len = SYNC_MAX_SUBTITLE_QUEUE_LEN;
pv->stream->min_len = SYNC_MIN_SUBTITLE_QUEUE_LEN;
if (pv->stream->in_queue == NULL) goto fail;
@@ -1507,6 +1917,9 @@ static int InitSubtitle( sync_common_t * common, int index )
pv->stream->first_pts = AV_NOPTS_VALUE;
pv->stream->next_pts = (int64_t)AV_NOPTS_VALUE;
pv->stream->last_pts = (int64_t)AV_NOPTS_VALUE;
+ pv->stream->last_scr_pts = (int64_t)AV_NOPTS_VALUE;
+ pv->stream->last_scr_sequence = -1;
+ pv->stream->last_duration = (int64_t)AV_NOPTS_VALUE;
pv->stream->subtitle.subtitle = subtitle;
pv->stream->fifo_out = subtitle->fifo_out;
@@ -1594,6 +2007,7 @@ static int syncVideoInit( hb_work_object_t * w, hb_job_t * job)
pv->stream->cond_full = hb_cond_init();
if (pv->stream->cond_full == NULL) goto fail;
pv->stream->in_queue = hb_list_init();
+ pv->stream->scr_delay_queue = hb_list_init();
pv->stream->max_len = SYNC_MAX_VIDEO_QUEUE_LEN;
pv->stream->min_len = SYNC_MIN_VIDEO_QUEUE_LEN;
if (pv->stream->in_queue == NULL) goto fail;
@@ -1603,6 +2017,9 @@ static int syncVideoInit( hb_work_object_t * w, hb_job_t * job)
pv->stream->first_pts = AV_NOPTS_VALUE;
pv->stream->next_pts = (int64_t)AV_NOPTS_VALUE;
pv->stream->last_pts = (int64_t)AV_NOPTS_VALUE;
+ pv->stream->last_scr_pts = (int64_t)AV_NOPTS_VALUE;
+ pv->stream->last_scr_sequence = -1;
+ pv->stream->last_duration = (int64_t)AV_NOPTS_VALUE;
pv->stream->fifo_out = job->fifo_sync;
pv->stream->video.id = job->title->video_id;