diff options
author | jstebbins <[email protected]> | 2011-09-19 15:35:46 +0000 |
---|---|---|
committer | jstebbins <[email protected]> | 2011-09-19 15:35:46 +0000 |
commit | d36f10c33b9f9f85134f70fc785e2523177b2444 (patch) | |
tree | e6906a5b917569d17da651ab6b897865609994d3 | |
parent | d145ee9d9ed7be65b83124afbf0cc71b60adddc6 (diff) |
Fix corrupt first frame in BD point-to-point
Seek point may be a recovery point which will not be a complete clean
frame. So consume frames till we reach the recovery frame count.
Patches Libav so it can tell us when the recovery point has been
reached.
Also improves detection of recovery points in TS files.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@4231 b64f7644-9d1e-0410-96f1-a4d463321fa5
-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; |