mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-20 00:31:13 +00:00
mpegpsmux: Add MPEG PS muxer
This commit is contained in:
parent
de1f057725
commit
adb4130e65
11 changed files with 2730 additions and 0 deletions
|
@ -274,6 +274,7 @@ AG_GST_CHECK_PLUGIN(librfb)
|
||||||
AG_GST_CHECK_PLUGIN(liveadder)
|
AG_GST_CHECK_PLUGIN(liveadder)
|
||||||
AG_GST_CHECK_PLUGIN(mpegdemux)
|
AG_GST_CHECK_PLUGIN(mpegdemux)
|
||||||
AG_GST_CHECK_PLUGIN(mpegtsmux)
|
AG_GST_CHECK_PLUGIN(mpegtsmux)
|
||||||
|
AG_GST_CHECK_PLUGIN(mpegpsmux)
|
||||||
AG_GST_CHECK_PLUGIN(mpeg4videoparse)
|
AG_GST_CHECK_PLUGIN(mpeg4videoparse)
|
||||||
AG_GST_CHECK_PLUGIN(mpegvideoparse)
|
AG_GST_CHECK_PLUGIN(mpegvideoparse)
|
||||||
AG_GST_CHECK_PLUGIN(mve)
|
AG_GST_CHECK_PLUGIN(mve)
|
||||||
|
@ -1724,6 +1725,7 @@ gst/liveadder/Makefile
|
||||||
gst/mpegdemux/Makefile
|
gst/mpegdemux/Makefile
|
||||||
gst/mpegtsmux/Makefile
|
gst/mpegtsmux/Makefile
|
||||||
gst/mpegtsmux/tsmux/Makefile
|
gst/mpegtsmux/tsmux/Makefile
|
||||||
|
gst/mpegpsmux/Makefile
|
||||||
gst/mpeg4videoparse/Makefile
|
gst/mpeg4videoparse/Makefile
|
||||||
gst/mpegvideoparse/Makefile
|
gst/mpegvideoparse/Makefile
|
||||||
gst/mve/Makefile
|
gst/mve/Makefile
|
||||||
|
|
23
gst/mpegpsmux/Makefile.am
Normal file
23
gst/mpegpsmux/Makefile.am
Normal file
|
@ -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
|
95
gst/mpegpsmux/bits.h
Normal file
95
gst/mpegpsmux/bits.h
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* bits.h
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (C) 2001, 2002 the VideoLAN team
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
|
||||||
|
* Eric Petit <titer@videolan.org>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Lin YANG <oxcsnicho@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <glib.h>
|
||||||
|
|
||||||
|
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
|
100
gst/mpegpsmux/crc.h
Normal file
100
gst/mpegpsmux/crc.h
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/* MPEG-PS muxer plugin for GStreamer
|
||||||
|
* Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 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<datalen; i++) {
|
||||||
|
crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
702
gst/mpegpsmux/mpegpsmux.c
Normal file
702
gst/mpegpsmux/mpegpsmux.c
Normal file
|
@ -0,0 +1,702 @@
|
||||||
|
/* MPEG-PS muxer plugin for GStreamer
|
||||||
|
* Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 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 <string.h>
|
||||||
|
|
||||||
|
#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 <oxcsnicho@gmail.com>"
|
||||||
|
};
|
||||||
|
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);
|
120
gst/mpegpsmux/mpegpsmux.h
Normal file
120
gst/mpegpsmux/mpegpsmux.h
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/* MPEG-PS muxer plugin for GStreamer
|
||||||
|
* Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 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 <gst/gst.h>
|
||||||
|
#include <gst/base/gstcollectpads.h>
|
||||||
|
#include <gst/base/gstadapter.h>
|
||||||
|
|
||||||
|
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
|
443
gst/mpegpsmux/psmux.c
Normal file
443
gst/mpegpsmux/psmux.c
Normal file
|
@ -0,0 +1,443 @@
|
||||||
|
/* MPEG-PS muxer plugin for GStreamer
|
||||||
|
* Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 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 <string.h>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
114
gst/mpegpsmux/psmux.h
Normal file
114
gst/mpegpsmux/psmux.h
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/* MPEG-PS muxer plugin for GStreamer
|
||||||
|
* Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 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 <glib.h>
|
||||||
|
|
||||||
|
#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
|
162
gst/mpegpsmux/psmuxcommon.h
Normal file
162
gst/mpegpsmux/psmuxcommon.h
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/* MPEG-PS muxer plugin for GStreamer
|
||||||
|
* Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 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 <glib.h>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#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
|
757
gst/mpegpsmux/psmuxstream.c
Normal file
757
gst/mpegpsmux/psmuxstream.c
Normal file
|
@ -0,0 +1,757 @@
|
||||||
|
/* MPEG-PS muxer plugin for GStreamer
|
||||||
|
* Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 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 <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
212
gst/mpegpsmux/psmuxstream.h
Normal file
212
gst/mpegpsmux/psmuxstream.h
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/* MPEG-PS muxer plugin for GStreamer
|
||||||
|
* Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 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 <glib.h>
|
||||||
|
|
||||||
|
#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
|
Loading…
Reference in a new issue