tsmux/tsdemux: Add support for JPEG2000

Based on patches by Milos Seleceni.

https://bugzilla.gnome.org/show_bug.cgi?id=753323
This commit is contained in:
Aaron Boxer 2017-07-19 10:14:21 -04:00 committed by Sebastian Dröge
parent 3e0db49f5b
commit bbbdc2cd7e
11 changed files with 656 additions and 10 deletions

View 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;
};
#define VIDEO_CAPS \
@ -215,8 +223,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 ( \
@ -1449,6 +1458,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");
@ -2553,11 +2639,150 @@ 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;
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)
@ -2615,6 +2840,8 @@ 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);
} else {
buffer = gst_buffer_new_wrapped (stream->data, stream->current_size);
}
@ -2649,6 +2876,8 @@ 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);
} else {
buffer = gst_buffer_new_wrapped (stream->data, stream->current_size);
}

View file

@ -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())

View file

@ -6,7 +6,8 @@ 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) \
$(GST_BASE_CFLAGS) $(GST_CFLAGS)
@ -21,4 +22,5 @@ noinst_HEADERS = \
mpegtsmux.h \
mpegtsmux_aac.h \
mpegtsmux_ttxt.h \
mpegtsmux_opus.h
mpegtsmux_opus.h \
mpegtsmux_jpeg2000.h

View file

@ -98,6 +98,9 @@
#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
@ -125,6 +128,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 +153,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",
@ -588,6 +593,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 +749,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 +847,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 +901,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);

View file

@ -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);
}

View file

@ -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__ */

View file

@ -1104,6 +1104,8 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
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 */

View 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];

View 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,
@ -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 */

View 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

View 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