From de858d1600ac61258dc895b2fc8756b7c5d1d3a8 Mon Sep 17 00:00:00 2001 From: John Stebbins Date: Tue, 17 May 2016 11:51:25 -0600 Subject: libhb: send initial chapter through pipeline Eliminate the need for everyone to assume that the first chapter starts at the first frame. --- libhb/bd.c | 28 +++++--- libhb/common.c | 73 +++++++++++++++++++++ libhb/decavcodec.c | 8 --- libhb/dvd.c | 18 +++--- libhb/dvd.h | 1 + libhb/dvdnav.c | 13 +--- libhb/enc_qsv.c | 138 ++++++++-------------------------------- libhb/encavcodec.c | 45 +++++++++---- libhb/encx264.c | 179 +++++++++++++++++----------------------------------- libhb/internal.h | 19 ++++++ libhb/muxavformat.c | 54 ++++++++-------- libhb/stream.c | 59 +++++++++++++---- 12 files changed, 316 insertions(+), 319 deletions(-) (limited to 'libhb') diff --git a/libhb/bd.c b/libhb/bd.c index c153f5ab6..16f79dc4c 100644 --- a/libhb/bd.c +++ b/libhb/bd.c @@ -636,7 +636,8 @@ int hb_bd_start( hb_bd_t * d, hb_title_t *title ) // Calling bd_get_event initializes libbluray event queue. bd_select_title( d->bd, d->title_info[title->index - 1]->idx ); bd_get_event( d->bd, &event ); - d->chapter = 1; + d->chapter = 0; + d->next_chap = 1; d->stream = hb_bd_stream_open( d->h, title ); if ( d->stream == NULL ) { @@ -700,15 +701,10 @@ hb_buffer_t * hb_bd_read( hb_bd_t * d ) uint64_t pos; hb_buffer_t * out = NULL; uint8_t discontinuity; - int new_chap = 0; while ( 1 ) { discontinuity = 0; - if ( d->next_chap != d->chapter ) - { - new_chap = d->chapter = d->next_chap; - } result = next_packet( d->bd, buf ); if ( result < 0 ) { @@ -737,7 +733,10 @@ hb_buffer_t * hb_bd_read( hb_bd_t * d ) case BD_EVENT_CHAPTER: // The muxers expect to only get chapter 2 and above // They write chapter 1 when chapter 2 is detected. - d->next_chap = event.param; + if (event.param > d->chapter) + { + d->next_chap = event.param; + } break; case BD_EVENT_PLAYITEM: @@ -754,10 +753,19 @@ hb_buffer_t * hb_bd_read( hb_bd_t * d ) } } // buf+4 to skip the BD timestamp at start of packet - out = hb_ts_decode_pkt( d->stream, buf+4, new_chap, discontinuity ); + if (d->chapter != d->next_chap) + { + d->chapter = d->next_chap; + out = hb_ts_decode_pkt(d->stream, buf+4, d->chapter, discontinuity); + } + else + { + out = hb_ts_decode_pkt(d->stream, buf+4, 0, discontinuity); + } if (out != NULL) + { return out; - new_chap = 0; + } } return NULL; } @@ -770,7 +778,7 @@ hb_buffer_t * hb_bd_read( hb_bd_t * d ) **********************************************************************/ int hb_bd_chapter( hb_bd_t * d ) { - return d->next_chap; + return d->chapter; } /*********************************************************************** diff --git a/libhb/common.c b/libhb/common.c index f282bbbcf..0310eeeac 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -5361,3 +5361,76 @@ void hb_str_vfree( char **strv ) free( strv ); } +hb_chapter_queue_t * hb_chapter_queue_init(void) +{ + hb_chapter_queue_t * q; + + q = calloc(1, sizeof(*q)); + if (q != NULL) + { + q->list_chapter = hb_list_init(); + if (q->list_chapter == NULL) + { + free(q); + q = NULL; + } + } + return q; +} + +void hb_chapter_queue_close(hb_chapter_queue_t **_q) +{ + hb_chapter_queue_t * q = *_q; + hb_chapter_queue_item_t * item; + + if (q == NULL) + { + return; + } + while ((item = hb_list_item(q->list_chapter, 0)) != NULL) + { + hb_list_rem(q->list_chapter, item); + free(item); + } + hb_list_close(&q->list_chapter); + free(q); + *_q = NULL; +} + +void hb_chapter_enqueue(hb_chapter_queue_t *q, hb_buffer_t *buf) +{ + /* + * Chapter markers are sometimes so close we can get a new + * one before the previous goes through the encoding queue. + * + * Dropping markers can cause weird side-effects downstream, + * including but not limited to missing chapters in the + * output, so we need to save it somehow. + */ + hb_chapter_queue_item_t *item = malloc(sizeof(hb_chapter_queue_item_t)); + if (item != NULL) + { + item->start = buf->s.start; + item->new_chap = buf->s.new_chap; + hb_list_add(q->list_chapter, item); + } +} + +void hb_chapter_dequeue(hb_chapter_queue_t *q, hb_buffer_t *buf) +{ + hb_chapter_queue_item_t *item = hb_list_item(q->list_chapter, 0); + if (item != NULL) + { + if (buf->s.start < item->start) + { + // Have not reached the next chapter yet. + return; + } + + // we're done with this chapter + hb_list_rem(q->list_chapter, item); + buf->s.new_chap = item->new_chap; + free(item); + } +} + diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c index bc6637143..f20afb2e9 100644 --- a/libhb/decavcodec.c +++ b/libhb/decavcodec.c @@ -1345,10 +1345,6 @@ static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int sequen pv->new_chap = 0; pv->chap_time = 0; } - else if ( pv->nframes == 0 && pv->job ) - { - log_chapter( pv, pv->job->chapter_start, buf->s.start ); - } checkCadence( pv->cadence, flags, buf->s.start ); hb_buffer_list_append(&pv->list, buf); ++pv->nframes; @@ -1389,10 +1385,6 @@ static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int sequen pv->new_chap = 0; pv->chap_time = 0; } - else if ( pv->nframes == 0 && pv->job ) - { - log_chapter( pv, pv->job->chapter_start, buf->s.start ); - } checkCadence( pv->cadence, buf->s.flags, buf->s.start ); hb_buffer_list_append(&pv->list, buf); } diff --git a/libhb/dvd.c b/libhb/dvd.c index 7255aec74..ab06efd3a 100644 --- a/libhb/dvd.c +++ b/libhb/dvd.c @@ -1056,11 +1056,7 @@ static hb_buffer_t * hb_dvdread_read( hb_dvd_t * e ) d->cur_vob_id = dsi_pack.dsi_gi.vobu_vob_idn; d->cur_cell_id = dsi_pack.dsi_gi.vobu_c_idn; - if( d->cell_overlap ) - { - b->s.new_chap = hb_dvdread_is_break( d ); - d->cell_overlap = 0; - } + d->cell_overlap = 0; } } @@ -1095,6 +1091,10 @@ static hb_buffer_t * hb_dvdread_read( hb_dvd_t * e ) } d->pack_len--; } + if (b != NULL) + { + b->s.new_chap = hb_dvdread_is_break( d ); + } d->block++; @@ -1149,9 +1149,7 @@ static int hb_dvdread_is_break( hb_dvdread_t * d ) pgc_t * pgc; int cell; - for( i = nr_of_ptts - 1; - i > 0; - i-- ) + for (i = nr_of_ptts - 1; i >= 0; i--) { /* Get pgc for chapter (i+1) */ pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgcn; @@ -1159,11 +1157,11 @@ static int hb_dvdread_is_break( hb_dvdread_t * d ) pgc = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc; cell = pgc->program_map[pgn-1] - 1; - if( cell <= d->cell_start ) + if( cell < d->cell_start ) break; // This must not match against the start cell. - if( pgc->cell_playback[cell].first_sector == d->block && cell != d->cell_start ) + if (pgc->cell_playback[cell].first_sector == d->block) { return i + 1; } diff --git a/libhb/dvd.h b/libhb/dvd.h index 55ddb66b9..f5e904433 100644 --- a/libhb/dvd.h +++ b/libhb/dvd.h @@ -43,6 +43,7 @@ struct hb_dvdread_s uint16_t cur_vob_id; uint8_t cur_cell_id; hb_handle_t * h; + int chapter; }; struct hb_dvdnav_s diff --git a/libhb/dvdnav.c b/libhb/dvdnav.c index e2a472cb5..6075a8ee6 100644 --- a/libhb/dvdnav.c +++ b/libhb/dvdnav.c @@ -1646,11 +1646,7 @@ static hb_buffer_t * hb_dvdnav_read( hb_dvd_t * e ) case DVDNAV_BLOCK_OK: // We have received a regular block of the currently playing // MPEG stream. - - // The muxers expect to only get chapter 2 and above - // They write chapter 1 when chapter 2 is detected. - if (chapter > 1) - b->s.new_chap = chapter; + b->s.new_chap = chapter; chapter = 0; error_count = 0; return b; @@ -1791,12 +1787,7 @@ static hb_buffer_t * hb_dvdnav_read( hb_dvd_t * e ) // mpegdemux expects to get these. I don't think it does // anything useful with them however. - - // The muxers expect to only get chapter 2 and above - // They write chapter 1 when chapter 2 is detected. - if (chapter > 1) - b->s.new_chap = chapter; - chapter = 0; + b->s.new_chap = chapter; return b; break; diff --git a/libhb/enc_qsv.c b/libhb/enc_qsv.c index 5e1a77824..4030cd30c 100644 --- a/libhb/enc_qsv.c +++ b/libhb/enc_qsv.c @@ -67,51 +67,43 @@ hb_work_object_t hb_encqsv = struct hb_work_private_s { - hb_job_t * job; - uint32_t frames_in; - uint32_t frames_out; - int64_t last_start; + hb_job_t * job; + uint32_t frames_in; + uint32_t frames_out; + int64_t last_start; - hb_qsv_param_t param; - av_qsv_space enc_space; - hb_qsv_info_t * qsv_info; + hb_qsv_param_t param; + av_qsv_space enc_space; + hb_qsv_info_t * qsv_info; - hb_list_t * delayed_chapters; - int64_t next_chapter_pts; + hb_chapter_queue_t * chapter_queue; #define BFRM_DELAY_MAX 16 - int * init_delay; - int bfrm_delay; - int64_t init_pts[BFRM_DELAY_MAX + 1]; - hb_list_t * list_dts; + int * init_delay; + int bfrm_delay; + int64_t init_pts[BFRM_DELAY_MAX + 1]; + hb_list_t * list_dts; - int64_t frame_duration[FRAME_INFO_SIZE]; + int64_t frame_duration[FRAME_INFO_SIZE]; - int async_depth; - int max_async_depth; + int async_depth; + int max_async_depth; // if encode-only, system memory used - int is_sys_mem; - mfxSession mfx_session; - struct SwsContext * sws_context_to_nv12; + int is_sys_mem; + mfxSession mfx_session; + struct SwsContext * sws_context_to_nv12; // whether to expect input from VPP or from QSV decode - int is_vpp_present; + int is_vpp_present; // whether the encoder is initialized - int init_done; + int init_done; - hb_list_t * delayed_processing; - hb_buffer_list_t encoded_frames; + hb_list_t * delayed_processing; + hb_buffer_list_t encoded_frames; - hb_list_t * loaded_plugins; -}; - -// used in delayed_chapters list -struct chapter_s -{ - int index; - int64_t start; + hb_list_t * loaded_plugins; }; static void hb_qsv_add_new_dts(hb_list_t *list, int64_t new_dts) @@ -683,8 +675,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job) pv->last_start = INT64_MIN; hb_buffer_list_clear(&pv->encoded_frames); - pv->next_chapter_pts = AV_NOPTS_VALUE; - pv->delayed_chapters = hb_list_init(); + pv->chapter_queue = hb_chapter_queue_init(); // default encoding parameters if (hb_qsv_param_default_preset(&pv->param, &pv->enc_space.m_mfxVideoParam, @@ -1486,6 +1477,7 @@ void encqsvClose(hb_work_object_t *w) if (pv != NULL) { + hb_chapter_queue_close(&pv->chapter_queue); if (pv->delayed_processing != NULL) { /* the list is already empty */ @@ -1505,16 +1497,6 @@ void encqsvClose(hb_work_object_t *w) } hb_list_close(&pv->list_dts); } - if (pv->delayed_chapters != NULL) - { - struct chapter_s *item; - while ((item = hb_list_item(pv->delayed_chapters, 0)) != NULL) - { - hb_list_rem(pv->delayed_chapters, item); - free(item); - } - hb_list_close(&pv->delayed_chapters); - } hb_buffer_list_close(&pv->encoded_frames); } @@ -1522,68 +1504,6 @@ void encqsvClose(hb_work_object_t *w) w->private_data = NULL; } -static void save_chapter(hb_work_private_t *pv, hb_buffer_t *buf) -{ - /* - * Since there may be several frames buffered in the encoder, remember the - * timestamp so when this frame finally pops out of the encoder we'll mark - * its buffer as the start of a chapter. - */ - if (pv->next_chapter_pts == AV_NOPTS_VALUE) - { - pv->next_chapter_pts = buf->s.start; - } - - /* - * Chapter markers are sometimes so close we can get a new - * one before the previous goes through the encoding queue. - * - * Dropping markers can cause weird side-effects downstream, - * including but not limited to missing chapters in the - * output, so we need to save it somehow. - */ - struct chapter_s *item = malloc(sizeof(struct chapter_s)); - - if (item != NULL) - { - item->start = buf->s.start; - item->index = buf->s.new_chap; - hb_list_add(pv->delayed_chapters, item); - } - - /* don't let 'work_loop' put a chapter mark on the wrong buffer */ - buf->s.new_chap = 0; -} - -static void restore_chapter(hb_work_private_t *pv, hb_buffer_t *buf) -{ - /* we're no longer looking for this chapter */ - pv->next_chapter_pts = AV_NOPTS_VALUE; - - /* get the chapter index from the list */ - struct chapter_s *item = hb_list_item(pv->delayed_chapters, 0); - - if (item != NULL) - { - /* we're done with this chapter */ - hb_list_rem(pv->delayed_chapters, item); - buf->s.new_chap = item->index; - free(item); - - /* we may still have another pending chapter */ - item = hb_list_item(pv->delayed_chapters, 0); - - if (item != NULL) - { - /* - * we're looking for this chapter now - * we still need it, don't remove it - */ - pv->next_chapter_pts = item->start; - } - } -} - static void compute_init_delay(hb_work_private_t *pv, mfxBitstream *bs) { if (pv->init_delay == NULL) @@ -1766,11 +1686,9 @@ static void qsv_bitstream_slurp(hb_work_private_t *pv, mfxBitstream *bs) * If we have a chapter marker pending and this frame's PTS * is at or after the marker's PTS, use it as the chapter start. */ - if (pv->next_chapter_pts != AV_NOPTS_VALUE && - pv->next_chapter_pts <= buf->s.start && - qsv_frame_is_key(bs->FrameType)) + if (qsv_frame_is_key(bs->FrameType)) { - restore_chapter(pv, buf); + hb_chapter_dequeue(pv->chapter_queue, buf); } hb_buffer_list_append(&pv->encoded_frames, buf); @@ -2040,7 +1958,7 @@ int encqsvWork(hb_work_object_t *w, hb_buffer_t **buf_in, hb_buffer_t **buf_out) goto fail; } - save_chapter(pv, in); + hb_chapter_enqueue(pv->chapter_queue, in); } /* diff --git a/libhb/encavcodec.c b/libhb/encavcodec.c index 129921d0b..59c787326 100644 --- a/libhb/encavcodec.c +++ b/libhb/encavcodec.c @@ -26,20 +26,22 @@ struct hb_work_private_s { - hb_job_t * job; - AVCodecContext * context; - FILE * file; + hb_job_t * job; + AVCodecContext * context; + FILE * file; - int frameno_in; - int frameno_out; - hb_buffer_list_t delay_list; + int frameno_in; + int frameno_out; + hb_buffer_list_t delay_list; - int64_t dts_delay; + int64_t dts_delay; struct { - int64_t start; - int64_t duration; + int64_t start; + int64_t duration; } frame_info[FRAME_INFO_SIZE]; + + hb_chapter_queue_t * chapter_queue; }; int encavcodecInit( hb_work_object_t *, hb_job_t * ); @@ -62,8 +64,9 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) AVRational fps; hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); - w->private_data = pv; - pv->job = job; + w->private_data = pv; + pv->job = job; + pv->chapter_queue = hb_chapter_queue_init(); hb_buffer_list_clear(&pv->delay_list); @@ -319,6 +322,11 @@ void encavcodecClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; + if (pv == NULL) + { + return; + } + hb_chapter_queue_close(&pv->chapter_queue); if( pv->context && pv->context->codec ) { hb_deep_log( 2, "encavcodec: closing libavcodec" ); @@ -513,6 +521,17 @@ int encavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, frame->linesize[1] = in->plane[1].stride; frame->linesize[2] = in->plane[2].stride; + if (in->s.new_chap > 0 && job->chapter_markers) + { + /* chapters have to start with an IDR frame so request that this + frame be coded as IDR. Since there may be multiple frames + currently buffered in the encoder remember the timestamp so + when this frame finally pops out of the encoder we'll mark + its buffer as the start of a chapter. */ + frame->pict_type = AV_PICTURE_TYPE_I; + hb_chapter_enqueue(pv->chapter_queue, in); + } + // For constant quality, setting the quality in AVCodecContext // doesn't do the trick. It must be set in the AVFrame. frame->quality = pv->context->global_quality; @@ -558,6 +577,10 @@ int encavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, buf->s.stop = buf->s.stop + buf->s.duration; buf->s.flags &= ~HB_FRAME_REF; buf->s.frametype = convert_pict_type( pv->context->coded_frame->pict_type, pkt.flags & AV_PKT_FLAG_KEY, &buf->s.flags ); + if (buf->s.frametype & HB_FRAME_KEY) + { + hb_chapter_dequeue(pv->chapter_queue, buf); + } buf = process_delay_list( pv, buf ); hb_buffer_list_append(&list, buf); diff --git a/libhb/encx264.c b/libhb/encx264.c index 30cee1096..50f7fe197 100644 --- a/libhb/encx264.c +++ b/libhb/encx264.c @@ -48,34 +48,27 @@ hb_work_object_t hb_encx264 = struct hb_work_private_s { - hb_job_t * job; - x264_t * x264; - x264_picture_t pic_in; + hb_job_t * job; + x264_t * x264; + x264_picture_t pic_in; - int64_t last_stop; // Debugging - stop time of previous input frame + int64_t last_stop; // Debugging - stop time of previous input frame + + hb_chapter_queue_t * chapter_queue; - hb_list_t * delayed_chapters; - int64_t next_chapter_pts; struct { - int64_t duration; + int64_t duration; } frame_info[FRAME_INFO_SIZE]; - char filename[1024]; + char filename[1024]; // Multiple bit-depth - const x264_api_t * api; + const x264_api_t * api; }; #define HB_X264_API_COUNT 2 static x264_api_t x264_apis[HB_X264_API_COUNT]; -// used in delayed_chapters list -struct chapter_s -{ - int index; - int64_t start; -}; - const char *libx264_10bit_names[] = { "libx26410b", "libx264_main10", NULL }; @@ -313,12 +306,12 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job ) if (pv->api == NULL) { hb_error("encx264: hb_x264_api_get failed, bit-depth %d", bit_depth); + return 1; } pv->job = job; - pv->next_chapter_pts = AV_NOPTS_VALUE; pv->last_stop = AV_NOPTS_VALUE; - pv->delayed_chapters = hb_list_init(); + pv->chapter_queue = hb_chapter_queue_init(); if (pv->api->param_default_preset(¶m, job->encoder_preset, job->encoder_tune) < 0) @@ -613,22 +606,12 @@ void encx264Close( hb_work_object_t * w ) // Not initialized return; } - if (pv->delayed_chapters != NULL) - { - struct chapter_s *item; - while ((item = hb_list_item(pv->delayed_chapters, 0)) != NULL) - { - hb_list_rem(pv->delayed_chapters, item); - free(item); - } - hb_list_close(&pv->delayed_chapters); - } + + hb_chapter_queue_close(&pv->chapter_queue); pv->api->encoder_close( pv->x264 ); free( pv ); w->private_data = NULL; - - /* TODO */ } /* @@ -669,6 +652,50 @@ static hb_buffer_t *nal_encode( hb_work_object_t *w, x264_picture_t *pic_out, w->config->h264.init_delay = -pic_out->i_dts; } + /* Decide what type of frame we have. */ + switch( pic_out->i_type ) + { + case X264_TYPE_IDR: + // Handled in b_keyframe check below. + break; + + case X264_TYPE_I: + buf->s.frametype = HB_FRAME_I; + break; + + case X264_TYPE_P: + buf->s.frametype = HB_FRAME_P; + break; + + case X264_TYPE_B: + buf->s.frametype = HB_FRAME_B; + break; + + /* This is for b-pyramid, which has reference b-frames + However, it doesn't seem to ever be used... */ + case X264_TYPE_BREF: + buf->s.frametype = HB_FRAME_BREF; + break; + + // If it isn't the above, what type of frame is it?? + default: + buf->s.frametype = 0; + break; + } + + // PIR has no IDR frames, but x264 marks recovery points + // as keyframes. So fake an IDR at these points. This flag + // is also set for real IDR frames. + if (pic_out->b_keyframe) + { + buf->s.frametype = HB_FRAME_IDR; + /* if we have a chapter marker pending and this + frame's presentation time stamp is at or after + the marker's time stamp, use this as the + chapter start. */ + hb_chapter_dequeue(pv->chapter_queue, buf); + } + /* Encode all the NALs we were given into buf. NOTE: This code assumes one video frame per NAL (but there can be other stuff like SPS and/or PPS). If there are multiple @@ -701,37 +728,6 @@ static hb_buffer_t *nal_encode( hb_work_object_t *w, x264_picture_t *pic_out, break; } - /* Decide what type of frame we have. */ - switch( pic_out->i_type ) - { - case X264_TYPE_IDR: - // Handled in b_keyframe check below. - break; - - case X264_TYPE_I: - buf->s.frametype = HB_FRAME_I; - break; - - case X264_TYPE_P: - buf->s.frametype = HB_FRAME_P; - break; - - case X264_TYPE_B: - buf->s.frametype = HB_FRAME_B; - break; - - /* This is for b-pyramid, which has reference b-frames - However, it doesn't seem to ever be used... */ - case X264_TYPE_BREF: - buf->s.frametype = HB_FRAME_BREF; - break; - - // If it isn't the above, what type of frame is it?? - default: - buf->s.frametype = 0; - break; - } - /* Since libx264 doesn't tell us when b-frames are themselves reference frames, figure it out on our own. */ if( (buf->s.frametype == HB_FRAME_B) && @@ -744,43 +740,6 @@ static hb_buffer_t *nal_encode( hb_work_object_t *w, x264_picture_t *pic_out, else buf->s.flags |= HB_FRAME_REF; - // PIR has no IDR frames, but x264 marks recovery points - // as keyframes. So fake an IDR at these points. This flag - // is also set for real IDR frames. - if( pic_out->b_keyframe ) - { - buf->s.frametype = HB_FRAME_IDR; - /* if we have a chapter marker pending and this - frame's presentation time stamp is at or after - the marker's time stamp, use this as the - chapter start. */ - if (pv->next_chapter_pts != AV_NOPTS_VALUE && - pv->next_chapter_pts <= pic_out->i_pts) - { - // we're no longer looking for this chapter - pv->next_chapter_pts = AV_NOPTS_VALUE; - - // get the chapter index from the list - struct chapter_s *item = hb_list_item(pv->delayed_chapters, 0); - if (item != NULL) - { - // we're done with this chapter - buf->s.new_chap = item->index; - hb_list_rem(pv->delayed_chapters, item); - free(item); - - // we may still have another pending chapter - item = hb_list_item(pv->delayed_chapters, 0); - if (item != NULL) - { - // we're looking for this one now - // we still need it, don't remove it - pv->next_chapter_pts = item->start; - } - } - } - } - buf->size += size; } // make sure we found at least one video frame @@ -845,7 +804,7 @@ static hb_buffer_t *x264_encode( hb_work_object_t *w, hb_buffer_t *in ) pv->pic_in.img.plane[2] = in->plane[2].data; } - if( in->s.new_chap && job->chapter_markers ) + if( in->s.new_chap > 0 && job->chapter_markers ) { /* chapters have to start with an IDR frame so request that this frame be coded as IDR. Since there may be up to 16 frames @@ -853,27 +812,7 @@ static hb_buffer_t *x264_encode( hb_work_object_t *w, hb_buffer_t *in ) when this frame finally pops out of the encoder we'll mark its buffer as the start of a chapter. */ pv->pic_in.i_type = X264_TYPE_IDR; - if (pv->next_chapter_pts == AV_NOPTS_VALUE) - { - pv->next_chapter_pts = in->s.start; - } - /* - * Chapter markers are sometimes so close we can get a new one before the - * previous marker has been through the encoding queue. - * - * Dropping markers can cause weird side-effects downstream, including but - * not limited to missing chapters in the output, so we need to save it - * somehow. - */ - struct chapter_s *item = malloc(sizeof(struct chapter_s)); - if (item != NULL) - { - item->start = in->s.start; - item->index = in->s.new_chap; - hb_list_add(pv->delayed_chapters, item); - } - /* don't let 'work_loop' put a chapter mark on the wrong buffer */ - in->s.new_chap = 0; + hb_chapter_enqueue(pv->chapter_queue, in); } else { diff --git a/libhb/internal.h b/libhb/internal.h index 4e477eba6..82012a7d7 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -509,3 +509,22 @@ void hb_muxmp4_process_subtitle_style( uint8_t *input, void hb_deinterlace(hb_buffer_t *dst, hb_buffer_t *src); void hb_avfilter_combine( hb_list_t * list ); +struct hb_chapter_queue_item_s +{ + int64_t start; + int new_chap; +}; + +struct hb_chapter_queue_s +{ + hb_list_t * list_chapter; +}; + +typedef struct hb_chapter_queue_item_s hb_chapter_queue_item_t; +typedef struct hb_chapter_queue_s hb_chapter_queue_t; + +hb_chapter_queue_t * hb_chapter_queue_init(void); +void hb_chapter_queue_close(hb_chapter_queue_t **_q); +void hb_chapter_enqueue(hb_chapter_queue_t *q, hb_buffer_t *b); +void hb_chapter_dequeue(hb_chapter_queue_t *q, hb_buffer_t *b); + diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c index ef72130b6..0a69d83f8 100644 --- a/libhb/muxavformat.c +++ b/libhb/muxavformat.c @@ -197,7 +197,7 @@ static int avformatInit( hb_mux_object_t * m ) job->mux_data = track; track->type = MUX_TYPE_VIDEO; - track->prev_chapter_tc = 0; + track->prev_chapter_tc = AV_NOPTS_VALUE; track->st = avformat_new_stream(m->oc, NULL); if (track->st == NULL) { @@ -1156,33 +1156,37 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu { if (job->chapter_markers && buf->s.new_chap) { - hb_chapter_t *chapter; - - // reached chapter N, write marker for chapter N-1 - // we don't know the end time of chapter N-1 till we receive - // chapter N. So we are always writing the previous chapter - // mark. - track->current_chapter = buf->s.new_chap - 1; - - // chapter numbers start at 1, but the list starts at 0 - chapter = hb_list_item(job->list_chapter, - track->current_chapter - 1); - - // make sure we're not writing a chapter that has 0 length - if (chapter != NULL && track->prev_chapter_tc < pkt.pts) + if (track->current_chapter > 0) { - char title[1024]; - if (chapter->title != NULL) - { - snprintf(title, 1023, "%s", chapter->title); - } - else + hb_chapter_t *chapter; + + // reached chapter N, write marker for chapter N-1 + // we don't know the end time of chapter N-1 till we receive + // chapter N. So we are always writing the previous chapter + // mark. + // chapter numbers start at 1, but the list starts at 0 + chapter = hb_list_item(job->list_chapter, + track->current_chapter - 1); + + // make sure we're not writing a chapter that has 0 length + if (chapter != NULL && + track->prev_chapter_tc != AV_NOPTS_VALUE && + track->prev_chapter_tc < pkt.pts) { - snprintf(title, 1023, "Chapter %d", - track->current_chapter); + char title[1024]; + if (chapter->title != NULL) + { + snprintf(title, 1023, "%s", chapter->title); + } + else + { + snprintf(title, 1023, "Chapter %d", + track->current_chapter); + } + add_chapter(m, track->prev_chapter_tc, pkt.pts, title); } - add_chapter(m, track->prev_chapter_tc, pkt.pts, title); } + track->current_chapter = buf->s.new_chap; track->prev_chapter_tc = pkt.pts; } } break; @@ -1362,7 +1366,7 @@ static int avformatEnd(hb_mux_object_t *m) hb_chapter_t *chapter; // get the last chapter - chapter = hb_list_item(job->list_chapter, track->current_chapter++); + chapter = hb_list_item(job->list_chapter, track->current_chapter - 1); // only write the last chapter marker if it lasts at least 1.5 second if (chapter != NULL && chapter->duration > 135000LL) diff --git a/libhb/stream.c b/libhb/stream.c index 65db14396..3fb7ca5a2 100644 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -1749,20 +1749,22 @@ int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num ) return 0; } - int64_t sum_dur = 0; - hb_chapter_t *chapter = NULL; - int i; - for ( i = 0; i < chapter_num; ++i) - { - chapter = hb_list_item( stream->title->list_chapter, i ); + // TODO: add chapter start time to hb_chapter_t + // The first chapter does not necessarily start at time 0. + int64_t sum_dur = 0; + hb_chapter_t * chapter = NULL; + int ii; + for (ii = 0; ii < chapter_num - 1; ii++) + { + chapter = hb_list_item(stream->title->list_chapter, ii); sum_dur += chapter->duration; } - stream->chapter = chapter_num - 1; + stream->chapter = chapter_num - 1; stream->chapter_end = sum_dur; if (chapter != NULL && chapter_num > 1) { - int64_t pos = (((sum_dur - chapter->duration) * AV_TIME_BASE) / 90000) + + int64_t pos = ((sum_dur * AV_TIME_BASE) / 90000) + ffmpeg_initial_timestamp(stream); if (pos > 0) @@ -1770,7 +1772,7 @@ int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num ) hb_deep_log(2, "Seeking to chapter %d: starts %"PRId64", ends %"PRId64 ", AV pos %"PRId64, - chapter_num, sum_dur - chapter->duration, sum_dur, pos); + chapter_num, sum_dur, sum_dur + chapter->duration, pos); AVStream *st = stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]; // timebase must be adjusted to match timebase of stream we are @@ -1792,7 +1794,7 @@ int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num ) **********************************************************************/ int hb_stream_chapter( hb_stream_t * src_stream ) { - return( src_stream->chapter + 1 ); + return( src_stream->chapter ); } /*********************************************************************** @@ -5829,16 +5831,20 @@ hb_buffer_t * hb_ffmpeg_read( hb_stream_t *stream ) buf->s.start >= stream->chapter_end ) { hb_chapter_t *chapter = hb_list_item( stream->title->list_chapter, - stream->chapter+1 ); - if( chapter ) + stream->chapter); + if (chapter != NULL) { stream->chapter++; stream->chapter_end += chapter->duration; - buf->s.new_chap = stream->chapter + 1; + buf->s.new_chap = stream->chapter; hb_deep_log( 2, "ffmpeg_read starting chapter %i at %"PRId64, - stream->chapter + 1, buf->s.start); + stream->chapter, buf->s.start); } else { + // Some titles run longer than the sum of their chapters + // Don't increment to a nonexistent chapter number // Must have run out of chapters, stop looking. + hb_deep_log( 2, "ffmpeg_read end of chapter %i at %"PRId64, + stream->chapter, buf->s.start); stream->chapter_end = INT64_MAX; buf->s.new_chap = 0; } @@ -5883,6 +5889,31 @@ static int ffmpeg_seek_ts( hb_stream_t *stream, int64_t ts ) int64_t pos; int ret; + // Find the initial chapter we have seeked into + int count = hb_list_count(stream->title->list_chapter); + if (count > 0) + { + int64_t sum_dur = 0; + hb_chapter_t * chapter; + int ii; + for (ii = 0; ii < count; ii++) + { + chapter = hb_list_item( stream->title->list_chapter, ii ); + if (sum_dur + chapter->duration > ts) + { + break; + } + sum_dur += chapter->duration; + } + stream->chapter = ii; + stream->chapter_end = sum_dur; + } + else + { + stream->chapter = 0; + stream->chapter_end = INT64_MAX; + } + pos = ts * AV_TIME_BASE / 90000 + ffmpeg_initial_timestamp( stream ); AVStream *st = stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]; // timebase must be adjusted to match timebase of stream we are -- cgit v1.2.3