typefind: Add typefinder for AV1 obu/annexb raw streams

AV1 specification [1] define 2 AV1 raw bitstreams storage
formats without containers:
- OBU in chapiter 5.2
- Annexb in chapiter 11.

Implement a detection function for the both cases mostly
by testing OBU forbidden, type, has_size_field and reserved bits.
For annexb case testing if temporal unit size, frame unit size and
obu length are valid. If they are check that the first OBU is
a temporal delimiter.

[1] https://aomediacodec.github.io/av1-spec/av1-spec.pdf

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8502>
This commit is contained in:
Benjamin Gaignard 2024-12-05 14:05:52 +01:00 committed by GStreamer Marge Bot
parent a4a1de05f1
commit 515e241b38
3 changed files with 152 additions and 0 deletions

View File

@ -6777,6 +6777,154 @@ aa_type_find (GstTypeFind * tf, gpointer private)
}
}
/*** video/x-av1 ***/
static GstStaticCaps av1_caps =
GST_STATIC_CAPS
("video/x-av1,stream-format={obu-stream, annexb},alignment=none");
#define AV1_CAPS gst_static_caps_get(&av1_caps)
static gboolean
av1_is_valid_obu_type (guint obu_type)
{
if ((obu_type > 0 && obu_type < 9) || obu_type == 15)
return TRUE;
return FALSE;
}
static gboolean
av1_leb128 (const guint8 * data, guint32 * retval, gint * read_bytes)
{
guint8 leb128_byte = 0;
guint64 value = 0;
gint i;
*retval = 0;
*read_bytes = 0;
for (i = 0; i < 8; i++) {
leb128_byte = data[i];
value |= (((gint) leb128_byte & 0x7f) << (i * 7));
if (!(leb128_byte & 0x80))
break;
if (i == 7 && leb128_byte & 0x80)
return FALSE;
}
if (i == 8)
return FALSE;
/* check for bitstream conformance see chapter 4.10.5 */
if (value < G_MAXUINT32) {
*retval = (guint32) value;
*read_bytes = i + 1;
return TRUE;
}
return FALSE;
}
static gboolean
av1_is_valid_obu (const guint8 * data, guint * obu_type, gint * read_bytes)
{
gboolean obu_forbidden_bit;
gboolean obu_extension_flag;
gboolean obu_has_size_field;
gboolean obu_reserved_1bit;
int offset = 1;
*obu_type = 0;
*read_bytes = 0;
/* Detect OBU header */
obu_forbidden_bit = !!(data[0] & 0x80);
if (obu_forbidden_bit)
return FALSE;
*obu_type = (data[0] & 0x78) >> 3;
obu_extension_flag = !!(data[0] & 0x4);
obu_has_size_field = !!(data[0] & 0x2);
obu_reserved_1bit = !!(data[0] & 0x1);
/* if obu_extension_flag is set temporal_id (3 bits)
* spatial_id (2 bits) and extension_header_reserved_3bits (3 bits)
* field are coded in the header so OBU size field is
* 1 byte after */
if (obu_extension_flag)
offset++;
*read_bytes += offset;
if (av1_is_valid_obu_type (*obu_type) && !obu_reserved_1bit) {
if (obu_has_size_field) {
guint32 obu_size;
gint bytes;
if (!av1_leb128 (data + offset, &obu_size, &bytes))
return FALSE;
*read_bytes += bytes;
}
return TRUE;
}
return FALSE;
}
static void
av1_type_find (GstTypeFind * tf, gpointer unused)
{
guint32 temporal_unit_size;
guint32 frame_unit_size;
guint32 obu_length;
const guint8 *data;
guint obu_type;
gint read_bytes;
gint offset = 0;
data = gst_type_find_peek (tf, 0, 25);
if (!data)
return;
if (av1_is_valid_obu (data, &obu_type, &read_bytes)) {
gst_type_find_suggest_simple (tf, GST_TYPE_FIND_MINIMUM, "video/x-av1",
"stream-format", G_TYPE_STRING, "obu-stream",
"alignment", G_TYPE_STRING, "none", NULL);
return;
}
if (!av1_leb128 (data, &temporal_unit_size, &read_bytes))
return;
offset += read_bytes;
if (!av1_leb128 (data + offset, &frame_unit_size, &read_bytes))
return;
offset += read_bytes;
if (frame_unit_size > temporal_unit_size)
return;
if (!av1_leb128 (data + offset, &obu_length, &read_bytes))
return;
offset += read_bytes;
if (obu_length > frame_unit_size)
return;
if (!av1_is_valid_obu (data + offset, &obu_type, &read_bytes))
return;
offset += read_bytes;
/* The first OBU must be a temporal delimiter */
if (obu_type == 2)
gst_type_find_suggest_simple (tf, GST_TYPE_FIND_MINIMUM, "video/x-av1",
"stream-format", G_TYPE_STRING, "annexb",
"alignment", G_TYPE_STRING, "none", NULL);
}
/*Type find definition by functions */
GST_TYPE_FIND_REGISTER_DEFINE (musepack, "audio/x-musepack", GST_RANK_PRIMARY,
musepack_type_find, "mpc,mpp,mp+", MUSEPACK_CAPS, NULL, NULL);
@ -7042,3 +7190,5 @@ GST_TYPE_FIND_REGISTER_DEFINE (wsaud, "application/x-wsaud", GST_RANK_MARGINAL,
wsaud_type_find, "wsaud", WSAUD_CAPS, NULL, NULL);
GST_TYPE_FIND_REGISTER_DEFINE (wsvqa, "application/x-wsvqa", GST_RANK_MARGINAL,
wsvqa_type_find, "wsvqa", WSVQA_CAPS, NULL, NULL);
GST_TYPE_FIND_REGISTER_DEFINE (av1, "video/x-av1", GST_RANK_MARGINAL,
av1_type_find, "av1", AV1_CAPS, NULL, NULL);

View File

@ -214,6 +214,7 @@ plugin_init (GstPlugin * plugin)
GST_TYPE_FIND_REGISTER (film_cpk, plugin);
GST_TYPE_FIND_REGISTER (gxf, plugin);
GST_TYPE_FIND_REGISTER (iff, plugin);
GST_TYPE_FIND_REGISTER (av1, plugin);
return TRUE;
}

View File

@ -220,5 +220,6 @@ GST_TYPE_FIND_REGISTER_DECLARE (vmd);
GST_TYPE_FIND_REGISTER_DECLARE (wc3movie);
GST_TYPE_FIND_REGISTER_DECLARE (wsaud);
GST_TYPE_FIND_REGISTER_DECLARE (wsvqa);
GST_TYPE_FIND_REGISTER_DECLARE (av1);
#endif //__GST_TYPE_FIND_FUNCTIONS_PLUGIN_H__