diff --git a/libavcodec/h264_picture.c b/libavcodec/h264_picture.c index bd31f700cb..31b5e231c2 100644 --- a/libavcodec/h264_picture.c +++ b/libavcodec/h264_picture.c @@ -54,6 +54,7 @@ void ff_h264_unref_picture(H264Context *h, H264Picture *pic) av_buffer_unref(&pic->motion_val_buf[i]); av_buffer_unref(&pic->ref_index_buf[i]); } + av_buffer_unref(&pic->decode_error_flags); memset((uint8_t*)pic + off, 0, sizeof(*pic) - off); } @@ -136,6 +137,10 @@ int ff_h264_ref_picture(H264Context *h, H264Picture *dst, H264Picture *src) dst->hwaccel_picture_private = dst->hwaccel_priv_buf->data; } + ret = av_buffer_replace(&dst->decode_error_flags, src->decode_error_flags); + if (ret < 0) + goto fail; + h264_copy_picture_params(dst, src); return 0; @@ -186,6 +191,10 @@ int ff_h264_replace_picture(H264Context *h, H264Picture *dst, const H264Picture dst->hwaccel_picture_private = src->hwaccel_picture_private; + ret = av_buffer_replace(&dst->decode_error_flags, src->decode_error_flags); + if (ret < 0) + goto fail; + h264_copy_picture_params(dst, src); return 0; diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index f3af345c99..5657327f0c 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -210,6 +210,13 @@ static int alloc_picture(H264Context *h, H264Picture *pic) if (ret < 0) goto fail; + if (h->decode_error_flags_pool) { + pic->decode_error_flags = av_buffer_pool_get(h->decode_error_flags_pool); + if (!pic->decode_error_flags) + goto fail; + atomic_init((atomic_int*)pic->decode_error_flags->data, 0); + } + if (CONFIG_GRAY && !h->avctx->hwaccel && h->flags & AV_CODEC_FLAG_GRAY && pic->f->data[2]) { int h_chroma_shift, v_chroma_shift; av_pix_fmt_get_chroma_sub_sample(pic->f->format, diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c index 8e90678125..796f80be8d 100644 --- a/libavcodec/h264dec.c +++ b/libavcodec/h264dec.c @@ -307,6 +307,12 @@ static int h264_init_context(AVCodecContext *avctx, H264Context *h) ff_h264_sei_uninit(&h->sei); + if (avctx->active_thread_type & FF_THREAD_FRAME) { + h->decode_error_flags_pool = av_buffer_pool_init(sizeof(atomic_int), NULL); + if (!h->decode_error_flags_pool) + return AVERROR(ENOMEM); + } + h->nb_slice_ctx = (avctx->active_thread_type & FF_THREAD_SLICE) ? avctx->thread_count : 1; h->slice_ctx = av_calloc(h->nb_slice_ctx, sizeof(*h->slice_ctx)); if (!h->slice_ctx) { @@ -353,6 +359,8 @@ static av_cold int h264_decode_end(AVCodecContext *avctx) h->cur_pic_ptr = NULL; + av_buffer_pool_uninit(&h->decode_error_flags_pool); + av_freep(&h->slice_ctx); h->nb_slice_ctx = 0; @@ -739,7 +747,16 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) // set decode_error_flags to allow users to detect concealed decoding errors if ((ret < 0 || h->er.error_occurred) && h->cur_pic_ptr) { - h->cur_pic_ptr->f->decode_error_flags |= FF_DECODE_ERROR_DECODE_SLICES; + if (h->cur_pic_ptr->decode_error_flags) { + /* Frame-threading in use */ + atomic_int *decode_error = (atomic_int*)h->cur_pic_ptr->decode_error_flags->data; + /* Using atomics here is not supposed to provide syncronisation; + * they are merely used to allow to set decode_error from both + * decoding threads in case of coded slices. */ + atomic_fetch_or_explicit(decode_error, FF_DECODE_ERROR_DECODE_SLICES, + memory_order_relaxed); + } else + h->cur_pic_ptr->f->decode_error_flags |= FF_DECODE_ERROR_DECODE_SLICES; } ret = 0; @@ -762,6 +779,7 @@ end: H264SliceContext *sl = h->slice_ctx; int use_last_pic = h->last_pic_for_ec.f->buf[0] && !sl->ref_count[0]; + int decode_error_flags = 0; ff_h264_set_erpic(&h->er.cur_pic, h->cur_pic_ptr); @@ -779,7 +797,15 @@ end: if (sl->ref_count[1]) ff_h264_set_erpic(&h->er.next_pic, sl->ref_list[1][0].parent); - ff_er_frame_end(&h->er, NULL); + ff_er_frame_end(&h->er, &decode_error_flags); + if (decode_error_flags) { + if (h->cur_pic_ptr->decode_error_flags) { + atomic_int *decode_error = (atomic_int*)h->cur_pic_ptr->decode_error_flags->data; + atomic_fetch_or_explicit(decode_error, decode_error_flags, + memory_order_relaxed); + } else + h->cur_pic_ptr->f->decode_error_flags |= decode_error_flags; + } if (use_last_pic) memset(&sl->ref_list[0][0], 0, sizeof(sl->ref_list[0][0])); } @@ -851,6 +877,14 @@ static int output_frame(H264Context *h, AVFrame *dst, H264Picture *srcp) if (srcp->needs_fg && (ret = av_frame_copy_props(dst, srcp->f)) < 0) return ret; + if (srcp->decode_error_flags) { + atomic_int *decode_error = (atomic_int*)srcp->decode_error_flags->data; + /* The following is not supposed to provide synchronisation at all: + * given that srcp has already finished decoding, decode_error + * has already been set to its final value. */ + dst->decode_error_flags |= atomic_load_explicit(decode_error, memory_order_relaxed); + } + av_dict_set(&dst->metadata, "stereo_mode", ff_h264_sei_stereo_mode(&h->sei.common.frame_packing), 0); if (srcp->sei_recovery_frame_cnt == 0) diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h index beaab3902c..322c06a19c 100644 --- a/libavcodec/h264dec.h +++ b/libavcodec/h264dec.h @@ -152,6 +152,9 @@ typedef struct H264Picture { int mb_width, mb_height; int mb_stride; + + /* data points to an atomic_int */ + AVBufferRef *decode_error_flags; } H264Picture; typedef struct H264Ref { @@ -549,6 +552,7 @@ typedef struct H264Context { AVBufferPool *mb_type_pool; AVBufferPool *motion_val_pool; AVBufferPool *ref_index_pool; + AVBufferPool *decode_error_flags_pool; int ref2frm[MAX_SLICES][2][64]; ///< reference to frame number lists, used in the loop filter, the first 2 are for -2,-1 } H264Context;