summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@gmail.com>2004-06-09 12:22:46 +0000
committerWim Taymans <wim.taymans@gmail.com>2004-06-09 12:22:46 +0000
commitad028c8562821235a8303025cb42b983f4a8ac1c (patch)
tree122cbd54dfadb2abab36c6772d69604e180237f1
parentf8044583c0e8e048be43ee44f03a9f6c7a5b0f51 (diff)
ext/ogg/gstoggmux.c: Use stream caps to setup the initial pages in the ogg stream.
Original commit message from CVS: * ext/ogg/gstoggmux.c: (gst_ogg_mux_init), (gst_ogg_mux_next_buffer), (gst_ogg_mux_buffer_from_page), (gst_ogg_mux_push_page), (gst_ogg_mux_get_headers), (gst_ogg_mux_set_header_on_caps), (gst_ogg_mux_send_headers), (gst_ogg_mux_loop): Use stream caps to setup the initial pages in the ogg stream. Correctly set the streamheader caps on the srcpad.
-rw-r--r--ChangeLog10
-rw-r--r--ext/ogg/gstoggmux.c287
2 files changed, 274 insertions, 23 deletions
diff --git a/ChangeLog b/ChangeLog
index e69a6584d..dcc50c967 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2004-06-09 Wim Taymans <wim@fluendo.com>
+
+ * ext/ogg/gstoggmux.c: (gst_ogg_mux_init),
+ (gst_ogg_mux_next_buffer), (gst_ogg_mux_buffer_from_page),
+ (gst_ogg_mux_push_page), (gst_ogg_mux_get_headers),
+ (gst_ogg_mux_set_header_on_caps), (gst_ogg_mux_send_headers),
+ (gst_ogg_mux_loop):
+ Use stream caps to setup the initial pages in the ogg stream.
+ Correctly set the streamheader caps on the srcpad.
+
2004-06-09 Thomas Vander Stichele <thomas at apestaart dot org>
* sys/v4l/gstv4lsrc.c: (gst_v4lsrc_get_fps_list),
diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c
index 0a9316129..ba5d16b9b 100644
--- a/ext/ogg/gstoggmux.c
+++ b/ext/ogg/gstoggmux.c
@@ -54,6 +54,8 @@ typedef struct
gboolean eos;
guint state; /* state of the pad */
+
+ GList *headers;
}
GstOggPad;
@@ -82,6 +84,9 @@ struct _GstOggMux
/* offset in stream */
guint64 offset;
+
+ /* need_headers */
+ gboolean need_headers;
};
typedef enum
@@ -231,6 +236,7 @@ gst_ogg_mux_init (GstOggMux * ogg_mux)
ogg_mux->sinkpads = NULL;
ogg_mux->pulling = NULL;
+ ogg_mux->need_headers = TRUE;
gst_element_set_loop_function (GST_ELEMENT (ogg_mux), gst_ogg_mux_loop);
}
@@ -371,7 +377,7 @@ gst_ogg_mux_next_buffer (GstOggPad * pad)
GstData *data = NULL;
while (data == NULL) {
- GST_LOG ("muxer: pulling %s:%s\n", GST_DEBUG_PAD_NAME (pad->pad));
+ GST_LOG ("muxer: pulling %s:%s", GST_DEBUG_PAD_NAME (pad->pad));
data = gst_pad_pull (pad->pad);
/* if it's an event, handle it */
if (GST_IS_EVENT (data)) {
@@ -394,13 +400,33 @@ gst_ogg_mux_next_buffer (GstOggPad * pad)
break;
}
data = NULL;
+ } else {
+ GstBuffer *buf = GST_BUFFER (data);
+ gboolean incaps;
+
+ incaps = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_IN_CAPS);
+ /* if we need headers */
+ if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
+ /* and we have one */
+ if (incaps) {
+ GST_LOG ("muxer: got incaps buffer in control state, ignoring");
+ /* just ignore */
+ gst_buffer_unref (buf);
+ data = NULL;
+ } else {
+ GST_LOG
+ ("muxer: got data buffer in control state, switching to data mode");
+ /* this is a data buffer so switch to data state */
+ pad->state = GST_OGG_PAD_STATE_DATA;
+ }
+ }
}
}
return GST_BUFFER (data);
}
-static void
-gst_ogg_mux_push_page (GstOggMux * mux, ogg_page * page)
+static GstBuffer *
+gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page)
{
GstBuffer *buffer;
@@ -417,10 +443,16 @@ gst_ogg_mux_push_page (GstOggMux * mux, ogg_page * page)
mux->offset += GST_BUFFER_SIZE (buffer);
GST_BUFFER_OFFSET_END (buffer) = mux->offset;
- if (GST_PAD_IS_USABLE (mux->srcpad))
+ return buffer;
+}
+
+static void
+gst_ogg_mux_push_page (GstOggMux * mux, ogg_page * page)
+{
+ if (GST_PAD_IS_USABLE (mux->srcpad)) {
+ GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page);
+
gst_pad_push (mux->srcpad, GST_DATA (buffer));
- else {
- gst_buffer_unref (buffer);
}
}
@@ -507,6 +539,228 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
return bestpad;
}
+static GList *
+gst_ogg_mux_get_headers (GstOggPad * pad)
+{
+ GList *res = NULL;
+ GstOggMux *ogg_mux;
+ GstStructure *structure;
+ const GstCaps *caps;
+
+ ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad->pad));
+
+ GST_LOG ("getting headers from pad %s:%s", GST_DEBUG_PAD_NAME (pad->pad));
+
+ caps = gst_pad_get_negotiated_caps (pad->pad);
+ if (caps != NULL) {
+ const GValue *streamheader;
+
+ structure = gst_caps_get_structure (caps, 0);
+ streamheader = gst_structure_get_value (structure, "streamheader");
+ if (streamheader != NULL) {
+ if (G_VALUE_TYPE (streamheader) == GST_TYPE_FIXED_LIST) {
+ GArray *bufarr = g_value_peek_pointer (streamheader);
+ gint i;
+
+ for (i = 0; i < bufarr->len; i++) {
+ GValue *bufval = &g_array_index (bufarr, GValue, i);
+
+ if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
+ GstBuffer *buf = g_value_peek_pointer (bufval);
+
+ res = g_list_append (res, buf);
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+static void
+gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
+{
+ GstStructure *structure = gst_caps_get_structure (caps, 0);
+ GValue list = { 0 };
+ GList *walk = buffers;
+
+ /* put buffers in a fixed list */
+ g_value_init (&list, GST_TYPE_FIXED_LIST);
+
+ while (walk) {
+ GstBuffer *buf = GST_BUFFER (walk->data);
+ GValue value = { 0 };
+
+ walk = walk->next;
+
+ /* mark buffer */
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_IN_CAPS);
+
+ g_value_init (&value, GST_TYPE_BUFFER);
+ g_value_set_boxed (&value, buf);
+ gst_value_list_append_value (&list, &value);
+ g_value_unset (&value);
+ }
+ gst_structure_set_value (structure, "streamheader", &list);
+ g_value_unset (&list);
+}
+
+/**
+ * For each pad we need to write out one (small) header in one
+ * page that allows decoders to identify the type of the stream.
+ * After that we need to write out all extra info for the decoders.
+ * In the case of a codec that also needs data as configuration, we can
+ * find that info in the streamcaps.
+ * After writing the headers we must start a new page for the data.
+ */
+static void
+gst_ogg_mux_send_headers (GstOggMux * mux)
+{
+ GSList *walk;
+ GList *hbufs, *hwalk;
+ GstCaps *caps;
+
+ hbufs = NULL;
+
+ GST_LOG ("collecting headers");
+
+ walk = mux->sinkpads;
+ while (walk) {
+ GstOggPad *pad = (GstOggPad *) walk->data;
+
+ walk = walk->next;
+
+ GST_LOG ("looking at pad %s:%s", GST_DEBUG_PAD_NAME (pad->pad));
+
+ /* if the pad has no buffer, we don't care */
+ if (pad->buffer == NULL)
+ continue;
+
+ /* now figure out the headers */
+ pad->headers = gst_ogg_mux_get_headers (pad);
+ }
+
+ GST_LOG ("creating first headers");
+ walk = mux->sinkpads;
+ while (walk) {
+ GstOggPad *pad = (GstOggPad *) walk->data;
+ GstBuffer *buf;
+ ogg_packet packet;
+ ogg_page page;
+
+ walk = walk->next;
+
+ pad->packetno = 0;
+
+ GST_LOG ("looping over headers for pad %s:%s",
+ GST_DEBUG_PAD_NAME (pad->pad));
+
+ if (pad->headers) {
+ buf = GST_BUFFER (pad->headers->data);
+ pad->headers = g_list_remove (pad->headers, buf);
+ } else {
+ /* create empty buffer for the stream */
+ buf = gst_buffer_new_and_alloc (0);
+ }
+
+ /* create a packet from the buffer */
+ packet.packet = GST_BUFFER_DATA (buf);
+ packet.bytes = GST_BUFFER_SIZE (buf);
+ packet.granulepos = GST_BUFFER_OFFSET_END (buf);
+ /* mark BOS and packet number */
+ packet.b_o_s = (pad->packetno == 0);
+ packet.packetno = pad->packetno++;
+ /* mark EOS */
+ packet.e_o_s = 0;
+
+ /* swap the packet in */
+ ogg_stream_packetin (&pad->stream, &packet);
+ gst_buffer_unref (buf);
+
+ GST_LOG ("flushing page with first packet");
+ while (ogg_stream_flush (&pad->stream, &page)) {
+ GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page);
+
+ GST_LOG ("swapped out page");
+ hbufs = g_list_append (hbufs, hbuf);
+ }
+ }
+
+ GST_LOG ("creating next headers");
+ walk = mux->sinkpads;
+ while (walk) {
+ GstOggPad *pad = (GstOggPad *) walk->data;
+
+ walk = walk->next;
+
+ GST_LOG ("looping over headers for pad %s:%s",
+ GST_DEBUG_PAD_NAME (pad->pad));
+
+ hwalk = pad->headers;
+ while (hwalk) {
+ GstBuffer *buf = GST_BUFFER (hwalk->data);
+ ogg_packet packet;
+ ogg_page page;
+
+ hwalk = hwalk->next;
+
+ /* create a packet from the buffer */
+ packet.packet = GST_BUFFER_DATA (buf);
+ packet.bytes = GST_BUFFER_SIZE (buf);
+ packet.granulepos = GST_BUFFER_OFFSET_END (buf);
+ /* mark BOS and packet number */
+ packet.b_o_s = (pad->packetno == 0);
+ packet.packetno = pad->packetno++;
+ /* mark EOS */
+ packet.e_o_s = 0;
+
+ /* swap the packet in */
+ ogg_stream_packetin (&pad->stream, &packet);
+ gst_buffer_unref (buf);
+
+ /* if last header, flush page */
+ if (hwalk == NULL) {
+ GST_LOG ("flushing page as packet %d is first or last packet",
+ pad->packetno);
+ while (ogg_stream_flush (&pad->stream, &page)) {
+ GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page);
+
+ GST_LOG ("swapped out page");
+ hbufs = g_list_append (hbufs, hbuf);
+ }
+ } else {
+ GST_LOG ("try to swap out page");
+ /* just try to swap out a page then */
+ while (ogg_stream_pageout (&pad->stream, &page) > 0) {
+ GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page);
+
+ GST_LOG ("swapped out page");
+ hbufs = g_list_append (hbufs, hbuf);
+ }
+ }
+ }
+ }
+ /* hbufs holds all buffers for the headers now */
+
+ /* create caps with the buffers */
+ caps = gst_pad_get_caps (mux->srcpad);
+ if (caps) {
+ gst_ogg_mux_set_header_on_caps (caps, hbufs);
+ gst_pad_try_set_caps (mux->srcpad, caps);
+ }
+ /* and send the buffers */
+ hwalk = hbufs;
+ while (hwalk) {
+ GstBuffer *buf = GST_BUFFER (hwalk->data);
+
+ hwalk = hwalk->next;
+
+ if (GST_PAD_IS_USABLE (mux->srcpad)) {
+ gst_pad_push (mux->srcpad, GST_DATA (buf));
+ }
+ }
+}
+
/* basic idea:
*
* 1) find a pad to pull on, this is done by pulling on all pads and
@@ -540,6 +794,10 @@ gst_ogg_mux_loop (GstElement * element)
return;
}
}
+ if (ogg_mux->need_headers) {
+ gst_ogg_mux_send_headers (ogg_mux);
+ ogg_mux->need_headers = FALSE;
+ }
/* we are pulling from a pad, continue to do so until a page
* has been filled and pushed */
@@ -548,7 +806,6 @@ gst_ogg_mux_loop (GstElement * element)
ogg_page page;
GstBuffer *buf, *tmpbuf;
GstOggPad *pad = ogg_mux->pulling;
- GstOggPadState newstate;
/* now see if we have a buffer */
buf = pad->buffer;
@@ -563,22 +820,6 @@ gst_ogg_mux_loop (GstElement * element)
}
}
- /* ogg expects headers and data to be in a different page, we
- * have no way of knowing headers from data here so we use the
- * buffer control flag as a hint */
- newstate = (pad->packetno < 3 ?
- GST_OGG_PAD_STATE_CONTROL : GST_OGG_PAD_STATE_DATA);
-
- if (newstate != pad->state) {
- /* state switch, flush page */
- while (ogg_stream_flush (&pad->stream, &page)) {
- gst_ogg_mux_push_page (ogg_mux, &page);
- pad->pageno++;
- }
- /* switching state */
- pad->state = newstate;
- }
-
/* create a packet from the buffer */
packet.packet = GST_BUFFER_DATA (buf);
packet.bytes = GST_BUFFER_SIZE (buf);