[PR] avformat & h264: avoid-decode-for-parse (PR #24320)

michaelni via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
PR #24320 opened by michaelni
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24320
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24320.patch

Previously av_find_stream_info() did decode a h264 frame to obtain all needed info, 
this patchset attempts to extract that information without fully decoding frame(s)
this depends on all SPS matching as without a frame we cannot know which is correct otherwise


>From 54635d2abf4b792cedf4b53588d0d658fe95436b Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 29 Aug 2026 19:45:07 +0200
Subject: [PATCH 1/3] h264: factor the software pixel format choice out of
 get_pixel_format()

---
 libavcodec/h264_slice.c | 64 +++++------------------------------------
 libavcodec/h264dec.h    | 30 +++++++++++++++++++
 2 files changed, 37 insertions(+), 57 deletions(-)

diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index 9b5ed8f77e..95911cfdef 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -799,15 +799,6 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
 
     switch (h->ps.sps->bit_depth_luma) {
     case 9:
-        if (CHROMA444(h)) {
-            if (h->avctx->colorspace == AVCOL_SPC_RGB) {
-                *fmt++ = AV_PIX_FMT_GBRP9;
-            } else
-                *fmt++ = AV_PIX_FMT_YUV444P9;
-        } else if (CHROMA422(h))
-            *fmt++ = AV_PIX_FMT_YUV422P9;
-        else
-            *fmt++ = AV_PIX_FMT_YUV420P9;
         break;
     case 10:
 #if CONFIG_H264_VIDEOTOOLBOX_HWACCEL
@@ -823,47 +814,20 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
 #if CONFIG_H264_NVDEC_CUARRAY_HWACCEL
         *fmt++ = AV_PIX_FMT_CUARRAY;
 #endif
-        if (CHROMA444(h)) {
-            if (h->avctx->colorspace == AVCOL_SPC_RGB) {
-                *fmt++ = AV_PIX_FMT_GBRP10;
-            } else
-                *fmt++ = AV_PIX_FMT_YUV444P10;
-        } else if (CHROMA422(h))
-            *fmt++ = AV_PIX_FMT_YUV422P10;
-        else {
 #if CONFIG_H264_VAAPI_HWACCEL
-            // Just add as candidate. Whether VAProfileH264High10 usable or
-            // not is decided by vaapi_decode_make_config() defined in FFmpeg
-            // and vaQueryCodingProfile() defined in libva.
+        // Just add as candidate. Whether VAProfileH264High10 usable or
+        // not is decided by vaapi_decode_make_config() defined in FFmpeg
+        // and vaQueryCodingProfile() defined in libva.
+        if (!CHROMA444(h) && !CHROMA422(h))
             *fmt++ = AV_PIX_FMT_VAAPI;
 #endif
-            *fmt++ = AV_PIX_FMT_YUV420P10;
-        }
         break;
     case 12:
 #if CONFIG_H264_VULKAN_HWACCEL
         *fmt++ = AV_PIX_FMT_VULKAN;
 #endif
-        if (CHROMA444(h)) {
-            if (h->avctx->colorspace == AVCOL_SPC_RGB) {
-                *fmt++ = AV_PIX_FMT_GBRP12;
-            } else
-                *fmt++ = AV_PIX_FMT_YUV444P12;
-        } else if (CHROMA422(h))
-            *fmt++ = AV_PIX_FMT_YUV422P12;
-        else
-            *fmt++ = AV_PIX_FMT_YUV420P12;
         break;
     case 14:
-        if (CHROMA444(h)) {
-            if (h->avctx->colorspace == AVCOL_SPC_RGB) {
-                *fmt++ = AV_PIX_FMT_GBRP14;
-            } else
-                *fmt++ = AV_PIX_FMT_YUV444P14;
-        } else if (CHROMA422(h))
-            *fmt++ = AV_PIX_FMT_YUV422P14;
-        else
-            *fmt++ = AV_PIX_FMT_YUV420P14;
         break;
     case 8:
 #if CONFIG_H264_VDPAU_HWACCEL
@@ -882,19 +846,7 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
         if (h->avctx->colorspace != AVCOL_SPC_RGB)
             *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX;
 #endif
-        if (CHROMA444(h)) {
-            if (h->avctx->colorspace == AVCOL_SPC_RGB)
-                *fmt++ = AV_PIX_FMT_GBRP;
-            else if (h->avctx->color_range == AVCOL_RANGE_JPEG)
-                *fmt++ = AV_PIX_FMT_YUVJ444P;
-            else
-                *fmt++ = AV_PIX_FMT_YUV444P;
-        } else if (CHROMA422(h)) {
-            if (h->avctx->color_range == AVCOL_RANGE_JPEG)
-                *fmt++ = AV_PIX_FMT_YUVJ422P;
-            else
-                *fmt++ = AV_PIX_FMT_YUV422P;
-        } else {
+        if (!CHROMA444(h) && !CHROMA422(h)) {
 #if CONFIG_H264_DXVA2_HWACCEL
             *fmt++ = AV_PIX_FMT_DXVA2_VLD;
 #endif
@@ -908,10 +860,6 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
 #if CONFIG_H264_VAAPI_HWACCEL
             *fmt++ = AV_PIX_FMT_VAAPI;
 #endif
-            if (h->avctx->color_range == AVCOL_RANGE_JPEG)
-                *fmt++ = AV_PIX_FMT_YUVJ420P;
-            else
-                *fmt++ = AV_PIX_FMT_YUV420P;
         }
         break;
     default:
@@ -920,6 +868,8 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback)
         return AVERROR_INVALIDDATA;
     }
 
+    *fmt++ = h264_sw_pix_fmt(h->ps.sps, h->avctx);
+
     *fmt = AV_PIX_FMT_NONE;
 
     for (int i = 0; pix_fmts[i] != AV_PIX_FMT_NONE; i++)
diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h
index 74fd09dfaa..5817f5381f 100644
--- a/libavcodec/h264dec.h
+++ b/libavcodec/h264dec.h
@@ -703,4 +703,34 @@ void ff_h264_free_tables(H264Context *h);
 
 void ff_h264_set_erpic(ERPicture *dst, const H264Picture *src);
 
+static inline enum AVPixelFormat h264_sw_pix_fmt(const SPS *sps,
+                                                 const AVCodecContext *avctx)
+{
+    const int chroma444 = sps->chroma_format_idc == 3;
+    const int chroma422 = sps->chroma_format_idc == 2;
+    const int rgb       = avctx->colorspace  == AVCOL_SPC_RGB;
+    const int jpeg      = avctx->color_range == AVCOL_RANGE_JPEG;
+
+    switch (sps->bit_depth_luma) {
+    case 9:
+        return chroma444 ? (rgb ? AV_PIX_FMT_GBRP9  : AV_PIX_FMT_YUV444P9)  :
+               chroma422 ?        AV_PIX_FMT_YUV422P9  : AV_PIX_FMT_YUV420P9;
+    case 10:
+        return chroma444 ? (rgb ? AV_PIX_FMT_GBRP10 : AV_PIX_FMT_YUV444P10) :
+               chroma422 ?        AV_PIX_FMT_YUV422P10 : AV_PIX_FMT_YUV420P10;
+    case 12:
+        return chroma444 ? (rgb ? AV_PIX_FMT_GBRP12 : AV_PIX_FMT_YUV444P12) :
+               chroma422 ?        AV_PIX_FMT_YUV422P12 : AV_PIX_FMT_YUV420P12;
+    case 14:
+        return chroma444 ? (rgb ? AV_PIX_FMT_GBRP14 : AV_PIX_FMT_YUV444P14) :
+               chroma422 ?        AV_PIX_FMT_YUV422P14 : AV_PIX_FMT_YUV420P14;
+    case 8:
+        return chroma444 ? (rgb  ? AV_PIX_FMT_GBRP     :
+                            jpeg ? AV_PIX_FMT_YUVJ444P : AV_PIX_FMT_YUV444P) :
+               chroma422 ? (jpeg ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV422P) :
+                           (jpeg ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_YUV420P);
+    }
+    return AV_PIX_FMT_NONE;
+}
+
 #endif /* AVCODEC_H264DEC_H */
-- 
2.52.0


>From 6e79ab0fb1c99a1a75bf63daaaa17452a4e36108 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 29 Aug 2026 15:13:52 +0200
Subject: [PATCH 2/3] h264dec: export stream parameters from the extradata at
 init

avformat_find_stream_info() then sees the complete parameter set
without decoding a frame.
---
 libavcodec/h264dec.c | 70 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 66 insertions(+), 4 deletions(-)

diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index b78b7989ea..09d1153b2f 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -59,6 +59,71 @@
 
 const uint16_t ff_h264_mb_sizes[4] = { 256, 384, 512, 768 };
 
+static const SPS *h264_effective_sps(const H264ParamSets *ps)
+{
+    const SPS *sps = ps->sps;
+
+    if (sps)
+        return sps;
+
+    for (int i = 0; i < MAX_SPS_COUNT; i++) {
+        const SPS *s = ps->sps_list[i];
+        if (!s)
+            continue;
+        if (!sps) {
+            sps = s;
+            continue;
+        }
+        if (s->chroma_format_idc          != sps->chroma_format_idc          ||
+            s->bit_depth_luma             != sps->bit_depth_luma             ||
+            s->level_idc                  != sps->level_idc                  ||
+            ff_h264_get_profile(s)        != ff_h264_get_profile(sps)        ||
+            s->bitstream_restriction_flag != sps->bitstream_restriction_flag ||
+            s->num_reorder_frames         != sps->num_reorder_frames         ||
+            s->vui.sar.num                != sps->vui.sar.num                ||
+            s->vui.sar.den                != sps->vui.sar.den                ||
+            s->vui.video_signal_type_present_flag  != sps->vui.video_signal_type_present_flag  ||
+            s->vui.video_full_range_flag           != sps->vui.video_full_range_flag           ||
+            s->vui.colour_description_present_flag != sps->vui.colour_description_present_flag ||
+            s->vui.colour_primaries       != sps->vui.colour_primaries       ||
+            s->vui.transfer_characteristics != sps->vui.transfer_characteristics ||
+            s->vui.matrix_coeffs          != sps->vui.matrix_coeffs          ||
+            s->vui.chroma_location        != sps->vui.chroma_location)
+            return NULL;
+    }
+    return sps;
+}
+
+static void h264_export_extradata_params(AVCodecContext *avctx, const H264Context *h)
+{
+    const SPS *sps = h264_effective_sps(&h->ps);
+
+    if (!sps)
+        return;
+
+    avctx->profile = ff_h264_get_profile(sps);
+    avctx->level   = sps->level_idc;
+    if (sps->vui.video_signal_type_present_flag) {
+        avctx->color_range = sps->vui.video_full_range_flag > 0 ? AVCOL_RANGE_JPEG
+                                                                : AVCOL_RANGE_MPEG;
+        if (sps->vui.colour_description_present_flag) {
+            avctx->color_primaries = sps->vui.colour_primaries;
+            avctx->color_trc       = sps->vui.transfer_characteristics;
+            avctx->colorspace      = sps->vui.matrix_coeffs;
+        }
+    }
+    avctx->chroma_sample_location = sps->vui.chroma_location;
+    avctx->bits_per_raw_sample    = sps->bit_depth_luma;
+    if (!avctx->sample_aspect_ratio.num && sps->vui.sar.num > 0)
+        avctx->sample_aspect_ratio = sps->vui.sar;
+    if (sps->bitstream_restriction_flag &&
+        avctx->has_b_frames < sps->num_reorder_frames)
+        avctx->has_b_frames = sps->num_reorder_frames;
+    if (avctx->pix_fmt == AV_PIX_FMT_NONE &&
+        avctx->get_format == avcodec_default_get_format)
+        avctx->pix_fmt = h264_sw_pix_fmt(sps, avctx);
+}
+
 int avpriv_h264_has_num_reorder_frames(AVCodecContext *avctx)
 {
     H264Context *h = avctx->priv_data;
@@ -413,10 +478,7 @@ static av_cold int h264_decode_init(AVCodecContext *avctx)
         }
     }
 
