video: add support for AV12

AV12 is an internally conceived format that is actually
the combination of NV12 and an alpha plane.

This format is to add to gstreamer's webM
transparency support for vp8 and vp9. To this end, two
I420 streams are independently decoded simultaneously for
the actual content and the alpha plane respectively
and these are then combined into A420.

Since most hardware decoders output NV12, this patch adds
NV12+A to make the same workflow possible.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/1152>
This commit is contained in:
Daniel Almeida 2021-05-21 12:24:37 -03:00 committed by Nicolas Dufresne
parent 038ba1bffe
commit 6760c7fd76
5 changed files with 158 additions and 2 deletions

View file

@ -6595,6 +6595,10 @@ get_scale_format (GstVideoFormat format, gint plane)
case GST_VIDEO_FORMAT_NV24:
res = plane == 0 ? GST_VIDEO_FORMAT_GRAY8 : GST_VIDEO_FORMAT_NV12;
break;
case GST_VIDEO_FORMAT_AV12:
res = (plane == 0
|| plane == 2) ? GST_VIDEO_FORMAT_GRAY8 : GST_VIDEO_FORMAT_NV12;
break;
case GST_VIDEO_FORMAT_UNKNOWN:
case GST_VIDEO_FORMAT_ENCODED:
case GST_VIDEO_FORMAT_v210:

View file

@ -1753,6 +1753,98 @@ unpack_NV21 (const GstVideoFormatInfo * info, GstVideoPackFlags flags,
}
}
#define PACK_AV12 GST_VIDEO_FORMAT_AYUV, unpack_AV12, 1, pack_AV12
static void
unpack_AV12 (const GstVideoFormatInfo * info, GstVideoPackFlags flags,
gpointer dest, const gpointer data[GST_VIDEO_MAX_PLANES],
const gint stride[GST_VIDEO_MAX_PLANES], gint x, gint y, gint width)
{
gint uv = GET_UV_420 (y, flags);
const guint8 *restrict sy = GET_PLANE_LINE (0, y);
const guint8 *restrict suv = GET_PLANE_LINE (1, uv);
const guint8 *restrict sa = GET_PLANE_LINE (2, y); /* a is for 'alpha' */
guint8 *restrict d = dest;
sy += x;
sa += x;
suv += (x & ~1);
if (x & 1) {
d[0] = *sa++;
d[1] = *sy++;
d[2] = suv[0];
d[3] = suv[1];
width--;
d += 4;
suv += 2;
}
if (IS_ALIGNED (d, 8)) {
video_orc_unpack_AV12 (d, sy, suv, sa, width / 2);
} else {
gint i;
for (i = 0; i < width / 2; i++) {
d[i * 8 + 0] = sa[i * 2 + 0];
d[i * 8 + 1] = sy[i * 2 + 0];
d[i * 8 + 2] = suv[i * 2 + 0];
d[i * 8 + 3] = suv[i * 2 + 1];
d[i * 8 + 4] = sa[i * 2 + 1];
d[i * 8 + 5] = sy[i * 2 + 1];
d[i * 8 + 6] = suv[i * 2 + 0];
d[i * 8 + 7] = suv[i * 2 + 1];
}
}
if (width & 1) {
gint i = width - 1;
d[i * 4 + 0] = sa[i];
d[i * 4 + 1] = sy[i];
d[i * 4 + 2] = suv[i + 0];
d[i * 4 + 3] = suv[i + 1];
}
}
static void
pack_AV12 (const GstVideoFormatInfo * info, GstVideoPackFlags flags,
const gpointer src, gint sstride, gpointer data[GST_VIDEO_MAX_PLANES],
const gint stride[GST_VIDEO_MAX_PLANES], GstVideoChromaSite chroma_site,
gint y, gint width)
{
gint uv = GET_UV_420 (y, flags);
guint8 *restrict dy = GET_PLANE_LINE (0, y);
guint8 *restrict duv = GET_PLANE_LINE (1, uv);
guint8 *restrict da = GET_PLANE_LINE (2, y); /* a is for 'alpha' */
const guint8 *restrict s = src;
if (IS_CHROMA_LINE_420 (y, flags)) {
if (IS_ALIGNED (s, 8)) {
video_orc_pack_AV12 (dy, duv, da, s, width / 2);
} else {
gint i;
for (i = 0; i < width / 2; i++) {
/* AYUV_AYUV: alpha is on bytes 0 and 4 */
da[i * 2 + 0] = s[i * 8 + 0];
da[i * 2 + 1] = s[i * 8 + 4];
dy[i * 2 + 0] = s[i * 8 + 1];
dy[i * 2 + 1] = s[i * 8 + 5];
duv[i * 2 + 0] = s[i * 8 + 2];
duv[i * 2 + 1] = s[i * 8 + 3];
}
}
if (width & 1) {
gint i = width - 1;
da[i] = s[i * 4 + 0]; /* AYUV: alpha is byte 0 */
dy[i] = s[i * 4 + 1];
duv[i + 0] = s[i * 4 + 2];
duv[i + 1] = s[i * 4 + 3];
}
} else {
video_orc_pack_YA (dy, da, s, width);
}
}
static void
pack_NV21 (const GstVideoFormatInfo * info, GstVideoPackFlags flags,
const gpointer src, gint sstride, gpointer data[GST_VIDEO_MAX_PLANES],
@ -6374,6 +6466,7 @@ typedef struct
#define PSTR111 { 1, 1, 1, 0 }
#define PSTR1111 { 1, 1, 1, 1 }
#define PSTR122 { 1, 2, 2, 0 }
#define PSTR1221 { 1, 2, 2, 1 }
#define PSTR2 { 2, 0, 0, 0 }
#define PSTR222 { 2, 2, 2, 0 }
#define PSTR2222 { 2, 2, 2, 2 }
@ -6389,6 +6482,7 @@ typedef struct
#define PLANE0 1, { 0, 0, 0, 0 }
#define PLANE01 2, { 0, 1, 0, 0 }
#define PLANE011 2, { 0, 1, 1, 0 }
#define PLANE0112 3, { 0, 1, 1, 2 }
#define PLANE012 3, { 0, 1, 2, 0 }
#define PLANE0123 4, { 0, 1, 2, 3 }
#define PLANE021 3, { 0, 2, 1, 0 }
@ -6708,6 +6802,8 @@ static const VideoFormat formats[] = {
PACK_RGBP),
MAKE_RGB_FORMAT (BGRP, "raw video", DPTH888, PSTR111, PLANE210, OFFS0, SUB444,
PACK_BGRP),
MAKE_YUV_FORMAT (AV12, "raw video", GST_MAKE_FOURCC ('A', 'V', '1', '2'),
DPTH8888, PSTR1221, PLANE0112, OFFS001, SUB4204, PACK_AV12),
};
static GstVideoFormat

