basetsmux: extract m2ts-mode to mpegtsmux

This commit is contained in:
Mathieu Duponchelle 2019-04-30 00:50:06 +02:00 committed by Mathieu Duponchelle
parent 649cc2d5e8
commit 44c701d113
4 changed files with 320 additions and 193 deletions

View file

@ -108,7 +108,6 @@ enum
{ {
PROP_0, PROP_0,
PROP_PROG_MAP, PROP_PROG_MAP,
PROP_M2TS_MODE,
PROP_PAT_INTERVAL, PROP_PAT_INTERVAL,
PROP_PMT_INTERVAL, PROP_PMT_INTERVAL,
PROP_ALIGNMENT, PROP_ALIGNMENT,
@ -117,7 +116,6 @@ enum
}; };
#define BASETSMUX_DEFAULT_ALIGNMENT -1 #define BASETSMUX_DEFAULT_ALIGNMENT -1
#define BASETSMUX_DEFAULT_M2TS FALSE
static GstStaticPadTemplate basetsmux_src_factory = static GstStaticPadTemplate basetsmux_src_factory =
GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_PAD_TEMPLATE ("src",
@ -142,8 +140,6 @@ static void release_buffer_cb (guint8 * data, void *user_data);
static GstFlowReturn basetsmux_collect_packet (BaseTsMux * mux, static GstFlowReturn basetsmux_collect_packet (BaseTsMux * mux,
GstBuffer * buf); GstBuffer * buf);
static GstFlowReturn basetsmux_push_packets (BaseTsMux * mux, gboolean force); static GstFlowReturn basetsmux_push_packets (BaseTsMux * mux, gboolean force);
static gboolean new_packet_m2ts (BaseTsMux * mux, GstBuffer * buf,
gint64 new_pcr);
static void basetsmux_prepare_srcpad (BaseTsMux * mux); static void basetsmux_prepare_srcpad (BaseTsMux * mux);
GstFlowReturn basetsmux_clip_inc_running_time (GstCollectPads * pads, GstFlowReturn basetsmux_clip_inc_running_time (GstCollectPads * pads,
@ -204,6 +200,25 @@ stream_data_free (StreamData * data)
#define parent_class basetsmux_parent_class #define parent_class basetsmux_parent_class
static void
basetsmux_default_allocate_packet (BaseTsMux * mux, GstBuffer ** buffer)
{
GstBuffer *buf;
buf = gst_buffer_new_and_alloc (mux->packet_size);
*buffer = buf;
}
static gboolean
basetsmux_default_output_packet (BaseTsMux * mux, GstBuffer * buffer,
gint64 new_pcr)
{
basetsmux_collect_packet (mux, buffer);
return TRUE;
}
static void static void
basetsmux_class_init (BaseTsMuxClass * klass) basetsmux_class_init (BaseTsMuxClass * klass)
{ {
@ -229,6 +244,8 @@ basetsmux_class_init (BaseTsMuxClass * klass)
gstelement_class->send_event = basetsmux_send_event; gstelement_class->send_event = basetsmux_send_event;
klass->create_ts_mux = basetsmux_default_create_ts_mux; klass->create_ts_mux = basetsmux_default_create_ts_mux;
klass->allocate_packet = basetsmux_default_allocate_packet;
klass->output_packet = basetsmux_default_output_packet;
#if 0 #if 0
gstelement_class->set_index = GST_DEBUG_FUNCPTR (basetsmux_set_index); gstelement_class->set_index = GST_DEBUG_FUNCPTR (basetsmux_set_index);
@ -240,12 +257,6 @@ basetsmux_class_init (BaseTsMuxClass * klass)
"A GstStructure specifies the mapping from elementary streams to programs", "A GstStructure specifies the mapping from elementary streams to programs",
GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_M2TS_MODE,
g_param_spec_boolean ("m2ts-mode", "M2TS(192 bytes) Mode",
"Set to TRUE to output Blu-Ray disc format with 192 byte packets. "
"FALSE for standard TS format with 188 byte packets.",
BASETSMUX_DEFAULT_M2TS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PAT_INTERVAL, g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PAT_INTERVAL,
g_param_spec_uint ("pat-interval", "PAT interval", g_param_spec_uint ("pat-interval", "PAT interval",
"Set the interval (in ticks of the 90kHz clock) for writing out the PAT table", "Set the interval (in ticks of the 90kHz clock) for writing out the PAT table",
@ -300,17 +311,18 @@ basetsmux_init (BaseTsMux * mux)
gst_collect_pads_set_clip_function (mux->collect, (GstCollectPadsClipFunction) gst_collect_pads_set_clip_function (mux->collect, (GstCollectPadsClipFunction)
GST_DEBUG_FUNCPTR (basetsmux_clip_inc_running_time), mux); GST_DEBUG_FUNCPTR (basetsmux_clip_inc_running_time), mux);
mux->adapter = gst_adapter_new ();
mux->out_adapter = gst_adapter_new (); mux->out_adapter = gst_adapter_new ();
/* properties */ /* properties */
mux->m2ts_mode = BASETSMUX_DEFAULT_M2TS;
mux->pat_interval = TSMUX_DEFAULT_PAT_INTERVAL; mux->pat_interval = TSMUX_DEFAULT_PAT_INTERVAL;
mux->pmt_interval = TSMUX_DEFAULT_PMT_INTERVAL; mux->pmt_interval = TSMUX_DEFAULT_PMT_INTERVAL;
mux->si_interval = TSMUX_DEFAULT_SI_INTERVAL; mux->si_interval = TSMUX_DEFAULT_SI_INTERVAL;
mux->prog_map = NULL; mux->prog_map = NULL;
mux->alignment = BASETSMUX_DEFAULT_ALIGNMENT; mux->alignment = BASETSMUX_DEFAULT_ALIGNMENT;
mux->bitrate = TSMUX_DEFAULT_BITRATE; mux->bitrate = TSMUX_DEFAULT_BITRATE;
mux->packet_size = NORMAL_TS_PACKET_LENGTH;
mux->automatic_alignment = 0;
} }
static void static void
@ -348,12 +360,10 @@ basetsmux_reset (BaseTsMux * mux, gboolean alloc)
{ {
GstBuffer *buf; GstBuffer *buf;
GSList *walk; GSList *walk;
BaseTsMuxClass *klass = GST_BASE_TSMUX_GET_CLASS (mux);
mux->first = TRUE; mux->first = TRUE;
mux->last_flow_ret = GST_FLOW_OK; mux->last_flow_ret = GST_FLOW_OK;
mux->previous_pcr = -1;
mux->previous_offset = 0;
mux->pcr_rate_num = mux->pcr_rate_den = 1;
mux->last_ts = 0; mux->last_ts = 0;
mux->is_delta = TRUE; mux->is_delta = TRUE;
mux->is_header = FALSE; mux->is_header = FALSE;
@ -369,8 +379,6 @@ basetsmux_reset (BaseTsMux * mux, gboolean alloc)
mux->element_index = NULL; mux->element_index = NULL;
} }
#endif #endif
if (mux->adapter)
gst_adapter_clear (mux->adapter);
if (mux->out_adapter) if (mux->out_adapter)
gst_adapter_clear (mux->out_adapter); gst_adapter_clear (mux->out_adapter);
@ -398,12 +406,13 @@ basetsmux_reset (BaseTsMux * mux, gboolean alloc)
} }
if (alloc) { if (alloc) {
BaseTsMuxClass *klass = GST_BASE_TSMUX_GET_CLASS (mux);
g_assert (klass->create_ts_mux); g_assert (klass->create_ts_mux);
mux->tsmux = klass->create_ts_mux (mux); mux->tsmux = klass->create_ts_mux (mux);
} }
if (klass->reset)
klass->reset (mux);
} }
static void static void
@ -413,10 +422,6 @@ basetsmux_dispose (GObject * object)
basetsmux_reset (mux, FALSE); basetsmux_reset (mux, FALSE);
if (mux->adapter) {
g_object_unref (mux->adapter);
mux->adapter = NULL;
}
if (mux->out_adapter) { if (mux->out_adapter) {
g_object_unref (mux->out_adapter); g_object_unref (mux->out_adapter);
mux->out_adapter = NULL; mux->out_adapter = NULL;
@ -453,10 +458,6 @@ gst_basetsmux_set_property (GObject * object, guint prop_id,
GSList *walk; GSList *walk;
switch (prop_id) { switch (prop_id) {
case PROP_M2TS_MODE:
/*set incase if the output stream need to be of 192 bytes */
mux->m2ts_mode = g_value_get_boolean (value);
break;
case PROP_PROG_MAP: case PROP_PROG_MAP:
{ {
const GstStructure *s = gst_value_get_structure (value); const GstStructure *s = gst_value_get_structure (value);
@ -510,9 +511,6 @@ gst_basetsmux_get_property (GObject * object, guint prop_id,
BaseTsMux *mux = GST_BASE_TSMUX (object); BaseTsMux *mux = GST_BASE_TSMUX (object);
switch (prop_id) { switch (prop_id) {
case PROP_M2TS_MODE:
g_value_set_boolean (value, mux->m2ts_mode);
break;
case PROP_PROG_MAP: case PROP_PROG_MAP:
gst_value_set_structure (value, mux->prog_map); gst_value_set_structure (value, mux->prog_map);
break; break;
@ -1358,10 +1356,12 @@ basetsmux_collected_buffer (GstCollectPads * pads, GstCollectData * data,
} }
if (G_UNLIKELY (best == NULL)) { if (G_UNLIKELY (best == NULL)) {
BaseTsMuxClass *klass = GST_BASE_TSMUX_GET_CLASS (mux);
/* EOS */ /* EOS */
GST_INFO_OBJECT (mux, "EOS"); GST_INFO_OBJECT (mux, "EOS");
/* drain some possibly cached data */ /* drain some possibly cached data */
new_packet_m2ts (mux, NULL, -1); if (klass->drain)
klass->drain (mux);
basetsmux_push_packets (mux, TRUE); basetsmux_push_packets (mux, TRUE);
gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
@ -1645,15 +1645,10 @@ basetsmux_push_packets (BaseTsMux * mux, gboolean force)
gint align = mux->alignment; gint align = mux->alignment;
gint av, packet_size; gint av, packet_size;
if (mux->m2ts_mode) { packet_size = mux->packet_size;
packet_size = M2TS_PACKET_LENGTH;
if (align < 0) if (align < 0)
align = 32; align = mux->automatic_alignment;
} else {
packet_size = NORMAL_TS_PACKET_LENGTH;
if (align < 0)
align = 0;
}
av = gst_adapter_available (mux->out_adapter); av = gst_adapter_available (mux->out_adapter);
GST_LOG_OBJECT (mux, "align %d, av %d", align, av); GST_LOG_OBJECT (mux, "align %d, av %d", align, av);
@ -1753,174 +1748,39 @@ basetsmux_collect_packet (BaseTsMux * mux, GstBuffer * buf)
return GST_FLOW_OK; return GST_FLOW_OK;
} }
static gboolean
new_packet_m2ts (BaseTsMux * mux, GstBuffer * buf, gint64 new_pcr)
{
GstBuffer *out_buf;
int chunk_bytes;
GstMapInfo map;
GST_LOG_OBJECT (mux, "Have buffer %p with new_pcr=%" G_GINT64_FORMAT,
buf, new_pcr);
chunk_bytes = gst_adapter_available (mux->adapter);
if (G_LIKELY (buf)) {
if (new_pcr < 0) {
/* If there is no pcr in current ts packet then just add the packet
to the adapter for later output when we see a PCR */
GST_LOG_OBJECT (mux, "Accumulating non-PCR packet");
gst_adapter_push (mux->adapter, buf);
goto exit;
}
/* no first interpolation point yet, then this is the one,
* otherwise it is the second interpolation point */
if (mux->previous_pcr < 0 && chunk_bytes) {
mux->previous_pcr = new_pcr;
mux->previous_offset = chunk_bytes;
GST_LOG_OBJECT (mux, "Accumulating non-PCR packet");
gst_adapter_push (mux->adapter, buf);
goto exit;
}
} else {
g_assert (new_pcr == -1);
}
/* interpolate if needed, and 2 points available */
if (chunk_bytes && (new_pcr != mux->previous_pcr)) {
gint64 offset = 0;
GST_LOG_OBJECT (mux, "Processing pending packets; "
"previous pcr %" G_GINT64_FORMAT ", previous offset %d, "
"current pcr %" G_GINT64_FORMAT ", current offset %d",
mux->previous_pcr, (gint) mux->previous_offset,
new_pcr, (gint) chunk_bytes);
g_assert (chunk_bytes > mux->previous_offset);
/* if draining, use previous rate */
if (G_LIKELY (new_pcr > 0)) {
mux->pcr_rate_num = new_pcr - mux->previous_pcr;
mux->pcr_rate_den = chunk_bytes - mux->previous_offset;
}
while (offset < chunk_bytes) {
guint64 cur_pcr, ts;
/* Loop, pulling packets of the adapter, updating their 4 byte
* timestamp header and pushing */
/* interpolate PCR */
if (G_LIKELY (offset >= mux->previous_offset))
cur_pcr = mux->previous_pcr +
gst_util_uint64_scale (offset - mux->previous_offset,
mux->pcr_rate_num, mux->pcr_rate_den);
else
cur_pcr = mux->previous_pcr -
gst_util_uint64_scale (mux->previous_offset - offset,
mux->pcr_rate_num, mux->pcr_rate_den);
/* FIXME: what about DTS here? */
ts = gst_adapter_prev_pts (mux->adapter, NULL);
out_buf = gst_adapter_take_buffer (mux->adapter, M2TS_PACKET_LENGTH);
g_assert (out_buf);
offset += M2TS_PACKET_LENGTH;
GST_BUFFER_PTS (out_buf) = ts;
gst_buffer_map (out_buf, &map, GST_MAP_WRITE);
/* The header is the bottom 30 bits of the PCR, apparently not
* encoded into base + ext as in the packets themselves */
GST_WRITE_UINT32_BE (map.data, cur_pcr & 0x3FFFFFFF);
gst_buffer_unmap (out_buf, &map);
GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, cur_pcr);
basetsmux_collect_packet (mux, out_buf);
}
}
if (G_UNLIKELY (!buf))
goto exit;
gst_buffer_map (buf, &map, GST_MAP_WRITE);
/* Finally, output the passed in packet */
/* Only write the bottom 30 bits of the PCR */
GST_WRITE_UINT32_BE (map.data, new_pcr & 0x3FFFFFFF);
gst_buffer_unmap (buf, &map);
GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, new_pcr);
basetsmux_collect_packet (mux, buf);
if (new_pcr != mux->previous_pcr) {
mux->previous_pcr = new_pcr;
mux->previous_offset = -M2TS_PACKET_LENGTH;
}
exit:
return TRUE;
}
/* Called when the TsMux has prepared a packet for output. Return FALSE /* Called when the TsMux has prepared a packet for output. Return FALSE
* on error */ * on error */
static gboolean static gboolean
new_packet_cb (GstBuffer * buf, void *user_data, gint64 new_pcr) new_packet_cb (GstBuffer * buf, void *user_data, gint64 new_pcr)
{ {
BaseTsMux *mux = (BaseTsMux *) user_data; BaseTsMux *mux = (BaseTsMux *) user_data;
gint offset = 0; BaseTsMuxClass *klass = GST_BASE_TSMUX_GET_CLASS (mux);
GstMapInfo map; GstMapInfo map;
#if 0 g_assert (klass->output_packet);
GST_LOG_OBJECT (mux, "handling packet %d", mux->spn_count);
mux->spn_count++;
#endif
if (mux->m2ts_mode) {
offset = 4;
gst_buffer_set_size (buf, NORMAL_TS_PACKET_LENGTH + offset);
}
gst_buffer_map (buf, &map, GST_MAP_READWRITE); gst_buffer_map (buf, &map, GST_MAP_READWRITE);
if (offset) {
/* there should be a better way to do this */
memmove (map.data + offset, map.data, map.size - offset);
}
GST_BUFFER_PTS (buf) = mux->last_ts; GST_BUFFER_PTS (buf) = mux->last_ts;
/* do common init (flags and streamheaders) */ /* do common init (flags and streamheaders) */
new_packet_common_init (mux, buf, map.data + offset, map.size); new_packet_common_init (mux, buf, map.data, map.size);
gst_buffer_unmap (buf, &map); gst_buffer_unmap (buf, &map);
/* all is meant for downstream, including any prefix */ return klass->output_packet (mux, buf, new_pcr);
if (offset)
return new_packet_m2ts (mux, buf, new_pcr);
else
basetsmux_collect_packet (mux, buf);
return TRUE;
} }
/* called when TsMux needs new packet to write into */ /* called when TsMux needs new packet to write into */
static void static void
alloc_packet_cb (GstBuffer ** _buf, void *user_data) alloc_packet_cb (GstBuffer ** buf, void *user_data)
{ {
BaseTsMux *mux = (BaseTsMux *) user_data; BaseTsMux *mux = (BaseTsMux *) user_data;
GstBuffer *buf; BaseTsMuxClass *klass = GST_BASE_TSMUX_GET_CLASS (mux);
gint offset = 0;
if (mux->m2ts_mode == TRUE) g_assert (klass->allocate_packet);
offset = 4;
buf = gst_buffer_new_and_alloc (NORMAL_TS_PACKET_LENGTH + offset); klass->allocate_packet (mux, buf);
gst_buffer_set_size (buf, NORMAL_TS_PACKET_LENGTH);
*_buf = buf;
} }
static void static void
@ -1962,8 +1822,7 @@ basetsmux_prepare_srcpad (BaseTsMux * mux)
gchar s_id[32]; gchar s_id[32];
GstCaps *caps = gst_caps_new_simple ("video/mpegts", GstCaps *caps = gst_caps_new_simple ("video/mpegts",
"systemstream", G_TYPE_BOOLEAN, TRUE, "systemstream", G_TYPE_BOOLEAN, TRUE,
"packetsize", G_TYPE_INT, "packetsize", G_TYPE_INT, mux->packet_size,
(mux->m2ts_mode ? M2TS_PACKET_LENGTH : NORMAL_TS_PACKET_LENGTH),
NULL); NULL);
/* stream-start (FIXME: create id based on input ids) */ /* stream-start (FIXME: create id based on input ids) */
@ -2052,3 +1911,15 @@ basetsmux_default_create_ts_mux (BaseTsMux * mux)
return tsmux; return tsmux;
} }
void
gst_base_tsmux_set_packet_size (BaseTsMux * mux, gsize size)
{
mux->packet_size = size;
}
void
gst_base_tsmux_set_automatic_alignment (BaseTsMux * mux, gsize alignment)
{
mux->automatic_alignment = alignment;
}

View file

@ -134,7 +134,6 @@ struct BaseTsMux {
GHashTable *programs; GHashTable *programs;
/* properties */ /* properties */
gboolean m2ts_mode;
GstStructure *prog_map; GstStructure *prog_map;
guint pat_interval; guint pat_interval;
guint pmt_interval; guint pmt_interval;
@ -155,12 +154,8 @@ struct BaseTsMux {
gboolean is_header; gboolean is_header;
GstClockTime last_ts; GstClockTime last_ts;
/* m2ts specific */ gsize packet_size;
gint64 previous_pcr; gsize automatic_alignment;
gint64 previous_offset;
gint64 pcr_rate_num;
gint64 pcr_rate_den;
GstAdapter *adapter;
/* output buffer aggregation */ /* output buffer aggregation */
GstAdapter *out_adapter; GstAdapter *out_adapter;
@ -183,8 +178,15 @@ struct BaseTsMuxClass {
TsMux * (*create_ts_mux) (BaseTsMux *mux); TsMux * (*create_ts_mux) (BaseTsMux *mux);
guint (*handle_media_type) (BaseTsMux *mux, const gchar *media_type, BaseTsPadData * ts_data); guint (*handle_media_type) (BaseTsMux *mux, const gchar *media_type, BaseTsPadData * ts_data);
void (*allocate_packet) (BaseTsMux *mux, GstBuffer **buffer);
gboolean (*output_packet) (BaseTsMux *mux, GstBuffer *buffer, gint64 new_pcr);
void (*reset) (BaseTsMux *mux);
gboolean (*drain) (BaseTsMux *mux);
}; };
void gst_base_tsmux_set_packet_size (BaseTsMux *mux, gsize size);
void gst_base_tsmux_set_automatic_alignment (BaseTsMux *mux, gsize alignment);
struct BaseTsPadData { struct BaseTsPadData {
/* parent */ /* parent */
GstCollectData collect; GstCollectData collect;

View file

@ -84,6 +84,14 @@
#include "mpegtsmux.h" #include "mpegtsmux.h"
#define MPEGTSMUX_DEFAULT_M2TS FALSE
enum
{
PROP_0,
PROP_M2TS_MODE,
};
static GstStaticPadTemplate mpegtsmux_sink_factory = static GstStaticPadTemplate mpegtsmux_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink_%d", GST_STATIC_PAD_TEMPLATE ("sink_%d",
GST_PAD_SINK, GST_PAD_SINK,
@ -135,14 +143,242 @@ GST_DEBUG_CATEGORY (mpegtsmux_debug);
G_DEFINE_TYPE (MpegTsMux, mpegtsmux, GST_TYPE_BASE_TSMUX); G_DEFINE_TYPE (MpegTsMux, mpegtsmux, GST_TYPE_BASE_TSMUX);
#define parent_class mpegtsmux_parent_class #define parent_class mpegtsmux_parent_class
static void
gst_mpegtsmux_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
MpegTsMux *mux = GST_MPEG_TSMUX (object);
switch (prop_id) {
case PROP_M2TS_MODE:
/* set incase if the output stream need to be of 192 bytes */
mux->m2ts_mode = g_value_get_boolean (value);
gst_base_tsmux_set_packet_size (GST_BASE_TSMUX (mux),
mux->m2ts_mode ? M2TS_PACKET_LENGTH : NORMAL_TS_PACKET_LENGTH);
gst_base_tsmux_set_automatic_alignment (GST_BASE_TSMUX (mux),
mux->m2ts_mode ? 32 : 0);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_mpegtsmux_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
MpegTsMux *mux = GST_MPEG_TSMUX (object);
switch (prop_id) {
case PROP_M2TS_MODE:
g_value_set_boolean (value, mux->m2ts_mode);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_mpegtsmux_allocate_packet (BaseTsMux * mux, GstBuffer ** buffer)
{
((BaseTsMuxClass *) parent_class)->allocate_packet (mux, buffer);
gst_buffer_set_size (*buffer, NORMAL_TS_PACKET_LENGTH);
}
static gboolean
new_packet_m2ts (MpegTsMux * mux, GstBuffer * buf, gint64 new_pcr)
{
GstBuffer *out_buf;
int chunk_bytes;
GstMapInfo map;
GST_LOG_OBJECT (mux, "Have buffer %p with new_pcr=%" G_GINT64_FORMAT,
buf, new_pcr);
chunk_bytes = gst_adapter_available (mux->adapter);
if (G_LIKELY (buf)) {
if (new_pcr < 0) {
/* If there is no pcr in current ts packet then just add the packet
to the adapter for later output when we see a PCR */
GST_LOG_OBJECT (mux, "Accumulating non-PCR packet");
gst_adapter_push (mux->adapter, buf);
goto exit;
}
/* no first interpolation point yet, then this is the one,
* otherwise it is the second interpolation point */
if (mux->previous_pcr < 0 && chunk_bytes) {
mux->previous_pcr = new_pcr;
mux->previous_offset = chunk_bytes;
GST_LOG_OBJECT (mux, "Accumulating non-PCR packet");
gst_adapter_push (mux->adapter, buf);
goto exit;
}
} else {
g_assert (new_pcr == -1);
}
/* interpolate if needed, and 2 points available */
if (chunk_bytes && (new_pcr != mux->previous_pcr)) {
gint64 offset = 0;
GST_LOG_OBJECT (mux, "Processing pending packets; "
"previous pcr %" G_GINT64_FORMAT ", previous offset %d, "
"current pcr %" G_GINT64_FORMAT ", current offset %d",
mux->previous_pcr, (gint) mux->previous_offset,
new_pcr, (gint) chunk_bytes);
g_assert (chunk_bytes > mux->previous_offset);
/* if draining, use previous rate */
if (G_LIKELY (new_pcr > 0)) {
mux->pcr_rate_num = new_pcr - mux->previous_pcr;
mux->pcr_rate_den = chunk_bytes - mux->previous_offset;
}
while (offset < chunk_bytes) {
guint64 cur_pcr, ts;
/* Loop, pulling packets of the adapter, updating their 4 byte
* timestamp header and pushing */
/* interpolate PCR */
if (G_LIKELY (offset >= mux->previous_offset))
cur_pcr = mux->previous_pcr +
gst_util_uint64_scale (offset - mux->previous_offset,
mux->pcr_rate_num, mux->pcr_rate_den);
else
cur_pcr = mux->previous_pcr -
gst_util_uint64_scale (mux->previous_offset - offset,
mux->pcr_rate_num, mux->pcr_rate_den);
/* FIXME: what about DTS here? */
ts = gst_adapter_prev_pts (mux->adapter, NULL);
out_buf = gst_adapter_take_buffer (mux->adapter, M2TS_PACKET_LENGTH);
g_assert (out_buf);
offset += M2TS_PACKET_LENGTH;
GST_BUFFER_PTS (out_buf) = ts;
gst_buffer_map (out_buf, &map, GST_MAP_WRITE);
/* The header is the bottom 30 bits of the PCR, apparently not
* encoded into base + ext as in the packets themselves */
GST_WRITE_UINT32_BE (map.data, cur_pcr & 0x3FFFFFFF);
gst_buffer_unmap (out_buf, &map);
GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, cur_pcr);
((BaseTsMuxClass *) parent_class)->output_packet (GST_BASE_TSMUX (mux),
out_buf, -1);
}
}
if (G_UNLIKELY (!buf))
goto exit;
gst_buffer_map (buf, &map, GST_MAP_WRITE);
/* Finally, output the passed in packet */
/* Only write the bottom 30 bits of the PCR */
GST_WRITE_UINT32_BE (map.data, new_pcr & 0x3FFFFFFF);
gst_buffer_unmap (buf, &map);
GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, new_pcr);
((BaseTsMuxClass *) parent_class)->output_packet (GST_BASE_TSMUX (mux), buf,
-1);
if (new_pcr != mux->previous_pcr) {
mux->previous_pcr = new_pcr;
mux->previous_offset = -M2TS_PACKET_LENGTH;
}
exit:
return TRUE;
}
static gboolean
gst_mpegtsmux_output_packet (BaseTsMux * base_tsmux, GstBuffer * buffer,
gint64 new_pcr)
{
MpegTsMux *mux = (MpegTsMux *) base_tsmux;
GstMapInfo map;
if (!mux->m2ts_mode)
return ((BaseTsMuxClass *) parent_class)->output_packet (base_tsmux, buffer,
new_pcr);
gst_buffer_set_size (buffer, M2TS_PACKET_LENGTH);
gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
/* there should be a better way to do this */
memmove (map.data + 4, map.data, map.size - 4);
gst_buffer_unmap (buffer, &map);
return new_packet_m2ts (mux, buffer, new_pcr);
}
static void
gst_mpegtsmux_reset (BaseTsMux * base_tsmux)
{
MpegTsMux *mux = (MpegTsMux *) base_tsmux;
if (mux->adapter)
gst_adapter_clear (mux->adapter);
mux->previous_pcr = -1;
mux->previous_offset = 0;
mux->pcr_rate_num = mux->pcr_rate_den = 1;
}
static void
gst_mpegtsmux_dispose (GObject * object)
{
MpegTsMux *mux = (MpegTsMux *) object;
if (mux->adapter) {
g_object_unref (mux->adapter);
mux->adapter = NULL;
}
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
}
static gboolean
gst_mpegtsmux_drain (BaseTsMux * mux)
{
return new_packet_m2ts ((MpegTsMux *) mux, NULL, -1);
}
static void static void
mpegtsmux_class_init (MpegTsMuxClass * klass) mpegtsmux_class_init (MpegTsMuxClass * klass)
{ {
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
BaseTsMuxClass *base_tsmux_class = (BaseTsMuxClass *) klass;
GST_DEBUG_CATEGORY_INIT (mpegtsmux_debug, "mpegtsmux", 0, GST_DEBUG_CATEGORY_INIT (mpegtsmux_debug, "mpegtsmux", 0,
"MPEG Transport Stream muxer"); "MPEG Transport Stream muxer");
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mpegtsmux_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_mpegtsmux_get_property);
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_mpegtsmux_dispose);
base_tsmux_class->allocate_packet =
GST_DEBUG_FUNCPTR (gst_mpegtsmux_allocate_packet);
base_tsmux_class->output_packet =
GST_DEBUG_FUNCPTR (gst_mpegtsmux_output_packet);
base_tsmux_class->reset = GST_DEBUG_FUNCPTR (gst_mpegtsmux_reset);
base_tsmux_class->drain = GST_DEBUG_FUNCPTR (gst_mpegtsmux_drain);
gst_element_class_set_static_metadata (gstelement_class, gst_element_class_set_static_metadata (gstelement_class,
"MPEG Transport Stream Muxer", "Codec/Muxer", "MPEG Transport Stream Muxer", "Codec/Muxer",
"Multiplexes media streams into an MPEG Transport Stream", "Multiplexes media streams into an MPEG Transport Stream",
@ -153,9 +389,17 @@ mpegtsmux_class_init (MpegTsMuxClass * klass)
gst_element_class_add_static_pad_template (gstelement_class, gst_element_class_add_static_pad_template (gstelement_class,
&mpegtsmux_src_factory); &mpegtsmux_src_factory);
g_object_class_install_property (gobject_class, PROP_M2TS_MODE,
g_param_spec_boolean ("m2ts-mode", "M2TS(192 bytes) Mode",
"Set to TRUE to output Blu-Ray disc format with 192 byte packets. "
"FALSE for standard TS format with 188 byte packets.",
MPEGTSMUX_DEFAULT_M2TS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
} }
static void static void
mpegtsmux_init (MpegTsMux * mux) mpegtsmux_init (MpegTsMux * mux)
{ {
mux->m2ts_mode = MPEGTSMUX_DEFAULT_M2TS;
mux->adapter = gst_adapter_new ();
} }

View file

@ -102,6 +102,16 @@ typedef struct MpegTsPadData MpegTsPadData;
struct MpegTsMux { struct MpegTsMux {
BaseTsMux parent; BaseTsMux parent;
/* Properties */
gboolean m2ts_mode;
/* m2ts specific */
gint64 previous_pcr;
gint64 previous_offset;
gint64 pcr_rate_num;
gint64 pcr_rate_den;
GstAdapter *adapter;
}; };
struct MpegTsMuxClass { struct MpegTsMuxClass {