Based on patches by Milos Seleceni.
https://bugzilla.gnome.org/show_bug.cgi?id=753323
#include "pesparse.h"
#include <gst/codecparsers/gsth264parser.h>
#include <gst/codecparsers/gstmpegvideoparser.h>
+#include <gst/video/video-color.h>
#include <math.h>
typedef struct _TSDemuxStream TSDemuxStream;
typedef struct _TSDemuxH264ParsingInfos TSDemuxH264ParsingInfos;
+typedef struct _TSDemuxJP2KParsingInfos TSDemuxJP2KParsingInfos;
/* Returns TRUE if a keyframe was found */
typedef gboolean (*GstTsDemuxKeyFrameScanFunction) (TSDemuxStream * stream,
SimpleBuffer framedata;
};
+struct _TSDemuxJP2KParsingInfos
+{
+ /* J2K parsing data */
+ gboolean interlace;
+};
struct _TSDemuxStream
{
MpegTSBaseStream stream;
GstTsDemuxKeyFrameScanFunction scan_function;
TSDemuxH264ParsingInfos h264infos;
+ TSDemuxJP2KParsingInfos jp2kInfos;
};
#define VIDEO_CAPS \
"video/x-cavs;" \
"video/x-wmv," \
"wmvversion = (int) 3, " \
- "format = (string) WVC1" \
- )
+ "format = (string) WVC1;" \
+ "image/x-jpc;" \
+)
#define AUDIO_CAPS \
GST_STATIC_CAPS ( \
"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");
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;
+
+ guint8 *packet_data = NULL;
+ guint packet_size;
+ guint remaining = 0;
+
+ 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);
+
+ /* 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 */
+ header_tag = gst_byte_reader_get_uint32_be_unchecked (&reader);
+ 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);
+ remaining = gst_byte_reader_get_remaining (&reader);
+ packet_size = remaining;
+
+ GST_DEBUG_OBJECT (stream->pad,
+ "Size of first codestream in TS stream: %d; bytes remaining: %d", AUF[0],
+ remaining);
+
+ if (!gst_byte_reader_dup_data (&reader, packet_size, &packet_data)) {
+ GST_ERROR ("Required size %d > %d than remaining size in buffer", AUF[0],
+ packet_size);
+ goto error;
+ }
+#ifdef TSDEMUX_JP2K_SUPPORT_INTERLACE
+ if (stream->jp2kInfos.interlace) {
+ remaining = gst_byte_reader_get_remaining (&reader);
+ if (!gst_byte_reader_dup_data (&reader, AUF[1], &packet_data)) {
+ GST_ERROR ("Required size %d > %d than remaining size in buffer", AUF[1],
+ remaining);
+ goto error;
+ }
+
+ }
+#endif
+ g_free (stream->data);
+ stream->data = NULL;
+ stream->current_size = 0;
+ return gst_buffer_new_wrapped (packet_data, packet_size);
+
+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)
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);
} else {
buffer = gst_buffer_new_wrapped (stream->data, stream->current_size);
}
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);
} else {
buffer = gst_buffer_new_wrapped (stream->data, stream->current_size);
}
#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())
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) \
$(GST_BASE_CFLAGS) $(GST_CFLAGS)
mpegtsmux.h \
mpegtsmux_aac.h \
mpegtsmux_ttxt.h \
- mpegtsmux_opus.h
+ mpegtsmux_opus.h \
+ mpegtsmux_jpeg2000.h
#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
"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,"
"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",
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);
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) {
}
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);
}
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);
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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__ */
gst_buffer_unmap (buf, &map);
+ GST_DEBUG_OBJECT (mux, "Writing PES of size %d",
+ (int) gst_buffer_get_size (buf));
res = tsmux_packet_out (mux, buf, cur_pcr);
/* Reset all dynamic flags */
#include <string.h>
#include <gst/mpegts/mpegts.h>
+#include <gst/base/gstbytewriter.h>
#include "tsmuxcommon.h"
#include "tsmuxstream.h"
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:
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];
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,
/* 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 */
/* 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
/* 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