View file

@ -133,6 +133,7 @@ G_BEGIN_DECLS
* @GST_VIDEO_FORMAT_NV12_32L32: NV12 with 32x32 tiles in linear order (Since: 1.18)
* @GST_VIDEO_FORMAT_RGBP: planar 4:4:4 RGB, 8 bits per channel (Since: 1.20)
* @GST_VIDEO_FORMAT_BGRP: planar 4:4:4 RGB, 8 bits per channel (Since: 1.20)
* @GST_VIDEO_FORMAT_AV12: Planar 4:2:0 YUV with interleaved UV plane with alpha as 3rd plane (Since: 1.20)
*
* Enum value describing the most common video formats.
*
@ -271,6 +272,16 @@ typedef enum {
* Since: 1.20
*/
GST_VIDEO_FORMAT_BGRP,
/**
* GST_VIDEO_FORMAT_AV12:
*
* Planar 4:2:0 YUV with interleaved UV plane with alpha as
* 3rd plane.
*
* Since: 1.20
*/
GST_VIDEO_FORMAT_AV12,
} GstVideoFormat;
#define GST_VIDEO_MAX_PLANES 4
@ -645,7 +656,7 @@ gconstpointer gst_video_format_get_palette (GstVideoFormat format, gsi
"I422_10BE, I422_10LE, NV16_10LE32, Y210, v210, UYVP, I420_10BE, I420_10LE, " \
"P010_10BE, P010_10LE, NV12_10LE32, NV12_10LE40, Y444, GBR, RGBP, BGRP, NV24, xBGR, BGRx, " \
"xRGB, RGBx, BGR, IYU2, v308, RGB, Y42B, NV61, NV16, VYUY, UYVY, YVYU, YUY2, I420, " \
"YV12, NV21, NV12, NV12_64Z32, NV12_4L4, NV12_32L32, Y41B, IYU1, YVU9, YUV9, RGB16, " \
"YV12, NV21, NV12, NV12_64Z32, NV12_4L4, NV12_32L32, AV12, Y41B, IYU1, YVU9, YUV9, RGB16, " \
"BGR16, RGB15, BGR15, RGB8P, GRAY16_BE, GRAY16_LE, GRAY10_LE32, GRAY8 }"
#elif G_BYTE_ORDER == G_LITTLE_ENDIAN
#define GST_VIDEO_FORMATS_ALL "{ AYUV64, ARGB64, GBRA_12LE, GBRA_12BE, Y412_LE, " \
@ -657,7 +668,7 @@ gconstpointer gst_video_format_get_palette (GstVideoFormat format, gsi
"I422_10LE, I422_10BE, NV16_10LE32, Y210, v210, UYVP, I420_10LE, I420_10BE, " \
"P010_10LE, NV12_10LE32, NV12_10LE40, P010_10BE, Y444, GBR, RGBP, BGRP, NV24, xBGR, BGRx, " \
"xRGB, RGBx, BGR, IYU2, v308, RGB, Y42B, NV61, NV16, VYUY, UYVY, YVYU, YUY2, I420, " \
"YV12, NV21, NV12, NV12_64Z32, NV12_4L4, NV12_32L32, Y41B, IYU1, YVU9, YUV9, RGB16, " \
"YV12, NV21, NV12, NV12_64Z32, NV12_4L4, NV12_32L32, AV12, Y41B, IYU1, YVU9, YUV9, RGB16, " \
"BGR16, RGB15, BGR15, RGB8P, GRAY16_LE, GRAY16_BE, GRAY10_LE32, GRAY8 }"
#endif

View file

@ -944,6 +944,16 @@ fill_planes (GstVideoInfo * info, gsize plane_size[GST_VIDEO_MAX_PLANES])
cr_h = GST_ROUND_UP_2 (cr_h);
info->size = info->offset[1] + info->stride[0] * cr_h;
break;
case GST_VIDEO_FORMAT_AV12:
info->stride[0] = GST_ROUND_UP_4 (width);
info->stride[1] = info->stride[0];
info->stride[2] = info->stride[0];
info->offset[0] = 0;
info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height);
info->offset[2] =
info->offset[1] + (info->stride[1] * GST_ROUND_UP_2 (height) / 2);
info->size = info->offset[2] + info->stride[2] * GST_ROUND_UP_2 (height);
break;
case GST_VIDEO_FORMAT_NV16:
case GST_VIDEO_FORMAT_NV61:
info->stride[0] = GST_ROUND_UP_4 (width);

View file

@ -104,6 +104,16 @@ select0wb v, vv
select0lw ay, ayuv
select1wb y, ay
.function video_orc_pack_YA
.dest 1 y guint8
.dest 1 a guint8
.source 4 ayuv guint8
.temp 2 ay
select0lw ay, ayuv
select1wb y, ay
select0wb a, ay
.function video_orc_unpack_YUY2
.dest 8 ayuv guint8
.source 4 yuy2 guint8
@ -446,6 +456,31 @@ x2 splitlw uvuv, ay, ayuv
x2 select1wb y, ay
select0lw uv, uvuv
.function video_orc_unpack_AV12
.dest 8 d guint8
.source 2 y guint8
.source 2 uv guint8
.source 2 a guint8
.temp 4 ay
.temp 4 uvuv
mergewl uvuv, uv, uv
x2 mergebw ay, a, y
x2 mergewl d, ay, uvuv
.function video_orc_pack_AV12
.dest 2 y guint8
.dest 2 uv guint8
.dest 2 a guint8
.source 8 ayuv guint8
.temp 4 ay
.temp 4 uvuv
x2 splitlw uvuv, ay, ayuv
x2 select1wb y, ay
x2 select0wb a, ay
select0lw uv, uvuv
.function video_orc_unpack_NV21
.dest 8 d guint8
.source 2 y guint8