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:
parent
a4a1de05f1
commit
515e241b38
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__
|
||||
|
Loading…
x
Reference in New Issue
Block a user