qtmux: add support for writing vpcC box for VP9

Increases compatibility for VP9 in .mov in at least VLC.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3260>
This commit is contained in:
Matthew Waters 2022-10-25 13:28:26 +11:00 committed by GStreamer Marge Bot
parent f77f27f4c9
commit 5bed545113
5 changed files with 118 additions and 3 deletions

View file

@ -8328,7 +8328,7 @@
"type": "GstQTMuxPad"
},
"video_%%u": {
"caps": "video/x-raw:\n format: { RGB, UYVY, v210 }\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/mpeg:\n mpegversion: 4\n systemstream: false\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-divx:\n divxversion: 5\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-prores:\n variant: { (string)standard, (string)lt, (string)hq, (string)proxy, (string)4444, (string)4444xq }\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-cineform:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-h263:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-svq:\n svqversion: 3\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-dv:\n systemstream: false\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nimage/jpeg:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nimage/png:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-vp8:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-vp9:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-dirac:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-qt-part:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-av1:\n alignment: tu\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\n",
"caps": "video/x-raw:\n format: { RGB, UYVY, v210 }\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/mpeg:\n mpegversion: 4\n systemstream: false\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-divx:\n divxversion: 5\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-prores:\n variant: { (string)standard, (string)lt, (string)hq, (string)proxy, (string)4444, (string)4444xq }\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-cineform:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-h263:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-svq:\n svqversion: 3\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-dv:\n systemstream: false\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nimage/jpeg:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nimage/png:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-vp8:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-vp9:\n profile: { (string)0, (string)1, (string)2, (string)3 }\n chroma-format: { (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-dirac:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-qt-part:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-av1:\n alignment: tu\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\n",
"direction": "sink",
"presence": "request",
"type": "GstQTMuxPad"

View file

@ -5766,3 +5766,50 @@ build_uuid_xmp_atom (GstBuffer * xmp_data)
return build_atom_info_wrapper ((Atom *) uuid, atom_uuid_copy_data,
atom_uuid_free);
}
/* https://www.webmproject.org/vp9/mp4/#vp-codec-configuration-box */
AtomInfo *
build_vpcC_extension (guint8 profile, guint8 level, guint8 bit_depth,
guint8 chroma_subsampling, gboolean video_full_range,
guint8 colour_primaries, guint8 transfer_characteristics,
guint8 matrix_coefficients)
{
AtomData *atom_data;
guint8 *data_block;
guint data_block_len;
GstByteWriter bw;
gboolean hdl = TRUE;
guint8 val = 0;
gst_byte_writer_init (&bw);
/* version, always 1 */
hdl &= gst_byte_writer_put_uint8 (&bw, 1);
/* flags of 24 bits */
hdl &= gst_byte_writer_put_uint8 (&bw, 0);
hdl &= gst_byte_writer_put_uint8 (&bw, 0);
hdl &= gst_byte_writer_put_uint8 (&bw, 0);
hdl &= gst_byte_writer_put_uint8 (&bw, profile);
hdl &= gst_byte_writer_put_uint8 (&bw, level);
val |= (bit_depth & 0xF) << 4;
val |= (chroma_subsampling & 0x3) << 1;
val |= !(!video_full_range);
hdl &= gst_byte_writer_put_uint8 (&bw, val);
hdl &= gst_byte_writer_put_uint8 (&bw, colour_primaries);
hdl &= gst_byte_writer_put_uint8 (&bw, transfer_characteristics);
hdl &= gst_byte_writer_put_uint8 (&bw, matrix_coefficients);
/* codec initialization data, currently unused */
hdl &= gst_byte_writer_put_uint16_le (&bw, 0);
if (!hdl) {
GST_WARNING ("error creating header");
return NULL;
}
data_block_len = gst_byte_writer_get_size (&bw);
data_block = gst_byte_writer_reset_and_get_data (&bw);
atom_data = atom_data_new_from_data (FOURCC_vpcC, data_block, data_block_len);
g_free (data_block);
return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
atom_data_free);
}

View file

