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 \
fieldanalysis freeverb freeze frei0r \
hdvparse id3tag inter interlace ivfparse jpegformat jp2kdecimator \
kate liveadder librfb mpegtsmux \
kate liveadder librfb \
mpegpsmux mve mxf mythtv nsf nuvdemux \
patchdetect pnm real \
sdi siren speed subenc stereo tta videofilters \

View file

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

View file

@ -96,7 +96,6 @@
#include "mpegtsmux.h"
#include "mpegtsmux_h264.h"
#include "mpegtsmux_aac.h"
GST_DEBUG_CATEGORY (mpegtsmux_debug);
@ -162,64 +161,83 @@ static gboolean new_packet_m2ts (MpegTsMux * mux, GstBuffer * buf,
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);
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
mpegtsmux_base_init (gpointer g_class)
stream_data_free (StreamData * data)
{
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);
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");
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",
@ -253,21 +271,26 @@ mpegtsmux_class_init (MpegTsMuxClass * klass)
}
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 ();
@ -286,11 +309,15 @@ static void
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);
@ -325,14 +352,14 @@ mpegtsmux_reset (MpegTsMux * mux, gboolean alloc)
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);
@ -357,10 +384,10 @@ mpegtsmux_reset (MpegTsMux * mux, gboolean alloc)
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 ();
@ -472,6 +499,7 @@ gst_mpegtsmux_get_property (GObject * object, guint prop_id,
}
}
#if 0
static void
mpegtsmux_set_index (GstElement * element, GstIndex * index)
{
@ -501,12 +529,12 @@ mpegtsmux_get_index (GstElement * element)
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
@ -522,12 +550,12 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
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);
@ -547,16 +575,6 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
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;
@ -575,9 +593,10 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
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 {
@ -629,7 +648,7 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
ret = GST_FLOW_OK;
}
#if 0
GST_OBJECT_LOCK (mux);
if (mux->element_index) {
gboolean parsed = FALSE;
@ -652,6 +671,7 @@ mpegtsmux_create_stream (MpegTsMux * mux, MpegTsPadData * ts_data)
}
}
GST_OBJECT_UNLOCK (mux);
#endif
gst_caps_unref (caps);
return ret;
@ -674,7 +694,7 @@ mpegtsmux_create_streams (MpegTsMux * mux)
/* 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;
@ -743,15 +763,14 @@ mpegtsmux_choose_best_stream (MpegTsMux * mux)
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) {
@ -763,30 +782,40 @@ mpegtsmux_choose_best_stream (MpegTsMux * mux)
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 {
@ -798,9 +827,9 @@ mpegtsmux_choose_best_stream (MpegTsMux * mux)
/* 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 {
@ -812,14 +841,15 @@ mpegtsmux_choose_best_stream (MpegTsMux * mux)
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;
@ -835,6 +865,7 @@ mpegtsmux_sink_event (GstCollectPads2 * pads, GstCollectData2 * data,
goto out;
res = TRUE;
forward = FALSE;
gst_video_event_parse_downstream_force_key_unit (event,
&timestamp, &stream_time, &running_time, &all_headers, &count);
@ -861,14 +892,16 @@ mpegtsmux_sink_event (GstCollectPads2 * pads, GstCollectData2 * data,
}
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;
@ -879,6 +912,7 @@ mpegtsmux_src_event (GstPad * pad, GstEvent * event)
GstIterator *iter;
GstIteratorResult iter_ret;
GstPad *sinkpad;
GValue sinkpad_value = G_VALUE_INIT;
GstClockTime running_time;
gboolean all_headers, done;
guint count;
@ -906,7 +940,9 @@ mpegtsmux_src_event (GstPad * pad, GstEvent * event)
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:
@ -928,6 +964,7 @@ mpegtsmux_src_event (GstPad * pad, GstEvent * event)
break;
}
}
g_value_reset (&sinkpad_value);
gst_iterator_free (iter);
break;
}
@ -936,7 +973,7 @@ mpegtsmux_src_event (GstPad * pad, GstEvent * event)
}
if (forward)
res = gst_pad_event_default (pad, event);
res = gst_pad_event_default (pad, parent, event);
else
gst_event_unref (event);
@ -993,7 +1030,7 @@ out:
}
static GstFlowReturn
mpegtsmux_collected (GstCollectPads2 * pads, MpegTsMux * mux)
mpegtsmux_collected (GstCollectPads * pads, MpegTsMux * mux)
{
GstFlowReturn ret = GST_FLOW_OK;
MpegTsPadData *best = NULL;
@ -1016,7 +1053,9 @@ mpegtsmux_collected (GstCollectPads2 * pads, MpegTsMux * mux)
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;
@ -1072,14 +1111,21 @@ mpegtsmux_collected (GstCollectPads2 * pads, MpegTsMux * mux)
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,
@ -1088,19 +1134,24 @@ mpegtsmux_collected (GstCollectPads2 * pads, MpegTsMux * mux)
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 */
@ -1139,8 +1190,8 @@ no_program:
}
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;
@ -1160,8 +1211,8 @@ mpegtsmux_request_new_pad (GstElement * element,
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;
@ -1184,7 +1235,7 @@ could_not_add:
{
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;
}
@ -1205,7 +1256,7 @@ mpegtsmux_release_pad (GstElement * element, GstPad * pad)
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 */
@ -1227,7 +1278,7 @@ new_packet_common_init (MpegTsMux * mux, GstBuffer * buf, guint8 * data,
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);
}
@ -1239,10 +1290,6 @@ new_packet_common_init (MpegTsMux * mux, GstBuffer * buf, guint8 * data,
}
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);
@ -1287,7 +1334,7 @@ mpegtsmux_push_packets (MpegTsMux * mux, gboolean force)
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;
}
@ -1296,19 +1343,22 @@ mpegtsmux_push_packets (MpegTsMux * mux, gboolean force)
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--) {
@ -1332,6 +1382,8 @@ mpegtsmux_push_packets (MpegTsMux * mux, gboolean force)
data += packet_size;
}
gst_buffer_unmap (buf, &map);
ret = gst_pad_push (mux->srcpad, buf);
}
@ -1341,7 +1393,8 @@ mpegtsmux_push_packets (MpegTsMux * mux, gboolean force)
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;
@ -1352,6 +1405,7 @@ new_packet_m2ts (MpegTsMux * mux, GstBuffer * buf, gint64 new_pcr)
{
GstBuffer *out_buf;
int chunk_bytes;
GstMapInfo map;
GST_LOG_OBJECT (mux, "Have buffer %p with new_pcr=%" G_GINT64_FORMAT,
buf, new_pcr);
@ -1418,12 +1472,14 @@ new_packet_m2ts (MpegTsMux * mux, GstBuffer * buf, gint64 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);
@ -1434,9 +1490,13 @@ new_packet_m2ts (MpegTsMux * mux, GstBuffer * buf, gint64 new_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);
@ -1458,20 +1518,30 @@ new_packet_cb (GstBuffer * buf, void *user_data, gint64 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
offset = GST_BUFFER_DATA (buf) - GST_BUFFER_MALLOCDATA (buf);
GST_BUFFER_TIMESTAMP (buf) = mux->last_ts;
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);
}
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
@ -1491,11 +1561,8 @@ alloc_packet_cb (GstBuffer ** _buf, void *user_data)
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;
}
@ -1510,7 +1577,7 @@ mpegtsdemux_set_header_on_caps (MpegTsMux * mux)
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);
@ -1518,7 +1585,6 @@ mpegtsdemux_set_header_on_caps (MpegTsMux * mux)
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);
@ -1538,15 +1604,23 @@ mpegtsdemux_set_header_on_caps (MpegTsMux * mux)
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);
@ -1566,12 +1640,12 @@ mpegtsmux_change_state (GstElement * element, GstStateChange transition)
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;

