mpegtsmux: port to 0.11

https://bugzilla.gnome.org/show_bug.cgi?id=678130
This commit is contained in:
Matej Knopp 2012-06-14 14:49:55 -07:00 committed by Mark Nauwelaerts
parent 830e8b6ec6
commit 6229305d7b
9 changed files with 264 additions and 555 deletions

View file

@ -315,7 +315,7 @@ GST_PLUGINS_NONPORTED=" aiff \
dccp faceoverlay festival \ dccp faceoverlay festival \
fieldanalysis freeverb freeze frei0r \ fieldanalysis freeverb freeze frei0r \
hdvparse id3tag inter interlace ivfparse jpegformat jp2kdecimator \ hdvparse id3tag inter interlace ivfparse jpegformat jp2kdecimator \
kate liveadder librfb mpegtsmux \ kate liveadder librfb \
mpegpsmux mve mxf mythtv nsf nuvdemux \ mpegpsmux mve mxf mythtv nsf nuvdemux \
patchdetect pnm real \ patchdetect pnm real \
sdi siren speed subenc stereo tta videofilters \ sdi siren speed subenc stereo tta videofilters \

View file

@ -4,7 +4,6 @@ SUBDIRS = tsmux
libgstmpegtsmux_la_SOURCES = \ libgstmpegtsmux_la_SOURCES = \
mpegtsmux.c \ mpegtsmux.c \
mpegtsmux_h264.c \
mpegtsmux_aac.c mpegtsmux_aac.c
libgstmpegtsmux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) libgstmpegtsmux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
@ -15,5 +14,4 @@ libgstmpegtsmux_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = \ noinst_HEADERS = \
mpegtsmux.h \ mpegtsmux.h \
mpegtsmux_h264.h \
mpegtsmux_aac.h mpegtsmux_aac.h

View file

@ -96,7 +96,6 @@
#include "mpegtsmux.h" #include "mpegtsmux.h"
#include "mpegtsmux_h264.h"
#include "mpegtsmux_aac.h" #include "mpegtsmux_aac.h"
GST_DEBUG_CATEGORY (mpegtsmux_debug); GST_DEBUG_CATEGORY (mpegtsmux_debug);
@ -162,64 +161,83 @@ static gboolean new_packet_m2ts (MpegTsMux * mux, GstBuffer * buf,
gint64 new_pcr); gint64 new_pcr);
static void mpegtsdemux_prepare_srcpad (MpegTsMux * mux); static void mpegtsdemux_prepare_srcpad (MpegTsMux * mux);
static GstFlowReturn mpegtsmux_collected (GstCollectPads2 * pads, static GstFlowReturn mpegtsmux_collected (GstCollectPads * pads,
MpegTsMux * mux); MpegTsMux * mux);
static gboolean mpegtsmux_sink_event (GstCollectPads2 * pads, static gboolean mpegtsmux_sink_event (GstCollectPads * pads,
GstCollectData2 * data, GstEvent * event, gpointer user_data); GstCollectData * data, GstEvent * event, gpointer user_data);
static GstPad *mpegtsmux_request_new_pad (GstElement * element, static GstPad *mpegtsmux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name); GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
static void mpegtsmux_release_pad (GstElement * element, GstPad * pad); static void mpegtsmux_release_pad (GstElement * element, GstPad * pad);
static GstStateChangeReturn mpegtsmux_change_state (GstElement * element, static GstStateChangeReturn mpegtsmux_change_state (GstElement * element,
GstStateChange transition); GstStateChange transition);
static void mpegtsdemux_set_header_on_caps (MpegTsMux * mux); static void mpegtsdemux_set_header_on_caps (MpegTsMux * mux);
static gboolean mpegtsmux_src_event (GstPad * pad, GstEvent * event); static gboolean mpegtsmux_src_event (GstPad * pad, GstObject * parent,
GstEvent * event);
#if 0
static void mpegtsmux_set_index (GstElement * element, GstIndex * index); static void mpegtsmux_set_index (GstElement * element, GstIndex * index);
static GstIndex *mpegtsmux_get_index (GstElement * element); static GstIndex *mpegtsmux_get_index (GstElement * element);
static GstFormat pts_format; static GstFormat pts_format;
static GstFormat spn_format; static GstFormat spn_format;
#endif
GST_BOILERPLATE (MpegTsMux, mpegtsmux, GstElement, GST_TYPE_ELEMENT); typedef struct
{
GstMapInfo map_info;
GstBuffer *buffer;
} StreamData;
G_DEFINE_TYPE (MpegTsMux, mpegtsmux, GST_TYPE_ELEMENT)
static StreamData *stream_data_new (GstBuffer * buffer)
{
StreamData *res = g_new (StreamData, 1);
res->buffer = buffer;
gst_buffer_map (buffer, &(res->map_info), GST_MAP_READ);
return res;
}
static void static void
mpegtsmux_base_init (gpointer g_class) stream_data_free (StreamData * data)
{ {
if (data) {
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); gst_buffer_unmap (data->buffer, &data->map_info);
gst_buffer_unref (data->buffer);
gst_element_class_add_static_pad_template (element_class, g_free (data);
&mpegtsmux_sink_factory); }
gst_element_class_add_static_pad_template (element_class,
&mpegtsmux_src_factory);
gst_element_class_set_details_simple (element_class,
"MPEG Transport Stream Muxer", "Codec/Muxer",
"Multiplexes media streams into an MPEG Transport Stream",
"Fluendo <contact@fluendo.com>");
pts_format =
gst_format_register ("PTS", "MPEG System Presentation Time Stamp");
spn_format = gst_format_register ("SPN", "Source Packet Number");
} }
#define parent_class mpegtsmux_parent_class
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); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&mpegtsmux_sink_factory));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&mpegtsmux_src_factory));
gst_element_class_set_details_simple (gstelement_class,
"MPEG Transport Stream Muxer", "Codec/Muxer",
"Multiplexes media streams into an MPEG Transport Stream",
"Fluendo <contact@fluendo.com>");
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mpegtsmux_set_property); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mpegtsmux_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_mpegtsmux_get_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_mpegtsmux_get_property);
gobject_class->dispose = mpegtsmux_dispose; gobject_class->dispose = mpegtsmux_dispose;
gstelement_class->request_new_pad = gstelement_class->request_new_pad = mpegtsmux_request_new_pad;
GST_DEBUG_FUNCPTR (mpegtsmux_request_new_pad); gstelement_class->release_pad = mpegtsmux_release_pad;
gstelement_class->release_pad = GST_DEBUG_FUNCPTR (mpegtsmux_release_pad); gstelement_class->change_state = mpegtsmux_change_state;
gstelement_class->change_state = GST_DEBUG_FUNCPTR (mpegtsmux_change_state);
#if 0
gstelement_class->set_index = GST_DEBUG_FUNCPTR (mpegtsmux_set_index); gstelement_class->set_index = GST_DEBUG_FUNCPTR (mpegtsmux_set_index);
gstelement_class->get_index = GST_DEBUG_FUNCPTR (mpegtsmux_get_index); gstelement_class->get_index = GST_DEBUG_FUNCPTR (mpegtsmux_get_index);
#endif
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PROG_MAP, g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PROG_MAP,
g_param_spec_boxed ("prog-map", "Program map", g_param_spec_boxed ("prog-map", "Program map",
@ -253,21 +271,26 @@ mpegtsmux_class_init (MpegTsMuxClass * klass)
} }
static void static void
mpegtsmux_init (MpegTsMux * mux, MpegTsMuxClass * g_class) mpegtsmux_init (MpegTsMux * mux)
{ {
mux->srcpad = mux->srcpad =
gst_pad_new_from_static_template (&mpegtsmux_src_factory, "src"); gst_pad_new_from_static_template (&mpegtsmux_src_factory, "src");
gst_pad_use_fixed_caps (mux->srcpad); gst_pad_use_fixed_caps (mux->srcpad);
gst_pad_set_event_function (mux->srcpad, mpegtsmux_src_event); gst_pad_set_event_function (mux->srcpad,
GST_DEBUG_FUNCPTR (mpegtsmux_src_event));
gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad); gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
mux->collect = gst_collect_pads2_new (); mux->collect = gst_collect_pads_new ();
gst_collect_pads2_set_function (mux->collect, gst_collect_pads_set_function (mux->collect,
(GstCollectPads2Function) GST_DEBUG_FUNCPTR (mpegtsmux_collected), mux); (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (mpegtsmux_collected), mux);
gst_collect_pads2_set_event_function (mux->collect,
(GstCollectPads2EventFunction) GST_DEBUG_FUNCPTR (mpegtsmux_sink_event), gst_collect_pads_set_event_function (mux->collect,
(GstCollectPadsEventFunction) GST_DEBUG_FUNCPTR (mpegtsmux_sink_event),
mux); mux);
mux->tsmux = tsmux_new ();
tsmux_set_write_func (mux->tsmux, new_packet_cb, mux);
mux->adapter = gst_adapter_new (); mux->adapter = gst_adapter_new ();
mux->out_adapter = gst_adapter_new (); mux->out_adapter = gst_adapter_new ();
@ -286,11 +309,15 @@ static void
mpegtsmux_pad_reset (MpegTsPadData * pad_data) mpegtsmux_pad_reset (MpegTsPadData * pad_data)
{ {
pad_data->pid = 0; pad_data->pid = 0;
pad_data->last_ts = GST_CLOCK_TIME_NONE; pad_data->last_pts = GST_CLOCK_TIME_NONE;
pad_data->cur_ts = GST_CLOCK_TIME_NONE; pad_data->last_dts = GST_CLOCK_TIME_NONE;
pad_data->cur_pts = GST_CLOCK_TIME_NONE;
pad_data->cur_dts = GST_CLOCK_TIME_NONE;
pad_data->prog_id = -1; pad_data->prog_id = -1;
pad_data->eos = FALSE; pad_data->eos = FALSE;
#if 0
pad_data->element_index_writer_id = -1; pad_data->element_index_writer_id = -1;
#endif
if (pad_data->free_func) if (pad_data->free_func)
pad_data->free_func (pad_data->prepare_data); pad_data->free_func (pad_data->prepare_data);
@ -325,14 +352,14 @@ mpegtsmux_reset (MpegTsMux * mux, gboolean alloc)
mux->streamheader_sent = FALSE; mux->streamheader_sent = FALSE;
mux->force_key_unit_event = NULL; mux->force_key_unit_event = NULL;
mux->pending_key_unit_ts = GST_CLOCK_TIME_NONE; mux->pending_key_unit_ts = GST_CLOCK_TIME_NONE;
#if 0
mux->spn_count = 0; mux->spn_count = 0;
if (mux->element_index) { if (mux->element_index) {
gst_object_unref (mux->element_index); gst_object_unref (mux->element_index);
mux->element_index = NULL; mux->element_index = NULL;
} }
#endif
gst_adapter_clear (mux->adapter); gst_adapter_clear (mux->adapter);
gst_adapter_clear (mux->out_adapter); gst_adapter_clear (mux->out_adapter);
@ -357,10 +384,10 @@ mpegtsmux_reset (MpegTsMux * mux, gboolean alloc)
gst_event_replace (&mux->force_key_unit_event, NULL); gst_event_replace (&mux->force_key_unit_event, NULL);
gst_buffer_replace (&mux->out_buffer, NULL); gst_buffer_replace (&mux->out_buffer, NULL);
GST_COLLECT_PADS2_STREAM_LOCK (mux->collect); GST_COLLECT_PADS_STREAM_LOCK (mux->collect);
for (walk = mux->collect->data; walk != NULL; walk = g_slist_next (walk)) for (walk = mux->collect->data; walk != NULL; walk = g_slist_next (walk))
mpegtsmux_pad_reset ((MpegTsPadData *) walk->data); mpegtsmux_pad_reset ((MpegTsPadData *) walk->data);
GST_COLLECT_PADS2_STREAM_UNLOCK (mux->collect); GST_COLLECT_PADS_STREAM_UNLOCK (mux->collect);
if (alloc) { if (alloc) {
mux->tsmux = tsmux_new (); mux->tsmux = tsmux_new ();
@ -472,6 +499,7 @@ gst_mpegtsmux_get_property (GObject * object, guint prop_id,
} }
} }
#if 0
static void static void
mpegtsmux_set_index (GstElement * element, GstIndex * index) mpegtsmux_set_index (GstElement * element, GstIndex * index)
{ {
@ -501,12 +529,12 @@ mpegtsmux_get_index (GstElement * element)
return result; return result;
} }
#endif
static void static void
release_buffer_cb (guint8 * data, void *user_data) release_buffer_cb (guint8 * data, void *user_data)
{ {
GstBuffer *buf = (GstBuffer *) user_data; stream_data_free (user_data);
gst_buffer_unref (buf);
} }
static GstFlowReturn static GstFlowReturn
@ -522,12 +550,12 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
GstBuffer *codec_data = NULL; GstBuffer *codec_data = NULL;
pad = ts_data->collect.pad; pad = ts_data->collect.pad;
caps = gst_pad_get_negotiated_caps (pad); caps = gst_pad_get_current_caps (pad);
if (caps == NULL) if (caps == NULL)
goto not_negotiated; goto not_negotiated;
GST_DEBUG_OBJECT (pad, "Creating stream with PID 0x%04x for caps %" GST_DEBUG_OBJECT (pad, "Creating stream with PID 0x%04x for caps %"
GST_PTR_FORMAT, caps); GST_PTR_FORMAT, ts_data->pid, caps);
s = gst_caps_get_structure (caps, 0); s = gst_caps_get_structure (caps, 0);
g_return_val_if_fail (s != NULL, FALSE); g_return_val_if_fail (s != NULL, FALSE);
@ -547,16 +575,6 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
st = TSMUX_ST_PS_AUDIO_LPCM; st = TSMUX_ST_PS_AUDIO_LPCM;
} else if (strcmp (mt, "video/x-h264") == 0) { } else if (strcmp (mt, "video/x-h264") == 0) {
st = TSMUX_ST_VIDEO_H264; st = TSMUX_ST_VIDEO_H264;
/* Codec data contains SPS/PPS which need to go in stream for valid ES */
if (codec_data) {
GST_DEBUG_OBJECT (pad, "we have additional codec data (%d bytes)",
GST_BUFFER_SIZE (codec_data));
ts_data->codec_data = gst_buffer_ref (codec_data);
ts_data->prepare_func = mpegtsmux_prepare_h264;
ts_data->free_func = mpegtsmux_free_h264;
} else {
ts_data->codec_data = NULL;
}
} else if (strcmp (mt, "audio/mpeg") == 0) { } else if (strcmp (mt, "audio/mpeg") == 0) {
gint mpegversion; gint mpegversion;
@ -575,9 +593,10 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
case 4: case 4:
{ {
st = TSMUX_ST_AUDIO_AAC; st = TSMUX_ST_AUDIO_AAC;
if (codec_data) { if (codec_data) { /* TODO - Check stream format - codec data should only come with RAW stream */
GST_DEBUG_OBJECT (pad, "we have additional codec data (%d bytes)", GST_DEBUG_OBJECT (pad,
GST_BUFFER_SIZE (codec_data)); "we have additional codec data (%" G_GSIZE_FORMAT " bytes)",
gst_buffer_get_size (codec_data));
ts_data->codec_data = gst_buffer_ref (codec_data); ts_data->codec_data = gst_buffer_ref (codec_data);
ts_data->prepare_func = mpegtsmux_prepare_aac; ts_data->prepare_func = mpegtsmux_prepare_aac;
} else { } else {
@ -629,7 +648,7 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
ret = GST_FLOW_OK; ret = GST_FLOW_OK;
} }
#if 0
GST_OBJECT_LOCK (mux); GST_OBJECT_LOCK (mux);
if (mux->element_index) { if (mux->element_index) {
gboolean parsed = FALSE; gboolean parsed = FALSE;
@ -652,6 +671,7 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
} }
} }
GST_OBJECT_UNLOCK (mux); GST_OBJECT_UNLOCK (mux);
#endif
gst_caps_unref (caps); gst_caps_unref (caps);
return ret; return ret;
@ -674,7 +694,7 @@ mpegtsmux_create_streams (MpegTsMux * mux)
/* Create the streams */ /* Create the streams */
while (walk) { while (walk) {
GstCollectData2 *c_data = (GstCollectData2 *) walk->data; GstCollectData *c_data = (GstCollectData *) walk->data;
MpegTsPadData *ts_data = (MpegTsPadData *) walk->data; MpegTsPadData *ts_data = (MpegTsPadData *) walk->data;
gchar *name = NULL; gchar *name = NULL;
@ -743,15 +763,14 @@ mpegtsmux_choose_best_stream (MpegTsMux * mux)
GSList *walk; GSList *walk;
for (walk = mux->collect->data; walk != NULL; walk = g_slist_next (walk)) { for (walk = mux->collect->data; walk != NULL; walk = g_slist_next (walk)) {
GstCollectData2 *c_data = (GstCollectData2 *) walk->data; GstCollectData *c_data = (GstCollectData *) walk->data;
MpegTsPadData *ts_data = (MpegTsPadData *) walk->data; MpegTsPadData *ts_data = (MpegTsPadData *) walk->data;
if (ts_data->eos == FALSE) { if (ts_data->eos == FALSE) {
if (ts_data->queued_buf == NULL) { if (ts_data->queued_buf == NULL) {
GstBuffer *buf; GstBuffer *buf;
ts_data->queued_buf = buf = ts_data->queued_buf = buf = gst_collect_pads_pop (mux->collect, c_data);
gst_collect_pads2_pop (mux->collect, c_data);
if (buf != NULL) { if (buf != NULL) {
if (ts_data->prepare_func) { if (ts_data->prepare_func) {
@ -763,30 +782,40 @@ mpegtsmux_choose_best_stream (MpegTsMux * mux)
buf = ts_data->queued_buf; buf = ts_data->queued_buf;
} }
} }
if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) { if (GST_BUFFER_PTS (buf) != GST_CLOCK_TIME_NONE) {
/* Ignore timestamps that go backward for now. FIXME: Handle all
* incoming PTS */ ts_data->cur_pts = ts_data->last_pts =
if (ts_data->last_ts == GST_CLOCK_TIME_NONE || gst_segment_to_running_time (&c_data->segment,
ts_data->last_ts < GST_BUFFER_TIMESTAMP (buf)) { GST_FORMAT_TIME, GST_BUFFER_PTS (buf));
ts_data->cur_ts = ts_data->last_ts = } else
gst_segment_to_running_time (&c_data->segment, ts_data->cur_pts = GST_CLOCK_TIME_NONE;
GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf));
if (GST_BUFFER_DTS (buf) != GST_CLOCK_TIME_NONE) {
GstClockTime dts = gst_segment_to_running_time (&c_data->segment,
GST_FORMAT_TIME, GST_BUFFER_DTS (buf));
if (ts_data->last_dts == GST_CLOCK_TIME_NONE
|| ts_data->last_dts < dts) {
ts_data->cur_dts = ts_data->last_dts = dts;
} else { } else {
GST_DEBUG_OBJECT (mux, "Ignoring PTS that has gone backward"); GST_WARNING_OBJECT (mux,
"Got DTS that is going backward (%" GST_TIME_FORMAT " >= %"
GST_TIME_FORMAT "). You should provide valid monotone DTS.",
GST_TIME_ARGS (ts_data->last_dts), GST_TIME_ARGS (dts));
ts_data->cur_dts = GST_CLOCK_TIME_NONE;
} }
} else } else
ts_data->cur_ts = GST_CLOCK_TIME_NONE; ts_data->cur_dts = GST_CLOCK_TIME_NONE;
GST_DEBUG_OBJECT (mux, "Pulled buffer with ts %" GST_TIME_FORMAT GST_DEBUG_OBJECT (mux, "Pulled buffer with ts %" GST_TIME_FORMAT
" (uncorrected ts %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT " (uncorrected pts %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
") for PID 0x%04x", ") for PID 0x%04x",
GST_TIME_ARGS (ts_data->cur_ts), GST_TIME_ARGS (ts_data->cur_pts),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
GST_BUFFER_TIMESTAMP (buf), ts_data->pid); GST_BUFFER_TIMESTAMP (buf), ts_data->pid);
/* Choose a stream we've never seen a timestamp for to ensure /* Choose a stream we've never seen a timestamp for to ensure
* we push enough buffers from it to reach a timestamp */ * we push enough buffers from it to reach a timestamp */
if (ts_data->last_ts == GST_CLOCK_TIME_NONE) { if (ts_data->last_pts == GST_CLOCK_TIME_NONE) {
best = ts_data; best = ts_data;
} }
} else { } else {
@ -798,9 +827,9 @@ mpegtsmux_choose_best_stream (MpegTsMux * mux)
/* If we don't yet have a best pad, take this one, otherwise take /* If we don't yet have a best pad, take this one, otherwise take
* whichever has the oldest timestamp */ * whichever has the oldest timestamp */
if (best != NULL) { if (best != NULL) {
if (ts_data->last_ts != GST_CLOCK_TIME_NONE && if (ts_data->last_pts != GST_CLOCK_TIME_NONE &&
best->last_ts != GST_CLOCK_TIME_NONE && best->last_pts != GST_CLOCK_TIME_NONE &&
ts_data->last_ts < best->last_ts) { ts_data->last_pts < best->last_pts) {
best = ts_data; best = ts_data;
} }
} else { } else {
@ -812,14 +841,15 @@ mpegtsmux_choose_best_stream (MpegTsMux * mux)
return best; return best;
} }
#define COLLECT_DATA_PAD(collect_data) (((GstCollectData2 *)(collect_data))->pad) #define COLLECT_DATA_PAD(collect_data) (((GstCollectData *)(collect_data))->pad)
static gboolean static gboolean
mpegtsmux_sink_event (GstCollectPads2 * pads, GstCollectData2 * data, mpegtsmux_sink_event (GstCollectPads * pads, GstCollectData * data,
GstEvent * event, gpointer user_data) GstEvent * event, gpointer user_data)
{ {
MpegTsMux *mux = GST_MPEG_TSMUX (user_data); MpegTsMux *mux = GST_MPEG_TSMUX (user_data);
gboolean res = FALSE; gboolean res = FALSE;
gboolean forward = TRUE;
GstPad *pad; GstPad *pad;
pad = data->pad; pad = data->pad;
@ -835,6 +865,7 @@ mpegtsmux_sink_event (GstCollectPads2 * pads, GstCollectData2 * data,
goto out; goto out;
res = TRUE; res = TRUE;
forward = FALSE;
gst_video_event_parse_downstream_force_key_unit (event, gst_video_event_parse_downstream_force_key_unit (event,
&timestamp, &stream_time, &running_time, &all_headers, &count); &timestamp, &stream_time, &running_time, &all_headers, &count);
@ -861,14 +892,16 @@ mpegtsmux_sink_event (GstCollectPads2 * pads, GstCollectData2 * data,
} }
out: out:
if (res) if (!forward)
gst_event_unref (event); gst_event_unref (event);
else
res = gst_collect_pads_event_default (pads, data, event, FALSE);
return res; return res;
} }
static gboolean static gboolean
mpegtsmux_src_event (GstPad * pad, GstEvent * event) mpegtsmux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{ {
MpegTsMux *mux = GST_MPEG_TSMUX (gst_pad_get_parent (pad)); MpegTsMux *mux = GST_MPEG_TSMUX (gst_pad_get_parent (pad));
gboolean res = TRUE, forward = TRUE; gboolean res = TRUE, forward = TRUE;
@ -879,6 +912,7 @@ mpegtsmux_src_event (GstPad * pad, GstEvent * event)
GstIterator *iter; GstIterator *iter;
GstIteratorResult iter_ret; GstIteratorResult iter_ret;
GstPad *sinkpad; GstPad *sinkpad;
GValue sinkpad_value = G_VALUE_INIT;
GstClockTime running_time; GstClockTime running_time;
gboolean all_headers, done; gboolean all_headers, done;
guint count; guint count;
@ -906,7 +940,9 @@ mpegtsmux_src_event (GstPad * pad, GstEvent * event)
done = FALSE; done = FALSE;
while (!done) { while (!done) {
gboolean res = FALSE, tmp; gboolean res = FALSE, tmp;
iter_ret = gst_iterator_next (iter, (gpointer *) & sinkpad); g_value_reset (&sinkpad_value);
iter_ret = gst_iterator_next (iter, &sinkpad_value);
sinkpad = g_value_get_object (&sinkpad_value);
switch (iter_ret) { switch (iter_ret) {
case GST_ITERATOR_DONE: case GST_ITERATOR_DONE:
@ -928,6 +964,7 @@ mpegtsmux_src_event (GstPad * pad, GstEvent * event)
break; break;
} }
} }
g_value_reset (&sinkpad_value);
gst_iterator_free (iter); gst_iterator_free (iter);
break; break;
} }
@ -936,7 +973,7 @@ mpegtsmux_src_event (GstPad * pad, GstEvent * event)
} }
if (forward) if (forward)
res = gst_pad_event_default (pad, event); res = gst_pad_event_default (pad, parent, event);
else else
gst_event_unref (event); gst_event_unref (event);
@ -993,7 +1030,7 @@ out:
} }
static GstFlowReturn static GstFlowReturn
mpegtsmux_collected (GstCollectPads2 * pads, MpegTsMux * mux) mpegtsmux_collected (GstCollectPads * pads, MpegTsMux * mux)
{ {
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
MpegTsPadData *best = NULL; MpegTsPadData *best = NULL;
@ -1016,7 +1053,9 @@ mpegtsmux_collected (GstCollectPads2 * pads, MpegTsMux * mux)
TsMuxProgram *prog = best->prog; TsMuxProgram *prog = best->prog;
GstBuffer *buf = best->queued_buf; GstBuffer *buf = best->queued_buf;
gint64 pts = -1; gint64 pts = -1;
guint64 dts = -1;
gboolean delta = TRUE; gboolean delta = TRUE;
StreamData *stream_data;
if (prog == NULL) if (prog == NULL)
goto no_program; goto no_program;
@ -1072,14 +1111,21 @@ mpegtsmux_collected (GstCollectPads2 * pads, MpegTsMux * mux)
GST_DEBUG_OBJECT (COLLECT_DATA_PAD (best), GST_DEBUG_OBJECT (COLLECT_DATA_PAD (best),
"Chose stream for output (PID: 0x%04x)", best->pid); "Chose stream for output (PID: 0x%04x)", best->pid);
if (GST_CLOCK_TIME_IS_VALID (best->cur_ts)) { if (GST_CLOCK_TIME_IS_VALID (best->cur_pts)) {
pts = GSTTIME_TO_MPEGTIME (best->cur_ts); pts = GSTTIME_TO_MPEGTIME (best->cur_pts);
GST_DEBUG_OBJECT (mux, "Buffer has TS %" GST_TIME_FORMAT " pts %" GST_DEBUG_OBJECT (mux, "Buffer has PTS %" GST_TIME_FORMAT " pts %"
G_GINT64_FORMAT, GST_TIME_ARGS (best->cur_ts), pts); G_GINT64_FORMAT, GST_TIME_ARGS (best->cur_pts), pts);
}
if (GST_CLOCK_TIME_IS_VALID (best->cur_dts)) {
dts = GSTTIME_TO_MPEGTIME (best->cur_dts);
GST_DEBUG_OBJECT (mux, "Buffer has DTS %" GST_TIME_FORMAT " dts %"
G_GINT64_FORMAT, GST_TIME_ARGS (best->cur_dts), dts);
} }
if (best->stream->is_video_stream) { if (best->stream->is_video_stream) {
delta = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); delta = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
#if 0
GST_OBJECT_LOCK (mux); GST_OBJECT_LOCK (mux);
if (mux->element_index && !delta && best->element_index_writer_id != -1) { if (mux->element_index && !delta && best->element_index_writer_id != -1) {
gst_index_add_association (mux->element_index, gst_index_add_association (mux->element_index,
@ -1088,19 +1134,24 @@ mpegtsmux_collected (GstCollectPads2 * pads, MpegTsMux * mux)
pts_format, pts, NULL); pts_format, pts, NULL);
} }
GST_OBJECT_UNLOCK (mux); GST_OBJECT_UNLOCK (mux);
#endif
} }
GST_DEBUG_OBJECT (mux, "delta: %d", delta); GST_DEBUG_OBJECT (mux, "delta: %d", delta);
tsmux_stream_add_data (best->stream, GST_BUFFER_DATA (buf), stream_data = stream_data_new (buf);
GST_BUFFER_SIZE (buf), buf, pts, -1, !delta); tsmux_stream_add_data (best->stream, stream_data->map_info.data,
stream_data->map_info.size, stream_data, pts, dts, !delta);
/* outgoing ts follows ts of PCR program stream */ /* outgoing ts follows ts of PCR program stream */
if (prog->pcr_stream == best->stream) { if (prog->pcr_stream == best->stream) {
mux->last_ts = best->last_ts; /* prefer DTS if present for PCR as it should be monotone */
mux->last_ts =
GST_CLOCK_TIME_IS_VALID (best->last_dts) ? best->last_dts : best->
last_pts;
} }
mux->is_delta = delta; mux->is_delta = delta;
mux->last_size = GST_BUFFER_SIZE (buf); mux->last_size = stream_data->map_info.size;
while (tsmux_stream_bytes_in_buffer (best->stream) > 0) { while (tsmux_stream_bytes_in_buffer (best->stream) > 0) {
if (!tsmux_write_stream_packet (mux->tsmux, best->stream)) { if (!tsmux_write_stream_packet (mux->tsmux, best->stream)) {
/* Failed writing data for some reason. Set appropriate error */ /* Failed writing data for some reason. Set appropriate error */
@ -1139,8 +1190,8 @@ no_program:
} }
static GstPad * static GstPad *
mpegtsmux_request_new_pad (GstElement * element, mpegtsmux_request_new_pad (GstElement * element, GstPadTemplate * templ,
GstPadTemplate * templ, const gchar * name) const gchar * name, const GstCaps * caps)
{ {
MpegTsMux *mux = GST_MPEG_TSMUX (element); MpegTsMux *mux = GST_MPEG_TSMUX (element);
gint pid = -1; gint pid = -1;
@ -1160,8 +1211,8 @@ mpegtsmux_request_new_pad (GstElement * element,
g_free (pad_name); g_free (pad_name);
pad_data = (MpegTsPadData *) pad_data = (MpegTsPadData *)
gst_collect_pads2_add_pad_full (mux->collect, pad, sizeof (MpegTsPadData), gst_collect_pads_add_pad_full (mux->collect, pad, sizeof (MpegTsPadData),
(GstCollectData2DestroyNotify) (mpegtsmux_pad_reset), TRUE); (GstCollectDataDestroyNotify) (mpegtsmux_pad_reset), TRUE);
if (pad_data == NULL) if (pad_data == NULL)
goto pad_failure; goto pad_failure;
@ -1184,7 +1235,7 @@ could_not_add:
{ {
GST_ELEMENT_ERROR (element, STREAM, FAILED, GST_ELEMENT_ERROR (element, STREAM, FAILED,
("Internal data stream error."), ("Could not add pad to element")); ("Internal data stream error."), ("Could not add pad to element"));
gst_collect_pads2_remove_pad (mux->collect, pad); gst_collect_pads_remove_pad (mux->collect, pad);
gst_object_unref (pad); gst_object_unref (pad);
return NULL; return NULL;
} }
@ -1205,7 +1256,7 @@ mpegtsmux_release_pad (GstElement * element, GstPad * pad)
GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " being released", pad); GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " being released", pad);
if (mux->collect) { if (mux->collect) {
gst_collect_pads2_remove_pad (mux->collect, pad); gst_collect_pads_remove_pad (mux->collect, pad);
} }
/* chain up */ /* chain up */
@ -1227,7 +1278,7 @@ new_packet_common_init (MpegTsMux * mux, GstBuffer * buf, guint8 * data,
if (!buf) { if (!buf) {
hbuf = gst_buffer_new_and_alloc (len); hbuf = gst_buffer_new_and_alloc (len);
memcpy (GST_BUFFER_DATA (hbuf), data, len); gst_buffer_fill (hbuf, 0, data, len);
} else { } else {
hbuf = gst_buffer_copy (buf); hbuf = gst_buffer_copy (buf);
} }
@ -1239,10 +1290,6 @@ new_packet_common_init (MpegTsMux * mux, GstBuffer * buf, guint8 * data,
} }
if (buf) { if (buf) {
/* Set the caps on the buffer only after possibly setting the stream headers
* into the pad caps above */
gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad));
if (mux->is_delta) { if (mux->is_delta) {
GST_LOG_OBJECT (mux, "marking as delta unit"); GST_LOG_OBJECT (mux, "marking as delta unit");
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
@ -1287,7 +1334,7 @@ mpegtsmux_push_packets (MpegTsMux * mux, gboolean force)
buf = gst_adapter_take_buffer (mux->out_adapter, av - (av % align)); buf = gst_adapter_take_buffer (mux->out_adapter, av - (av % align));
g_assert (buf); g_assert (buf);
GST_BUFFER_TIMESTAMP (buf) = ts; GST_BUFFER_TIMESTAMP (buf) = ts;
gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad));
ret = gst_pad_push (mux->srcpad, buf); ret = gst_pad_push (mux->srcpad, buf);
av = av % align; av = av % align;
} }
@ -1296,19 +1343,22 @@ mpegtsmux_push_packets (MpegTsMux * mux, gboolean force)
guint8 *data; guint8 *data;
guint32 header; guint32 header;
gint dummy; gint dummy;
GstMapInfo map;
GST_LOG_OBJECT (mux, "handling %d leftover bytes", av); GST_LOG_OBJECT (mux, "handling %d leftover bytes", av);
buf = gst_buffer_new_and_alloc (align); buf = gst_buffer_new_and_alloc (align);
gst_buffer_map (buf, &map, GST_MAP_READ);
data = map.data;
ts = gst_adapter_prev_timestamp (mux->out_adapter, NULL); ts = gst_adapter_prev_timestamp (mux->out_adapter, NULL);
gst_adapter_copy (mux->out_adapter, GST_BUFFER_DATA (buf), 0, av);
gst_adapter_copy (mux->out_adapter, data, 0, av);
gst_adapter_clear (mux->out_adapter); gst_adapter_clear (mux->out_adapter);
GST_BUFFER_TIMESTAMP (buf) = ts; GST_BUFFER_TIMESTAMP (buf) = ts;
gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad));
data = GST_BUFFER_DATA (buf) + av; data += av;
header = GST_READ_UINT32_BE (data - packet_size); header = GST_READ_UINT32_BE (data - packet_size);
dummy = (GST_BUFFER_SIZE (buf) - av) / packet_size; dummy = (map.size - av) / packet_size;
GST_LOG_OBJECT (mux, "adding %d null packets", dummy); GST_LOG_OBJECT (mux, "adding %d null packets", dummy);
for (; dummy > 0; dummy--) { for (; dummy > 0; dummy--) {
@ -1332,6 +1382,8 @@ mpegtsmux_push_packets (MpegTsMux * mux, gboolean force)
data += packet_size; data += packet_size;
} }
gst_buffer_unmap (buf, &map);
ret = gst_pad_push (mux->srcpad, buf); ret = gst_pad_push (mux->srcpad, buf);
} }
@ -1341,7 +1393,8 @@ mpegtsmux_push_packets (MpegTsMux * mux, gboolean force)
static GstFlowReturn static GstFlowReturn
mpegtsmux_collect_packet (MpegTsMux * mux, GstBuffer * buf) mpegtsmux_collect_packet (MpegTsMux * mux, GstBuffer * buf)
{ {
GST_LOG_OBJECT (mux, "collecting packet size %d", GST_BUFFER_SIZE (buf)); GST_LOG_OBJECT (mux, "collecting packet size %" G_GSIZE_FORMAT,
gst_buffer_get_size (buf));
gst_adapter_push (mux->out_adapter, buf); gst_adapter_push (mux->out_adapter, buf);
return GST_FLOW_OK; return GST_FLOW_OK;
@ -1352,6 +1405,7 @@ new_packet_m2ts (MpegTsMux * mux, GstBuffer * buf, gint64 new_pcr)
{ {
GstBuffer *out_buf; GstBuffer *out_buf;
int chunk_bytes; int chunk_bytes;
GstMapInfo map;
GST_LOG_OBJECT (mux, "Have buffer %p with new_pcr=%" G_GINT64_FORMAT, GST_LOG_OBJECT (mux, "Have buffer %p with new_pcr=%" G_GINT64_FORMAT,
buf, new_pcr); buf, new_pcr);
@ -1418,12 +1472,14 @@ new_packet_m2ts (MpegTsMux * mux, GstBuffer * buf, gint64 new_pcr)
g_assert (out_buf); g_assert (out_buf);
offset += M2TS_PACKET_LENGTH; offset += M2TS_PACKET_LENGTH;
gst_buffer_set_caps (out_buf, GST_PAD_CAPS (mux->srcpad));
GST_BUFFER_TIMESTAMP (out_buf) = ts; GST_BUFFER_TIMESTAMP (out_buf) = ts;
gst_buffer_map (out_buf, &map, GST_MAP_WRITE);
/* The header is the bottom 30 bits of the PCR, apparently not /* The header is the bottom 30 bits of the PCR, apparently not
* encoded into base + ext as in the packets themselves */ * encoded into base + ext as in the packets themselves */
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (out_buf), cur_pcr & 0x3FFFFFFF); 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 %" GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, cur_pcr); G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, cur_pcr);
@ -1434,9 +1490,13 @@ new_packet_m2ts (MpegTsMux * mux, GstBuffer * buf, gint64 new_pcr)
if (G_UNLIKELY (!buf)) if (G_UNLIKELY (!buf))
goto exit; goto exit;
gst_buffer_map (buf, &map, GST_MAP_WRITE);
/* Finally, output the passed in packet */ /* Finally, output the passed in packet */
/* Only write the bottom 30 bits of the PCR */ /* Only write the bottom 30 bits of the PCR */
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), new_pcr & 0x3FFFFFFF); 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 %" GST_LOG_OBJECT (mux, "Outputting a packet of length %d PCR %"
G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, new_pcr); G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, new_pcr);
@ -1458,20 +1518,30 @@ new_packet_cb (GstBuffer * buf, void *user_data, gint64 new_pcr)
{ {
MpegTsMux *mux = (MpegTsMux *) user_data; MpegTsMux *mux = (MpegTsMux *) user_data;
gint offset = 0; gint offset = 0;
GstMapInfo map;
#if 0
GST_LOG_OBJECT (mux, "handling packet %d", mux->spn_count); GST_LOG_OBJECT (mux, "handling packet %d", mux->spn_count);
mux->spn_count++; mux->spn_count++;
#endif
offset = GST_BUFFER_DATA (buf) - GST_BUFFER_MALLOCDATA (buf); if (mux->m2ts_mode) {
GST_BUFFER_TIMESTAMP (buf) = mux->last_ts; offset = 4;
gst_buffer_set_size (buf, NORMAL_TS_PACKET_LENGTH + offset);
}
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;
/* do common init (flags and streamheaders) */ /* do common init (flags and streamheaders) */
new_packet_common_init (mux, buf, new_packet_common_init (mux, buf, map.data + offset, map.size);
GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
/* all is meant for downstream, including any prefix */ /* all is meant for downstream, including any prefix */
GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf);
GST_BUFFER_SIZE (buf) += offset;
if (offset) if (offset)
return new_packet_m2ts (mux, buf, new_pcr); return new_packet_m2ts (mux, buf, new_pcr);
else else
@ -1491,11 +1561,8 @@ alloc_packet_cb (GstBuffer ** _buf, void *user_data)
if (mux->m2ts_mode == TRUE) if (mux->m2ts_mode == TRUE)
offset = 4; offset = 4;
/* TODO might be even more efficient to avoid later memcpy
* if these are subbuffer from a larger buffer or so */
buf = gst_buffer_new_and_alloc (NORMAL_TS_PACKET_LENGTH + offset); buf = gst_buffer_new_and_alloc (NORMAL_TS_PACKET_LENGTH + offset);
GST_BUFFER_DATA (buf) += offset; gst_buffer_set_size (buf, NORMAL_TS_PACKET_LENGTH);
GST_BUFFER_SIZE (buf) -= offset;
*_buf = buf; *_buf = buf;
} }
@ -1510,7 +1577,7 @@ mpegtsdemux_set_header_on_caps (MpegTsMux * mux)
GstCaps *caps; GstCaps *caps;
GList *sh; GList *sh;
caps = gst_caps_copy (GST_PAD_CAPS (mux->srcpad)); caps = gst_caps_make_writable (gst_pad_get_current_caps (mux->srcpad));
structure = gst_caps_get_structure (caps, 0); structure = gst_caps_get_structure (caps, 0);
g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&array, GST_TYPE_ARRAY);
@ -1518,7 +1585,6 @@ mpegtsdemux_set_header_on_caps (MpegTsMux * mux)
sh = mux->streamheader; sh = mux->streamheader;
while (sh) { while (sh) {
buf = sh->data; buf = sh->data;
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
g_value_init (&value, GST_TYPE_BUFFER); g_value_init (&value, GST_TYPE_BUFFER);
gst_value_take_buffer (&value, buf); gst_value_take_buffer (&value, buf);
gst_value_array_append_value (&array, &value); gst_value_array_append_value (&array, &value);
@ -1538,15 +1604,23 @@ mpegtsdemux_set_header_on_caps (MpegTsMux * mux)
static void static void
mpegtsdemux_prepare_srcpad (MpegTsMux * mux) mpegtsdemux_prepare_srcpad (MpegTsMux * mux)
{ {
GstSegment seg;
/* we are not going to seek */ /* we are not going to seek */
GstEvent *new_seg = GstEvent *new_seg;
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0);
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->m2ts_mode ? M2TS_PACKET_LENGTH : NORMAL_TS_PACKET_LENGTH), (mux->m2ts_mode ? M2TS_PACKET_LENGTH : NORMAL_TS_PACKET_LENGTH),
NULL); NULL);
seg.rate = 1.0;
seg.format = GST_FORMAT_TIME;
seg.start = 0;
seg.stop = -1;
seg.position = 0;
new_seg = gst_event_new_segment (&seg);
/* Set caps on src pad from our template and push new segment */ /* Set caps on src pad from our template and push new segment */
gst_pad_set_caps (mux->srcpad, caps); gst_pad_set_caps (mux->srcpad, caps);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -1566,12 +1640,12 @@ mpegtsmux_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_NULL_TO_READY: case GST_STATE_CHANGE_NULL_TO_READY:
break; break;
case GST_STATE_CHANGE_READY_TO_PAUSED: case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_collect_pads2_start (mux->collect); gst_collect_pads_start (mux->collect);
break; break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING: case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break; break;
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_collect_pads2_stop (mux->collect); gst_collect_pads_stop (mux->collect);
break; break;
case GST_STATE_CHANGE_READY_TO_NULL: case GST_STATE_CHANGE_READY_TO_NULL:
break; break;

View file

@ -84,7 +84,7 @@
#define __MPEGTSMUX_H__ #define __MPEGTSMUX_H__
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstcollectpads2.h> #include <gst/base/gstcollectpads.h>
#include <gst/base/gstadapter.h> #include <gst/base/gstadapter.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -129,7 +129,7 @@ struct MpegTsMux {
GstPad *srcpad; GstPad *srcpad;
GstCollectPads2 *collect; GstCollectPads *collect;
TsMux *tsmux; TsMux *tsmux;
TsMuxProgram *programs[MAX_PROG_NUMBER]; TsMuxProgram *programs[MAX_PROG_NUMBER];
@ -166,9 +166,11 @@ struct MpegTsMux {
gint out_offset; gint out_offset;
gint last_size; gint last_size;
#if 0
/* SPN/PTS index handling */ /* SPN/PTS index handling */
GstIndex *element_index; GstIndex *element_index;
gint spn_count; gint spn_count;
#endif
}; };
struct MpegTsMuxClass { struct MpegTsMuxClass {
@ -179,7 +181,7 @@ struct MpegTsMuxClass {
struct MpegTsPadData { struct MpegTsPadData {
/* parent */ /* parent */
GstCollectData2 collect; GstCollectData collect;
gint pid; gint pid;
TsMuxStream *stream; TsMuxStream *stream;
@ -187,12 +189,17 @@ struct MpegTsPadData {
/* currently pulled buffer */ /* currently pulled buffer */
GstBuffer *queued_buf; GstBuffer *queued_buf;
/* adjusted TS for the pulled buffer */ /* adjusted TS for the pulled buffer */
GstClockTime cur_ts; GstClockTime cur_pts;
GstClockTime cur_dts;
/* most recent valid TS for this stream */ /* most recent valid TS for this stream */
GstClockTime last_ts; GstClockTime last_pts;
GstClockTime last_dts;
#if 0
/* (optional) index writing */ /* (optional) index writing */
gint element_index_writer_id; gint element_index_writer_id;
#endif
/* optional codec data available in the caps */ /* optional codec data available in the caps */
GstBuffer *codec_data; GstBuffer *codec_data;

View file

@ -93,23 +93,26 @@ GstBuffer *
mpegtsmux_prepare_aac (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux) mpegtsmux_prepare_aac (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux)
{ {
guint8 adts_header[7] = { 0, }; guint8 adts_header[7] = { 0, };
GstBuffer *out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) + 7); gsize out_size = gst_buffer_get_size (buf) + 7;
GstBuffer *out_buf = gst_buffer_new_and_alloc (out_size);
gsize out_offset = 0; gsize out_offset = 0;
guint8 rate_idx = 0, channels = 0, obj_type = 0; guint8 rate_idx = 0, channels = 0, obj_type = 0;
GstMapInfo codec_data_map;
GstMapInfo buf_map;
GST_DEBUG_OBJECT (mux, "Preparing AAC buffer for output"); GST_DEBUG_OBJECT (mux, "Preparing AAC buffer for output");
/* We want the same metadata */ gst_buffer_copy_into (out_buf, buf,
gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL); GST_BUFFER_COPY_METADATA | GST_BUFFER_COPY_TIMESTAMPS, 0, 0);
gst_buffer_map (data->codec_data, &codec_data_map, GST_MAP_READ);
/* Generate ADTS header */ /* Generate ADTS header */
obj_type = (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data)) & 0xC) >> 2; obj_type = (GST_READ_UINT8 (codec_data_map.data) & 0xC) >> 2;
obj_type++; obj_type++;
rate_idx = (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data)) & 0x3) << 1; rate_idx = (GST_READ_UINT8 (codec_data_map.data) & 0x3) << 1;
rate_idx |= rate_idx |= (GST_READ_UINT8 (codec_data_map.data + 1) & 0x80) >> 7;
(GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + 1) & 0x80) >> 7; channels = (GST_READ_UINT8 (codec_data_map.data + 1) & 0x78) >> 3;
channels =
(GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + 1) & 0x78) >> 3;
GST_DEBUG_OBJECT (mux, "Rate index %u, channels %u, object type %u", rate_idx, GST_DEBUG_OBJECT (mux, "Rate index %u, channels %u, object type %u", rate_idx,
channels, obj_type); channels, obj_type);
/* Sync point over a full byte */ /* Sync point over a full byte */
@ -126,11 +129,11 @@ mpegtsmux_prepare_aac (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux)
/* channels continued over next 2 bits + 4 bits at zero */ /* channels continued over next 2 bits + 4 bits at zero */
adts_header[3] = (channels & 0x3) << 6; adts_header[3] = (channels & 0x3) << 6;
/* frame size over last 2 bits */ /* frame size over last 2 bits */
adts_header[3] |= (GST_BUFFER_SIZE (out_buf) & 0x1800) >> 11; adts_header[3] |= (out_size & 0x1800) >> 11;
/* frame size continued over full byte */ /* frame size continued over full byte */
adts_header[4] = (GST_BUFFER_SIZE (out_buf) & 0x1FF8) >> 3; adts_header[4] = (out_size & 0x1FF8) >> 3;
/* frame size continued first 3 bits */ /* frame size continued first 3 bits */
adts_header[5] = (GST_BUFFER_SIZE (out_buf) & 0x7) << 5; adts_header[5] = (out_size & 0x7) << 5;
/* buffer fullness (0x7FF for VBR) over 5 last bits */ /* buffer fullness (0x7FF for VBR) over 5 last bits */
adts_header[5] |= 0x1F; adts_header[5] |= 0x1F;
/* buffer fullness (0x7FF for VBR) continued over 6 first bits + 2 zeros for /* buffer fullness (0x7FF for VBR) continued over 6 first bits + 2 zeros for
@ -138,12 +141,16 @@ mpegtsmux_prepare_aac (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux)
adts_header[6] = 0xFC; adts_header[6] = 0xFC;
/* Insert ADTS header */ /* Insert ADTS header */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, adts_header, 7); gst_buffer_fill (out_buf, out_offset, adts_header, 7);
out_offset += 7; out_offset += 7;
gst_buffer_map (buf, &buf_map, GST_MAP_READ);
/* Now copy complete frame */ /* Now copy complete frame */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, GST_BUFFER_DATA (buf), gst_buffer_fill (out_buf, out_offset, buf_map.data, buf_map.size);
GST_BUFFER_SIZE (buf));
gst_buffer_unmap (data->codec_data, &codec_data_map);
gst_buffer_unmap (buf, &buf_map);
return out_buf; return out_buf;
} }

