mpegpsmux: port to 0.11

Naive port. Barely tested.
This commit is contained in:
Tim-Philipp Müller 2012-12-31 19:42:03 +00:00
parent c3c33d88d4
commit c056ecb02b
7 changed files with 167 additions and 229 deletions

View file

@ -315,7 +315,7 @@ GST_PLUGINS_NONPORTED=" aiff \
freeverb \ freeverb \
hdvparse ivfparse jp2kdecimator \ hdvparse ivfparse jp2kdecimator \
kate librfb \ kate librfb \
mpegpsmux mve mythtv nsf nuvdemux \ mve mythtv nsf nuvdemux \
patchdetect real \ patchdetect real \
sdi stereo tta videofilters \ sdi stereo tta videofilters \
videomeasure videosignal vmnc \ videomeasure videosignal vmnc \

View file

@ -93,37 +93,20 @@ static void gst_mpegpsmux_set_property (GObject * object, guint prop_id,
static void gst_mpegpsmux_get_property (GObject * object, guint prop_id, static void gst_mpegpsmux_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec); GValue * value, GParamSpec * pspec);
static void mpegpsmux_dispose (GObject * object); static void mpegpsmux_finalize (GObject * object);
static gboolean new_packet_cb (guint8 * data, guint len, void *user_data); static gboolean new_packet_cb (guint8 * data, guint len, void *user_data);
static void release_buffer_cb (guint8 * data, void *user_data);
static gboolean mpegpsdemux_prepare_srcpad (MpegPsMux * mux); static gboolean mpegpsdemux_prepare_srcpad (MpegPsMux * mux);
static GstFlowReturn mpegpsmux_collected (GstCollectPads * pads, static GstFlowReturn mpegpsmux_collected (GstCollectPads * pads,
MpegPsMux * mux); MpegPsMux * mux);
static GstPad *mpegpsmux_request_new_pad (GstElement * element, static GstPad *mpegpsmux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name); GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
static void mpegpsmux_release_pad (GstElement * element, GstPad * pad); static void mpegpsmux_release_pad (GstElement * element, GstPad * pad);
static GstStateChangeReturn mpegpsmux_change_state (GstElement * element, static GstStateChangeReturn mpegpsmux_change_state (GstElement * element,
GstStateChange transition); GstStateChange transition);
GST_BOILERPLATE (MpegPsMux, mpegpsmux, GstElement, GST_TYPE_ELEMENT); #define parent_class mpegpsmux_parent_class
G_DEFINE_TYPE (MpegPsMux, mpegpsmux, GST_TYPE_ELEMENT);
static void
mpegpsmux_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&mpegpsmux_sink_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&mpegpsmux_src_factory));
gst_element_class_set_static_metadata (element_class,
"MPEG Program Stream Muxer", "Codec/Muxer",
"Multiplexes media streams into an MPEG Program Stream",
"Lin YANG <oxcsnicho@gmail.com>");
}
static void static void
mpegpsmux_class_init (MpegPsMuxClass * klass) mpegpsmux_class_init (MpegPsMuxClass * klass)
@ -131,9 +114,9 @@ mpegpsmux_class_init (MpegPsMuxClass * 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);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mpegpsmux_set_property); gobject_class->set_property = gst_mpegpsmux_set_property;
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_mpegpsmux_get_property); gobject_class->get_property = gst_mpegpsmux_get_property;
gobject_class->dispose = mpegpsmux_dispose; gobject_class->finalize = mpegpsmux_finalize;
gstelement_class->request_new_pad = mpegpsmux_request_new_pad; gstelement_class->request_new_pad = mpegpsmux_request_new_pad;
gstelement_class->release_pad = mpegpsmux_release_pad; gstelement_class->release_pad = mpegpsmux_release_pad;
@ -143,10 +126,20 @@ mpegpsmux_class_init (MpegPsMuxClass * klass)
g_param_spec_boolean ("aggregate-gops", "Aggregate GOPs", g_param_spec_boolean ("aggregate-gops", "Aggregate GOPs",
"Whether to aggregate GOPs and push them out as buffer lists", "Whether to aggregate GOPs and push them out as buffer lists",
DEFAULT_AGGREGATE_GOPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); DEFAULT_AGGREGATE_GOPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&mpegpsmux_sink_factory));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&mpegpsmux_src_factory));
gst_element_class_set_static_metadata (gstelement_class,
"MPEG Program Stream Muxer", "Codec/Muxer",
"Multiplexes media streams into an MPEG Program Stream",
"Lin YANG <oxcsnicho@gmail.com>");
} }
static void static void
mpegpsmux_init (MpegPsMux * mux, MpegPsMuxClass * g_class) mpegpsmux_init (MpegPsMux * mux)
{ {
mux->srcpad = gst_pad_new_from_static_template (&mpegpsmux_src_factory, mux->srcpad = gst_pad_new_from_static_template (&mpegpsmux_src_factory,
"src"); "src");
@ -166,7 +159,7 @@ mpegpsmux_init (MpegPsMux * mux, MpegPsMuxClass * g_class)
} }
static void static void
mpegpsmux_dispose (GObject * object) mpegpsmux_finalize (GObject * object)
{ {
MpegPsMux *mux = GST_MPEG_PSMUX (object); MpegPsMux *mux = GST_MPEG_PSMUX (object);
@ -184,7 +177,7 @@ mpegpsmux_dispose (GObject * object)
mux->gop_list = NULL; mux->gop_list = NULL;
} }
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); G_OBJECT_CLASS (mpegpsmux_parent_class)->finalize (object);
} }
static void static void
@ -219,25 +212,17 @@ gst_mpegpsmux_get_property (GObject * object, guint prop_id,
} }
} }
static void
release_buffer_cb (guint8 * data, void *user_data)
{
/* release a given buffer. callback func */
GstBuffer *buf = (GstBuffer *) user_data;
gst_buffer_unref (buf);
}
static GstFlowReturn static GstFlowReturn
mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad) mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad)
{ {
/* Create a steam. Fill in codec specific information */ /* Create a steam. Fill in codec specific information */
GstFlowReturn ret = GST_FLOW_ERROR; GstFlowReturn ret = GST_FLOW_ERROR;
GstCaps *caps = gst_pad_get_negotiated_caps (pad); GstCaps *caps;
GstStructure *s; GstStructure *s;
gboolean is_video = FALSE; gboolean is_video = FALSE;
caps = gst_pad_get_current_caps (pad);
if (caps == NULL) { if (caps == NULL) {
GST_DEBUG_OBJECT (pad, "Sink pad caps were not set before pushing"); GST_DEBUG_OBJECT (pad, "Sink pad caps were not set before pushing");
return GST_FLOW_NOT_NEGOTIATED; return GST_FLOW_NOT_NEGOTIATED;
@ -266,8 +251,8 @@ mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad)
value = gst_structure_get_value (s, "codec_data"); value = gst_structure_get_value (s, "codec_data");
if (value) { if (value) {
ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value)); ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value));
GST_DEBUG_OBJECT (pad, "we have additional codec data (%d bytes)", GST_DEBUG_OBJECT (pad, "%" G_GSIZE_FORMAT " bytes of codec data",
GST_BUFFER_SIZE (ps_data->codec_data)); gst_buffer_get_size (ps_data->codec_data));
ps_data->prepare_func = mpegpsmux_prepare_h264; ps_data->prepare_func = mpegpsmux_prepare_h264;
} else { } else {
ps_data->codec_data = NULL; ps_data->codec_data = NULL;
@ -302,8 +287,8 @@ mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad)
value = gst_structure_get_value (s, "codec_data"); value = gst_structure_get_value (s, "codec_data");
if (value) { if (value) {
ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value)); ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value));
GST_DEBUG_OBJECT (pad, "we have additional codec data (%d bytes)", GST_DEBUG_OBJECT (pad, "%" G_GSIZE_FORMAT " bytes of codec data",
GST_BUFFER_SIZE (ps_data->codec_data)); gst_buffer_get_size (ps_data->codec_data));
ps_data->prepare_func = mpegpsmux_prepare_aac; ps_data->prepare_func = mpegpsmux_prepare_aac;
} else { } else {
ps_data->codec_data = NULL; ps_data->codec_data = NULL;
@ -347,8 +332,6 @@ mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad)
gst_structure_get_int (s, "channels", &ps_data->stream->audio_channels); gst_structure_get_int (s, "channels", &ps_data->stream->audio_channels);
gst_structure_get_int (s, "bitrate", &ps_data->stream->audio_bitrate); gst_structure_get_int (s, "bitrate", &ps_data->stream->audio_bitrate);
psmux_stream_set_buffer_release_func (ps_data->stream, release_buffer_cb);
ret = GST_FLOW_OK; ret = GST_FLOW_OK;
if (is_video && mux->video_stream_id == 0) { if (is_video && mux->video_stream_id == 0) {
@ -483,7 +466,7 @@ mpegpsmux_push_gop_list (MpegPsMux * mux)
g_assert (mux->gop_list != NULL); g_assert (mux->gop_list != NULL);
GST_DEBUG_OBJECT (mux, "Sending pending GOP of %u buffers", GST_DEBUG_OBJECT (mux, "Sending pending GOP of %u buffers",
gst_buffer_list_n_groups (mux->gop_list)); gst_buffer_list_length (mux->gop_list));
flow = gst_pad_push_list (mux->srcpad, mux->gop_list); flow = gst_pad_push_list (mux->srcpad, mux->gop_list);
mux->gop_list = NULL; mux->gop_list = NULL;
return flow; return flow;
@ -549,9 +532,9 @@ mpegpsmux_collected (GstCollectPads * pads, MpegPsMux * mux)
goto done; goto done;
} }
/* FIXME: porting: add DTS */
/* give the buffer to libpsmux for processing */ /* give the buffer to libpsmux for processing */
psmux_stream_add_data (best->stream, GST_BUFFER_DATA (buf), psmux_stream_add_data (best->stream, buf, pts, -1, keyunit);
GST_BUFFER_SIZE (buf), buf, pts, -1, keyunit);
best->queued_buf = NULL; best->queued_buf = NULL;
@ -588,9 +571,8 @@ write_fail:
static GstPad * static GstPad *
mpegpsmux_request_new_pad (GstElement * element, mpegpsmux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name) GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
{ {
MpegPsMux *mux = GST_MPEG_PSMUX (element); MpegPsMux *mux = GST_MPEG_PSMUX (element);
GstPad *pad = NULL; GstPad *pad = NULL;
MpegPsPadData *pad_data = NULL; MpegPsPadData *pad_data = NULL;
@ -652,26 +634,6 @@ mpegpsmux_release_pad (GstElement * element, GstPad * pad)
gst_collect_pads_remove_pad (mux->collect, pad); gst_collect_pads_remove_pad (mux->collect, pad);
} }
static void
add_buffer_to_goplist (MpegPsMux * mux, GstBuffer * buf)
{
GstBufferListIterator *it;
if (mux->gop_list == NULL)
mux->gop_list = gst_buffer_list_new ();
it = gst_buffer_list_iterate (mux->gop_list);
/* move iterator to end */
while (gst_buffer_list_iterator_next_group (it)) {
/* .. */
}
gst_buffer_list_iterator_add_group (it);
gst_buffer_list_iterator_add (it, buf);
gst_buffer_list_iterator_free (it);
}
static gboolean static gboolean
new_packet_cb (guint8 * data, guint len, void *user_data) new_packet_cb (guint8 * data, guint len, void *user_data)
{ {
@ -683,18 +645,17 @@ new_packet_cb (guint8 * data, guint len, void *user_data)
GstFlowReturn ret; GstFlowReturn ret;
GST_LOG_OBJECT (mux, "Outputting a packet of length %d", len); GST_LOG_OBJECT (mux, "Outputting a packet of length %d", len);
buf = gst_buffer_new_and_alloc (len);
if (G_UNLIKELY (buf == NULL)) {
mux->last_flow_ret = GST_FLOW_ERROR;
return FALSE;
}
gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad));
memcpy (GST_BUFFER_DATA (buf), data, len); data = g_memdup (data, len);
buf = gst_buffer_new_wrapped (data, len);
GST_BUFFER_TIMESTAMP (buf) = mux->last_ts; GST_BUFFER_TIMESTAMP (buf) = mux->last_ts;
if (mux->aggregate_gops) { if (mux->aggregate_gops) {
add_buffer_to_goplist (mux, buf); if (mux->gop_list == NULL)
mux->gop_list = gst_buffer_list_new ();
gst_buffer_list_add (mux->gop_list, buf);
return TRUE; return TRUE;
} }
@ -708,22 +669,17 @@ new_packet_cb (guint8 * data, guint len, void *user_data)
return TRUE; return TRUE;
} }
/* prepare the source pad for output */
static gboolean static gboolean
mpegpsdemux_prepare_srcpad (MpegPsMux * mux) mpegpsdemux_prepare_srcpad (MpegPsMux * mux)
{ {
GstSegment segment;
GValue val = { 0, }; GValue val = { 0, };
GList *headers, *l; GList *headers, *l;
GstCaps *caps;
/* prepare the source pad for output */ caps = gst_caps_new_simple ("video/mpeg",
"mpegversion", G_TYPE_INT, 2, "systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
GstEvent *new_seg =
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0);
GstCaps *caps = gst_caps_new_simple ("video/mpeg",
"mpegversion", G_TYPE_INT, 2,
"systemstream", G_TYPE_BOOLEAN, TRUE,
NULL);
/* gst_static_pad_template_get_caps (&mpegpsmux_src_factory); */
headers = psmux_get_stream_headers (mux->psmux); headers = psmux_get_stream_headers (mux->psmux);
g_value_init (&val, GST_TYPE_ARRAY); g_value_init (&val, GST_TYPE_ARRAY);
@ -740,13 +696,12 @@ mpegpsdemux_prepare_srcpad (MpegPsMux * mux)
g_value_unset (&val); g_value_unset (&val);
g_list_free (headers); g_list_free (headers);
/* Set caps on src pad from our template and push new segment */ /* Set caps on src pad and push new segment */
gst_pad_set_caps (mux->srcpad, caps); gst_pad_push_event (mux->srcpad, gst_event_new_caps (caps));
gst_caps_unref (caps);
if (!gst_pad_push_event (mux->srcpad, new_seg)) { gst_segment_init (&segment, GST_FORMAT_BYTES);
GST_WARNING_OBJECT (mux, "New segment event was not handled"); gst_pad_push_event (mux->srcpad, gst_event_new_segment (&segment));
return FALSE;
}
return TRUE; return TRUE;
} }

View file

@ -95,24 +95,28 @@ GST_DEBUG_CATEGORY_EXTERN (mpegpsmux_debug);
GstBuffer * GstBuffer *
mpegpsmux_prepare_aac (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * mux) mpegpsmux_prepare_aac (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * mux)
{ {
guint8 adts_header[7] = { 0, }; GstBuffer *out_buf;
GstBuffer *out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) + 7); GstMemory *mem;
gsize out_offset = 0; gsize out_size;
guint8 *adts_header, codec_data[2];
guint8 rate_idx = 0, channels = 0, obj_type = 0; guint8 rate_idx = 0, channels = 0, obj_type = 0;
GST_DEBUG_OBJECT (mux, "Preparing AAC buffer for output"); GST_DEBUG_OBJECT (mux, "Preparing AAC buffer for output");
/* We want the same metadata */ adts_header = g_malloc0 (7);
gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL);
/* We want the same data and metadata, and then prepend some bytes */
out_buf = gst_buffer_copy (buf);
out_size = gst_buffer_get_size (buf) + 7;
gst_buffer_extract (data->codec_data, 0, codec_data, 2);
/* Generate ADTS header */ /* Generate ADTS header */
obj_type = (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data)) & 0xC) >> 2; obj_type = (codec_data[0] & 0xC) >> 2;
obj_type++; obj_type++;
rate_idx = (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data)) & 0x3) << 1; rate_idx = (codec_data[0] & 0x3) << 1;
rate_idx |= rate_idx |= (codec_data[1] & 0x80) >> 7;
(GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + 1) & 0x80) >> 7; channels = (codec_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 */
@ -129,24 +133,20 @@ mpegpsmux_prepare_aac (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * 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] |= (gst_buffer_get_size (out_buf) & 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
* number of raw data blocks */ * number of raw data blocks */
adts_header[6] = 0xFC; adts_header[6] = 0xFC;
/* Insert ADTS header */ /* Prepend ADTS header */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, adts_header, 7); mem = gst_memory_new_wrapped (0, adts_header, 7, 0, 7, adts_header, g_free);
out_offset += 7; gst_buffer_prepend_memory (out_buf, mem);
/* Now copy complete frame */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf));
return out_buf; return out_buf;
} }

