From 28096e0a806e57376541e6222d315619906e3c55 Mon Sep 17 00:00:00 2001 From: John Stebbins Date: Fri, 11 Oct 2013 09:51:50 -0700 Subject: [PATCH] h264: wait for initial complete frame before outputing frames This can be optionally disabled whith the "output_corrupt" flags option. When in "output_corrupt" mode, incomplete frames are signalled through AVFrame.flags FRAME_FLAG_INCOMPLETE_FRAME. Signed-off-by: Anton Khirnov --- doc/APIchanges | 3 +++ libavcodec/avcodec.h | 1 + libavcodec/h264.c | 44 ++++++++++++++++++++++++++++++++++---- libavcodec/h264.h | 21 ++++++++++++++++++ libavcodec/mpegvideo.h | 1 + libavcodec/options_table.h | 1 + libavutil/frame.h | 10 +++++++++ libavutil/version.h | 2 +- 8 files changed, 78 insertions(+), 5 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 20fb16e28d..561c828a1b 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -13,6 +13,9 @@ libavutil: 2012-10-22 API changes, most recent first: +2013-08-xx - xxxxxxx - lavu 52.17.0 - avframe.h + Add AVFrame.flags and AV_FRAME_FLAG_CORRUPT. + 2013-08-xx - xxxxxxx - lavfi 3.11.0 - avfilter.h Add AVFilterGraph.execute and AVFilterGraph.opaque for custom slice threading implementations. diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 6cd70768a9..3f3b4d2662 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -629,6 +629,7 @@ typedef struct RcOverride{ #define CODEC_FLAG_UNALIGNED 0x0001 #define CODEC_FLAG_QSCALE 0x0002 ///< Use fixed qscale. #define CODEC_FLAG_4MV 0x0004 ///< 4 MV per MB allowed / advanced prediction for H.263. +#define CODEC_FLAG_OUTPUT_CORRUPT 0x0008 ///< Output even those frames that might be corrupted #define CODEC_FLAG_QPEL 0x0010 ///< Use qpel MC. #define CODEC_FLAG_GMC 0x0020 ///< Use GMC. #define CODEC_FLAG_MV0 0x0040 ///< Always try a MB with MV=<0,0>. diff --git a/libavcodec/h264.c b/libavcodec/h264.c index d093928af2..f0ef2eb9b3 100644 --- a/libavcodec/h264.c +++ b/libavcodec/h264.c @@ -336,6 +336,7 @@ static int ref_picture(H264Context *h, Picture *dst, Picture *src) dst->field_picture = src->field_picture; dst->needs_realloc = src->needs_realloc; dst->reference = src->reference; + dst->recovered = src->recovered; return 0; fail: @@ -1560,6 +1561,8 @@ av_cold int ff_h264_decode_init(AVCodecContext *avctx) h->prev_poc_msb = 1 << 16; h->x264_build = -1; ff_h264_reset_sei(h); + h->recovery_frame = -1; + h->frame_recovered = 0; if (avctx->codec_id == AV_CODEC_ID_H264) { if (avctx->ticks_per_frame == 1) h->avctx->time_base.den *= 2; @@ -1839,6 +1842,9 @@ static int decode_update_thread_context(AVCodecContext *dst, h->prev_frame_num = h->frame_num; h->outputed_poc = h->next_outputed_poc; + h->recovery_frame = h1->recovery_frame; + h->frame_recovered = h1->frame_recovered; + return err; } @@ -1868,6 +1874,7 @@ static int h264_frame_start(H264Context *h) */ pic->f.key_frame = 0; pic->mmco_reset = 0; + pic->recovered = 0; if ((ret = alloc_picture(h, pic)) < 0) return ret; @@ -2139,6 +2146,15 @@ static void decode_postinit(H264Context *h, int setup_finished) av_log(h->avctx, AV_LOG_DEBUG, "no picture\n"); } + if (h->next_output_pic) { + if (h->next_output_pic->recovered) { + // We have reached an recovery point and all frames after it in + // display order are "recovered". + h->frame_recovered |= FRAME_RECOVERED_SEI; + } + h->next_output_pic->recovered |= !!(h->frame_recovered & FRAME_RECOVERED_SEI); + } + if (setup_finished && !h->avctx->hwaccel) ff_thread_finish_setup(h->avctx); } @@ -2720,6 +2736,8 @@ static void flush_change(H264Context *h) memset(h->default_ref_list[0], 0, sizeof(h->default_ref_list[0])); memset(h->default_ref_list[1], 0, sizeof(h->default_ref_list[1])); ff_h264_reset_sei(h); + h->recovery_frame = -1; + h->frame_recovered = 0; } /* forget old pics after a seek */ @@ -4609,10 +4627,26 @@ again: if ((err = decode_slice_header(hx, h))) break; + if (h->sei_recovery_frame_cnt >= 0 && h->recovery_frame < 0) { + h->recovery_frame = (h->frame_num + h->sei_recovery_frame_cnt) & + ((1 << h->sps.log2_max_frame_num) - 1); + } + h->cur_pic_ptr->f.key_frame |= (hx->nal_unit_type == NAL_IDR_SLICE) || (h->sei_recovery_frame_cnt >= 0); + if (hx->nal_unit_type == NAL_IDR_SLICE || + h->recovery_frame == h->frame_num) { + h->recovery_frame = -1; + h->cur_pic_ptr->recovered = 1; + } + // If we have an IDR, all frames after it in decoded order are + // "recovered". + if (hx->nal_unit_type == NAL_IDR_SLICE) + h->frame_recovered |= FRAME_RECOVERED_IDR; + h->cur_pic_ptr->recovered |= !!(h->frame_recovered & FRAME_RECOVERED_IDR); + if (h->current_slice == 1) { if (!(avctx->flags2 & CODEC_FLAG2_CHUNKS)) decode_postinit(h, nal_index >= nals_needed); @@ -4842,10 +4876,12 @@ out: field_end(h, 0); - if (!h->next_output_pic) { - /* Wait for second field. */ - *got_frame = 0; - } else { + *got_frame = 0; + if (h->next_output_pic && ((avctx->flags & CODEC_FLAG_OUTPUT_CORRUPT) || + h->next_output_pic->recovered)) { + if (!h->next_output_pic->recovered) + h->next_output_pic->f.flags |= AV_FRAME_FLAG_CORRUPT; + ret = output_frame(h, pict, &h->next_output_pic->f); if (ret < 0) return ret; diff --git a/libavcodec/h264.h b/libavcodec/h264.h index cb970673ad..920e3fc1c8 100644 --- a/libavcodec/h264.h +++ b/libavcodec/h264.h @@ -611,6 +611,27 @@ typedef struct H264Context { */ 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; + +/** + * We have seen an IDR, so all the following frames in coded order are correctly + * decodable. + */ +#define FRAME_RECOVERED_IDR (1 << 0) +/** + * Sufficient number of frames have been decoded since a SEI recovery point, + * so all the following frames in presentation order are correct. + */ +#define FRAME_RECOVERED_SEI (1 << 1) + + int frame_recovered; ///< Initial frame has been completely recovered + 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/mpegvideo.h b/libavcodec/mpegvideo.h index 9f222c5773..78792cf9a1 100644 --- a/libavcodec/mpegvideo.h +++ b/libavcodec/mpegvideo.h @@ -172,6 +172,7 @@ typedef struct Picture{ int reference; int shared; + int recovered; ///< Picture at IDR or recovery point + recovery count } Picture; /** diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h index 0706f9ad91..f6be31b492 100644 --- a/libavcodec/options_table.h +++ b/libavcodec/options_table.h @@ -68,6 +68,7 @@ static const AVOption avcodec_options[] = { {"aic", "H.263 advanced intra coding / MPEG-4 AC prediction", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG_AC_PRED }, INT_MIN, INT_MAX, V|E, "flags"}, {"ilme", "interlaced motion estimation", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG_INTERLACED_ME }, INT_MIN, INT_MAX, V|E, "flags"}, {"cgop", "closed GOP", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG_CLOSED_GOP }, INT_MIN, INT_MAX, V|E, "flags"}, +{"output_corrupt", "Output even potentially corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG_OUTPUT_CORRUPT }, INT_MIN, INT_MAX, V|D, "flags"}, {"fast", "allow non-spec-compliant speedup tricks", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG2_FAST }, INT_MIN, INT_MAX, V|E, "flags2"}, {"noout", "skip bitstream encoding", 0, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG2_NO_OUTPUT }, INT_MIN, INT_MAX, V|E, "flags2"}, {"ignorecrop", "ignore cropping information from sps", 1, AV_OPT_TYPE_CONST, {.i64 = CODEC_FLAG2_IGNORE_CROP }, INT_MIN, INT_MAX, V|D, "flags2"}, diff --git a/libavutil/frame.h b/libavutil/frame.h index bbff3d11dc..449a6b799b 100644 --- a/libavutil/frame.h +++ b/libavutil/frame.h @@ -350,6 +350,16 @@ typedef struct AVFrame { AVFrameSideData **side_data; int nb_side_data; + +/** + * The frame data may be corrupted, e.g. due to decoding errors. + */ +#define AV_FRAME_FLAG_CORRUPT (1 << 0) + + /** + * Frame flags, a combination of AV_FRAME_FLAG_* + */ + int flags; } AVFrame; /** diff --git a/libavutil/version.h b/libavutil/version.h index 635055fb14..aa13a0fa97 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -37,7 +37,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 52 -#define LIBAVUTIL_VERSION_MINOR 16 +#define LIBAVUTIL_VERSION_MINOR 17 #define LIBAVUTIL_VERSION_MICRO 0 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \