mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-10 17:35:59 +00:00
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>
This commit is contained in:
parent
cb75eda13b
commit
9bd8d608d5
2 changed files with 79 additions and 8 deletions
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue