mpegts: Add support for JPEG-XS

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7172>
This commit is contained in:
Edward Hervey 2024-07-15 16:10:10 +02:00 committed by Tim-Philipp Müller
parent 838ad5c7e4
commit 2e8afcf51a
13 changed files with 822 additions and 7 deletions

View file

@ -1311,6 +1311,20 @@ two bytes are the @tag and @length.</doc>
<type name="gpointer" c:type="gpointer"/> <type name="gpointer" c:type="gpointer"/>
</array> </array>
</field> </field>
<method name="copy" c:identifier="gst_mpegts_descriptor_copy" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">Copy the given descriptor.</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
<return-value transfer-ownership="full">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">A copy of @desc.</doc>
<type name="Descriptor" c:type="GstMpegtsDescriptor*"/>
</return-value>
<parameters>
<instance-parameter name="desc" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">A #GstMpegtsDescriptor:</doc>
<type name="Descriptor" c:type="GstMpegtsDescriptor*"/>
</instance-parameter>
</parameters>
</method>
<method name="free" c:identifier="gst_mpegts_descriptor_free"> <method name="free" c:identifier="gst_mpegts_descriptor_free">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">Frees @desc</doc> <doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">Frees @desc</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/> <source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
@ -2047,6 +2061,24 @@ ISO 639-1 language code from the returned ISO 639-2 one.</doc>
</instance-parameter> </instance-parameter>
</parameters> </parameters>
</method> </method>
<method name="parse_jpeg_xs" c:identifier="gst_mpegts_descriptor_parse_jpeg_xs" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">Parses the JPEG-XS descriptor information from @descriptor:</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
<return-value transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">TRUE if the information could be parsed, else FALSE.</doc>
<type name="gboolean" c:type="gboolean"/>
</return-value>
<parameters>
<instance-parameter name="descriptor" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">A #GstMpegtsDescriptor</doc>
<type name="Descriptor" c:type="const GstMpegtsDescriptor*"/>
</instance-parameter>
<parameter name="res" direction="out" caller-allocates="1" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">A parsed #GstMpegtsJpegXsDescriptor</doc>
<type name="JpegXsDescriptor" c:type="GstMpegtsJpegXsDescriptor*"/>
</parameter>
</parameters>
</method>
<method name="parse_logical_channel" c:identifier="gst_mpegts_descriptor_parse_logical_channel"> <method name="parse_logical_channel" c:identifier="gst_mpegts_descriptor_parse_logical_channel">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">Extracts the logical channels from @descriptor.</doc> <doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">Extracts the logical channels from @descriptor.</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/> <source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
@ -2310,6 +2342,20 @@ a single language</doc>
</parameter> </parameter>
</parameters> </parameters>
</function> </function>
<function name="from_jpeg_xs" c:identifier="gst_mpegts_descriptor_from_jpeg_xs" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">Create a new #GstMpegtsDescriptor based on the information in @jpegxs</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
<return-value transfer-ownership="full">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">The #GstMpegtsDescriptor</doc>
<type name="Descriptor" c:type="GstMpegtsDescriptor*"/>
</return-value>
<parameters>
<parameter name="jpegxs" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">A #GstMpegtsJpegXsDescriptor</doc>
<type name="JpegXsDescriptor" c:type="const GstMpegtsJpegXsDescriptor*"/>
</parameter>
</parameters>
</function>
<function name="from_metadata" c:identifier="gst_mpegts_descriptor_from_metadata" version="1.26"> <function name="from_metadata" c:identifier="gst_mpegts_descriptor_from_metadata" version="1.26">
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/> <source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
<return-value transfer-ownership="full"> <return-value transfer-ownership="full">
@ -2799,6 +2845,94 @@ Consult the relevant specifications for more details.</doc>
<member name="visual_impaired_commentary" value="3" c:identifier="GST_MPEGTS_AUDIO_TYPE_VISUAL_IMPAIRED_COMMENTARY"> <member name="visual_impaired_commentary" value="3" c:identifier="GST_MPEGTS_AUDIO_TYPE_VISUAL_IMPAIRED_COMMENTARY">
</member> </member>
</enumeration> </enumeration>
<record name="JpegXsDescriptor" c:type="GstMpegtsJpegXsDescriptor" version="1.26" glib:type-name="GstMpegtsJpegXsDescriptor" glib:get-type="gst_mpegts_jpeg_xs_descriptor_get_type" c:symbol-prefix="jpeg_xs_descriptor">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h">JPEG-XS descriptor</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
<field name="descriptor_version" writable="1">
<type name="guint8" c:type="guint8"/>
</field>
<field name="horizontal_size" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="vertical_size" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="brat" writable="1">
<type name="guint32" c:type="guint32"/>
</field>
<field name="frat" writable="1">
<type name="guint32" c:type="guint32"/>
</field>
<field name="schar" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="Ppih" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="Plev" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="max_buffer_size" writable="1">
<type name="guint32" c:type="guint32"/>
</field>
<field name="buffer_model_type" writable="1">
<type name="guint8" c:type="guint8"/>
</field>
<field name="colour_primaries" writable="1">
<type name="guint8" c:type="guint8"/>
</field>
<field name="transfer_characteristics" writable="1">
<type name="guint8" c:type="guint8"/>
</field>
<field name="matrix_coefficients" writable="1">
<type name="guint8" c:type="guint8"/>
</field>
<field name="video_full_range_flag" writable="1">
<type name="gboolean" c:type="gboolean"/>
</field>
<field name="still_mode" writable="1">
<type name="gboolean" c:type="gboolean"/>
</field>
<field name="mdm_flag" writable="1">
<type name="gboolean" c:type="gboolean"/>
</field>
<field name="X_c0" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="Y_c0" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="X_c1" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="Y_c1" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="X_c2" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="Y_c2" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="X_wp" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="Y_wp" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="L_max" writable="1">
<type name="guint32" c:type="guint32"/>
</field>
<field name="L_min" writable="1">
<type name="guint32" c:type="guint32"/>
</field>
<field name="MaxCLL" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
<field name="MaxFALL" writable="1">
<type name="guint16" c:type="guint16"/>
</field>
</record>
<record name="LogicalChannel" c:type="GstMpegtsLogicalChannel" glib:type-name="GstMpegtsLogicalChannel" glib:get-type="gst_mpegts_logical_channel_get_type" c:symbol-prefix="logical_channel"> <record name="LogicalChannel" c:type="GstMpegtsLogicalChannel" glib:type-name="GstMpegtsLogicalChannel" glib:get-type="gst_mpegts_logical_channel_get_type" c:symbol-prefix="logical_channel">
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/> <source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
<field name="service_id" writable="1"> <field name="service_id" writable="1">
@ -4746,6 +4880,9 @@ profiles defined in Annex A for service-compatible stereoscopic 3D services</doc
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtssection.h">Rec. ITU-T H.265 | ISO/IEC 23008-2 video <doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtssection.h">Rec. ITU-T H.265 | ISO/IEC 23008-2 video
stream or an HEVC temporal video sub-bitstream</doc> stream or an HEVC temporal video sub-bitstream</doc>
</member> </member>
<member name="video_jpeg_xs" value="50" c:identifier="GST_MPEGTS_STREAM_TYPE_VIDEO_JPEG_XS" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtssection.h">JPEG-XS stream type</doc>
</member>
<member name="ipmp_stream" value="127" c:identifier="GST_MPEGTS_STREAM_TYPE_IPMP_STREAM"> <member name="ipmp_stream" value="127" c:identifier="GST_MPEGTS_STREAM_TYPE_IPMP_STREAM">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtssection.h">IPMP stream</doc> <doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtssection.h">IPMP stream</doc>
</member> </member>
@ -5091,6 +5228,20 @@ a single language</doc>
</parameter> </parameter>
</parameters> </parameters>
</function> </function>
<function name="descriptor_from_jpeg_xs" c:identifier="gst_mpegts_descriptor_from_jpeg_xs" moved-to="Descriptor.from_jpeg_xs" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">Create a new #GstMpegtsDescriptor based on the information in @jpegxs</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
<return-value transfer-ownership="full">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">The #GstMpegtsDescriptor</doc>
<type name="Descriptor" c:type="GstMpegtsDescriptor*"/>
</return-value>
<parameters>
<parameter name="jpegxs" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.c">A #GstMpegtsJpegXsDescriptor</doc>
<type name="JpegXsDescriptor" c:type="const GstMpegtsJpegXsDescriptor*"/>
</parameter>
</parameters>
</function>
<function name="descriptor_from_metadata" c:identifier="gst_mpegts_descriptor_from_metadata" moved-to="Descriptor.from_metadata" version="1.26"> <function name="descriptor_from_metadata" c:identifier="gst_mpegts_descriptor_from_metadata" moved-to="Descriptor.from_metadata" version="1.26">
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/> <source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gstmpegtsdescriptor.h"/>
<return-value transfer-ownership="full"> <return-value transfer-ownership="full">

