diff options
author | Aaron Boxer <boxerab@gmail.com> | 2017-07-19 10:14:21 -0400 |
---|---|---|
committer | Sebastian Dröge <sebastian@centricular.com> | 2017-07-21 09:33:31 +0300 |
commit | bbbdc2cd7ed282edabb6715a0b9b72d23676ee0f (patch) | |
tree | db3a202dd0c96cff9b176ecc86cf6cc1db2d2b9c /gst/mpegtsmux | |
parent | 3e0db49f5b83e2b4ad4386c11c64a5d391c79256 (diff) |
tsmux/tsdemux: Add support for JPEG2000
Based on patches by Milos Seleceni.
https://bugzilla.gnome.org/show_bug.cgi?id=753323
Diffstat (limited to 'gst/mpegtsmux')
-rw-r--r-- | gst/mpegtsmux/Makefile.am | 6 | ||||
-rw-r--r-- | gst/mpegtsmux/mpegtsmux.c | 122 | ||||
-rw-r--r-- | gst/mpegtsmux/mpegtsmux_jpeg2000.c | 134 | ||||
-rw-r--r-- | gst/mpegtsmux/mpegtsmux_jpeg2000.h | 63 | ||||
-rw-r--r-- | gst/mpegtsmux/tsmux/tsmux.c | 2 | ||||
-rw-r--r-- | gst/mpegtsmux/tsmux/tsmuxstream.c | 75 | ||||
-rw-r--r-- | gst/mpegtsmux/tsmux/tsmuxstream.h | 11 |
7 files changed, 408 insertions, 5 deletions
diff --git a/gst/mpegtsmux/Makefile.am b/gst/mpegtsmux/Makefile.am index 18d58d1ad..27c31c27b 100644 --- a/gst/mpegtsmux/Makefile.am +++ b/gst/mpegtsmux/Makefile.am @@ -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 diff --git a/gst/mpegtsmux/mpegtsmux.c b/gst/mpegtsmux/mpegtsmux.c index 67c2b72bc..98e97c3cf 100644 --- a/gst/mpegtsmux/mpegtsmux.c +++ b/gst/mpegtsmux/mpegtsmux.c @@ -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); diff --git a/gst/mpegtsmux/mpegtsmux_jpeg2000.c b/gst/mpegtsmux/mpegtsmux_jpeg2000.c new file mode 100644 index 000000000..2e8db8c46 --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux_jpeg2000.c @@ -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 index 000000000..37ed433b7 --- /dev/null +++ b/gst/mpegtsmux/mpegtsmux_jpeg2000.h @@ -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__ */ diff --git a/gst/mpegtsmux/tsmux/tsmux.c b/gst/mpegtsmux/tsmux/tsmux.c index ee2870498..24ae7e2b8 100644 --- a/gst/mpegtsmux/tsmux/tsmux.c +++ b/gst/mpegtsmux/tsmux/tsmux.c @@ -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 */ diff --git a/gst/mpegtsmux/tsmux/tsmuxstream.c b/gst/mpegtsmux/tsmux/tsmuxstream.c index f719d8f3f..4cbd21770 100644 --- a/gst/mpegtsmux/tsmux/tsmuxstream.c +++ b/gst/mpegtsmux/tsmux/tsmuxstream.c @@ -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]; diff --git a/gst/mpegtsmux/tsmux/tsmuxstream.h b/gst/mpegtsmux/tsmux/tsmuxstream.h index 2766718ed..8befbe5f4 100644 --- a/gst/mpegtsmux/tsmux/tsmuxstream.h +++ b/gst/mpegtsmux/tsmux/tsmuxstream.h @@ -134,6 +134,7 @@ enum TsMuxStreamType { TSMUX_ST_VIDEO_MPEG4 = 0x10, TSMUX_ST_VIDEO_H264 = 0x1b, TSMUX_ST_VIDEO_HEVC = 0x24, + TSMUX_ST_VIDEO_JP2K = 0x21, /* private stream types */ TSMUX_ST_PS_AUDIO_AC3 = 0x81, @@ -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 */ |