@ -1118,6 +1118,10 @@ AtomInfo * build_SMI_atom (const GstBuffer *seqh);
AtomInfo * build_ima_adpcm_extension (gint channels, gint rate,
gint blocksize);
AtomInfo * build_uuid_xmp_atom (GstBuffer * xmp);
AtomInfo * build_vpcC_extension (guint8 profile, guint8 level, guint8 bit_depth,
guint8 chroma_subsampling, gboolean video_full_range,
guint8 colour_primaries, guint8 transfer_characteristics,
guint8 matrix_coefficients);
/*

View file

@ -6484,7 +6484,66 @@ gst_qt_mux_video_sink_set_caps (GstQTMuxPad * qtpad, GstCaps * caps)
} else if (strcmp (mimetype, "video/x-vp8") == 0) {
entry.fourcc = FOURCC_vp08;
} else if (strcmp (mimetype, "video/x-vp9") == 0) {
const char *profile_str, *chroma_format_str, *colorimetry_str;
guint bitdepth_luma, bitdepth_chroma;
guint8 profile = -1, chroma_format = -1;
gboolean video_full_range;
GstVideoColorimetry cinfo = { 0, };
entry.fourcc = FOURCC_vp09;
profile_str = gst_structure_get_string (structure, "profile");
if (g_strcmp0 (profile_str, "0") == 0) {
profile = 0;
} else if (g_strcmp0 (profile_str, "1") == 0) {
profile = 1;
} else if (g_strcmp0 (profile_str, "2") == 0) {
profile = 2;
} else if (g_strcmp0 (profile_str, "3") == 0) {
profile = 3;
}
colorimetry_str = gst_structure_get_string (structure, "colorimetry");
gst_video_colorimetry_from_string (&cinfo, colorimetry_str);
video_full_range = cinfo.range == GST_VIDEO_COLOR_RANGE_0_255;
chroma_format_str = gst_structure_get_string (structure, "chroma-format");
if (g_strcmp0 (chroma_format_str, "4:2:0") == 0) {
const char *chroma_site_str;
GstVideoChromaSite chroma_site;
chroma_site_str = gst_structure_get_string (structure, "chroma-site");
chroma_site = gst_video_chroma_site_from_string (chroma_site_str);
if (chroma_site == GST_VIDEO_CHROMA_SITE_V_COSITED) {
chroma_format = 0;
} else if (chroma_site == GST_VIDEO_CHROMA_SITE_COSITED) {
chroma_format = 1;
} else {
chroma_format = 1;
}
} else if (g_strcmp0 (chroma_format_str, "4:2:2") == 0) {
chroma_format = 2;
} else if (g_strcmp0 (chroma_format_str, "4:4:4") == 0) {
chroma_format = 3;
}
gst_structure_get (structure, "bit-depth-luma", G_TYPE_UINT,
&bitdepth_luma, "bit-depth-chroma", G_TYPE_UINT, &bitdepth_chroma,
NULL);
if (profile == 0xFF || chroma_format == 0xFF
|| bitdepth_luma != bitdepth_chroma || bitdepth_luma == 0) {
GST_WARNING_OBJECT (qtmux, "cannot construct vpcC atom from "
"incomplete caps");
} else {
ext_atom = build_vpcC_extension (profile, /* XXX: level */ 10,
bitdepth_luma, chroma_format, video_full_range,
gst_video_color_primaries_to_iso (cinfo.primaries),
gst_video_transfer_function_to_iso (cinfo.transfer),
gst_video_color_matrix_to_iso (cinfo.matrix));
if (ext_atom)
ext_atom_list = g_list_append (ext_atom_list, ext_atom);
}
} else if (strcmp (mimetype, "video/x-dirac") == 0) {
entry.fourcc = FOURCC_drac;
} else if (strcmp (mimetype, "video/x-qt-part") == 0) {

View file

@ -93,6 +93,12 @@
"svqversion = (int) 3, " \
COMMON_VIDEO_CAPS
#define VP9_CAPS \
"video/x-vp9, " \
"profile = (string) { 0, 1, 2, 3 }, " \
"chroma-format = (string) { 4:2:0, 4:2:2, 4:4:4 }, " \
COMMON_VIDEO_CAPS
#define COMMON_AUDIO_CAPS(c, r) \
"channels = (int) [ 1, " G_STRINGIFY (c) " ], " \
"rate = (int) [ 1, " G_STRINGIFY (r) " ]"
@ -201,8 +207,7 @@ GstQTMuxFormatProp gst_qt_mux_format_list[] = {
COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
"video/x-vp8, "
COMMON_VIDEO_CAPS "; "
"video/x-vp9, "
COMMON_VIDEO_CAPS "; "
VP9_CAPS "; "
"video/x-dirac, "
COMMON_VIDEO_CAPS "; " "video/x-qt-part, " COMMON_VIDEO_CAPS "; "
"video/x-av1, " "alignment = (string) \"tu\", "