ext/ogg/: Ported ogg muxer.

Original commit message from CVS:
* ext/ogg/Makefile.am:
* ext/ogg/README:
* ext/ogg/gstoggdemux.c: (gst_ogg_pad_typefind),
(gst_ogg_pad_submit_packet), (gst_ogg_demux_sink_activate),
(gst_ogg_print):
* ext/ogg/gstoggmux.c: (gst_ogg_mux_init),
(gst_ogg_mux_request_new_pad), (gst_ogg_mux_next_buffer),
(gst_ogg_mux_push_page), (gst_ogg_mux_queue_pads),
(gst_ogg_mux_get_headers), (gst_ogg_mux_set_header_on_caps),
(gst_ogg_mux_send_headers), (gst_ogg_mux_collected),
(gst_ogg_mux_change_state):
Ported ogg muxer.
This commit is contained in:
Wim Taymans 2005-05-05 09:39:35 +00:00
parent 658bd2cac6
commit 59241d5e74
5 changed files with 235 additions and 156 deletions

View file

@ -1,3 +1,18 @@
2005-05-05 Wim Taymans <wim@fluendo.com>
* ext/ogg/Makefile.am:
* ext/ogg/README:
* ext/ogg/gstoggdemux.c: (gst_ogg_pad_typefind),
(gst_ogg_pad_submit_packet), (gst_ogg_demux_sink_activate),
(gst_ogg_print):
* ext/ogg/gstoggmux.c: (gst_ogg_mux_init),
(gst_ogg_mux_request_new_pad), (gst_ogg_mux_next_buffer),
(gst_ogg_mux_push_page), (gst_ogg_mux_queue_pads),
(gst_ogg_mux_get_headers), (gst_ogg_mux_set_header_on_caps),
(gst_ogg_mux_send_headers), (gst_ogg_mux_collected),
(gst_ogg_mux_change_state):
Ported ogg muxer.
2005-05-05 Wim Taymans <wim@fluendo.com> 2005-05-05 Wim Taymans <wim@fluendo.com>
* docs/design-audiosinks.txt: * docs/design-audiosinks.txt:

View file

@ -10,6 +10,6 @@ libgstogg_la_SOURCES = \
libgstogg_la_CFLAGS = $(GST_CFLAGS) $(OGG_CFLAGS) libgstogg_la_CFLAGS = $(GST_CFLAGS) $(OGG_CFLAGS)
libgstogg_la_LIBADD = $(OGG_LIBS) \ libgstogg_la_LIBADD = $(OGG_LIBS) \
$(top_builddir)/gst-libs/gst/riff/libgstriff-@GST_MAJORMINOR@.la $(top_builddir)/gst-libs/gst/riff/libgstriff-@GST_MAJORMINOR@.la $(GST_BASE_LIBS)
libgstogg_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstogg_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)

View file

@ -125,6 +125,8 @@ unique correct timestamp and a framenumber.
in a raw theroa stream we use the granulepos as the offset field. in a raw theroa stream we use the granulepos as the offset field.
The granulepos of an ogg page is the framenumber of the last frame in the page.
vorbis and granulepos vorbis and granulepos
--------------------- ---------------------
@ -134,6 +136,8 @@ from granulepos is therefore easy.
in a raw vorbis stream we use the granulepos as the offset field. in a raw vorbis stream we use the granulepos as the offset field.
The granulepos of an ogg page is the sample number of the next page in the ogg stream.
What can ogg do? What can ogg do?
---------------- ----------------
@ -183,4 +187,3 @@ TODO
- use the OFFSET field in the GstBuffer to store/read the granulepos as - use the OFFSET field in the GstBuffer to store/read the granulepos as
opposed to the OFFSET_END field. opposed to the OFFSET_END field.
- Seeking should be implemented with a binary search.

View file

