From 494f01ee2f00dc7fe39d4d02a3f3495b0f0c7969 Mon Sep 17 00:00:00 2001 From: Gilbok Lee Date: Thu, 21 Feb 2019 17:42:28 +0900 Subject: [PATCH] tsdemux, tsmux, psmux: apply 1.14.4 upstream code Change-Id: I6d51014acca7512a5f33b69e2b095ae56d56c3d0 --- gst/mpegpsmux/mpegpsmux.c | 5 +- gst/mpegtsdemux/mpegtsbase.c | 9 +- gst/mpegtsdemux/mpegtsbase.h | 2 +- gst/mpegtsdemux/mpegtspacketizer.h | 2 - gst/mpegtsdemux/tsdemux.c | 281 +++++++++++++++++++++++++++++++++++- gst/mpegtsdemux/tsdemux.h | 14 ++ gst/mpegtsmux/Makefile.am | 7 +- gst/mpegtsmux/meson.build | 1 + gst/mpegtsmux/mpegtsmux.c | 145 +++++++++++++++++-- gst/mpegtsmux/mpegtsmux_jpeg2000.c | 134 +++++++++++++++++ gst/mpegtsmux/mpegtsmux_jpeg2000.h | 63 ++++++++ gst/mpegtsmux/tsmux/tsmux.c | 46 +++++- gst/mpegtsmux/tsmux/tsmux.h | 3 + gst/mpegtsmux/tsmux/tsmuxstream.c | 75 ++++++++++ gst/mpegtsmux/tsmux/tsmuxstream.h | 15 +- gst/videoparsers/gstjpeg2000parse.c | 258 ++++++++++++++++++++++++++------- gst/videoparsers/gstjpeg2000parse.h | 61 +++++++- 17 files changed, 1034 insertions(+), 87 deletions(-) create mode 100644 gst/mpegtsmux/mpegtsmux_jpeg2000.c create mode 100644 gst/mpegtsmux/mpegtsmux_jpeg2000.h diff --git a/gst/mpegpsmux/mpegpsmux.c b/gst/mpegpsmux/mpegpsmux.c index cdd3681..f627496 100644 --- a/gst/mpegpsmux/mpegpsmux.c +++ b/gst/mpegpsmux/mpegpsmux.c @@ -229,7 +229,6 @@ mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad) } s = gst_caps_get_structure (caps, 0); - g_return_val_if_fail (s != NULL, FALSE); if (gst_structure_has_name (s, "video/x-dirac")) { GST_DEBUG_OBJECT (pad, "Creating Dirac stream"); @@ -651,9 +650,9 @@ mpegpsmux_release_pad (GstElement * element, GstPad * pad) gst_buffer_unref (pad_data->codec_data); pad_data->codec_data = NULL; } + if (pad_data->stream_id == mux->video_stream_id) + mux->video_stream_id = 0; } - if (pad_data->stream_id == mux->video_stream_id) - mux->video_stream_id = 0; GST_OBJECT_UNLOCK (pad); gst_collect_pads_remove_pad (mux->collect, pad); diff --git a/gst/mpegtsdemux/mpegtsbase.c b/gst/mpegtsdemux/mpegtsbase.c index e492cab..3e832da 100644 --- a/gst/mpegtsdemux/mpegtsbase.c +++ b/gst/mpegtsdemux/mpegtsbase.c @@ -243,7 +243,7 @@ mpegts_base_reset (MpegTSBase * base) } gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED); - base->last_seek_seqnum = (guint32) - 1; + base->last_seek_seqnum = GST_SEQNUM_INVALID; base->mode = BASE_MODE_STREAMING; base->seen_pat = FALSE; @@ -321,6 +321,7 @@ mpegts_base_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } + /* returns NULL if no matching descriptor found * * otherwise returns a descriptor that needs to * * be freed */ @@ -1417,6 +1418,8 @@ mpegts_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) if (base->segment.format == GST_FORMAT_TIME) { base->packetizer->calculate_offset = FALSE; base->packetizer->calculate_skew = TRUE; + /* Seek was handled upstream */ + base->last_seek_seqnum = gst_event_get_seqnum (event); } else { base->packetizer->calculate_offset = TRUE; base->packetizer->calculate_skew = FALSE; @@ -1720,6 +1723,10 @@ mpegts_base_loop (MpegTSBase * base) GST_DEBUG ("Pulling data from %" G_GUINT64_FORMAT, base->seek_offset); + if (G_UNLIKELY (base->last_seek_seqnum == GST_SEQNUM_INVALID)) { + /* No configured seek, set a valid seqnum */ + base->last_seek_seqnum = gst_util_seqnum_next (); + } ret = gst_pad_pull_range (base->sinkpad, base->seek_offset, 100 * base->packetsize, &buf); if (G_UNLIKELY (ret != GST_FLOW_OK)) diff --git a/gst/mpegtsdemux/mpegtsbase.h b/gst/mpegtsdemux/mpegtsbase.h index e5117c1..4121c0e 100644 --- a/gst/mpegtsdemux/mpegtsbase.h +++ b/gst/mpegtsdemux/mpegtsbase.h @@ -152,7 +152,7 @@ struct _MpegTSBase { /* Upstream segment */ GstSegment segment; - /* Last received seek event seqnum (default -1) */ + /* Last received seek event seqnum (default GST_SEQNUM_INVALID) */ guint last_seek_seqnum; /* Whether to parse private section or not */ diff --git a/gst/mpegtsdemux/mpegtspacketizer.h b/gst/mpegtsdemux/mpegtspacketizer.h index c59e7bc..c86ef6b 100644 --- a/gst/mpegtsdemux/mpegtspacketizer.h +++ b/gst/mpegtsdemux/mpegtspacketizer.h @@ -360,8 +360,6 @@ G_GNUC_INTERNAL GstMpegtsSection *mpegts_packetizer_push_section (MpegTSPacketiz MpegTSPacketizerPacket *packet, GList **remaining); /* Only valid if calculate_offset is TRUE */ -G_GNUC_INTERNAL guint mpegts_packetizer_get_seen_pcr (MpegTSPacketizer2 *packetizer); - G_GNUC_INTERNAL GstClockTime mpegts_packetizer_offset_to_ts (MpegTSPacketizer2 * packetizer, guint64 offset, guint16 pcr_pid); diff --git a/gst/mpegtsdemux/tsdemux.c b/gst/mpegtsdemux/tsdemux.c index 939a24a..d4107ae 100644 --- a/gst/mpegtsdemux/tsdemux.c +++ b/gst/mpegtsdemux/tsdemux.c @@ -48,6 +48,7 @@ #include "pesparse.h" #include #include +#include #include @@ -112,6 +113,7 @@ typedef struct typedef struct _TSDemuxStream TSDemuxStream; typedef struct _TSDemuxH264ParsingInfos TSDemuxH264ParsingInfos; +typedef struct _TSDemuxJP2KParsingInfos TSDemuxJP2KParsingInfos; /* Returns TRUE if a keyframe was found */ typedef gboolean (*GstTsDemuxKeyFrameScanFunction) (TSDemuxStream * stream, @@ -133,6 +135,11 @@ struct _TSDemuxH264ParsingInfos SimpleBuffer framedata; }; +struct _TSDemuxJP2KParsingInfos +{ + /* J2K parsing data */ + gboolean interlace; +}; struct _TSDemuxStream { MpegTSBaseStream stream; @@ -200,6 +207,7 @@ struct _TSDemuxStream GstTsDemuxKeyFrameScanFunction scan_function; TSDemuxH264ParsingInfos h264infos; + TSDemuxJP2KParsingInfos jp2kInfos; #ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING /* For pad matching to avoid switching pads */ TSDemuxStream *matched_stream; @@ -219,8 +227,9 @@ struct _TSDemuxStream "video/x-cavs;" \ "video/x-wmv," \ "wmvversion = (int) 3, " \ - "format = (string) WVC1" \ - ) + "format = (string) WVC1;" \ + "image/x-jpc;" \ +) #define AUDIO_CAPS \ GST_STATIC_CAPS ( \ @@ -500,6 +509,11 @@ gst_ts_demux_get_duration (GstTSDemux * demux, GstClockTime * dur) gboolean res = FALSE; gint64 val; + if (!demux->program) { + GST_DEBUG_OBJECT (demux, "No active program yet, can't provide duration"); + return FALSE; + } + /* Get total size in bytes */ if (gst_pad_peer_query_duration (base->sinkpad, GST_FORMAT_BYTES, &val)) { /* Convert it to duration */ @@ -1499,6 +1513,83 @@ create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream, "stream-format", G_TYPE_STRING, "byte-stream", "alignment", G_TYPE_STRING, "nal", NULL); break; + case GST_MPEGTS_STREAM_TYPE_VIDEO_JP2K: + is_video = TRUE; + desc = + mpegts_get_descriptor_from_stream (bstream, GST_MTS_DESC_J2K_VIDEO); + if (desc == NULL) { + caps = gst_caps_new_empty_simple ("image/x-jpc"); + break; + } else { + GstByteReader br; + guint16 DEN_frame_rate = 0; + guint16 NUM_frame_rate = 0; + guint8 color_specification = 0; + guint8 remaining_8b = 0; + gboolean interlaced_video = 0; + const gchar *interlace_mode = NULL; + const gchar *colorspace = NULL; + const gchar *colorimetry_mode = NULL; + guint16 profile_and_level G_GNUC_UNUSED; + guint32 horizontal_size G_GNUC_UNUSED; + guint32 vertical_size G_GNUC_UNUSED; + guint32 max_bit_rate G_GNUC_UNUSED; + guint32 max_buffer_size G_GNUC_UNUSED; + const guint desc_min_length = 24; + + if (desc->length < desc_min_length) { + GST_ERROR + ("GST_MPEGTS_STREAM_TYPE_VIDEO_JP2K: descriptor length %d too short", + desc->length); + return NULL; + } + + /* Skip the descriptor tag and length */ + gst_byte_reader_init (&br, desc->data + 2, desc->length); + + profile_and_level = gst_byte_reader_get_uint16_be_unchecked (&br); + horizontal_size = gst_byte_reader_get_uint32_be_unchecked (&br); + vertical_size = gst_byte_reader_get_uint32_be_unchecked (&br); + max_bit_rate = gst_byte_reader_get_uint32_be_unchecked (&br); + max_buffer_size = gst_byte_reader_get_uint32_be_unchecked (&br); + DEN_frame_rate = gst_byte_reader_get_uint16_be_unchecked (&br); + NUM_frame_rate = gst_byte_reader_get_uint16_be_unchecked (&br); + color_specification = gst_byte_reader_get_uint8_unchecked (&br); + remaining_8b = gst_byte_reader_get_uint8_unchecked (&br); + interlaced_video = remaining_8b & 0x40; + /* we don't support demuxing interlaced at the moment */ + if (interlaced_video) { + GST_ERROR + ("GST_MPEGTS_STREAM_TYPE_VIDEO_JP2K: interlaced video not supported"); + return NULL; + } else { + interlace_mode = "progressive"; + stream->jp2kInfos.interlace = FALSE; + } + switch (color_specification) { + case GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_SRGB: + colorspace = "sRGB"; + colorimetry_mode = GST_VIDEO_COLORIMETRY_SRGB; + break; + case GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_REC601: + colorspace = "sYUV"; + colorimetry_mode = GST_VIDEO_COLORIMETRY_BT601; + break; + case GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_REC709: + case GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_CIELUV: + colorspace = "sYUV"; + colorimetry_mode = GST_VIDEO_COLORIMETRY_BT709; + break; + default: + break; + } + caps = gst_caps_new_simple ("image/x-jpc", + "framerate", GST_TYPE_FRACTION, NUM_frame_rate, DEN_frame_rate, + "interlace-mode", G_TYPE_STRING, interlace_mode, + "colorimetry", G_TYPE_STRING, colorimetry_mode, + "colorspace", G_TYPE_STRING, colorspace, NULL); + } + break; case ST_VIDEO_DIRAC: if (bstream->registration_id == 0x64726163) { GST_LOG ("dirac"); @@ -2305,15 +2396,28 @@ check_pending_buffers (GstTSDemux * demux) /* The biggest offset */ guint64 offset = 0; GList *tmp; + gboolean have_only_sparse = TRUE; + + /* 0. Do we only have sparse stream */ + for (tmp = demux->program->stream_list; tmp; tmp = tmp->next) { + TSDemuxStream *tmpstream = (TSDemuxStream *) tmp->data; + + if (!tmpstream->sparse) { + have_only_sparse = FALSE; + break; + } + } /* 1. Go over all streams */ for (tmp = demux->program->stream_list; tmp; tmp = tmp->next) { TSDemuxStream *tmpstream = (TSDemuxStream *) tmp->data; /* 1.1 check if at least one stream got a valid DTS */ - if ((tmpstream->raw_dts != -1 && tmpstream->dts != GST_CLOCK_TIME_NONE) || - (tmpstream->raw_pts != -1 && tmpstream->pts != GST_CLOCK_TIME_NONE)) { - have_observation = TRUE; - break; + if (have_only_sparse || !tmpstream->sparse) { + if ((tmpstream->raw_dts != -1 && tmpstream->dts != GST_CLOCK_TIME_NONE) || + (tmpstream->raw_pts != -1 && tmpstream->pts != GST_CLOCK_TIME_NONE)) { + have_observation = TRUE; + break; + } } } @@ -2644,7 +2748,9 @@ calculate_and_push_newsegment (GstTSDemux * demux, TSDemuxStream * stream, if (!demux->segment_event) { demux->segment_event = gst_event_new_segment (&demux->segment); - GST_EVENT_SEQNUM (demux->segment_event) = base->last_seek_seqnum; + + if (base->last_seek_seqnum != GST_SEQNUM_INVALID) + gst_event_set_seqnum (demux->segment_event, base->last_seek_seqnum); } push_new_segment: @@ -2825,11 +2931,158 @@ error: g_free (stream->data); stream->data = NULL; stream->current_size = 0; - gst_buffer_list_unref (buffer_list); + if (buffer_list) + gst_buffer_list_unref (buffer_list); return NULL; } } +/* interlaced mode is disabled at the moment */ +/*#define TSDEMUX_JP2K_SUPPORT_INTERLACE */ +static GstBuffer * +parse_jp2k_access_unit (TSDemuxStream * stream) +{ + GstByteReader reader; + /* header tag */ + guint32 header_tag; + /* Framerate box */ + guint16 den G_GNUC_UNUSED; + guint16 num G_GNUC_UNUSED; + /* Maximum bitrate box */ + guint32 MaxBr G_GNUC_UNUSED; + guint32 AUF[2] = { 0, 0 }; +#ifdef TSDEMUX_JP2K_SUPPORT_INTERLACE + /* Field Coding Box */ + guint8 Fic G_GNUC_UNUSED = 1; + guint8 Fio G_GNUC_UNUSED = 0; + /* header size equals 38 for non-interlaced, and 48 for interlaced */ + guint header_size = stream->jp2kInfos.interlace ? 48 : 38; +#else + /* header size equals 38 for non-interlaced, and 48 for interlaced */ + guint header_size = 38; +#endif + /* Time Code box */ + guint32 HHMMSSFF G_GNUC_UNUSED; + /* Broadcast color box */ + guint8 CollC G_GNUC_UNUSED; + guint8 b G_GNUC_UNUSED; + + guint data_location; + GstBuffer *retbuf = NULL; + + if (stream->current_size < header_size) { + GST_ERROR_OBJECT (stream->pad, "Not enough data for header"); + goto error; + } + + gst_byte_reader_init (&reader, stream->data, stream->current_size); + + /* Check for the location of the jp2k magic */ + data_location = + gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff, 0xff4fff51, 0, + stream->current_size); + GST_DEBUG_OBJECT (stream->pad, "data location %d", data_location); + if (data_location == -1) { + GST_ERROR_OBJECT (stream->pad, "Stream does not contain jp2k magic header"); + goto error; + } + + /* Elementary stream header box 'elsm' == 0x656c736d */ + header_tag = gst_byte_reader_get_uint32_be_unchecked (&reader); + if (header_tag != 0x656c736d) { + GST_ERROR_OBJECT (stream->pad, "Expected ELSM box but found box %x instead", + header_tag); + goto error; + } + /* Frame rate box 'frat' == 0x66726174 */ + header_tag = gst_byte_reader_get_uint32_be_unchecked (&reader); + if (header_tag != 0x66726174) { + GST_ERROR_OBJECT (stream->pad, + "Expected frame rate box, but found box %x instead", header_tag); + goto error; + + } + den = gst_byte_reader_get_uint16_be_unchecked (&reader); + num = gst_byte_reader_get_uint16_be_unchecked (&reader); + /* Maximum bit rate box 'brat' == 0x62726174 */ + header_tag = gst_byte_reader_get_uint32_be_unchecked (&reader); + if (header_tag != 0x62726174) { + GST_ERROR_OBJECT (stream->pad, "Expected brat box but read box %x instead", + header_tag); + goto error; + + } + MaxBr = gst_byte_reader_get_uint32_be_unchecked (&reader); + AUF[0] = gst_byte_reader_get_uint32_be_unchecked (&reader); + if (stream->jp2kInfos.interlace) { +#ifdef TSDEMUX_JP2K_SUPPORT_INTERLACE + AUF[1] = gst_byte_reader_get_uint32_be_unchecked (&reader); + /* Field Coding Box 'fiel' == 0x6669656c */ + header_tag = gst_byte_reader_get_uint32_be_unchecked (&reader); + if (header_tag != 0x6669656c) { + GST_ERROR_OBJECT (stream->pad, + "Expected Field Coding box but found box %x instead", header_tag); + goto error; + } + Fic = gst_byte_reader_get_uint8_unchecked (&reader); + Fio = gst_byte_reader_get_uint8_unchecked (&reader); +#else + GST_ERROR_OBJECT (stream->pad, "interlaced mode not supported"); + goto error; +#endif + } + + /* Time Code Box 'tcod' == 0x74636f64 */ + /* Some progressive streams might have a AUF[1] of value 0 present */ + header_tag = gst_byte_reader_get_uint32_be_unchecked (&reader); + if (header_tag == 0 && !stream->jp2kInfos.interlace) { + AUF[1] = header_tag; + header_tag = gst_byte_reader_get_uint32_be_unchecked (&reader); + /* Bump up header size and recheck */ + header_size += 4; + if (stream->current_size < header_size) { + GST_ERROR_OBJECT (stream->pad, "Not enough data for header"); + goto error; + } + } + if (header_tag != 0x74636f64) { + GST_ERROR_OBJECT (stream->pad, + "Expected Time code box but found %d box instead", header_tag); + goto error; + } + HHMMSSFF = gst_byte_reader_get_uint32_be_unchecked (&reader); + /* Broadcast Color Box 'bcol' == 0x6263686c */ + header_tag = gst_byte_reader_get_uint32_be_unchecked (&reader); + if (header_tag != 0x62636f6c) { + GST_ERROR_OBJECT (stream->pad, + "Expected Broadcast color box but found %x box instead", header_tag); + goto error; + } + CollC = gst_byte_reader_get_uint8_unchecked (&reader); + b = gst_byte_reader_get_uint8_unchecked (&reader); + + /* Check if we have enough data to create a valid buffer */ + if ((stream->current_size - data_location) < (AUF[0] + AUF[1])) { + GST_ERROR ("Required size (%d) greater than remaining size in buffer (%d)", + AUF[0] + AUF[1], (stream->current_size - data_location)); + goto error; + } + + retbuf = gst_buffer_new_wrapped_full (0, stream->data, stream->current_size, + data_location, stream->current_size - data_location, + stream->data, g_free); + stream->data = NULL; + stream->current_size = 0; + return retbuf; + +error: + GST_ERROR ("Failed to parse JP2K access unit"); + g_free (stream->data); + stream->data = NULL; + stream->current_size = 0; + return NULL; +} + static GstFlowReturn gst_ts_demux_push_pending_data (GstTSDemux * demux, TSDemuxStream * stream, MpegTSBaseProgram * target_program) @@ -2887,6 +3140,12 @@ gst_ts_demux_push_pending_data (GstTSDemux * demux, TSDemuxStream * stream, gst_buffer_list_unref (buffer_list); buffer_list = NULL; } + } else if (bs->stream_type == GST_MPEGTS_STREAM_TYPE_VIDEO_JP2K) { + buffer = parse_jp2k_access_unit (stream); + if (!buffer) { + res = GST_FLOW_ERROR; + goto beach; + } } else { buffer = gst_buffer_new_wrapped (stream->data, stream->current_size); } @@ -2921,6 +3180,12 @@ gst_ts_demux_push_pending_data (GstTSDemux * demux, TSDemuxStream * stream, gst_buffer_list_unref (buffer_list); buffer_list = NULL; } + } else if (bs->stream_type == GST_MPEGTS_STREAM_TYPE_VIDEO_JP2K) { + buffer = parse_jp2k_access_unit (stream); + if (!buffer) { + res = GST_FLOW_ERROR; + goto beach; + } } else { buffer = gst_buffer_new_wrapped (stream->data, stream->current_size); } diff --git a/gst/mpegtsdemux/tsdemux.h b/gst/mpegtsdemux/tsdemux.h index 0a6e19a..a7c08da 100644 --- a/gst/mpegtsdemux/tsdemux.h +++ b/gst/mpegtsdemux/tsdemux.h @@ -33,6 +33,20 @@ #include "mpegtsbase.h" #include "mpegtspacketizer.h" +/* color specifications for JPEG 2000 stream over MPEG TS */ +typedef enum +{ + GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_UNKNOWN, + GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_SRGB, + GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_REC601, + GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_REC709, + GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_CIELUV, + GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_CIEXYZ, + GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_REC2020, + GST_MPEGTSDEMUX_JPEG2000_COLORSPEC_SMPTE2084 +} GstMpegTsDemuxJpeg2000ColorSpec; + + G_BEGIN_DECLS #define GST_TYPE_TS_DEMUX \ (gst_ts_demux_get_type()) diff --git a/gst/mpegtsmux/Makefile.am b/gst/mpegtsmux/Makefile.am index 0fa78d7..05c468c 100644 --- a/gst/mpegtsmux/Makefile.am +++ b/gst/mpegtsmux/Makefile.am @@ -6,9 +6,11 @@ libgstmpegtsmux_la_SOURCES = \ mpegtsmux.c \ mpegtsmux_aac.c \ mpegtsmux_ttxt.c \ - mpegtsmux_opus.c + mpegtsmux_opus.c \ + mpegtsmux_jpeg2000.c libgstmpegtsmux_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ + -I$(top_srcdir) \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) libgstmpegtsmux_la_LIBADD = $(top_builddir)/gst/mpegtsmux/tsmux/libtsmux.la \ $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_API_VERSION@ \ @@ -22,4 +24,5 @@ noinst_HEADERS = \ mpegtsmux.h \ mpegtsmux_aac.h \ mpegtsmux_ttxt.h \ - mpegtsmux_opus.h + mpegtsmux_opus.h \ + mpegtsmux_jpeg2000.h diff --git a/gst/mpegtsmux/meson.build b/gst/mpegtsmux/meson.build index 45648f1..568b695 100644 --- a/gst/mpegtsmux/meson.build +++ b/gst/mpegtsmux/meson.build @@ -3,6 +3,7 @@ tsmux_sources = [ 'mpegtsmux_aac.c', 'mpegtsmux_opus.c', 'mpegtsmux_ttxt.c', + 'mpegtsmux_jpeg2000.c', 'tsmux/tsmux.c', 'tsmux/tsmuxstream.c', ] diff --git a/gst/mpegtsmux/mpegtsmux.c b/gst/mpegtsmux/mpegtsmux.c index 67c2b72..99562fd 100644 --- a/gst/mpegtsmux/mpegtsmux.c +++ b/gst/mpegtsmux/mpegtsmux.c @@ -98,10 +98,15 @@ #include "mpegtsmux_aac.h" #include "mpegtsmux_ttxt.h" #include "mpegtsmux_opus.h" +#include "mpegtsmux_jpeg2000.h" +#include +#include GST_DEBUG_CATEGORY (mpegtsmux_debug); #define GST_CAT_DEFAULT mpegtsmux_debug +#define COLLECT_DATA_PAD(collect_data) (((GstCollectData *)(collect_data))->pad) + enum { PROP_0, @@ -125,6 +130,7 @@ static GstStaticPadTemplate mpegtsmux_sink_factory = "mpegversion = (int) { 1, 2, 4 }, " "systemstream = (boolean) false; " "video/x-dirac;" + "image/x-jpc;" "video/x-h264,stream-format=(string)byte-stream," "alignment=(string){au, nal}; " "video/x-h265,stream-format=(string)byte-stream," @@ -149,7 +155,8 @@ static GstStaticPadTemplate mpegtsmux_sink_factory = "audio/x-opus, " "channels = (int) [1, 8], " "channel-mapping-family = (int) {0, 1};" - "subpicture/x-dvb; application/x-teletext; meta/x-klv, parsed=true")); + "subpicture/x-dvb; application/x-teletext; meta/x-klv, parsed=true;" + "image/x-jpc, profile = (int)[0, 49151];")); static GstStaticPadTemplate mpegtsmux_src_factory = GST_STATIC_PAD_TEMPLATE ("src", @@ -375,12 +382,15 @@ mpegtsmux_reset (MpegTsMux * mux, gboolean alloc) mux->first = TRUE; mux->last_flow_ret = GST_FLOW_OK; mux->previous_pcr = -1; + mux->previous_offset = 0; mux->pcr_rate_num = mux->pcr_rate_den = 1; mux->last_ts = 0; mux->is_delta = TRUE; + mux->is_header = FALSE; mux->streamheader_sent = FALSE; mux->pending_key_unit_ts = GST_CLOCK_TIME_NONE; + gst_event_replace (&mux->force_key_unit_event, NULL); #if 0 mux->spn_count = 0; @@ -588,6 +598,11 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data) const GValue *value = NULL; GstBuffer *codec_data = NULL; guint8 opus_channel_config_code = 0; + guint16 profile = 0; + guint8 main_level = 0; + guint32 max_rate = 0; + guint8 color_spec = 0; + j2k_private_data *private_data = NULL; pad = ts_data->collect.pad; caps = gst_pad_get_current_caps (pad); @@ -739,6 +754,94 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data) ts_data->prepare_func = mpegtsmux_prepare_opus; } else if (strcmp (mt, "meta/x-klv") == 0) { st = TSMUX_ST_PS_KLV; + } else if (strcmp (mt, "image/x-jpc") == 0) { + /* + * See this document for more details on standard: + * + * https://www.itu.int/rec/T-REC-H.222.0-201206-S/en + * Annex S describes J2K details + * Page 104 of this document describes J2k video descriptor + */ + + const GValue *vProfile = gst_structure_get_value (s, "profile"); + const GValue *vMainlevel = gst_structure_get_value (s, "main-level"); + const GValue *vFramerate = gst_structure_get_value (s, "framerate"); + const GValue *vColorimetry = gst_structure_get_value (s, "colorimetry"); + private_data = g_new0 (j2k_private_data, 1); + profile = g_value_get_uint (vProfile); + if (profile != GST_JPEG2000_PARSE_PROFILE_BC_SINGLE) { + /* for now, we will relax the condition that the profile must equal GST_JPEG2000_PARSE_PROFILE_BC_SINGLE */ + /*GST_ERROR_OBJECT (pad, "Invalid JPEG 2000 profile %d", profile); + goto not_negotiated; */ + } + /* for now, we will relax the condition that the main level must be present */ + if (vMainlevel) { + main_level = g_value_get_uint (vMainlevel); + if (main_level > 11) { + GST_ERROR_OBJECT (pad, "Invalid main level %d", main_level); + goto not_negotiated; + } + if (main_level >= 6) { + max_rate = 2 ^ (main_level - 6) * 1600 * 1000000; + } else { + switch (main_level) { + case 0: + case 1: + case 2: + case 3: + max_rate = 200 * 1000000; + break; + case 4: + max_rate = 400 * 1000000; + break; + case 5: + max_rate = 800 * 1000000; + break; + default: + break; + } + } + } else { + /*GST_ERROR_OBJECT (pad, "Missing main level"); + goto not_negotiated; */ + } + /* We always mux video in J2K-over-MPEG-TS non-interlaced mode */ + private_data->interlace = FALSE; + private_data->den = 0; + private_data->num = 0; + private_data->max_bitrate = max_rate; + private_data->color_spec = 1; + /* these two fields are not used, since we always mux as non-interlaced */ + private_data->Fic = 1; + private_data->Fio = 0; + + /* Get Framerate */ + if (vFramerate != NULL) { + /* Data for ELSM header */ + private_data->num = gst_value_get_fraction_numerator (vFramerate); + private_data->den = gst_value_get_fraction_denominator (vFramerate); + } + /* Get Colorimetry */ + if (vColorimetry) { + const char *colorimetry = g_value_get_string (vColorimetry); + color_spec = GST_MPEGTS_JPEG2000_COLORSPEC_SRGB; /* RGB as default */ + if (g_str_equal (colorimetry, GST_VIDEO_COLORIMETRY_BT601)) { + color_spec = GST_MPEGTS_JPEG2000_COLORSPEC_REC601; + } else { + if (g_str_equal (colorimetry, GST_VIDEO_COLORIMETRY_BT709) + || g_str_equal (colorimetry, GST_VIDEO_COLORIMETRY_SMPTE240M)) { + color_spec = GST_MPEGTS_JPEG2000_COLORSPEC_REC709; + } + } + private_data->color_spec = color_spec; + } else { + GST_ERROR_OBJECT (pad, "Colorimetry not present in caps"); + goto not_negotiated; + } + st = TSMUX_ST_VIDEO_JP2K; + ts_data->prepare_func = mpegtsmux_prepare_jpeg2000; + ts_data->prepare_data = private_data; + ts_data->free_func = mpegtsmux_free_jpeg2000; } if (st != TSMUX_ST_RESERVED) { @@ -749,10 +852,29 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data) } if (ts_data->stream != NULL) { + const char *interlace_mode = gst_structure_get_string (s, "interlace-mode"); gst_structure_get_int (s, "rate", &ts_data->stream->audio_sampling); gst_structure_get_int (s, "channels", &ts_data->stream->audio_channels); gst_structure_get_int (s, "bitrate", &ts_data->stream->audio_bitrate); + /* frame rate */ + gst_structure_get_fraction (s, "framerate", &ts_data->stream->num, + &ts_data->stream->den); + + /* Interlace mode */ + ts_data->stream->interlace_mode = FALSE; + if (interlace_mode) { + ts_data->stream->interlace_mode = + g_str_equal (interlace_mode, "interleaved"); + } + /* Width and Height */ + gst_structure_get_int (s, "width", &ts_data->stream->horizontal_size); + gst_structure_get_int (s, "height", &ts_data->stream->vertical_size); + + ts_data->stream->color_spec = color_spec; + ts_data->stream->max_bitrate = max_rate; + ts_data->stream->profile_and_level = profile | main_level; + ts_data->stream->opus_channel_config_code = opus_channel_config_code; tsmux_stream_set_buffer_release_func (ts_data->stream, release_buffer_cb); @@ -784,13 +906,12 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data) } GST_OBJECT_UNLOCK (mux); #endif - gst_caps_unref (caps); return ret; - /* ERRORS */ not_negotiated: { + g_free (private_data); GST_DEBUG_OBJECT (pad, "Sink pad caps were not set before pushing"); if (caps) gst_caps_unref (caps); @@ -844,6 +965,13 @@ mpegtsmux_create_streams (MpegTsMux * mux) tsmux_set_pmt_interval (ts_data->prog, mux->pmt_interval); g_hash_table_insert (mux->programs, GINT_TO_POINTER (ts_data->prog_id), ts_data->prog); + + /* Take the first stream of the program for the PCR */ + GST_DEBUG_OBJECT (COLLECT_DATA_PAD (ts_data), + "Use stream (pid=%d) from pad as PCR for program (prog_id = %d)", + ts_data->pid, ts_data->prog_id); + + tsmux_program_set_pcr_stream (ts_data->prog, ts_data->stream); } if (ts_data->stream == NULL) { @@ -870,9 +998,6 @@ no_stream: } } - -#define COLLECT_DATA_PAD(collect_data) (((GstCollectData *)(collect_data))->pad) - static gboolean mpegtsmux_sink_event (GstCollectPads * pads, GstCollectData * data, GstEvent * event, gpointer user_data) @@ -1253,16 +1378,16 @@ mpegtsmux_collected_buffer (GstCollectPads * pads, GstCollectData * data, GST_TIME_ARGS (running_time), count); gst_pad_push_event (mux->srcpad, event); - /* output PAT */ - mux->tsmux->last_pat_ts = -1; + /* output PAT, SI tables */ + tsmux_resend_pat (mux->tsmux); + tsmux_resend_si (mux->tsmux); /* output PMT for each program */ for (cur = mux->tsmux->programs; cur; cur = cur->next) { TsMuxProgram *program = (TsMuxProgram *) cur->data; - program->last_pmt_ts = -1; + tsmux_resend_pmt (program); } - tsmux_program_set_pcr_stream (prog, NULL); } } diff --git a/gst/mpegtsmux/mpegtsmux_jpeg2000.c b/gst/mpegtsmux/mpegtsmux_jpeg2000.c new file mode 100644 index 0000000..1298d38 --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux_jpeg2000.c @@ -0,0 +1,134 @@ +/* GStreamer JPEG 2000 Parser + * + * Copyright (C) <2016> Milos Seleceni + * @author Milos Seleceni + * + * Copyright (C) <2016-2017> Grok Image Compression Inc. + * @author Aaron Boxer + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "mpegtsmux_jpeg2000.h" +#include +#include +#include +#include + +#define GST_CAT_DEFAULT mpegtsmux_debug + +GstBuffer * +mpegtsmux_prepare_jpeg2000 (GstBuffer * buf, MpegTsPadData * data, + MpegTsMux * mux) +{ + j2k_private_data *private_data = data->prepare_data; + GstByteWriter wr; + GstBuffer *out_buf = NULL; + guint8 *elsm_header = NULL; + const guint header_size = private_data->interlace ? 48 : 38; + GstClockTime seconds = buf->pts / GST_SECOND; + GstClockTime minutes = seconds / 60; + GstClockTime hours = minutes / 60; + + /* interlaced not supported */ + if (private_data->interlace) { + GST_ERROR_OBJECT (mux, "Interlaced not supported"); + return NULL; + } + + seconds = seconds % 60; + minutes = minutes % 60; + hours = hours % 24; + + /* ??? Hack for missing frame number index in buffer offset */ + /* guint8 frame_number = private_data->frame_number % 60; */ + gst_byte_writer_init_with_size (&wr, header_size, FALSE); + + /* Elementary stream header box 'elsm' == 0x656c736d */ + gst_byte_writer_put_uint32_be (&wr, 0x656c736d); + /* Framerate box 'frat' == 0x66726174 */ + gst_byte_writer_put_uint32_be (&wr, 0x66726174); + /* put framerate denominator */ + gst_byte_writer_put_uint16_be (&wr, private_data->den); + /* put framerate numerator */ + gst_byte_writer_put_uint16_be (&wr, private_data->num); + /* Maximum bitrate box 'brat' == 0x62726174 */ + gst_byte_writer_put_uint32_be (&wr, 0x62726174); + /* put Maximum bitrate */ + gst_byte_writer_put_uint32_be (&wr, private_data->max_bitrate); + /* put size of first codestream */ + /* private_data->AUF[0] */ + gst_byte_writer_put_uint32_be (&wr, gst_buffer_get_size (buf)); + + /* ToDo: the if block below is never called, because we do not support muxing J2K-over-mpeg-TS interlaced data + * If we ever do, then the code below will need to tested and perhaps modified + */ + if (private_data->interlace) { + /* put size of second codestream */ + gst_byte_writer_put_uint32_be (&wr, gst_buffer_get_size (buf)); + /* Time Code Box 'fiel' == 0x6669656c */ + gst_byte_writer_put_uint32_be (&wr, 0x6669656c); + /* put Fic */ + gst_byte_writer_put_uint8 (&wr, private_data->Fic); + /* put Fio */ + gst_byte_writer_put_uint8 (&wr, private_data->Fio); + } + + /* Time Code Box 'tcod' == 0x74636f64 */ + gst_byte_writer_put_uint32_be (&wr, 0x74636f64); + + /* 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); + /* ??? Hack for missing frame number index in buffer offset */ + /* private_data->frame_number++; */ + + /* Broadcast Color Box 'bcol' == 0x62636f6c */ + gst_byte_writer_put_uint32_be (&wr, 0x62636f6c); + /* put color spec */ + gst_byte_writer_put_uint8 (&wr, private_data->color_spec); + /* put reserved 8-bit */ + gst_byte_writer_put_uint8 (&wr, 0xff); + /* Allocate ELSM header size only; gst_buffer_copy_into will add gst_buffer_get_size (buf) bytes to out_buf */ + out_buf = gst_buffer_new_and_alloc (header_size); + + /* Copy ELSM header */ + elsm_header = gst_byte_writer_reset_and_get_data (&wr); + gst_buffer_fill (out_buf, 0, elsm_header, header_size); + g_free (elsm_header); + /* 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 J2K PES of size %d", + (int) gst_buffer_get_size (out_buf)); + + return out_buf; +} + +void +mpegtsmux_free_jpeg2000 (gpointer prepare_data) +{ + /* Free prepare data memory object */ + g_free (prepare_data); +} diff --git a/gst/mpegtsmux/mpegtsmux_jpeg2000.h b/gst/mpegtsmux/mpegtsmux_jpeg2000.h new file mode 100644 index 0000000..c0e4ab9 --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux_jpeg2000.h @@ -0,0 +1,63 @@ +/* GStreamer JPEG 2000 Parser + * + * Copyright (C) <2016> Milos Seleceni + * @author Milos Seleceni + * + * Copyright (C) <2016-2017> Grok Image Compression Inc. + * @author Aaron Boxer + * + * 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. + */ + +#ifndef __MPEGTSMUX_JPEG2000_H__ +#define __MPEGTSMUX_JPEG2000_H__ + +#include "mpegtsmux.h" + +/* color specifications for JPEG 2000 stream over MPEG TS */ +typedef enum +{ + GST_MPEGTS_JPEG2000_COLORSPEC_UNKNOWN, + GST_MPEGTS_JPEG2000_COLORSPEC_SRGB, + GST_MPEGTS_JPEG2000_COLORSPEC_REC601, + GST_MPEGTS_JPEG2000_COLORSPEC_REC709, + GST_MPEGTS_JPEG2000_COLORSPEC_CIELUV, + GST_MPEGTS_JPEG2000_COLORSPEC_CIEXYZ, + GST_MPEGTS_JPEG2000_COLORSPEC_REC2020, + GST_MPEGTS_JPEG2000_COLORSPEC_SMPTE2084 +} GstMpegTsJpeg2000ColorSpec; + + +typedef struct j2k_private_data +{ + gboolean interlace; + guint16 den; + guint16 num; + /* Maximum bitrate box */ + guint32 max_bitrate; + /* Field Coding Box */ + guint8 Fic; + guint8 Fio; + /* Broadcast color box */ + guint8 color_spec; +} j2k_private_data; + +GstBuffer *mpegtsmux_prepare_jpeg2000 (GstBuffer * buf, MpegTsPadData * data, + MpegTsMux * mux); + +void mpegtsmux_free_jpeg2000 (gpointer prepare_data); + +#endif /* __MPEGTSMUX_JPEG2000_H__ */ diff --git a/gst/mpegtsmux/tsmux/tsmux.c b/gst/mpegtsmux/tsmux/tsmux.c index ee28704..6fa9e0d 100644 --- a/gst/mpegtsmux/tsmux/tsmux.c +++ b/gst/mpegtsmux/tsmux/tsmux.c @@ -229,6 +229,20 @@ tsmux_get_pat_interval (TsMux * mux) } /** + * tsmux_resend_pat: + * @mux: a #TsMux + * + * Resends the PAT before the next stream packet. + */ +void +tsmux_resend_pat (TsMux * mux) +{ + g_return_if_fail (mux != NULL); + + mux->last_pat_ts = G_MININT64; +} + +/** * tsmux_set_si_interval: * @mux: a #TsMux * @freq: a new SI table interval @@ -261,13 +275,28 @@ tsmux_get_si_interval (TsMux * mux) } /** + * tsmux_resend_si: + * @mux: a #TsMux + * + * Resends the SI tables before the next stream packet. + * + */ +void +tsmux_resend_si (TsMux * mux) +{ + g_return_if_fail (mux != NULL); + + mux->last_si_ts = G_MININT64; +} + +/** * tsmux_add_mpegts_si_section: * @mux: a #TsMux * @section: (transfer full): a #GstMpegtsSection to add * * Add a Service Information #GstMpegtsSection to the stream * - * Returns: #TRUE on success, #FALSE otherwise + * Returns: %TRUE on success, %FALSE otherwise */ gboolean tsmux_add_mpegts_si_section (TsMux * mux, GstMpegtsSection * section) @@ -428,6 +457,20 @@ tsmux_get_pmt_interval (TsMuxProgram * program) } /** + * tsmux_resend_pmt: + * @program: a #TsMuxProgram + * + * Resends the PMT before the next stream packet. + */ +void +tsmux_resend_pmt (TsMuxProgram * program) +{ + g_return_if_fail (program != NULL); + + program->last_pmt_ts = G_MININT64; +} + +/** * tsmux_program_add_stream: * @program: a #TsMuxProgram * @stream: a #TsMuxStream @@ -1104,6 +1147,7 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream) gst_buffer_unmap (buf, &map); + GST_DEBUG ("Writing PES of size %d", (int) gst_buffer_get_size (buf)); res = tsmux_packet_out (mux, buf, cur_pcr); /* Reset all dynamic flags */ diff --git a/gst/mpegtsmux/tsmux/tsmux.h b/gst/mpegtsmux/tsmux/tsmux.h index dd1185a..11a1480 100644 --- a/gst/mpegtsmux/tsmux/tsmux.h +++ b/gst/mpegtsmux/tsmux/tsmux.h @@ -189,6 +189,7 @@ void tsmux_set_write_func (TsMux *mux, TsMuxWriteFunc func, void *user_data) void tsmux_set_alloc_func (TsMux *mux, TsMuxAllocFunc func, void *user_data); void tsmux_set_pat_interval (TsMux *mux, guint interval); guint tsmux_get_pat_interval (TsMux *mux); +void tsmux_resend_pat (TsMux *mux); guint16 tsmux_get_new_pid (TsMux *mux); /* pid/program management */ @@ -196,10 +197,12 @@ TsMuxProgram * tsmux_program_new (TsMux *mux, gint prog_id); void tsmux_program_free (TsMuxProgram *program); void tsmux_set_pmt_interval (TsMuxProgram *program, guint interval); guint tsmux_get_pmt_interval (TsMuxProgram *program); +void tsmux_resend_pmt (TsMuxProgram *program); /* SI table management */ void tsmux_set_si_interval (TsMux *mux, guint interval); guint tsmux_get_si_interval (TsMux *mux); +void tsmux_resend_si (TsMux *mux); gboolean tsmux_add_mpegts_si_section (TsMux * mux, GstMpegtsSection * section); /* stream management */ diff --git a/gst/mpegtsmux/tsmux/tsmuxstream.c b/gst/mpegtsmux/tsmux/tsmuxstream.c index f719d8f..4cbd217 100644 --- a/gst/mpegtsmux/tsmux/tsmuxstream.c +++ b/gst/mpegtsmux/tsmux/tsmuxstream.c @@ -84,6 +84,7 @@ #include #include +#include #include "tsmuxcommon.h" #include "tsmuxstream.h" @@ -144,6 +145,11 @@ tsmux_stream_new (guint16 pid, TsMuxStreamType stream_type) stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER; stream->is_video_stream = TRUE; break; + case TSMUX_ST_VIDEO_JP2K: + stream->id = 0xBD; + stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER; + stream->is_video_stream = TRUE; + break; case TSMUX_ST_AUDIO_AAC: case TSMUX_ST_AUDIO_MPEG1: case TSMUX_ST_AUDIO_MPEG2: @@ -768,6 +774,75 @@ tsmux_stream_get_es_descrs (TsMuxStream * stream, descriptor = gst_mpegts_descriptor_from_registration ("drac", NULL, 0); g_ptr_array_add (pmt_stream->descriptors, descriptor); break; + case TSMUX_ST_VIDEO_JP2K: + { + /* J2K video descriptor + * descriptor_tag 8 uimsbf + * descriptor_length 8 uimsbf + * profile_and_level 16 uimsbf + * horizontal_size 32 uimsbf + * vertical_size 32 uimsbf + * max_bit_rate 32 uimsbf + * max_buffer_size 32 uimsbf + * DEN_frame_rate 16 uimsbf + * NUM_frame_rate 16 uimsbf + * color_specification 8 bslbf + * still_mode 1 bslbf + * interlace_video 1 bslbf + * reserved 6 bslbf + * private_data_byte 8 bslbf + */ + gint8 still_interlace_reserved = 0x00; + int wr_size = 0; + guint8 *add_info = NULL; + guint8 level = stream->profile_and_level & 0xF; + guint32 max_buffer_size = 0; + GstByteWriter writer; + gst_byte_writer_init_with_size (&writer, 32, FALSE); + + switch (level) { + case 1: + case 2: + case 3: + max_buffer_size = 1250000; + break; + case 4: + max_buffer_size = 2500000; + break; + case 5: + max_buffer_size = 5000000; + break; + case 6: + max_buffer_size = 10000000; + break; + default: + break; + } + + gst_byte_writer_put_uint16_be (&writer, stream->profile_and_level); + gst_byte_writer_put_uint32_be (&writer, stream->horizontal_size); + gst_byte_writer_put_uint32_be (&writer, stream->vertical_size); + gst_byte_writer_put_uint32_be (&writer, max_buffer_size); + gst_byte_writer_put_uint32_be (&writer, stream->max_bitrate); + gst_byte_writer_put_uint16_be (&writer, stream->den); + gst_byte_writer_put_uint16_be (&writer, stream->num); + gst_byte_writer_put_uint8 (&writer, stream->color_spec); + + if (stream->interlace_mode) + still_interlace_reserved |= 0x40; + + gst_byte_writer_put_uint8 (&writer, still_interlace_reserved); + gst_byte_writer_put_uint8 (&writer, 0x00); /* private data byte */ + + wr_size = gst_byte_writer_get_size (&writer); + add_info = gst_byte_writer_reset_and_get_data (&writer); + + descriptor = + gst_mpegts_descriptor_from_custom (GST_MTS_DESC_J2K_VIDEO, add_info, + wr_size); + g_ptr_array_add (pmt_stream->descriptors, descriptor); + } + break; case TSMUX_ST_PS_AUDIO_AC3: { guint8 add_info[6]; diff --git a/gst/mpegtsmux/tsmux/tsmuxstream.h b/gst/mpegtsmux/tsmux/tsmuxstream.h index 2766718..7edfb61 100644 --- a/gst/mpegtsmux/tsmux/tsmuxstream.h +++ b/gst/mpegtsmux/tsmux/tsmuxstream.h @@ -134,6 +134,7 @@ enum TsMuxStreamType { TSMUX_ST_VIDEO_MPEG4 = 0x10, TSMUX_ST_VIDEO_H264 = 0x1b, TSMUX_ST_VIDEO_HEVC = 0x24, + TSMUX_ST_VIDEO_JP2K = 0x21, /* private stream types */ TSMUX_ST_PS_AUDIO_AC3 = 0x81, @@ -183,9 +184,9 @@ struct TsMuxStream { /* optional fixed PES size for stream type */ guint16 pes_payload_size; /* current PES payload size being written */ - guint16 cur_pes_payload_size; + guint32 cur_pes_payload_size; /* ... of which already this much written */ - guint16 pes_bytes_written; + guint32 pes_bytes_written; /* PTS/DTS to write if the flags in the packet info are set */ /* in MPEG PTS clock time */ @@ -216,6 +217,16 @@ struct TsMuxStream { /* Opus */ gboolean is_opus; guint8 opus_channel_config_code; + /* Jpeg2000 */ + gint32 horizontal_size; + gint32 vertical_size; + gint32 den; + gint32 num; + /* Maximum bitrate box */ + guint32 max_bitrate; + guint16 profile_and_level; + gboolean interlace_mode; + guint8 color_spec; }; /* stream management */ diff --git a/gst/videoparsers/gstjpeg2000parse.c b/gst/videoparsers/gstjpeg2000parse.c index e2ebc8c..d55f503 100644 --- a/gst/videoparsers/gstjpeg2000parse.c +++ b/gst/videoparsers/gstjpeg2000parse.c @@ -1,5 +1,5 @@ /* GStreamer JPEG 2000 Parser - * Copyright (C) <2016> Grok Image Compession Inc. + * Copyright (C) <2016-2017> Grok Image Compression Inc. * @author Aaron Boxer * * This library is free software; you can redistribute it and/or @@ -25,6 +25,35 @@ #include "gstjpeg2000parse.h" #include +/* Not used at the moment +static gboolean gst_jpeg2000_parse_is_cinema(guint16 rsiz) { + return ((rsiz >= GST_JPEG2000_PARSE_PROFILE_CINEMA_2K) && (rsiz <= GST_JPEG2000_PARSE_PROFILE_CINEMA_S4K)); +} +static gboolean gst_jpeg2000_parse_is_storage(guint16 rsiz) { + return (rsiz == GST_JPEG2000_PARSE_PROFILE_CINEMA_LTS); +} +*/ +static gboolean +gst_jpeg2000_parse_is_broadcast (guint16 rsiz) +{ + return ((rsiz >= GST_JPEG2000_PARSE_PROFILE_BC_SINGLE) && + (rsiz <= ((GST_JPEG2000_PARSE_PROFILE_BC_MULTI_R) | (0x000b))) + && ((rsiz & (~GST_JPEG2000_PARSE_PROFILE_BC_MASK)) == 0)); +} + +static gboolean +gst_jpeg2000_parse_is_imf (guint16 rsiz) +{ + return ((rsiz >= GST_JPEG2000_PARSE_PROFILE_IMF_2K) + && (rsiz <= ((GST_JPEG2000_PARSE_PROFILE_IMF_8K_R) | (0x009b)))); +} + +static gboolean +gst_jpeg2000_parse_is_part_2 (guint16 rsiz) +{ + return (rsiz & GST_JPEG2000_PARSE_PROFILE_PART2); +} + static void @@ -67,24 +96,19 @@ static GstStaticPadTemplate srctemplate = " width = (int)[1, MAX], height = (int)[1, MAX]," GST_JPEG2000_SAMPLING_LIST "," GST_JPEG2000_COLORSPACE_LIST "," + " profile = (int)[0, 49151]," " parsed = (boolean) true;" "image/x-j2c," " width = (int)[1, MAX], height = (int)[1, MAX]," GST_JPEG2000_SAMPLING_LIST "," - GST_JPEG2000_COLORSPACE_LIST "," " parsed = (boolean) true") + GST_JPEG2000_COLORSPACE_LIST "," + " profile = (int)[0, 49151]," " parsed = (boolean) true") ); static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("image/x-jpc," - GST_JPEG2000_SAMPLING_LIST ";" - "image/x-jpc, " - GST_JPEG2000_COLORSPACE_LIST ";" - "image/x-j2c," - GST_JPEG2000_SAMPLING_LIST ";" - "image/x-j2c, " GST_JPEG2000_COLORSPACE_LIST) - ); + GST_STATIC_CAPS ("image/jp2;image/x-jpc;image/x-j2c")); #define parent_class gst_jpeg2000_parse_parent_class G_DEFINE_TYPE (GstJPEG2000Parse, gst_jpeg2000_parse, GST_TYPE_BASE_PARSE); @@ -219,17 +243,12 @@ gst_jpeg2000_parse_negotiate (GstJPEG2000Parse * parse, GstCaps * in_caps) } } - /* FIXME We could fail the negotiation immediatly if caps are empty */ if (caps && !gst_caps_is_empty (caps)) { /* fixate to avoid ambiguity with lists when parsing */ caps = gst_caps_fixate (caps); codec_format = format_from_media_type (gst_caps_get_structure (caps, 0)); } - /* default */ - if (codec_format == GST_JPEG2000_PARSE_NO_CODEC) - codec_format = GST_JPEG2000_PARSE_J2C; - GST_DEBUG_OBJECT (parse, "selected codec format %d", codec_format); parse->codec_format = codec_format; @@ -265,13 +284,17 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse, guint eoc_offset = 0; GstCaps *current_caps = NULL; GstStructure *current_caps_struct = NULL; - const gchar *colorspace_string = NULL; GstJPEG2000Colorspace colorspace = GST_JPEG2000_COLORSPACE_NONE; guint x0, y0, x1, y1; guint width = 0, height = 0; guint8 dx[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS]; guint8 dy[GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS]; guint16 numcomps; + guint16 capabilities = 0; + guint16 profile = 0; + gboolean validate_main_level = FALSE; + guint8 main_level = 0; + guint8 sub_level = 0; guint16 compno; GstJPEG2000Sampling parsed_sampling = GST_JPEG2000_SAMPLING_NONE; const gchar *sink_sampling_string = NULL; @@ -283,27 +306,49 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse, GstCaps *src_caps = NULL; guint frame_size = 0; gboolean is_j2c; + gboolean parsed_j2c_4cc = FALSE; if (!gst_buffer_map (frame->buffer, &map, GST_MAP_READ)) { GST_ERROR_OBJECT (jpeg2000parse, "Unable to map buffer"); return GST_FLOW_ERROR; } + gst_byte_reader_init (&reader, map.data, map.size); + /* try to get from caps */ if (jpeg2000parse->codec_format == GST_JPEG2000_PARSE_NO_CODEC) gst_jpeg2000_parse_negotiate (jpeg2000parse, NULL); - is_j2c = jpeg2000parse->codec_format == GST_JPEG2000_PARSE_J2C; + /* if we can't get from caps, then try to parse */ + if (jpeg2000parse->codec_format == GST_JPEG2000_PARSE_NO_CODEC) { + /* check for "jp2c" box */ + /* both jp2 and j2c will be found with this scan, and both will be treated as j2c format */ + j2c_box_id_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff, + GST_MAKE_FOURCC ('j', 'p', '2', 'c'), 0, + gst_byte_reader_get_remaining (&reader)); + parsed_j2c_4cc = TRUE; + is_j2c = j2c_box_id_offset != -1; + jpeg2000parse->codec_format = + is_j2c ? GST_JPEG2000_PARSE_J2C : GST_JPEG2000_PARSE_JPC; - gst_byte_reader_init (&reader, map.data, map.size); - num_prefix_bytes = GST_JPEG2000_MARKER_SIZE; + } else { + /* for now, just treat JP2 as J2C */ + if (jpeg2000parse->codec_format == GST_JPEG2000_PARSE_JP2) { + jpeg2000parse->codec_format = GST_JPEG2000_PARSE_J2C; + } + is_j2c = jpeg2000parse->codec_format == GST_JPEG2000_PARSE_J2C; + } + num_prefix_bytes = GST_JPEG2000_MARKER_SIZE; if (is_j2c) { num_prefix_bytes += GST_JPEG2000_JP2_SIZE_OF_BOX_LEN + GST_JPEG2000_JP2_SIZE_OF_BOX_ID; - /* check for "jp2c" */ - j2c_box_id_offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff, - GST_MAKE_FOURCC ('j', 'p', '2', 'c'), 0, - gst_byte_reader_get_remaining (&reader)); + /* check for "jp2c" (may have already parsed j2c_box_id_offset if caps are empty) */ + if (!parsed_j2c_4cc) { + j2c_box_id_offset = + gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff, + GST_MAKE_FOURCC ('j', 'p', '2', 'c'), 0, + gst_byte_reader_get_remaining (&reader)); + } if (j2c_box_id_offset == -1) { GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL, @@ -361,10 +406,47 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse, goto beach; } - /* 2 to skip marker size, and another 2 to skip rsiz field */ - if (!gst_byte_reader_skip (&reader, num_prefix_bytes + 2 + 2)) + /* 2 to skip marker size */ + if (!gst_byte_reader_skip (&reader, num_prefix_bytes + 2)) + goto beach; + + if (!gst_byte_reader_get_uint16_be (&reader, &capabilities)) goto beach; + profile = capabilities & GST_JPEG2000_PARSE_PROFILE_MASK; + if (!gst_jpeg2000_parse_is_part_2 (capabilities)) { + if ((profile > GST_JPEG2000_PARSE_PROFILE_CINEMA_LTS) + && !gst_jpeg2000_parse_is_broadcast (profile) + && !gst_jpeg2000_parse_is_imf (profile)) { + GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL, + ("Unrecognized JPEG 2000 profile %d", profile)); + ret = GST_FLOW_ERROR; + goto beach; + } + if (gst_jpeg2000_parse_is_broadcast (profile)) { + main_level = capabilities & 0xF; + validate_main_level = TRUE; + } else if (gst_jpeg2000_parse_is_imf (profile)) { + main_level = capabilities & 0xF; + validate_main_level = TRUE; + sub_level = (capabilities >> 4) & 0xF; + if (sub_level > 9) { + GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL, + ("Sub level %d is invalid", sub_level)); + ret = GST_FLOW_ERROR; + goto beach; + } + } + if (validate_main_level && main_level > 11) { + GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL, + ("Main level %d is invalid", main_level)); + ret = GST_FLOW_ERROR; + goto beach; + + } + } + + if (!gst_byte_reader_get_uint32_be (&reader, &x1)) goto beach; @@ -399,7 +481,7 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse, if (!gst_byte_reader_get_uint16_be (&reader, &numcomps)) goto beach; - if (numcomps == 2 || numcomps > GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS) { + if (numcomps == 0 || numcomps > GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS) { GST_ELEMENT_ERROR (jpeg2000parse, STREAM, DECODE, NULL, ("Unsupported number of components %d", numcomps)); ret = GST_FLOW_NOT_NEGOTIATED; @@ -407,28 +489,46 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse, } current_caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse)); - if (!current_caps) { - GST_ERROR_OBJECT (jpeg2000parse, "Unable to get current caps"); - ret = GST_FLOW_NOT_NEGOTIATED; - goto beach; - } + if (current_caps) { + const gchar *colorspace_string = NULL; + current_caps_struct = gst_caps_get_structure (current_caps, 0); + if (!current_caps_struct) { + GST_ERROR_OBJECT (jpeg2000parse, + "Unable to get structure of current caps struct"); + ret = GST_FLOW_NOT_NEGOTIATED; + goto beach; + } - current_caps_struct = gst_caps_get_structure (current_caps, 0); - if (!current_caps_struct) { - GST_ERROR_OBJECT (jpeg2000parse, - "Unable to get structure of current caps struct"); - ret = GST_FLOW_NOT_NEGOTIATED; - goto beach; - } + colorspace_string = gst_structure_get_string + (current_caps_struct, "colorspace"); + if (colorspace_string) + colorspace = gst_jpeg2000_colorspace_from_string (colorspace_string); + sink_sampling_string = gst_structure_get_string + (current_caps_struct, "sampling"); + if (sink_sampling_string) + sink_sampling = gst_jpeg2000_sampling_from_string (sink_sampling_string); - colorspace_string = gst_structure_get_string - (current_caps_struct, "colorspace"); - if (colorspace_string) - colorspace = gst_jpeg2000_colorspace_from_string (colorspace_string); - sink_sampling_string = gst_structure_get_string - (current_caps_struct, "sampling"); - if (sink_sampling_string) - sink_sampling = gst_jpeg2000_sampling_from_string (sink_sampling_string); + } else { + /* guess color space based on number of components */ + if (numcomps == 0 || numcomps > 4) { + GST_ERROR_OBJECT (jpeg2000parse, + "Unable to guess color space from number of components %d", numcomps); + ret = GST_FLOW_NOT_NEGOTIATED; + goto beach; + } + colorspace = + (numcomps >= + 3) ? GST_JPEG2000_COLORSPACE_RGB : GST_JPEG2000_COLORSPACE_GRAY; + if (numcomps == 4) { + GST_WARNING_OBJECT (jpeg2000parse, "No caps available: assuming RGBA"); + } else if (numcomps == 3) { + GST_WARNING_OBJECT (jpeg2000parse, "No caps available: assuming RGB"); + } else if (numcomps == 2) { + GST_WARNING_OBJECT (jpeg2000parse, + "No caps available: assuming grayscale with alpha"); + } + + } for (compno = 0; compno < numcomps; ++compno) { @@ -560,18 +660,64 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse, src_caps = gst_caps_new_simple (media_type_from_codec_format - (jpeg2000parse->codec_format), "width", G_TYPE_INT, width, "height", - G_TYPE_INT, height, "colorspace", G_TYPE_STRING, + (jpeg2000parse->codec_format), + "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, + "colorspace", G_TYPE_STRING, gst_jpeg2000_colorspace_to_string (colorspace), "sampling", - G_TYPE_STRING, gst_jpeg2000_sampling_to_string (source_sampling), NULL); + G_TYPE_STRING, gst_jpeg2000_sampling_to_string (source_sampling), + "profile", G_TYPE_UINT, profile, NULL); + + if (gst_jpeg2000_parse_is_broadcast (capabilities) + || gst_jpeg2000_parse_is_imf (capabilities)) { + gst_caps_set_simple (src_caps, "main-level", G_TYPE_UINT, main_level, + NULL); + if (gst_jpeg2000_parse_is_imf (capabilities)) { + gst_caps_set_simple (src_caps, "sub-level", G_TYPE_UINT, sub_level, + NULL); + } + } - if (gst_structure_get_fraction (current_caps_struct, "framerate", &fr_num, - &fr_denom)) { - gst_caps_set_simple (src_caps, "framerate", GST_TYPE_FRACTION, fr_num, - fr_denom, NULL); - } else { - GST_WARNING_OBJECT (jpeg2000parse, "No framerate set"); + if (current_caps_struct) { + const gchar *caps_string = gst_structure_get_string + (current_caps_struct, "colorimetry"); + if (caps_string) { + gst_caps_set_simple (src_caps, "colorimetry", G_TYPE_STRING, + caps_string, NULL); + } + caps_string = gst_structure_get_string + (current_caps_struct, "interlace-mode"); + if (caps_string) { + gst_caps_set_simple (src_caps, "interlace-mode", G_TYPE_STRING, + caps_string, NULL); + } + caps_string = gst_structure_get_string + (current_caps_struct, "field-order"); + if (caps_string) { + gst_caps_set_simple (src_caps, "field-order", G_TYPE_STRING, + caps_string, NULL); + } + caps_string = gst_structure_get_string + (current_caps_struct, "multiview-mode"); + if (caps_string) { + gst_caps_set_simple (src_caps, "multiview-mode", G_TYPE_STRING, + caps_string, NULL); + } + caps_string = gst_structure_get_string + (current_caps_struct, "chroma-site"); + if (caps_string) { + gst_caps_set_simple (src_caps, "chroma-site", G_TYPE_STRING, + caps_string, NULL); + } + if (gst_structure_get_fraction (current_caps_struct, "framerate", &fr_num, + &fr_denom)) { + gst_caps_set_simple (src_caps, "framerate", GST_TYPE_FRACTION, fr_num, + fr_denom, NULL); + } else { + GST_WARNING_OBJECT (jpeg2000parse, "No framerate set"); + } } + if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), src_caps)) { GST_ERROR_OBJECT (jpeg2000parse, "Unable to set source caps"); ret = GST_FLOW_NOT_NEGOTIATED; @@ -604,6 +750,8 @@ gst_jpeg2000_parse_handle_frame (GstBaseParse * parse, frame_size, eoc_frame_size); } frame_size = eoc_frame_size; + } else { + goto beach; } /* clean up and finish frame */ diff --git a/gst/videoparsers/gstjpeg2000parse.h b/gst/videoparsers/gstjpeg2000parse.h index 93236db..05c03d1 100644 --- a/gst/videoparsers/gstjpeg2000parse.h +++ b/gst/videoparsers/gstjpeg2000parse.h @@ -1,5 +1,5 @@ /* GStreamer JPEG 2000 Parser - * Copyright (C) <2016> Grok Image Compression Inc. + * Copyright (C) <2016-2017> Grok Image Compression Inc. * @author Aaron Boxer * * This library is free software; you can redistribute it and/or @@ -42,7 +42,64 @@ G_BEGIN_DECLS typedef struct _GstJPEG2000Parse GstJPEG2000Parse; typedef struct _GstJPEG2000ParseClass GstJPEG2000ParseClass; -#define GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS 4 + +/** + * JPEG 2000 Profiles (stored in rsiz/capabilities field in code stream header) + * See Table A.10 from 15444-1 (updated in various AMDs) + * + * For broadcast profiles, the GST_JPEG2000_PARSE_PROFILE_BC_XXXX profile value must be combined with the target + * main level (3-0 LSBs, with value between 0 and 11). + * Example: + * capabilities GST_JPEG2000_PARSE_PROFILE_BC_MULTI | 0x0005 (in this case, main level equals 5) + * + * For IMF profiles, the GST_JPEG2000_PARSE_PROFILE_IMF_XXXX profile value must be combined with the target main level + * (3-0 LSBs, with value between 0 and 11), and target sub level (7-4 LSBs, with value between 0 and 9). + * Example: + * capabilities GST_JPEG2000_PARSE_PROFILE_IMF_2K | 0x0040 | 0x0005 (in this case, main level equals 5 and sub level equals 4) + * + * + * Broadcast main level (15444-1 AMD4,AMD8) + * + * Note: Mbit/s == 10^6 bits/s; Msamples/s == 10^6 samples/s + * + * Level 0: no max rate + * Level 1: 200 Mbits/s, 65 Msamples/s + * Level 2: 200 Mbits/s, 130 Msamples/s + * Level 3: 200 Mbits/s, 195 Msamples/s + * Level 4: 400 Mbits/s, 260 Msamples/s + * Level 5: 800Mbits/s, 520 Msamples/s + * Level >= 6: 2^(Level-6) * 1600 Mbits/s, 2^(Level-6) * 1200 Msamples/s + * + * Broadcast tiling + * + * Either single-tile or multi-tile. Multi-tile only permits + * 1 or 4 tiles per frame, where multiple tiles have identical + * sizes, and are configured in either 2x2 or 1x4 layout. + * + * */ + +#define GST_JPEG2000_PARSE_PROFILE_NONE 0x0000 /** no profile - defined in 15444-1 */ +#define GST_JPEG2000_PARSE_PROFILE_0 0x0001 /** Profile 0 - defined in 15444-1,Table A.45 */ +#define GST_JPEG2000_PARSE_PROFILE_1 0x0002 /** Profile 1 - defined in 15444-1,Table A.45 */ +#define GST_JPEG2000_PARSE_PROFILE_CINEMA_2K 0x0003 /** 2K Cinema profile - defined in 15444-1 AMD1 */ +#define GST_JPEG2000_PARSE_PROFILE_CINEMA_4K 0x0004 /** 4K Cinema profile - defined in 15444-1 AMD1 */ +#define GST_JPEG2000_PARSE_PROFILE_CINEMA_S2K 0x0005 /** Scalable 2K Cinema profile - defined in 15444-1 AMD2 */ +#define GST_JPEG2000_PARSE_PROFILE_CINEMA_S4K 0x0006 /** Scalable 4K Cinema profile - defined in 15444-1 AMD2 */ +#define GST_JPEG2000_PARSE_PROFILE_CINEMA_LTS 0x0007/** Long Term Storage Cinema profile - defined in 15444-1 AMD2 */ +#define GST_JPEG2000_PARSE_PROFILE_BC_SINGLE 0x0100 /** Single Tile Broadcast profile - defined in 15444-1 AMD3 */ +#define GST_JPEG2000_PARSE_PROFILE_BC_MULTI 0x0200 /** Multi Tile Broadcast profile - defined in 15444-1 AMD3 */ +#define GST_JPEG2000_PARSE_PROFILE_BC_MULTI_R 0x0300 /** Multi Tile Reversible Broadcast profile - defined in 15444-1 AMD3 */ +#define GST_JPEG2000_PARSE_PROFILE_BC_MASK 0x0F0F /** Mask for broadcast profile, including main level */ +#define GST_JPEG2000_PARSE_PROFILE_IMF_2K 0x0400 /** 2K Single Tile Lossy IMF profile - defined in 15444-1 AMD 8 */ +#define GST_JPEG2000_PARSE_PROFILE_IMF_4K 0x0401 /** 4K Single Tile Lossy IMF profile - defined in 15444-1 AMD 8 */ +#define GST_JPEG2000_PARSE_PROFILE_IMF_8K 0x0402 /** 8K Single Tile Lossy IMF profile - defined in 15444-1 AMD 8 */ +#define GST_JPEG2000_PARSE_PROFILE_IMF_2K_R 0x0403 /** 2K Single/Multi Tile Reversible IMF profile - defined in 15444-1 AMD 8 */ +#define GST_JPEG2000_PARSE_PROFILE_IMF_4K_R 0x0800 /** 4K Single/Multi Tile Reversible IMF profile - defined in 15444-1 AMD 8 */ +#define GST_JPEG2000_PARSE_PROFILE_IMF_8K_R 0x0801 /** 8K Single/Multi Tile Reversible IMF profile - defined in 15444-1 AMD 8 */ +#define GST_JPEG2000_PARSE_PROFILE_MASK 0xBFFF /** Mask for profile bits */ +#define GST_JPEG2000_PARSE_PROFILE_PART2 0x8000 /** At least 1 extension defined in 15444-2 (Part-2) */ + +#define GST_JPEG2000_PARSE_MAX_SUPPORTED_COMPONENTS 4 typedef enum { -- 2.7.4