From adb4130e65fab0a34ec9dec7b0484dbf932efc53 Mon Sep 17 00:00:00 2001 From: Lin Yang Date: Tue, 11 Aug 2009 12:00:10 +0200 Subject: [PATCH] mpegpsmux: Add MPEG PS muxer --- configure.ac | 2 + gst/mpegpsmux/Makefile.am | 23 ++ gst/mpegpsmux/bits.h | 95 +++++ gst/mpegpsmux/crc.h | 100 +++++ gst/mpegpsmux/mpegpsmux.c | 702 +++++++++++++++++++++++++++++++++ gst/mpegpsmux/mpegpsmux.h | 120 ++++++ gst/mpegpsmux/psmux.c | 443 +++++++++++++++++++++ gst/mpegpsmux/psmux.h | 114 ++++++ gst/mpegpsmux/psmuxcommon.h | 162 ++++++++ gst/mpegpsmux/psmuxstream.c | 757 ++++++++++++++++++++++++++++++++++++ gst/mpegpsmux/psmuxstream.h | 212 ++++++++++ 11 files changed, 2730 insertions(+) create mode 100644 gst/mpegpsmux/Makefile.am create mode 100644 gst/mpegpsmux/bits.h create mode 100644 gst/mpegpsmux/crc.h create mode 100644 gst/mpegpsmux/mpegpsmux.c create mode 100644 gst/mpegpsmux/mpegpsmux.h create mode 100644 gst/mpegpsmux/psmux.c create mode 100644 gst/mpegpsmux/psmux.h create mode 100644 gst/mpegpsmux/psmuxcommon.h create mode 100644 gst/mpegpsmux/psmuxstream.c create mode 100644 gst/mpegpsmux/psmuxstream.h diff --git a/configure.ac b/configure.ac index e639cd0b78..25556e7d08 100644 --- a/configure.ac +++ b/configure.ac @@ -274,6 +274,7 @@ AG_GST_CHECK_PLUGIN(librfb) AG_GST_CHECK_PLUGIN(liveadder) AG_GST_CHECK_PLUGIN(mpegdemux) AG_GST_CHECK_PLUGIN(mpegtsmux) +AG_GST_CHECK_PLUGIN(mpegpsmux) AG_GST_CHECK_PLUGIN(mpeg4videoparse) AG_GST_CHECK_PLUGIN(mpegvideoparse) AG_GST_CHECK_PLUGIN(mve) @@ -1724,6 +1725,7 @@ gst/liveadder/Makefile gst/mpegdemux/Makefile gst/mpegtsmux/Makefile gst/mpegtsmux/tsmux/Makefile +gst/mpegpsmux/Makefile gst/mpeg4videoparse/Makefile gst/mpegvideoparse/Makefile gst/mve/Makefile diff --git a/gst/mpegpsmux/Makefile.am b/gst/mpegpsmux/Makefile.am new file mode 100644 index 0000000000..221a61230b --- /dev/null +++ b/gst/mpegpsmux/Makefile.am @@ -0,0 +1,23 @@ +plugin_LTLIBRARIES = libgstmpegpsmux.la + +libgstmpegpsmux_la_SOURCES = \ + mpegpsmux.c \ + psmux.c \ + psmuxstream.c \ + mpegpsmux_aac.c \ + mpegpsmux_h264.c + +libgstmpegpsmux_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CLFAGS) +libgstmpegpsmux_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) +libgstmpegpsmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstmpegpsmux_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = \ + mpegpsmux.h \ + psmux.h \ + psmuxstream.h \ + psmuxcommon.h \ + mpegpsmux_aac.h \ + mpegpsmux_h264.h \ + bits.h \ + crc.h diff --git a/gst/mpegpsmux/bits.h b/gst/mpegpsmux/bits.h new file mode 100644 index 0000000000..b2ff746023 --- /dev/null +++ b/gst/mpegpsmux/bits.h @@ -0,0 +1,95 @@ +/***************************************************************************** + * bits.h + ***************************************************************************** + * Copyright (C) 2001, 2002 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * Eric Petit + * + * Copyright (C) 2008 Lin YANG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef __BITS_H__ +#define __BITS_H__ + +#include + +G_BEGIN_DECLS +typedef struct bits_buffer_s +{ + gint i_size; + + gint i_data; + guint8 i_mask; + guint8* p_data; + +} bits_buffer_t; + +static inline gint bits_initwrite( bits_buffer_t *p_buffer, + gint i_size, void *p_data ) +{ + p_buffer->i_size = i_size; + p_buffer->i_data = 0; + p_buffer->i_mask = 0x80; + p_buffer->p_data = p_data; + if( !p_buffer->p_data ) + { + if( !( p_buffer->p_data = g_slice_alloc0( i_size ) ) ) + return -1; + } + p_buffer->p_data[0] = 0; + return 0; +} + +static inline void bits_align( bits_buffer_t *p_buffer ) +{ + if( p_buffer->i_mask != 0x80 && p_buffer->i_data < p_buffer->i_size ) + { + p_buffer->i_mask = 0x80; + p_buffer->i_data++; + p_buffer->p_data[p_buffer->i_data] = 0x00; + } +} + +static inline void bits_write( bits_buffer_t *p_buffer, + gint i_count, guint64 i_bits ) +{ + while( i_count > 0 ) + { + i_count--; + + if( ( i_bits >> i_count )&0x01 ) + { + p_buffer->p_data[p_buffer->i_data] |= p_buffer->i_mask; + } + else + { + p_buffer->p_data[p_buffer->i_data] &= ~p_buffer->i_mask; + } + p_buffer->i_mask >>= 1; + if( p_buffer->i_mask == 0 ) + { + p_buffer->i_data++; + p_buffer->i_mask = 0x80; + } + } +} + +G_END_DECLS + +#endif diff --git a/gst/mpegpsmux/crc.h b/gst/mpegpsmux/crc.h new file mode 100644 index 0000000000..b71dcbeab3 --- /dev/null +++ b/gst/mpegpsmux/crc.h @@ -0,0 +1,100 @@ +/* MPEG-PS muxer plugin for GStreamer + * Copyright 2008 Lin YANG + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +static guint32 crc_tab[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +static guint32 +calc_crc32 (guint8 *data, guint datalen) +{ + guint i; + guint32 crc = 0xffffffff; + + for (i=0; i> 24) ^ *data++) & 0xff]; + } + + return crc; +} diff --git a/gst/mpegpsmux/mpegpsmux.c b/gst/mpegpsmux/mpegpsmux.c new file mode 100644 index 0000000000..da31f28a4f --- /dev/null +++ b/gst/mpegpsmux/mpegpsmux.c @@ -0,0 +1,702 @@ +/* MPEG-PS muxer plugin for GStreamer + * Copyright 2008 Lin YANG + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#include "mpegpsmux.h" +#include "mpegpsmux_aac.h" +#include "mpegpsmux_h264.h" + +GST_DEBUG_CATEGORY (mpegpsmux_debug); +#define GST_CAT_DEFAULT mpegpsmux_debug + +enum +{ + ARG_0 +}; + +static GstStaticPadTemplate mpegpsmux_sink_factory = + GST_STATIC_PAD_TEMPLATE ("sink_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("video/mpeg, " + "mpegversion = (int) { 1, 2, 4 }, " + "systemstream = (boolean) false; " + "video/x-dirac;" + "video/x-h264;" + "audio/mpeg, " + "mpegversion = (int) { 1, 2, 4 };" + "audio/x-lpcm, " + "width = (int) { 16, 20, 24 }, " + "rate = (int) { 48000, 96000 }, " + "channels = (int) [ 1, 8 ], " + "dynamic_range = (int) [ 0, 255 ], " + "emphasis = (boolean) { FALSE, TRUE }, " + "mute = (boolean) { FALSE, TRUE }")); + +static GstStaticPadTemplate mpegpsmux_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpeg, " + "mpegversion = (int) 2, " "systemstream = (boolean) true") + ); + +static void gst_mpegpsmux_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_mpegpsmux_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void mpegpsmux_dispose (GObject * object); +static gboolean new_packet_cb (guint8 * data, guint len, void *user_data); +static void release_buffer_cb (guint8 * data, void *user_data); + +static gboolean mpegpsdemux_prepare_srcpad (MpegPsMux * mux); +static GstFlowReturn mpegpsmux_collected (GstCollectPads * pads, + MpegPsMux * mux); +static GstPad *mpegpsmux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name); +static void mpegpsmux_release_pad (GstElement * element, GstPad * pad); +static GstStateChangeReturn mpegpsmux_change_state (GstElement * element, + GstStateChange transition); + +GST_BOILERPLATE (MpegPsMux, mpegpsmux, GstElement, GST_TYPE_ELEMENT); + +static void +mpegpsmux_base_init (gpointer g_class) +{ + const GstElementDetails mpegpsmux_details = { + "MPEG Program Stream Muxer", + "Codec/Muxer", + "Multiplexes media streams into an MPEG Program Stream", + "Lin YANG " + }; + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&mpegpsmux_sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&mpegpsmux_src_factory)); + + gst_element_class_set_details (element_class, &mpegpsmux_details); +} + +static void +mpegpsmux_class_init (MpegPsMuxClass * klass) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mpegpsmux_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_mpegpsmux_get_property); + gobject_class->dispose = mpegpsmux_dispose; + + gstelement_class->request_new_pad = mpegpsmux_request_new_pad; + gstelement_class->release_pad = mpegpsmux_release_pad; + gstelement_class->change_state = mpegpsmux_change_state; + +} + +static void +mpegpsmux_init (MpegPsMux * mux, MpegPsMuxClass * g_class) +{ + mux->srcpad = + gst_pad_new_from_template (gst_static_pad_template_get + (&mpegpsmux_src_factory), "src"); + gst_pad_use_fixed_caps (mux->srcpad); + gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad); + + mux->collect = gst_collect_pads_new (); + gst_collect_pads_set_function (mux->collect, + (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (mpegpsmux_collected), mux); + + mux->psmux = psmux_new (); + psmux_set_write_func (mux->psmux, new_packet_cb, mux); + + mux->first = TRUE; + mux->last_flow_ret = GST_FLOW_OK; + mux->last_ts = 0; /* XXX: or -1? */ +} + +static void +mpegpsmux_dispose (GObject * object) +{ + MpegPsMux *mux = GST_MPEG_PSMUX (object); + + if (mux->collect) { + gst_object_unref (mux->collect); + mux->collect = NULL; + } + if (mux->psmux) { + psmux_free (mux->psmux); + mux->psmux = NULL; + } + + GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); +} + +static void +gst_mpegpsmux_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ +/* MpegPsMux *mux = GST_MPEG_PSMUX (object); */ + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mpegpsmux_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ +/* MpegPsMux *mux = GST_MPEG_PSMUX (object); */ + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +release_buffer_cb (guint8 * data, void *user_data) +{ + /* release a given buffer. callback func */ + + GstBuffer *buf = (GstBuffer *) user_data; + gst_buffer_unref (buf); +} + +static GstFlowReturn +mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad) +{ + /* Create a steam. Fill in codec specific information */ + + GstFlowReturn ret = GST_FLOW_ERROR; + GstCaps *caps = gst_pad_get_negotiated_caps (pad); + GstStructure *s; + + if (caps == NULL) { + GST_DEBUG_OBJECT (pad, "Sink pad caps were not set before pushing"); + return GST_FLOW_NOT_NEGOTIATED; + } + + s = gst_caps_get_structure (caps, 0); + g_return_val_if_fail (s != NULL, FALSE); + + if (gst_structure_has_name (s, "video/x-dirac")) { + GST_DEBUG_OBJECT (pad, "Creating Dirac stream"); + ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_DIRAC); + } else if (gst_structure_has_name (s, "audio/x-ac3")) { + GST_DEBUG_OBJECT (pad, "Creating AC3 stream"); + ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_PS_AUDIO_AC3); + } else if (gst_structure_has_name (s, "audio/x-dts")) { + GST_DEBUG_OBJECT (pad, "Creating DTS stream"); + ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_PS_AUDIO_DTS); + } else if (gst_structure_has_name (s, "audio/x-lpcm")) { + GST_DEBUG_OBJECT (pad, "Creating LPCM stream"); + ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_PS_AUDIO_LPCM); + } else if (gst_structure_has_name (s, "video/x-h264")) { + const GValue *value; + GST_DEBUG_OBJECT (pad, "Creating H264 stream"); + /* Codec data contains SPS/PPS which need to go in stream for valid ES */ + value = gst_structure_get_value (s, "codec_data"); + if (value) { + ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value)); + GST_DEBUG_OBJECT (pad, "we have additional codec data (%d bytes)", + GST_BUFFER_SIZE (ps_data->codec_data)); + ps_data->prepare_func = mpegpsmux_prepare_h264; + } else { + ps_data->codec_data = NULL; + } + ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_H264); + } else if (gst_structure_has_name (s, "audio/mpeg")) { + gint mpegversion; + if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) { + GST_ELEMENT_ERROR (pad, STREAM, FORMAT, + ("Invalid data format presented"), + ("Caps with type audio/mpeg did not have mpegversion")); + goto beach; + } + + switch (mpegversion) { + case 1: + GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 1 stream"); + ps_data->stream = + psmux_create_stream (mux->psmux, PSMUX_ST_AUDIO_MPEG1); + break; + case 2: + GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 2 stream"); + ps_data->stream = + psmux_create_stream (mux->psmux, PSMUX_ST_AUDIO_MPEG2); + break; + case 4: + { + const GValue *value; + /* Codec data contains SPS/PPS which need to go in stream for valid ES */ + GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 4 stream"); + value = gst_structure_get_value (s, "codec_data"); + if (value) { + ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value)); + GST_DEBUG_OBJECT (pad, "we have additional codec data (%d bytes)", + GST_BUFFER_SIZE (ps_data->codec_data)); + ps_data->prepare_func = mpegpsmux_prepare_aac; + } else { + ps_data->codec_data = NULL; + } + ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_AUDIO_AAC); + break; + } + default: + GST_WARNING_OBJECT (pad, "unsupported mpegversion %d", mpegversion); + goto beach; + } + } else if (gst_structure_has_name (s, "video/mpeg")) { + gint mpegversion; + if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) { + GST_ELEMENT_ERROR (mux, STREAM, FORMAT, + ("Invalid data format presented"), + ("Caps with type video/mpeg did not have mpegversion")); + goto beach; + } + + if (mpegversion == 1) { + GST_DEBUG_OBJECT (pad, "Creating MPEG Video, version 1 stream"); + ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_MPEG1); + } else if (mpegversion == 2) { + GST_DEBUG_OBJECT (pad, "Creating MPEG Video, version 2 stream"); + ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_MPEG2); + } else { + GST_DEBUG_OBJECT (pad, "Creating MPEG Video, version 4 stream"); + ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_MPEG4); + } + } + + if (ps_data->stream != NULL) { + ps_data->stream_id = ps_data->stream->stream_id; + ps_data->stream_id_ext = ps_data->stream->stream_id_ext; + GST_DEBUG_OBJECT (pad, "Stream created, stream_id=%04x, stream_id_ext=%04x", + ps_data->stream_id, ps_data->stream_id_ext); + + gst_structure_get_int (s, "rate", &ps_data->stream->audio_sampling); + gst_structure_get_int (s, "channels", &ps_data->stream->audio_channels); + gst_structure_get_int (s, "bitrate", &ps_data->stream->audio_bitrate); + + psmux_stream_set_buffer_release_func (ps_data->stream, release_buffer_cb); + + ret = GST_FLOW_OK; + } + +beach: + return ret; +} + +static GstFlowReturn +mpegpsmux_create_streams (MpegPsMux * mux) +{ + /* Create stream for each pad */ + + GstFlowReturn ret = GST_FLOW_OK; + GSList *walk = mux->collect->data; + + /* Create the streams */ + while (walk) { + GstCollectData *c_data = (GstCollectData *) walk->data; + MpegPsPadData *ps_data = (MpegPsPadData *) walk->data; + + walk = g_slist_next (walk); + + if (ps_data->stream == NULL) { + ret = mpegpsmux_create_stream (mux, ps_data, c_data->pad); + if (ret != GST_FLOW_OK) + goto no_stream; + } + } + + return GST_FLOW_OK; +no_stream: + GST_ELEMENT_ERROR (mux, STREAM, MUX, + ("Could not create handler for stream"), (NULL)); + return ret; +} + +static MpegPsPadData * +mpegpsmux_choose_best_stream (MpegPsMux * mux) +{ + /* Choose from which stream to mux with */ + + MpegPsPadData *best = NULL; + GstCollectData *c_best = NULL; + GSList *walk; + + for (walk = mux->collect->data; walk != NULL; walk = g_slist_next (walk)) { + GstCollectData *c_data = (GstCollectData *) walk->data; + MpegPsPadData *ps_data = (MpegPsPadData *) walk->data; + + if (ps_data->eos == FALSE) { + if (ps_data->queued_buf == NULL) { + GstBuffer *buf; + + ps_data->queued_buf = buf = + gst_collect_pads_peek (mux->collect, c_data); + + if (buf != NULL) { + if (ps_data->prepare_func) { + buf = ps_data->prepare_func (buf, ps_data, mux); + if (buf) { /* Take the prepared buffer instead */ + gst_buffer_unref (ps_data->queued_buf); + ps_data->queued_buf = buf; + } else { /* If data preparation returned NULL, use unprepared one */ + buf = ps_data->queued_buf; + } + } + if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) { + /* Ignore timestamps that go backward for now. FIXME: Handle all + * incoming PTS */ + if (ps_data->last_ts == GST_CLOCK_TIME_NONE || + ps_data->last_ts < GST_BUFFER_TIMESTAMP (buf)) { + ps_data->cur_ts = ps_data->last_ts = + gst_segment_to_running_time (&c_data->segment, + GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf)); + } else { + GST_DEBUG_OBJECT (mux, "Ignoring PTS that has gone backward"); + } + } else + ps_data->cur_ts = GST_CLOCK_TIME_NONE; + + GST_DEBUG_OBJECT (mux, "Pulled buffer with ts %" GST_TIME_FORMAT + " (uncorrected ts %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT + ") for PID 0x%04x", + GST_TIME_ARGS (ps_data->cur_ts), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), + GST_BUFFER_TIMESTAMP (buf), ps_data->stream_id); + + /* Choose a stream we've never seen a timestamp for to ensure + * we push enough buffers from it to reach a timestamp */ + if (ps_data->last_ts == GST_CLOCK_TIME_NONE) { + best = ps_data; + c_best = c_data; + } + } else { + ps_data->eos = TRUE; + continue; + } + } + + /* If we don't yet have a best pad, take this one, otherwise take + * whichever has the oldest timestamp */ + if (best != NULL) { + if (ps_data->last_ts != GST_CLOCK_TIME_NONE && + best->last_ts != GST_CLOCK_TIME_NONE && + ps_data->last_ts < best->last_ts) { + best = ps_data; + c_best = c_data; + } + } else { + best = ps_data; + c_best = c_data; + } + } + } + if (c_best) { + gst_buffer_unref (gst_collect_pads_pop (mux->collect, c_best)); + } + + return best; +} + +static GstFlowReturn +mpegpsmux_collected (GstCollectPads * pads, MpegPsMux * mux) +{ + /* main muxing function */ + + GstFlowReturn ret = GST_FLOW_OK; + MpegPsPadData *best = NULL; + + GST_DEBUG_OBJECT (mux, "Pads collected"); + + if (mux->first) { /* process block for the first mux */ + /* iterate through the collect pads and add streams to @mux */ + ret = mpegpsmux_create_streams (mux); + /* Assumption : all pads are already added at this time */ + + if (G_UNLIKELY (ret != GST_FLOW_OK)) + return ret; + + best = mpegpsmux_choose_best_stream (mux); + + /* prepare the src pad (output), return if failed */ + if (!mpegpsdemux_prepare_srcpad (mux)) { + GST_DEBUG_OBJECT (mux, "Failed to send new segment"); + goto new_seg_fail; + } + + mux->first = FALSE; + } else { + best = mpegpsmux_choose_best_stream (mux); + } + + if (best != NULL) { + /* @*buf : the buffer to be processed */ + GstBuffer *buf = best->queued_buf; + GstCollectData *c_data = (GstCollectData *) best; + gint64 pts = -1; + + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + + GST_DEBUG_OBJECT (mux, + "Chose stream from pad %" GST_PTR_FORMAT " for output (PID: 0x%04x)", + c_data->pad, best->stream_id); + + /* set timestamp */ + if (GST_CLOCK_TIME_IS_VALID (best->cur_ts)) { + pts = GSTTIME_TO_MPEGTIME (best->cur_ts); /* @pts: current timestamp */ + GST_DEBUG_OBJECT (mux, "Buffer has TS %" GST_TIME_FORMAT " pts %" + G_GINT64_FORMAT, GST_TIME_ARGS (best->cur_ts), pts); + } + + /* give the buffer to libpsmux for processing */ + psmux_stream_add_data (best->stream, GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf), buf, pts, -1); + best->queued_buf = NULL; + + /* write the data from libpsmux to stream */ + while (psmux_stream_bytes_in_buffer (best->stream) > 0) { + GST_LOG_OBJECT (mux, "Before @psmux_write_stream_packet"); + if (!psmux_write_stream_packet (mux->psmux, best->stream)) { + GST_DEBUG_OBJECT (mux, "Failed to write data packet"); + goto write_fail; + } + } + mux->last_ts = best->last_ts; + } else { + /* FIXME: Drain all remaining streams */ + /* At EOS */ + if (psmux_write_end_code (mux->psmux)) { + GST_WARNING_OBJECT (mux, "Writing MPEG PS Program end code failed."); + } + gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); + } + + return ret; +new_seg_fail: + return GST_FLOW_ERROR; +write_fail: + /* FIXME: Failed writing data for some reason. Should set appropriate error */ + return mux->last_flow_ret; +} + +static GstPad * +mpegpsmux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name) +{ + + MpegPsMux *mux = GST_MPEG_PSMUX (element); + GstPad *pad = NULL; + MpegPsPadData *pad_data = NULL; + + pad = gst_pad_new_from_template (templ, name); + + pad_data = (MpegPsPadData *) gst_collect_pads_add_pad (mux->collect, pad, + sizeof (MpegPsPadData)); + if (pad_data == NULL) + goto pad_failure; + + pad_data->last_ts = GST_CLOCK_TIME_NONE; + pad_data->codec_data = NULL; + pad_data->prepare_func = NULL; + + if (G_UNLIKELY (!gst_element_add_pad (element, pad))) + goto could_not_add; + + return pad; + +could_not_add: + GST_ELEMENT_ERROR (element, STREAM, FAILED, + ("Internal data stream error."), ("Could not add pad to element")); + gst_collect_pads_remove_pad (mux->collect, pad); + gst_object_unref (pad); + return NULL; +pad_failure: + GST_ELEMENT_ERROR (element, STREAM, FAILED, + ("Internal data stream error."), ("Could not add pad to collectpads")); + gst_object_unref (pad); + return NULL; +} + +static void +mpegpsmux_release_pad (GstElement * element, GstPad * pad) +{ + /* unref pad data (and codec data) */ + + MpegPsMux *mux = GST_MPEG_PSMUX (element); + MpegPsPadData *pad_data = NULL; + + GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " being released", pad); + + /* Get the MpegPsPadData out of the pad */ + GST_OBJECT_LOCK (pad); + pad_data = (MpegPsPadData *) gst_pad_get_element_private (pad); + if (G_LIKELY (pad_data)) { + /* Free codec data reference if any */ + if (pad_data->codec_data) { + GST_DEBUG_OBJECT (element, "releasing codec_data reference"); + gst_buffer_unref (pad_data->codec_data); + pad_data->codec_data = NULL; + } + } + GST_OBJECT_UNLOCK (pad); + + gst_collect_pads_remove_pad (mux->collect, pad); +} + +static gboolean +new_packet_cb (guint8 * data, guint len, void *user_data) +{ + /* Called when the PsMux has prepared a packet for output. Return FALSE + * on error */ + + MpegPsMux *mux = (MpegPsMux *) user_data; + GstBuffer *buf; + GstFlowReturn ret; + + GST_LOG_OBJECT (mux, "Outputting a packet of length %d", len); + buf = gst_buffer_new_and_alloc (len); + if (G_UNLIKELY (buf == NULL)) { + mux->last_flow_ret = GST_FLOW_ERROR; + return FALSE; + } + gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad)); + + memcpy (GST_BUFFER_DATA (buf), data, len); + GST_BUFFER_TIMESTAMP (buf) = mux->last_ts; + ret = gst_pad_push (mux->srcpad, buf); + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + mux->last_flow_ret = ret; + return FALSE; + } + + return TRUE; +} + +static gboolean +mpegpsdemux_prepare_srcpad (MpegPsMux * mux) +{ + /* prepare the source pad for output */ + + GstEvent *new_seg = + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0); + GstCaps *caps = gst_caps_new_simple ("video/mpeg", + "mpegversion", G_TYPE_INT, 2, + "systemstream", G_TYPE_BOOLEAN, TRUE, + NULL); + +/* gst_static_pad_template_get_caps (&mpegpsmux_src_factory); */ + + /* Set caps on src pad from our template and push new segment */ + gst_pad_set_caps (mux->srcpad, caps); + + if (!gst_pad_push_event (mux->srcpad, new_seg)) { + GST_WARNING_OBJECT (mux, "New segment event was not handled"); + return FALSE; + } + + return TRUE; +} + +static GstStateChangeReturn +mpegpsmux_change_state (GstElement * element, GstStateChange transition) +{ + /* control the collect pads */ + + MpegPsMux *mux = GST_MPEG_PSMUX (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_collect_pads_start (mux->collect); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_collect_pads_stop (mux->collect); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + default: + break; + } + + return ret; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "mpegpsmux", GST_RANK_PRIMARY, + mpegpsmux_get_type ())) + return FALSE; + + GST_DEBUG_CATEGORY_INIT (mpegpsmux_debug, "mpegpsmux", 0, + "MPEG Program Stream muxer"); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, + "mpegpsmux", "MPEG-PS muxer", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/mpegpsmux/mpegpsmux.h b/gst/mpegpsmux/mpegpsmux.h new file mode 100644 index 0000000000..4da1bea66d --- /dev/null +++ b/gst/mpegpsmux/mpegpsmux.h @@ -0,0 +1,120 @@ +/* MPEG-PS muxer plugin for GStreamer + * Copyright 2008 Lin YANG + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + + +#ifndef __MPEGPSMUX_H__ +#define __MPEGPSMUX_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#include "psmux.h" + +#define GST_TYPE_MPEG_PSMUX (mpegpsmux_get_type()) +#define GST_MPEG_PSMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_MPEG_PSMUX, MpegPsMux)) + +typedef struct MpegPsMux MpegPsMux; +typedef struct MpegPsMuxClass MpegPsMuxClass; +typedef struct MpegPsPadData MpegPsPadData; + +typedef GstBuffer * (*MpegPsPadDataPrepareFunction) (GstBuffer * buf, + MpegPsPadData * data, MpegPsMux * mux); + +struct MpegPsMux { + GstElement parent; + + GstPad *srcpad; + + GstCollectPads *collect; // pads collector + + PsMux *psmux; + + gboolean first; + GstFlowReturn last_flow_ret; + + GstClockTime last_ts; +}; + +struct MpegPsMuxClass { + GstElementClass parent_class; +}; + +struct MpegPsPadData { + GstCollectData collect; /* Parent */ + + guint8 stream_id; + guint8 stream_id_ext; + PsMuxStream *stream; + + GstBuffer *queued_buf; /* Currently pulled buffer */ + GstClockTime cur_ts; /* Adjusted TS for the pulled buffer */ + GstClockTime last_ts; /* Most recent valid TS for this stream */ + + GstBuffer * codec_data; /* Optional codec data available in the caps */ + + MpegPsPadDataPrepareFunction prepare_func; /* Handler to prepare input data */ + + gboolean eos; +}; + +GType mpegpsmux_get_type (void); + +#define CLOCK_BASE 9LL +#define CLOCK_FREQ (CLOCK_BASE * 10000) + +#define MPEGTIME_TO_GSTTIME(time) (gst_util_uint64_scale ((time), \ + GST_MSECOND/10, CLOCK_BASE)) +#define GSTTIME_TO_MPEGTIME(time) (gst_util_uint64_scale ((time), \ + CLOCK_BASE, GST_MSECOND/10)) + +#define NORMAL_TS_PACKET_LENGTH 188 +#define M2TS_PACKET_LENGTH 192 +#define STANDARD_TIME_CLOCK 27000000 +/*33 bits as 1 ie 0x1ffffffff*/ +#define TWO_POW_33_MINUS1 ((0xffffffff * 2) - 1) +G_END_DECLS + +#endif diff --git a/gst/mpegpsmux/psmux.c b/gst/mpegpsmux/psmux.c new file mode 100644 index 0000000000..a4d7b2c1b5 --- /dev/null +++ b/gst/mpegpsmux/psmux.c @@ -0,0 +1,443 @@ +/* MPEG-PS muxer plugin for GStreamer + * Copyright 2008 Lin YANG + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "mpegpsmux.h" +#include "psmuxcommon.h" +#include "psmuxstream.h" +#include "psmux.h" +#include "crc.h" + +static gboolean psmux_packet_out (PsMux * mux); +static gboolean psmux_write_pack_header (PsMux * mux); +static gboolean psmux_write_system_header (PsMux * mux); +static gboolean psmux_write_program_stream_map (PsMux * mux); + +/** + * psmux_new: + * + * Create a new muxer session. + * + * Returns: A new #PsMux object. + */ +PsMux * +psmux_new () +{ + PsMux *mux; + + mux = g_slice_new0 (PsMux); + + mux->pts = -1; /* uninitialized values */ + mux->pack_hdr_pts = -1; + mux->sys_hdr_pts = -1; + mux->psm_pts = -1; + + mux->bit_pts = 0; + + mux->pes_max_payload = PSMUX_PES_MAX_PAYLOAD; + mux->bit_rate = 400 * 1024; /* XXX: better default values? */ + mux->rate_bound = 2 * 1024; /* 2* bit_rate / (8*50). XXX: any better default? */ + + mux->pack_hdr_freq = PSMUX_PACK_HDR_FREQ; + mux->sys_hdr_freq = PSMUX_SYS_HDR_FREQ; + mux->psm_freq = PSMUX_PSM_FREQ; + + psmux_stream_id_info_init (&mux->id_info); + + return mux; +} + +/** + * psmux_set_write_func: + * @mux: a #PsMux + * @func: a user callback function + * @user_data: user data passed to @func + * + * Set the callback function and user data to be called when @mux has output to + * produce. @user_data will be passed as user data in @func. + */ +void +psmux_set_write_func (PsMux * mux, PsMuxWriteFunc func, void *user_data) +{ + g_return_if_fail (mux != NULL); + + mux->write_func = func; + mux->write_func_data = user_data; +} + +gboolean +psmux_write_end_code (PsMux * mux) +{ + guint8 end_code[4] = { 0, 0, 1, PSMUX_PROGRAM_END }; + return mux->write_func (end_code, 4, mux->write_func_data); +} + + +/** + * psmux_free: + * @mux: a #PsMux + * + * Free all resources associated with @mux. After calling this function @mux can + * not be used anymore. + */ +void +psmux_free (PsMux * mux) +{ + GList *cur; + + g_return_if_fail (mux != NULL); + + /* Free all streams */ + for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) { + PsMuxStream *stream = (PsMuxStream *) cur->data; + + psmux_stream_free (stream); + } + g_list_free (mux->streams); + + g_slice_free (PsMux, mux); +} + +/** + * psmux_create_stream: + * @mux: a #PsMux + * @stream_type: a #PsMuxStreamType + * + * Create a new stream of @stream_type in the muxer session @mux. + * + * Returns: a new #PsMuxStream. + */ +PsMuxStream * +psmux_create_stream (PsMux * mux, PsMuxStreamType stream_type) +{ + PsMuxStream *stream; +// guint16 new_pid; + + g_return_val_if_fail (mux != NULL, NULL); + +#if 0 + if (pid == PSMUX_PID_AUTO) { + new_pid = psmux_get_new_pid (mux); + } else { + new_pid = pid & 0x1FFF; + } + + /* Ensure we're not creating a PID collision */ + if (psmux_find_stream (mux, new_pid)) + return NULL; +#endif + + stream = psmux_stream_new (mux, stream_type); + + mux->streams = g_list_prepend (mux->streams, stream); + if (stream->stream_id_ext) { + if (!mux->nb_private_streams) + mux->nb_streams++; + mux->nb_private_streams++; + } else + mux->nb_streams++; + + if (stream->is_video_stream) { + mux->video_bound++; + if (mux->video_bound > 32) + g_critical ("Number of video es exceeds upper limit"); + } else if (stream->is_audio_stream) { + mux->audio_bound++; + if (mux->audio_bound > 64) + g_critical ("Number of audio es exceeds upper limit"); + } + + return stream; +} + +static gboolean +psmux_packet_out (PsMux * mux) +{ + gboolean res; + if (G_UNLIKELY (mux->write_func == NULL)) + return TRUE; + + res = mux->write_func (mux->packet_buf, mux->packet_bytes_written, + mux->write_func_data); + + if (res) { + mux->bit_size += mux->packet_bytes_written; + } + mux->packet_bytes_written = 0; + return res; +} + +/** + * psmux_write_stream_packet: + * @mux: a #PsMux + * @stream: a #PsMuxStream + * + * Write a packet of @stream. + * + * Returns: TRUE if the packet could be written. + */ +gboolean +psmux_write_stream_packet (PsMux * mux, PsMuxStream * stream) +{ + gboolean res; + + g_return_val_if_fail (mux != NULL, FALSE); + g_return_val_if_fail (stream != NULL, FALSE); + + + { + guint64 ts = psmux_stream_get_pts (stream); + if (ts != -1) + mux->pts = ts; + } + + if (mux->pts - mux->pack_hdr_pts > PSMUX_PACK_HDR_INTERVAL + || mux->pes_cnt % mux->pack_hdr_freq == 0) { + /* Time to write pack header */ + /* FIXME: currently we write the mux rate of the PREVIOUS pack into the + * pack header, because of the incapability to calculate the mux_rate + * before outputing the pack. To calculate the mux_rate for the current + * pack, we need to put the whole pack into buffer, calculate the + * mux_rate, and then output the whole trunck. + */ + if (mux->pts != -1 && mux->pts > mux->bit_pts + && mux->pts - mux->bit_pts > PSMUX_BITRATE_CALC_INTERVAL) { + /* XXX: smoothing the rate? */ + mux->bit_rate = + gst_util_uint64_scale (mux->bit_size, 8 * CLOCKBASE, + (mux->pts - mux->bit_pts)); + + mux->bit_size = 0; + mux->bit_pts = mux->pts; + } + + psmux_write_pack_header (mux); + mux->pack_hdr_pts = mux->pts; + } + + if (mux->pes_cnt % mux->sys_hdr_freq == 0) { + /* Time to write system header */ + psmux_write_system_header (mux); + mux->sys_hdr_pts = mux->pts; + } + + if (mux->pes_cnt % mux->psm_freq == 0) { + /* Time to write program stream map (PSM) */ + psmux_write_program_stream_map (mux); + mux->psm_pts = mux->pts; + } + + /* Write the packet */ + if (!(mux->packet_bytes_written = + psmux_stream_get_data (stream, mux->packet_buf, + mux->pes_max_payload + PSMUX_PES_MAX_HDR_LEN))) { + return FALSE; + } + + res = psmux_packet_out (mux); + if (!res) { + PS_DEBUG ("packet write false"); + return FALSE; + } + + mux->pes_cnt += 1; + + return res; +} + +static gboolean +psmux_write_pack_header (PsMux * mux) +{ + bits_buffer_t bw; + guint64 scr = mux->pts; /* XXX: is this correct? necessary to put any offset? */ + if (mux->pts == -1) + scr = 0; + + /* pack_start_code */ + bits_initwrite (&bw, 14, mux->packet_buf); + bits_write (&bw, 24, PSMUX_START_CODE_PREFIX); + bits_write (&bw, 8, PSMUX_PACK_HEADER); + + /* scr */ + bits_write (&bw, 2, 0x1); + bits_write (&bw, 3, (scr >> 30) & 0x07); + bits_write (&bw, 1, 1); + bits_write (&bw, 15, (scr >> 15) & 0x7fff); + bits_write (&bw, 1, 1); + bits_write (&bw, 15, scr & 0x7fff); + bits_write (&bw, 1, 1); + bits_write (&bw, 9, 0); /* system_clock_reference_extension: set to 0 (like what VLC does) */ + bits_write (&bw, 1, 1); + + { + /* Scale to get the mux_rate, rounding up */ + guint mux_rate = + gst_util_uint64_scale (mux->bit_rate + 8 * 50 - 1, 1, 8 * 50); + if (mux_rate > mux->rate_bound / 2) + mux->rate_bound = mux_rate * 2; + bits_write (&bw, 22, mux_rate); /* program_mux_rate */ + bits_write (&bw, 2, 3); + } + + bits_write (&bw, 5, 0x1f); + bits_write (&bw, 3, 0); /* pack_stuffing_length */ + + mux->packet_bytes_written = 14; + return psmux_packet_out (mux); +} + +static gboolean +psmux_write_system_header (PsMux * mux) +{ + bits_buffer_t bw; + guint len = 12 + (mux->nb_streams + + (mux->nb_private_streams > 1 ? mux->nb_private_streams - 1 : 0)) * 3; + GList *cur; + gboolean private_hit = FALSE; + + /* system_header_start_code */ + bits_initwrite (&bw, len, mux->packet_buf); + + /* system_header start code */ + bits_write (&bw, 24, PSMUX_START_CODE_PREFIX); + bits_write (&bw, 8, PSMUX_SYSTEM_HEADER); + + bits_write (&bw, 16, len); /* header_length */ + bits_write (&bw, 1, 1); /* marker */ + bits_write (&bw, 22, mux->rate_bound); /* rate_bound */ + bits_write (&bw, 1, 1); /* marker */ + bits_write (&bw, 6, mux->audio_bound); /* audio_bound */ + bits_write (&bw, 1, 0); /* fixed_flag */ + bits_write (&bw, 1, 0); /* CSPS_flag */ + bits_write (&bw, 1, 0); /* system_audio_lock_flag */ + bits_write (&bw, 1, 0); /* system_video_lock_flag */ + bits_write (&bw, 1, 1); /* marker */ + bits_write (&bw, 5, mux->video_bound); /* video_bound */ + bits_write (&bw, 1, 0); /* packet_rate_restriction_flag */ + bits_write (&bw, 7, 0x7f); /* reserved_bits */ + + for (cur = g_list_first (mux->streams), private_hit = FALSE; cur != NULL; + cur = g_list_next (cur)) { + PsMuxStream *stream = (PsMuxStream *) cur->data; + + if (private_hit && stream->stream_id == PSMUX_EXTENDED_STREAM) + continue; + + bits_write (&bw, 8, stream->stream_id); /* stream_id */ + bits_write (&bw, 2, 0x3); /* reserved */ + bits_write (&bw, 1, stream->is_video_stream); /* buffer_bound_scale */ + bits_write (&bw, 13, stream->max_buffer_size / (stream->is_video_stream ? 1024 : 128)); /* buffer_size_bound */ + + if (stream->stream_id == PSMUX_EXTENDED_STREAM) + private_hit = TRUE; + } + + mux->packet_bytes_written = len; + return psmux_packet_out (mux); +} + +static gboolean +psmux_write_program_stream_map (PsMux * mux) +{ + gint psm_size = 16, es_map_size = 0; + bits_buffer_t bw; + GList *cur; + guint16 len; + guint8 *pos; + + /* pre-write the descriptor loop */ + pos = mux->es_info_buf; + for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) { + PsMuxStream *stream = (PsMuxStream *) cur->data; + len = 0; + + *pos++ = stream->stream_type; + *pos++ = stream->stream_id; + + psmux_stream_get_es_descrs (stream, pos + 2, &len); + psmux_put16 (&pos, len); + + es_map_size += len + 4; + pos += len; +#if 0 + if (stream->lang[0] != 0) + es_map_size += 6; +#endif + } + + psm_size += es_map_size; + bits_initwrite (&bw, psm_size, mux->packet_buf); + + /* psm start code */ + bits_write (&bw, 24, PSMUX_START_CODE_PREFIX); + bits_write (&bw, 8, PSMUX_PROGRAM_STREAM_MAP); + + bits_write (&bw, 16, psm_size - 6); /* psm_length */ + bits_write (&bw, 1, 1); /* current_next_indicator */ + bits_write (&bw, 2, 0xF); /* reserved */ + bits_write (&bw, 5, 0x1); /* psm_version = 1 */ + bits_write (&bw, 7, 0xFF); /* reserved */ + bits_write (&bw, 1, 1); /* marker */ + + bits_write (&bw, 16, 0); /* program_stream_info_length */ + /* program_stream_info empty */ + + bits_write (&bw, 16, es_map_size); /* elementary_stream_map_length */ + memcpy (bw.p_data + bw.i_data, mux->es_info_buf, es_map_size); + + /* CRC32 */ + { + guint32 crc = calc_crc32 (mux->packet_buf, psm_size - 4); + guint8 *pos = mux->packet_buf + psm_size - 4; + psmux_put32 (&pos, crc); + } + + mux->packet_bytes_written = psm_size; + return psmux_packet_out (mux); +} diff --git a/gst/mpegpsmux/psmux.h b/gst/mpegpsmux/psmux.h new file mode 100644 index 0000000000..5aacf94cec --- /dev/null +++ b/gst/mpegpsmux/psmux.h @@ -0,0 +1,114 @@ +/* MPEG-PS muxer plugin for GStreamer + * Copyright 2008 Lin YANG + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + + +#ifndef __PSMUX_H__ +#define __PSMUX_H__ + +#include + +#include "psmuxcommon.h" +#include "psmuxstream.h" + +G_BEGIN_DECLS + +#define PSMUX_MAX_ES_INFO_LENGTH ((1 << 12) - 1) + +typedef gboolean (*PsMuxWriteFunc) (guint8 *data, guint len, void *user_data); + +struct PsMux { + GList *streams; /* PsMuxStream* array of all streams */ + guint nb_streams; + guint nb_private_streams; + PsMuxStreamIdInfo id_info; /* carrying the info which ids are used */ + + /* timestamps: pts */ + GstClockTime pts; + + guint32 pes_cnt; /* # of pes that has been created */ + guint16 pes_max_payload; /* maximum payload size in pes packets */ + + guint64 bit_size; /* accumulated bit size of processed data */ + guint bit_rate; /* bit rate */ + GstClockTime bit_pts; /* last time the bit_rate is updated */ + + guint pack_hdr_freq; /* PS pack header frequency */ + GstClockTime pack_hdr_pts; /* last time a pack header is written */ + + guint sys_hdr_freq; /* system header frequency */ + GstClockTime sys_hdr_pts; /* last time a system header is written */ + + guint psm_freq; /* program stream map frequency */ + GstClockTime psm_pts; /* last time a psm is written */ + + guint8 packet_buf[PSMUX_MAX_PACKET_LEN]; + guint packet_bytes_written; /* # of bytes written in the buf */ + PsMuxWriteFunc write_func; + void *write_func_data; + + /* Scratch space for writing ES_info descriptors */ + guint8 es_info_buf[PSMUX_MAX_ES_INFO_LENGTH]; + + /* bounds in system header */ + guint8 audio_bound; + guint8 video_bound; + guint32 rate_bound; +}; + +/* create/free new muxer session */ +PsMux * psmux_new (void); +void psmux_free (PsMux *mux); + +/* Setting muxing session properties */ +void psmux_set_write_func (PsMux *mux, PsMuxWriteFunc func, void *user_data); + +/* stream management */ +PsMuxStream * psmux_create_stream (PsMux *mux, PsMuxStreamType stream_type); + +/* writing stuff */ +gboolean psmux_write_stream_packet (PsMux *mux, PsMuxStream *stream); +gboolean psmux_write_end_code (PsMux *mux); + +G_END_DECLS + +#endif diff --git a/gst/mpegpsmux/psmuxcommon.h b/gst/mpegpsmux/psmuxcommon.h new file mode 100644 index 0000000000..5c7cecb342 --- /dev/null +++ b/gst/mpegpsmux/psmuxcommon.h @@ -0,0 +1,162 @@ +/* MPEG-PS muxer plugin for GStreamer + * Copyright 2008 Lin YANG + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + + +#ifndef __PSMUX_COMMON_H__ +#define __PSMUX_COMMON_H__ + +#include +#include +#include "bits.h" /* from VLC */ + +#undef PS_DEBUG_ON + +G_BEGIN_DECLS + +#define PSMUX_PACK_HDR_FREQ 30 +#define PSMUX_SYS_HDR_FREQ 300 +#define PSMUX_PSM_FREQ 300 + +#define PSMUX_PES_MAX_PAYLOAD 65500 /* from VLC */ +#define PSMUX_PES_MAX_HDR_LEN 30 +#define PSMUX_MAX_PACKET_LEN (PSMUX_PES_MAX_PAYLOAD + PSMUX_PES_MAX_HDR_LEN) + +#define CLOCKBASE 90000 +#define PSMUX_PACK_HDR_INTERVAL ( 0.7 * CLOCKBASE) /* interval to update pack header. 0.7 sec */ +#define PSMUX_BITRATE_CALC_INTERVAL CLOCKBASE /* interval to update bitrate in pack header. 1 sec */ + +#define PSMUX_PES_BITRATE_DEFAULT 1000 /* Default bit_rate to write in the first pack header */ + +#define PSMUX_START_CODE_PREFIX 0x01 + +/* stream_id */ +#define PSMUX_PACK_HEADER 0xba +#define PSMUX_SYSTEM_HEADER 0xbb +#define PSMUX_PROGRAM_STREAM_MAP 0xbc +#define PSMUX_PRIVATE_STREAM_1 0xbd +#define PSMUX_PADDING_STREAM 0xbe +#define PSMUX_PRIVATE_STREAM_2 0xbf +#define PSMUX_ECM 0xb0 +#define PSMUX_EMM 0xb1 +#define PSMUX_PROGRAM_STREAM_DIRECTORY 0xff +#define PSMUX_DSMCC_STREAM 0xf2 +#define PSMUX_ITU_T_H222_1_TYPE_E 0xf8 +#define PSMUX_EXTENDED_STREAM 0xfd +#define PSMUX_PROGRAM_END 0xb9 + +#define PSMUX_MIN_ES_DESC_LEN 8 + +/* Frequency for PCR representation */ +#define PSMUX_SYS_CLOCK_FREQ (27000000L) +/* Frequency for PTS values */ +#define PSMUX_CLOCK_FREQ (PSMUX_SYS_CLOCK_FREQ / 300) + +/* TODO: flags? looks that we don't need these */ +#define PSMUX_PACKET_FLAG_NONE (0) +#define PSMUX_PACKET_FLAG_ADAPTATION (1 << 0) +#define PSMUX_PACKET_FLAG_DISCONT (1 << 1) +#define PSMUX_PACKET_FLAG_RANDOM_ACCESS (1 << 2) +#define PSMUX_PACKET_FLAG_PRIORITY (1 << 3) +#define PSMUX_PACKET_FLAG_WRITE_PCR (1 << 4) +#define PSMUX_PACKET_FLAG_WRITE_OPCR (1 << 5) +#define PSMUX_PACKET_FLAG_WRITE_SPLICE (1 << 6) +#define PSMUX_PACKET_FLAG_WRITE_ADAPT_EXT (1 << 7) + +/* PES stream specific flags */ +#define PSMUX_PACKET_FLAG_PES_FULL_HEADER (1 << 8) +#define PSMUX_PACKET_FLAG_PES_WRITE_PTS (1 << 9) +#define PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS (1 << 10) +#define PSMUX_PACKET_FLAG_PES_WRITE_ESCR (1 << 11) +#define PSMUX_PACKET_FLAG_PES_EXT_STREAMID (1 << 12) +#define PSMUX_PACKET_FLAG_PES_DATA_ALIGN (1 << 13) + +typedef struct PsMuxPacketInfo PsMuxPacketInfo; +typedef struct PsMuxProgram PsMuxProgram; +typedef struct PsMuxStream PsMuxStream; +typedef struct PsMuxStreamIdInfo PsMuxStreamIdInfo; +typedef struct PsMux PsMux; +typedef enum PsMuxStreamType PsMuxStreamType; +typedef struct PsMuxStreamBuffer PsMuxStreamBuffer; + +/* clearup and see if we need this anymore */ +struct PsMuxPacketInfo { + guint32 flags; +}; + +/* bitstream writers */ +static inline void +psmux_put16 (guint8 **pos, guint16 val) +{ + *(*pos)++ = (val >> 8) & 0xff; + *(*pos)++ = val & 0xff; +} + +static inline void +psmux_put32 (guint8 **pos, guint32 val) +{ + *(*pos)++ = (val >> 24) & 0xff; + *(*pos)++ = (val >> 16) & 0xff; + *(*pos)++ = (val >> 8) & 0xff; + *(*pos)++ = val & 0xff; +} + +static inline void +psmux_put_ts (guint8 **pos, guint8 id, gint64 ts) +{ + /* 1: 4 bit id value | TS [32..30] | marker_bit */ + *(*pos)++ = ((id << 4) | ((ts >> 29) & 0x0E) | 0x01) & 0xff; + /* 2, 3: TS[29..15] | marker_bit */ + psmux_put16 (pos, ((ts >> 14) & 0xfffe) | 0x01); + /* 4, 5: TS[14..0] | marker_bit */ + psmux_put16 (pos, ((ts << 1) & 0xfffe) | 0x01); +} + +#ifdef PS_DEBUG_ON +#define PS_DEBUG(...) g_print(__VA_ARGS__); g_print ("\n") +#else +#define PS_DEBUG(...) +#endif + +G_END_DECLS + +#endif diff --git a/gst/mpegpsmux/psmuxstream.c b/gst/mpegpsmux/psmuxstream.c new file mode 100644 index 0000000000..e91cc006b6 --- /dev/null +++ b/gst/mpegpsmux/psmuxstream.c @@ -0,0 +1,757 @@ +/* MPEG-PS muxer plugin for GStreamer + * Copyright 2008 Lin YANG + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "psmuxcommon.h" +#include "psmuxstream.h" +#include "psmux.h" + +static guint8 psmux_stream_pes_header_length (PsMuxStream * stream); +static void psmux_stream_write_pes_header (PsMuxStream * stream, guint8 * data); +static void psmux_stream_find_pts_dts_within (PsMuxStream * stream, guint bound, + gint64 * pts, gint64 * dts); + +/** + * psmux_stream_new: + * @pid: a PID + * @stream_type: the stream type + * + * Create a new stream with type = @stream_type, assign stream id accordingly + * + * Returns: a new #PsMuxStream. + */ +PsMuxStream * +psmux_stream_new (PsMux * mux, PsMuxStreamType stream_type) +{ + PsMuxStream *stream = g_slice_new0 (PsMuxStream); + PsMuxStreamIdInfo *info = &(mux->id_info); + + stream->stream_type = stream_type; + stream->is_audio_stream = FALSE; + stream->is_video_stream = FALSE; + stream->stream_id = 0; + stream->max_buffer_size = 0; + + switch (stream_type) { + /* MPEG AUDIO */ + case PSMUX_ST_AUDIO_MPEG1: + case PSMUX_ST_AUDIO_MPEG2: + stream->max_buffer_size = 2484; /* ISO/IEC 13818 2.5.2.4 */ + case PSMUX_ST_AUDIO_AAC: + if (info->id_mpga > PSMUX_STREAM_ID_MPGA_MAX) + break; + stream->stream_id = info->id_mpga++; + stream->stream_id_ext = 0; + stream->is_audio_stream = TRUE; + break; + /* MPEG VIDEO */ + case PSMUX_ST_VIDEO_MPEG1: + case PSMUX_ST_VIDEO_MPEG2: + case PSMUX_ST_VIDEO_MPEG4: + case PSMUX_ST_VIDEO_H264: + if (info->id_mpgv > PSMUX_STREAM_ID_MPGV_MAX) + break; + stream->stream_id = info->id_mpgv++; + stream->stream_id_ext = 0; + stream->is_video_stream = TRUE; + break; + /* AC3 / A52 */ + case PSMUX_ST_PS_AUDIO_AC3: + if (info->id_ac3 > PSMUX_STREAM_ID_AC3_MAX) + break; + stream->stream_id = PSMUX_PRIVATE_STREAM_1; + stream->stream_id_ext = info->id_ac3++; + stream->is_audio_stream = TRUE; + /* AC3 requires data alignment */ + stream->pi.flags |= PSMUX_PACKET_FLAG_PES_DATA_ALIGN; + break; + /* TODO: SPU missing */ +#if 0 + case spu: + if (info->id_spu > PSMUX_STREAM_ID_SPU_MAX) + break; + return info->id_spu++; +#endif + /* DTS */ + case PSMUX_ST_PS_AUDIO_DTS: + if (info->id_dts > PSMUX_STREAM_ID_DTS_MAX) + break; + stream->stream_id = PSMUX_PRIVATE_STREAM_1; + stream->stream_id_ext = info->id_dts++; + stream->is_audio_stream = TRUE; + break; + /* LPCM */ + case PSMUX_ST_PS_AUDIO_LPCM: + if (info->id_lpcm > PSMUX_STREAM_ID_LPCM_MAX) + break; + stream->stream_id = PSMUX_PRIVATE_STREAM_1; + stream->stream_id_ext = info->id_lpcm++; + stream->is_audio_stream = TRUE; + break; + case PSMUX_ST_VIDEO_DIRAC: + if (info->id_dirac > PSMUX_STREAM_ID_DIRAC_MAX) + break; + stream->stream_id = PSMUX_EXTENDED_STREAM; + stream->stream_id_ext = info->id_dirac++; + stream->is_video_stream = TRUE; + break; + default: + g_critical ("Stream type 0x%0x not yet implemented", stream_type); + break; + } + + if (stream->stream_id == 0) { + g_critical ("Number of elementary streams of type %04x exceeds maximum", + stream->stream_type); + return NULL; + } + + /* XXX: Are private streams also using stream_id_ext? */ + if (stream->stream_id == PSMUX_EXTENDED_STREAM) + stream->pi.flags |= PSMUX_PACKET_FLAG_PES_EXT_STREAMID; + + /* Are these useful at all? */ + if (stream->stream_id == PSMUX_PROGRAM_STREAM_MAP || + stream->stream_id == PSMUX_PADDING_STREAM || + stream->stream_id == PSMUX_PRIVATE_STREAM_2 || + stream->stream_id == PSMUX_ECM || + stream->stream_id == PSMUX_EMM || + stream->stream_id == PSMUX_PROGRAM_STREAM_DIRECTORY || + stream->stream_id == PSMUX_DSMCC_STREAM || + stream->stream_id == PSMUX_ITU_T_H222_1_TYPE_E) + stream->pi.flags &= ~PSMUX_PACKET_FLAG_PES_FULL_HEADER; + else + stream->pi.flags |= PSMUX_PACKET_FLAG_PES_FULL_HEADER; + + stream->buffers = NULL; + stream->bytes_avail = 0; + stream->cur_buffer = NULL; + stream->cur_buffer_consumed = 0; + + stream->cur_pes_payload_size = 0; + + stream->buffer_release = NULL; + + stream->pts = -1; + stream->dts = -1; + stream->last_pts = -1; + + /* These fields are set by gstreamer */ + stream->audio_sampling = 0; + stream->audio_channels = 0; + stream->audio_bitrate = 0; + + if (stream->max_buffer_size == 0) { + /* XXX: VLC'S VALUE. Better default? */ + if (stream->is_video_stream) + stream->max_buffer_size = 400 * 1024; + else if (stream->is_audio_stream) + stream->max_buffer_size = 4 * 1024; + else /* Unknown */ + stream->max_buffer_size = 4 * 1024; + } + + return stream; +} + +/** + * psmux_stream_free: + * @stream: a #PsMuxStream + * + * Free the resources of @stream. + */ +void +psmux_stream_free (PsMuxStream * stream) +{ + g_return_if_fail (stream != NULL); + + if (psmux_stream_bytes_in_buffer (stream)) { + g_warning ("Freeing stream with data not yet processed"); + } + g_slice_free (PsMuxStream, stream); +} + +/** + * psmux_stream_set_buffer_release_func: + * @stream: a #PsMuxStream + * @func: the new #PsMuxStreamBufferReleaseFunc + * + * Set the function that will be called when a a piece of data fed to @stream + * with psmux_stream_add_data() can be freed. @func will be called with user + * data as provided with the call to psmux_stream_add_data(). + */ +void +psmux_stream_set_buffer_release_func (PsMuxStream * stream, + PsMuxStreamBufferReleaseFunc func) +{ + g_return_if_fail (stream != NULL); + + stream->buffer_release = func; +} + +/* Advance the current packet stream position by len bytes. + * Mustn't consume more than available in the current packet */ +static void +psmux_stream_consume (PsMuxStream * stream, guint len) +{ + g_assert (stream->cur_buffer != NULL); + g_assert (len <= stream->cur_buffer->size - stream->cur_buffer_consumed); + + stream->cur_buffer_consumed += len; + stream->bytes_avail -= len; + + if (stream->cur_buffer_consumed == 0) + return; + + if (stream->cur_buffer->pts != -1) + stream->last_pts = stream->cur_buffer->pts; + + if (stream->cur_buffer_consumed == stream->cur_buffer->size) { + /* Current packet is completed, move along */ + stream->buffers = g_list_delete_link (stream->buffers, stream->buffers); + + if (stream->buffer_release) { + stream->buffer_release (stream->cur_buffer->data, + stream->cur_buffer->user_data); + } + + g_slice_free (PsMuxStreamBuffer, stream->cur_buffer); + stream->cur_buffer = NULL; + } +} + + +/** + * psmux_stream_bytes_in_buffer: + * @stream: a #PsMuxStream + * + * Calculate how much bytes are in the buffer. + * + * Returns: The number of bytes in the buffer. + */ +gint +psmux_stream_bytes_in_buffer (PsMuxStream * stream) +{ + g_return_val_if_fail (stream != NULL, 0); + + return stream->bytes_avail; +} + +/** + * psmux_stream_get_data: + * @stream: a #PsMuxStream + * @buf: a buffer to hold the result + * @len: the length of @buf + * + * Write a PES packet to @buf, up to @len bytes + * + * Returns: number of bytes having been written, 0 if error + */ +guint +psmux_stream_get_data (PsMuxStream * stream, guint8 * buf, guint len) +{ + guint8 pes_hdr_length; + guint w; + + g_return_val_if_fail (stream != NULL, FALSE); + g_return_val_if_fail (buf != NULL, FALSE); + g_return_val_if_fail (len >= PSMUX_PES_MAX_HDR_LEN, FALSE); + + stream->cur_pes_payload_size = + MIN (psmux_stream_bytes_in_buffer (stream), len - PSMUX_PES_MAX_HDR_LEN); + /* Note that we cannot make a better estimation of the header length for the + * time being; because the header length is dependent on whether we can find a + * timestamp in the upcomming buffers, which in turn depends on + * cur_pes_payload_size, which is exactly what we want to decide. + */ + + psmux_stream_find_pts_dts_within (stream, stream->cur_pes_payload_size, + &stream->pts, &stream->dts); + + /* clear pts/dts flag */ + stream->pi.flags &= ~(PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS | + PSMUX_PACKET_FLAG_PES_WRITE_PTS); + /* update pts/dts flag */ + if (stream->pts != -1 && stream->dts != -1) + stream->pi.flags |= PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS; + else { + if (stream->pts != -1) + stream->pi.flags |= PSMUX_PACKET_FLAG_PES_WRITE_PTS; + } + + pes_hdr_length = psmux_stream_pes_header_length (stream); + + /* write pes header */ + PS_DEBUG ("Writing PES header of length %u and payload %d", + pes_hdr_length, stream->cur_pes_payload_size); + psmux_stream_write_pes_header (stream, buf); + + buf += pes_hdr_length; + w = stream->cur_pes_payload_size; /* number of bytes of payload to write */ + + while (w > 0) { + guint32 avail; + guint8 *cur; + + if (stream->cur_buffer == NULL) { + /* Start next packet */ + if (stream->buffers == NULL) + return FALSE; + stream->cur_buffer = (PsMuxStreamBuffer *) (stream->buffers->data); + stream->cur_buffer_consumed = 0; + } + + /* Take as much as we can from the current buffer */ + avail = stream->cur_buffer->size - stream->cur_buffer_consumed; + cur = stream->cur_buffer->data + stream->cur_buffer_consumed; + if (avail < w) { + memcpy (buf, cur, avail); + psmux_stream_consume (stream, avail); + + buf += avail; + w -= avail; + } else { + memcpy (buf, cur, w); + psmux_stream_consume (stream, w); + + w = 0; + } + } + + return pes_hdr_length + stream->cur_pes_payload_size; +} + +static guint8 +psmux_stream_pes_header_length (PsMuxStream * stream) +{ + guint8 packet_len; + + /* Calculate the length of the header for this stream */ + + /* start_code prefix + stream_id + pes_packet_length = 6 bytes */ + packet_len = 6; + + if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_FULL_HEADER) { + /* For a PES 'full header' we have at least 3 more bytes, + * and then more based on flags */ + packet_len += 3; + if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) { + packet_len += 10; + } else if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS) { + packet_len += 5; + } + if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_EXT_STREAMID) { + /* Need basic extension flags (1 byte), plus 2 more bytes for the + * length + extended stream id */ + packet_len += 3; + } + } + + return packet_len; +} + +/* Find a PTS/DTS to write into the pes header within the next bound bytes + * of the data */ +static void +psmux_stream_find_pts_dts_within (PsMuxStream * stream, guint bound, + gint64 * pts, gint64 * dts) +{ + /* 1. When there are at least one buffer then output the first buffer with + * pts/dts + * 2. If the bound is too small to include even one buffer, output the pts/dts + * of that buffer. + */ + GList *cur; + + *pts = -1; + *dts = -1; + + for (cur = g_list_first (stream->buffers); cur != NULL; + cur = g_list_next (cur)) { + PsMuxStreamBuffer *curbuf = cur->data; + + /* FIXME: This isn't quite correct - if the 'bound' is within this + * buffer, we don't know if the timestamp is before or after the split + * so we shouldn't return it */ + if (bound <= curbuf->size) { + *pts = curbuf->pts; + *dts = curbuf->dts; + return; + } + + /* Have we found a buffer with pts/dts set? */ + if (curbuf->pts != -1 || curbuf->dts != -1) { + *pts = curbuf->pts; + *dts = curbuf->dts; + return; + } + + bound -= curbuf->size; + } +} + +static void +psmux_stream_write_pes_header (PsMuxStream * stream, guint8 * data) +{ + guint16 length_to_write; + guint8 hdr_len = psmux_stream_pes_header_length (stream); + + /* start_code prefix + stream_id + pes_packet_length = 6 bytes */ + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x01; + data[3] = stream->stream_id; + data += 4; + + length_to_write = hdr_len - 6 + stream->cur_pes_payload_size; + + psmux_put16 (&data, length_to_write); + + if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_FULL_HEADER) { + guint8 flags = 0; + + /* Not scrambled, original, not-copyrighted, data_alignment specified by flag */ + flags = 0x81; + if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_DATA_ALIGN) + flags |= 0x04; /* Enable data_alignment_indicator */ + *data++ = flags; + + /* Flags */ + flags = 0; + if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) + flags |= 0xC0; + else if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS) + flags |= 0x80; + if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_EXT_STREAMID) + flags |= 0x01; /* Enable PES_extension_flag */ + *data++ = flags; + + /* Header length is the total pes length, + * minus the 9 bytes of start codes, flags + hdr_len */ + g_return_if_fail (hdr_len >= 9); + *data++ = (hdr_len - 9); + + if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) { + psmux_put_ts (&data, 0x3, stream->pts); + psmux_put_ts (&data, 0x1, stream->dts); + } else if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS) { + psmux_put_ts (&data, 0x2, stream->pts); + } + + if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_EXT_STREAMID) { + guint8 ext_len; + + flags = 0x0f; /* preceeding flags all 0 | (reserved bits) | PES_extension_flag_2 */ + *data++ = flags; + + ext_len = 1; /* Only writing 1 byte into the extended fields */ + *data++ = 0x80 | ext_len; /* marker | PES_extension_field_length */ + *data++ = 0x80 | stream->stream_id_ext; /* stream_id_extension_flag | extended_stream_id */ + } + } +} + +/** + * psmux_stream_add_data: + * @stream: a #PsMuxStream + * @data: data to add + * @len: length of @data + * @user_data: user data to pass to release func + * @pts: PTS of access unit in @data + * @dts: DTS of access unit in @data + * + * Submit @len bytes of @data into @stream. @pts and @dts can be set to the + * timestamp (against a 90Hz clock) of the first access unit in @data. A + * timestamp of -1 for @pts or @dts means unknown. + * + * @user_data will be passed to the release function as set with + * psmux_stream_set_buffer_release_func() when @data can be freed. + */ +void +psmux_stream_add_data (PsMuxStream * stream, guint8 * data, guint len, + void *user_data, gint64 pts, gint64 dts) +{ + PsMuxStreamBuffer *packet; + + g_return_if_fail (stream != NULL); + + packet = g_slice_new (PsMuxStreamBuffer); + packet->data = data; + packet->size = len; + packet->user_data = user_data; + + packet->pts = pts; + packet->dts = dts; + + if (stream->bytes_avail == 0) + stream->last_pts = pts; + + stream->bytes_avail += len; + stream->buffers = g_list_append (stream->buffers, packet); + +} + +/** + * psmux_stream_get_es_descrs: + * @stream: a #PsMuxStream + * @buf: a buffer to hold the ES descriptor + * @len: the length used in @buf + * + * Write an Elementary Stream Descriptor for @stream into @buf. the number of + * bytes consumed in @buf will be updated in @len. + * + * @buf and @len must be at least #PSMUX_MIN_ES_DESC_LEN. + */ +void +psmux_stream_get_es_descrs (PsMuxStream * stream, guint8 * buf, guint16 * len) +{ + guint8 *pos; + + g_return_if_fail (stream != NULL); + + if (buf == NULL) { + if (len != NULL) + *len = 0; + return; + } + + /* Based on the stream type, write out any descriptors to go in the + * PMT ES_info field */ + pos = buf; + + /* tag (registration_descriptor), length, format_identifier */ + switch (stream->stream_type) { + case PSMUX_ST_AUDIO_AAC: + /* FIXME */ + break; + case PSMUX_ST_VIDEO_MPEG4: + /* FIXME */ + break; + case PSMUX_ST_VIDEO_H264: + *pos++ = 0x05; + *pos++ = 8; + *pos++ = 0x48; /* 'H' */ + *pos++ = 0x44; /* 'D' */ + *pos++ = 0x4D; /* 'M' */ + *pos++ = 0x56; /* 'V' */ + /* FIXME : Not sure about this additional_identification_info */ + *pos++ = 0xFF; + *pos++ = 0x1B; + *pos++ = 0x44; + *pos++ = 0x3F; + break; + case PSMUX_ST_VIDEO_DIRAC: + *pos++ = 0x05; + *pos++ = 4; + *pos++ = 0x64; /* 'd' */ + *pos++ = 0x72; /* 'r' */ + *pos++ = 0x61; /* 'a' */ + *pos++ = 0x63; /* 'c' */ + break; + case PSMUX_ST_PS_AUDIO_AC3: + { + *pos++ = 0x05; + *pos++ = 4; + *pos++ = 0x41; /* 'A' */ + *pos++ = 0x43; /* 'C' */ + *pos++ = 0x2D; /* '-' */ + *pos++ = 0x33; /* '3' */ + + /* audio_stream_descriptor () | ATSC A/52-2001 Annex A + * + * descriptor_tag 8 uimsbf + * descriptor_length 8 uimsbf + * sample_rate_code 3 bslbf + * bsid 5 bslbf + * bit_rate_code 6 bslbf + * surround_mode 2 bslbf + * bsmod 3 bslbf + * num_channels 4 bslbf + * full_svc 1 bslbf + * langcod 8 bslbf + * [...] + */ + *pos++ = 0x81; + *pos++ = 0x04; + + /* 3 bits sample_rate_code, 5 bits hardcoded bsid (default ver 8) */ + switch (stream->audio_sampling) { + case 48000: + *pos++ = 0x08; + break; + case 44100: + *pos++ = 0x28; + break; + case 32000: + *pos++ = 0x48; + break; + default: + *pos++ = 0xE8; + break; /* 48, 44.1 or 32 Khz */ + } + + /* 1 bit bit_rate_limit, 5 bits bit_rate_code, 2 bits suround_mode */ + switch (stream->audio_bitrate) { + case 32: + *pos++ = 0x00 << 2; + break; + case 40: + *pos++ = 0x01 << 2; + break; + case 48: + *pos++ = 0x02 << 2; + break; + case 56: + *pos++ = 0x03 << 2; + break; + case 64: + *pos++ = 0x04 << 2; + break; + case 80: + *pos++ = 0x05 << 2; + break; + case 96: + *pos++ = 0x06 << 2; + break; + case 112: + *pos++ = 0x07 << 2; + break; + case 128: + *pos++ = 0x08 << 2; + break; + case 160: + *pos++ = 0x09 << 2; + break; + case 192: + *pos++ = 0x0A << 2; + break; + case 224: + *pos++ = 0x0B << 2; + break; + case 256: + *pos++ = 0x0C << 2; + break; + case 320: + *pos++ = 0x0D << 2; + break; + case 384: + *pos++ = 0x0E << 2; + break; + case 448: + *pos++ = 0x0F << 2; + break; + case 512: + *pos++ = 0x10 << 2; + break; + case 576: + *pos++ = 0x11 << 2; + break; + case 640: + *pos++ = 0x12 << 2; + break; + default: + *pos++ = 0x32 << 2; + break; /* 640 Kb/s upper limit */ + } + + /* 3 bits bsmod, 4 bits num_channels, 1 bit full_svc */ + switch (stream->audio_channels) { + case 1: + *pos++ = 0x01 << 1; + break; /* 1/0 */ + case 2: + *pos++ = 0x02 << 1; + break; /* 2/0 */ + case 3: + *pos++ = 0x0A << 1; + break; /* <= 3 */ + case 4: + *pos++ = 0x0B << 1; + break; /* <= 4 */ + case 5: + *pos++ = 0x0C << 1; + break; /* <= 5 */ + case 6: + default: + *pos++ = 0x0D << 1; + break; /* <= 6 */ + } + + *pos++ = 0x00; + + break; + } + case PSMUX_ST_PS_AUDIO_DTS: + /* FIXME */ + break; + case PSMUX_ST_PS_AUDIO_LPCM: + /* FIXME */ + break; + default: + break; + } + + if (len) + *len = (pos - buf); +} + +/** + * psmux_stream_get_pts: + * @stream: a #PsMuxStream + * + * Return the PTS of the last buffer that has had bytes written and + * which _had_ a PTS in @stream. + * + * Returns: the PTS of the last buffer in @stream. + */ +guint64 +psmux_stream_get_pts (PsMuxStream * stream) +{ + g_return_val_if_fail (stream != NULL, -1); + + return stream->last_pts; +} diff --git a/gst/mpegpsmux/psmuxstream.h b/gst/mpegpsmux/psmuxstream.h new file mode 100644 index 0000000000..9860e62302 --- /dev/null +++ b/gst/mpegpsmux/psmuxstream.h @@ -0,0 +1,212 @@ +/* MPEG-PS muxer plugin for GStreamer + * Copyright 2008 Lin YANG + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + + +#ifndef __PSMUXSTREAM_H__ +#define __PSMUXSTREAM_H__ + +#include + +#include "psmuxcommon.h" + +G_BEGIN_DECLS + + +typedef void (*PsMuxStreamBufferReleaseFunc) (guint8 *data, void *user_data); + +enum PsMuxStreamType { /* Table 2-29 in spec */ + PSMUX_ST_RESERVED = 0x00, + PSMUX_ST_VIDEO_MPEG1 = 0x01, + PSMUX_ST_VIDEO_MPEG2 = 0x02, + PSMUX_ST_AUDIO_MPEG1 = 0x03, + PSMUX_ST_AUDIO_MPEG2 = 0x04, + PSMUX_ST_PRIVATE_SECTIONS = 0x05, + PSMUX_ST_PRIVATE_DATA = 0x06, + PSMUX_ST_MHEG = 0x07, + PSMUX_ST_DSMCC = 0x08, + PSMUX_ST_H222_1 = 0x09, + + /* later extensions */ + PSMUX_ST_AUDIO_AAC = 0x0f, + PSMUX_ST_VIDEO_MPEG4 = 0x10, + PSMUX_ST_VIDEO_H264 = 0x1b, + + /* private stream types */ + PSMUX_ST_PS_AUDIO_AC3 = 0x81, + PSMUX_ST_PS_AUDIO_DTS = 0x8a, + PSMUX_ST_PS_AUDIO_LPCM = 0x8b, + PSMUX_ST_PS_DVD_SUBPICTURE = 0xff, + + /* Non-standard definitions */ + PSMUX_ST_VIDEO_DIRAC = 0xD1 +}; + +struct PsMuxStreamBuffer +{ + guint8 *data; + guint32 size; + + /* PTS & DTS associated with the contents of this buffer */ + GstClockTime pts; + GstClockTime dts; + + void *user_data; +}; + +/* PsMuxStream receives elementary streams for parsing. + * Via the write_bytes() method, it can output a PES stream piecemeal */ +struct PsMuxStream{ + PsMuxPacketInfo pi; + + PsMuxStreamType stream_type; + guint8 stream_id; + guint8 stream_id_ext; /* extended stream id (13818-1 Amdt 2) */ + + /* List of data buffers available for writing out */ + GList *buffers; + guint32 bytes_avail; + + /* Current data buffer being consumed */ + PsMuxStreamBuffer *cur_buffer; + guint32 cur_buffer_consumed; + + /* PES payload */ + guint16 cur_pes_payload_size; + guint16 pes_bytes_written; /* delete*/ + + /* Release function */ + PsMuxStreamBufferReleaseFunc buffer_release; + + /* PTS/DTS to write if the flags in the packet info are set */ + gint64 pts; /* TODO: cur_buffer->pts?*/ + gint64 dts; /* TODO: cur_buffer->dts?*/ + gint64 last_pts; + + /* stream type */ + gboolean is_video_stream; + gboolean is_audio_stream; + + /* for writing descriptors */ + gint audio_sampling; + gint audio_channels; + gint audio_bitrate; + + /* for writing buffer size in system header */ + guint max_buffer_size; +}; + +/* stream management */ +PsMuxStream* psmux_stream_new (PsMux * mux, PsMuxStreamType stream_type); +void psmux_stream_free (PsMuxStream *stream); + +/* The callback when a buffer is released. Used to unref the buffer in GStreamer */ +void psmux_stream_set_buffer_release_func (PsMuxStream *stream, + PsMuxStreamBufferReleaseFunc func); + +/* Add a new buffer to the pool of available bytes. If pts or dts are not -1, they + * indicate the PTS or DTS of the first access unit within this packet */ +void psmux_stream_add_data (PsMuxStream *stream, guint8 *data, guint len, + void *user_data, gint64 pts, gint64 dts); + +/* total bytes in buffer */ +gint psmux_stream_bytes_in_buffer (PsMuxStream *stream); +/* number of bytes of raw data available for writing */ +gint psmux_stream_bytes_avail (PsMuxStream *stream); + +/* write PES data */ +guint psmux_stream_get_data (PsMuxStream *stream, guint8 *buf, guint len); + +/* write corresponding descriptors of the stream */ +void psmux_stream_get_es_descrs (PsMuxStream *stream, guint8 *buf, guint16 *len); + +/* get the pts of stream */ +guint64 psmux_stream_get_pts (PsMuxStream *stream); + +/* stream_id assignemnt */ +#define PSMUX_STREAM_ID_MPGA_INIT 0xc0 +#define PSMUX_STREAM_ID_MPGA_MAX 0xcf + +#define PSMUX_STREAM_ID_MPGV_INIT 0xe0 +#define PSMUX_STREAM_ID_MPGV_MAX 0xef + +#define PSMUX_STREAM_ID_AC3_INIT 0x80 +#define PSMUX_STREAM_ID_AC3_MAX 0x87 + +#define PSMUX_STREAM_ID_SPU_INIT 0x20 +#define PSMUX_STREAM_ID_SPU_MAX 0x3f + +#define PSMUX_STREAM_ID_DTS_INIT 0x88 +#define PSMUX_STREAM_ID_DTS_MAX 0x8f + +#define PSMUX_STREAM_ID_LPCM_INIT 0xa0 +#define PSMUX_STREAM_ID_LPCM_MAX 0xaf + +#define PSMUX_STREAM_ID_DIRAC_INIT 0x60 +#define PSMUX_STREAM_ID_DIRAC_MAX 0x6f + +struct PsMuxStreamIdInfo { + guint8 id_mpga; + guint8 id_mpgv; + guint8 id_ac3; + guint8 id_spu; + guint8 id_dts; + guint8 id_lpcm; + guint8 id_dirac; +}; + +static inline void +psmux_stream_id_info_init (PsMuxStreamIdInfo * info) +{ + g_return_if_fail (info != NULL); + info->id_mpga = PSMUX_STREAM_ID_MPGA_INIT; + info->id_mpgv = PSMUX_STREAM_ID_MPGV_INIT; + info->id_ac3 = PSMUX_STREAM_ID_AC3_INIT; + info->id_spu = PSMUX_STREAM_ID_SPU_INIT; + info->id_dts = PSMUX_STREAM_ID_DTS_INIT; + info->id_lpcm = PSMUX_STREAM_ID_LPCM_INIT; + info->id_dirac= PSMUX_STREAM_ID_DIRAC_INIT; +} + +G_END_DECLS + +#endif