@ -703,6 +703,9 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
ret = GST_RPAD_CHAINFUNC (pad->elem_pad) (pad->elem_pad, buf); ret = GST_RPAD_CHAINFUNC (pad->elem_pad) (pad->elem_pad, buf);
} }
#if 0
done:
#endif
pad->packetno++; pad->packetno++;
return ret; return ret;
@ -1975,7 +1978,7 @@ gst_ogg_demux_sink_activate (GstPad * sinkpad, GstActivateMode mode)
ogg->seekable = FALSE; ogg->seekable = FALSE;
result = TRUE; result = TRUE;
break; break;
case GST_ACTIVATE_PULL: case GST_ACTIVATE_PULL_RANGE:
/* if we have a scheduler we can start the task */ /* if we have a scheduler we can start the task */
if (GST_ELEMENT_SCHEDULER (ogg)) { if (GST_ELEMENT_SCHEDULER (ogg)) {
gst_pad_peer_set_active (sinkpad, mode); gst_pad_peer_set_active (sinkpad, mode);
@ -2007,6 +2010,9 @@ gst_ogg_demux_sink_activate (GstPad * sinkpad, GstActivateMode mode)
result = TRUE; result = TRUE;
break; break;
case GST_ACTIVATE_PULL:
result = FALSE;
break;
} }
return result; return result;
} }
@ -2142,7 +2148,7 @@ gst_ogg_print (GstOggDemux * ogg)
{ {
guint j, i; guint j, i;
GST_INFO_OBJECT (ogg, "%u chains, total time %" GST_TIME_FORMAT ":", GST_INFO_OBJECT (ogg, "%u chains, total time: %" GST_TIME_FORMAT,
ogg->chains->len, GST_TIME_ARGS (ogg->total_time)); ogg->chains->len, GST_TIME_ARGS (ogg->total_time));
for (i = 0; i < ogg->chains->len; i++) { for (i = 0; i < ogg->chains->len; i++) {
@ -2158,17 +2164,17 @@ gst_ogg_print (GstOggDemux * ogg)
GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j); GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
GST_INFO_OBJECT (ogg, " stream %08lx:", stream->serialno); GST_INFO_OBJECT (ogg, " stream %08lx:", stream->serialno);
GST_INFO_OBJECT (ogg, " start time %" GST_TIME_FORMAT ":", GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->start_time)); GST_TIME_ARGS (stream->start_time));
GST_INFO_OBJECT (ogg, " first granulepos %" G_GINT64_FORMAT ":", GST_INFO_OBJECT (ogg, " first granulepos: %" G_GINT64_FORMAT,
stream->first_granule); stream->first_granule);
GST_INFO_OBJECT (ogg, " first time %" GST_TIME_FORMAT ":", GST_INFO_OBJECT (ogg, " first time: %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->first_time)); GST_TIME_ARGS (stream->first_time));
GST_INFO_OBJECT (ogg, " last granulepos %" G_GINT64_FORMAT ":", GST_INFO_OBJECT (ogg, " last granulepos: %" G_GINT64_FORMAT,
stream->last_granule); stream->last_granule);
GST_INFO_OBJECT (ogg, " last time %" GST_TIME_FORMAT ":", GST_INFO_OBJECT (ogg, " last time: %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->last_time)); GST_TIME_ARGS (stream->last_time));
GST_INFO_OBJECT (ogg, " total time %" GST_TIME_FORMAT ":", GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->total_time)); GST_TIME_ARGS (stream->total_time));
} }
} }

View file

