mpegts: Add custom mapping for vp9

This is a custom mapping. There isn't much needed apart from that to store vp9
in mpeg-ts since the bitstream is self contained.

Since there are no official specification we don't want people to be mistaken in
believing that. Therefore that mapping is only used in the muxer if the (new)
property `enable-custom-mappings` is set to TRUE.

* The MPEG-TS Stream Type is Private Data (0x6) with the registration descriptor
  set to `VP09`.
* The Access Unit are VP9 frames stored in PES packets
* As there is no emulation prevention byte in VP9 elementary stream, the can be
  misdetection of PES start code. To avoid this, the start of a PES packet must
  be signalled using the Payload Unit Start Indicator in the transport packet
  header

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7707>
This commit is contained in:
Edward Hervey 2024-10-22 08:42:17 +02:00
parent 23ce0d2901
commit 244b00ac08
8 changed files with 65 additions and 3 deletions

View file

@ -222936,7 +222936,7 @@
"presence": "sometimes"
},
"video_%%01x_%%05x": {
"caps": "video/mpeg:\n mpegversion: { (int)1, (int)2, (int)4 }\n systemstream: false\nvideo/x-h264:\n stream-format: byte-stream\nvideo/x-h265:\n stream-format: byte-stream\nvideo/x-dirac:\nvideo/x-cavs:\nvideo/x-wmv:\n wmvversion: 3\n format: WVC1\nimage/x-jpc:\nimage/x-jxsc:\n",
"caps": "video/mpeg:\n mpegversion: { (int)1, (int)2, (int)4 }\n systemstream: false\nvideo/x-h264:\n stream-format: byte-stream\nvideo/x-h265:\n stream-format: byte-stream\nvideo/x-vp9:\nvideo/x-dirac:\nvideo/x-cavs:\nvideo/x-wmv:\n wmvversion: 3\n format: WVC1\nimage/x-jpc:\nimage/x-jxsc:\n",
"direction": "src",
"presence": "sometimes"
}
@ -223194,7 +223194,7 @@
"long-name": "MPEG Transport Stream Muxer",
"pad-templates": {
"sink_%%d": {
"caps": "video/mpeg:\n parsed: true\n mpegversion: { (int)1, (int)2, (int)4 }\n systemstream: false\nvideo/x-dirac:\nimage/x-jpc:\n alignment: frame\nvideo/x-h264:\n stream-format: byte-stream\n alignment: { (string)au, (string)nal }\nvideo/x-h265:\n stream-format: byte-stream\n alignment: { (string)au, (string)nal }\naudio/mpeg:\n parsed: true\n mpegversion: 1\naudio/mpeg:\n framed: true\n mpegversion: { (int)2, (int)4 }\n stream-format: { (string)adts, (string)raw }\naudio/x-lpcm:\n width: { (int)16, (int)20, (int)24 }\n rate: { (int)48000, (int)96000 }\n channels: [ 1, 8 ]\n dynamic_range: [ 0, 255 ]\n emphasis: { (boolean)false, (boolean)true }\n mute: { (boolean)false, (boolean)true }\naudio/x-ac3:\n framed: true\naudio/x-dts:\n framed: true\naudio/x-opus:\n channels: [ 1, 255 ]\naudio/x-smpte-302m:\nsubpicture/x-dvb:\napplication/x-teletext:\nmeta/x-klv:\n parsed: true\nmeta/x-id3:\n parsed: true\nmeta/x-st-2038:\n alignment: line\nimage/x-jpc:\n alignment: frame\n profile: [ 0, 49151 ]\nimage/x-jxsc:\n alignment: frame\n sampling: { (string)YCbCr-4:2:2, (string)YCbCr-4:4:4 }\n",
"caps": "video/mpeg:\n parsed: true\n mpegversion: { (int)1, (int)2, (int)4 }\n systemstream: false\nvideo/x-dirac:\nimage/x-jpc:\n alignment: frame\nvideo/x-h264:\n stream-format: byte-stream\n alignment: { (string)au, (string)nal }\nvideo/x-h265:\n stream-format: byte-stream\n alignment: { (string)au, (string)nal }\naudio/mpeg:\n parsed: true\n mpegversion: 1\naudio/mpeg:\n framed: true\n mpegversion: { (int)2, (int)4 }\n stream-format: { (string)adts, (string)raw }\naudio/x-lpcm:\n width: { (int)16, (int)20, (int)24 }\n rate: { (int)48000, (int)96000 }\n channels: [ 1, 8 ]\n dynamic_range: [ 0, 255 ]\n emphasis: { (boolean)false, (boolean)true }\n mute: { (boolean)false, (boolean)true }\naudio/x-ac3:\n framed: true\naudio/x-dts:\n framed: true\naudio/x-opus:\n channels: [ 1, 255 ]\naudio/x-smpte-302m:\nsubpicture/x-dvb:\napplication/x-teletext:\nmeta/x-klv:\n parsed: true\nmeta/x-id3:\n parsed: true\nmeta/x-st-2038:\n alignment: line\nvideo/x-vp9:\nimage/x-jpc:\n alignment: frame\n profile: [ 0, 49151 ]\nimage/x-jxsc:\n alignment: frame\n sampling: { (string)YCbCr-4:2:2, (string)YCbCr-4:4:4 }\n",
"direction": "sink",
"presence": "request",
"type": "GstBaseTsMuxPad"
@ -223265,6 +223265,18 @@
"type": "guint64",
"writable": true
},
"enable-custom-mappings": {
"blurb": "Enable custom mappings for which there are no official specifications",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "false",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": true
},
"pat-interval": {
"blurb": "Set the interval (in ticks of the 90kHz clock) for writing out the PAT table",
"conditionally-available": false,

View file

@ -241,5 +241,6 @@
#define DRF_ID_AC4 0x41432D34 /* defined in ETSI TS 103 190-2 Annex D */
#define DRF_ID_ID3 0x49443320 /* defined in SMPTE registration authority */
#define DRF_ID_VANC 0x56414e43 /* defined in SMPTE ST-2038 */
#define DRF_ID_VP09 0x56503039 /* Custom GStreamer vp9 */
#endif /* __GST_MPEG_DESC_H__ */

View file

@ -229,6 +229,7 @@ struct _TSDemuxStream
"systemstream = (boolean) FALSE; " \
"video/x-h264,stream-format=(string)byte-stream;" \
"video/x-h265,stream-format=(string)byte-stream;" \
"video/x-vp9;" \
"video/x-dirac;" \
"video/x-cavs;" \
"video/x-wmv," \
@ -1674,6 +1675,10 @@ create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream,
is_audio = TRUE;
caps = gst_caps_new_empty_simple ("audio/x-ac4");
break;
case DRF_ID_VP09:
is_video = TRUE;
caps = gst_caps_new_empty_simple ("video/x-vp9");
break;
case DRF_ID_VANC:
is_private = TRUE;
caps =

View file

@ -256,10 +256,12 @@ enum
PROP_BITRATE,
PROP_PCR_INTERVAL,
PROP_SCTE_35_PID,
PROP_SCTE_35_NULL_INTERVAL
PROP_SCTE_35_NULL_INTERVAL,
PROP_ENABLE_CUSTOM_MAPPINGS
};
#define DEFAULT_SCTE_35_PID 0
#define DEFAULT_ENABLE_CUSTOM_MAPPINGS FALSE
#define BASETSMUX_DEFAULT_ALIGNMENT -1
@ -624,6 +626,13 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
st = TSMUX_ST_VIDEO_H264;
} else if (strcmp (mt, "video/x-h265") == 0) {
st = TSMUX_ST_VIDEO_HEVC;
} else if (strcmp (mt, "video/x-vp9") == 0) {
if (mux->enable_custom_mappings) {
st = TSMUX_ST_PS_VP9;
} else {
GST_ERROR_OBJECT (mux,
"VP9 requires enabling custom mapping which does not have a public specification");
}
} else if (strcmp (mt, "audio/mpeg") == 0) {
gint mpegversion;
@ -2880,6 +2889,9 @@ gst_base_ts_mux_set_property (GObject * object, guint prop_id,
case PROP_SCTE_35_NULL_INTERVAL:
mux->scte35_null_interval = g_value_get_uint (value);
break;
case PROP_ENABLE_CUSTOM_MAPPINGS:
mux->enable_custom_mappings = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -2920,6 +2932,9 @@ gst_base_ts_mux_get_property (GObject * object, guint prop_id,
case PROP_SCTE_35_NULL_INTERVAL:
g_value_set_uint (value, mux->scte35_null_interval);
break;
case PROP_ENABLE_CUSTOM_MAPPINGS:
g_value_set_boolean (value, mux->enable_custom_mappings);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -3074,6 +3089,20 @@ gst_base_ts_mux_class_init (GstBaseTsMuxClass * klass)
TSMUX_DEFAULT_SCTE_35_NULL_INTERVAL,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
/**
* GstBaseTsMux:enable-custom-mappings:
*
* Enable custom mappings for which there are no official specifications
*
* Since: 1.26
*/
g_object_class_install_property (G_OBJECT_CLASS (klass),
PROP_ENABLE_CUSTOM_MAPPINGS,
g_param_spec_boolean ("enable-custom-mappings", "Enable custom mappings",
"Enable custom mappings for which there are no official specifications",
DEFAULT_ENABLE_CUSTOM_MAPPINGS,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
&gst_base_ts_mux_src_factory, GST_TYPE_AGGREGATOR_PAD);
@ -3095,6 +3124,7 @@ gst_base_ts_mux_init (GstBaseTsMux * mux)
mux->bitrate = TSMUX_DEFAULT_BITRATE;
mux->scte35_pid = DEFAULT_SCTE_35_PID;
mux->scte35_null_interval = TSMUX_DEFAULT_SCTE_35_NULL_INTERVAL;
mux->enable_custom_mappings = DEFAULT_ENABLE_CUSTOM_MAPPINGS;
mux->packet_size = GST_BASE_TS_MUX_NORMAL_PACKET_LENGTH;
mux->automatic_alignment = 0;

View file

@ -164,6 +164,7 @@ struct GstBaseTsMux {
guint scte35_pid;
guint scte35_null_interval;
guint32 last_scte35_event_seqnum;
gboolean enable_custom_mappings;
/* state */
gboolean first;

View file

@ -128,6 +128,7 @@ static GstStaticPadTemplate gst_mpeg_ts_mux_sink_factory =
"subpicture/x-dvb; application/x-teletext; meta/x-klv, parsed=true;"
"meta/x-id3, parsed=true;"
"meta/x-st-2038, alignment = (string) line;"
"video/x-vp9;"
"image/x-jpc, alignment = (string) frame, profile = (int)[0, 49151];"
"image/x-jxsc, alignment = (string) frame, sampling = { YCbCr-4:2:2, YCbCr-4:4:4 };"));

View file

@ -158,6 +158,12 @@ tsmux_stream_new (guint16 pid, guint stream_type, guint stream_number)
stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
stream->gst_stream_type = GST_STREAM_TYPE_VIDEO;
break;
case TSMUX_ST_PS_VP9:
stream->id = 0xBD; /* Private Stream 1 */
stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
stream->stream_type = TSMUX_ST_PRIVATE_DATA;
stream->gst_stream_type = GST_STREAM_TYPE_VIDEO;
break;
case TSMUX_ST_AUDIO_AAC:
case TSMUX_ST_AUDIO_MPEG1:
case TSMUX_ST_AUDIO_MPEG2:
@ -1034,6 +1040,11 @@ tsmux_stream_default_get_es_descrs (TsMuxStream * stream,
GST_DEBUG ("adding SMPTE 302M registration descriptor");
g_ptr_array_add (pmt_stream->descriptors, descriptor);
}
if (stream->internal_stream_type == TSMUX_ST_PS_VP9) {
descriptor = gst_mpegts_descriptor_from_registration ("VP09", NULL, 0);
GST_DEBUG ("adding VP09 registration descriptor");
g_ptr_array_add (pmt_stream->descriptors, descriptor);
}
default:
break;
}

View file

@ -135,6 +135,7 @@ enum TsMuxStreamType {
TSMUX_ST_PS_ID3 = 0x90, /* only used internally */
TSMUX_ST_PS_ST_2038 = 0x91, /* only used internally */
TSMUX_ST_PS_S302M = 0x92, /* only used internally */
TSMUX_ST_PS_VP9 = 0x93, /* only used internally */
TSMUX_ST_PS_DVD_SUBPICTURE = 0xff,
/* Non-standard definitions */