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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7339>
This commit is contained in:
Ruben Gonzalez 2024-08-10 18:53:41 +02:00
parent 5d25ab1306
commit dc4dd415f0
3 changed files with 98 additions and 0 deletions

View file

@ -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 */

View file

@ -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);

View file

@ -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);