View file

@ -220605,7 +220605,7 @@
"presence": "sometimes" "presence": "sometimes"
}, },
"video_%%01x_%%05x": { "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:\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-dirac:\nvideo/x-cavs:\nvideo/x-wmv:\n wmvversion: 3\n format: WVC1\nimage/x-jpc:\nimage/x-jxsc:\n",
"direction": "src", "direction": "src",
"presence": "sometimes" "presence": "sometimes"
} }
@ -220863,7 +220863,7 @@
"long-name": "MPEG Transport Stream Muxer", "long-name": "MPEG Transport Stream Muxer",
"pad-templates": { "pad-templates": {
"sink_%%d": { "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 ]\nsubpicture/x-dvb:\napplication/x-teletext:\nmeta/x-klv:\n parsed: true\nmeta/x-id3:\n parsed: true\nimage/x-jpc:\n alignment: frame\n profile: [ 0, 49151 ]\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 ]\nsubpicture/x-dvb:\napplication/x-teletext:\nmeta/x-klv:\n parsed: true\nmeta/x-id3:\n parsed: true\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", "direction": "sink",
"presence": "request", "presence": "request",
"type": "GstBaseTsMuxPad" "type": "GstBaseTsMuxPad"

View file

@ -696,8 +696,18 @@ _new_descriptor_with_extension (guint8 tag, guint8 tag_extension, guint8 length)
return descriptor; return descriptor;
} }
static GstMpegtsDescriptor * /**
_copy_descriptor (GstMpegtsDescriptor * desc) * gst_mpegts_descriptor_copy:
* @desc: (transfer none): A #GstMpegtsDescriptor:
*
* Copy the given descriptor.
*
* Returns: (transfer full): A copy of @desc.
*
* Since: 1.26
*/
GstMpegtsDescriptor *
gst_mpegts_descriptor_copy (GstMpegtsDescriptor * desc)
{ {
GstMpegtsDescriptor *copy; GstMpegtsDescriptor *copy;
@ -721,7 +731,7 @@ gst_mpegts_descriptor_free (GstMpegtsDescriptor * desc)
} }
G_DEFINE_BOXED_TYPE (GstMpegtsDescriptor, gst_mpegts_descriptor, G_DEFINE_BOXED_TYPE (GstMpegtsDescriptor, gst_mpegts_descriptor,
(GBoxedCopyFunc) _copy_descriptor, (GBoxedCopyFunc) gst_mpegts_descriptor_copy,
(GBoxedFreeFunc) gst_mpegts_descriptor_free); (GBoxedFreeFunc) gst_mpegts_descriptor_free);
/** /**
@ -1544,3 +1554,167 @@ gst_mpegts_descriptor_from_metadata_pointer (const
return descriptor; return descriptor;
} }
DEFINE_STATIC_COPY_FUNCTION (GstMpegtsJpegXsDescriptor,
gst_mpegts_jpeg_xs_descriptor);
DEFINE_STATIC_FREE_FUNCTION (GstMpegtsJpegXsDescriptor,
gst_mpegts_jpeg_xs_descriptor);
G_DEFINE_BOXED_TYPE (GstMpegtsJpegXsDescriptor, gst_mpegts_jpeg_xs_descriptor,
(GBoxedCopyFunc) _gst_mpegts_jpeg_xs_descriptor_copy,
(GFreeFunc) _gst_mpegts_jpeg_xs_descriptor_free);
/**
* gst_mpegts_descriptor_parse_jpeg_xs:
* @descriptor: A #GstMpegtsDescriptor
* @res: (out): A parsed #GstMpegtsJpegXsDescriptor
*
* Parses the JPEG-XS descriptor information from @descriptor:
*
* Returns: TRUE if the information could be parsed, else FALSE.
*
* Since: 1.26
*/
gboolean
gst_mpegts_descriptor_parse_jpeg_xs (const GstMpegtsDescriptor * descriptor,
GstMpegtsJpegXsDescriptor * res)
{
GstByteReader br;
guint8 flags;
g_return_val_if_fail (descriptor != NULL && res != NULL, FALSE);
/* The smallest jpegxs descriptor doesn't contain the MDM, but is an H.222.0 extension (so additional one byte) */
__common_desc_ext_checks (descriptor, GST_MTS_DESC_EXT_JXS_VIDEO, 32, FALSE);
/* Skip tag/length/extension/tag/length */
gst_byte_reader_init (&br, descriptor->data + 5, descriptor->length - 3);
memset (res, 0, sizeof (*res));
/* First part can be scanned out with unchecked reader */
res->descriptor_version = gst_byte_reader_get_uint8_unchecked (&br);
if (res->descriptor_version != 0) {
GST_WARNING ("Unsupported JPEG-XS descriptor version (%d != 0)",
res->descriptor_version);
return FALSE;
}
res->horizontal_size = gst_byte_reader_get_uint16_be_unchecked (&br);
res->vertical_size = gst_byte_reader_get_uint16_be_unchecked (&br);
res->brat = gst_byte_reader_get_uint32_be_unchecked (&br);
res->frat = gst_byte_reader_get_uint32_be_unchecked (&br);
res->schar = gst_byte_reader_get_uint16_be_unchecked (&br);
res->Ppih = gst_byte_reader_get_uint16_be_unchecked (&br);
res->Plev = gst_byte_reader_get_uint16_be_unchecked (&br);
res->max_buffer_size = gst_byte_reader_get_uint32_be_unchecked (&br);
res->buffer_model_type = gst_byte_reader_get_uint8_unchecked (&br);
res->colour_primaries = gst_byte_reader_get_uint8_unchecked (&br);
res->transfer_characteristics = gst_byte_reader_get_uint8_unchecked (&br);
res->matrix_coefficients = gst_byte_reader_get_uint8_unchecked (&br);
res->video_full_range_flag =
(gst_byte_reader_get_uint8_unchecked (&br) & 0x80) == 0x80;
flags = gst_byte_reader_get_uint8_unchecked (&br);
res->still_mode = flags >> 7;
if ((flags & 0x40) == 0x40) {
if (gst_byte_reader_get_remaining (&br) < 28) {
GST_ERROR ("MDM present on JPEG-XS descriptor but not enough bytes");
return FALSE;
}
res->X_c0 = gst_byte_reader_get_uint16_be_unchecked (&br);
res->Y_c0 = gst_byte_reader_get_uint16_be_unchecked (&br);
res->X_c1 = gst_byte_reader_get_uint16_be_unchecked (&br);
res->Y_c1 = gst_byte_reader_get_uint16_be_unchecked (&br);
res->X_c2 = gst_byte_reader_get_uint16_be_unchecked (&br);
res->Y_c2 = gst_byte_reader_get_uint16_be_unchecked (&br);
res->X_wp = gst_byte_reader_get_uint16_be_unchecked (&br);
res->Y_wp = gst_byte_reader_get_uint16_be_unchecked (&br);
res->L_max = gst_byte_reader_get_uint32_be_unchecked (&br);
res->L_min = gst_byte_reader_get_uint32_be_unchecked (&br);
res->MaxCLL = gst_byte_reader_get_uint16_be_unchecked (&br);
res->MaxFALL = gst_byte_reader_get_uint16_be_unchecked (&br);
}
return TRUE;
}
/**
* gst_mpegts_descriptor_from_jpeg_xs:
* @jpegxs: A #GstMpegtsJpegXsDescriptor
*
* Create a new #GstMpegtsDescriptor based on the information in @jpegxs
*
* Returns: (transfer full): The #GstMpegtsDescriptor
*
* Since: 1.26
*/
GstMpegtsDescriptor *
gst_mpegts_descriptor_from_jpeg_xs (const GstMpegtsJpegXsDescriptor * jpegxs)
{
gsize desc_size = 30;
GstByteWriter writer;
guint8 *desc_data;
GstMpegtsDescriptor *descriptor;
/* Extension descriptor
* tag/length are take care of by gst_mpegts_descriptor_from_custom
* The size of the "internal" descriptor (in the extension) is 1 (for the extension_descriptor_tag) and 29 for JXS_video_descriptor
*/
gst_byte_writer_init_with_size (&writer, desc_size, FALSE);
/* extension tag */
gst_byte_writer_put_uint8 (&writer, GST_MTS_DESC_EXT_JXS_VIDEO);
/* tag/length again */
gst_byte_writer_put_uint8 (&writer, GST_MTS_DESC_EXT_JXS_VIDEO);
/* Size is 27 (29 minus 2 initial bytes for tag/length */
gst_byte_writer_put_uint8 (&writer, 27);
/* descriptor version: 0 */
gst_byte_writer_put_uint8 (&writer, 0);
/* horizontal/vertical size */
gst_byte_writer_put_uint16_be (&writer, jpegxs->horizontal_size);
gst_byte_writer_put_uint16_be (&writer, jpegxs->vertical_size);
/* brat/frat */
gst_byte_writer_put_uint32_be (&writer, jpegxs->brat);
gst_byte_writer_put_uint32_be (&writer, jpegxs->frat);
/* schar, Ppih, Plev */
gst_byte_writer_put_uint16_be (&writer, jpegxs->schar);
gst_byte_writer_put_uint16_be (&writer, jpegxs->Ppih);
gst_byte_writer_put_uint16_be (&writer, jpegxs->Plev);
gst_byte_writer_put_uint32_be (&writer, jpegxs->max_buffer_size);
/* Buffer model type */
gst_byte_writer_put_uint8 (&writer, jpegxs->buffer_model_type);
/* color_primaries */
gst_byte_writer_put_uint8 (&writer, jpegxs->colour_primaries);
/* transfer_characteristics */
gst_byte_writer_put_uint8 (&writer, jpegxs->transfer_characteristics);
/* matrix_coefficients */
gst_byte_writer_put_uint8 (&writer, jpegxs->matrix_coefficients);
/* video_full_range_flag */
gst_byte_writer_put_uint8 (&writer,
jpegxs->video_full_range_flag ? 1 << 7 : 0);
/* still_mode_flag : off
* mdm_flag : off */
gst_byte_writer_put_uint8 (&writer, jpegxs->still_mode ? 1 : 0);
if (jpegxs->mdm_flag) {
GST_ERROR ("Mastering Display Metadata not supported yet !");
}
desc_size = gst_byte_writer_get_size (&writer);
desc_data = gst_byte_writer_reset_and_get_data (&writer);
descriptor =
gst_mpegts_descriptor_from_custom (GST_MTS_DESC_EXTENSION, desc_data,
desc_size);
g_free (desc_data);
return descriptor;
}

View file

@ -226,6 +226,9 @@ struct _GstMpegtsDescriptor
GST_MPEGTS_API GST_MPEGTS_API
void gst_mpegts_descriptor_free (GstMpegtsDescriptor *desc); void gst_mpegts_descriptor_free (GstMpegtsDescriptor *desc);
GST_MPEGTS_API
GstMpegtsDescriptor * gst_mpegts_descriptor_copy (GstMpegtsDescriptor *desc);
GST_MPEGTS_API GST_MPEGTS_API
GPtrArray *gst_mpegts_parse_descriptors (guint8 * buffer, gsize buf_len); GPtrArray *gst_mpegts_parse_descriptors (guint8 * buffer, gsize buf_len);
@ -663,6 +666,55 @@ GType gst_mpegts_metadata_pointer_descriptor_get_type(void);
GST_MPEGTS_API GST_MPEGTS_API
GstMpegtsDescriptor *gst_mpegts_descriptor_from_metadata_pointer(const GstMpegtsMetadataPointerDescriptor *metadata_pointer_descriptor); GstMpegtsDescriptor *gst_mpegts_descriptor_from_metadata_pointer(const GstMpegtsMetadataPointerDescriptor *metadata_pointer_descriptor);
/* JPEG-XS descriptor */
/**
* GstMpegtsJpegXsDescriptor:
*
* JPEG-XS descriptor
*
* Since: 1.26
*/
typedef struct _GstMpegtsJpegXsDescriptor {
guint8 descriptor_version;
guint16 horizontal_size, vertical_size;
guint32 brat, frat;
guint16 schar, Ppih, Plev;
guint32 max_buffer_size;
guint8 buffer_model_type;
guint8 colour_primaries;
guint8 transfer_characteristics;
guint8 matrix_coefficients;
gboolean video_full_range_flag;
gboolean still_mode;
gboolean mdm_flag;
guint16 X_c0, Y_c0, X_c1, Y_c1, X_c2, Y_c2;
guint16 X_wp, Y_wp;
guint32 L_max, L_min;
guint16 MaxCLL, MaxFALL;
} GstMpegtsJpegXsDescriptor;
/**
* GST_TYPE_MPEGTS_JPEG_XS_DESCRIPTOR:
*
* Since: 1.26
*/
#define GST_TYPE_MPEGTS_JPEG_XS_DESCRIPTOR \
(gst_mpegts_jpeg_xs_descriptor_get_type())
GST_MPEGTS_API
GType gst_mpegts_jpeg_xs_descriptor_get_type(void);
GST_MPEGTS_API
gboolean
gst_mpegts_descriptor_parse_jpeg_xs(const GstMpegtsDescriptor *descriptor,
GstMpegtsJpegXsDescriptor *res);
GST_MPEGTS_API
GstMpegtsDescriptor *
gst_mpegts_descriptor_from_jpeg_xs(const GstMpegtsJpegXsDescriptor *jpegxs);
G_END_DECLS G_END_DECLS
#endif /* GST_MPEGTS_DESCRIPTOR_H */ #endif /* GST_MPEGTS_DESCRIPTOR_H */

View file

@ -399,6 +399,14 @@ typedef enum {
GST_MPEGTS_STREAM_TYPE_VIDEO_H264_STEREO_ADDITIONAL_VIEW = 0x23, GST_MPEGTS_STREAM_TYPE_VIDEO_H264_STEREO_ADDITIONAL_VIEW = 0x23,
GST_MPEGTS_STREAM_TYPE_VIDEO_HEVC = 0x24, GST_MPEGTS_STREAM_TYPE_VIDEO_HEVC = 0x24,
/* 0x24 - 0x7e : Rec. ITU-T H.222.0 | ISO/IEC 13818-1 Reserved */ /* 0x24 - 0x7e : Rec. ITU-T H.222.0 | ISO/IEC 13818-1 Reserved */
/**
* GST_MPEGTS_STREAM_TYPE_VIDEO_JPEG_XS:
*
* JPEG-XS stream type
*
* Since: 1.26
*/
GST_MPEGTS_STREAM_TYPE_VIDEO_JPEG_XS = 0x32,
GST_MPEGTS_STREAM_TYPE_IPMP_STREAM = 0x7f, GST_MPEGTS_STREAM_TYPE_IPMP_STREAM = 0x7f,
/* 0x80 - 0xff : User Private (or defined in other specs) */ /* 0x80 - 0xff : User Private (or defined in other specs) */

View file

@ -243,6 +243,7 @@ struct _TSDemuxStream
"wmvversion = (int) 3, " \ "wmvversion = (int) 3, " \
"format = (string) WVC1;" \ "format = (string) WVC1;" \
"image/x-jpc;" \ "image/x-jpc;" \
"image/x-jxsc;" \
) )
#define AUDIO_CAPS \ #define AUDIO_CAPS \
@ -1848,6 +1849,53 @@ create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream,
"colorspace", G_TYPE_STRING, colorspace, NULL); "colorspace", G_TYPE_STRING, colorspace, NULL);
} }
break; break;
case GST_MPEGTS_STREAM_TYPE_VIDEO_JPEG_XS:
{
GstMpegtsJpegXsDescriptor jpegxs;
desc =
mpegts_get_descriptor_from_stream_with_extension (bstream,
GST_MTS_DESC_EXTENSION, GST_MTS_DESC_EXT_JXS_VIDEO);
if (!desc) {
GST_WARNING_OBJECT (demux,
"image/x-jxsc stream does not have mandatory descriptor");
break;
}
if (!gst_mpegts_descriptor_parse_jpeg_xs (desc, &jpegxs)) {
GST_WARNING_OBJECT (demux, "Invalid JPEG XS descriptor");
break;
}
if (jpegxs.frat >> 30) {
GST_WARNING_OBJECT (demux, "Interlaced JPEG-XS not supported yet");
break;
}
if ((jpegxs.schar >> 15) == 0) {
GST_WARNING_OBJECT (demux, "JPEG-XS sampling properties are required");
break;
}
is_video = TRUE;
caps =
gst_caps_from_string
("image/x-jxsc, alignment=(string)frame, interlace-mode=(string)progressive");
/* interlace-mode, sampling, depth */
gst_caps_set_simple (caps, "width", G_TYPE_INT, jpegxs.horizontal_size,
"height", G_TYPE_INT, jpegxs.vertical_size, "depth", G_TYPE_INT,
(jpegxs.schar >> 4) & 0xf, NULL);
switch (jpegxs.schar & 0xf) {
case 0:
gst_caps_set_simple (caps, "sampling", G_TYPE_STRING, "YCbCr-4:2:2",
NULL);
break;
case 1:
gst_caps_set_simple (caps, "sampling", G_TYPE_STRING, "YCbCr-4:4:4",
NULL);
break;
default:
GST_WARNING_OBJECT (demux, "Unsupported JPEG-XS sampling format");
break;
}
break;
}
case ST_VIDEO_DIRAC: case ST_VIDEO_DIRAC:
if (bstream->registration_id == 0x64726163) { if (bstream->registration_id == 0x64726163) {
GST_LOG_OBJECT (demux, "dirac"); GST_LOG_OBJECT (demux, "dirac");
@ -3119,6 +3167,50 @@ error:
} }
} }
static GstBuffer *
parse_jpegxs_access_unit (TSDemuxStream * stream)
{
GstByteReader br;
guint32 header_tag;
guint32 header_size;
GstBuffer *retbuf;
if (stream->current_size < 30) {
GST_ERROR_OBJECT (stream->pad, "Not enough data for header");
goto error;
}
gst_byte_reader_init (&br, stream->data, stream->current_size);
/* Should start with `jxes` box header */
header_size = gst_byte_reader_get_uint32_be_unchecked (&br);
header_tag = gst_byte_reader_get_uint32_be_unchecked (&br);
if (header_size != 30 || header_tag != 0x6a786573) {
GST_ERROR_OBJECT (stream->pad,
"Invalid 'jxes' header (size:%u, tag:%" GST_FOURCC_FORMAT ")",
header_size, GST_FOURCC_ARGS (header_tag));
return NULL;
}
/* FIXME : Parse/extract timecode */
/* Ignore the rest of that box */
retbuf =
gst_buffer_new_wrapped_full (0, stream->data, stream->current_size,
header_size, stream->current_size - header_size, stream->data, g_free);
stream->data = NULL;
stream->current_size = 0;
return retbuf;
error:
GST_ERROR ("Failed to parse JPEG-XS access unit");
g_free (stream->data);
stream->data = NULL;
stream->current_size = 0;
return NULL;
}
/* interlaced mode is disabled at the moment */ /* interlaced mode is disabled at the moment */
/*#define TSDEMUX_JP2K_SUPPORT_INTERLACE */ /*#define TSDEMUX_JP2K_SUPPORT_INTERLACE */
static GstBuffer * static GstBuffer *
@ -3472,6 +3564,8 @@ gst_ts_demux_push_pending_data (GstTSDemux * demux, TSDemuxStream * stream,
} else if (bs->stream_type == GST_MPEGTS_STREAM_TYPE_METADATA_PES_PACKETS } else if (bs->stream_type == GST_MPEGTS_STREAM_TYPE_METADATA_PES_PACKETS
&& bs->registration_id == DRF_ID_KLVA) { && bs->registration_id == DRF_ID_KLVA) {
buffer_list = parse_pes_metadata_frame (stream); buffer_list = parse_pes_metadata_frame (stream);
} else if (bs->stream_type == GST_MPEGTS_STREAM_TYPE_VIDEO_JPEG_XS) {
buffer = parse_jpegxs_access_unit (stream);
} else { } else {
buffer = gst_buffer_new_wrapped (stream->data, stream->current_size); buffer = gst_buffer_new_wrapped (stream->data, stream->current_size);
} }
@ -3522,6 +3616,8 @@ gst_ts_demux_push_pending_data (GstTSDemux * demux, TSDemuxStream * stream,
} else if (bs->stream_type == GST_MPEGTS_STREAM_TYPE_METADATA_PES_PACKETS } else if (bs->stream_type == GST_MPEGTS_STREAM_TYPE_METADATA_PES_PACKETS
&& bs->registration_id == DRF_ID_KLVA) { && bs->registration_id == DRF_ID_KLVA) {
buffer_list = parse_pes_metadata_frame (stream); buffer_list = parse_pes_metadata_frame (stream);
} else if (bs->stream_type == GST_MPEGTS_STREAM_TYPE_VIDEO_JPEG_XS) {
buffer = parse_jpegxs_access_unit (stream);
} else { } else {
buffer = gst_buffer_new_wrapped (stream->data, stream->current_size); buffer = gst_buffer_new_wrapped (stream->data, stream->current_size);
} }