@ -22,6 +22,8 @@
#endif #endif
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstcollectpads.h>
#include <ogg/ogg.h> #include <ogg/ogg.h>
/* memcpy - if someone knows a way to get rid of it, please speak up /* memcpy - if someone knows a way to get rid of it, please speak up
* note: the ogg docs even say you need this... */ * note: the ogg docs even say you need this... */
@ -40,10 +42,17 @@ GST_DEBUG_CATEGORY_STATIC (gst_ogg_mux_debug);
typedef struct _GstOggMux GstOggMux; typedef struct _GstOggMux GstOggMux;
typedef struct _GstOggMuxClass GstOggMuxClass; typedef struct _GstOggMuxClass GstOggMuxClass;
typedef enum
{
GST_OGG_PAD_STATE_CONTROL = 0,
GST_OGG_PAD_STATE_DATA = 1
}
GstOggPadState;
/* all information needed for one ogg stream */ /* all information needed for one ogg stream */
typedef struct typedef struct
{ {
GstPad *pad; /* reference for this pad is held by element we belong to */ GstCollectData collect; /* we extend the CollectData */
GstBuffer *buffer; /* the queued buffer for this pad */ GstBuffer *buffer; /* the queued buffer for this pad */
@ -55,7 +64,7 @@ typedef struct
gboolean eos; gboolean eos;
gint64 offset; gint64 offset;
guint state; /* state of the pad */ GstOggPadState state; /* state of the pad */
GList *headers; GList *headers;
@ -65,24 +74,17 @@ typedef struct
} }
GstOggPad; GstOggPad;
typedef enum
{
GST_OGG_PAD_STATE_CONTROL = 0,
GST_OGG_PAD_STATE_DATA = 1
}
GstOggPadState;
struct _GstOggMux struct _GstOggMux
{ {
GstElement element; GstElement element;
/* pad */ /* source pad */
GstPad *srcpad; GstPad *srcpad;
/* sinkpads, a GSList of GstOggPads */ /* sinkpads */
GSList *sinkpads; GstCollectPads *collect;
/* the pad we are currently pulling from to fill a page */ /* the pad we are currently using to fill a page */
GstOggPad *pulling; GstOggPad *pulling;
/* next timestamp for the page */ /* next timestamp for the page */
@ -128,9 +130,9 @@ enum
LAST_SIGNAL LAST_SIGNAL
}; };
/* set to 5 seconds by default */ /* set to 0.5 seconds by default */
#define DEFAULT_MAX_DELAY 5000000000LL #define DEFAULT_MAX_DELAY 500000000LL
#define DEFAULT_MAX_PAGE_DELAY 5000000000LL #define DEFAULT_MAX_PAGE_DELAY 500000000LL
enum enum
{ {
ARG_0, ARG_0,
@ -156,7 +158,8 @@ static void gst_ogg_mux_base_init (gpointer g_class);
static void gst_ogg_mux_class_init (GstOggMuxClass * klass); static void gst_ogg_mux_class_init (GstOggMuxClass * klass);
static void gst_ogg_mux_init (GstOggMux * ogg_mux); static void gst_ogg_mux_init (GstOggMux * ogg_mux);
static void gst_ogg_mux_loop (GstElement * element); static GstFlowReturn
gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux);
static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event); static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event);
static GstPad *gst_ogg_mux_request_new_pad (GstElement * element, static GstPad *gst_ogg_mux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name); GstPadTemplate * templ, const gchar * name);
@ -265,16 +268,15 @@ gst_ogg_mux_init (GstOggMux * ogg_mux)
/* seed random number generator for creation of serial numbers */ /* seed random number generator for creation of serial numbers */
srand (time (NULL)); srand (time (NULL));
ogg_mux->sinkpads = NULL; ogg_mux->collect = gst_collectpads_new ();
gst_collectpads_set_function (ogg_mux->collect,
(GstCollectPadsFunction) gst_ogg_mux_collected, ogg_mux);
ogg_mux->pulling = NULL; ogg_mux->pulling = NULL;
ogg_mux->need_headers = TRUE; ogg_mux->need_headers = TRUE;
ogg_mux->max_delay = DEFAULT_MAX_DELAY; ogg_mux->max_delay = DEFAULT_MAX_DELAY;
ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY; ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;
ogg_mux->delta_pad = NULL; ogg_mux->delta_pad = NULL;
//gst_element_set_loop_function (GST_ELEMENT (ogg_mux), gst_ogg_mux_loop);
gst_ogg_mux_loop (GST_ELEMENT (ogg_mux));
} }
static GstPadLinkReturn static GstPadLinkReturn
@ -290,42 +292,28 @@ gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer)
return GST_PAD_LINK_OK; return GST_PAD_LINK_OK;
} }
static void
gst_ogg_mux_pad_link (GstPad * pad, GstPad * peer, gpointer data)
{
GstOggMux *ogg_mux = ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
GST_DEBUG_OBJECT (ogg_mux, "pad '%s' connected", gst_pad_get_name (pad));
}
static void
gst_ogg_mux_pad_unlink (GstPad * pad, GstPad * peer, gpointer data)
{
GstOggMux *ogg_mux = ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
GST_DEBUG_OBJECT (ogg_mux, "pad '%s' unlinked", gst_pad_get_name (pad));
}
static GstPad * static GstPad *
gst_ogg_mux_request_new_pad (GstElement * element, gst_ogg_mux_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * req_name) GstPadTemplate * templ, const gchar * req_name)
{ {
GstOggMux *ogg_mux; GstOggMux *ogg_mux;
GstPad *newpad; GstPad *newpad;
GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); GstElementClass *klass;
g_return_val_if_fail (templ != NULL, NULL); g_return_val_if_fail (templ != NULL, NULL);
if (templ->direction != GST_PAD_SINK) { if (templ->direction != GST_PAD_SINK)
g_warning ("ogg_mux: request pad that is not a SINK pad\n"); goto wrong_direction;
return NULL;
}
g_return_val_if_fail (GST_IS_OGG_MUX (element), NULL); g_return_val_if_fail (GST_IS_OGG_MUX (element), NULL);
ogg_mux = GST_OGG_MUX (element); ogg_mux = GST_OGG_MUX (element);
if (templ == gst_element_class_get_pad_template (klass, "sink_%d")) { klass = GST_ELEMENT_GET_CLASS (element);
if (templ != gst_element_class_get_pad_template (klass, "sink_%d"))
goto wrong_template;
{
gint serial; gint serial;
gchar *name; gchar *name;
@ -344,9 +332,12 @@ gst_ogg_mux_request_new_pad (GstElement * element,
/* construct our own wrapper data structure for the pad to /* construct our own wrapper data structure for the pad to
* keep track of its status */ * keep track of its status */
{ {
GstOggPad *oggpad = g_new0 (GstOggPad, 1); GstOggPad *oggpad;
oggpad = (GstOggPad *)
gst_collectpads_add_pad (ogg_mux->collect, newpad,
sizeof (GstOggPad));
oggpad->pad = newpad;
oggpad->serial = serial; oggpad->serial = serial;
ogg_stream_init (&oggpad->stream, serial); ogg_stream_init (&oggpad->stream, serial);
oggpad->packetno = 0; oggpad->packetno = 0;
@ -357,22 +348,9 @@ gst_ogg_mux_request_new_pad (GstElement * element,
oggpad->new_page = TRUE; oggpad->new_page = TRUE;
oggpad->first_delta = FALSE; oggpad->first_delta = FALSE;
oggpad->prev_delta = FALSE; oggpad->prev_delta = FALSE;
/* save a pointer to our data in the pad */
gst_pad_set_element_private (newpad, oggpad);
/* store our data for the pad */
ogg_mux->sinkpads = g_slist_prepend (ogg_mux->sinkpads, oggpad);
} }
} else {
g_warning ("ogg_mux: this is not our template!\n");
return NULL;
} }
g_signal_connect (newpad, "linked",
G_CALLBACK (gst_ogg_mux_pad_link), (gpointer) ogg_mux);
g_signal_connect (newpad, "unlinked",
G_CALLBACK (gst_ogg_mux_pad_unlink), (gpointer) ogg_mux);
/* setup some pad functions */ /* setup some pad functions */
gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect); gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect);
gst_pad_set_event_mask_function (newpad, gst_ogg_mux_get_sink_event_masks); gst_pad_set_event_mask_function (newpad, gst_ogg_mux_get_sink_event_masks);
@ -380,6 +358,18 @@ gst_ogg_mux_request_new_pad (GstElement * element,
gst_element_add_pad (element, newpad); gst_element_add_pad (element, newpad);
return newpad; return newpad;
/* ERRORS */
wrong_direction:
{
g_warning ("ogg_mux: request pad that is not a SINK pad\n");
return NULL;
}
wrong_template:
{
g_warning ("ogg_mux: this is not our template!\n");
return NULL;
}
} }
/* handle events */ /* handle events */
@ -404,6 +394,7 @@ gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event)
return gst_pad_event_default (pad, event); return gst_pad_event_default (pad, event);
} }
#if 0
static GstBuffer * static GstBuffer *
gst_ogg_mux_next_buffer (GstOggPad * pad, gboolean * interrupt) gst_ogg_mux_next_buffer (GstOggPad * pad, gboolean * interrupt)
{ {
@ -473,6 +464,7 @@ gst_ogg_mux_next_buffer (GstOggPad * pad, gboolean * interrupt)
} }
return GST_BUFFER (data); return GST_BUFFER (data);
} }
#endif
static GstBuffer * static GstBuffer *
gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta) gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
@ -497,14 +489,17 @@ gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
return buffer; return buffer;
} }
static void static GstFlowReturn
gst_ogg_mux_push_page (GstOggMux * mux, ogg_page * page, gboolean delta) gst_ogg_mux_push_page (GstOggMux * mux, ogg_page * page, gboolean delta)
{ {
if (GST_PAD_IS_USABLE (mux->srcpad)) { GstBuffer *buffer;
GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta); GstFlowReturn ret;
gst_pad_push (mux->srcpad, buffer); buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
}
ret = gst_pad_push (mux->srcpad, buffer);
return ret;
} }
/* /*
@ -555,34 +550,50 @@ gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPad * old, GstOggPad * new)
/* make sure a buffer is queued on all pads, returns a pointer to an oggpad /* make sure a buffer is queued on all pads, returns a pointer to an oggpad
* that holds the best buffer or NULL when no pad was usable */ * that holds the best buffer or NULL when no pad was usable */
static GstOggPad * static GstOggPad *
gst_ogg_mux_queue_pads (GstOggMux * ogg_mux, gboolean * interrupt) gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
{ {
GstOggPad *bestpad = NULL; GstOggPad *bestpad = NULL;
GSList *walk; GSList *walk;
/* try to make sure we have a buffer from each usable pad first */ /* try to make sure we have a buffer from each usable pad first */
walk = ogg_mux->sinkpads; walk = ogg_mux->collect->data;
while (walk) { while (walk) {
GstOggPad *pad = (GstOggPad *) walk->data; GstOggPad *pad;
GstCollectData *data;
walk = walk->next; data = (GstCollectData *) walk->data;
pad = (GstOggPad *) data;
if (pad->eos) walk = g_slist_next (walk);
if (data->eos)
continue; continue;
/* try to get a new buffer for this pad if needed and possible */ /* try to get a new buffer for this pad if needed and possible */
if (pad->buffer == NULL && GST_PAD_IS_USABLE (pad->pad)) { if (pad->buffer == NULL) {
pad->buffer = gst_ogg_mux_next_buffer (pad, interrupt); GstBuffer *buf;
if (*interrupt) gboolean incaps;
return NULL;
/* no next buffer, try another pad */
if (pad->buffer == NULL)
continue;
}
/* skip unusable pads */ buf = gst_collectpads_pop (ogg_mux->collect, data);
if (!GST_PAD_IS_USABLE (pad->pad))
continue; 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_DEBUG ("muxer: got incaps buffer in control state, ignoring");
/* just ignore */
gst_buffer_unref (buf);
buf = NULL;
} else {
GST_DEBUG
("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;
}
}
pad->buffer = buf;
}
/* we should have a buffer now, see if it is the best pad to /* we should have a buffer now, see if it is the best pad to
* pull on */ * pull on */
@ -602,45 +613,64 @@ gst_ogg_mux_get_headers (GstOggPad * pad)
GstOggMux *ogg_mux; GstOggMux *ogg_mux;
GstStructure *structure; GstStructure *structure;
const GstCaps *caps; const GstCaps *caps;
GstPad *thepad;
ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad->pad)); thepad = pad->collect.pad;
GST_LOG ("getting headers from pad %s:%s", GST_DEBUG_PAD_NAME (pad->pad)); ogg_mux = GST_OGG_MUX (GST_PAD_PARENT (thepad));
caps = gst_pad_get_negotiated_caps (pad->pad); GST_LOG ("getting headers from pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
caps = gst_pad_get_negotiated_caps (thepad);
if (caps != NULL) { if (caps != NULL) {
const GValue *streamheader; const GValue *streamheader;
structure = gst_caps_get_structure (caps, 0); structure = gst_caps_get_structure (caps, 0);
streamheader = gst_structure_get_value (structure, "streamheader"); streamheader = gst_structure_get_value (structure, "streamheader");
if (streamheader != NULL) { if (streamheader != NULL) {
GST_LOG ("got header");
if (G_VALUE_TYPE (streamheader) == GST_TYPE_FIXED_LIST) { if (G_VALUE_TYPE (streamheader) == GST_TYPE_FIXED_LIST) {
GArray *bufarr = g_value_peek_pointer (streamheader); GArray *bufarr = g_value_peek_pointer (streamheader);
gint i; gint i;
GST_LOG ("got fixed list");
for (i = 0; i < bufarr->len; i++) { for (i = 0; i < bufarr->len; i++) {
GValue *bufval = &g_array_index (bufarr, GValue, i); GValue *bufval = &g_array_index (bufarr, GValue, i);
GST_LOG ("item %d", i);
if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) { if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
GstBuffer *buf = g_value_peek_pointer (bufval); GstBuffer *buf = g_value_peek_pointer (bufval);
GST_LOG ("adding item %d to header list", i);
gst_buffer_ref (buf); gst_buffer_ref (buf);
res = g_list_append (res, buf); res = g_list_append (res, buf);
} }
} }
} else {
GST_LOG ("streamheader is not fixed list");
} }
} else {
GST_LOG ("caps done have streamheader");
} }
} else {
GST_LOG ("got empty caps as negotiated format");
} }
return res; return res;
} }
static void static GstCaps *
gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers) gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
{ {
GstStructure *structure = gst_caps_get_structure (caps, 0); GstStructure *structure;
GValue list = { 0 }; GValue list = { 0 };
GList *walk = buffers; GList *walk = buffers;
caps = gst_caps_make_writable (caps);
structure = gst_caps_get_structure (caps, 0);
/* put buffers in a fixed list */ /* put buffers in a fixed list */
g_value_init (&list, GST_TYPE_FIXED_LIST); g_value_init (&list, GST_TYPE_FIXED_LIST);
@ -660,6 +690,8 @@ gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
} }
gst_structure_set_value (structure, "streamheader", &list); gst_structure_set_value (structure, "streamheader", &list);
g_value_unset (&list); g_value_unset (&list);
return caps;
} }
/** /**
@ -670,24 +702,30 @@ gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
* find that info in the streamcaps. * find that info in the streamcaps.
* After writing the headers we must start a new page for the data. * After writing the headers we must start a new page for the data.
*/ */
static void static GstFlowReturn
gst_ogg_mux_send_headers (GstOggMux * mux) gst_ogg_mux_send_headers (GstOggMux * mux)
{ {
GSList *walk; GSList *walk;
GList *hbufs, *hwalk; GList *hbufs, *hwalk;
GstCaps *caps; GstCaps *caps;
GstFlowReturn ret;
hbufs = NULL; hbufs = NULL;
ret = GST_FLOW_OK;
GST_LOG ("collecting headers"); GST_LOG ("collecting headers");
walk = mux->sinkpads; walk = mux->collect->data;
while (walk) { while (walk) {
GstOggPad *pad = (GstOggPad *) walk->data; GstOggPad *pad;
GstPad *thepad;
walk = walk->next; pad = (GstOggPad *) walk->data;
thepad = pad->collect.pad;
GST_LOG ("looking at pad %s:%s", GST_DEBUG_PAD_NAME (pad->pad)); walk = g_slist_next (walk);
GST_LOG ("looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
/* if the pad has no buffer, we don't care */ /* if the pad has no buffer, we don't care */
if (pad->buffer == NULL) if (pad->buffer == NULL)
@ -698,19 +736,22 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
} }
GST_LOG ("creating first headers"); GST_LOG ("creating first headers");
walk = mux->sinkpads; walk = mux->collect->data;
while (walk) { while (walk) {
GstOggPad *pad = (GstOggPad *) walk->data; GstOggPad *pad;
GstBuffer *buf; GstBuffer *buf;
ogg_packet packet; ogg_packet packet;
ogg_page page; ogg_page page;
GstPad *thepad;
pad = (GstOggPad *) walk->data;
thepad = pad->collect.pad;
walk = walk->next; walk = walk->next;
pad->packetno = 0; pad->packetno = 0;
GST_LOG ("looping over headers for pad %s:%s", GST_LOG ("looping over headers for pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
GST_DEBUG_PAD_NAME (pad->pad));
if (pad->headers) { if (pad->headers) {
buf = GST_BUFFER (pad->headers->data); buf = GST_BUFFER (pad->headers->data);
@ -746,14 +787,17 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
} }
GST_LOG ("creating next headers"); GST_LOG ("creating next headers");
walk = mux->sinkpads; walk = mux->collect->data;
while (walk) { while (walk) {
GstOggPad *pad = (GstOggPad *) walk->data; GstOggPad *pad;
GstPad *thepad;
pad = (GstOggPad *) walk->data;
thepad = pad->collect.pad;
walk = walk->next; walk = walk->next;
GST_LOG ("looping over headers for pad %s:%s", GST_LOG ("looping over headers for pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
GST_DEBUG_PAD_NAME (pad->pad));
hwalk = pad->headers; hwalk = pad->headers;
while (hwalk) { while (hwalk) {
@ -808,8 +852,8 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
/* create caps with the buffers */ /* create caps with the buffers */
caps = gst_pad_get_caps (mux->srcpad); caps = gst_pad_get_caps (mux->srcpad);
if (caps) { if (caps) {
gst_ogg_mux_set_header_on_caps (caps, hbufs); caps = gst_ogg_mux_set_header_on_caps (caps, hbufs);
//gst_pad_try_set_caps (mux->srcpad, caps); gst_pad_set_caps (mux->srcpad, caps);
} }
/* and send the buffers */ /* and send the buffers */
hwalk = hbufs; hwalk = hbufs;
@ -818,19 +862,20 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
hwalk = hwalk->next; hwalk = hwalk->next;
if (GST_PAD_IS_USABLE (mux->srcpad)) { if ((ret = gst_pad_push (mux->srcpad, buf)) != GST_FLOW_OK)
gst_pad_push (mux->srcpad, buf); break;
} else {
gst_buffer_unref (buf);
}
} }
g_list_free (hbufs); g_list_free (hbufs);
return ret;
} }
/* basic idea: /* this function is called when there is data on all pads.
* *
* 1) find a pad to pull on, this is done by pulling on all pads and * basic idea:
* looking at the buffers to decide which one should be muxed first. *
* 1) find a pad to pull on, this is done by looking at the buffers
* to decide which one should be muxed first.
* 2) store the selected pad and keep on pulling until we fill a * 2) store the selected pad and keep on pulling until we fill a
* complete ogg page or the ogg page is filled above the max-delay * complete ogg page or the ogg page is filled above the max-delay
* threshold. This is needed because the ogg spec says that * threshold. This is needed because the ogg spec says that
@ -841,19 +886,20 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
* spec mandates that the last packet should have the EOS flag set before * spec mandates that the last packet should have the EOS flag set before
* sending it to ogg. * sending it to ogg.
*/ */
static void static GstFlowReturn
gst_ogg_mux_loop (GstElement * element) gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
{ {
GstOggMux *ogg_mux;
GstOggPad *best; GstOggPad *best;
gboolean delta_unit; gboolean delta_unit;
gboolean interrupt = FALSE; GstFlowReturn ret;
ogg_mux = GST_OGG_MUX (element); GST_DEBUG ("collected");
best = gst_ogg_mux_queue_pads (ogg_mux, &interrupt); best = gst_ogg_mux_queue_pads (ogg_mux);
if (interrupt) if (best == NULL)
return; return GST_FLOW_OK;
GST_DEBUG ("best pad %p", best);
/* we're pulling a pad and there is a better one, see if we need /* we're pulling a pad and there is a better one, see if we need
* to flush the current page */ * to flush the current page */
@ -863,13 +909,14 @@ gst_ogg_mux_loop (GstElement * element)
GstClockTime last_ts = GstClockTime last_ts =
GST_BUFFER_TIMESTAMP (pad->buffer) + GST_BUFFER_DURATION (pad->buffer); GST_BUFFER_TIMESTAMP (pad->buffer) + GST_BUFFER_DURATION (pad->buffer);
/* if the next packet in the current page is going to make the page /* if the next packet in the current page is going to make the page
* too long, we need to flush */ * too long, we need to flush */
if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) { if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) {
ogg_page page; ogg_page page;
while (ogg_stream_flush (&pad->stream, &page)) { while (ogg_stream_flush (&pad->stream, &page)) {
gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta); ret = gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta);
/* increment the page number counter */ /* increment the page number counter */
pad->pageno++; pad->pageno++;
/* mark other pages as delta */ /* mark other pages as delta */
@ -888,15 +935,13 @@ gst_ogg_mux_loop (GstElement * element)
ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer); ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer);
} else { } else {
/* no pad to pull on, send EOS */ /* no pad to pull on, send EOS */
if (GST_PAD_IS_USABLE (ogg_mux->srcpad)) gst_pad_push_event (ogg_mux->srcpad, gst_event_new (GST_EVENT_EOS));
gst_pad_push_event (ogg_mux->srcpad, gst_event_new (GST_EVENT_EOS)); return GST_FLOW_WRONG_STATE;
//gst_element_set_eos (element);
return;
} }
} }
if (ogg_mux->need_headers) { if (ogg_mux->need_headers) {
gst_ogg_mux_send_headers (ogg_mux); ret = gst_ogg_mux_send_headers (ogg_mux);
ogg_mux->need_headers = FALSE; ogg_mux->need_headers = FALSE;
} }
@ -912,18 +957,6 @@ gst_ogg_mux_loop (GstElement * element)
/* now see if we have a buffer */ /* now see if we have a buffer */
buf = pad->buffer; buf = pad->buffer;
if (buf == NULL) {
/* no buffer, get one, and store in the pad so we free it later on */
buf = pad->buffer = gst_ogg_mux_next_buffer (pad, &interrupt);
if (interrupt)
return;
/* data exhausted on this pad (EOS) */
if (buf == NULL) {
/* stop pulling from the pad */
ogg_mux->pulling = NULL;
return;
}
}
delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_DELTA_UNIT); delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_DELTA_UNIT);
duration = GST_BUFFER_DURATION (buf); duration = GST_BUFFER_DURATION (buf);
@ -938,6 +971,7 @@ gst_ogg_mux_loop (GstElement * element)
packet.b_o_s = (pad->packetno == 0); packet.b_o_s = (pad->packetno == 0);
packet.packetno = pad->packetno++; packet.packetno = pad->packetno++;
#if 0
/* read ahead one more buffer to find EOS */ /* read ahead one more buffer to find EOS */
tmpbuf = gst_ogg_mux_next_buffer (pad, &interrupt); tmpbuf = gst_ogg_mux_next_buffer (pad, &interrupt);
if (interrupt) if (interrupt)
@ -949,6 +983,10 @@ gst_ogg_mux_loop (GstElement * element)
} }
/* mark EOS */ /* mark EOS */
packet.e_o_s = (tmpbuf == NULL ? 1 : 0); packet.e_o_s = (tmpbuf == NULL ? 1 : 0);
#else
packet.e_o_s = 0;
tmpbuf = NULL;
#endif
/* we flush when we see a new keyframe */ /* we flush when we see a new keyframe */
force_flush = (pad->prev_delta && !delta_unit); force_flush = (pad->prev_delta && !delta_unit);
@ -964,7 +1002,7 @@ gst_ogg_mux_loop (GstElement * element)
/* force flush */ /* force flush */
if (force_flush) { if (force_flush) {
while (ogg_stream_flush (&pad->stream, &page)) { while (ogg_stream_flush (&pad->stream, &page)) {
gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta); ret = gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta);
/* increment the page number counter */ /* increment the page number counter */
pad->pageno++; pad->pageno++;
/* mark other pages as delta */ /* mark other pages as delta */
@ -1004,6 +1042,11 @@ gst_ogg_mux_loop (GstElement * element)
pad->prev_delta = delta_unit; pad->prev_delta = delta_unit;
/* swap the packet in */ /* swap the packet in */
if (packet.e_o_s == 1)
GST_DEBUG_OBJECT (pad, "swapping in EOS packet");
if (packet.b_o_s == 1)
GST_DEBUG_OBJECT (pad, "swapping in BOS packet");
ogg_stream_packetin (&pad->stream, &packet); ogg_stream_packetin (&pad->stream, &packet);
/* don't need the old buffer anymore */ /* don't need the old buffer anymore */
@ -1015,7 +1058,7 @@ gst_ogg_mux_loop (GstElement * element)
* up in more than one page so we need to write them all */ * 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->stream, &page) > 0) {
/* push the page */ /* push the page */
gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta); ret = gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta);
pad->pageno++; pad->pageno++;
/* mark next pages as delta */ /* mark next pages as delta */
pad->first_delta = TRUE; pad->first_delta = TRUE;
@ -1025,7 +1068,7 @@ gst_ogg_mux_loop (GstElement * element)
while (ogg_stream_pageout (&pad->stream, &page) > 0) { while (ogg_stream_pageout (&pad->stream, &page) > 0) {
/* we have a complete page now, we can push the page /* we have a complete page now, we can push the page
* and make sure to pull on a new pad the next time around */ * and make sure to pull on a new pad the next time around */
gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta); ret = gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta);
/* increment the page number counter */ /* increment the page number counter */
pad->pageno++; pad->pageno++;
} }
@ -1037,6 +1080,8 @@ gst_ogg_mux_loop (GstElement * element)
ogg_mux->pulling = NULL; ogg_mux->pulling = NULL;
} }
} }
return GST_FLOW_OK;
} }
static void static void
@ -1085,10 +1130,10 @@ static GstElementStateReturn
gst_ogg_mux_change_state (GstElement * element) gst_ogg_mux_change_state (GstElement * element)
{ {
GstOggMux *ogg_mux; GstOggMux *ogg_mux;
gint transition = GST_STATE_TRANSITION (element); gint transition;
GstElementStateReturn ret;
g_return_val_if_fail (GST_IS_OGG_MUX (element), GST_STATE_FAILURE);
transition = GST_STATE_TRANSITION (element);
ogg_mux = GST_OGG_MUX (element); ogg_mux = GST_OGG_MUX (element);
switch (transition) { switch (transition) {
@ -1097,18 +1142,28 @@ gst_ogg_mux_change_state (GstElement * element)
ogg_mux->next_ts = 0; ogg_mux->next_ts = 0;
ogg_mux->offset = 0; ogg_mux->offset = 0;
ogg_mux->pulling = NULL; ogg_mux->pulling = NULL;
gst_collectpads_start (ogg_mux->collect);
break; break;
case GST_STATE_PAUSED_TO_PLAYING: case GST_STATE_PAUSED_TO_PLAYING:
case GST_STATE_PLAYING_TO_PAUSED: break;
case GST_STATE_PAUSED_TO_READY: default:
case GST_STATE_READY_TO_NULL:
break; break;
} }
if (GST_ELEMENT_CLASS (parent_class)->change_state) ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_STATE_SUCCESS; switch (transition) {
case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
gst_collectpads_stop (ogg_mux->collect);
break;
case GST_STATE_READY_TO_NULL:
break;
default:
break;
}
return ret;
} }
gboolean gboolean