diff options
-rw-r--r-- | contrib/ffmpeg/A06-h264-recovery-point.patch | 55 | ||||
-rw-r--r-- | libhb/bd.c | 2 | ||||
-rw-r--r-- | libhb/decavcodec.c | 27 | ||||
-rw-r--r-- | libhb/internal.h | 1 | ||||
-rw-r--r-- | libhb/scan.c | 31 | ||||
-rw-r--r-- | libhb/stream.c | 128 |
6 files changed, 128 insertions, 116 deletions
diff --git a/contrib/ffmpeg/A06-h264-recovery-point.patch b/contrib/ffmpeg/A06-h264-recovery-point.patch new file mode 100644 index 000000000..8b1531444 --- /dev/null +++ b/contrib/ffmpeg/A06-h264-recovery-point.patch @@ -0,0 +1,55 @@ +diff --git a/libavcodec/h264.c b/libavcodec/h264.c +index d047448..e96a129 100644 +--- a/libavcodec/h264.c ++++ b/libavcodec/h264.c +@@ -3654,9 +3654,18 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size){ + if((err = decode_slice_header(hx, h))) + break; + ++ if (h->sei_recovery_frame_cnt >= 0) { ++ h->recovery_frame = (h->frame_num + h->sei_recovery_frame_cnt) % ++ (1 << h->sps.log2_max_frame_num); ++ } ++ + s->current_picture_ptr->key_frame |= +- (hx->nal_unit_type == NAL_IDR_SLICE) || +- (h->sei_recovery_frame_cnt >= 0); ++ (hx->nal_unit_type == NAL_IDR_SLICE); ++ ++ if (h->recovery_frame == h->frame_num) { ++ s->current_picture_ptr->key_frame |= 1; ++ h->recovery_frame = -1; ++ } + + if (h->current_slice == 1) { + if(!(s->flags2 & CODEC_FLAG2_CHUNKS)) { +diff --git a/libavcodec/h264.h b/libavcodec/h264.h +index 122a54a..cd044b0 100644 +--- a/libavcodec/h264.h ++++ b/libavcodec/h264.h +@@ -575,6 +575,13 @@ typedef struct H264Context{ + * frames. + */ + int sei_recovery_frame_cnt; ++ /** ++ * recovery_frame is the frame_num at which the next frame should ++ * be fully constructed. ++ * ++ * Set to -1 when not expecting a recovery point. ++ */ ++ int recovery_frame; + + int luma_weight_flag[2]; ///< 7.4.3.2 luma_weight_lX_flag + int chroma_weight_flag[2]; ///< 7.4.3.2 chroma_weight_lX_flag +diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c +index 4f52bbe..8d3c40b 100644 +--- a/libavcodec/h264_sei.c ++++ b/libavcodec/h264_sei.c +@@ -38,6 +38,7 @@ static const uint8_t sei_num_clock_ts_table[9]={ + }; + + void ff_h264_reset_sei(H264Context *h) { ++ h->recovery_frame = -1; + h->sei_recovery_frame_cnt = -1; + h->sei_dpb_output_delay = 0; + h->sei_cpb_removal_delay = -1; diff --git a/libhb/bd.c b/libhb/bd.c index 3adf765a1..93ca20d02 100644 --- a/libhb/bd.c +++ b/libhb/bd.c @@ -529,6 +529,7 @@ int hb_bd_seek_pts( hb_bd_t * d, uint64_t pts ) { bd_seek_time(d->bd, pts); d->next_chap = bd_get_current_chapter( d->bd ) + 1; + hb_ts_stream_reset(d->stream); return 1; } @@ -537,6 +538,7 @@ int hb_bd_seek_chapter( hb_bd_t * d, int c ) int64_t pos; d->next_chap = c; pos = bd_seek_chapter( d->bd, c - 1 ); + hb_ts_stream_reset(d->stream); return 1; } diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c index fcd3ce032..478934b3b 100644 --- a/libhb/decavcodec.c +++ b/libhb/decavcodec.c @@ -93,6 +93,7 @@ struct hb_work_private_s struct SwsContext *sws_context; // if we have to rescale or convert color space hb_downmix_t *downmix; int cadence[12]; + int wait_for_keyframe; }; static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *data, int size, int64_t pts ); @@ -170,6 +171,7 @@ 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->wait_for_keyframe = 60; pv->job = job; if ( job ) pv->title = job->title; @@ -702,8 +704,9 @@ static void checkCadence( int * cadence, uint16_t flags, int64_t start ) * until enough packets have been decoded so that the timestamps can be * correctly rewritten, if this is necessary. */ -static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size, int sequence, int64_t pts, int64_t dts ) +static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int sequence, int64_t pts, int64_t dts ) { + hb_work_private_t *pv = w->private_data; int got_picture, oldlevel = 0; AVFrame frame; AVPacket avp; @@ -727,6 +730,23 @@ static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size, int sequ { av_log_set_level( oldlevel ); } + if( got_picture && pv->wait_for_keyframe > 0 ) + { + // Libav is inconsistant about how it flags keyframes. For many + // codecs it simply sets frame.key_frame. But for others, it only + // sets frame.pict_type. And for yet others neither gets set at all + // (qtrle). + int key = frame.key_frame || + ( w->codec_param != CODEC_ID_H264 && + ( frame.pict_type == AV_PICTURE_TYPE_I || + frame.pict_type == 0 ) ); + if( !key ) + { + pv->wait_for_keyframe--; + return 0; + } + pv->wait_for_keyframe = 0; + } if( got_picture ) { uint16_t flags = 0; @@ -894,14 +914,14 @@ static void decodeVideo( hb_work_object_t *w, uint8_t *data, int size, int seque if ( pout_len > 0 ) { - decodeFrame( pv, pout, pout_len, sequence, parser_pts, parser_dts ); + decodeFrame( w, pout, pout_len, sequence, parser_pts, parser_dts ); } } while ( pos < size ); /* the stuff above flushed the parser, now flush the decoder */ if ( size <= 0 ) { - while ( decodeFrame( pv, NULL, 0, sequence, AV_NOPTS_VALUE, AV_NOPTS_VALUE ) ) + while ( decodeFrame( w, NULL, 0, sequence, AV_NOPTS_VALUE, AV_NOPTS_VALUE ) ) { } flushDelayQueue( pv ); @@ -1303,6 +1323,7 @@ static void decavcodecvFlush( hb_work_object_t *w ) avcodec_flush_buffers( pv->context ); } } + pv->wait_for_keyframe = 60; } hb_work_object_t hb_decavcodecv = diff --git a/libhb/internal.h b/libhb/internal.h index 2d137dde8..6f6420a56 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -265,7 +265,6 @@ int hb_stream_seek( hb_stream_t *, float ); int hb_stream_seek_ts( hb_stream_t * stream, int64_t ts ); int hb_stream_seek_chapter( hb_stream_t *, int ); int hb_stream_chapter( hb_stream_t * ); -int hb_stream_recovery_count( hb_stream_t * ); hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ); diff --git a/libhb/scan.c b/libhb/scan.c index 3582f7d64..73b39e376 100644 --- a/libhb/scan.c +++ b/libhb/scan.c @@ -569,25 +569,6 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) vid_decoder->flush( vid_decoder ); hb_buffer_t * vid_buf = NULL; - int vidskip = 0; - - if ( title->flags & HBTF_NO_IDR ) - { - // title doesn't have IDR frames so we decode but drop one second's - // worth of frames to allow the decoder to converge. - if ( ! title->rate_base ) - { - vidskip = 30; - } - else - { - vidskip = (double)title->rate / (double)title->rate_base + 0.5; - } - // If it's a BD, we can relax this a bit. Since seeks will - // at least get us to a recovery point. - if (data->bd || title->type == HB_FF_STREAM_TYPE) - vidskip = 2; - } for( j = 0; j < 10240 ; j++ ) { @@ -626,9 +607,6 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); goto skip_preview; } - int count = hb_stream_recovery_count( data->stream ); - if ( count ) - vidskip = count - 1; } else { @@ -646,15 +624,6 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) if( buf_es->id == title->video_id && vid_buf == NULL ) { vid_decoder->work( vid_decoder, &buf_es, &vid_buf ); - // we're dropping frames to get the video decoder in sync - // when the video stream doesn't contain IDR frames - while (vid_buf && --vidskip >= 0) - { - hb_buffer_t * next = vid_buf->next; - vid_buf->next = NULL; - hb_buffer_close( &vid_buf ); - vid_buf = next; - } } else if( ! AllAudioOK( title ) ) { diff --git a/libhb/stream.c b/libhb/stream.c index d9252550d..f77e03c9a 100644 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -149,7 +149,6 @@ struct hb_stream_s int packetsize; /* Transport Stream packet size */ int need_keyframe; // non-zero if want to start at a keyframe - hb_buffer_t *fwrite_buf; /* PS buffer (set by hb_ts_stream_decode) */ int chapter; /* Chapter that we are currently in */ int64_t chapter_end; /* HB time that the current chapter ends */ @@ -189,8 +188,6 @@ struct hb_stream_s #define TS_HAS_PCR (1 << 0) // at least one PCR seen #define TS_HAS_RAP (1 << 1) // Random Access Point bit seen #define TS_HAS_RSEI (1 << 2) // "Restart point" SEI seen - int recovery_frames; - char *path; FILE *file_handle; @@ -272,6 +269,7 @@ static int ffmpeg_seek( hb_stream_t *stream, float frac ); static int ffmpeg_seek_ts( hb_stream_t *stream, int64_t ts ); static inline unsigned int bits_get(bitbuf_t *bb, int bits); static inline void bits_init(bitbuf_t *bb, uint8_t* buf, int bufsize, int clear); +static inline unsigned int bits_peek(bitbuf_t *bb, int bits); static inline int bits_eob(bitbuf_t *bb); static inline int bits_read_ue(bitbuf_t *bb ); static void pes_add_audio_to_title(hb_stream_t *s, int i, hb_title_t *t, int sort); @@ -983,8 +981,13 @@ hb_stream_t * hb_bd_stream_open( hb_title_t *title ) // lot of data before finding the PCR. if ( title->job ) { + /* BD has PCRs, but the BD index always points to a packet + * after a PCR packet, so we will not see the initial PCR + * after any seek. So don't set the flag that causes us + * to drop packets till we see a PCR. */ + //d->ts_flags = TS_HAS_RAP | TS_HAS_PCR; + // BD PCR PID is specified to always be 0x1001 - d->ts_flags = TS_HAS_RAP | TS_HAS_PCR; update_ts_streams( d, 0x1001, 0, -1, P, NULL ); } @@ -1246,28 +1249,11 @@ static int isRecoveryPoint( const uint8_t *buf, int len ) break; } - if ( ii + size + 1 > nal_len ) - { - // Is it an SEI recovery point? - if ( type == 6 ) - { - // Recovery point found, but can't parse the entire NAL. - // So return an arbitrary recovery_frames count. - recovery_frames = 3; - } - break; - } - - // Is it an SEI recovery point? - if ( type == 6 ) + if( type == 6 ) { - bitbuf_t bb; - bits_init(&bb, nal+ii, size, 0); - int count = bits_read_ue( &bb ); - recovery_frames = count + 3; + recovery_frames = 1; break; } - ii += size; } @@ -1332,6 +1318,11 @@ static int isIframe( hb_stream_t *stream, const uint8_t *buf, int len ) { // we found a start code - remove the ref_idc from the nal type uint8_t nal_type = strid & 0x1f; + if ( nal_type == 0x01 ) + { + // Found slice and no recovery point + return 0; + } if ( nal_type == 0x05 ) { // h.264 IDR picture start @@ -1826,20 +1817,20 @@ int hb_stream_seek( hb_stream_t * stream, float f ) // forwards to the next transport stream packet. hb_ts_stream_reset(stream); align_to_next_packet(stream); - if ( stream->has_IDRs ) + if ( !stream->has_IDRs ) { - // the stream has IDRs so look for one. - stream->need_keyframe = 1; + // the stream has no IDRs so don't look for one. + stream->need_keyframe = 0; } } else if ( stream->hb_stream_type == program ) { hb_ps_stream_reset(stream); skip_to_next_pack( stream ); - if ( stream->has_IDRs ) + if ( !stream->has_IDRs ) { - // the stream has IDRs so look for one. - stream->need_keyframe = 1; + // the stream has no IDRs so don't look for one. + stream->need_keyframe = 0; } } @@ -3393,9 +3384,8 @@ static hb_buffer_t * hb_ps_stream_decode( hb_stream_t *stream ) { // we're looking for the first video frame because we're // doing random access during 'scan' - if ( buf->type == VIDEO_BUF ) - stream->recovery_frames = isIframe( stream, buf->data, buf->size ); - if ( buf->type != VIDEO_BUF || !stream->recovery_frames ) + if ( buf->type != VIDEO_BUF || + !isIframe( stream, buf->data, buf->size ) ) { // not the video stream or didn't find an I frame // but we'll only wait 255 video frames for an I frame. @@ -4320,21 +4310,6 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream) } -static void fwrite64( hb_buffer_t * buf, void *data, int len ) -{ - if ( len > 0 ) - { - int pos = buf->size; - if ( pos + len > buf->alloc ) - { - int size = MAX(buf->alloc * 2, pos + len); - hb_buffer_realloc(buf, size); - } - memcpy( &(buf->data[pos]), data, len ); - buf->size += len; - } -} - // convert a PES PTS or DTS to an int64 static int64_t pes_timestamp( const uint8_t *buf ) { @@ -4360,7 +4335,7 @@ static hb_buffer_t * generate_output_data(hb_stream_t *stream, int curstream) return NULL; } - uint8_t *tdat = b->data; + uint8_t *tdat = b->data + pes_info.header_len; int size = b->size - pes_info.header_len; if ( size <= 0 ) @@ -4369,8 +4344,27 @@ static hb_buffer_t * generate_output_data(hb_stream_t *stream, int curstream) return NULL; } - // Check all substreams to see if this packet matches int pes_idx; + pes_idx = stream->ts.list[curstream].pes_list; + if( stream->need_keyframe ) + { + // we're looking for the first video frame because we're + // doing random access during 'scan' + int kind = stream->pes.list[pes_idx].stream_kind; + if( kind != V || !isIframe( stream, tdat, size ) ) + { + // not the video stream or didn't find an I frame + // but we'll only wait 255 video frames for an I frame. + if ( kind != V || ++stream->need_keyframe < 512 ) + { + b->size = 0; + return NULL; + } + } + stream->need_keyframe = 0; + } + + // Check all substreams to see if this packet matches for ( pes_idx = stream->ts.list[curstream].pes_list; pes_idx != -1; pes_idx = stream->pes.list[pes_idx].next ) { @@ -4393,8 +4387,6 @@ static hb_buffer_t * generate_output_data(hb_stream_t *stream, int curstream) buf = tmp; } - buf->size = 0; - buf->id = get_id( &stream->pes.list[pes_idx] ); switch (stream->pes.list[pes_idx].stream_kind) { @@ -4450,8 +4442,7 @@ static hb_buffer_t * generate_output_data(hb_stream_t *stream, int curstream) buf->start = pes_info.pts; buf->renderOffset = pes_info.dts; } - - fwrite64( buf, tdat + pes_info.header_len, size ); + memcpy( buf->data, tdat, size ); } b->size = 0; @@ -4473,13 +4464,6 @@ static void hb_ts_stream_append_pkt(hb_stream_t *stream, int idx, const uint8_t stream->ts.list[idx].buf->size += len; } -int hb_stream_recovery_count( hb_stream_t *stream ) -{ - int count = stream->recovery_frames; - stream->recovery_frames = 0; - return count; -} - /*********************************************************************** * hb_ts_stream_decode *********************************************************************** @@ -4646,25 +4630,7 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) return NULL; } - if ( stream->need_keyframe && video_index >= 0 ) - { - // we're looking for the first video frame because we're - // doing random access during 'scan' - if ( curstream == video_index ) - stream->recovery_frames = ts_isIframe( stream, pkt, adapt_len ); - if ( curstream != video_index || !stream->recovery_frames ) - { - // not the video stream or didn't find an I frame - // but we'll only wait 255 video frames for an I frame. - if ( curstream != video_index || ++stream->need_keyframe < 512 ) - { - return NULL; - } - } - stream->need_keyframe = 0; - } - - // If we were skipping a bad packet, start fresh on this new PES packet.. + // If we were skipping a bad packet, start fresh on this new PES packet if (stream->ts.list[curstream].skipbad == 1) { stream->ts.list[curstream].skipbad = 0; @@ -4776,7 +4742,7 @@ void hb_ts_stream_reset(hb_stream_t *stream) stream->ts.list[i].continuity = -1; } - stream->need_keyframe = 0; + stream->need_keyframe = 1; stream->ts.found_pcr = 0; stream->ts.pcr_out = 0; @@ -4792,7 +4758,7 @@ void hb_ts_stream_reset(hb_stream_t *stream) void hb_ps_stream_reset(hb_stream_t *stream) { - stream->need_keyframe = 0; + stream->need_keyframe = 1; stream->pes.found_scr = 0; stream->pes.scr = -1; |