summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathieu Duponchelle <mathieu@centricular.com>2021-08-10 00:53:57 +0200
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>2021-08-20 00:16:43 +0000
commit9bd8d608d5bae27ec5ff09e733f76ca32b17420c (patch)
tree006c24cb55c6c4e1d4b5139f055f9977ae8c4843
parentcb75eda13b20b5633546e40e7d8fcc0d479ee901 (diff)
downloadgstreamer-plugins-good-9bd8d608d5bae27ec5ff09e733f76ca32b17420c.tar.gz
matroska-mux: support H264 avc3 / H265 hev1
The matroska codec specs is unfortunately vague on the subject, stating for H264: AVC/H.264 stored as described in [@!ISO.14496-15] and for H265: HEVC/H.265 stored as described in [@!ISO.14496-15] This spec however specifies multiple stream formats, our implementation has opted for interpreting this as avc1 / hvc1, both of which disallow in-band SPS. Most decoders however will support in-band SPS / PPS, and this commit gives the option to explicitly mux in avc3 / hev1, which allows changing stream parameters on the fly, that is useful for smart encoding for example. When either of these stream formats are picked as the input, changes in codec_data / tier / level / profile do not cause renegotiation failure, a warning is logged however as it isn't clear how compliant such a stream is. The stream-format field is correctly ordered in the template caps to avoid selecting potentially non-compliant options on automatic negotiation. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/1047>
-rw-r--r--docs/gst_plugins_cache.json10
-rw-r--r--gst/matroska/matroska-mux.c77
2 files changed, 79 insertions, 8 deletions
diff --git a/docs/gst_plugins_cache.json b/docs/gst_plugins_cache.json
index 0d7eeb229..2e098f78f 100644
--- a/docs/gst_plugins_cache.json
+++ b/docs/gst_plugins_cache.json
@@ -7537,7 +7537,7 @@
"type": "GstQTMuxPad"
},
"video_%%u": {
- "caps": "video/x-h263:\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-h264:\n stream-format: avc\n alignment: au\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\n",
+ "caps": "video/x-h263:\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-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\n",
"direction": "sink",
"presence": "request",
"type": "GstQTMuxPad"
@@ -7592,7 +7592,7 @@
"type": "GstAggregatorPad"
},
"video_%%u": {
- "caps": "video/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-h264:\n stream-format: avc\n alignment: au\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\n",
+ "caps": "video/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-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\n",
"direction": "sink",
"presence": "request",
"type": "GstQTMuxPad"
@@ -7708,7 +7708,7 @@
"type": "GstQTMuxPad"
},
"video_%%u": {
- "caps": "video/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-h264:\n stream-format: avc\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-mp4-part:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-av1:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\n",
+ "caps": "video/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-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-mp4-part:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\nvideo/x-av1:\n width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\n",
"direction": "sink",
"presence": "request",
"type": "GstQTMuxPad"
@@ -7881,7 +7881,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: avc\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 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 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 width: [ 16, 2147483647 ]\n height: [ 16, 2147483647 ]\n",
"direction": "sink",
"presence": "request",
"type": "GstQTMuxPad"
@@ -9114,7 +9114,7 @@
"presence": "request"
},
"video_%%u": {
- "caps": "video/mpeg:\n mpegversion: { (int)1, (int)2, (int)4 }\n systemstream: false\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-h264:\n stream-format: avc\n alignment: au\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-h265:\n stream-format: hvc1\n alignment: au\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-divx:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-huffyuv:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-dv:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-h263:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-msmpeg:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nimage/jpeg:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-theora:\nvideo/x-dirac:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-pn-realvideo:\n rmversion: [ 1, 4 ]\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-vp8:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-vp9:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-raw:\n format: { YUY2, I420, YV12, UYVY, AYUV, GRAY8, BGR, RGB }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-prores:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-wmv:\n wmvversion: [ 1, 3 ]\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-av1:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n",
+ "caps": "video/mpeg:\n mpegversion: { (int)1, (int)2, (int)4 }\n systemstream: false\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-divx:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-huffyuv:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-dv:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-h263:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-msmpeg:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nimage/jpeg:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-theora:\nvideo/x-dirac:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-pn-realvideo:\n rmversion: [ 1, 4 ]\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-vp8:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-vp9:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-raw:\n format: { YUY2, I420, YV12, UYVY, AYUV, GRAY8, BGR, RGB }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-prores:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-wmv:\n wmvversion: [ 1, 3 ]\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\nvideo/x-av1:\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n",
"direction": "sink",
"presence": "request"
}
diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c
index 7dd8901ff..28970a6f1 100644
--- a/gst/matroska/matroska-mux.c
+++ b/gst/matroska/matroska-mux.c
@@ -114,9 +114,9 @@ static GstStaticPadTemplate videosink_templ =
"mpegversion = (int) { 1, 2, 4 }, "
"systemstream = (boolean) false, "
COMMON_VIDEO_CAPS "; "
- "video/x-h264, stream-format=avc, alignment=au, "
+ "video/x-h264, stream-format = (string) { avc, avc3 }, alignment=au, "
COMMON_VIDEO_CAPS "; "
- "video/x-h265, stream-format=hvc1, alignment=au, "
+ "video/x-h265, stream-format = (string) { hvc1, hev1 }, alignment=au, "
COMMON_VIDEO_CAPS "; "
"video/x-divx, "
COMMON_VIDEO_CAPS "; "
@@ -961,6 +961,63 @@ gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
context->codec_id = g_strdup (id);
}
+static gboolean
+check_field (GQuark field_id, const GValue * value, gpointer user_data)
+{
+ GstStructure *structure = (GstStructure *) user_data;
+ const gchar *name = gst_structure_get_name (structure);
+
+ if ((g_strcmp0 (name, "video/x-h264") == 0 &&
+ !g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
+ "avc3")) || (g_strcmp0 (name, "video/x-h265") == 0
+ && !g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
+ "hev1"))
+ ) {
+ /* While in theory, matroska only supports avc1 / hvc1, and doesn't support codec_data
+ * changes, in practice most decoders will use in-band SPS / PPS (avc3 / hev1), if the
+ * input stream is avc3 / hev1 we let the new codec_data slide to support "smart" encoding.
+ *
+ * We don't warn here as we already warned elsewhere.
+ */
+ if (field_id == g_quark_from_static_string ("codec_data")) {
+ return FALSE;
+ } else if (field_id == g_quark_from_static_string ("tier")) {
+ return FALSE;
+ } else if (field_id == g_quark_from_static_string ("profile")) {
+ return FALSE;
+ } else if (field_id == g_quark_from_static_string ("level")) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_new_caps (GstCaps * old_caps, GstCaps * new_caps)
+{
+ GstStructure *old_s, *new_s;
+ gboolean ret;
+
+ old_caps = gst_caps_copy (old_caps);
+ new_caps = gst_caps_copy (new_caps);
+
+ new_s = gst_caps_get_structure (new_caps, 0);
+ old_s = gst_caps_get_structure (old_caps, 0);
+
+ gst_structure_filter_and_map_in_place (new_s,
+ (GstStructureFilterMapFunc) check_field, new_s);
+ gst_structure_filter_and_map_in_place (old_s,
+ (GstStructureFilterMapFunc) check_field, old_s);
+
+ ret = gst_caps_is_subset (new_caps, old_caps);
+
+ gst_caps_unref (new_caps);
+ gst_caps_unref (old_caps);
+
+ return ret;
+}
+
/**
* gst_matroska_mux_video_pad_setcaps:
* @pad: Pad which got the caps.
@@ -991,7 +1048,7 @@ gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
if ((old_caps = gst_pad_get_current_caps (pad))) {
if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
- && !gst_caps_is_subset (caps, old_caps)) {
+ && !check_new_caps (old_caps, caps)) {
GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
("Caps changes are not supported by Matroska\nCurrent: `%"
GST_PTR_FORMAT "`\nNew: `%" GST_PTR_FORMAT "`", old_caps, caps));
@@ -1231,6 +1288,13 @@ skip_details:
gst_matroska_mux_set_codec_id (context,
GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
gst_matroska_mux_free_codec_priv (context);
+
+ if (!g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
+ "avc3")) {
+ GST_WARNING_OBJECT (mux,
+ "avc3 is not officially supported, only use this format for smart encoding");
+ }
+
/* Create avcC header */
if (codec_buf != NULL) {
context->codec_priv_size = gst_buffer_get_size (codec_buf);
@@ -1241,6 +1305,13 @@ skip_details:
gst_matroska_mux_set_codec_id (context,
GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
gst_matroska_mux_free_codec_priv (context);
+
+ if (!g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
+ "hev1")) {
+ GST_WARNING_OBJECT (mux,
+ "hev1 is not officially supported, only use this format for smart encoding");
+ }
+
/* Create hvcC header */
if (codec_buf != NULL) {
context->codec_priv_size = gst_buffer_get_size (codec_buf);