diff options
-rw-r--r-- | libhb/decavcodec.c | 743 | ||||
-rw-r--r-- | libhb/deccc608sub.c | 14 | ||||
-rw-r--r-- | libhb/deccc608sub.h | 2 | ||||
-rw-r--r-- | libhb/declpcm.c | 20 | ||||
-rw-r--r-- | libhb/decpgssub.c | 2 | ||||
-rw-r--r-- | libhb/decssasub.c | 47 | ||||
-rw-r--r-- | libhb/dectx3gsub.c | 7 | ||||
-rw-r--r-- | libhb/decvobsub.c | 46 | ||||
-rw-r--r-- | libhb/fifo.c | 16 | ||||
-rw-r--r-- | libhb/internal.h | 2 | ||||
-rw-r--r-- | libhb/reader.c | 514 | ||||
-rw-r--r-- | libhb/sync.c | 501 |
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; |