mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
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:
parent
3e0db49f5b
commit
bbbdc2cd7e
11 changed files with 656 additions and 10 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
134
gst/mpegtsmux/mpegtsmux_jpeg2000.c
Normal file
134
gst/mpegtsmux/mpegtsmux_jpeg2000.c
Normal 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);
|
||||
}
|
63
gst/mpegtsmux/mpegtsmux_jpeg2000.h
Normal file
63
gst/mpegtsmux/mpegtsmux_jpeg2000.h
Normal 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__ */
|
|
@ -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 */
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue