diff options
author | Jesper Larsen <jesper.larsen@ixonos.com> | 2013-11-20 11:14:46 +0100 |
---|---|---|
committer | Jesper Larsen <jesper.larsen@ixonos.com> | 2014-02-06 15:55:46 +0100 |
commit | 93a8137be62075fbcfa2b21cc7dd13e25b8d70af (patch) | |
tree | 9ab551a29a75d8b5aae1101f00b1f0ee2c3dce42 | |
parent | b7d256b4c292d0a83f8c7eb2a02de9700082700b (diff) |
mpegtsmux: Add support for muxing SI tables
The muxer is now able to include DVB sections in the transport stream.
The si-interval property will determine how often the SI tables are
muxed into the stream.
The section is handled by the mpeg-ts library. Below is a small example
that will include a Netork Information Table with a Network Name
descriptor in the stream.
GstMpegTsNIT *nit;
GstMpegTsDescriptor *descriptor;
GstMpegTsSection *section;
GstElement *mpegtsmux;
gst_mpegts_initialize ();
nit = gst_mpegts_section_nit_new ();
nit->actual_network = TRUE;
descriptor = gst_mpegts_descriptor_from_dvb_network_name ("Network name");
g_ptr_array_add (nit->descriptors, descriptor);
section = gst_mpegts_section_from_nit (nit);
// mpegtsmux should be retrieved from the pipeline
gst_mpegts_section_send_event (section, mpegtsmux);
gst_mpegts_section_unref (section);
-rw-r--r-- | gst/mpegtsmux/Makefile.am | 3 | ||||
-rw-r--r-- | gst/mpegtsmux/mpegtsmux.c | 43 | ||||
-rw-r--r-- | gst/mpegtsmux/mpegtsmux.h | 1 | ||||
-rw-r--r-- | gst/mpegtsmux/tsmux/Makefile.am | 5 | ||||
-rw-r--r-- | gst/mpegtsmux/tsmux/tsmux.c | 207 | ||||
-rw-r--r-- | gst/mpegtsmux/tsmux/tsmux.h | 18 | ||||
-rw-r--r-- | gst/mpegtsmux/tsmux/tsmuxcommon.h | 2 |
7 files changed, 275 insertions, 4 deletions
diff --git a/gst/mpegtsmux/Makefile.am b/gst/mpegtsmux/Makefile.am index 8dd002c4b..8e3cdba61 100644 --- a/gst/mpegtsmux/Makefile.am +++ b/gst/mpegtsmux/Makefile.am @@ -7,7 +7,8 @@ libgstmpegtsmux_la_SOURCES = \ mpegtsmux_aac.c \ mpegtsmux_ttxt.c -libgstmpegtsmux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstmpegtsmux_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) libgstmpegtsmux_la_LIBADD = $(top_builddir)/gst/mpegtsmux/tsmux/libtsmux.la \ -lgsttag-@GST_API_VERSION@ \ $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_API_VERSION@ $(GST_BASE_LIBS) $(GST_LIBS) diff --git a/gst/mpegtsmux/mpegtsmux.c b/gst/mpegtsmux/mpegtsmux.c index 848833fc6..da57ff410 100644 --- a/gst/mpegtsmux/mpegtsmux.c +++ b/gst/mpegtsmux/mpegtsmux.c @@ -94,6 +94,7 @@ #include <gst/tag/tag.h> #include <gst/video/video.h> +#include <gst/mpegts/mpegts.h> #include "mpegtsmux.h" @@ -110,7 +111,8 @@ enum ARG_M2TS_MODE, ARG_PAT_INTERVAL, ARG_PMT_INTERVAL, - ARG_ALIGNMENT + ARG_ALIGNMENT, + ARG_SI_INTERVAL }; #define MPEGTSMUX_DEFAULT_ALIGNMENT -1 @@ -177,6 +179,7 @@ static GstPad *mpegtsmux_request_new_pad (GstElement * element, static void mpegtsmux_release_pad (GstElement * element, GstPad * pad); static GstStateChangeReturn mpegtsmux_change_state (GstElement * element, GstStateChange transition); +static gboolean mpegtsmux_send_event (GstElement * element, GstEvent * event); static void mpegtsdemux_set_header_on_caps (MpegTsMux * mux); static gboolean mpegtsmux_src_event (GstPad * pad, GstObject * parent, GstEvent * event); @@ -242,6 +245,7 @@ mpegtsmux_class_init (MpegTsMuxClass * klass) gstelement_class->request_new_pad = mpegtsmux_request_new_pad; gstelement_class->release_pad = mpegtsmux_release_pad; gstelement_class->change_state = mpegtsmux_change_state; + gstelement_class->send_event = mpegtsmux_send_event; #if 0 gstelement_class->set_index = GST_DEBUG_FUNCPTR (mpegtsmux_set_index); @@ -277,6 +281,12 @@ mpegtsmux_class_init (MpegTsMuxClass * klass) "(-1 = auto, 0 = all available packets)", -1, G_MAXINT, MPEGTSMUX_DEFAULT_ALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SI_INTERVAL, + g_param_spec_uint ("si-interval", "SI interval", + "Set the interval (in ticks of the 90kHz clock) for writing out the Service" + "Information tables", 1, G_MAXUINT, TSMUX_DEFAULT_SI_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void @@ -310,6 +320,7 @@ mpegtsmux_init (MpegTsMux * mux) mux->m2ts_mode = MPEGTSMUX_DEFAULT_M2TS; mux->pat_interval = TSMUX_DEFAULT_PAT_INTERVAL; mux->pmt_interval = TSMUX_DEFAULT_PMT_INTERVAL; + mux->si_interval = TSMUX_DEFAULT_SI_INTERVAL; mux->prog_map = NULL; mux->alignment = MPEGTSMUX_DEFAULT_ALIGNMENT; @@ -478,6 +489,10 @@ gst_mpegtsmux_set_property (GObject * object, guint prop_id, case ARG_ALIGNMENT: mux->alignment = g_value_get_int (value); break; + case ARG_SI_INTERVAL: + mux->si_interval = g_value_get_uint (value); + tsmux_set_si_interval (mux->tsmux, mux->si_interval); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -506,6 +521,9 @@ gst_mpegtsmux_get_property (GObject * object, guint prop_id, case ARG_ALIGNMENT: g_value_set_int (value, mux->alignment); break; + case ARG_SI_INTERVAL: + g_value_set_uint (value, mux->si_interval); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1707,6 +1725,29 @@ mpegtsmux_change_state (GstElement * element, GstStateChange transition) } static gboolean +mpegtsmux_send_event (GstElement * element, GstEvent * event) +{ + GstMpegTsSection *section; + MpegTsMux *mux = GST_MPEG_TSMUX (element); + + g_return_val_if_fail (event != NULL, FALSE); + + section = gst_event_parse_mpegts_section (event); + gst_event_unref (event); + + if (section) { + GST_DEBUG ("Received event with mpegts section"); + + /* TODO: Check that the section type is supported */ + tsmux_add_mpegts_si_section (mux->tsmux, section); + + return TRUE; + } + + return FALSE; +} + +static gboolean plugin_init (GstPlugin * plugin) { if (!gst_element_register (plugin, "mpegtsmux", GST_RANK_PRIMARY, diff --git a/gst/mpegtsmux/mpegtsmux.h b/gst/mpegtsmux/mpegtsmux.h index c894e5857..f47707feb 100644 --- a/gst/mpegtsmux/mpegtsmux.h +++ b/gst/mpegtsmux/mpegtsmux.h @@ -140,6 +140,7 @@ struct MpegTsMux { guint pat_interval; guint pmt_interval; gint alignment; + guint si_interval; /* state */ gboolean first; diff --git a/gst/mpegtsmux/tsmux/Makefile.am b/gst/mpegtsmux/tsmux/Makefile.am index e995cf7cf..29e52a056 100644 --- a/gst/mpegtsmux/tsmux/Makefile.am +++ b/gst/mpegtsmux/tsmux/Makefile.am @@ -1,7 +1,8 @@ noinst_LTLIBRARIES = libtsmux.la -libtsmux_la_CFLAGS = $(GST_CFLAGS) -libtsmux_la_LIBADD = $(GST_LIBS) +libtsmux_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_CFLAGS) +libtsmux_la_LIBADD = $(GST_LIBS) \ + $(top_builddir)/gst-libs/gst/mpegts/libgstmpegts-$(GST_API_VERSION).la libtsmux_la_LDFLAGS = -module -avoid-version libtsmux_la_SOURCES = tsmux.c tsmuxstream.c diff --git a/gst/mpegtsmux/tsmux/tsmux.c b/gst/mpegtsmux/tsmux/tsmux.c index 78359408b..e9b88dd97 100644 --- a/gst/mpegtsmux/tsmux/tsmux.c +++ b/gst/mpegtsmux/tsmux/tsmux.c @@ -83,6 +83,8 @@ #include <string.h> +#include <gst/mpegts/mpegts.h> + #include "tsmux.h" #include "tsmuxstream.h" #include "crc.h" @@ -114,6 +116,11 @@ static gboolean tsmux_write_pat (TsMux * mux); static gboolean tsmux_write_pmt (TsMux * mux, TsMuxProgram * program); +static void +tsmux_section_free (TsMuxSection * section) +{ + gst_mpegts_section_unref (section->section); +} /** * tsmux_new: @@ -139,6 +146,13 @@ tsmux_new (void) mux->last_pat_ts = -1; mux->pat_interval = TSMUX_DEFAULT_PAT_INTERVAL; + mux->si_changed = TRUE; + mux->last_si_ts = -1; + mux->si_interval = TSMUX_DEFAULT_SI_INTERVAL; + + mux->si_sections = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) tsmux_section_free); + return mux; } @@ -215,6 +229,72 @@ tsmux_get_pat_interval (TsMux * mux) } /** + * tsmux_set_si_interval: + * @mux: a #TsMux + * @freq: a new SI table interval + * + * Set the interval (in cycles of the 90kHz clock) for writing out the SI tables. + * + */ +void +tsmux_set_si_interval (TsMux * mux, guint freq) +{ + g_return_if_fail (mux != NULL); + + mux->si_interval = freq; +} + +/** + * tsmux_get_si_interval: + * @mux: a #TsMux + * + * Get the configured SI table interval. See also tsmux_set_si_interval(). + * + * Returns: the configured SI interval + */ +guint +tsmux_get_si_interval (TsMux * mux) +{ + g_return_val_if_fail (mux != NULL, 0); + + return mux->si_interval; +} + +/** + * tsmux_add_mpegts_si_section: + * @mux: a #TsMux + * @section: (transfer full): a #GstMpegTsSection to add + * + * Add a Service Information #GstMpegTsSection to the stream + * + * Returns: #TRUE on success, #FALSE otherwise + */ +gboolean +tsmux_add_mpegts_si_section (TsMux * mux, GstMpegTsSection * section) +{ + TsMuxSection *tsmux_section; + + g_return_val_if_fail (mux != NULL, FALSE); + g_return_val_if_fail (section != NULL, FALSE); + g_return_val_if_fail (mux->si_sections != NULL, FALSE); + + tsmux_section = g_slice_new0 (TsMuxSection); + + GST_DEBUG ("Adding mpegts section with type %d to mux", + section->section_type); + + tsmux_section->section = section; + tsmux_section->pi.pid = section->pid; + + g_hash_table_insert (mux->si_sections, + GINT_TO_POINTER (section->section_type), tsmux_section); + + mux->si_changed = TRUE; + + return TRUE; +} + +/** * tsmux_free: * @mux: a #TsMux * @@ -244,6 +324,9 @@ tsmux_free (TsMux * mux) } g_list_free (mux->streams); + /* Free SI table sections */ + g_hash_table_destroy (mux->si_sections); + g_slice_free (TsMux, mux); } @@ -754,6 +837,115 @@ tsmux_write_ts_header (guint8 * buf, TsMuxPacketInfo * pi, return TRUE; } +static gboolean +tsmux_section_write_packet (GstMpegTsSectionType * type, + TsMuxSection * section, TsMux * mux) +{ + GstBuffer *section_buffer; + GstBuffer *packet_buffer = NULL; + GstMemory *mem; + guint8 *packet; + guint8 *data; + gsize data_size; + gsize payload_written; + guint len = 0, offset = 0, payload_len = 0; + + g_return_val_if_fail (section != NULL, FALSE); + g_return_val_if_fail (mux != NULL, FALSE); + + /* Mark the start of new PES unit */ + section->pi.packet_start_unit_indicator = TRUE; + + data = gst_mpegts_section_packetize (section->section, &data_size); + + if (!data) { + TS_DEBUG ("Could not packetize section"); + return FALSE; + } + + /* Mark payload data size */ + section->pi.stream_avail = data_size; + payload_written = 0; + + section_buffer = gst_buffer_new_wrapped (data, data_size); + + TS_DEBUG ("Section buffer with size %" G_GSIZE_FORMAT " created", + gst_buffer_get_size (section_buffer)); + + while (section->pi.stream_avail > 0) { + + packet = g_malloc (TSMUX_PACKET_LENGTH); + + if (section->pi.packet_start_unit_indicator) { + /* Wee need room for a pointer byte */ + section->pi.stream_avail++; + + if (!tsmux_write_ts_header (packet, §ion->pi, &len, &offset)) + goto fail; + + /* Write the pointer byte */ + packet[offset++] = 0x00; + payload_len = len - 1; + + } else { + if (!tsmux_write_ts_header (packet, §ion->pi, &len, &offset)) + goto fail; + payload_len = len; + } + + /* Wrap the TS header and adaption field in a GstMemory */ + mem = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY, + packet, TSMUX_PACKET_LENGTH, 0, offset, packet, g_free); + + TS_DEBUG ("Creating packet buffer at offset " + "%" G_GSIZE_FORMAT " with length %u", payload_written, payload_len); + + packet_buffer = gst_buffer_copy_region (section_buffer, GST_BUFFER_COPY_ALL, + payload_written, payload_len); + + /* Prepend the header to the section data */ + gst_buffer_prepend_memory (packet_buffer, mem); + + TS_DEBUG ("Writing %d bytes to section. %d bytes remaining", + len, section->pi.stream_avail - len); + + /* Push the packet without PCR */ + if G_UNLIKELY + (!tsmux_packet_out (mux, packet_buffer, -1)) { + /* Buffer given away */ + packet_buffer = NULL; + goto fail; + } + + section->pi.stream_avail -= len; + payload_written += payload_len; + section->pi.packet_start_unit_indicator = FALSE; + } + + return TRUE; + +fail: + if (packet) + g_free (packet); + if (packet_buffer) + gst_buffer_unref (packet_buffer); + if (section_buffer) + gst_buffer_unref (section_buffer); + return FALSE; +} + +static gboolean +tsmux_write_si (TsMux * mux) +{ + g_hash_table_foreach (mux->si_sections, + (GHFunc) tsmux_section_write_packet, mux); + + mux->si_changed = FALSE; + + return TRUE; + +} + /** * tsmux_write_stream_packet: * @mux: a #TsMux @@ -779,6 +971,7 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream) if (tsmux_stream_is_pcr (stream)) { gint64 cur_pts = tsmux_stream_get_pts (stream); gboolean write_pat; + gboolean write_si; GList *cur; cur_pcr = 0; @@ -822,6 +1015,20 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream) return FALSE; } + /* check if we need to rewrite sit */ + if (mux->last_si_ts == -1 || mux->si_changed) + write_si = TRUE; + else if (cur_pts >= mux->last_si_ts + mux->si_interval) + write_si = TRUE; + else + write_si = FALSE; + + if (write_si) { + mux->last_si_ts = cur_pts; + if (!tsmux_write_si (mux)) + return FALSE; + } + /* check if we need to rewrite any of the current pmts */ for (cur = mux->programs; cur; cur = cur->next) { TsMuxProgram *program = (TsMuxProgram *) cur->data; diff --git a/gst/mpegtsmux/tsmux/tsmux.h b/gst/mpegtsmux/tsmux/tsmux.h index acebcb5d3..93eba33a7 100644 --- a/gst/mpegtsmux/tsmux/tsmux.h +++ b/gst/mpegtsmux/tsmux/tsmux.h @@ -82,6 +82,8 @@ #include <glib.h> +#include <gst/mpegts/mpegts.h> + #include "tsmuxcommon.h" #include "tsmuxstream.h" @@ -104,6 +106,7 @@ typedef void (*TsMuxAllocFunc) (GstBuffer ** buf, void *user_data); struct TsMuxSection { TsMuxPacketInfo pi; + GstMpegTsSection *section; /* Private sections can be up to 4096 bytes */ guint8 data[TSMUX_MAX_SECTION_LENGTH]; @@ -148,6 +151,9 @@ struct TsMux { guint16 next_pmt_pid; guint16 next_stream_pid; + /* Table with TsMuxSection to write */ + GHashTable *si_sections; + TsMuxSection pat; /* PAT transport_stream_id */ guint16 transport_id; @@ -160,6 +166,13 @@ struct TsMux { /* last time PAT written in MPEG PTS clock time */ gint64 last_pat_ts; + /* trigger writing Service Information Tables */ + gboolean si_changed; + /* interval between SIT in MPEG PTS clock time */ + guint si_interval; + /* last time SIT written in MPEG PTS clock time */ + gint64 last_si_ts; + /* callback to write finished packet */ TsMuxWriteFunc write_func; void *write_func_data; @@ -188,6 +201,11 @@ void tsmux_program_free (TsMuxProgram *program); void tsmux_set_pmt_interval (TsMuxProgram *program, guint interval); guint tsmux_get_pmt_interval (TsMuxProgram *program); +/* SI table management */ +void tsmux_set_si_interval (TsMux *mux, guint interval); +guint tsmux_get_si_interval (TsMux *mux); +gboolean tsmux_add_mpegts_si_section (TsMux * mux, GstMpegTsSection * section); + /* stream management */ TsMuxStream * tsmux_create_stream (TsMux *mux, TsMuxStreamType stream_type, guint16 pid, gchar *language); TsMuxStream * tsmux_find_stream (TsMux *mux, guint16 pid); diff --git a/gst/mpegtsmux/tsmux/tsmuxcommon.h b/gst/mpegtsmux/tsmux/tsmuxcommon.h index 1accc184c..6d5e87fd5 100644 --- a/gst/mpegtsmux/tsmux/tsmuxcommon.h +++ b/gst/mpegtsmux/tsmux/tsmuxcommon.h @@ -119,6 +119,8 @@ G_BEGIN_DECLS #define TSMUX_DEFAULT_PAT_INTERVAL (TSMUX_CLOCK_FREQ / 10) /* PMT interval (1/10th sec) */ #define TSMUX_DEFAULT_PMT_INTERVAL (TSMUX_CLOCK_FREQ / 10) +/* SI interval (1/10th sec) */ +#define TSMUX_DEFAULT_SI_INTERVAL (TSMUX_CLOCK_FREQ / 10) typedef struct TsMuxPacketInfo TsMuxPacketInfo; typedef struct TsMuxProgram TsMuxProgram; |