View file

@ -1,293 +0,0 @@
/*
* Copyright 2006, 2007, 2008, 2009, 2010 Fluendo S.A.
* Authors: Jan Schmidt <jan@fluendo.com>
* Kapil Agrawal <kapil@fluendo.com>
* Julien Moutte <julien@fluendo.com>
*
* This library is licensed under 4 different licenses and you
* can choose to use it under the terms of any one of them. The
* four licenses are the MPL 1.1, the LGPL, the GPL and the MIT
* license.
*
* MPL:
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* LGPL:
*
* 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.
*
* GPL:
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* MIT:
*
* 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 "mpegtsmux_h264.h"
#include <string.h>
#define GST_CAT_DEFAULT mpegtsmux_debug
#define SPS_PPS_PERIOD GST_SECOND
typedef struct PrivDataH264 PrivDataH264;
struct PrivDataH264
{
GstBuffer *last_codec_data;
GstClockTime last_resync_ts;
GstBuffer *cached_es;
guint8 nal_length_size;
};
void
mpegtsmux_free_h264 (gpointer prepare_data)
{
PrivDataH264 *h264_data = (PrivDataH264 *) prepare_data;
if (h264_data->cached_es) {
gst_buffer_unref (h264_data->cached_es);
h264_data->cached_es = NULL;
}
g_free (prepare_data);
}
static inline gboolean
mpegtsmux_process_codec_data_h264 (MpegTsPadData * data, MpegTsMux * mux)
{
PrivDataH264 *h264_data;
gboolean ret = FALSE;
/* Initialize our private data structure for caching */
if (G_UNLIKELY (!data->prepare_data)) {
data->prepare_data = g_new0 (PrivDataH264, 1);
h264_data = (PrivDataH264 *) data->prepare_data;
h264_data->last_resync_ts = GST_CLOCK_TIME_NONE;
}
h264_data = (PrivDataH264 *) data->prepare_data;
/* Detect a codec data change */
if (h264_data->last_codec_data != data->codec_data) {
if (h264_data->cached_es) {
gst_buffer_unref (h264_data->cached_es);
h264_data->cached_es = NULL;
}
ret = TRUE;
}
/* Generate the SPS/PPS ES header that will be prepended regularly */
if (G_UNLIKELY (!h264_data->cached_es)) {
gint offset = 4, i = 0, nb_sps = 0, nb_pps = 0;
gsize out_offset = 0;
guint8 startcode[4] = { 0x00, 0x00, 0x00, 0x01 };
h264_data->last_codec_data = data->codec_data;
h264_data->cached_es =
gst_buffer_new_and_alloc (GST_BUFFER_SIZE (data->codec_data) * 10);
/* Get NAL length size */
h264_data->nal_length_size =
(GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x03) +
1;
GST_LOG_OBJECT (mux, "NAL length will be coded on %u bytes",
h264_data->nal_length_size);
offset++;
/* How many SPS */
nb_sps =
GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x1f;
GST_DEBUG_OBJECT (mux, "we have %d Sequence Parameter Set", nb_sps);
offset++;
/* For each SPS */
for (i = 0; i < nb_sps; i++) {
guint16 sps_size =
GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset);
GST_LOG_OBJECT (mux, "Sequence Parameter Set is %d bytes", sps_size);
/* Jump over SPS size */
offset += 2;
/* Fake a start code */
memcpy (GST_BUFFER_DATA (h264_data->cached_es) + out_offset,
startcode, 4);
out_offset += 4;
/* Now push the SPS */
memcpy (GST_BUFFER_DATA (h264_data->cached_es) + out_offset,
GST_BUFFER_DATA (data->codec_data) + offset, sps_size);
out_offset += sps_size;
offset += sps_size;
}
/* How many PPS */
nb_pps = GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset);
GST_LOG_OBJECT (mux, "we have %d Picture Parameter Set", nb_sps);
offset++;
/* For each PPS */
for (i = 0; i < nb_pps; i++) {
gint pps_size =
GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset);
GST_LOG_OBJECT (mux, "Picture Parameter Set is %d bytes", pps_size);
/* Jump over PPS size */
offset += 2;
/* Fake a start code */
memcpy (GST_BUFFER_DATA (h264_data->cached_es) + out_offset,
startcode, 4);
out_offset += 4;
/* Now push the PPS */
memcpy (GST_BUFFER_DATA (h264_data->cached_es) + out_offset,
GST_BUFFER_DATA (data->codec_data) + offset, pps_size);
out_offset += pps_size;
offset += pps_size;
}
GST_BUFFER_SIZE (h264_data->cached_es) = out_offset;
GST_DEBUG_OBJECT (mux, "generated a %" G_GSIZE_FORMAT
" bytes SPS/PPS header", out_offset);
}
return ret;
}
GstBuffer *
mpegtsmux_prepare_h264 (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux)
{
guint8 startcode[4] = { 0x00, 0x00, 0x00, 0x01 };
gsize out_offset = 0, in_offset = 0;
GstBuffer *out_buf;
gboolean changed;
PrivDataH264 *h264_data;
GstClockTimeDiff diff = GST_CLOCK_TIME_NONE;
GST_DEBUG_OBJECT (mux, "Preparing H264 buffer for output");
changed = mpegtsmux_process_codec_data_h264 (data, mux);
h264_data = (PrivDataH264 *) data->prepare_data;
if (GST_CLOCK_TIME_IS_VALID (h264_data->last_resync_ts) &&
GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf))) {
diff = GST_CLOCK_DIFF (h264_data->last_resync_ts,
GST_BUFFER_TIMESTAMP (buf));
}
if (changed || (GST_CLOCK_TIME_IS_VALID (diff) && diff > SPS_PPS_PERIOD)) {
out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) * 2 +
GST_BUFFER_SIZE (h264_data->cached_es));
h264_data->last_resync_ts = GST_BUFFER_TIMESTAMP (buf);
memcpy (GST_BUFFER_DATA (out_buf), GST_BUFFER_DATA (h264_data->cached_es),
GST_BUFFER_SIZE (h264_data->cached_es));
out_offset = GST_BUFFER_SIZE (h264_data->cached_es);
GST_DEBUG_OBJECT (mux, "prepending SPS/PPS information to that packet");
} else {
out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) * 2);
}
/* We want the same metadata */
gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL);
while (in_offset < GST_BUFFER_SIZE (buf) &&
out_offset < GST_BUFFER_SIZE (out_buf) - 4) {
guint32 nal_size = 0;
switch (h264_data->nal_length_size) {
case 1:
nal_size = GST_READ_UINT8 (GST_BUFFER_DATA (buf) + in_offset);
break;
case 2:
nal_size = GST_READ_UINT16_BE (GST_BUFFER_DATA (buf) + in_offset);
break;
case 4:
nal_size = GST_READ_UINT32_BE (GST_BUFFER_DATA (buf) + in_offset);
break;
default:
GST_WARNING_OBJECT (mux, "unsupported NAL length size %u",
h264_data->nal_length_size);
}
in_offset += h264_data->nal_length_size;
/* Generate an Elementary stream buffer by inserting a startcode */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4);
out_offset += 4;
memcpy (GST_BUFFER_DATA (out_buf) + out_offset,
GST_BUFFER_DATA (buf) + in_offset,
MIN (nal_size, GST_BUFFER_SIZE (out_buf) - out_offset));
in_offset += nal_size;
out_offset += nal_size;
}
if (out_offset > GST_BUFFER_SIZE (out_buf)) {
GST_WARNING_OBJECT (mux, "Calculated buffer size %" G_GSIZE_FORMAT
" is greater than max expected size %u, "
"using max expected size (Input might not be in "
"avc format", out_offset, GST_BUFFER_SIZE (out_buf));
out_offset = GST_BUFFER_SIZE (out_buf);
}
GST_BUFFER_SIZE (out_buf) = out_offset;
return out_buf;
}

