oggmux: use oggstream for less brittleness in recognizing headers

Using the IN_CAPS flag for this is brittle, and will fail if either
vorbisparse or vorbistag (which is itself based on vorbisparse) is
inserted between oggdemux and oggmux. Possibly other elements too
(eg, theoraparse, etc).
Using oggstream ensures we Get It Right More Often Than Not.

https://bugzilla.gnome.org/show_bug.cgi?id=629196
This commit is contained in:
Vincent Penquerc'h 2011-01-21 16:05:46 +00:00 committed by Tim-Philipp Müller
parent 2c017d2a70
commit 440002a137
2 changed files with 54 additions and 34 deletions

View file

@ -282,7 +282,7 @@ gst_ogg_mux_ogg_pad_destroy_notify (GstCollectData * data)
GstOggPadData *oggpad = (GstOggPadData *) data;
GstBuffer *buf;
ogg_stream_clear (&oggpad->stream);
ogg_stream_clear (&oggpad->map.stream);
if (oggpad->pagebuffers) {
while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
@ -384,8 +384,8 @@ gst_ogg_mux_request_new_pad (GstElement * element,
sizeof (GstOggPadData), gst_ogg_mux_ogg_pad_destroy_notify);
ogg_mux->active_pads++;
oggpad->serial = serial;
ogg_stream_init (&oggpad->stream, serial);
oggpad->map.serialno = serial;
ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
oggpad->packetno = 0;
oggpad->pageno = 0;
oggpad->eos = FALSE;
@ -396,6 +396,8 @@ gst_ogg_mux_request_new_pad (GstElement * element,
oggpad->prev_delta = FALSE;
oggpad->data_pushed = FALSE;
oggpad->pagebuffers = g_queue_new ();
oggpad->map.headers = NULL;
oggpad->map.queued = NULL;
oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
gst_pad_set_event_function (newpad,
@ -751,7 +753,6 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
/* try to get a new buffer for this pad if needed and possible */
if (pad->buffer == NULL) {
GstBuffer *buf;
gboolean incaps;
/* shift the buffer along if needed (it's okay if next_buffer is NULL) */
if (pad->buffer == NULL) {
@ -770,13 +771,31 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
ogg_mux->delta_pad = pad;
incaps = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
/* if we need headers */
if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
/* and we have one */
if (incaps) {
ogg_packet packet;
packet.packet = GST_BUFFER_DATA (buf);
packet.bytes = GST_BUFFER_SIZE (buf);
packet.granulepos = GST_BUFFER_OFFSET_END (buf);
if (packet.granulepos == -1)
packet.granulepos = 0;
/* if we're not yet in data mode, ensure we're setup on the first packet */
if (!pad->have_type) {
pad->have_type = gst_ogg_stream_setup_map (&pad->map, &packet);
if (!pad->have_type) {
pad->map.caps =
gst_caps_new_simple ("application/x-unknown", NULL);
}
GST_DEBUG_OBJECT (ogg_mux, "New pad has caps: %s",
gst_caps_to_string (pad->map.caps));
}
if (gst_ogg_stream_packet_is_header (&pad->map, &packet)) {
GST_DEBUG_OBJECT (ogg_mux,
"got incaps buffer in control state, ignoring");
"got header buffer in control state, ignoring");
/* just ignore */
gst_buffer_unref (buf);
buf = NULL;
@ -799,7 +818,7 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
/* Just gone to EOS. Flush existing page(s) */
pad->eos = TRUE;
while (ogg_stream_flush (&pad->stream, &page)) {
while (ogg_stream_flush (&pad->map.stream, &page)) {
/* Place page into the per-pad queue */
ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
pad->first_delta);
@ -974,7 +993,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
continue;
/* now figure out the headers */
pad->headers = gst_ogg_mux_get_headers (pad);
pad->map.headers = gst_ogg_mux_get_headers (pad);
}
GST_LOG_OBJECT (mux, "creating BOS pages");
@ -1000,9 +1019,9 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
GST_LOG_OBJECT (thepad, "looping over headers");
if (pad->headers) {
buf = GST_BUFFER (pad->headers->data);
pad->headers = g_list_remove (pad->headers, buf);
if (pad->map.headers) {
buf = GST_BUFFER (pad->map.headers->data);
pad->map.headers = g_list_remove (pad->map.headers, buf);
} else if (pad->buffer) {
buf = pad->buffer;
gst_buffer_ref (buf);
@ -1031,11 +1050,11 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
packet.e_o_s = 0;
/* swap the packet in */
ogg_stream_packetin (&pad->stream, &packet);
ogg_stream_packetin (&pad->map.stream, &packet);
gst_buffer_unref (buf);
GST_LOG_OBJECT (thepad, "flushing out BOS page");
if (!ogg_stream_flush (&pad->stream, &page))
if (!ogg_stream_flush (&pad->map.stream, &page))
g_critical ("Could not flush BOS page");
hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
@ -1079,7 +1098,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
GST_DEBUG_PAD_NAME (thepad));
hwalk = pad->headers;
hwalk = pad->map.headers;
while (hwalk) {
GstBuffer *buf = GST_BUFFER (hwalk->data);
ogg_packet packet;
@ -1100,7 +1119,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
packet.e_o_s = 0;
/* swap the packet in */
ogg_stream_packetin (&pad->stream, &packet);
ogg_stream_packetin (&pad->map.stream, &packet);
gst_buffer_unref (buf);
/* if last header, flush page */
@ -1108,7 +1127,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
GST_LOG_OBJECT (mux,
"flushing page as packet %" G_GUINT64_FORMAT " is first or "
"last packet", packet.packetno);
while (ogg_stream_flush (&pad->stream, &page)) {
while (ogg_stream_flush (&pad->map.stream, &page)) {
GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
GST_LOG_OBJECT (mux, "swapped out page");
@ -1117,7 +1136,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
} else {
GST_LOG_OBJECT (mux, "try to swap out page");
/* just try to swap out a page then */
while (ogg_stream_pageout (&pad->stream, &page) > 0) {
while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
GST_LOG_OBJECT (mux, "swapped out page");
@ -1125,8 +1144,8 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
}
}
}
g_list_free (pad->headers);
pad->headers = NULL;
g_list_free (pad->map.headers);
pad->map.headers = NULL;
}
/* hbufs holds all buffers for the headers now */
@ -1207,9 +1226,10 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
GST_LOG_OBJECT (pad->collect.pad,
GST_GP_FORMAT " stored packet %" G_GINT64_FORMAT
" will make page too long, flushing",
GST_BUFFER_OFFSET_END (pad->buffer), (gint64) pad->stream.packetno);
GST_BUFFER_OFFSET_END (pad->buffer),
(gint64) pad->map.stream.packetno);
while (ogg_stream_flush (&pad->stream, &page)) {
while (ogg_stream_flush (&pad->map.stream, &page)) {
/* end time of this page is the timestamp of the next buffer */
ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
/* Place page into the per-pad queue */
@ -1311,7 +1331,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
GST_LOG_OBJECT (pad->collect.pad, "got discont");
packet.packetno++;
/* No public API for this; hack things in */
pad->stream.pageno++;
pad->map.stream.pageno++;
force_flush = TRUE;
} else {
GST_LOG_OBJECT (pad->collect.pad, "discont at stream start");
@ -1323,7 +1343,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
GST_LOG_OBJECT (pad->collect.pad,
GST_GP_FORMAT " forced flush of page before this packet",
GST_BUFFER_OFFSET_END (pad->buffer));
while (ogg_stream_flush (&pad->stream, &page)) {
while (ogg_stream_flush (&pad->map.stream, &page)) {
/* end time of this page is the timestamp of the next buffer */
ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
@ -1368,7 +1388,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
if (packet.b_o_s == 1)
GST_DEBUG_OBJECT (pad->collect.pad, "swapping in BOS packet");
ogg_stream_packetin (&pad->stream, &packet);
ogg_stream_packetin (&pad->map.stream, &packet);
pad->data_pushed = TRUE;
gp_time = GST_BUFFER_OFFSET (pad->buffer);
@ -1387,7 +1407,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
/* let ogg write out the pages now. The packet we got could end
* up in more than one page so we need to write them all */
if (ogg_stream_pageout (&pad->stream, &page) > 0) {
if (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
/* we have a new page, so we need to timestamp it correctly.
* if this fresh packet ends on this page, then the page's granulepos
* comes from that packet, and we should set this buffer's timestamp */
@ -1398,7 +1418,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
granulepos, (gint64) packet.packetno, GST_TIME_ARGS (timestamp));
GST_LOG_OBJECT (pad->collect.pad,
GST_GP_FORMAT " new page %ld",
GST_GP_CAST (ogg_page_granulepos (&page)), pad->stream.pageno);
GST_GP_CAST (ogg_page_granulepos (&page)), pad->map.stream.pageno);
if (ogg_page_granulepos (&page) == granulepos) {
/* the packet we streamed in finishes on the current page,
@ -1427,7 +1447,7 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
/* use an inner loop here to flush the remaining pages and
* mark them as delta frames as well */
while (ogg_stream_pageout (&pad->stream, &page) > 0) {
while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
if (ogg_page_granulepos (&page) == granulepos) {
/* the page has taken up the new packet completely, which means
* the packet ends the page and we can update the gp time
@ -1604,7 +1624,7 @@ gst_ogg_mux_init_collectpads (GstCollectPads * collect)
while (walk) {
GstOggPadData *oggpad = (GstOggPadData *) walk->data;
ogg_stream_init (&oggpad->stream, oggpad->serial);
ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
oggpad->packetno = 0;
oggpad->pageno = 0;
oggpad->eos = FALSE;
@ -1630,7 +1650,7 @@ gst_ogg_mux_clear_collectpads (GstCollectPads * collect)
GstOggPadData *oggpad = (GstOggPadData *) walk->data;
GstBuffer *buf;
ogg_stream_clear (&oggpad->stream);
ogg_stream_clear (&oggpad->map.stream);
while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
gst_buffer_unref (buf);

View file

@ -25,6 +25,7 @@
#include <gst/gst.h>
#include <gst/base/gstcollectpads.h>
#include "gstoggstream.h"
G_BEGIN_DECLS
@ -49,13 +50,14 @@ typedef struct
{
GstCollectData collect; /* we extend the CollectData */
GstOggStream map;
gboolean have_type;
/* These two buffers make a very simple queue - they enter as 'next_buffer'
* and (usually) leave as 'buffer', except at EOS, when buffer will be NULL */
GstBuffer *buffer; /* the first waiting buffer for the pad */
GstBuffer *next_buffer; /* the second waiting buffer for the pad */
gint serial;
ogg_stream_state stream;
gint64 packetno; /* number of next packet */
gint64 pageno; /* number of next page */
guint64 duration; /* duration of current page */
@ -71,8 +73,6 @@ typedef struct
GstOggPadState state; /* state of the pad */
GList *headers;
GQueue *pagebuffers; /* List of pages in buffers ready for pushing */
gboolean new_page; /* starting a new page */