#include "mpegtsmux.h"
-#include "mpegtsmux_h264.h"
#include "mpegtsmux_aac.h"
GST_DEBUG_CATEGORY (mpegtsmux_debug);
gint64 new_pcr);
static void mpegtsdemux_prepare_srcpad (MpegTsMux * mux);
-static GstFlowReturn mpegtsmux_collected (GstCollectPads2 * pads,
+static GstFlowReturn mpegtsmux_collected (GstCollectPads * pads,
MpegTsMux * mux);
-static gboolean mpegtsmux_sink_event (GstCollectPads2 * pads,
- GstCollectData2 * data, GstEvent * event, gpointer user_data);
+static gboolean mpegtsmux_sink_event (GstCollectPads * pads,
+ GstCollectData * data, GstEvent * event, gpointer user_data);
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 GstStateChangeReturn mpegtsmux_change_state (GstElement * element,
GstStateChange transition);
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 GstIndex *mpegtsmux_get_index (GstElement * element);
static GstFormat pts_format;
static GstFormat spn_format;
+#endif
-GST_BOILERPLATE (MpegTsMux, mpegtsmux, GstElement, GST_TYPE_ELEMENT);
-
-static void
-mpegtsmux_base_init (gpointer g_class)
+typedef struct
{
+ GstMapInfo map_info;
+ GstBuffer *buffer;
+} StreamData;
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_add_static_pad_template (element_class,
- &mpegtsmux_sink_factory);
- gst_element_class_add_static_pad_template (element_class,
- &mpegtsmux_src_factory);
+G_DEFINE_TYPE (MpegTsMux, mpegtsmux, GST_TYPE_ELEMENT)
- 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>");
+ 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;
+}
- pts_format =
- gst_format_register ("PTS", "MPEG System Presentation Time Stamp");
- spn_format = gst_format_register ("SPN", "Source Packet Number");
+static void
+stream_data_free (StreamData * data)
+{
+ if (data) {
+ gst_buffer_unmap (data->buffer, &data->map_info);
+ gst_buffer_unref (data->buffer);
+ g_free (data);
+ }
}
+#define parent_class mpegtsmux_parent_class
+
static void
mpegtsmux_class_init (MpegTsMuxClass * klass)
{
GstElementClass *gstelement_class = GST_ELEMENT_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->get_property = GST_DEBUG_FUNCPTR (gst_mpegtsmux_get_property);
gobject_class->dispose = mpegtsmux_dispose;
- gstelement_class->request_new_pad =
- GST_DEBUG_FUNCPTR (mpegtsmux_request_new_pad);
- gstelement_class->release_pad = GST_DEBUG_FUNCPTR (mpegtsmux_release_pad);
- gstelement_class->change_state = GST_DEBUG_FUNCPTR (mpegtsmux_change_state);
+ gstelement_class->request_new_pad = mpegtsmux_request_new_pad;
+ gstelement_class->release_pad = mpegtsmux_release_pad;
+ gstelement_class->change_state = mpegtsmux_change_state;
+#if 0
gstelement_class->set_index = GST_DEBUG_FUNCPTR (mpegtsmux_set_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_param_spec_boxed ("prog-map", "Program map",
}
static void
-mpegtsmux_init (MpegTsMux * mux, MpegTsMuxClass * g_class)
+mpegtsmux_init (MpegTsMux * mux)
{
mux->srcpad =
gst_pad_new_from_static_template (&mpegtsmux_src_factory, "src");
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);
- mux->collect = gst_collect_pads2_new ();
- gst_collect_pads2_set_function (mux->collect,
- (GstCollectPads2Function) GST_DEBUG_FUNCPTR (mpegtsmux_collected), mux);
- gst_collect_pads2_set_event_function (mux->collect,
- (GstCollectPads2EventFunction) GST_DEBUG_FUNCPTR (mpegtsmux_sink_event),
+ mux->collect = gst_collect_pads_new ();
+ gst_collect_pads_set_function (mux->collect,
+ (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (mpegtsmux_collected), mux);
+
+ gst_collect_pads_set_event_function (mux->collect,
+ (GstCollectPadsEventFunction) GST_DEBUG_FUNCPTR (mpegtsmux_sink_event),
mux);
+ mux->tsmux = tsmux_new ();
+ tsmux_set_write_func (mux->tsmux, new_packet_cb, mux);
+
mux->adapter = gst_adapter_new ();
mux->out_adapter = gst_adapter_new ();
mpegtsmux_pad_reset (MpegTsPadData * pad_data)
{
pad_data->pid = 0;
- pad_data->last_ts = GST_CLOCK_TIME_NONE;
- pad_data->cur_ts = GST_CLOCK_TIME_NONE;
+ pad_data->last_pts = 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->eos = FALSE;
+#if 0
pad_data->element_index_writer_id = -1;
+#endif
if (pad_data->free_func)
pad_data->free_func (pad_data->prepare_data);
mux->streamheader_sent = FALSE;
mux->force_key_unit_event = NULL;
mux->pending_key_unit_ts = GST_CLOCK_TIME_NONE;
-
+#if 0
mux->spn_count = 0;
if (mux->element_index) {
gst_object_unref (mux->element_index);
mux->element_index = NULL;
}
-
+#endif
gst_adapter_clear (mux->adapter);
gst_adapter_clear (mux->out_adapter);
gst_event_replace (&mux->force_key_unit_event, 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))
mpegtsmux_pad_reset ((MpegTsPadData *) walk->data);
- GST_COLLECT_PADS2_STREAM_UNLOCK (mux->collect);
+ GST_COLLECT_PADS_STREAM_UNLOCK (mux->collect);
if (alloc) {
mux->tsmux = tsmux_new ();
}
}
+#if 0
static void
mpegtsmux_set_index (GstElement * element, GstIndex * index)
{
return result;
}
+#endif
static void
release_buffer_cb (guint8 * data, void *user_data)
{
- GstBuffer *buf = (GstBuffer *) user_data;
- gst_buffer_unref (buf);
+ stream_data_free (user_data);
}
static GstFlowReturn
GstBuffer *codec_data = NULL;
pad = ts_data->collect.pad;
- caps = gst_pad_get_negotiated_caps (pad);
+ caps = gst_pad_get_current_caps (pad);
if (caps == NULL)
goto not_negotiated;
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);
g_return_val_if_fail (s != NULL, FALSE);
st = TSMUX_ST_PS_AUDIO_LPCM;
} else if (strcmp (mt, "video/x-h264") == 0) {
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) {
gint mpegversion;
case 4:
{
st = TSMUX_ST_AUDIO_AAC;
- if (codec_data) {
- GST_DEBUG_OBJECT (pad, "we have additional codec data (%d bytes)",
- GST_BUFFER_SIZE (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 (%" G_GSIZE_FORMAT " bytes)",
+ gst_buffer_get_size (codec_data));
ts_data->codec_data = gst_buffer_ref (codec_data);
ts_data->prepare_func = mpegtsmux_prepare_aac;
} else {
ret = GST_FLOW_OK;
}
-
+#if 0
GST_OBJECT_LOCK (mux);
if (mux->element_index) {
gboolean parsed = FALSE;
}
}
GST_OBJECT_UNLOCK (mux);
+#endif
gst_caps_unref (caps);
return ret;
/* Create the streams */
while (walk) {
- GstCollectData2 *c_data = (GstCollectData2 *) walk->data;
+ GstCollectData *c_data = (GstCollectData *) walk->data;
MpegTsPadData *ts_data = (MpegTsPadData *) walk->data;
gchar *name = NULL;
GSList *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;
if (ts_data->eos == FALSE) {
if (ts_data->queued_buf == NULL) {
GstBuffer *buf;
- ts_data->queued_buf = buf =
- gst_collect_pads2_pop (mux->collect, c_data);
+ ts_data->queued_buf = buf = gst_collect_pads_pop (mux->collect, c_data);
if (buf != NULL) {
if (ts_data->prepare_func) {
buf = ts_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 (ts_data->last_ts == GST_CLOCK_TIME_NONE ||
- ts_data->last_ts < GST_BUFFER_TIMESTAMP (buf)) {
- ts_data->cur_ts = ts_data->last_ts =
- gst_segment_to_running_time (&c_data->segment,
- GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf));
+ if (GST_BUFFER_PTS (buf) != GST_CLOCK_TIME_NONE) {
+
+ ts_data->cur_pts = ts_data->last_pts =
+ gst_segment_to_running_time (&c_data->segment,
+ GST_FORMAT_TIME, GST_BUFFER_PTS (buf));
+ } else
+ ts_data->cur_pts = GST_CLOCK_TIME_NONE;
+
+ 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 {
- 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
- 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
- " (uncorrected ts %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
+ " (uncorrected pts %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
") for PID 0x%04x",
- GST_TIME_ARGS (ts_data->cur_ts),
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+ GST_TIME_ARGS (ts_data->cur_pts),
+ GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
GST_BUFFER_TIMESTAMP (buf), ts_data->pid);
/* Choose a stream we've never seen a timestamp for to ensure
* 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;
}
} else {
/* If we don't yet have a best pad, take this one, otherwise take
* whichever has the oldest timestamp */
if (best != NULL) {
- if (ts_data->last_ts != GST_CLOCK_TIME_NONE &&
- best->last_ts != GST_CLOCK_TIME_NONE &&
- ts_data->last_ts < best->last_ts) {
+ if (ts_data->last_pts != GST_CLOCK_TIME_NONE &&
+ best->last_pts != GST_CLOCK_TIME_NONE &&
+ ts_data->last_pts < best->last_pts) {
best = ts_data;
}
} else {
return best;
}
-#define COLLECT_DATA_PAD(collect_data) (((GstCollectData2 *)(collect_data))->pad)
+#define COLLECT_DATA_PAD(collect_data) (((GstCollectData *)(collect_data))->pad)
static gboolean
-mpegtsmux_sink_event (GstCollectPads2 * pads, GstCollectData2 * data,
+mpegtsmux_sink_event (GstCollectPads * pads, GstCollectData * data,
GstEvent * event, gpointer user_data)
{
MpegTsMux *mux = GST_MPEG_TSMUX (user_data);
gboolean res = FALSE;
+ gboolean forward = TRUE;
GstPad *pad;
pad = data->pad;
goto out;
res = TRUE;
+ forward = FALSE;
gst_video_event_parse_downstream_force_key_unit (event,
×tamp, &stream_time, &running_time, &all_headers, &count);
}
out:
- if (res)
+ if (!forward)
gst_event_unref (event);
+ else
+ res = gst_collect_pads_event_default (pads, data, event, FALSE);
return res;
}
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));
gboolean res = TRUE, forward = TRUE;
GstIterator *iter;
GstIteratorResult iter_ret;
GstPad *sinkpad;
+ GValue sinkpad_value = G_VALUE_INIT;
GstClockTime running_time;
gboolean all_headers, done;
guint count;
done = FALSE;
while (!done) {
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) {
case GST_ITERATOR_DONE:
break;
}
}
+ g_value_reset (&sinkpad_value);
gst_iterator_free (iter);
break;
}
}
if (forward)
- res = gst_pad_event_default (pad, event);
+ res = gst_pad_event_default (pad, parent, event);
else
gst_event_unref (event);
}
static GstFlowReturn
-mpegtsmux_collected (GstCollectPads2 * pads, MpegTsMux * mux)
+mpegtsmux_collected (GstCollectPads * pads, MpegTsMux * mux)
{
GstFlowReturn ret = GST_FLOW_OK;
MpegTsPadData *best = NULL;
TsMuxProgram *prog = best->prog;
GstBuffer *buf = best->queued_buf;
gint64 pts = -1;
+ guint64 dts = -1;
gboolean delta = TRUE;
+ StreamData *stream_data;
if (prog == NULL)
goto no_program;
GST_DEBUG_OBJECT (COLLECT_DATA_PAD (best),
"Chose stream for output (PID: 0x%04x)", best->pid);
- if (GST_CLOCK_TIME_IS_VALID (best->cur_ts)) {
- pts = GSTTIME_TO_MPEGTIME (best->cur_ts);
- GST_DEBUG_OBJECT (mux, "Buffer has TS %" GST_TIME_FORMAT " pts %"
- G_GINT64_FORMAT, GST_TIME_ARGS (best->cur_ts), pts);
+ if (GST_CLOCK_TIME_IS_VALID (best->cur_pts)) {
+ pts = GSTTIME_TO_MPEGTIME (best->cur_pts);
+ GST_DEBUG_OBJECT (mux, "Buffer has PTS %" GST_TIME_FORMAT " 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) {
delta = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
+#if 0
GST_OBJECT_LOCK (mux);
if (mux->element_index && !delta && best->element_index_writer_id != -1) {
gst_index_add_association (mux->element_index,
pts_format, pts, NULL);
}
GST_OBJECT_UNLOCK (mux);
+#endif
}
GST_DEBUG_OBJECT (mux, "delta: %d", delta);
- tsmux_stream_add_data (best->stream, GST_BUFFER_DATA (buf),
- GST_BUFFER_SIZE (buf), buf, pts, -1, !delta);
+ stream_data = stream_data_new (buf);
+ 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 */
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->last_size = GST_BUFFER_SIZE (buf);
+ mux->last_size = stream_data->map_info.size;
while (tsmux_stream_bytes_in_buffer (best->stream) > 0) {
if (!tsmux_write_stream_packet (mux->tsmux, best->stream)) {
/* Failed writing data for some reason. Set appropriate error */
}
static GstPad *
-mpegtsmux_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name)
+mpegtsmux_request_new_pad (GstElement * element, GstPadTemplate * templ,
+ const gchar * name, const GstCaps * caps)
{
MpegTsMux *mux = GST_MPEG_TSMUX (element);
gint pid = -1;
g_free (pad_name);
pad_data = (MpegTsPadData *)
- gst_collect_pads2_add_pad_full (mux->collect, pad, sizeof (MpegTsPadData),
- (GstCollectData2DestroyNotify) (mpegtsmux_pad_reset), TRUE);
+ gst_collect_pads_add_pad_full (mux->collect, pad, sizeof (MpegTsPadData),
+ (GstCollectDataDestroyNotify) (mpegtsmux_pad_reset), TRUE);
if (pad_data == NULL)
goto pad_failure;
{
GST_ELEMENT_ERROR (element, STREAM, FAILED,
("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);
return NULL;
}
GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " being released", pad);
if (mux->collect) {
- gst_collect_pads2_remove_pad (mux->collect, pad);
+ gst_collect_pads_remove_pad (mux->collect, pad);
}
/* chain up */
if (!buf) {
hbuf = gst_buffer_new_and_alloc (len);
- memcpy (GST_BUFFER_DATA (hbuf), data, len);
+ gst_buffer_fill (hbuf, 0, data, len);
} else {
hbuf = gst_buffer_copy (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) {
GST_LOG_OBJECT (mux, "marking as delta unit");
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
buf = gst_adapter_take_buffer (mux->out_adapter, av - (av % align));
g_assert (buf);
GST_BUFFER_TIMESTAMP (buf) = ts;
- gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad));
+
ret = gst_pad_push (mux->srcpad, buf);
av = av % align;
}
guint8 *data;
guint32 header;
gint dummy;
+ GstMapInfo map;
GST_LOG_OBJECT (mux, "handling %d leftover bytes", av);
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);
- 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_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);
- dummy = (GST_BUFFER_SIZE (buf) - av) / packet_size;
+ dummy = (map.size - av) / packet_size;
GST_LOG_OBJECT (mux, "adding %d null packets", dummy);
for (; dummy > 0; dummy--) {
data += packet_size;
}
+ gst_buffer_unmap (buf, &map);
+
ret = gst_pad_push (mux->srcpad, buf);
}
static GstFlowReturn
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);
return GST_FLOW_OK;
{
GstBuffer *out_buf;
int chunk_bytes;
+ GstMapInfo map;
GST_LOG_OBJECT (mux, "Have buffer %p with new_pcr=%" G_GINT64_FORMAT,
buf, new_pcr);
g_assert (out_buf);
offset += M2TS_PACKET_LENGTH;
- gst_buffer_set_caps (out_buf, GST_PAD_CAPS (mux->srcpad));
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
* 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 %"
G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, cur_pcr);
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 (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 %"
G_GUINT64_FORMAT, M2TS_PACKET_LENGTH, new_pcr);
{
MpegTsMux *mux = (MpegTsMux *) user_data;
gint offset = 0;
+ GstMapInfo map;
+#if 0
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);
+
+ if (offset) {
+ /* there should be a better way to do this */
+ memmove (map.data + offset, map.data, map.size - offset);
+ }
- offset = GST_BUFFER_DATA (buf) - GST_BUFFER_MALLOCDATA (buf);
- GST_BUFFER_TIMESTAMP (buf) = mux->last_ts;
+ GST_BUFFER_PTS (buf) = mux->last_ts;
/* do common init (flags and streamheaders) */
- new_packet_common_init (mux, buf,
- GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+ new_packet_common_init (mux, buf, map.data + offset, map.size);
/* all is meant for downstream, including any prefix */
- GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf);
- GST_BUFFER_SIZE (buf) += offset;
-
if (offset)
return new_packet_m2ts (mux, buf, new_pcr);
else
if (mux->m2ts_mode == TRUE)
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);
- GST_BUFFER_DATA (buf) += offset;
- GST_BUFFER_SIZE (buf) -= offset;
+ gst_buffer_set_size (buf, NORMAL_TS_PACKET_LENGTH);
*_buf = buf;
}
GstCaps *caps;
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);
g_value_init (&array, GST_TYPE_ARRAY);
sh = mux->streamheader;
while (sh) {
buf = sh->data;
- GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
g_value_init (&value, GST_TYPE_BUFFER);
gst_value_take_buffer (&value, buf);
gst_value_array_append_value (&array, &value);
static void
mpegtsdemux_prepare_srcpad (MpegTsMux * mux)
{
+ GstSegment seg;
/* we are not going to seek */
- GstEvent *new_seg =
- gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0);
+ GstEvent *new_seg;
GstCaps *caps = gst_caps_new_simple ("video/mpegts",
"systemstream", G_TYPE_BOOLEAN, TRUE,
"packetsize", G_TYPE_INT,
(mux->m2ts_mode ? M2TS_PACKET_LENGTH : NORMAL_TS_PACKET_LENGTH),
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 */
gst_pad_set_caps (mux->srcpad, caps);
gst_caps_unref (caps);
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
- gst_collect_pads2_start (mux->collect);
+ gst_collect_pads_start (mux->collect);
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
- gst_collect_pads2_stop (mux->collect);
+ gst_collect_pads_stop (mux->collect);
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
+++ /dev/null
-/*
- * 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;
-}