View file

@ -87,6 +87,7 @@
#endif #endif
#include "mpegpsmux_h264.h" #include "mpegpsmux_h264.h"
#include <gst/base/gstbytewriter.h>
#include <string.h> #include <string.h>
GST_DEBUG_CATEGORY_EXTERN (mpegpsmux_debug); GST_DEBUG_CATEGORY_EXTERN (mpegpsmux_debug);
@ -95,32 +96,37 @@ GST_DEBUG_CATEGORY_EXTERN (mpegpsmux_debug);
GstBuffer * GstBuffer *
mpegpsmux_prepare_h264 (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * mux) mpegpsmux_prepare_h264 (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * mux)
{ {
GstByteWriter bw;
GstMapInfo codec_data, map;
guint8 nal_length_size = 0; guint8 nal_length_size = 0;
guint8 startcode[4] = { 0x00, 0x00, 0x00, 0x01 }; GstBuffer *out_buf;
GstBuffer *out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) * 2); guint8 nb_sps = 0, nb_pps = 0;
gint offset = 4, i = 0, nb_sps = 0, nb_pps = 0; gint offset = 4, i = 0;
gsize out_offset = 0, in_offset = 0; gsize in_offset;
GST_DEBUG_OBJECT (mux, "Preparing H264 buffer for output"); GST_DEBUG_OBJECT (mux, "Preparing H264 buffer for output");
/* We want the same metadata */ /* FIXME: are we prepending SPS/PPS in front of every single buffer?
gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL); * (should only be in front of keyframes really, if at all) */
/* FIXME: create a byte-stream version of SPS/PPS once in set_caps */
if (!gst_buffer_map (data->codec_data, &codec_data, GST_MAP_READ))
return NULL;
gst_byte_writer_init_with_size (&bw, gst_buffer_get_size (buf) * 2, FALSE);
/* Get NAL length size */ /* Get NAL length size */
nal_length_size = nal_length_size = (codec_data.data[offset] & 0x03) + 1;
(GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x03) + 1;
GST_LOG_OBJECT (mux, "NAL length will be coded on %u bytes", nal_length_size); GST_LOG_OBJECT (mux, "NAL length will be coded on %u bytes", nal_length_size);
offset++; offset++;
/* Generate SPS */ /* Generate SPS */
nb_sps = GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x1f; nb_sps = codec_data.data[offset] & 0x1f;
GST_DEBUG_OBJECT (mux, "we have %d Sequence Parameter Set", nb_sps); GST_DEBUG_OBJECT (mux, "we have %d Sequence Parameter Set", nb_sps);
offset++; offset++;
/* For each SPS */ /* For each SPS */
for (i = 0; i < nb_sps; i++) { for (i = 0; i < nb_sps; i++) {
guint16 sps_size = guint16 sps_size = GST_READ_UINT16_BE (codec_data.data + offset);
GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset);
GST_LOG_OBJECT (mux, "Sequence Parameter Set is %d bytes", sps_size); GST_LOG_OBJECT (mux, "Sequence Parameter Set is %d bytes", sps_size);
@ -128,24 +134,21 @@ mpegpsmux_prepare_h264 (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * mux)
offset += 2; offset += 2;
/* Fake a start code */ /* Fake a start code */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4); gst_byte_writer_put_uint32_be (&bw, 0x00000001);
out_offset += 4;
/* Now push the SPS */ /* Now push the SPS */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, gst_byte_writer_put_data (&bw, codec_data.data + offset, sps_size);
GST_BUFFER_DATA (data->codec_data) + offset, sps_size);
out_offset += sps_size;
offset += sps_size; offset += sps_size;
} }
nb_pps = GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset); nb_pps = codec_data.data[offset];
GST_LOG_OBJECT (mux, "we have %d Picture Parameter Set", nb_sps); GST_LOG_OBJECT (mux, "we have %d Picture Parameter Set", nb_sps);
offset++; offset++;
/* For each PPS */ /* For each PPS */
for (i = 0; i < nb_pps; i++) { for (i = 0; i < nb_pps; i++) {
gint pps_size = gint pps_size = GST_READ_UINT16_BE (codec_data.data + offset);
GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset);
GST_LOG_OBJECT (mux, "Picture Parameter Set is %d bytes", pps_size); GST_LOG_OBJECT (mux, "Picture Parameter Set is %d bytes", pps_size);
@ -153,47 +156,52 @@ mpegpsmux_prepare_h264 (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * mux)
offset += 2; offset += 2;
/* Fake a start code */ /* Fake a start code */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4); gst_byte_writer_put_uint32_be (&bw, 0x00000001);
out_offset += 4;
/* Now push the PPS */ /* Now push the PPS */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, gst_byte_writer_put_data (&bw, codec_data.data + offset, pps_size);
GST_BUFFER_DATA (data->codec_data) + offset, pps_size);
out_offset += pps_size;
offset += pps_size; offset += pps_size;
} }
while (in_offset < GST_BUFFER_SIZE (buf) && gst_buffer_unmap (data->codec_data, &codec_data);
out_offset < GST_BUFFER_SIZE (out_buf) - 4) {
if (!gst_buffer_map (buf, &map, GST_MAP_READ))
return NULL;
/* now process NALs and change them to byte-stream format */
in_offset = 0;
while (in_offset < map.size) {
guint32 nal_size = 0; guint32 nal_size = 0;
switch (nal_length_size) { switch (nal_length_size) {
case 1: case 1:
nal_size = GST_READ_UINT8 (GST_BUFFER_DATA (buf) + in_offset); nal_size = GST_READ_UINT8 (map.data + in_offset);
break; break;
case 2: case 2:
nal_size = GST_READ_UINT16_BE (GST_BUFFER_DATA (buf) + in_offset); nal_size = GST_READ_UINT16_BE (map.data + in_offset);
break; break;
case 4: case 4:
nal_size = GST_READ_UINT32_BE (GST_BUFFER_DATA (buf) + in_offset); nal_size = GST_READ_UINT32_BE (map.data + in_offset);
break; break;
default: default:
GST_WARNING_OBJECT (mux, "unsupported NAL length size %u", GST_WARNING_OBJECT (mux, "unsupported NAL length size %u",
nal_length_size); nal_length_size);
break;
} }
in_offset += nal_length_size; in_offset += nal_length_size;
/* Generate an Elementary stream buffer by inserting a startcode */ /* Generate an Elementary stream buffer by inserting a startcode */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4); gst_byte_writer_put_uint32_be (&bw, 0x00000001);
out_offset += 4; gst_byte_writer_put_data (&bw, map.data + in_offset,
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, MIN (nal_size, map.size - in_offset));
GST_BUFFER_DATA (buf) + in_offset,
MIN (nal_size, GST_BUFFER_SIZE (out_buf) - out_offset));
in_offset += nal_size; in_offset += nal_size;
out_offset += nal_size;
} }
GST_BUFFER_SIZE (out_buf) = out_offset; out_buf = gst_byte_writer_reset_and_get_buffer (&bw);
/* We want the same metadata */
gst_buffer_copy_into (out_buf, buf, GST_BUFFER_COPY_METADATA, 0, 0);
return out_buf; return out_buf;
} }