-    if (h->ps.sps && h->ps.sps->bitstream_restriction_flag &&
-        h->avctx->has_b_frames < h->ps.sps->num_reorder_frames) {
-        h->avctx->has_b_frames = h->ps.sps->num_reorder_frames;
-    }
+    h264_export_extradata_params(avctx, h);
 
     ff_h264_flush_change(h);
 
-- 
2.52.0


>From 36a1338569eb3a7fd98ad5a6c051432e6a890335 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 29 Aug 2026 15:13:52 +0200
Subject: [PATCH 3/3] avformat/demux: settle the h264 reorder-delay guess
 without decoding

---
 libavcodec/h264dec.c | 4 +++-
 libavformat/demux.c  | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index 09d1153b2f..8c2878e03f 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -127,7 +127,9 @@ static void h264_export_extradata_params(AVCodecContext *avctx, const H264Contex
 int avpriv_h264_has_num_reorder_frames(AVCodecContext *avctx)
 {
     H264Context *h = avctx->priv_data;
-    return h && h->ps.sps ? h->ps.sps->num_reorder_frames : 0;
+    const SPS *sps = h ? h264_effective_sps(&h->ps) : NULL;
+
+    return sps ? sps->num_reorder_frames : 0;
 }
 
 static void h264_er_decode_mb(void *opaque, int ref, int mv_dir, int mv_type,
diff --git a/libavformat/demux.c b/libavformat/demux.c
index da0f5853cc..c789a06bdf 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -764,7 +764,7 @@ static int has_decode_delay_been_guessed(AVStream *st)
         return 1;
     av_assert0(sti->avctx->codec_id == AV_CODEC_ID_H264 || (sti->avctx->codec_id == AV_CODEC_ID_NONE && !avcodec_is_open(sti->avctx)));
 #if CONFIG_H264_DECODER
-    if (sti->avctx->has_b_frames && avcodec_is_open(sti->avctx) &&
+    if (avcodec_is_open(sti->avctx) &&
         avpriv_h264_has_num_reorder_frames(sti->avctx) == sti->avctx->has_b_frames)
         return 1;
 #endif
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.