View file

@ -85,6 +85,7 @@
#include "gstbasetsmuxttxt.h" #include "gstbasetsmuxttxt.h"
#include "gstbasetsmuxopus.h" #include "gstbasetsmuxopus.h"
#include "gstbasetsmuxjpeg2000.h" #include "gstbasetsmuxjpeg2000.h"
#include "gstbasetsmuxjpegxs.h"
GST_DEBUG_CATEGORY (gst_base_ts_mux_debug); GST_DEBUG_CATEGORY (gst_base_ts_mux_debug);
#define GST_CAT_DEFAULT gst_base_ts_mux_debug #define GST_CAT_DEFAULT gst_base_ts_mux_debug
@ -440,6 +441,140 @@ release_buffer_cb (guint8 * data, void *user_data)
stream_data_free ((StreamData *) user_data); stream_data_free ((StreamData *) user_data);
} }
static GstMpegtsJpegXsDescriptor *
gst_base_ts_mux_jpegxs_descriptor (GstBaseTsMux * mux,
GstBaseTsMuxPad * ts_pad, GstCaps * caps)
{
GstStructure *s = gst_caps_get_structure (caps, 0);
gint codestream_length, depth;
GstMpegtsJpegXsDescriptor *jpegxs_descriptor;
GstVideoInfo video_info;
const gchar *sampling;
if (!gst_video_info_from_caps (&video_info, caps))
return NULL;
/* Get (and calculate) all fields from the caps information */
sampling = gst_structure_get_string (s, "sampling");
if (!gst_structure_get_int (s, "codestream-length", &codestream_length)
|| !sampling || !gst_structure_get_int (s, "depth", &depth) || !depth) {
GST_ERROR_OBJECT (ts_pad,
"JPEG-XS caps doesn't contain all required fields");
return NULL;
}
jpegxs_descriptor = g_new0 (GstMpegtsJpegXsDescriptor, 1);
jpegxs_descriptor->horizontal_size = GST_VIDEO_INFO_WIDTH (&video_info);
jpegxs_descriptor->vertical_size = GST_VIDEO_INFO_HEIGHT (&video_info);
{
/* FIXME : Cap according to limit defined by profile/level */
guint32 brat = G_MAXUINT32;
if (GST_VIDEO_INFO_FPS_N (&video_info) > 0
&& GST_VIDEO_INFO_FPS_D (&video_info) > 0) {
// 125000 = * 8 / 1000000 (convert to bits, divide to Mbps)
guint64 v =
gst_util_uint64_scale_ceil (GST_VIDEO_INFO_FPS_N (&video_info),
codestream_length,
GST_VIDEO_INFO_FPS_D (&video_info) * 125000);
if (v < G_MAXUINT32)
brat = v & 0xffffffff;
}
jpegxs_descriptor->brat = brat;
}
{
guint32 frat = 0;
gint fps_n = GST_VIDEO_INFO_FPS_N (&video_info);
gint fps_d = GST_VIDEO_INFO_FPS_D (&video_info);
gint denom_value = 1;
/* Only framerate divisible by 1 or 1.001 are allowed */
if (fps_d == 1001) {
fps_n /= 1000;
denom_value = 2;
} else if (fps_d != 1) {
GST_ERROR_OBJECT (ts_pad, "framerate %d/%d is not allowed for JPEG-XS",
fps_n, fps_d);
goto free_return;
}
if (fps_n > G_MAXUINT16) {
GST_ERROR_OBJECT (ts_pad, "framerate %d/%d exceeds limits for JPEG-XS",
fps_n, fps_d);
goto free_return;
}
if (GST_VIDEO_INFO_IS_INTERLACED (&video_info)) {
if (GST_VIDEO_INFO_FIELD_ORDER (&video_info) ==
GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST) {
frat |= 1 << 30;
} else if (GST_VIDEO_INFO_FIELD_ORDER (&video_info) ==
GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST) {
frat |= 1 << 31;
} else {
GST_ERROR_OBJECT (ts_pad, "Unknown interlace mode");
goto free_return;
}
}
frat |= denom_value << 24;
frat |= fps_n;
jpegxs_descriptor->frat = frat;
}
{
guint16 schar = (depth & 0xf) << 4;
/* FIXME : Support all other variants */
if (!g_strcmp0 (sampling, "YCbCr-4:2:2")) {
schar |= 0;
} else if (!g_strcmp0 (sampling, "YCbCr-4:4:4")) {
schar |= 1;
} else {
GST_ERROR_OBJECT (ts_pad, "Unsupported sampling %s", sampling);
goto free_return;
}
/* schar is valid */
schar |= 1 << 15;
jpegxs_descriptor->schar = schar;
}
/* FIXME : Handle profile/level/sublevel once provided by caps. For now we are unrestricted */
jpegxs_descriptor->Ppih = 0;
jpegxs_descriptor->Plev = 0;
/* FIXME : Calculate max_buffer_size based on profile/level if specified */
jpegxs_descriptor->max_buffer_size = jpegxs_descriptor->brat / 160;
/* Hardcoded buffer_model_type of 2 accordingly to H.222.0 specification */
jpegxs_descriptor->buffer_model_type = 2;
jpegxs_descriptor->colour_primaries =
gst_video_color_primaries_to_iso (video_info.colorimetry.primaries);
jpegxs_descriptor->transfer_characteristics =
gst_video_transfer_function_to_iso (video_info.colorimetry.transfer);
jpegxs_descriptor->matrix_coefficients =
gst_video_color_matrix_to_iso (video_info.colorimetry.matrix);
jpegxs_descriptor->video_full_range_flag =
video_info.colorimetry.range == GST_VIDEO_COLOR_RANGE_0_255;
/* We don't accept still pictures */
jpegxs_descriptor->still_mode = FALSE;
/* FIXME : Add support for Mastering Display Metadata parsing */
return jpegxs_descriptor;
free_return:
{
g_free (jpegxs_descriptor);
return NULL;
}
}
/* Must be called with mux->lock held */ /* Must be called with mux->lock held */
static GstFlowReturn static GstFlowReturn
gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux, gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
@ -459,6 +594,7 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
const gchar *stream_format = NULL; const gchar *stream_format = NULL;
const char *interlace_mode = NULL; const char *interlace_mode = NULL;
gchar *pmt_name; gchar *pmt_name;
GstMpegtsDescriptor *pmt_descriptor = NULL;
GST_DEBUG_OBJECT (ts_pad, GST_DEBUG_OBJECT (ts_pad,
"%s stream with PID 0x%04x for caps %" GST_PTR_FORMAT, "%s stream with PID 0x%04x for caps %" GST_PTR_FORMAT,
@ -676,6 +812,22 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
st = TSMUX_ST_PS_KLV; st = TSMUX_ST_PS_KLV;
} else if (strcmp (mt, "meta/x-id3") == 0) { } else if (strcmp (mt, "meta/x-id3") == 0) {
st = TSMUX_ST_PS_ID3; st = TSMUX_ST_PS_ID3;
} else if (strcmp (mt, "image/x-jxsc") == 0) {
/* FIXME: Get actual values from caps */
GstMpegtsJpegXsDescriptor *jpegxs_descriptor =
gst_base_ts_mux_jpegxs_descriptor (mux, ts_pad, caps);
if (!jpegxs_descriptor)
goto not_negotiated;
pmt_descriptor = gst_mpegts_descriptor_from_jpeg_xs (jpegxs_descriptor);
if (!pmt_descriptor) {
g_free (jpegxs_descriptor);
goto not_negotiated;
}
st = TSMUX_ST_VIDEO_JPEG_XS;
ts_pad->prepare_func = gst_base_ts_mux_prepare_jpegxs;
ts_pad->prepare_data = jpegxs_descriptor;
ts_pad->free_func = gst_base_ts_mux_free_jpegxs;
} else if (strcmp (mt, "image/x-jpc") == 0) { } else if (strcmp (mt, "image/x-jpc") == 0) {
/* /*
* See this document for more details on standard: * See this document for more details on standard:
@ -802,6 +954,10 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
goto error; goto error;
} }
if (pmt_descriptor) {
ts_pad->stream->pmt_descriptor = pmt_descriptor;
}
pmt_name = g_strdup_printf ("PMT_%d", ts_pad->pid); pmt_name = g_strdup_printf ("PMT_%d", ts_pad->pid);
if (mux->prog_map && gst_structure_has_field (mux->prog_map, pmt_name)) { if (mux->prog_map && gst_structure_has_field (mux->prog_map, pmt_name)) {
gst_structure_get_int (mux->prog_map, pmt_name, &ts_pad->stream->pmt_index); gst_structure_get_int (mux->prog_map, pmt_name, &ts_pad->stream->pmt_index);
@ -842,9 +998,13 @@ gst_base_ts_mux_create_or_update_stream (GstBaseTsMux * mux,
/* ERRORS */ /* ERRORS */
not_negotiated: not_negotiated:
if (pmt_descriptor)
gst_mpegts_descriptor_free (pmt_descriptor);
return GST_FLOW_NOT_NEGOTIATED; return GST_FLOW_NOT_NEGOTIATED;
error: error:
if (pmt_descriptor)
gst_mpegts_descriptor_free (pmt_descriptor);
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }

View file

@ -0,0 +1,106 @@
/* JPEG-XS support for MPEG-TS
*
* Copyright (C) <2024> Centricular ltd
* @author Edward Hervey <edward@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include "gstbasetsmuxjpegxs.h"
#include <string.h>
#include <gst/base/gstbytewriter.h>
#include <gst/gst.h>
#define GST_CAT_DEFAULT gst_base_ts_mux_debug
GstBuffer *
gst_base_ts_mux_prepare_jpegxs (GstBuffer * buf, GstBaseTsMuxPad * pad,
GstBaseTsMux * mux)
{
GstMpegtsJpegXsDescriptor *private_data = pad->prepare_data;
GstByteWriter wr;
GstBuffer *out_buf = NULL;
gsize header_size = 30;
guint8 *jxes_header = NULL;
GstClockTime seconds = buf->pts / GST_SECOND;
GstClockTime minutes = seconds / 60;
GstClockTime hours = minutes / 60;
/* FIXME : Instead of constantly allocating/freeing a new header, we should:
* * Generate this header once
* * Update it only for the new tcod
*/
seconds = seconds % 60;
minutes = minutes % 60;
hours = hours % 24;
/* Box is fixed size */
gst_byte_writer_init_with_size (&wr, header_size, FALSE);
gst_byte_writer_put_uint32_be (&wr, header_size);
/* Elementary stream header box 'jxes' == 0x6a786573 */
gst_byte_writer_put_uint32_be (&wr, 0x6a786573);
/* brat, frat are 32 bytes */
gst_byte_writer_put_uint32_be (&wr, private_data->brat);
gst_byte_writer_put_uint32_be (&wr, private_data->frat);
/* schar, Ppih, Plev */
gst_byte_writer_put_uint16_be (&wr, private_data->schar);
gst_byte_writer_put_uint16_be (&wr, private_data->Ppih);
gst_byte_writer_put_uint16_be (&wr, private_data->Plev);
gst_byte_writer_put_uint8 (&wr, private_data->colour_primaries);
gst_byte_writer_put_uint8 (&wr, private_data->transfer_characteristics);
gst_byte_writer_put_uint8 (&wr, private_data->matrix_coefficients);
gst_byte_writer_put_uint8 (&wr, private_data->video_full_range_flag << 7);
/* put HHMMSSFF */
gst_byte_writer_put_uint8 (&wr, (guint8) hours);
gst_byte_writer_put_uint8 (&wr, (guint8) minutes);
gst_byte_writer_put_uint8 (&wr, (guint8) seconds);
gst_byte_writer_put_uint8 (&wr, 0x0);
/* Put jxes header in buffer */
header_size = gst_byte_writer_get_size (&wr);
jxes_header = gst_byte_writer_reset_and_get_data (&wr);
out_buf = gst_buffer_new_wrapped (jxes_header, header_size);
/* Copy complete frame */
gst_buffer_copy_into (out_buf, buf,
GST_BUFFER_COPY_METADATA | GST_BUFFER_COPY_TIMESTAMPS |
GST_BUFFER_COPY_MEMORY, 0, -1);
GST_DEBUG_OBJECT (mux, "Prepared JPEGXS PES of size %d",
(int) gst_buffer_get_size (out_buf));
return out_buf;
}
void
gst_base_ts_mux_free_jpegxs (gpointer prepare_data)
{
/* Free prepare data memory object */
g_free (prepare_data);
}