View file

@ -1,93 +0,0 @@
/*
* Copyright 2006, 2007, 2008, 2009, 2010 Fluendo S.A.
* Authors: Jan Schmidt <jan@fluendo.com>
* Kapil Agrawal <kapil@fluendo.com>
* Julien Moutte <julien@fluendo.com>
*
* This library is licensed under 4 different licenses and you
* can choose to use it under the terms of any one of them. The
* four licenses are the MPL 1.1, the LGPL, the GPL and the MIT
* license.
*
* MPL:
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* LGPL:
*
* 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.
*
* GPL:
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* MIT:
*
* 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 __MPEGTSMUX_H264_H__
#define __MPEGTSMUX_H264_H__
#include "mpegtsmux.h"
GstBuffer * mpegtsmux_prepare_h264 (GstBuffer * buf, MpegTsPadData * data,
MpegTsMux * mux);
void mpegtsmux_free_h264 (gpointer prepare_data);
#endif /* __MPEGTSMUX_H264_H__ */

View file

@ -45,12 +45,12 @@ static guint32 crc_tab[256] = {
}; };
static guint32 static guint32
calc_crc32 (guint8 *data, guint datalen) calc_crc32 (guint8 * data, guint datalen)
{ {
guint i; guint i;
guint32 crc = 0xffffffff; guint32 crc = 0xffffffff;
for (i=0; i<datalen; i++) { for (i = 0; i < datalen; i++) {
crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff]; crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
} }

View file

@ -466,7 +466,7 @@ tsmux_get_buffer (TsMux * mux, GstBuffer ** buf)
if (!*buf) if (!*buf)
return FALSE; return FALSE;
g_assert (GST_BUFFER_SIZE (*buf) == TSMUX_PACKET_LENGTH); g_assert (gst_buffer_get_size (*buf) == TSMUX_PACKET_LENGTH);
return TRUE; return TRUE;
} }
@ -746,7 +746,7 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
gboolean res; gboolean res;
gint64 cur_pcr = -1; gint64 cur_pcr = -1;
GstBuffer *buf = NULL; GstBuffer *buf = NULL;
guint8 *data; GstMapInfo map;
g_return_val_if_fail (mux != NULL, FALSE); g_return_val_if_fail (mux != NULL, FALSE);
g_return_val_if_fail (stream != NULL, FALSE); g_return_val_if_fail (stream != NULL, FALSE);
@ -767,7 +767,7 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
cur_pcr = (cur_pts - TSMUX_PCR_OFFSET) * cur_pcr = (cur_pts - TSMUX_PCR_OFFSET) *
(TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ); (TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
cur_pcr += CLOCK_BASE * (TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ); cur_pcr += (gint64) CLOCK_BASE *(TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
/* Need to decide whether to write a new PCR in this packet */ /* Need to decide whether to write a new PCR in this packet */
if (stream->last_pcr == -1 || if (stream->last_pcr == -1 ||
@ -830,14 +830,17 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
if (!tsmux_get_buffer (mux, &buf)) if (!tsmux_get_buffer (mux, &buf))
return FALSE; return FALSE;
data = GST_BUFFER_DATA (buf); gst_buffer_map (buf, &map, GST_MAP_READ);
if (!tsmux_write_ts_header (data, pi, &payload_len, &payload_offs)) if (!tsmux_write_ts_header (map.data, pi, &payload_len, &payload_offs))
goto fail; goto fail;
if (!tsmux_stream_get_data (stream, data + payload_offs, payload_len))
if (!tsmux_stream_get_data (stream, map.data + payload_offs, payload_len))
goto fail; goto fail;
gst_buffer_unmap (buf, &map);
res = tsmux_packet_out (mux, buf, cur_pcr); res = tsmux_packet_out (mux, buf, cur_pcr);
/* Reset all dynamic flags */ /* Reset all dynamic flags */
@ -848,6 +851,7 @@ tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
/* ERRORS */ /* ERRORS */
fail: fail:
{ {
gst_buffer_unmap (buf, &map);
if (buf) if (buf)
gst_buffer_unref (buf); gst_buffer_unref (buf);
return FALSE; return FALSE;
@ -878,6 +882,7 @@ tsmux_write_section (TsMux * mux, TsMuxSection * section)
guint payload_len, payload_offs; guint payload_len, payload_offs;
TsMuxPacketInfo *pi; TsMuxPacketInfo *pi;
GstBuffer *buf = NULL; GstBuffer *buf = NULL;
GstMapInfo map;
pi = &section->pi; pi = &section->pi;
@ -887,43 +892,45 @@ tsmux_write_section (TsMux * mux, TsMuxSection * section)
payload_remain = pi->stream_avail; payload_remain = pi->stream_avail;
while (payload_remain > 0) { while (payload_remain > 0) {
guint8 *data;
/* obtain buffer */ /* obtain buffer */
map.data = NULL;
if (!tsmux_get_buffer (mux, &buf)) if (!tsmux_get_buffer (mux, &buf))
goto fail; goto fail;
data = GST_BUFFER_DATA (buf); gst_buffer_map (buf, &map, GST_MAP_WRITE);
if (pi->packet_start_unit_indicator) { if (pi->packet_start_unit_indicator) {
/* Need to write an extra single byte start pointer */ /* Need to write an extra single byte start pointer */
pi->stream_avail++; pi->stream_avail++;
if (!tsmux_write_ts_header (data, pi, &payload_len, &payload_offs)) { if (!tsmux_write_ts_header (map.data, pi, &payload_len, &payload_offs)) {
pi->stream_avail--; pi->stream_avail--;
goto fail; goto fail;
} }
pi->stream_avail--; pi->stream_avail--;
/* Write the pointer byte */ /* Write the pointer byte */
data[payload_offs] = 0x00; map.data[payload_offs] = 0x00;
payload_offs++; payload_offs++;
payload_len--; payload_len--;
pi->packet_start_unit_indicator = FALSE; pi->packet_start_unit_indicator = FALSE;
} else { } else {
if (!tsmux_write_ts_header (data, pi, &payload_len, &payload_offs)) if (!tsmux_write_ts_header (map.data, pi, &payload_len, &payload_offs))
goto fail; goto fail;
} }
TS_DEBUG ("Outputting %d bytes to section. %d remaining after", TS_DEBUG ("Outputting %d bytes to section. %d remaining after",
payload_len, payload_remain - payload_len); payload_len, payload_remain - payload_len);
memcpy (data + payload_offs, cur_in, payload_len); memcpy (map.data + payload_offs, cur_in, payload_len);
cur_in += payload_len; cur_in += payload_len;
payload_remain -= payload_len; payload_remain -= payload_len;
gst_buffer_unmap (buf, &map);
/* we do not write PCR in section */ /* we do not write PCR in section */
if (G_UNLIKELY (!tsmux_packet_out (mux, buf, -1))) { if (G_UNLIKELY (!tsmux_packet_out (mux, buf, -1))) {
/* buffer given away */ /* buffer given away */
@ -938,6 +945,8 @@ tsmux_write_section (TsMux * mux, TsMuxSection * section)
/* ERRORS */ /* ERRORS */
fail: fail:
{ {
if (map.data && buf)
gst_buffer_unmap (buf, &map);
if (buf) if (buf)
gst_buffer_unref (buf); gst_buffer_unref (buf);
return FALSE; return FALSE;