View file

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

View file

@ -93,23 +93,26 @@ GstBuffer *
mpegtsmux_prepare_aac (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux)
{
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;
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");
/* We want the same metadata */
gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL);
gst_buffer_copy_into (out_buf, buf,
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 */
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++;
rate_idx = (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data)) & 0x3) << 1;
rate_idx |=
(GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + 1) & 0x80) >> 7;
channels =
(GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + 1) & 0x78) >> 3;
rate_idx = (GST_READ_UINT8 (codec_data_map.data) & 0x3) << 1;
rate_idx |= (GST_READ_UINT8 (codec_data_map.data + 1) & 0x80) >> 7;
channels = (GST_READ_UINT8 (codec_data_map.data + 1) & 0x78) >> 3;
GST_DEBUG_OBJECT (mux, "Rate index %u, channels %u, object type %u", rate_idx,
channels, obj_type);
/* 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 */
adts_header[3] = (channels & 0x3) << 6;
/* 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 */
adts_header[4] = (GST_BUFFER_SIZE (out_buf) & 0x1FF8) >> 3;
adts_header[4] = (out_size & 0x1FF8) >> 3;
/* 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 */
adts_header[5] |= 0x1F;
/* 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;
/* 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;
gst_buffer_map (buf, &buf_map, GST_MAP_READ);
/* Now copy complete frame */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf));
gst_buffer_fill (out_buf, out_offset, buf_map.data, buf_map.size);
gst_buffer_unmap (data->codec_data, &codec_data_map);
gst_buffer_unmap (buf, &buf_map);
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
calc_crc32 (guint8 *data, guint datalen)
calc_crc32 (guint8 * data, guint datalen)
{
guint i;
guint32 crc = 0xffffffff;
for (i=0; i<datalen; i++) {
for (i = 0; i < datalen; i++) {
crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
}

View file

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