View file

@ -0,0 +1,49 @@
/* Support for JPEG-XS
*
* Copyright (C) <2024> Centricular ltd
* @author Edward Hervey <edward@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef __BASETSMUX_JPEGXS_H__
#define __BASETSMUX_JPEGXS_H__
#include "glib.h"
#include "gstbasetsmux.h"
typedef struct jpegxs_private_data
{
guint32 brat;
guint32 frat;
guint16 schar;
guint16 Ppih;
guint16 Plev;
guint8 color_primaries;
guint8 transfer_characteristics;
guint8 matrix_coefficients;
gboolean video_full_range_flag;
guint32 tcod;
} jpegxs_private_data;
GstBuffer *gst_base_ts_mux_prepare_jpegxs (GstBuffer * buf, GstBaseTsMuxPad * pad,
GstBaseTsMux * mux);
void gst_base_ts_mux_free_jpegxs (gpointer prepare_data);
#endif /* __BASETSMUX_JPEGXS_H__ */

View file

@ -126,7 +126,8 @@ static GstStaticPadTemplate gst_mpeg_ts_mux_sink_factory =
"channels = (int) [1, 255];" "channels = (int) [1, 255];"
"subpicture/x-dvb; application/x-teletext; meta/x-klv, parsed=true;" "subpicture/x-dvb; application/x-teletext; meta/x-klv, parsed=true;"
"meta/x-id3, parsed=true;" "meta/x-id3, parsed=true;"
"image/x-jpc, alignment = (string) frame, profile = (int)[0, 49151];")); "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 };"));
static GstStaticPadTemplate gst_mpeg_ts_mux_src_factory = static GstStaticPadTemplate gst_mpeg_ts_mux_src_factory =
GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_PAD_TEMPLATE ("src",

View file

@ -5,6 +5,7 @@ tsmux_sources = [
'gstbasetsmuxopus.c', 'gstbasetsmuxopus.c',
'gstbasetsmuxttxt.c', 'gstbasetsmuxttxt.c',
'gstbasetsmuxjpeg2000.c', 'gstbasetsmuxjpeg2000.c',
'gstbasetsmuxjpegxs.c',
'gstmpegtsmux.c', 'gstmpegtsmux.c',
'gstatscmux.c', 'gstatscmux.c',
'tsmux/tsmux.c', 'tsmux/tsmux.c',

View file

@ -153,6 +153,11 @@ tsmux_stream_new (guint16 pid, guint stream_type, guint stream_number)
stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER; stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
stream->gst_stream_type = GST_STREAM_TYPE_VIDEO; stream->gst_stream_type = GST_STREAM_TYPE_VIDEO;
break; break;
case TSMUX_ST_VIDEO_JPEG_XS:
stream->id = 0xBD; /* Private Stream 1 */
stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
stream->gst_stream_type = GST_STREAM_TYPE_VIDEO;
break;
case TSMUX_ST_AUDIO_AAC: case TSMUX_ST_AUDIO_AAC:
case TSMUX_ST_AUDIO_MPEG1: case TSMUX_ST_AUDIO_MPEG1:
case TSMUX_ST_AUDIO_MPEG2: case TSMUX_ST_AUDIO_MPEG2:
@ -296,6 +301,8 @@ tsmux_stream_free (TsMuxStream * stream)
} }
g_list_free (stream->buffers); g_list_free (stream->buffers);
if (stream->pmt_descriptor)
gst_mpegts_descriptor_free (stream->pmt_descriptor);
g_free (stream); g_free (stream);
} }
@ -902,6 +909,12 @@ tsmux_stream_default_get_es_descrs (TsMuxStream * stream,
g_ptr_array_add (pmt_stream->descriptors, descriptor); g_ptr_array_add (pmt_stream->descriptors, descriptor);
} }
break; break;
case TSMUX_ST_VIDEO_JPEG_XS:
{
g_ptr_array_add (pmt_stream->descriptors,
gst_mpegts_descriptor_copy (stream->pmt_descriptor));
}
break;
case TSMUX_ST_PS_AUDIO_AC3: case TSMUX_ST_PS_AUDIO_AC3:
{ {
/* This is only called for DVB, ATSC ignores this case in favour of its /* This is only called for DVB, ATSC ignores this case in favour of its

View file

@ -122,6 +122,7 @@ enum TsMuxStreamType {
TSMUX_ST_VIDEO_H264 = 0x1b, TSMUX_ST_VIDEO_H264 = 0x1b,
TSMUX_ST_VIDEO_HEVC = 0x24, TSMUX_ST_VIDEO_HEVC = 0x24,
TSMUX_ST_VIDEO_JP2K = 0x21, TSMUX_ST_VIDEO_JP2K = 0x21,
TSMUX_ST_VIDEO_JPEG_XS = 0x32,
/* private stream types */ /* private stream types */
TSMUX_ST_PS_AUDIO_AC3 = 0x81, TSMUX_ST_PS_AUDIO_AC3 = 0x81,
@ -227,6 +228,9 @@ struct TsMuxStream {
guint16 profile_and_level; guint16 profile_and_level;
gboolean interlace_mode; gboolean interlace_mode;
guint8 color_spec; guint8 color_spec;
/* PMT descriptor for the stream */
GstMpegtsDescriptor *pmt_descriptor;
}; };
/* stream management */ /* stream management */