View file

@ -341,20 +341,19 @@ psmux_write_pack_header (PsMux * mux)
static void static void
psmux_ensure_system_header (PsMux * mux) psmux_ensure_system_header (PsMux * mux)
{ {
GstBuffer *buf;
bits_buffer_t bw; bits_buffer_t bw;
guint len = 12 + (mux->nb_streams + guint len = 12 + (mux->nb_streams +
(mux->nb_private_streams > 1 ? mux->nb_private_streams - 1 : 0)) * 3; (mux->nb_private_streams > 1 ? mux->nb_private_streams - 1 : 0)) * 3;
GList *cur; GList *cur;
gboolean private_hit = FALSE; gboolean private_hit = FALSE;
guint8 *data;
if (mux->sys_header != NULL) if (mux->sys_header != NULL)
return; return;
buf = gst_buffer_new_and_alloc (len); data = g_malloc (len);
/* system_header_start_code */ bits_initwrite (&bw, len, data);
bits_initwrite (&bw, len, GST_BUFFER_DATA (buf));
/* system_header start code */ /* system_header start code */
bits_write (&bw, 24, PSMUX_START_CODE_PREFIX); bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
@ -374,8 +373,7 @@ psmux_ensure_system_header (PsMux * mux)
bits_write (&bw, 1, 0); /* packet_rate_restriction_flag */ bits_write (&bw, 1, 0); /* packet_rate_restriction_flag */
bits_write (&bw, 7, 0x7f); /* reserved_bits */ bits_write (&bw, 7, 0x7f); /* reserved_bits */
for (cur = g_list_first (mux->streams), private_hit = FALSE; cur != NULL; for (cur = mux->streams, private_hit = FALSE; cur != NULL; cur = cur->next) {
cur = g_list_next (cur)) {
PsMuxStream *stream = (PsMuxStream *) cur->data; PsMuxStream *stream = (PsMuxStream *) cur->data;
if (private_hit && stream->stream_id == PSMUX_EXTENDED_STREAM) if (private_hit && stream->stream_id == PSMUX_EXTENDED_STREAM)
@ -390,19 +388,22 @@ psmux_ensure_system_header (PsMux * mux)
private_hit = TRUE; private_hit = TRUE;
} }
GST_MEMDUMP ("System Header", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); GST_MEMDUMP ("System Header", data, len);
mux->sys_header = buf; mux->sys_header = gst_buffer_new_wrapped (data, len);
} }
static gboolean static gboolean
psmux_write_system_header (PsMux * mux) psmux_write_system_header (PsMux * mux)
{ {
GstMapInfo map;
psmux_ensure_system_header (mux); psmux_ensure_system_header (mux);
memcpy (mux->packet_buf, GST_BUFFER_DATA (mux->sys_header), gst_buffer_map (mux->sys_header, &map, GST_MAP_READ);
GST_BUFFER_SIZE (mux->sys_header)); memcpy (mux->packet_buf, map.data, map.size);
mux->packet_bytes_written = GST_BUFFER_SIZE (mux->sys_header); mux->packet_bytes_written = map.size;
gst_buffer_unmap (mux->sys_header, &map);
return psmux_packet_out (mux); return psmux_packet_out (mux);
} }
@ -410,19 +411,19 @@ psmux_write_system_header (PsMux * mux)
static void static void
psmux_ensure_program_stream_map (PsMux * mux) psmux_ensure_program_stream_map (PsMux * mux)
{ {
GstBuffer *buf;
gint psm_size = 16, es_map_size = 0; gint psm_size = 16, es_map_size = 0;
bits_buffer_t bw; bits_buffer_t bw;
GList *cur; GList *cur;
guint16 len; guint16 len;
guint8 *pos; guint8 *pos;
guint8 *data;
if (mux->psm != NULL) if (mux->psm != NULL)
return; return;
/* pre-write the descriptor loop */ /* pre-write the descriptor loop */
pos = mux->es_info_buf; pos = mux->es_info_buf;
for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) { for (cur = mux->streams; cur != NULL; cur = cur->next) {
PsMuxStream *stream = (PsMuxStream *) cur->data; PsMuxStream *stream = (PsMuxStream *) cur->data;
len = 0; len = 0;
@ -442,9 +443,9 @@ psmux_ensure_program_stream_map (PsMux * mux)
psm_size += es_map_size; psm_size += es_map_size;
buf = gst_buffer_new_and_alloc (psm_size); data = g_malloc (psm_size);
bits_initwrite (&bw, psm_size, GST_BUFFER_DATA (buf)); bits_initwrite (&bw, psm_size, data);
/* psm start code */ /* psm start code */
bits_write (&bw, 24, PSMUX_START_CODE_PREFIX); bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
@ -471,20 +472,22 @@ psmux_ensure_program_stream_map (PsMux * mux)
psmux_put32 (&pos, crc); psmux_put32 (&pos, crc);
} }
GST_MEMDUMP ("Program Stream Map", GST_BUFFER_DATA (buf), GST_MEMDUMP ("Program Stream Map", data, psm_size);
GST_BUFFER_SIZE (buf));
mux->psm = buf; mux->psm = gst_buffer_new_wrapped (data, psm_size);
} }
static gboolean static gboolean
psmux_write_program_stream_map (PsMux * mux) psmux_write_program_stream_map (PsMux * mux)
{ {
GstMapInfo map;
psmux_ensure_program_stream_map (mux); psmux_ensure_program_stream_map (mux);
memcpy (mux->packet_buf, GST_BUFFER_DATA (mux->psm), gst_buffer_map (mux->psm, &map, GST_MAP_READ);
GST_BUFFER_SIZE (mux->psm)); memcpy (mux->packet_buf, map.data, map.size);
mux->packet_bytes_written = GST_BUFFER_SIZE (mux->psm); mux->packet_bytes_written = map.size;
gst_buffer_unmap (mux->psm, &map);
return psmux_packet_out (mux); return psmux_packet_out (mux);
} }

