Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH v3 3/3] avcodec/hevc: Add support for output_corrupt/showall flags
@ 2025-01-23 14:12 Zhao Zhili
  0 siblings, 0 replies; only message in thread
From: Zhao Zhili @ 2025-01-23 14:12 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Zhao Zhili

From: Zhao Zhili <zhilizhao@tencent.com>

Also handle gradual decoding refresh stream.
---
I can't find a perfect hevc intra refresh stream.

1. x265 doesn't output useful recovery point SEI. It only output a single recovery
point SEI with recovery_poc_cnt equal to 0 together with IDR.

2. With patch 1/3, nvenc output SEI with recovery_poc_cnt only about half the size
of required value. It looks like the encoder set recovery_poc_cnt = height / 64,
but encoded with intra block CTU size 32.

I would be very grateful if anyone could provide a test file that meets the standards.

 libavcodec/hevc/hevcdec.c | 22 +++++++++++++++++++++-
 libavcodec/hevc/hevcdec.h |  6 ++++++
 libavcodec/hevc/refs.c    | 24 +++++++++++++++++++++++-
 libavcodec/hevc/sei.c     |  1 +
 libavcodec/hevc/sei.h     |  1 +
 tests/fate/hevc.mak       |  4 ++--
 6 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/libavcodec/hevc/hevcdec.c b/libavcodec/hevc/hevcdec.c
