From dc4dd415f04f405e220a3a84dbe4f113de37b5b8 Mon Sep 17 00:00:00 2001 From: Ruben Gonzalez Date: Sat, 10 Aug 2024 18:53:41 +0200 Subject: [PATCH] typefind: Add typefinder for VVC/H.266 H.266 NAL unit header syntax [1] is similar to H.265 NAL unit header syntax[2]: ``` H.265 H.266 +---------------+---------------+ +---------------+---------------+ |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7| |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |F| NALType | LayerId | TID | |F|U| LayerId | NALType | TID | +-------------+-----------+-----+ +-------------+-----------------+ Where * F: `forbidden_zero_bit`: f(1) * U: `nuh_reserved_zero_bit`: u(1) only H.266 * LayerId: `nuh_layer_id`: u(6) * NALType: `nal_unit_type`: u(6) in H.265 and u(5) in H.266 * TID: `nuh_temporal_id_plus1`: u(3) ``` NAL unit types have different values: | NALType | H.265 | H.266 | |----------|------------------------------------|---------------------------| | VPS | HEVC_NAL_VPS(32) | VVC_VPS_NUT(14) | | SPS | HEVC_NAL_SPS(33) | VVC_SPS_NUT(15) | | PPS | HEVC_NAL_PPS(34) | VVC_PPS_NUT(16) | | IRAP | BLA_W_LP(19)..HEVC_NAL_CRA_NUT(21) | IDR_W_RADL(7)..CRA_NUT(9) | Implementation of `h266_video_type_find` is based on `h265_video_type_find` with next differences: - NAL unit header syntax for H.265 and H.266 - Diff NAL unit types values - Avoid checking nuh_layer_id is zero. H.266 conformance test suite[3] contains examples with more than one layer. This typefind was tested with H.266 conformance test suite [3]. Also, with the help of fluster[4], with H.264 and H.265 conformance test suites to avoid regresions. Pending test vectors to fix: - 8b422_H_Sony_4 - DEBLOCKING_E_Ericsson_3 [1] https://www.itu.int/rec/T-REC-H.266 [2] https://www.itu.int/rec/T-REC-H.265 [3] https://www.itu.int/wftp3/av-arch/jvet-site/bitstream_exchange/VVC/draft_conformance/draft6/ [4] https://github.com/fluendo/fluster/ Part-of: --- .../gst/typefind/gsttypefindfunctions.c | 96 +++++++++++++++++++ .../gst/typefind/gsttypefindfunctionsplugin.c | 1 + .../gst/typefind/gsttypefindfunctionsplugin.h | 1 + 3 files changed, 98 insertions(+) diff --git a/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctions.c b/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctions.c index 0574b756b4..334417f0fe 100644 --- a/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctions.c +++ b/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctions.c @@ -3028,6 +3028,100 @@ h265_video_type_find (GstTypeFind * tf, gpointer unused) } } +/*** video/x-h266 H266 elementary video stream ***/ + +static GstStaticCaps h266_video_caps = +GST_STATIC_CAPS ("video/x-h266,stream-format=byte-stream"); + +#define H266_VIDEO_CAPS gst_static_caps_get(&h266_video_caps) + +#define H266_MAX_PROBE_LENGTH (128 * 1024) /* 128kB for HD should be enough. */ + +static void +h266_video_type_find (GstTypeFind * tf, gpointer unused) +{ + DataScanCtx c = { 0, NULL, 0 }; + + /* Stream consists of: a series of sync codes (00 00 00 01) followed + * by NALs + */ + gboolean seen_irap = FALSE; + gboolean seen_vps = FALSE; + gboolean seen_sps = FALSE; + gboolean seen_pps = FALSE; + int nuh, nut; + int good = 0; + int bad = 0; + + while (c.offset < H266_MAX_PROBE_LENGTH) { + if (G_UNLIKELY (!data_scan_ctx_ensure_data (tf, &c, 5))) + break; + + if (IS_MPEG_HEADER (c.data)) { + /* forbidden_zero_bit(1) | nuh_reserved_zero_bit(1) | nuh_layer_id(6) */ + nuh = c.data[3] & 0xc0; + /* nal_unit_type(5) | nuh_temporal_id_plus1(3) */ + nut = c.data[4] & 0xf8; + + /* if forbidden bit and nuh_reserved_zero_bit are different to 0 won't be h266 */ + if (nuh) { + bad++; + break; + } + nut = nut >> 3; + + /* if nuh_temporal_id_plus1 is zero then it won't be h266 */ + if (!(c.data[4] & 0x07)) { + bad++; + break; + } + + /* collect statistics about the NAL types */ + if ((nut >= 0 && nut <= 27)) { + if (nut == 14) + seen_vps = TRUE; + else if (nut == 15) + seen_sps = TRUE; + else if (nut == 16) + seen_pps = TRUE; + else if (nut >= 7 && nut <= 9) { + /* BLA, IDR and CRA pictures are belongs to be IRAP picture */ + /* we are not counting the reserved IRAP pictures (22 and 23) to good */ + seen_irap = TRUE; + } + + good++; + } else if (nut >= 27) { + /* reserved values are counting as bad */ + bad++; + } + + GST_LOG ("good:%d, bad:%d, pps:%d, sps:%d, vps:%d, irap:%d", good, bad, + seen_pps, seen_sps, seen_vps, seen_irap); + + if (seen_sps && seen_pps && seen_irap && good >= 10 && bad < 4) { + gst_type_find_suggest (tf, GST_TYPE_FIND_LIKELY, H266_VIDEO_CAPS); + return; + } + + data_scan_ctx_advance (tf, &c, 5); + } + data_scan_ctx_advance (tf, &c, 1); + } + + GST_LOG ("good:%d, bad:%d, pps:%d, sps:%d, vps:%d, irap:%d", good, bad, + seen_pps, seen_sps, seen_vps, seen_irap); + + if (good >= 2 && bad == 0) { + GstTypeFindProbability probability = GST_TYPE_FIND_POSSIBLE; + + if (seen_pps && seen_sps && seen_vps) + probability = GST_TYPE_FIND_LIKELY; + + gst_type_find_suggest (tf, probability, H266_VIDEO_CAPS); + } +} + /*** video/mpeg video stream ***/ static GstStaticCaps mpeg_video_caps = GST_STATIC_CAPS ("video/mpeg, " @@ -6742,6 +6836,8 @@ GST_TYPE_FIND_REGISTER_DEFINE (h264_video, "video/x-h264", GST_RANK_PRIMARY, h264_video_type_find, "h264,x264,264", H264_VIDEO_CAPS, NULL, NULL); GST_TYPE_FIND_REGISTER_DEFINE (h265_video, "video/x-h265", GST_RANK_PRIMARY, h265_video_type_find, "h265,x265,265", H265_VIDEO_CAPS, NULL, NULL); +GST_TYPE_FIND_REGISTER_DEFINE (h266_video, "video/x-h266", GST_RANK_PRIMARY, + h266_video_type_find, "h266,266", H266_VIDEO_CAPS, NULL, NULL); GST_TYPE_FIND_REGISTER_DEFINE (nuv, "video/x-nuv", GST_RANK_SECONDARY, nuv_type_find, "nuv", NUV_CAPS, NULL, NULL); /* ISO formats */ diff --git a/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctionsplugin.c b/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctionsplugin.c index 998cfd4cf7..9ff09cc0bf 100644 --- a/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctionsplugin.c +++ b/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctionsplugin.c @@ -126,6 +126,7 @@ plugin_init (GstPlugin * plugin) GST_TYPE_FIND_REGISTER (h263_video, plugin); GST_TYPE_FIND_REGISTER (h264_video, plugin); GST_TYPE_FIND_REGISTER (h265_video, plugin); + GST_TYPE_FIND_REGISTER (h266_video, plugin); GST_TYPE_FIND_REGISTER (nuv, plugin); /* ISO formats */ GST_TYPE_FIND_REGISTER (m4a, plugin); diff --git a/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctionsplugin.h b/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctionsplugin.h index f23b99e458..b48e872dd2 100644 --- a/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctionsplugin.h +++ b/subprojects/gst-plugins-base/gst/typefind/gsttypefindfunctionsplugin.h @@ -120,6 +120,7 @@ GST_TYPE_FIND_REGISTER_DECLARE (mpeg4_video); GST_TYPE_FIND_REGISTER_DECLARE (h263_video); GST_TYPE_FIND_REGISTER_DECLARE (h264_video); GST_TYPE_FIND_REGISTER_DECLARE (h265_video); +GST_TYPE_FIND_REGISTER_DECLARE (h266_video); GST_TYPE_FIND_REGISTER_DECLARE (nuv); GST_TYPE_FIND_REGISTER_DECLARE (m4a); GST_TYPE_FIND_REGISTER_DECLARE (q3gp);