summaryrefslogtreecommitdiffstats
path: root/libhb/decavcodec.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhb/decavcodec.c')
-rw-r--r--libhb/decavcodec.c743
1 files changed, 345 insertions, 398 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;
}
}