tsdemux, tsmux, psmux: apply 1.14.4 upstream code
authorGilbok Lee <gilbok.lee@samsung.com>
Thu, 21 Feb 2019 08:42:28 +0000 (17:42 +0900)
committerGilbok Lee <gilbok.lee@samsung.com>
Mon, 25 Feb 2019 08:04:33 +0000 (17:04 +0900)
Change-Id: I6d51014acca7512a5f33b69e2b095ae56d56c3d0

17 files changed:
gst/mpegpsmux/mpegpsmux.c
gst/mpegtsdemux/mpegtsbase.c
gst/mpegtsdemux/mpegtsbase.h
gst/mpegtsdemux/mpegtspacketizer.h
gst/mpegtsdemux/tsdemux.c
gst/mpegtsdemux/tsdemux.h
gst/mpegtsmux/Makefile.am
gst/mpegtsmux/meson.build
gst/mpegtsmux/mpegtsmux.c
gst/mpegtsmux/mpegtsmux_jpeg2000.c [new file with mode: 0644]
gst/mpegtsmux/mpegtsmux_jpeg2000.h [new file with mode: 0644]
gst/mpegtsmux/tsmux/tsmux.c
gst/mpegtsmux/tsmux/tsmux.h
gst/mpegtsmux/tsmux/tsmuxstream.c
gst/mpegtsmux/tsmux/tsmuxstream.h
gst/videoparsers/gstjpeg2000parse.c
gst/videoparsers/gstjpeg2000parse.h

index cdd3681..f627496 100644 (file)
@@ -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);
index e492cab..3e832da 100644 (file)
@@ -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))
index e5117c1..4121c0e 100644 (file)
@@ -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 */
index c59e7bc..c86ef6b 100644 (file)
@@ -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);
index 939a24a..d4107ae 100644 (file)
@@ -48,6 +48,7 @@
 #include "pesparse.h"
 #include <gst/codecparsers/gsth264parser.h>
 #include <gst/codecparsers/gstmpegvideoparser.h>
+#include <gst/video/video-color.h>
 
 #include <math.h>
 
@@ -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);
     }
index 0a6e19a..a7c08da 100644 (file)
 #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())
index 0fa78d7..05c468c 100644 (file)
@@ -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
index 45648f1..568b695 100644 (file)
@@ -3,6 +3,7 @@ tsmux_sources = [
   'mpegtsmux_aac.c',
   'mpegtsmux_opus.c',
   'mpegtsmux_ttxt.c',
+  'mpegtsmux_jpeg2000.c',
   'tsmux/tsmux.c',
   'tsmux/tsmuxstream.c',
 ]
index 67c2b72..99562fd 100644 (file)
 #include "mpegtsmux_aac.h"
 #include "mpegtsmux_ttxt.h"
 #include "mpegtsmux_opus.h"
+#include "mpegtsmux_jpeg2000.h"
+#include <gst/videoparsers/gstjpeg2000parse.h>
+#include <gst/video/video-color.h>
 
 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 (file)
index 0000000..1298d38
--- /dev/null
@@ -0,0 +1,134 @@
+/* GStreamer JPEG 2000 Parser
+ *
+ * Copyright (C) <2016> Milos Seleceni
+ *  @author Milos Seleceni <milos.seleceni@comprimato.com>
+ *
+ * Copyright (C) <2016-2017> Grok Image Compression Inc.
+ *  @author Aaron Boxer <boxerab@gmail.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include "mpegtsmux_jpeg2000.h"
+#include <string.h>
+#include <gst/audio/audio.h>
+#include <gst/base/gstbytewriter.h>
+#include <gst/gst.h>
+
+#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 (file)
index 0000000..c0e4ab9
--- /dev/null
@@ -0,0 +1,63 @@
+/* GStreamer JPEG 2000 Parser
+ *
+ * Copyright (C) <2016> Milos Seleceni
+ *  @author Milos Seleceni <milos.seleceni@comprimato.com>
+ *
+ * Copyright (C) <2016-2017> Grok Image Compression Inc.
+ *  @author Aaron Boxer <boxerab@gmail.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.
+ */
+
+#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__ */
index ee28704..6fa9e0d 100644 (file)
@@ -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 */
index dd1185a..11a1480 100644 (file)
@@ -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 */
index f719d8f..4cbd217 100644 (file)
@@ -84,6 +84,7 @@
 #include <string.h>
 
 #include <gst/mpegts/mpegts.h>
+#include <gst/base/gstbytewriter.h>
 
 #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];
index 2766718..7edfb61 100644 (file)
@@ -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 */
index e2ebc8c..d55f503 100644 (file)
@@ -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 <boxerab@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
 #include "gstjpeg2000parse.h"
 #include <gst/base/base.h>
 
+/* 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 */
index 93236db..05c03d1 100644 (file)
@@ -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 <boxerab@gmail.com>
  *
  * 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
 {