index 79dd63092c..098ec4a9b4 100644
--- a/libavcodec/hevc/hevcdec.c
+++ b/libavcodec/hevc/hevcdec.c
@@ -3228,9 +3228,19 @@ static int hevc_frame_start(HEVCContext *s, HEVCLayerContext *l,
     s->first_nal_type    = s->nal_unit_type;
     s->poc               = s->sh.poc;
 
-    if (IS_IRAP(s))
+    if (IS_IRAP(s)) {
         s->no_rasl_output_flag = IS_IDR(s) || IS_BLA(s) ||
                                  (s->nal_unit_type == HEVC_NAL_CRA_NUT && s->last_eos);
+        s->recovery_poc = HEVC_RECOVERY_END;
+    }
+
+    if (s->recovery_poc != HEVC_RECOVERY_END &&
+        s->sei.recovery_point.has_recovery_poc) {
+        if (s->recovery_poc == HEVC_RECOVERY_UNSPECIFIED)
+            s->recovery_poc = s->poc + s->sei.recovery_point.recovery_poc_cnt;
+        else if (s->poc >= s->recovery_poc)
+            s->recovery_poc = HEVC_RECOVERY_END;
+    }
 
     /* 8.3.1 */
     if (s->temporal_id == 0 &&
@@ -3606,6 +3616,12 @@ fail:
     return ret;
 }
 
+static void decode_reset_recovery_point(HEVCContext *s)
+{
+    s->recovery_poc = HEVC_RECOVERY_UNSPECIFIED;
+    s->sei.recovery_point.has_recovery_poc = 0;
+}
+
 static int decode_nal_units(HEVCContext *s, const uint8_t *buf, int length)
 {
     int i, ret = 0;
@@ -3616,6 +3632,8 @@ static int decode_nal_units(HEVCContext *s, const uint8_t *buf, int length)
     s->last_eos = s->eos;
     s->eos = 0;
     s->slice_initialized = 0;
+    if (s->last_eos)
+        decode_reset_recovery_point(s);
 
     for (int i = 0; i < FF_ARRAY_ELEMS(s->layers); i++) {
         HEVCLayerContext *l = &s->layers[i];
@@ -3637,6 +3655,7 @@ static int decode_nal_units(HEVCContext *s, const uint8_t *buf, int length)
             s->pkt.nals[i].type == HEVC_NAL_EOS_NUT) {
             if (eos_at_start) {
                 s->last_eos = 1;
+                decode_reset_recovery_point(s);
             } else {
                 s->eos = 1;
             }
@@ -4010,6 +4029,7 @@ static int hevc_update_thread_context(AVCodecContext *dst,
     s->sei.common.alternative_transfer = s0->sei.common.alternative_transfer;
     s->sei.tdrdi                       = s0->sei.tdrdi;
     s->sei.recovery_point              = s0->sei.recovery_point;
+    s->recovery_poc                    = s0->recovery_poc;
 
     return 0;
 }
diff --git a/libavcodec/hevc/hevcdec.h b/libavcodec/hevc/hevcdec.h
index 4e95035688..d8c8c0d011 100644
--- a/libavcodec/hevc/hevcdec.h
+++ b/libavcodec/hevc/hevcdec.h
@@ -78,6 +78,10 @@
                    (s)->nal_unit_type == HEVC_NAL_BLA_N_LP)
 #define IS_IRAP(s) ((s)->nal_unit_type >= HEVC_NAL_BLA_W_LP && (s)->nal_unit_type <= HEVC_NAL_RSV_IRAP_VCL23)
 
+#define HEVC_RECOVERY_UNSPECIFIED INT_MAX
+#define HEVC_RECOVERY_END INT_MIN
+#define HEVC_IS_RECOVERING(s) ((s)->recovery_poc != HEVC_RECOVERY_UNSPECIFIED && (s)->recovery_poc != HEVC_RECOVERY_END)
+
 enum RPSType {
     ST_CURR_BEF = 0,
     ST_CURR_AFT,
@@ -353,6 +357,7 @@ typedef struct DBParams {
 #define HEVC_FRAME_FLAG_SHORT_REF (1 << 1)
 #define HEVC_FRAME_FLAG_LONG_REF  (1 << 2)
 #define HEVC_FRAME_FLAG_UNAVAILABLE (1 << 3)
+#define HEVC_FRAME_FLAG_CORRUPT (1 << 4)
 
 typedef struct HEVCFrame {
     union {
@@ -523,6 +528,7 @@ typedef struct HEVCContext {
     int slice_idx; ///< number of the slice being currently decoded
     int eos;       ///< current packet contains an EOS/EOB NAL
     int last_eos;  ///< last packet contains an EOS/EOB NAL
+    int recovery_poc;
 
     // NoRaslOutputFlag associated with the last IRAP frame
     int no_rasl_output_flag;
diff --git a/libavcodec/hevc/refs.c b/libavcodec/hevc/refs.c
index dd7f7f95a8..c5d0ad4be7 100644
--- a/libavcodec/hevc/refs.c
+++ b/libavcodec/hevc/refs.c
@@ -34,6 +34,8 @@
 void ff_hevc_unref_frame(HEVCFrame *frame, int flags)
 {
     frame->flags &= ~flags;
+    if (!(frame->flags & ~HEVC_FRAME_FLAG_CORRUPT))
+        frame->flags = 0;
     if (!frame->flags) {
         ff_progress_frame_unref(&frame->tf);
         av_frame_unref(frame->frame_grain);
@@ -176,6 +178,7 @@ int ff_hevc_set_new_ref(HEVCContext *s, HEVCLayerContext *l, int poc)
 {
     HEVCFrame *ref;
     int i;
+    int no_output;
 
     /* check that this POC doesn't already exist */
     for (i = 0; i < FF_ARRAY_ELEMS(l->DPB); i++) {
@@ -199,7 +202,10 @@ int ff_hevc_set_new_ref(HEVCContext *s, HEVCLayerContext *l, int poc)
     ref->base_layer_frame = (l != &s->layers[0] && s->layers[0].cur_frame) ?
                             s->layers[0].cur_frame - s->layers[0].DPB : -1;
 
-    if (s->sh.pic_output_flag)
+    no_output = !IS_IRAP(s) && (s->poc < s->recovery_poc) &&
+                !(s->avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT) &&
+                !(s->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL);
+    if (s->sh.pic_output_flag && !no_output)
         ref->flags = HEVC_FRAME_FLAG_OUTPUT | HEVC_FRAME_FLAG_SHORT_REF;
     else
         ref->flags = HEVC_FRAME_FLAG_SHORT_REF;
@@ -266,6 +272,8 @@ int ff_hevc_output_frames(HEVCContext *s,
             int output = !discard && (layers_active_output & (1 << min_layer));
 
             if (output) {
+                if (frame->flags & HEVC_FRAME_FLAG_CORRUPT)
+                    f->flags |= AV_FRAME_FLAG_CORRUPT;
                 f->pkt_dts = s->pkt_dts;
                 ret = av_container_fifo_write(s->output_fifo, f, AV_CONTAINER_FIFO_FLAG_REF);
             }
@@ -462,6 +470,20 @@ static int add_candidate_ref(HEVCContext *s, HEVCLayerContext *l,
     if (ref == s->cur_frame || list->nb_refs >= HEVC_MAX_REFS)
         return AVERROR_INVALIDDATA;
 
+    if (!IS_IRAP(s)) {
+        int ref_corrupt = !ref || ref->flags & (HEVC_FRAME_FLAG_CORRUPT |
+                                                HEVC_FRAME_FLAG_UNAVAILABLE);
+        int recovering = HEVC_IS_RECOVERING(s);
+
+        if (ref_corrupt && !recovering) {
+            if (!(s->avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT) &&
+                !(s->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL))
+                return AVERROR_INVALIDDATA;
+
+            s->cur_frame->flags |= HEVC_FRAME_FLAG_CORRUPT;
+        }
+    }
+
     if (!ref) {
         ref = generate_missing_ref(s, l, poc);
         if (!ref)
diff --git a/libavcodec/hevc/sei.c b/libavcodec/hevc/sei.c
index 9c3594ac2f..b8e98cde89 100644
--- a/libavcodec/hevc/sei.c
+++ b/libavcodec/hevc/sei.c
@@ -89,6 +89,7 @@ static int decode_nal_sei_recovery_point(HEVCSEI *s, GetBitContext *gb)
     rec->recovery_poc_cnt = recovery_poc_cnt;
     rec->exact_match_flag = get_bits1(gb);
     rec->broken_link_flag = get_bits1(gb);
+    rec->has_recovery_poc = 1;
 
     return 0;
 }
diff --git a/libavcodec/hevc/sei.h b/libavcodec/hevc/sei.h
index 7e1a2b0756..c4714bb7c5 100644
--- a/libavcodec/hevc/sei.h
+++ b/libavcodec/hevc/sei.h
@@ -99,6 +99,7 @@ typedef struct HEVCSEIRecoveryPoint {
     int16_t recovery_poc_cnt;
     uint8_t exact_match_flag;
     uint8_t broken_link_flag;
+    uint8_t has_recovery_poc;
 } HEVCSEIRecoveryPoint;
 
 typedef struct HEVCSEI {
diff --git a/tests/fate/hevc.mak b/tests/fate/hevc.mak
index 9e6fd72618..99f8aa83b5 100644
--- a/tests/fate/hevc.mak
+++ b/tests/fate/hevc.mak
@@ -207,7 +207,7 @@ $(HEVC_TESTS_444_8BIT): SCALE_OPTS := -pix_fmt yuv444p
 $(HEVC_TESTS_10BIT): SCALE_OPTS := -pix_fmt yuv420p10le -vf scale
 $(HEVC_TESTS_422_10BIT) $(HEVC_TESTS_422_10BIN): SCALE_OPTS := -pix_fmt yuv422p10le -vf scale
 $(HEVC_TESTS_444_12BIT): SCALE_OPTS := -pix_fmt yuv444p12le -vf scale
-fate-hevc-conformance-%: CMD = framecrc -i $(TARGET_SAMPLES)/hevc-conformance/$(subst fate-hevc-conformance-,,$(@)).bit $(SCALE_OPTS)
+fate-hevc-conformance-%: CMD = framecrc -flags output_corrupt -i $(TARGET_SAMPLES)/hevc-conformance/$(subst fate-hevc-conformance-,,$(@)).bit $(SCALE_OPTS)
 $(HEVC_TESTS_422_10BIN): CMD = framecrc -i $(TARGET_SAMPLES)/hevc-conformance/$(subst fate-hevc-conformance-,,$(@)).bin $(SCALE_OPTS)
 $(HEVC_TESTS_MULTIVIEW): CMD = framecrc -i $(TARGET_SAMPLES)/hevc-conformance/$(subst fate-hevc-conformance-,,$(@)).bit \
 	-pix_fmt yuv420p -map "0:view:0" -map "0:view:1" -vf setpts=N
@@ -248,7 +248,7 @@ FATE_HEVC_FFPROBE-$(call DEMDEC, HEVC, HEVC) += fate-hevc-paired-fields
 fate-hevc-monochrome-crop: CMD = probeframes -show_entries frame=width,height:stream=width,height $(TARGET_SAMPLES)/hevc/hevc-monochrome.hevc
 FATE_HEVC_FFPROBE-$(call PARSERDEMDEC, HEVC, HEVC, HEVC) += fate-hevc-monochrome-crop
 
-fate-hevc-afd-tc-sei: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -bitexact -show_entries frame_side_data_list -select_streams v $(TARGET_SAMPLES)/mpegts/loewe.ts
+fate-hevc-afd-tc-sei: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -bitexact -flags output_corrupt -show_entries frame_side_data_list -select_streams v $(TARGET_SAMPLES)/mpegts/loewe.ts
 FATE_HEVC_FFPROBE-$(call PARSERDEMDEC, HEVC, HEVC, HEVC) += fate-hevc-afd-tc-sei
 
 fate-hevc-hdr10-plus-metadata: CMD = probeframes -show_entries frame=side_data_list $(TARGET_SAMPLES)/hevc/hdr10_plus_h265_sample.hevc
-- 
2.46.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2025-01-23 14:12 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-01-23 14:12 [FFmpeg-devel] [PATCH v3 3/3] avcodec/hevc: Add support for output_corrupt/showall flags Zhao Zhili

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git