View file

@ -174,8 +174,6 @@ psmux_stream_new (PsMux * mux, PsMuxStreamType stream_type)
stream->cur_pes_payload_size = 0; stream->cur_pes_payload_size = 0;
stream->buffer_release = NULL;
stream->pts = -1; stream->pts = -1;
stream->dts = -1; stream->dts = -1;
stream->last_pts = -1; stream->last_pts = -1;
@ -215,31 +213,13 @@ psmux_stream_free (PsMuxStream * stream)
g_slice_free (PsMuxStream, stream); g_slice_free (PsMuxStream, stream);
} }
/**
* psmux_stream_set_buffer_release_func:
* @stream: a #PsMuxStream
* @func: the new #PsMuxStreamBufferReleaseFunc
*
* Set the function that will be called when a a piece of data fed to @stream
* with psmux_stream_add_data() can be freed. @func will be called with user
* data as provided with the call to psmux_stream_add_data().
*/
void
psmux_stream_set_buffer_release_func (PsMuxStream * stream,
PsMuxStreamBufferReleaseFunc func)
{
g_return_if_fail (stream != NULL);
stream->buffer_release = func;
}
/* Advance the current packet stream position by len bytes. /* Advance the current packet stream position by len bytes.
* Mustn't consume more than available in the current packet */ * Mustn't consume more than available in the current packet */
static void static void
psmux_stream_consume (PsMuxStream * stream, guint len) psmux_stream_consume (PsMuxStream * stream, guint len)
{ {
g_assert (stream->cur_buffer != NULL); g_assert (stream->cur_buffer != NULL);
g_assert (len <= stream->cur_buffer->size - stream->cur_buffer_consumed); g_assert (len <= stream->cur_buffer->map.size - stream->cur_buffer_consumed);
stream->cur_buffer_consumed += len; stream->cur_buffer_consumed += len;
stream->bytes_avail -= len; stream->bytes_avail -= len;
@ -250,15 +230,12 @@ psmux_stream_consume (PsMuxStream * stream, guint len)
if (stream->cur_buffer->pts != -1) if (stream->cur_buffer->pts != -1)
stream->last_pts = stream->cur_buffer->pts; stream->last_pts = stream->cur_buffer->pts;
if (stream->cur_buffer_consumed == stream->cur_buffer->size) { if (stream->cur_buffer_consumed == stream->cur_buffer->map.size) {
/* Current packet is completed, move along */ /* Current packet is completed, move along */
stream->buffers = g_list_delete_link (stream->buffers, stream->buffers); stream->buffers = g_list_delete_link (stream->buffers, stream->buffers);
if (stream->buffer_release) { gst_buffer_unmap (stream->cur_buffer->buf, &stream->cur_buffer->map);
stream->buffer_release (stream->cur_buffer->data, gst_buffer_unref (stream->cur_buffer->buf);
stream->cur_buffer->user_data);
}
g_slice_free (PsMuxStreamBuffer, stream->cur_buffer); g_slice_free (PsMuxStreamBuffer, stream->cur_buffer);
stream->cur_buffer = NULL; stream->cur_buffer = NULL;
} }
@ -346,8 +323,8 @@ psmux_stream_get_data (PsMuxStream * stream, guint8 * buf, guint len)
} }
/* Take as much as we can from the current buffer */ /* Take as much as we can from the current buffer */
avail = stream->cur_buffer->size - stream->cur_buffer_consumed; avail = stream->cur_buffer->map.size - stream->cur_buffer_consumed;
cur = stream->cur_buffer->data + stream->cur_buffer_consumed; cur = stream->cur_buffer->map.data + stream->cur_buffer_consumed;
if (avail < w) { if (avail < w) {
memcpy (buf, cur, avail); memcpy (buf, cur, avail);
psmux_stream_consume (stream, avail); psmux_stream_consume (stream, avail);
@ -417,7 +394,7 @@ psmux_stream_find_pts_dts_within (PsMuxStream * stream, guint bound,
/* FIXME: This isn't quite correct - if the 'bound' is within this /* FIXME: This isn't quite correct - if the 'bound' is within this
* buffer, we don't know if the timestamp is before or after the split * buffer, we don't know if the timestamp is before or after the split
* so we shouldn't return it */ * so we shouldn't return it */
if (bound <= curbuf->size) { if (bound <= curbuf->map.size) {
*pts = curbuf->pts; *pts = curbuf->pts;
*dts = curbuf->dts; *dts = curbuf->dts;
return; return;
@ -430,7 +407,7 @@ psmux_stream_find_pts_dts_within (PsMuxStream * stream, guint bound,
return; return;
} }
bound -= curbuf->size; bound -= curbuf->map.size;
} }
} }
@ -498,9 +475,7 @@ psmux_stream_write_pes_header (PsMuxStream * stream, guint8 * data)
/** /**
* psmux_stream_add_data: * psmux_stream_add_data:
* @stream: a #PsMuxStream * @stream: a #PsMuxStream
* @data: data to add * @buffer: (transfer full): buffer with data to add
* @len: length of @data
* @user_data: user data to pass to release func
* @pts: PTS of access unit in @data * @pts: PTS of access unit in @data
* @dts: DTS of access unit in @data * @dts: DTS of access unit in @data
* *
@ -508,21 +483,25 @@ psmux_stream_write_pes_header (PsMuxStream * stream, guint8 * data)
* timestamp (against a 90Hz clock) of the first access unit in @data. A * timestamp (against a 90Hz clock) of the first access unit in @data. A
* timestamp of -1 for @pts or @dts means unknown. * timestamp of -1 for @pts or @dts means unknown.
* *
* @user_data will be passed to the release function as set with * This function takes ownership of @buffer.
* psmux_stream_set_buffer_release_func() when @data can be freed.
*/ */
void void
psmux_stream_add_data (PsMuxStream * stream, guint8 * data, guint len, psmux_stream_add_data (PsMuxStream * stream, GstBuffer * buffer,
void *user_data, gint64 pts, gint64 dts, gboolean keyunit) gint64 pts, gint64 dts, gboolean keyunit)
{ {
PsMuxStreamBuffer *packet; PsMuxStreamBuffer *packet;
g_return_if_fail (stream != NULL); g_return_if_fail (stream != NULL);
packet = g_slice_new (PsMuxStreamBuffer); packet = g_slice_new (PsMuxStreamBuffer);
packet->data = data; packet->buf = buffer;
packet->size = len;
packet->user_data = user_data; if (!gst_buffer_map (packet->buf, &packet->map, GST_MAP_READ)) {
GST_ERROR ("Failed to map buffer for reading");
gst_buffer_unref (packet->buf);
g_slice_free (PsMuxStreamBuffer, packet);
return;
}
packet->keyunit = keyunit; packet->keyunit = keyunit;
packet->pts = pts; packet->pts = pts;
@ -531,7 +510,8 @@ psmux_stream_add_data (PsMuxStream * stream, guint8 * data, guint len,
if (stream->bytes_avail == 0) if (stream->bytes_avail == 0)
stream->last_pts = pts; stream->last_pts = pts;
stream->bytes_avail += len; stream->bytes_avail += packet->map.size;
/* FIXME: perhaps use GstQueueArray instead? */
stream->buffers = g_list_append (stream->buffers, packet); stream->buffers = g_list_append (stream->buffers, packet);
} }

View file

@ -45,7 +45,7 @@
#ifndef __PSMUXSTREAM_H__ #ifndef __PSMUXSTREAM_H__
#define __PSMUXSTREAM_H__ #define __PSMUXSTREAM_H__
#include <glib.h> #include <gst/gst.h>
#include "psmuxcommon.h" #include "psmuxcommon.h"
@ -83,16 +83,14 @@ enum PsMuxStreamType { /* Table 2-29 in spec */
struct PsMuxStreamBuffer struct PsMuxStreamBuffer
{ {
guint8 *data;
guint32 size;
gboolean keyunit; gboolean keyunit;
/* PTS & DTS associated with the contents of this buffer */ /* PTS & DTS associated with the contents of this buffer */
GstClockTime pts; GstClockTime pts;
GstClockTime dts; GstClockTime dts;
void *user_data; GstBuffer *buf;
GstMapInfo map;
}; };
/* PsMuxStream receives elementary streams for parsing. /* PsMuxStream receives elementary streams for parsing.
@ -116,9 +114,6 @@ struct PsMuxStream{
guint16 cur_pes_payload_size; guint16 cur_pes_payload_size;
guint16 pes_bytes_written; /* delete*/ guint16 pes_bytes_written; /* delete*/
/* Release function */
PsMuxStreamBufferReleaseFunc buffer_release;
/* PTS/DTS to write if the flags in the packet info are set */ /* PTS/DTS to write if the flags in the packet info are set */
gint64 pts; /* TODO: cur_buffer->pts?*/ gint64 pts; /* TODO: cur_buffer->pts?*/
gint64 dts; /* TODO: cur_buffer->dts?*/ gint64 dts; /* TODO: cur_buffer->dts?*/
@ -141,14 +136,11 @@ struct PsMuxStream{
PsMuxStream* psmux_stream_new (PsMux * mux, PsMuxStreamType stream_type); PsMuxStream* psmux_stream_new (PsMux * mux, PsMuxStreamType stream_type);
void psmux_stream_free (PsMuxStream *stream); void psmux_stream_free (PsMuxStream *stream);
/* The callback when a buffer is released. Used to unref the buffer in GStreamer */
void psmux_stream_set_buffer_release_func (PsMuxStream *stream,
PsMuxStreamBufferReleaseFunc func);
/* Add a new buffer to the pool of available bytes. If pts or dts are not -1, they /* Add a new buffer to the pool of available bytes. If pts or dts are not -1, they
* indicate the PTS or DTS of the first access unit within this packet */ * indicate the PTS or DTS of the first access unit within this packet */
void psmux_stream_add_data (PsMuxStream *stream, guint8 *data, guint len, void psmux_stream_add_data (PsMuxStream *stream,
void *user_data, gint64 pts, gint64 dts, GstBuffer * buffer,
gint64 pts, gint64 dts,
gboolean keyunit); gboolean keyunit);
/* total bytes in buffer */ /* total bytes in buffer */