mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
mpegpsmux: port to 0.11
Naive port. Barely tested.
This commit is contained in:
parent
c3c33d88d4
commit
c056ecb02b
7 changed files with 167 additions and 229 deletions
|
@ -315,7 +315,7 @@ GST_PLUGINS_NONPORTED=" aiff \
|
|||
freeverb \
|
||||
hdvparse ivfparse jp2kdecimator \
|
||||
kate librfb \
|
||||
mpegpsmux mve mythtv nsf nuvdemux \
|
||||
mve mythtv nsf nuvdemux \
|
||||
patchdetect real \
|
||||
sdi stereo tta videofilters \
|
||||
videomeasure videosignal vmnc \
|
||||
|
|
|
@ -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,
|
||||
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 void release_buffer_cb (guint8 * data, void *user_data);
|
||||
|
||||
static gboolean mpegpsdemux_prepare_srcpad (MpegPsMux * mux);
|
||||
static GstFlowReturn mpegpsmux_collected (GstCollectPads * pads,
|
||||
MpegPsMux * mux);
|
||||
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 GstStateChangeReturn mpegpsmux_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
|
||||
GST_BOILERPLATE (MpegPsMux, mpegpsmux, GstElement, 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>");
|
||||
}
|
||||
#define parent_class mpegpsmux_parent_class
|
||||
G_DEFINE_TYPE (MpegPsMux, mpegpsmux, GST_TYPE_ELEMENT);
|
||||
|
||||
static void
|
||||
mpegpsmux_class_init (MpegPsMuxClass * klass)
|
||||
|
@ -131,9 +114,9 @@ mpegpsmux_class_init (MpegPsMuxClass * klass)
|
|||
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mpegpsmux_set_property);
|
||||
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_mpegpsmux_get_property);
|
||||
gobject_class->dispose = mpegpsmux_dispose;
|
||||
gobject_class->set_property = gst_mpegpsmux_set_property;
|
||||
gobject_class->get_property = gst_mpegpsmux_get_property;
|
||||
gobject_class->finalize = mpegpsmux_finalize;
|
||||
|
||||
gstelement_class->request_new_pad = mpegpsmux_request_new_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",
|
||||
"Whether to aggregate GOPs and push them out as buffer lists",
|
||||
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
|
||||
mpegpsmux_init (MpegPsMux * mux, MpegPsMuxClass * g_class)
|
||||
mpegpsmux_init (MpegPsMux * mux)
|
||||
{
|
||||
mux->srcpad = gst_pad_new_from_static_template (&mpegpsmux_src_factory,
|
||||
"src");
|
||||
|
@ -166,7 +159,7 @@ mpegpsmux_init (MpegPsMux * mux, MpegPsMuxClass * g_class)
|
|||
}
|
||||
|
||||
static void
|
||||
mpegpsmux_dispose (GObject * object)
|
||||
mpegpsmux_finalize (GObject * object)
|
||||
{
|
||||
MpegPsMux *mux = GST_MPEG_PSMUX (object);
|
||||
|
||||
|
@ -184,7 +177,7 @@ mpegpsmux_dispose (GObject * object)
|
|||
mux->gop_list = NULL;
|
||||
}
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
|
||||
G_OBJECT_CLASS (mpegpsmux_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
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
|
||||
mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad)
|
||||
{
|
||||
/* Create a steam. Fill in codec specific information */
|
||||
|
||||
GstFlowReturn ret = GST_FLOW_ERROR;
|
||||
GstCaps *caps = gst_pad_get_negotiated_caps (pad);
|
||||
GstCaps *caps;
|
||||
GstStructure *s;
|
||||
gboolean is_video = FALSE;
|
||||
|
||||
caps = gst_pad_get_current_caps (pad);
|
||||
if (caps == NULL) {
|
||||
GST_DEBUG_OBJECT (pad, "Sink pad caps were not set before pushing");
|
||||
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");
|
||||
if (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_BUFFER_SIZE (ps_data->codec_data));
|
||||
GST_DEBUG_OBJECT (pad, "%" G_GSIZE_FORMAT " bytes of codec data",
|
||||
gst_buffer_get_size (ps_data->codec_data));
|
||||
ps_data->prepare_func = mpegpsmux_prepare_h264;
|
||||
} else {
|
||||
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");
|
||||
if (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_BUFFER_SIZE (ps_data->codec_data));
|
||||
GST_DEBUG_OBJECT (pad, "%" G_GSIZE_FORMAT " bytes of codec data",
|
||||
gst_buffer_get_size (ps_data->codec_data));
|
||||
ps_data->prepare_func = mpegpsmux_prepare_aac;
|
||||
} else {
|
||||
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, "bitrate", &ps_data->stream->audio_bitrate);
|
||||
|
||||
psmux_stream_set_buffer_release_func (ps_data->stream, release_buffer_cb);
|
||||
|
||||
ret = GST_FLOW_OK;
|
||||
|
||||
if (is_video && mux->video_stream_id == 0) {
|
||||
|
@ -483,7 +466,7 @@ mpegpsmux_push_gop_list (MpegPsMux * mux)
|
|||
g_assert (mux->gop_list != NULL);
|
||||
|
||||
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);
|
||||
mux->gop_list = NULL;
|
||||
return flow;
|
||||
|
@ -549,9 +532,9 @@ mpegpsmux_collected (GstCollectPads * pads, MpegPsMux * mux)
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* FIXME: porting: add DTS */
|
||||
/* give the buffer to libpsmux for processing */
|
||||
psmux_stream_add_data (best->stream, GST_BUFFER_DATA (buf),
|
||||
GST_BUFFER_SIZE (buf), buf, pts, -1, keyunit);
|
||||
psmux_stream_add_data (best->stream, buf, pts, -1, keyunit);
|
||||
|
||||
best->queued_buf = NULL;
|
||||
|
||||
|
@ -588,9 +571,8 @@ write_fail:
|
|||
|
||||
static GstPad *
|
||||
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);
|
||||
GstPad *pad = NULL;
|
||||
MpegPsPadData *pad_data = NULL;
|
||||
|
@ -652,26 +634,6 @@ mpegpsmux_release_pad (GstElement * element, GstPad * 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
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -708,22 +669,17 @@ new_packet_cb (guint8 * data, guint len, void *user_data)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/* prepare the source pad for output */
|
||||
static gboolean
|
||||
mpegpsdemux_prepare_srcpad (MpegPsMux * mux)
|
||||
{
|
||||
GstSegment segment;
|
||||
GValue val = { 0, };
|
||||
GList *headers, *l;
|
||||
GstCaps *caps;
|
||||
|
||||
/* prepare the source pad for output */
|
||||
|
||||
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); */
|
||||
caps = gst_caps_new_simple ("video/mpeg",
|
||||
"mpegversion", G_TYPE_INT, 2, "systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
|
||||
headers = psmux_get_stream_headers (mux->psmux);
|
||||
g_value_init (&val, GST_TYPE_ARRAY);
|
||||
|
@ -740,13 +696,12 @@ mpegpsdemux_prepare_srcpad (MpegPsMux * mux)
|
|||
g_value_unset (&val);
|
||||
g_list_free (headers);
|
||||
|
||||
/* Set caps on src pad from our template and push new segment */
|
||||
gst_pad_set_caps (mux->srcpad, caps);
|
||||
/* Set caps on src pad and push new segment */
|
||||
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_WARNING_OBJECT (mux, "New segment event was not handled");
|
||||
return FALSE;
|
||||
}
|
||||
gst_segment_init (&segment, GST_FORMAT_BYTES);
|
||||
gst_pad_push_event (mux->srcpad, gst_event_new_segment (&segment));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -95,24 +95,28 @@ GST_DEBUG_CATEGORY_EXTERN (mpegpsmux_debug);
|
|||
GstBuffer *
|
||||
mpegpsmux_prepare_aac (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * mux)
|
||||
{
|
||||
guint8 adts_header[7] = { 0, };
|
||||
GstBuffer *out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) + 7);
|
||||
gsize out_offset = 0;
|
||||
GstBuffer *out_buf;
|
||||
GstMemory *mem;
|
||||
gsize out_size;
|
||||
guint8 *adts_header, codec_data[2];
|
||||
guint8 rate_idx = 0, channels = 0, obj_type = 0;
|
||||
|
||||
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);
|
||||
adts_header = g_malloc0 (7);
|
||||
|
||||
/* 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 */
|
||||
obj_type = (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data)) & 0xC) >> 2;
|
||||
obj_type = (codec_data[0] & 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 = (codec_data[0] & 0x3) << 1;
|
||||
rate_idx |= (codec_data[1] & 0x80) >> 7;
|
||||
channels = (codec_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 */
|
||||
|
@ -129,24 +133,20 @@ mpegpsmux_prepare_aac (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * 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] |= (gst_buffer_get_size (out_buf) & 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
|
||||
* number of raw data blocks */
|
||||
adts_header[6] = 0xFC;
|
||||
|
||||
/* Insert ADTS header */
|
||||
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, adts_header, 7);
|
||||
out_offset += 7;
|
||||
|
||||
/* Now copy complete frame */
|
||||
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, GST_BUFFER_DATA (buf),
|
||||
GST_BUFFER_SIZE (buf));
|
||||
/* Prepend ADTS header */
|
||||
mem = gst_memory_new_wrapped (0, adts_header, 7, 0, 7, adts_header, g_free);
|
||||
gst_buffer_prepend_memory (out_buf, mem);
|
||||
|
||||
return out_buf;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
#endif
|
||||
|
||||
#include "mpegpsmux_h264.h"
|
||||
#include <gst/base/gstbytewriter.h>
|
||||
#include <string.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (mpegpsmux_debug);
|
||||
|
@ -95,32 +96,37 @@ GST_DEBUG_CATEGORY_EXTERN (mpegpsmux_debug);
|
|||
GstBuffer *
|
||||
mpegpsmux_prepare_h264 (GstBuffer * buf, MpegPsPadData * data, MpegPsMux * mux)
|
||||
{
|
||||
GstByteWriter bw;
|
||||
GstMapInfo codec_data, map;
|
||||
guint8 nal_length_size = 0;
|
||||
guint8 startcode[4] = { 0x00, 0x00, 0x00, 0x01 };
|
||||
GstBuffer *out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) * 2);
|
||||
gint offset = 4, i = 0, nb_sps = 0, nb_pps = 0;
|
||||
gsize out_offset = 0, in_offset = 0;
|
||||
GstBuffer *out_buf;
|
||||
guint8 nb_sps = 0, nb_pps = 0;
|
||||
gint offset = 4, i = 0;
|
||||
gsize in_offset;
|
||||
|
||||
GST_DEBUG_OBJECT (mux, "Preparing H264 buffer for output");
|
||||
|
||||
/* We want the same metadata */
|
||||
gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL);
|
||||
/* FIXME: are we prepending SPS/PPS in front of every single buffer?
|
||||
* (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 */
|
||||
nal_length_size =
|
||||
(GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x03) + 1;
|
||||
nal_length_size = (codec_data.data[offset] & 0x03) + 1;
|
||||
GST_LOG_OBJECT (mux, "NAL length will be coded on %u bytes", nal_length_size);
|
||||
offset++;
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
guint16 sps_size = GST_READ_UINT16_BE (codec_data.data + offset);
|
||||
|
||||
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;
|
||||
|
||||
/* Fake a start code */
|
||||
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4);
|
||||
out_offset += 4;
|
||||
/* Now push the SPS */
|
||||
memcpy (GST_BUFFER_DATA (out_buf) + out_offset,
|
||||
GST_BUFFER_DATA (data->codec_data) + offset, sps_size);
|
||||
gst_byte_writer_put_uint32_be (&bw, 0x00000001);
|
||||
|
||||
/* Now push the SPS */
|
||||
gst_byte_writer_put_data (&bw, codec_data.data + offset, sps_size);
|
||||
|
||||
out_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);
|
||||
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);
|
||||
gint pps_size = GST_READ_UINT16_BE (codec_data.data + offset);
|
||||
|
||||
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;
|
||||
|
||||
/* Fake a start code */
|
||||
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4);
|
||||
out_offset += 4;
|
||||
/* Now push the PPS */
|
||||
memcpy (GST_BUFFER_DATA (out_buf) + out_offset,
|
||||
GST_BUFFER_DATA (data->codec_data) + offset, pps_size);
|
||||
gst_byte_writer_put_uint32_be (&bw, 0x00000001);
|
||||
|
||||
/* Now push the PPS */
|
||||
gst_byte_writer_put_data (&bw, codec_data.data + offset, pps_size);
|
||||
|
||||
out_offset += pps_size;
|
||||
offset += pps_size;
|
||||
}
|
||||
|
||||
while (in_offset < GST_BUFFER_SIZE (buf) &&
|
||||
out_offset < GST_BUFFER_SIZE (out_buf) - 4) {
|
||||
gst_buffer_unmap (data->codec_data, &codec_data);
|
||||
|
||||
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;
|
||||
|
||||
switch (nal_length_size) {
|
||||
case 1:
|
||||
nal_size = GST_READ_UINT8 (GST_BUFFER_DATA (buf) + in_offset);
|
||||
nal_size = GST_READ_UINT8 (map.data + in_offset);
|
||||
break;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
GST_WARNING_OBJECT (mux, "unsupported NAL length size %u",
|
||||
nal_length_size);
|
||||
break;
|
||||
}
|
||||
in_offset += 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));
|
||||
gst_byte_writer_put_uint32_be (&bw, 0x00000001);
|
||||
gst_byte_writer_put_data (&bw, map.data + in_offset,
|
||||
MIN (nal_size, map.size - in_offset));
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -341,20 +341,19 @@ psmux_write_pack_header (PsMux * mux)
|
|||
static void
|
||||
psmux_ensure_system_header (PsMux * mux)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
bits_buffer_t bw;
|
||||
guint len = 12 + (mux->nb_streams +
|
||||
(mux->nb_private_streams > 1 ? mux->nb_private_streams - 1 : 0)) * 3;
|
||||
GList *cur;
|
||||
gboolean private_hit = FALSE;
|
||||
guint8 *data;
|
||||
|
||||
if (mux->sys_header != NULL)
|
||||
return;
|
||||
|
||||
buf = gst_buffer_new_and_alloc (len);
|
||||
data = g_malloc (len);
|
||||
|
||||
/* system_header_start_code */
|
||||
bits_initwrite (&bw, len, GST_BUFFER_DATA (buf));
|
||||
bits_initwrite (&bw, len, data);
|
||||
|
||||
/* system_header start code */
|
||||
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, 7, 0x7f); /* reserved_bits */
|
||||
|
||||
for (cur = g_list_first (mux->streams), private_hit = FALSE; cur != NULL;
|
||||
cur = g_list_next (cur)) {
|
||||
for (cur = mux->streams, private_hit = FALSE; cur != NULL; cur = cur->next) {
|
||||
PsMuxStream *stream = (PsMuxStream *) cur->data;
|
||||
|
||||
if (private_hit && stream->stream_id == PSMUX_EXTENDED_STREAM)
|
||||
|
@ -390,19 +388,22 @@ psmux_ensure_system_header (PsMux * mux)
|
|||
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
|
||||
psmux_write_system_header (PsMux * mux)
|
||||
{
|
||||
GstMapInfo map;
|
||||
|
||||
psmux_ensure_system_header (mux);
|
||||
|
||||
memcpy (mux->packet_buf, GST_BUFFER_DATA (mux->sys_header),
|
||||
GST_BUFFER_SIZE (mux->sys_header));
|
||||
mux->packet_bytes_written = GST_BUFFER_SIZE (mux->sys_header);
|
||||
gst_buffer_map (mux->sys_header, &map, GST_MAP_READ);
|
||||
memcpy (mux->packet_buf, map.data, map.size);
|
||||
mux->packet_bytes_written = map.size;
|
||||
gst_buffer_unmap (mux->sys_header, &map);
|
||||
|
||||
return psmux_packet_out (mux);
|
||||
}
|
||||
|
@ -410,19 +411,19 @@ psmux_write_system_header (PsMux * mux)
|
|||
static void
|
||||
psmux_ensure_program_stream_map (PsMux * mux)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
gint psm_size = 16, es_map_size = 0;
|
||||
bits_buffer_t bw;
|
||||
GList *cur;
|
||||
guint16 len;
|
||||
guint8 *pos;
|
||||
guint8 *data;
|
||||
|
||||
if (mux->psm != NULL)
|
||||
return;
|
||||
|
||||
/* pre-write the descriptor loop */
|
||||
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;
|
||||
len = 0;
|
||||
|
||||
|
@ -442,9 +443,9 @@ psmux_ensure_program_stream_map (PsMux * mux)
|
|||
|
||||
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 */
|
||||
bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
|
||||
|
@ -471,20 +472,22 @@ psmux_ensure_program_stream_map (PsMux * mux)
|
|||
psmux_put32 (&pos, crc);
|
||||
}
|
||||
|
||||
GST_MEMDUMP ("Program Stream Map", GST_BUFFER_DATA (buf),
|
||||
GST_BUFFER_SIZE (buf));
|
||||
GST_MEMDUMP ("Program Stream Map", data, psm_size);
|
||||
|
||||
mux->psm = buf;
|
||||
mux->psm = gst_buffer_new_wrapped (data, psm_size);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
psmux_write_program_stream_map (PsMux * mux)
|
||||
{
|
||||
GstMapInfo map;
|
||||
|
||||
psmux_ensure_program_stream_map (mux);
|
||||
|
||||
memcpy (mux->packet_buf, GST_BUFFER_DATA (mux->psm),
|
||||
GST_BUFFER_SIZE (mux->psm));
|
||||
mux->packet_bytes_written = GST_BUFFER_SIZE (mux->psm);
|
||||
gst_buffer_map (mux->psm, &map, GST_MAP_READ);
|
||||
memcpy (mux->packet_buf, map.data, map.size);
|
||||
mux->packet_bytes_written = map.size;
|
||||
gst_buffer_unmap (mux->psm, &map);
|
||||
|
||||
return psmux_packet_out (mux);
|
||||
}
|
||||
|
|
|
@ -174,8 +174,6 @@ psmux_stream_new (PsMux * mux, PsMuxStreamType stream_type)
|
|||
|
||||
stream->cur_pes_payload_size = 0;
|
||||
|
||||
stream->buffer_release = NULL;
|
||||
|
||||
stream->pts = -1;
|
||||
stream->dts = -1;
|
||||
stream->last_pts = -1;
|
||||
|
@ -215,31 +213,13 @@ psmux_stream_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.
|
||||
* Mustn't consume more than available in the current packet */
|
||||
static void
|
||||
psmux_stream_consume (PsMuxStream * stream, guint len)
|
||||
{
|
||||
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->bytes_avail -= len;
|
||||
|
@ -250,15 +230,12 @@ psmux_stream_consume (PsMuxStream * stream, guint len)
|
|||
if (stream->cur_buffer->pts != -1)
|
||||
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 */
|
||||
stream->buffers = g_list_delete_link (stream->buffers, stream->buffers);
|
||||
|
||||
if (stream->buffer_release) {
|
||||
stream->buffer_release (stream->cur_buffer->data,
|
||||
stream->cur_buffer->user_data);
|
||||
}
|
||||
|
||||
gst_buffer_unmap (stream->cur_buffer->buf, &stream->cur_buffer->map);
|
||||
gst_buffer_unref (stream->cur_buffer->buf);
|
||||
g_slice_free (PsMuxStreamBuffer, stream->cur_buffer);
|
||||
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 */
|
||||
avail = stream->cur_buffer->size - stream->cur_buffer_consumed;
|
||||
cur = stream->cur_buffer->data + stream->cur_buffer_consumed;
|
||||
avail = stream->cur_buffer->map.size - stream->cur_buffer_consumed;
|
||||
cur = stream->cur_buffer->map.data + stream->cur_buffer_consumed;
|
||||
if (avail < w) {
|
||||
memcpy (buf, cur, 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
|
||||
* buffer, we don't know if the timestamp is before or after the split
|
||||
* so we shouldn't return it */
|
||||
if (bound <= curbuf->size) {
|
||||
if (bound <= curbuf->map.size) {
|
||||
*pts = curbuf->pts;
|
||||
*dts = curbuf->dts;
|
||||
return;
|
||||
|
@ -430,7 +407,7 @@ psmux_stream_find_pts_dts_within (PsMuxStream * stream, guint bound,
|
|||
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:
|
||||
* @stream: a #PsMuxStream
|
||||
* @data: data to add
|
||||
* @len: length of @data
|
||||
* @user_data: user data to pass to release func
|
||||
* @buffer: (transfer full): buffer with data to add
|
||||
* @pts: PTS 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 of -1 for @pts or @dts means unknown.
|
||||
*
|
||||
* @user_data will be passed to the release function as set with
|
||||
* psmux_stream_set_buffer_release_func() when @data can be freed.
|
||||
* This function takes ownership of @buffer.
|
||||
*/
|
||||
void
|
||||
psmux_stream_add_data (PsMuxStream * stream, guint8 * data, guint len,
|
||||
void *user_data, gint64 pts, gint64 dts, gboolean keyunit)
|
||||
psmux_stream_add_data (PsMuxStream * stream, GstBuffer * buffer,
|
||||
gint64 pts, gint64 dts, gboolean keyunit)
|
||||
{
|
||||
PsMuxStreamBuffer *packet;
|
||||
|
||||
g_return_if_fail (stream != NULL);
|
||||
|
||||
packet = g_slice_new (PsMuxStreamBuffer);
|
||||
packet->data = data;
|
||||
packet->size = len;
|
||||
packet->user_data = user_data;
|
||||
packet->buf = buffer;
|
||||
|
||||
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->pts = pts;
|
||||
|
@ -531,7 +510,8 @@ psmux_stream_add_data (PsMuxStream * stream, guint8 * data, guint len,
|
|||
if (stream->bytes_avail == 0)
|
||||
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);
|
||||
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
#ifndef __PSMUXSTREAM_H__
|
||||
#define __PSMUXSTREAM_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "psmuxcommon.h"
|
||||
|
||||
|
@ -83,16 +83,14 @@ enum PsMuxStreamType { /* Table 2-29 in spec */
|
|||
|
||||
struct PsMuxStreamBuffer
|
||||
{
|
||||
guint8 *data;
|
||||
guint32 size;
|
||||
|
||||
gboolean keyunit;
|
||||
|
||||
/* PTS & DTS associated with the contents of this buffer */
|
||||
GstClockTime pts;
|
||||
GstClockTime dts;
|
||||
|
||||
void *user_data;
|
||||
GstBuffer *buf;
|
||||
GstMapInfo map;
|
||||
};
|
||||
|
||||
/* PsMuxStream receives elementary streams for parsing.
|
||||
|
@ -116,9 +114,6 @@ struct PsMuxStream{
|
|||
guint16 cur_pes_payload_size;
|
||||
guint16 pes_bytes_written; /* delete*/
|
||||
|
||||
/* Release function */
|
||||
PsMuxStreamBufferReleaseFunc buffer_release;
|
||||
|
||||
/* PTS/DTS to write if the flags in the packet info are set */
|
||||
gint64 pts; /* TODO: cur_buffer->pts?*/
|
||||
gint64 dts; /* TODO: cur_buffer->dts?*/
|
||||
|
@ -141,14 +136,11 @@ struct PsMuxStream{
|
|||
PsMuxStream* psmux_stream_new (PsMux * mux, PsMuxStreamType stream_type);
|
||||
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
|
||||
* 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 *user_data, gint64 pts, gint64 dts,
|
||||
void psmux_stream_add_data (PsMuxStream *stream,
|
||||
GstBuffer * buffer,
|
||||
gint64 pts, gint64 dts,
|
||||
gboolean keyunit);
|
||||
|
||||
/* total bytes in buffer */
|
||||
|
|
Loading…
Reference in a new issue