* [FFmpeg-devel] [PATCH v2 2/3] lavc: Add test for H.265 profile handling
2024-05-06 18:47 [FFmpeg-devel] [PATCH v2 1/3] lavc/h265_profile_level: Expand profile compatibility checking Mark Thompson
@ 2024-05-06 18:47 ` Mark Thompson
2024-05-06 18:47 ` [FFmpeg-devel] [PATCH v2 3/3] lavc/vaapi_hevc: Don't require exact profiles Mark Thompson
2024-05-06 19:56 ` [FFmpeg-devel] [PATCH v2 1/3] lavc/h265_profile_level: Expand profile compatibility checking Andreas Rheinhardt
2 siblings, 0 replies; 5+ messages in thread
From: Mark Thompson @ 2024-05-06 18:47 UTC (permalink / raw)
To: ffmpeg-devel
---
libavcodec/Makefile | 2 +-
libavcodec/tests/.gitignore | 1 +
libavcodec/tests/h265_profiles.c | 440 +++++++++++++++++++++++++++++++
tests/fate/libavcodec.mak | 5 +
4 files changed, 447 insertions(+), 1 deletion(-)
create mode 100644 libavcodec/tests/h265_profiles.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index cff6347bdb..15e38ded28 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1307,7 +1307,7 @@ TESTPROGS-$(CONFIG_MJPEG_ENCODER) += mjpegenc_huffman
TESTPROGS-$(HAVE_MMX) += motion
TESTPROGS-$(CONFIG_MPEGVIDEO) += mpeg12framerate
TESTPROGS-$(CONFIG_H264_METADATA_BSF) += h264_levels
-TESTPROGS-$(CONFIG_HEVC_METADATA_BSF) += h265_levels
+TESTPROGS-$(CONFIG_HEVC_METADATA_BSF) += h265_levels h265_profiles
TESTPROGS-$(CONFIG_RANGECODER) += rangecoder
TESTPROGS-$(CONFIG_SNOW_ENCODER) += snowenc
diff --git a/libavcodec/tests/.gitignore b/libavcodec/tests/.gitignore
index 0df4ae10a0..bf29f03911 100644
--- a/libavcodec/tests/.gitignore
+++ b/libavcodec/tests/.gitignore
@@ -10,6 +10,7 @@
/golomb
/h264_levels
/h265_levels
+/h265_profiles
/htmlsubtitles
/iirfilter
/jpeg2000dwt
diff --git a/libavcodec/tests/h265_profiles.c b/libavcodec/tests/h265_profiles.c
new file mode 100644
index 0000000000..6a0df58d0b
--- /dev/null
+++ b/libavcodec/tests/h265_profiles.c
@@ -0,0 +1,440 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/bprint.h"
+#include "libavutil/common.h"
+#include "libavcodec/h265_profile_level.h"
+
+
+enum {
+ TYPE_MAIN,
+ TYPE_SP,
+ TYPE_REXT_INTER,
+ TYPE_REXT_INTRA,
+ TYPE_HT,
+ TYPE_HT_INTRA,
+ TYPE_SCC,
+ TYPE_HT_SCC,
+ TYPE_COUNT,
+};
+enum {
+ DEPTH_8,
+ DEPTH_10,
+ DEPTH_12,
+ DEPTH_14,
+ DEPTH_16,
+ DEPTH_COUNT,
+};
+enum {
+ CHROMA_MONO,
+ CHROMA_420,
+ CHROMA_422,
+ CHROMA_444,
+ CHROMA_COUNT,
+};
+
+// Table of all profiles indexed by chroma subsampling, bit depth and
+// profile type. This is currently only being used to verify that
+// profile properties are correct, but if there is some other need for
+// this lookup in lavc then the table should be moved to the common
+// profile-level code. All profiles should appear exactly once in this
+// table (also verified by a test below).
+static int profile_table[CHROMA_COUNT][DEPTH_COUNT][TYPE_COUNT] = {
+ [CHROMA_MONO] = {
+ [DEPTH_8] = {
+ [TYPE_REXT_INTER] = H265_PROFILE_MONOCHROME,
+ },
+ [DEPTH_10] = {
+ [TYPE_REXT_INTER] = H265_PROFILE_MONOCHROME_10,
+ },
+ [DEPTH_12] = {
+ [TYPE_REXT_INTER] = H265_PROFILE_MONOCHROME_12,
+ },
+ [DEPTH_16] = {
+ [TYPE_REXT_INTER] = H265_PROFILE_MONOCHROME_16,
+ },
+ },
+ [CHROMA_420] = {
+ [DEPTH_8] = {
+ [TYPE_MAIN] = H265_PROFILE_MAIN,
+ [TYPE_SP] = H265_PROFILE_MAIN_STILL_PICTURE,
+ [TYPE_REXT_INTRA] = H265_PROFILE_MAIN_INTRA,
+ [TYPE_SCC] = H265_PROFILE_SCREEN_EXTENDED_MAIN,
+ },
+ [DEPTH_10] = {
+ [TYPE_MAIN] = H265_PROFILE_MAIN_10,
+ [TYPE_SP] = H265_PROFILE_MAIN_10_STILL_PICTURE,
+ [TYPE_REXT_INTRA] = H265_PROFILE_MAIN_10_INTRA,
+ [TYPE_SCC] = H265_PROFILE_SCREEN_EXTENDED_MAIN_10,
+ },
+ [DEPTH_12] = {
+ [TYPE_REXT_INTER] = H265_PROFILE_MAIN_12,
+ [TYPE_REXT_INTRA] = H265_PROFILE_MAIN_12_INTRA,
+ },
+ },
+ [CHROMA_422] ={
+ [DEPTH_10] = {
+ [TYPE_REXT_INTER] = H265_PROFILE_MAIN_422_10,
+ [TYPE_REXT_INTRA] = H265_PROFILE_MAIN_422_10_INTRA,
+ },
+ [DEPTH_12] = {
+ [TYPE_REXT_INTER] = H265_PROFILE_MAIN_422_12,
+ [TYPE_REXT_INTRA] = H265_PROFILE_MAIN_422_12_INTRA,
+ },
+ },
+ [CHROMA_444] ={
+ [DEPTH_8] = {
+ [TYPE_SP] = H265_PROFILE_MAIN_444_STILL_PICTURE,
+ [TYPE_REXT_INTER] = H265_PROFILE_MAIN_444,
+ [TYPE_REXT_INTRA] = H265_PROFILE_MAIN_444_INTRA,
+ [TYPE_HT] = H265_PROFILE_HIGH_THROUGHPUT_444,
+ [TYPE_SCC] = H265_PROFILE_SCREEN_EXTENDED_MAIN_444,
+ [TYPE_HT_SCC] = H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444,
+ },
+ [DEPTH_10] = {
+ [TYPE_REXT_INTER] = H265_PROFILE_MAIN_444_10,
+ [TYPE_REXT_INTRA] = H265_PROFILE_MAIN_444_10_INTRA,
+ [TYPE_HT] = H265_PROFILE_HIGH_THROUGHPUT_444_10,
+ [TYPE_SCC] = H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10,
+ [TYPE_HT_SCC] = H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_10,
+ },
+ [DEPTH_12] = {
+ [TYPE_REXT_INTER] = H265_PROFILE_MAIN_444_12,
+ [TYPE_REXT_INTRA] = H265_PROFILE_MAIN_444_12_INTRA,
+ },
+ [DEPTH_14] = {
+ [TYPE_HT] = H265_PROFILE_HIGH_THROUGHPUT_444_14,
+ [TYPE_HT_SCC] = H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_14,
+ },
+ [DEPTH_16] = {
+ [TYPE_SP] = H265_PROFILE_MAIN_444_16_STILL_PICTURE,
+ [TYPE_REXT_INTRA] = H265_PROFILE_MAIN_444_16_INTRA,
+ [TYPE_HT_INTRA] = H265_PROFILE_HIGH_THROUGHPUT_444_16_INTRA,
+ },
+ },
+};
+
+static int check_flags(const H265ProfileDescriptor *desc,
+ int chroma, int depth)
+{
+ int errors = 0;
+ if (chroma > CHROMA_MONO && desc->max_monochrome == 1) {
+ av_log(NULL, AV_LOG_ERROR, "%s requires monochrome.\n",
+ desc->name);
+ ++errors;
+ }
+ if (chroma > CHROMA_422 && desc->max_420chroma == 1) {
+ av_log(NULL, AV_LOG_ERROR, "%s requires 4:2:0.\n",
+ desc->name);
+ ++errors;
+ }
+ if (chroma > CHROMA_444 && desc->max_422chroma == 1) {
+ av_log(NULL, AV_LOG_ERROR, "%s requires 4:2:2.\n",
+ desc->name);
+ ++errors;
+ }
+ if (depth > DEPTH_8 && desc->max_8bit == 1) {
+ av_log(NULL, AV_LOG_ERROR, "%s requires 8-bit.\n",
+ desc->name);
+ ++errors;
+ }
+ if (depth > DEPTH_10 && desc->max_10bit == 1) {
+ av_log(NULL, AV_LOG_ERROR, "%s requires 10-bit.\n",
+ desc->name);
+ ++errors;
+ }
+ if (depth > DEPTH_12 && desc->max_12bit == 1) {
+ av_log(NULL, AV_LOG_ERROR, "%s requires 12-bit.\n",
+ desc->name);
+ ++errors;
+ }
+ if (depth > DEPTH_14 && desc->max_14bit == 1) {
+ av_log(NULL, AV_LOG_ERROR, "%s requires 14-bit.\n",
+ desc->name);
+ ++errors;
+ }
+ return errors;
+}
+
+static int check_profile_table(void)
+{
+ // Ensure that the profile table contains every non-still-picture
+ // profile exactly once, and that a profile for a given chroma mode
+ // and depth actually supports that chroma mode and depth.
+ int errors = 0;
+
+ for (int p = H265_PROFILE_MONOCHROME; p < H265_PROFILE_COUNT; p++) {
+ const H265ProfileDescriptor *desc = ff_h265_get_profile(p);
+ int found = 0;
+
+ for (int type= 0; type < TYPE_COUNT; type++) {
+ for (int chroma = 0; chroma < CHROMA_COUNT; chroma++) {
+ for (int depth = 0; depth < DEPTH_COUNT; depth++) {
+ if (profile_table[chroma][depth][type] == p) {
+ ++found;
+ errors += check_flags(desc, chroma, depth);
+ }
+ }
+ }
+ }
+
+ if (found != 1) {
+ av_log(NULL, AV_LOG_ERROR,
+ "%s appears %d times in the profile table.\n",
+ desc->name, found);
+
+ ++errors;
+ }
+ }
+ return errors;
+}
+
+static int get_profile(int type, int chroma, int depth)
+{
+ av_assert0(type >= 0 && type < TYPE_COUNT);
+ av_assert0(chroma >= 0 && chroma < CHROMA_COUNT);
+ av_assert0(depth >= 0 && depth < DEPTH_COUNT);
+ return profile_table[chroma][depth][type];
+}
+
+static void minimal_ptl_from_desc(H265RawProfileTierLevel *ptl,
+ const H265ProfileDescriptor *desc)
+{
+ *ptl = (H265RawProfileTierLevel) {
+ .general_profile_space = 0,
+ .general_profile_idc = desc->profile_idc,
+ // Tier/interlace/stereo/layering/level flags are ignored in
+ // this test.
+ };
+
+ ptl->general_profile_compatibility_flag[desc->profile_idc] = 1;
+
+#define FLAG(name) do { \
+ ptl->general_ ## name ## _constraint_flag = desc->name; \
+ } while (0)
+
+ FLAG(max_14bit);
+ FLAG(max_12bit);
+ FLAG(max_10bit);
+ FLAG(max_8bit);
+ FLAG(max_422chroma);
+ FLAG(max_420chroma);
+ FLAG(max_monochrome);
+ FLAG(intra);
+ FLAG(one_picture_only);
+ FLAG(lower_bit_rate);
+
+#undef FLAG
+}
+
+static void bprint_ptl(AVBPrint *buf, const H265RawProfileTierLevel *ptl)
+{
+ av_bprintf(buf, "profile_space %d tier %d profile_idc %d",
+ ptl->general_profile_space,
+ ptl->general_tier_flag,
+ ptl->general_profile_idc);
+
+ av_bprintf(buf, " profile_compatibility { ");
+ for (int i = 0; i < 32; i++)
+ av_bprintf(buf, "%d",
+ ptl->general_profile_compatibility_flag[i]);
+ av_bprintf(buf, " }");
+
+ av_bprintf(buf, " progressive %d interlaced %d",
+ ptl->general_progressive_source_flag,
+ ptl->general_interlaced_source_flag);
+ av_bprintf(buf, " non_packed %d frame_only %d",
+ ptl->general_non_packed_constraint_flag,
+ ptl->general_frame_only_constraint_flag);
+
+#define profile_compatible(x) (ptl->general_profile_idc == (x) || \
+ ptl->general_profile_compatibility_flag[x])
+ if (profile_compatible(4) || profile_compatible(5) ||
+ profile_compatible(6) || profile_compatible(7) ||
+ profile_compatible(8) || profile_compatible(9) ||
+ profile_compatible(10) || profile_compatible(11)) {
+
+ av_bprintf(buf, " 12bit %d", ptl->general_max_12bit_constraint_flag);
+ av_bprintf(buf, " 10bit %d", ptl->general_max_10bit_constraint_flag);
+ av_bprintf(buf, " 8bit %d", ptl->general_max_8bit_constraint_flag);
+ av_bprintf(buf, " 422chroma %d", ptl->general_max_422chroma_constraint_flag);
+ av_bprintf(buf, " 420chroma %d", ptl->general_max_420chroma_constraint_flag);
+ av_bprintf(buf, " monochrome %d", ptl->general_max_monochrome_constraint_flag);
+ av_bprintf(buf, " intra %d", ptl->general_intra_constraint_flag);
+ av_bprintf(buf, " one_picture %d", ptl->general_one_picture_only_constraint_flag);
+ av_bprintf(buf, " lower_bit_rate %d", ptl->general_lower_bit_rate_constraint_flag);
+
+ if (profile_compatible(5) || profile_compatible(9) ||
+ profile_compatible(10) || profile_compatible(11)) {
+ av_bprintf(buf, " 14bit %d", ptl->general_max_14bit_constraint_flag);
+ }
+
+ if (profile_compatible(1) || profile_compatible(2) ||
+ profile_compatible(3) || profile_compatible(4) ||
+ profile_compatible(5) || profile_compatible(9) ||
+ profile_compatible(11)) {
+ av_bprintf(buf, " inbld %d", ptl->general_inbld_flag);
+ }
+ }
+#undef profile_compatible
+
+ av_bprintf(buf, " level %d", ptl->general_level_idc);
+}
+
+
+#define CHECK_COMPATIBILITY(expected, ptl, profile) do { \
+ if (expected == !ff_h265_profile_compatible(ptl, profile)) { \
+ AVBPrint buf; \
+ char *str; \
+ const H265ProfileDescriptor *desc = \
+ ff_h265_get_profile(profile); \
+ av_log(NULL, AV_LOG_ERROR, expected ? \
+ "Incorrectly incompatible with %s:\n" : \
+ "Incorrectly compatible with %s:\n", \
+ desc->name); \
+ av_bprint_init(&buf, 1024, -1); \
+ bprint_ptl(&buf, ptl); \
+ av_bprint_finalize(&buf, &str); \
+ av_log(NULL, AV_LOG_ERROR, "%s\n", str); \
+ return 1; \
+ } \
+ } while (0)
+
+static int check_simple_rext_profiles(void)
+{
+ static const H265RawProfileTierLevel rext_420_8 = {
+ .general_profile_space = 0,
+ .general_profile_idc = 4,
+ .general_tier_flag = 0,
+ .general_profile_compatibility_flag[4] = 1,
+ .general_max_12bit_constraint_flag = 1,
+ .general_max_10bit_constraint_flag = 1,
+ .general_max_8bit_constraint_flag = 1,
+ .general_max_422chroma_constraint_flag = 1,
+ .general_max_420chroma_constraint_flag = 1,
+ .general_max_monochrome_constraint_flag = 0,
+ .general_intra_constraint_flag = 0,
+ .general_one_picture_only_constraint_flag = 0,
+ .general_lower_bit_rate_constraint_flag = 1,
+ };
+ static const H265RawProfileTierLevel rext_420_10 = {
+ .general_profile_space = 0,
+ .general_profile_idc = 4,
+ .general_tier_flag = 0,
+ .general_profile_compatibility_flag[4] = 1,
+ .general_max_12bit_constraint_flag = 1,
+ .general_max_10bit_constraint_flag = 1,
+ .general_max_8bit_constraint_flag = 0,
+ .general_max_422chroma_constraint_flag = 1,
+ .general_max_420chroma_constraint_flag = 1,
+ .general_max_monochrome_constraint_flag = 0,
+ .general_intra_constraint_flag = 0,
+ .general_one_picture_only_constraint_flag = 0,
+ .general_lower_bit_rate_constraint_flag = 1,
+ };
+ static const H265RawProfileTierLevel rext_422_8 = {
+ .general_profile_space = 0,
+ .general_profile_idc = 4,
+ .general_tier_flag = 0,
+ .general_profile_compatibility_flag[4] = 1,
+ .general_max_12bit_constraint_flag = 1,
+ .general_max_10bit_constraint_flag = 1,
+ .general_max_8bit_constraint_flag = 1,
+ .general_max_422chroma_constraint_flag = 1,
+ .general_max_420chroma_constraint_flag = 0,
+ .general_max_monochrome_constraint_flag = 0,
+ .general_intra_constraint_flag = 0,
+ .general_one_picture_only_constraint_flag = 0,
+ .general_lower_bit_rate_constraint_flag = 1,
+ };
+
+ CHECK_COMPATIBILITY(0, &rext_420_8, H265_PROFILE_MAIN);
+ CHECK_COMPATIBILITY(0, &rext_420_8, H265_PROFILE_MAIN_10);
+ CHECK_COMPATIBILITY(1, &rext_420_8, H265_PROFILE_MAIN_12);
+ CHECK_COMPATIBILITY(1, &rext_420_8, H265_PROFILE_MAIN_422_10);
+ CHECK_COMPATIBILITY(1, &rext_420_8, H265_PROFILE_MAIN_444);
+ CHECK_COMPATIBILITY(1, &rext_420_8, H265_PROFILE_MAIN_444_10);
+
+ CHECK_COMPATIBILITY(0, &rext_420_10, H265_PROFILE_MAIN);
+ CHECK_COMPATIBILITY(0, &rext_420_10, H265_PROFILE_MAIN_10);
+ CHECK_COMPATIBILITY(1, &rext_420_10, H265_PROFILE_MAIN_12);
+ CHECK_COMPATIBILITY(1, &rext_420_10, H265_PROFILE_MAIN_422_10);
+ CHECK_COMPATIBILITY(0, &rext_420_10, H265_PROFILE_MAIN_444);
+ CHECK_COMPATIBILITY(1, &rext_420_10, H265_PROFILE_MAIN_444_10);
+
+ CHECK_COMPATIBILITY(0, &rext_422_8, H265_PROFILE_MAIN);
+ CHECK_COMPATIBILITY(0, &rext_422_8, H265_PROFILE_MAIN_10);
+ CHECK_COMPATIBILITY(0, &rext_422_8, H265_PROFILE_MAIN_12);
+ CHECK_COMPATIBILITY(1, &rext_422_8, H265_PROFILE_MAIN_422_10);
+ CHECK_COMPATIBILITY(1, &rext_422_8, H265_PROFILE_MAIN_444);
+ CHECK_COMPATIBILITY(1, &rext_422_8, H265_PROFILE_MAIN_444_10);
+
+ return 0;
+}
+
+int main(void)
+{
+ if (check_profile_table())
+ return 1;
+
+ if (check_simple_rext_profiles())
+ return 1;
+
+ // Check compatibility pairs between all profiles of the same type.
+ // Profile A should be compatibile with a profile B which supports
+ // at least the same chroma subsampling and at least the same depth
+ // (including if A and B are the same).
+ for (int type = TYPE_REXT_INTER; type < TYPE_COUNT; type++) {
+ for (int chroma_a = 0; chroma_a < CHROMA_COUNT; chroma_a++) {
+ for (int depth_a = 0; depth_a < DEPTH_COUNT; depth_a++) {
+ int profile_a;
+ const H265ProfileDescriptor *desc_a;
+ H265RawProfileTierLevel ptl_a;
+
+ profile_a = get_profile(type, chroma_a, depth_a);
+ if (profile_a == H265_PROFILE_INVALID)
+ continue;
+ desc_a = ff_h265_get_profile(profile_a);
+ minimal_ptl_from_desc(&ptl_a, desc_a);
+
+ for (int chroma_b = 0; chroma_b < CHROMA_COUNT; chroma_b++) {
+ for (int depth_b = 0; depth_b < DEPTH_COUNT; depth_b++) {
+ int profile_b;
+ const H265ProfileDescriptor *desc_b;
+ int expect_compatible = (depth_b >= depth_a &&
+ chroma_b >= chroma_a);
+
+ profile_b = get_profile(type, chroma_b, depth_b);
+ if (profile_b == H265_PROFILE_INVALID)
+ continue;
+ desc_b = ff_h265_get_profile(profile_b);
+
+ av_log(NULL, AV_LOG_INFO,
+ "%d: A (%s: %d,%d) B (%s: %d,%d)\n", type,
+ desc_a->name, chroma_a, depth_a,
+ desc_b->name, chroma_b, depth_b);
+ CHECK_COMPATIBILITY(expect_compatible,
+ &ptl_a, profile_b);
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak
index 1a5694fa5f..a47e8b8237 100644
--- a/tests/fate/libavcodec.mak
+++ b/tests/fate/libavcodec.mak
@@ -71,6 +71,11 @@ fate-h265-levels: libavcodec/tests/h265_levels$(EXESUF)
fate-h265-levels: CMD = run libavcodec/tests/h265_levels$(EXESUF)
fate-h265-levels: REF = /dev/null
+FATE_LIBAVCODEC-$(CONFIG_HEVC_METADATA_BSF) += fate-h265-profiles
+fate-h265-profiles: libavcodec/tests/h265_profiles$(EXESUF)
+fate-h265-profiles: CMD = run libavcodec/tests/h265_profiles$(EXESUF)
+fate-h265-profiles: REF = /dev/null
+
FATE_LIBAVCODEC-$(CONFIG_IIRFILTER) += fate-iirfilter
fate-iirfilter: libavcodec/tests/iirfilter$(EXESUF)
fate-iirfilter: CMD = run libavcodec/tests/iirfilter$(EXESUF)
--
2.43.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] 5+ messages in thread
* [FFmpeg-devel] [PATCH v2 3/3] lavc/vaapi_hevc: Don't require exact profiles
2024-05-06 18:47 [FFmpeg-devel] [PATCH v2 1/3] lavc/h265_profile_level: Expand profile compatibility checking Mark Thompson
2024-05-06 18:47 ` [FFmpeg-devel] [PATCH v2 2/3] lavc: Add test for H.265 profile handling Mark Thompson
@ 2024-05-06 18:47 ` Mark Thompson
2024-05-06 19:56 ` [FFmpeg-devel] [PATCH v2 1/3] lavc/h265_profile_level: Expand profile compatibility checking Andreas Rheinhardt
2 siblings, 0 replies; 5+ messages in thread
From: Mark Thompson @ 2024-05-06 18:47 UTC (permalink / raw)
To: ffmpeg-devel
Rather than turning the constraint flags into a single profile and then
searching for that profile (and failing if it doesn't match any profile
exactly), instead search all supported profiles and use the first one
which supports the given set of constraint flags.
---
libavcodec/vaapi_decode.c | 45 +++++++++++-------
libavcodec/vaapi_hevc.c | 99 ++++++++++++++++++++++-----------------
libavcodec/vaapi_hevc.h | 4 +-
3 files changed, 87 insertions(+), 61 deletions(-)
diff --git a/libavcodec/vaapi_decode.c b/libavcodec/vaapi_decode.c
index 21b273cd0f..f1327464f5 100644
--- a/libavcodec/vaapi_decode.c
+++ b/libavcodec/vaapi_decode.c
@@ -387,7 +387,9 @@ static const struct {
enum AVCodecID codec_id;
int codec_profile;
VAProfile va_profile;
- VAProfile (*profile_parser)(AVCodecContext *avctx);
+ VAProfile (*match_profile)(AVCodecContext *avctx,
+ const VAProfile *profile_list,
+ int profile_count);
} vaapi_profile_map[] = {
#define MAP(c, p, v, ...) { AV_CODEC_ID_ ## c, AV_PROFILE_ ## p, VAProfile ## v, __VA_ARGS__ }
MAP(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple ),
@@ -414,9 +416,9 @@ static const struct {
#endif
#if VA_CHECK_VERSION(1, 2, 0) && CONFIG_HEVC_VAAPI_HWACCEL
MAP(HEVC, HEVC_REXT, None,
- ff_vaapi_parse_hevc_rext_scc_profile ),
+ ff_vaapi_hevc_match_rext_scc_profile ),
MAP(HEVC, HEVC_SCC, None,
- ff_vaapi_parse_hevc_rext_scc_profile ),
+ ff_vaapi_hevc_match_rext_scc_profile ),
#endif
MAP(MJPEG, MJPEG_HUFFMAN_BASELINE_DCT,
JPEGBaseline),
@@ -499,22 +501,33 @@ static int vaapi_decode_make_config(AVCodecContext *avctx,
vaapi_profile_map[i].codec_profile == AV_PROFILE_UNKNOWN)
profile_match = 1;
- va_profile = vaapi_profile_map[i].profile_parser ?
- vaapi_profile_map[i].profile_parser(avctx) :
- vaapi_profile_map[i].va_profile;
codec_profile = vaapi_profile_map[i].codec_profile;
-
- for (j = 0; j < profile_count; j++) {
- if (va_profile == profile_list[j]) {
- exact_match = profile_match;
+ if (vaapi_profile_map[i].match_profile) {
+ va_profile =
+ vaapi_profile_map[i].match_profile(avctx, profile_list,
+ profile_count);
+ if (va_profile != VAProfileNone) {
+ matched_va_profile = va_profile;
+ matched_ff_profile = codec_profile;
+ exact_match = 1;
break;
}
- }
- if (j < profile_count) {
- matched_va_profile = va_profile;
- matched_ff_profile = codec_profile;
- if (exact_match)
- break;
+ } else {
+ va_profile = vaapi_profile_map[i].va_profile;
+
+ for (j = 0; j < profile_count; j++) {
+ if (va_profile == profile_list[j]) {
+ exact_match = profile_match;
+ break;
+ }
+ }
+
+ if (j < profile_count) {
+ matched_va_profile = va_profile;
+ matched_ff_profile = codec_profile;
+ if (exact_match)
+ break;
+ }
}
}
av_freep(&profile_list);
diff --git a/libavcodec/vaapi_hevc.c b/libavcodec/vaapi_hevc.c
index 77f55ff8b1..bda9b5f589 100644
--- a/libavcodec/vaapi_hevc.c
+++ b/libavcodec/vaapi_hevc.c
@@ -590,63 +590,74 @@ static int ptl_convert(const PTLCommon *general_ptl, H265RawProfileTierLevel *h2
}
/*
- * Find exact va_profile for HEVC Range Extension and Screen Content Coding Extension
+ * Find compatible va_profile for HEVC Range Extension and Screen
+ * Content Coding Extension profiles.
*/
-VAProfile ff_vaapi_parse_hevc_rext_scc_profile(AVCodecContext *avctx)
+VAProfile ff_vaapi_hevc_match_rext_scc_profile(AVCodecContext *avctx,
+ const VAProfile *profile_list,
+ int profile_count)
{
const HEVCContext *h = avctx->priv_data;
const HEVCSPS *sps = h->ps.sps;
const PTL *ptl = &sps->ptl;
const PTLCommon *general_ptl = &ptl->general_ptl;
- const H265ProfileDescriptor *profile;
H265RawProfileTierLevel h265_raw_ptl = {0};
+ static const struct {
+ int profile;
+ VAProfile va_profile;
+ } map[] = {
+#if VA_CHECK_VERSION(1, 2, 0)
+ { H265_PROFILE_MAIN_12,
+ VAProfileHEVCMain12 },
+ { H265_PROFILE_MAIN_422_10,
+ VAProfileHEVCMain422_10 },
+ { H265_PROFILE_MAIN_422_12,
+ VAProfileHEVCMain422_12 },
+ { H265_PROFILE_MAIN_444,
+ VAProfileHEVCMain444 },
+ { H265_PROFILE_MAIN_444_10,
+ VAProfileHEVCMain444_10 },
+ { H265_PROFILE_MAIN_444_12,
+ VAProfileHEVCMain444_12 },
+ { H265_PROFILE_SCREEN_EXTENDED_MAIN,
+ VAProfileHEVCSccMain },
+ { H265_PROFILE_SCREEN_EXTENDED_MAIN_10,
+ VAProfileHEVCSccMain10 },
+ { H265_PROFILE_SCREEN_EXTENDED_MAIN_444,
+ VAProfileHEVCSccMain444 },
+#endif
+#if VA_CHECK_VERSION(1, 8, 0)
+ { H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10,
+ VAProfileHEVCSccMain444_10 },
+#endif
+ };
+
/* convert PTLCommon to H265RawProfileTierLevel */
ptl_convert(general_ptl, &h265_raw_ptl);
- profile = ff_h265_find_profile(&h265_raw_ptl);
- if (!profile) {
- av_log(avctx, AV_LOG_WARNING, "HEVC profile is not found.\n");
- goto end;
- } else {
- av_log(avctx, AV_LOG_VERBOSE, "HEVC profile %s is found.\n", profile->name);
+ for (int i = 0; i < FF_ARRAY_ELEMS(map); i++) {
+ int available = 0;
+ for (int j = 0; j < profile_count; j++) {
+ if (profile_list[j] == map[i].va_profile) {
+ available = 1;
+ break;
+ }
+ }
+ if (!available)
+ continue;
+
+ if (ff_h265_profile_compatible(&h265_raw_ptl,
+ map[i].profile)) {
+ const H265ProfileDescriptor *profile_desc =
+ ff_h265_get_profile(map[i].profile);
+ av_log(avctx, AV_LOG_VERBOSE,
+ "Decoding with HEVC profile %s.\n",
+ profile_desc->name);
+ return map[i].va_profile;
+ }
}
-#if VA_CHECK_VERSION(1, 2, 0)
- if (!strcmp(profile->name, "Main 12") ||
- !strcmp(profile->name, "Main 12 Intra"))
- return VAProfileHEVCMain12;
- else if (!strcmp(profile->name, "Main 4:2:2 10") ||
- !strcmp(profile->name, "Main 4:2:2 10 Intra"))
- return VAProfileHEVCMain422_10;
- else if (!strcmp(profile->name, "Main 4:2:2 12") ||
- !strcmp(profile->name, "Main 4:2:2 12 Intra"))
- return VAProfileHEVCMain422_12;
- else if (!strcmp(profile->name, "Main 4:4:4") ||
- !strcmp(profile->name, "Main 4:4:4 Intra"))
- return VAProfileHEVCMain444;
- else if (!strcmp(profile->name, "Main 4:4:4 10") ||
- !strcmp(profile->name, "Main 4:4:4 10 Intra"))
- return VAProfileHEVCMain444_10;
- else if (!strcmp(profile->name, "Main 4:4:4 12") ||
- !strcmp(profile->name, "Main 4:4:4 12 Intra"))
- return VAProfileHEVCMain444_12;
- else if (!strcmp(profile->name, "Screen-Extended Main"))
- return VAProfileHEVCSccMain;
- else if (!strcmp(profile->name, "Screen-Extended Main 10"))
- return VAProfileHEVCSccMain10;
- else if (!strcmp(profile->name, "Screen-Extended Main 4:4:4"))
- return VAProfileHEVCSccMain444;
-#if VA_CHECK_VERSION(1, 8, 0)
- else if (!strcmp(profile->name, "Screen-Extended Main 4:4:4 10"))
- return VAProfileHEVCSccMain444_10;
-#endif
-#else
- av_log(avctx, AV_LOG_WARNING, "HEVC profile %s is "
- "not supported with this VA version.\n", profile->name);
-#endif
-
-end:
if (avctx->hwaccel_flags & AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH) {
// Default to selecting Main profile if profile mismatch is allowed
return VAProfileHEVCMain;
diff --git a/libavcodec/vaapi_hevc.h b/libavcodec/vaapi_hevc.h
index 449635d0d7..455c68e6ba 100644
--- a/libavcodec/vaapi_hevc.h
+++ b/libavcodec/vaapi_hevc.h
@@ -22,6 +22,8 @@
#include <va/va.h>
#include "avcodec.h"
-VAProfile ff_vaapi_parse_hevc_rext_scc_profile(AVCodecContext *avctx);
+VAProfile ff_vaapi_hevc_match_rext_scc_profile(AVCodecContext *avctx,
+ const VAProfile *profile_list,
+ int profile_count);
#endif /* AVCODEC_VAAPI_HEVC_H */
--
2.43.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] 5+ messages in thread
* Re: [FFmpeg-devel] [PATCH v2 1/3] lavc/h265_profile_level: Expand profile compatibility checking
2024-05-06 18:47 [FFmpeg-devel] [PATCH v2 1/3] lavc/h265_profile_level: Expand profile compatibility checking Mark Thompson
2024-05-06 18:47 ` [FFmpeg-devel] [PATCH v2 2/3] lavc: Add test for H.265 profile handling Mark Thompson
2024-05-06 18:47 ` [FFmpeg-devel] [PATCH v2 3/3] lavc/vaapi_hevc: Don't require exact profiles Mark Thompson
@ 2024-05-06 19:56 ` Andreas Rheinhardt
2024-05-06 20:13 ` Mark Thompson
2 siblings, 1 reply; 5+ messages in thread
From: Andreas Rheinhardt @ 2024-05-06 19:56 UTC (permalink / raw)
To: ffmpeg-devel
Mark Thompson:
> Replace existing get_profile() with find_profile(), which finds the
> lowest compatible profile rather than requiring an exact match.
> ---
> Series changes since v1:
> * Added H265_PROFILE_INVALID with value zero because it simplifies some code (and the values don't matter).
What code is simplified by using zero (instead of -1)? I only see that
it leads to the addition of an unnecessary entry to h265_profiles.
> * Fixed the h265-levels test.
> * Added a new h265-profiles test which tests the profile compatibility checking.
> * Fixed missing VAAPI profiles.
>
>
> libavcodec/h265_profile_level.c | 78 +++++++++++++++++++++------------
> libavcodec/h265_profile_level.h | 71 +++++++++++++++++++++++++++++-
> libavcodec/tests/h265_levels.c | 2 +-
> libavcodec/vaapi_hevc.c | 2 +-
> libavcodec/vdpau_hevc.c | 2 +-
> 5 files changed, 123 insertions(+), 32 deletions(-)
>
> diff --git a/libavcodec/h265_profile_level.c b/libavcodec/h265_profile_level.c
> index 7ff9681f65..2e4a1c88bf 100644
> --- a/libavcodec/h265_profile_level.c
> +++ b/libavcodec/h265_profile_level.c
> @@ -40,6 +40,7 @@ static const H265LevelDescriptor h265_levels[] = {
> };
>
> static const H265ProfileDescriptor h265_profiles[] = {
> + { "Invalid" },
> // profile_idc 8bit one-picture
> // HT-profile | 422chroma | lower-bit-rate
> // | 14bit | | 420chroma | | CpbVclFactor MinCrScaleFactor
> @@ -119,41 +120,62 @@ static const H265ProfileDescriptor h265_profiles[] = {
> 5, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 4000, 4400, 6.000, 0.5, 6 },
> };
>
> +_Static_assert(H265_PROFILE_COUNT == FF_ARRAY_ELEMS(h265_profiles),
> + "Incorrect H.265 profiles table.");
>
> -const H265ProfileDescriptor *ff_h265_get_profile(const H265RawProfileTierLevel *ptl)
> +
> +const int ff_h265_profile_compatible(const H265RawProfileTierLevel *ptl,
> + int profile)
Why don't you add a tag to this enum and pass a proper enum here?
> {
> - int i;
> + const H265ProfileDescriptor *desc;
> +
> + av_assert0(profile > H265_PROFILE_INVALID &&
> + profile < H265_PROFILE_COUNT);
>
> if (ptl->general_profile_space)
> - return NULL;
> + return 0;
>
> - for (i = 0; i < FF_ARRAY_ELEMS(h265_profiles); i++) {
> - const H265ProfileDescriptor *profile = &h265_profiles[i];
> + desc = &h265_profiles[profile];
>
> - if (ptl->general_profile_idc &&
> - ptl->general_profile_idc != profile->profile_idc)
> - continue;
> - if (!ptl->general_profile_compatibility_flag[profile->profile_idc])
> - continue;
> + if (ptl->general_profile_idc &&
> + ptl->general_profile_idc != desc->profile_idc)
> + return 0;
> + if (!ptl->general_profile_compatibility_flag[desc->profile_idc])
> + return 0;
>
> -#define check_flag(name) \
> - if (profile->name < 2) { \
> - if (profile->name != ptl->general_ ## name ## _constraint_flag) \
> - continue; \
> - }
> - check_flag(max_14bit);
> - check_flag(max_12bit);
> - check_flag(max_10bit);
> - check_flag(max_8bit);
> - check_flag(max_422chroma);
> - check_flag(max_420chroma);
> - check_flag(max_monochrome);
> - check_flag(intra);
> - check_flag(one_picture_only);
> - check_flag(lower_bit_rate);
> +#define check_flag(flag) \
> + if (desc->flag < 2 && \
> + desc->flag > ptl->general_ ## flag ## _constraint_flag) \
> + return 0;
> + check_flag(max_14bit);
> + check_flag(max_12bit);
> + check_flag(max_10bit);
> + check_flag(max_8bit);
> + check_flag(max_422chroma);
> + check_flag(max_420chroma);
> + check_flag(max_monochrome);
> + check_flag(intra);
> + check_flag(one_picture_only);
> + check_flag(lower_bit_rate);
> #undef check_flag
>
> - return profile;
> + return 1;
> +}
> +
> +
> +const H265ProfileDescriptor *ff_h265_get_profile(int profile)
> +{
> + av_assert0(profile > H265_PROFILE_INVALID &&
> + profile < H265_PROFILE_COUNT);
> +
> + return &h265_profiles[profile];
> +}
> +
> +const H265ProfileDescriptor *ff_h265_find_profile(const H265RawProfileTierLevel *ptl)
> +{
> + for (int p = 1; p < H265_PROFILE_COUNT; p++) {
> + if (ff_h265_profile_compatible(ptl, p))
> + return &h265_profiles[p];
> }
>
> return NULL;
> @@ -171,12 +193,12 @@ const H265LevelDescriptor *ff_h265_guess_level(const H265RawProfileTierLevel *pt
> int i;
>
> if (ptl)
> - profile = ff_h265_get_profile(ptl);
> + profile = ff_h265_find_profile(ptl);
> else
> profile = NULL;
> if (!profile) {
> // Default to using multiplication factors for Main profile.
> - profile = &h265_profiles[4];
> + profile = &h265_profiles[H265_PROFILE_MAIN];
> }
>
> pic_size = width * height;
> diff --git a/libavcodec/h265_profile_level.h b/libavcodec/h265_profile_level.h
> index cd30ac5c50..e4e63dfef1 100644
> --- a/libavcodec/h265_profile_level.h
> +++ b/libavcodec/h265_profile_level.h
> @@ -24,6 +24,50 @@
> #include "cbs_h265.h"
>
>
> +// Enumeration of all H.265 profiles.
> +// The list is ordered to match table A.10; numeric values are an index
> +// into the latest version of this table and have no codec meaning.
> +enum {
> + H265_PROFILE_INVALID,
> + H265_PROFILE_MONOCHROME,
> + H265_PROFILE_MONOCHROME_10,
> + H265_PROFILE_MONOCHROME_12,
> + H265_PROFILE_MONOCHROME_16,
> + H265_PROFILE_MAIN,
> + H265_PROFILE_SCREEN_EXTENDED_MAIN,
> + H265_PROFILE_MAIN_10,
> + H265_PROFILE_SCREEN_EXTENDED_MAIN_10,
> + H265_PROFILE_MAIN_12,
> + H265_PROFILE_MAIN_STILL_PICTURE,
> + H265_PROFILE_MAIN_10_STILL_PICTURE,
> + H265_PROFILE_MAIN_422_10,
> + H265_PROFILE_MAIN_422_12,
> + H265_PROFILE_MAIN_444,
> + H265_PROFILE_HIGH_THROUGHPUT_444,
> + H265_PROFILE_SCREEN_EXTENDED_MAIN_444,
> + H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444,
> + H265_PROFILE_MAIN_444_10,
> + H265_PROFILE_HIGH_THROUGHPUT_444_10,
> + H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10,
> + H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_10,
> + H265_PROFILE_MAIN_444_12,
> + H265_PROFILE_HIGH_THROUGHPUT_444_14,
> + H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_14,
> + H265_PROFILE_MAIN_INTRA,
> + H265_PROFILE_MAIN_10_INTRA,
> + H265_PROFILE_MAIN_12_INTRA,
> + H265_PROFILE_MAIN_422_10_INTRA,
> + H265_PROFILE_MAIN_422_12_INTRA,
> + H265_PROFILE_MAIN_444_INTRA,
> + H265_PROFILE_MAIN_444_10_INTRA,
> + H265_PROFILE_MAIN_444_12_INTRA,
> + H265_PROFILE_MAIN_444_16_INTRA,
> + H265_PROFILE_MAIN_444_STILL_PICTURE,
> + H265_PROFILE_MAIN_444_16_STILL_PICTURE,
> + H265_PROFILE_HIGH_THROUGHPUT_444_16_INTRA,
> + H265_PROFILE_COUNT,
> +};
> +
> typedef struct H265LevelDescriptor {
> char name[4]; // Large enough for all current levels like "4.1"
> uint8_t level_idc;
> @@ -70,7 +114,32 @@ typedef struct H265ProfileDescriptor {
> } H265ProfileDescriptor;
>
>
> -const H265ProfileDescriptor *ff_h265_get_profile(const H265RawProfileTierLevel *ptl);
> +/**
> + * Test whether the PTL structure is compatible with the given profile.
> + *
> + * @param ptl PTL structure to check.
> + * @param profile Profile to test compatibility with. Must be a valid
> + * H265_PROFILE_* value.
> + * @return Nonzero if compatible, zero if incompatible.
> + */
> +const int ff_h265_profile_compatible(const H265RawProfileTierLevel *ptl,
> + int profile);
> +
> +/**
> + * Return profile descriptor for the given profile.
> + *
> + * @param profile Profile number; must be a valid H265_PROFILE_* value.
> + * @return Profile descriptor.
> + */
> +const H265ProfileDescriptor *ff_h265_get_profile(int profile);
> +
> +/**
> + * Find the first profile compatible with the given PTL structure.
> + *
> + * @param ptl PTL structure to search for.
> + * @return First compatible profile, or NULL if no compatible profiles.
> + */
> +const H265ProfileDescriptor *ff_h265_find_profile(const H265RawProfileTierLevel *ptl);
>
>
> /**
> diff --git a/libavcodec/tests/h265_levels.c b/libavcodec/tests/h265_levels.c
> index 66d72c63a3..9923f346f7 100644
> --- a/libavcodec/tests/h265_levels.c
> +++ b/libavcodec/tests/h265_levels.c
> @@ -275,7 +275,7 @@ int main(void)
> }
>
> for (i = 0; i < FF_ARRAY_ELEMS(test_bitrate); i++) {
> - profile = ff_h265_get_profile(test_bitrate[i].ptl);
> + profile = ff_h265_find_profile(test_bitrate[i].ptl);
> level = ff_h265_guess_level(test_bitrate[i].ptl,
> test_bitrate[i].bitrate,
> 0, 0, 0, 0, 0, 0);
> diff --git a/libavcodec/vaapi_hevc.c b/libavcodec/vaapi_hevc.c
> index 3bdd2dd1b8..77f55ff8b1 100644
> --- a/libavcodec/vaapi_hevc.c
> +++ b/libavcodec/vaapi_hevc.c
> @@ -604,7 +604,7 @@ VAProfile ff_vaapi_parse_hevc_rext_scc_profile(AVCodecContext *avctx)
> /* convert PTLCommon to H265RawProfileTierLevel */
> ptl_convert(general_ptl, &h265_raw_ptl);
>
> - profile = ff_h265_get_profile(&h265_raw_ptl);
> + profile = ff_h265_find_profile(&h265_raw_ptl);
> if (!profile) {
> av_log(avctx, AV_LOG_WARNING, "HEVC profile is not found.\n");
> goto end;
> diff --git a/libavcodec/vdpau_hevc.c b/libavcodec/vdpau_hevc.c
> index 4cd7ce5621..a59a030b1c 100644
> --- a/libavcodec/vdpau_hevc.c
> +++ b/libavcodec/vdpau_hevc.c
> @@ -478,7 +478,7 @@ static int vdpau_hevc_parse_rext_profile(AVCodecContext *avctx, VdpDecoderProfil
> /* convert PTLCommon to H265RawProfileTierLevel */
> ptl_convert(general_ptl, &h265_raw_ptl);
>
> - profile = ff_h265_get_profile(&h265_raw_ptl);
> + profile = ff_h265_find_profile(&h265_raw_ptl);
> if (!profile) {
> av_log(avctx, AV_LOG_WARNING, "HEVC profile is not found.\n");
> if (avctx->hwaccel_flags & AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH) {
_______________________________________________
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] 5+ messages in thread