expand tabs
[platform/upstream/gstreamer.git] / ext / ogg / gstoggmux.c
1 /* OGG muxer plugin for GStreamer
2  * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <gst/gst.h>
25 #include <gst/base/gstcollectpads.h>
26
27 #include <ogg/ogg.h>
28 /* memcpy - if someone knows a way to get rid of it, please speak up 
29  * note: the ogg docs even say you need this... */
30 #include <string.h>
31 #include <time.h>
32
33 GST_DEBUG_CATEGORY_STATIC (gst_ogg_mux_debug);
34 #define GST_CAT_DEFAULT gst_ogg_mux_debug
35
36 #define GST_TYPE_OGG_MUX (gst_ogg_mux_get_type())
37 #define GST_OGG_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_MUX, GstOggMux))
38 #define GST_OGG_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_MUX, GstOggMux))
39 #define GST_IS_OGG_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_MUX))
40 #define GST_IS_OGG_MUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_MUX))
41
42 #define GST_GP_FORMAT "[gp %8" G_GINT64_FORMAT "]"
43
44 typedef struct _GstOggMux GstOggMux;
45 typedef struct _GstOggMuxClass GstOggMuxClass;
46
47 typedef enum
48 {
49   GST_OGG_PAD_STATE_CONTROL = 0,
50   GST_OGG_PAD_STATE_DATA = 1
51 }
52 GstOggPadState;
53
54 /* all information needed for one ogg stream */
55 typedef struct
56 {
57   GstCollectData collect;       /* we extend the CollectData */
58
59   GstBuffer *buffer;            /* the queued buffer for this pad */
60
61   gint serial;
62   ogg_stream_state stream;
63   gint64 packetno;              /* number of next packet */
64   gint64 pageno;                /* number of next page */
65   guint64 duration;             /* duration of current page */
66   gboolean eos;
67   gint64 offset;
68   GstClockTime timestamp;       /* timestamp for granulepos of last complete
69                                    packet on this page */
70
71   GstOggPadState state;         /* state of the pad */
72
73   GList *headers;
74
75   GQueue *pagebuffers;          /* List of pages in buffers ready for pushing */
76
77   gboolean new_page;            /* starting a new page */
78   gboolean first_delta;         /* was the first packet in the page a delta */
79   gboolean prev_delta;          /* was the previous buffer a delta frame */
80 }
81 GstOggPad;
82
83 struct _GstOggMux
84 {
85   GstElement element;
86
87   /* source pad */
88   GstPad *srcpad;
89
90   /* sinkpads */
91   GstCollectPads *collect;
92
93   /* the pad we are currently using to fill a page */
94   GstOggPad *pulling;
95
96   /* next timestamp for the page */
97   GstClockTime next_ts;
98
99   /* offset in stream */
100   guint64 offset;
101
102   /* need_headers */
103   gboolean need_headers;
104
105   guint64 max_delay;
106   guint64 max_page_delay;
107
108   GstOggPad *delta_pad;         /* when a delta frame is detected on a stream, we mark
109                                    pages as delta frames up to the page that has the
110                                    keyframe */
111
112 };
113
114 typedef enum
115 {
116   GST_OGG_FLAG_BOS = GST_ELEMENT_FLAG_LAST,
117   GST_OGG_FLAG_EOS
118 }
119 GstOggFlag;
120
121 struct _GstOggMuxClass
122 {
123   GstElementClass parent_class;
124 };
125
126 /* elementfactory information */
127 static GstElementDetails gst_ogg_mux_details = GST_ELEMENT_DETAILS ("ogg muxer",
128     "Codec/Muxer",
129     "mux ogg streams (info about ogg: http://xiph.org)",
130     "Wim Taymans <wim@fluendo.com>");
131
132 /* OggMux signals and args */
133 enum
134 {
135   /* FILL ME */
136   LAST_SIGNAL
137 };
138
139 /* set to 0.5 seconds by default */
140 #define DEFAULT_MAX_DELAY       G_GINT64_CONSTANT(500000000)
141 #define DEFAULT_MAX_PAGE_DELAY  G_GINT64_CONSTANT(500000000)
142 enum
143 {
144   ARG_0,
145   ARG_MAX_DELAY,
146   ARG_MAX_PAGE_DELAY,
147 };
148
149 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
150     GST_PAD_SRC,
151     GST_PAD_ALWAYS,
152     GST_STATIC_CAPS ("application/ogg")
153     );
154
155 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
156     GST_PAD_SINK,
157     GST_PAD_REQUEST,
158     GST_STATIC_CAPS ("video/x-theora; "
159         "audio/x-vorbis; audio/x-flac; audio/x-speex; "
160         "application/x-ogm-video; application/x-ogm-audio")
161     );
162
163 static void gst_ogg_mux_base_init (gpointer g_class);
164 static void gst_ogg_mux_class_init (GstOggMuxClass * klass);
165 static void gst_ogg_mux_init (GstOggMux * ogg_mux);
166 static void gst_ogg_mux_finalize (GObject * object);
167
168 static GstFlowReturn
169 gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux);
170 static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event);
171 static GstPad *gst_ogg_mux_request_new_pad (GstElement * element,
172     GstPadTemplate * templ, const gchar * name);
173 static void gst_ogg_mux_set_property (GObject * object,
174     guint prop_id, const GValue * value, GParamSpec * pspec);
175 static void gst_ogg_mux_get_property (GObject * object,
176     guint prop_id, GValue * value, GParamSpec * pspec);
177 static GstStateChangeReturn gst_ogg_mux_change_state (GstElement * element,
178     GstStateChange transition);
179
180 static GstElementClass *parent_class = NULL;
181
182 /*static guint gst_ogg_mux_signals[LAST_SIGNAL] = { 0 }; */
183
184 GType
185 gst_ogg_mux_get_type (void)
186 {
187   static GType ogg_mux_type = 0;
188
189   if (!ogg_mux_type) {
190     static const GTypeInfo ogg_mux_info = {
191       sizeof (GstOggMuxClass),
192       gst_ogg_mux_base_init,
193       NULL,
194       (GClassInitFunc) gst_ogg_mux_class_init,
195       NULL,
196       NULL,
197       sizeof (GstOggMux),
198       0,
199       (GInstanceInitFunc) gst_ogg_mux_init,
200     };
201
202     ogg_mux_type =
203         g_type_register_static (GST_TYPE_ELEMENT, "GstOggMux", &ogg_mux_info,
204         0);
205   }
206   return ogg_mux_type;
207 }
208
209 static void
210 gst_ogg_mux_base_init (gpointer g_class)
211 {
212   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
213
214   gst_element_class_add_pad_template (element_class,
215       gst_static_pad_template_get (&src_factory));
216   gst_element_class_add_pad_template (element_class,
217       gst_static_pad_template_get (&sink_factory));
218
219   gst_element_class_set_details (element_class, &gst_ogg_mux_details);
220 }
221
222 static void
223 gst_ogg_mux_class_init (GstOggMuxClass * klass)
224 {
225   GObjectClass *gobject_class;
226   GstElementClass *gstelement_class;
227
228   gobject_class = (GObjectClass *) klass;
229   gstelement_class = (GstElementClass *) klass;
230
231   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
232
233   gobject_class->finalize = gst_ogg_mux_finalize;
234   gobject_class->get_property = gst_ogg_mux_get_property;
235   gobject_class->set_property = gst_ogg_mux_set_property;
236
237   gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad;
238
239   g_object_class_install_property (gobject_class, ARG_MAX_DELAY,
240       g_param_spec_uint64 ("max-delay", "Max delay",
241           "Maximum delay in multiplexing streams", 0, G_MAXUINT64,
242           DEFAULT_MAX_DELAY, (GParamFlags) G_PARAM_READWRITE));
243   g_object_class_install_property (gobject_class, ARG_MAX_PAGE_DELAY,
244       g_param_spec_uint64 ("max-page-delay", "Max page delay",
245           "Maximum delay for sending out a page", 0, G_MAXUINT64,
246           DEFAULT_MAX_PAGE_DELAY, (GParamFlags) G_PARAM_READWRITE));
247
248   gstelement_class->change_state = gst_ogg_mux_change_state;
249
250 }
251
252 #if 0
253 static const GstEventMask *
254 gst_ogg_mux_get_sink_event_masks (GstPad * pad)
255 {
256   static const GstEventMask gst_ogg_mux_sink_event_masks[] = {
257     {GST_EVENT_EOS, 0},
258     {GST_EVENT_DISCONTINUOUS, 0},
259     {0,}
260   };
261
262   return gst_ogg_mux_sink_event_masks;
263 }
264 #endif
265
266 static void
267 gst_ogg_mux_clear (GstOggMux * ogg_mux)
268 {
269   ogg_mux->pulling = NULL;
270   ogg_mux->need_headers = TRUE;
271   ogg_mux->max_delay = DEFAULT_MAX_DELAY;
272   ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;
273   ogg_mux->delta_pad = NULL;
274 }
275
276 static void
277 gst_ogg_mux_init (GstOggMux * ogg_mux)
278 {
279   GstElementClass *klass = GST_ELEMENT_GET_CLASS (ogg_mux);
280
281   ogg_mux->srcpad =
282       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
283           "src"), "src");
284   gst_pad_set_event_function (ogg_mux->srcpad, gst_ogg_mux_handle_src_event);
285   gst_element_add_pad (GST_ELEMENT (ogg_mux), ogg_mux->srcpad);
286
287   GST_OBJECT_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS);
288
289   /* seed random number generator for creation of serial numbers */
290   srand (time (NULL));
291
292   ogg_mux->collect = gst_collect_pads_new ();
293   gst_collect_pads_set_function (ogg_mux->collect,
294       (GstCollectPadsFunction) gst_ogg_mux_collected, ogg_mux);
295
296   gst_ogg_mux_clear (ogg_mux);
297 }
298
299 static void
300 gst_ogg_mux_finalize (GObject * object)
301 {
302   GstOggMux *ogg_mux;
303
304   ogg_mux = GST_OGG_MUX (object);
305
306   if (ogg_mux->collect) {
307     gst_object_unref (ogg_mux->collect);
308     ogg_mux->collect = NULL;
309   }
310
311   G_OBJECT_CLASS (parent_class)->finalize (object);
312 }
313
314 static GstPadLinkReturn
315 gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer)
316 {
317   GstOggMux *ogg_mux;
318   gchar *name;
319
320   ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
321
322   name = gst_pad_get_name (pad);
323
324   GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s", name);
325
326   g_free (name);
327
328   return GST_PAD_LINK_OK;
329 }
330
331 static GstPad *
332 gst_ogg_mux_request_new_pad (GstElement * element,
333     GstPadTemplate * templ, const gchar * req_name)
334 {
335   GstOggMux *ogg_mux;
336   GstPad *newpad;
337   GstElementClass *klass;
338
339   g_return_val_if_fail (templ != NULL, NULL);
340
341   if (templ->direction != GST_PAD_SINK)
342     goto wrong_direction;
343
344   g_return_val_if_fail (GST_IS_OGG_MUX (element), NULL);
345   ogg_mux = GST_OGG_MUX (element);
346
347   klass = GST_ELEMENT_GET_CLASS (element);
348
349   if (templ != gst_element_class_get_pad_template (klass, "sink_%d"))
350     goto wrong_template;
351
352   {
353     gint serial;
354     gchar *name;
355
356     if (req_name == NULL || strlen (req_name) < 6) {
357       /* no name given when requesting the pad, use random serial number */
358       serial = rand ();
359     } else {
360       /* parse serial number from requested padname */
361       serial = atoi (&req_name[5]);
362     }
363     /* create new pad with the name */
364     name = g_strdup_printf ("sink_%d", serial);
365     newpad = gst_pad_new_from_template (templ, name);
366     g_free (name);
367
368     /* construct our own wrapper data structure for the pad to
369      * keep track of its status */
370     {
371       GstOggPad *oggpad;
372
373       oggpad = (GstOggPad *)
374           gst_collect_pads_add_pad (ogg_mux->collect, newpad,
375           sizeof (GstOggPad));
376
377       oggpad->serial = serial;
378       ogg_stream_init (&oggpad->stream, serial);
379       oggpad->packetno = 0;
380       oggpad->pageno = 0;
381       oggpad->eos = FALSE;
382       /* we assume there will be some control data first for this pad */
383       oggpad->state = GST_OGG_PAD_STATE_CONTROL;
384       oggpad->new_page = TRUE;
385       oggpad->first_delta = FALSE;
386       oggpad->prev_delta = FALSE;
387       /* TODO: delete this queue (and the things contained within) later,
388        * possibly when doing gst_collect_pads_remove_pad() (which we don't seem
389        * to do at all?)
390        */
391       oggpad->pagebuffers = g_queue_new ();
392     }
393   }
394
395   /* setup some pad functions */
396   gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect);
397   /* dd the pad to the element */
398   gst_element_add_pad (element, newpad);
399
400   return newpad;
401
402   /* ERRORS */
403 wrong_direction:
404   {
405     g_warning ("ogg_mux: request pad that is not a SINK pad\n");
406     return NULL;
407   }
408 wrong_template:
409   {
410     g_warning ("ogg_mux: this is not our template!\n");
411     return NULL;
412   }
413 }
414
415 /* handle events */
416 static gboolean
417 gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event)
418 {
419   GstOggMux *ogg_mux;
420   GstEventType type;
421
422   ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
423
424   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
425
426   switch (type) {
427     case GST_EVENT_SEEK:
428       /* disable seeking for now */
429       return FALSE;
430     default:
431       break;
432   }
433
434   return gst_pad_event_default (pad, event);
435 }
436
437 static GstBuffer *
438 gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
439 {
440   GstBuffer *buffer;
441
442   /* allocate space for header and body */
443   buffer = gst_buffer_new_and_alloc (page->header_len + page->body_len);
444   memcpy (GST_BUFFER_DATA (buffer), page->header, page->header_len);
445   memcpy (GST_BUFFER_DATA (buffer) + page->header_len,
446       page->body, page->body_len);
447
448   /* next_ts was the timestamp of the first buffer put in this page */
449   GST_BUFFER_TIMESTAMP (buffer) = mux->next_ts;
450   GST_BUFFER_OFFSET (buffer) = mux->offset;
451   mux->offset += GST_BUFFER_SIZE (buffer);
452   /* Here we set granulepos as our OFFSET_END to give easy direct access to
453    * this value later. Before we push it, we reset this to OFFSET + SIZE
454    * (see gst_ogg_mux_push_buffer). */
455   GST_BUFFER_OFFSET_END (buffer) = ogg_page_granulepos (page);
456   if (delta)
457     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
458
459   return buffer;
460 }
461
462 static GstFlowReturn
463 gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer)
464 {
465   GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET (buffer) +
466       GST_BUFFER_SIZE (buffer);
467
468   return gst_pad_push (mux->srcpad, buffer);
469 }
470
471 /* if all queues have at least one page, dequeue the page with the lowest
472  * timestamp */
473 static gboolean
474 gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
475 {
476   GSList *walk;
477   GstOggPad *opad = NULL;       /* "oldest" pad */
478   GstClockTime oldest = GST_CLOCK_TIME_NONE;
479   GstBuffer *buf = NULL;
480   gboolean ret = FALSE;
481
482   *flowret = GST_FLOW_OK;
483
484   walk = mux->collect->data;
485   while (walk) {
486     GstOggPad *pad = (GstOggPad *) walk->data;
487
488     /* We need each queue to either be at EOS, or have one or more pages
489      * available with a set granulepos (i.e. not -1), otherwise we don't have
490      * enough data yet to determine which stream needs to go next for correct
491      * time ordering. */
492     if (pad->pagebuffers->length == 0) {
493       if (pad->eos) {
494         GST_LOG_OBJECT (pad, "pad is EOS, skipping for dequeue decision");
495       } else {
496         GST_LOG_OBJECT (pad, "no pages in this queue, can't dequeue");
497         return FALSE;
498       }
499     } else {
500       /* We then need to check for a non-negative granulepos */
501       int i;
502       gboolean valid = FALSE;
503
504       for (i = 0; i < pad->pagebuffers->length; i++) {
505         buf = g_queue_peek_nth (pad->pagebuffers, i);
506         /* Here we check the OFFSET_END, which is actually temporarily the
507          * granulepos value for this buffer */
508         if (GST_BUFFER_OFFSET_END (buf) != -1) {
509           valid = TRUE;
510           break;
511         }
512       }
513       if (!valid) {
514         GST_LOG_OBJECT (pad, "No page timestamps in queue, can't dequeue");
515         return FALSE;
516       }
517     }
518
519     walk = g_slist_next (walk);
520   }
521
522   walk = mux->collect->data;
523   while (walk) {
524     GstOggPad *pad = (GstOggPad *) walk->data;
525
526     /* any page with a granulepos of -1 can be pushed immediately. 
527      * TODO: it CAN be, but it seems silly to do so? */
528     buf = g_queue_peek_head (pad->pagebuffers);
529     while (buf && GST_BUFFER_OFFSET_END (buf) == -1) {
530       GST_LOG_OBJECT (pad, GST_GP_FORMAT " pushing page", -1);
531       g_queue_pop_head (pad->pagebuffers);
532       *flowret = gst_ogg_mux_push_buffer (mux, buf);
533       buf = g_queue_peek_head (pad->pagebuffers);
534       ret = TRUE;
535     }
536
537     if (buf) {
538       /* if no oldest buffer yet, take this one */
539       if (oldest == GST_CLOCK_TIME_NONE) {
540         oldest = GST_BUFFER_TIMESTAMP (buf);
541         opad = pad;
542       } else {
543         /* if we have an oldest, compare with this one */
544         if (GST_BUFFER_TIMESTAMP (buf) < oldest) {
545           oldest = GST_BUFFER_TIMESTAMP (buf);
546           opad = pad;
547         }
548       }
549     }
550     walk = g_slist_next (walk);
551   }
552
553   if (oldest != GST_CLOCK_TIME_NONE) {
554     g_assert (opad);
555     buf = g_queue_pop_head (opad->pagebuffers);
556     GST_LOG_OBJECT (opad,
557         GST_GP_FORMAT " pushing oldest page (time %" GST_TIME_FORMAT ")",
558         GST_BUFFER_OFFSET_END (buf),
559         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
560     *flowret = gst_ogg_mux_push_buffer (mux, buf);
561     ret = TRUE;
562   }
563
564   return ret;
565 }
566
567 /* put the given page on a per-pad queue, timestamping it correctly.
568  * after that, dequeue and push as many pages as possible
569  * before calling me, make sure that the the pad's timestamp matches
570  * the page's granulepos */
571 static GstFlowReturn
572 gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPad * pad, ogg_page * page,
573     gboolean delta)
574 {
575   GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
576   GstFlowReturn ret;
577
578   /* take the timestamp of the last completed packet on this page */
579   GST_BUFFER_TIMESTAMP (buffer) = pad->timestamp;
580   g_queue_push_tail (pad->pagebuffers, buffer);
581   GST_LOG_OBJECT (pad, GST_GP_FORMAT " queued buffer page (time %" GST_TIME_FORMAT "), %d page buffers queued", ogg_page_granulepos (page), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), pad->pagebuffers->length);   /* no g_queue_get_length in 2.2 */
582
583   while (gst_ogg_mux_dequeue_page (mux, &ret)) {
584     if (ret != GST_FLOW_OK)
585       break;
586   }
587
588   return ret;
589 }
590
591 /*
592  * Given two pads, compare the buffers queued on it and return 0 if they have
593  * an equal priority, 1 if the new pad is better, -1 if the old pad is better 
594  */
595 static gint
596 gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPad * old, GstOggPad * new)
597 {
598   guint64 oldtime, newtime;
599
600   /* if the old pad doesn't contain anything or is even NULL, return 
601    * the new pad as best candidate and vice versa */
602   if (old == NULL || old->buffer == NULL)
603     return 1;
604   if (new == NULL || new->buffer == NULL)
605     return -1;
606
607   /* no timestamp on old buffer, it must go first */
608   oldtime = GST_BUFFER_TIMESTAMP (old->buffer);
609   if (oldtime == GST_CLOCK_TIME_NONE)
610     return -1;
611
612   /* no timestamp on new buffer, it must go first */
613   newtime = GST_BUFFER_TIMESTAMP (new->buffer);
614   if (newtime == GST_CLOCK_TIME_NONE)
615     return 1;
616
617   /* old buffer has higher timestamp, new one should go first */
618   if (newtime < oldtime)
619     return 1;
620   /* new buffer has higher timestamp, old one should go first */
621   else if (newtime > oldtime)
622     return -1;
623   else {
624     /* buffers with equal timestamps, prefer the pad that has the
625      * least number of pages muxed */
626     if (new->pageno < old->pageno)
627       return 1;
628     else if (new->pageno > old->pageno)
629       return -1;
630   }
631
632   /* same priority if all of the above failed */
633   return 0;
634 }
635
636 /* make sure a buffer is queued on all pads, returns a pointer to an oggpad
637  * that holds the best buffer or NULL when no pad was usable.
638  * "best" means the buffer marked with the lowest timestamp */
639 static GstOggPad *
640 gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
641 {
642   GstOggPad *bestpad = NULL, *still_hungry = NULL;
643   GSList *walk;
644
645   /* try to make sure we have a buffer from each usable pad first */
646   walk = ogg_mux->collect->data;
647   while (walk) {
648     GstOggPad *pad;
649     GstCollectData *data;
650
651     data = (GstCollectData *) walk->data;
652     pad = (GstOggPad *) data;
653
654     walk = g_slist_next (walk);
655
656     GST_DEBUG_OBJECT (ogg_mux, "looking at pad %p", pad);
657
658     /* try to get a new buffer for this pad if needed and possible */
659     if (pad->buffer == NULL) {
660       GstBuffer *buf;
661       gboolean incaps;
662
663       buf = gst_collect_pads_pop (ogg_mux->collect, data);
664       GST_DEBUG_OBJECT (ogg_mux, "popping buffer %p", buf);
665
666       /* On EOS we get a NULL buffer */
667       if (buf != NULL) {
668         incaps = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
669         /* if we need headers */
670         if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
671           /* and we have one */
672           if (incaps) {
673             GST_DEBUG_OBJECT (ogg_mux,
674                 "got incaps buffer in control state, ignoring");
675             /* just ignore */
676             gst_buffer_unref (buf);
677             buf = NULL;
678           } else {
679             GST_DEBUG_OBJECT (ogg_mux,
680                 "got data buffer in control state, switching " "to data mode");
681             /* this is a data buffer so switch to data state */
682             pad->state = GST_OGG_PAD_STATE_DATA;
683           }
684         }
685       } else {
686         GST_DEBUG_OBJECT (ogg_mux, "EOS on pad");
687         pad->eos = TRUE;
688       }
689
690       pad->buffer = buf;
691     }
692
693     /* we should have a buffer now, see if it is the best pad to
694      * pull on */
695     if (pad->buffer) {
696       if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) {
697         GST_DEBUG_OBJECT (ogg_mux, "best pad now %p", pad);
698         bestpad = pad;
699       }
700     } else if (!pad->eos) {
701       GST_DEBUG_OBJECT (ogg_mux, "hungry pad %p", pad);
702       still_hungry = pad;
703     }
704   }
705
706   if (still_hungry)
707     /* drop back into collectpads... */
708     return still_hungry;
709   else
710     return bestpad;
711 }
712
713 static GList *
714 gst_ogg_mux_get_headers (GstOggPad * pad)
715 {
716   GList *res = NULL;
717   GstOggMux *ogg_mux;
718   GstStructure *structure;
719   const GstCaps *caps;
720   GstPad *thepad;
721
722   thepad = pad->collect.pad;
723
724   ogg_mux = GST_OGG_MUX (GST_PAD_PARENT (thepad));
725
726   GST_LOG_OBJECT (thepad, "getting headers");
727
728   caps = gst_pad_get_negotiated_caps (thepad);
729   if (caps != NULL) {
730     const GValue *streamheader;
731
732     structure = gst_caps_get_structure (caps, 0);
733     streamheader = gst_structure_get_value (structure, "streamheader");
734     if (streamheader != NULL) {
735       GST_LOG_OBJECT (thepad, "got header");
736       if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
737         GArray *bufarr = g_value_peek_pointer (streamheader);
738         gint i;
739
740         GST_LOG_OBJECT (thepad, "got fixed list");
741
742         for (i = 0; i < bufarr->len; i++) {
743           GValue *bufval = &g_array_index (bufarr, GValue, i);
744
745           GST_LOG_OBJECT (thepad, "item %d", i);
746           if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
747             GstBuffer *buf = g_value_peek_pointer (bufval);
748
749             GST_LOG_OBJECT (thepad, "adding item %d to header list", i);
750
751             gst_buffer_ref (buf);
752             res = g_list_append (res, buf);
753           }
754         }
755       } else {
756         GST_LOG_OBJECT (thepad, "streamheader is not fixed list");
757       }
758     } else {
759       GST_LOG_OBJECT (thepad, "caps done have streamheader");
760     }
761   } else {
762     GST_LOG_OBJECT (thepad, "got empty caps as negotiated format");
763   }
764   return res;
765 }
766
767 static GstCaps *
768 gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
769 {
770   GstStructure *structure;
771   GValue array = { 0 };
772   GList *walk = buffers;
773
774   caps = gst_caps_make_writable (caps);
775
776   structure = gst_caps_get_structure (caps, 0);
777
778   /* put buffers in a fixed list */
779   g_value_init (&array, GST_TYPE_ARRAY);
780
781   while (walk) {
782     GstBuffer *buf = GST_BUFFER (walk->data);
783     GValue value = { 0 };
784
785     walk = walk->next;
786
787     /* mark buffer */
788     GST_LOG ("Setting IN_CAPS on buffer of length %d", GST_BUFFER_SIZE (buf));
789     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
790
791     g_value_init (&value, GST_TYPE_BUFFER);
792     gst_value_set_buffer (&value, buf);
793     gst_value_array_append_value (&array, &value);
794     g_value_unset (&value);
795   }
796   gst_structure_set_value (structure, "streamheader", &array);
797   g_value_unset (&array);
798
799   return caps;
800 }
801
802 /*
803  * For each pad we need to write out one (small) header in one
804  * page that allows decoders to identify the type of the stream.
805  * After that we need to write out all extra info for the decoders.
806  * In the case of a codec that also needs data as configuration, we can
807  * find that info in the streamcaps. 
808  * After writing the headers we must start a new page for the data.
809  */
810 static GstFlowReturn
811 gst_ogg_mux_send_headers (GstOggMux * mux)
812 {
813   GSList *walk;
814   GList *hbufs, *hwalk;
815   GstCaps *caps;
816   GstFlowReturn ret;
817
818   hbufs = NULL;
819   ret = GST_FLOW_OK;
820
821   GST_LOG_OBJECT (mux, "collecting headers");
822
823   walk = mux->collect->data;
824   while (walk) {
825     GstOggPad *pad;
826     GstPad *thepad;
827
828     pad = (GstOggPad *) walk->data;
829     thepad = pad->collect.pad;
830
831     walk = g_slist_next (walk);
832
833     GST_LOG_OBJECT (mux, "looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
834
835     /* if the pad has no buffer, we don't care */
836     if (pad->buffer == NULL)
837       continue;
838
839     /* now figure out the headers */
840     pad->headers = gst_ogg_mux_get_headers (pad);
841   }
842
843   GST_LOG_OBJECT (mux, "creating first headers");
844   walk = mux->collect->data;
845   while (walk) {
846     GstOggPad *pad;
847     GstBuffer *buf;
848     ogg_packet packet;
849     ogg_page page;
850     GstPad *thepad;
851
852     pad = (GstOggPad *) walk->data;
853     thepad = pad->collect.pad;
854
855     walk = walk->next;
856
857     pad->packetno = 0;
858
859     GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
860         GST_DEBUG_PAD_NAME (thepad));
861
862     if (pad->headers) {
863       buf = GST_BUFFER (pad->headers->data);
864       pad->headers = g_list_remove (pad->headers, buf);
865     } else if (pad->buffer) {
866       buf = pad->buffer;
867       gst_buffer_ref (buf);
868     } else {
869       /* fixme -- should be caught in the previous list traversal. */
870       GST_OBJECT_LOCK (pad);
871       g_critical ("No headers or buffers on pad %s:%s",
872           GST_DEBUG_PAD_NAME (pad));
873       GST_OBJECT_UNLOCK (pad);
874       continue;
875     }
876
877     /* create a packet from the buffer */
878     packet.packet = GST_BUFFER_DATA (buf);
879     packet.bytes = GST_BUFFER_SIZE (buf);
880     packet.granulepos = GST_BUFFER_OFFSET_END (buf);
881     if (packet.granulepos == -1)
882       packet.granulepos = 0;
883     /* mark BOS and packet number */
884     packet.b_o_s = (pad->packetno == 0);
885     packet.packetno = pad->packetno++;
886     /* mark EOS */
887     packet.e_o_s = 0;
888
889     /* swap the packet in */
890     ogg_stream_packetin (&pad->stream, &packet);
891     gst_buffer_unref (buf);
892
893     GST_LOG_OBJECT (mux, "flushing page with first packet");
894     while (ogg_stream_flush (&pad->stream, &page)) {
895       GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
896
897       GST_LOG_OBJECT (mux, "swapped out page");
898       hbufs = g_list_append (hbufs, hbuf);
899     }
900   }
901
902   GST_LOG_OBJECT (mux, "creating next headers");
903   walk = mux->collect->data;
904   while (walk) {
905     GstOggPad *pad;
906     GstPad *thepad;
907
908     pad = (GstOggPad *) walk->data;
909     thepad = pad->collect.pad;
910
911     walk = walk->next;
912
913     GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
914         GST_DEBUG_PAD_NAME (thepad));
915
916     hwalk = pad->headers;
917     while (hwalk) {
918       GstBuffer *buf = GST_BUFFER (hwalk->data);
919       ogg_packet packet;
920       ogg_page page;
921
922       hwalk = hwalk->next;
923
924       /* create a packet from the buffer */
925       packet.packet = GST_BUFFER_DATA (buf);
926       packet.bytes = GST_BUFFER_SIZE (buf);
927       packet.granulepos = GST_BUFFER_OFFSET_END (buf);
928       if (packet.granulepos == -1)
929         packet.granulepos = 0;
930       /* mark BOS and packet number */
931       packet.b_o_s = (pad->packetno == 0);
932       packet.packetno = pad->packetno++;
933       /* mark EOS */
934       packet.e_o_s = 0;
935
936       /* swap the packet in */
937       ogg_stream_packetin (&pad->stream, &packet);
938       gst_buffer_unref (buf);
939
940       /* if last header, flush page */
941       if (hwalk == NULL) {
942         GST_LOG_OBJECT (mux,
943             "flushing page as packet %d is first or last packet",
944             pad->packetno);
945         while (ogg_stream_flush (&pad->stream, &page)) {
946           GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
947
948           GST_LOG_OBJECT (mux, "swapped out page");
949           hbufs = g_list_append (hbufs, hbuf);
950         }
951       } else {
952         GST_LOG_OBJECT (mux, "try to swap out page");
953         /* just try to swap out a page then */
954         while (ogg_stream_pageout (&pad->stream, &page) > 0) {
955           GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
956
957           GST_LOG_OBJECT (mux, "swapped out page");
958           hbufs = g_list_append (hbufs, hbuf);
959         }
960       }
961     }
962     g_list_free (pad->headers);
963     pad->headers = NULL;
964   }
965   /* hbufs holds all buffers for the headers now */
966
967   /* create caps with the buffers */
968   caps = gst_pad_get_caps (mux->srcpad);
969   if (caps) {
970     caps = gst_ogg_mux_set_header_on_caps (caps, hbufs);
971     gst_pad_set_caps (mux->srcpad, caps);
972     gst_caps_unref (caps);
973   }
974   /* and send the buffers */
975   hwalk = hbufs;
976   while (hwalk) {
977     GstBuffer *buf = GST_BUFFER (hwalk->data);
978
979     hwalk = hwalk->next;
980
981     if ((ret = gst_ogg_mux_push_buffer (mux, buf)) != GST_FLOW_OK)
982       break;
983   }
984   g_list_free (hbufs);
985
986   return ret;
987 }
988
989 /* this function is called when there is data on all pads.
990  *
991  * basic idea:
992  *
993  * 1) find a pad to pull on, this is done by looking at the buffers 
994  *    to decide which one to use, we use the 'oldest' one first.
995  * 2) store the selected pad and keep on pulling until we fill a 
996  *    complete ogg page or the ogg page is filled above the max-delay 
997  *    threshold. This is needed because the ogg spec says that
998  *    you should fill a complete page with data from the same logical
999  *    stream. When the page is filled, go back to 1).
1000  * 3) before filling a page, read ahead one more buffer to see if this
1001  *    packet is the last of the stream. We need to do this because the ogg
1002  *    spec mandates that the last packet should have the EOS flag set before
1003  *    sending it to ogg. FIXME: Apparently we're allowed to send empty 'nil'
1004  *    pages with the EOS flag set for EOS, so we could do this. Not sure how
1005  *    that works, though. TODO: 'read ahead one more buffer' is a bit funky
1006  *    with collectpads. Rethink this.
1007  * 4) pages get queued on a per-pad queue. Every time a page is queued, a
1008  *    dequeue is called, which will dequeue the oldest page on any pad, provided
1009  *    that ALL pads have at least one marked page in the queue (or remaining
1010  *    pad are at EOS)
1011  */
1012 static GstFlowReturn
1013 gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
1014 {
1015   GstOggPad *best;
1016   gboolean delta_unit;
1017   GstFlowReturn ret;
1018   gint64 granulepos = 0;
1019   GstClockTime timestamp;
1020
1021   GST_DEBUG_OBJECT (ogg_mux, "collected");
1022
1023   /* queue buffers on all pads; find a buffer with the lowest timestamp */
1024   best = gst_ogg_mux_queue_pads (ogg_mux);
1025   if (best && !best->buffer)
1026     return GST_FLOW_OK;
1027
1028   GST_DEBUG_OBJECT (ogg_mux, "best pad %p, pulling %p", best, ogg_mux->pulling);
1029
1030   if (!best) {                  /* EOS : FIXME !! We need to handle EOS correctly, and set EOS
1031                                    flags on the ogg pages. */
1032     GST_DEBUG_OBJECT (ogg_mux, "Pushing EOS");
1033     gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1034     return GST_FLOW_WRONG_STATE;
1035   }
1036
1037   /* if we were already pulling from one pad, but the new "best" buffer is
1038    * from another pad, we need to check if we have reason to flush a page
1039    * for the pad we were pulling from before */
1040   if (ogg_mux->pulling && best &&
1041       ogg_mux->pulling != best && ogg_mux->pulling->buffer) {
1042     GstOggPad *pad = ogg_mux->pulling;
1043
1044     GstClockTime last_ts =
1045         GST_BUFFER_TIMESTAMP (pad->buffer) + GST_BUFFER_DURATION (pad->buffer);
1046
1047     /* if the next packet in the current page is going to make the page 
1048      * too long, we need to flush */
1049     if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) {
1050       ogg_page page;
1051
1052       while (ogg_stream_flush (&pad->stream, &page)) {
1053         /* Place page into the per-pad queue */
1054         ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1055             pad->first_delta);
1056         /* increment the page number counter */
1057         pad->pageno++;
1058         /* mark other pages as delta */
1059         pad->first_delta = TRUE;
1060       }
1061       pad->new_page = TRUE;
1062       ogg_mux->pulling = NULL;
1063     }
1064   }
1065
1066   if (ogg_mux->need_headers) {
1067     ret = gst_ogg_mux_send_headers (ogg_mux);
1068     ogg_mux->need_headers = FALSE;
1069   }
1070
1071   /* if we don't know which pad to pull on, use the best one */
1072   if (ogg_mux->pulling == NULL) {
1073     ogg_mux->pulling = best;
1074     GST_DEBUG_OBJECT (ogg_mux, "pulling now %p", ogg_mux->pulling);
1075     /* remember timestamp of first buffer for this new pad */
1076     if (ogg_mux->pulling != NULL) {
1077       ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer);
1078     } else {
1079       /* no pad to pull on, send EOS */
1080       gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1081       return GST_FLOW_WRONG_STATE;
1082     }
1083   }
1084
1085   /* we are pulling from a pad, continue to do so until a page
1086    * has been filled and queued */
1087   if (ogg_mux->pulling != NULL) {
1088     ogg_packet packet;
1089     ogg_page page;
1090     GstBuffer *buf, *tmpbuf;
1091     GstOggPad *pad = ogg_mux->pulling;
1092     gint64 duration;
1093     gboolean force_flush;
1094
1095     GST_DEBUG_OBJECT (ogg_mux, "pulling now %p", ogg_mux->pulling);
1096
1097     /* now see if we have a buffer */
1098     buf = pad->buffer;
1099     if (buf == NULL) {
1100       GST_DEBUG_OBJECT (ogg_mux, "pad was EOS");
1101       ogg_mux->pulling = NULL;
1102       return GST_FLOW_OK;
1103     }
1104
1105     delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
1106     duration = GST_BUFFER_DURATION (buf);
1107
1108     /* create a packet from the buffer */
1109     packet.packet = GST_BUFFER_DATA (buf);
1110     packet.bytes = GST_BUFFER_SIZE (buf);
1111     packet.granulepos = GST_BUFFER_OFFSET_END (buf);
1112     if (packet.granulepos == -1)
1113       packet.granulepos = 0;
1114     /* mark BOS and packet number */
1115     packet.b_o_s = (pad->packetno == 0);
1116     packet.packetno = pad->packetno++;
1117
1118     packet.e_o_s = 0;
1119     tmpbuf = NULL;
1120
1121     /* we flush when we see a new keyframe */
1122     force_flush = (pad->prev_delta && !delta_unit);
1123     if (duration != -1) {
1124       pad->duration += duration;
1125       /* if page duration exceeds max, flush page */
1126       if (pad->duration > ogg_mux->max_page_delay) {
1127         force_flush = TRUE;
1128         pad->duration = 0;
1129       }
1130     }
1131
1132     /* flush the currently built page if neccesary */
1133     if (force_flush) {
1134       while (ogg_stream_flush (&pad->stream, &page)) {
1135         ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1136             pad->first_delta);
1137         /* increment the page number counter */
1138         pad->pageno++;
1139         /* mark other pages as delta */
1140         pad->first_delta = TRUE;
1141       }
1142       pad->new_page = TRUE;
1143     }
1144
1145     /* if this is the first packet of a new page figure out the delta flag */
1146     if (pad->new_page) {
1147       if (delta_unit) {
1148         /* This page is a delta frame */
1149         if (ogg_mux->delta_pad == NULL) {
1150           /* we got a delta unit on this pad */
1151           ogg_mux->delta_pad = pad;
1152         }
1153         /* mark the page as delta */
1154         pad->first_delta = TRUE;
1155       } else {
1156         /* got a keyframe */
1157         if (ogg_mux->delta_pad == pad) {
1158           /* if we get it on the pad with deltaunits,
1159            * we mark the page as non delta */
1160           pad->first_delta = FALSE;
1161         } else if (ogg_mux->delta_pad != NULL) {
1162           /* if there are pads with delta frames, we
1163            * must mark this one as delta */
1164           pad->first_delta = TRUE;
1165         } else {
1166           pad->first_delta = FALSE;
1167         }
1168       }
1169       pad->new_page = FALSE;
1170     }
1171
1172     /* save key unit to track delta->key unit transitions */
1173     pad->prev_delta = delta_unit;
1174
1175     /* swap the packet in */
1176     if (packet.e_o_s == 1)
1177       GST_DEBUG_OBJECT (pad, "swapping in EOS packet");
1178     if (packet.b_o_s == 1)
1179       GST_DEBUG_OBJECT (pad, "swapping in BOS packet");
1180
1181     ogg_stream_packetin (&pad->stream, &packet);
1182
1183     granulepos = GST_BUFFER_OFFSET_END (pad->buffer);
1184     timestamp = GST_BUFFER_TIMESTAMP (pad->buffer);
1185
1186     /* don't need the old buffer anymore */
1187     gst_buffer_unref (pad->buffer);
1188     /* store new readahead buffer */
1189     pad->buffer = tmpbuf;
1190
1191     /* let ogg write out the pages now. The packet we got could end 
1192      * up in more than one page so we need to write them all */
1193     if (ogg_stream_pageout (&pad->stream, &page) > 0) {
1194       if (ogg_page_granulepos (&page) == granulepos) {
1195         /* the packet we streamed in finishes on the page,
1196          * because the page's granulepos is the granulepos of the last
1197          * packet completed on that page,
1198          * so update the timestamp that we will give to the page */
1199         pad->timestamp = timestamp;
1200         GST_DEBUG_OBJECT (pad, "Timestamp of pad is %" GST_TIME_FORMAT
1201             ", granulepos is %lld", GST_TIME_ARGS (timestamp), granulepos);
1202       }
1203
1204       /* push the page */
1205       ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, pad->first_delta);
1206       pad->pageno++;
1207       /* mark next pages as delta */
1208       pad->first_delta = TRUE;
1209
1210       /* use an inner loop here to flush the remaining pages and
1211        * mark them as delta frames as well */
1212       while (ogg_stream_pageout (&pad->stream, &page) > 0) {
1213         if (ogg_page_granulepos (&page) == granulepos) {
1214           /* the page has taken up the new packet completely, which means
1215            * the packet ends the page and we can update the timestamp
1216            * before pushing out */
1217           pad->timestamp = timestamp;
1218         }
1219
1220         /* we have a complete page now, we can push the page 
1221          * and make sure to pull on a new pad the next time around */
1222         ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1223             pad->first_delta);
1224         /* increment the page number counter */
1225         pad->pageno++;
1226       }
1227       /* need a new page as well */
1228       pad->new_page = TRUE;
1229       pad->duration = 0;
1230       /* we're done pulling on this pad, make sure to choose a new 
1231        * pad for pulling in the next iteration */
1232       ogg_mux->pulling = NULL;
1233     }
1234
1235     /* Update the timestamp, if neccesary, since and future page will have at
1236      * least this timestamp.
1237      */
1238     if (pad->timestamp < timestamp) {
1239       pad->timestamp = timestamp;
1240       GST_DEBUG_OBJECT (pad, "Updated timestamp of pad to %" GST_TIME_FORMAT,
1241           GST_TIME_ARGS (timestamp));
1242     }
1243   }
1244
1245   return GST_FLOW_OK;
1246 }
1247
1248 static void
1249 gst_ogg_mux_get_property (GObject * object,
1250     guint prop_id, GValue * value, GParamSpec * pspec)
1251 {
1252   GstOggMux *ogg_mux;
1253
1254   ogg_mux = GST_OGG_MUX (object);
1255
1256   switch (prop_id) {
1257     case ARG_MAX_DELAY:
1258       g_value_set_uint64 (value, ogg_mux->max_delay);
1259       break;
1260     case ARG_MAX_PAGE_DELAY:
1261       g_value_set_uint64 (value, ogg_mux->max_page_delay);
1262       break;
1263     default:
1264       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1265       break;
1266   }
1267 }
1268
1269 static void
1270 gst_ogg_mux_set_property (GObject * object,
1271     guint prop_id, const GValue * value, GParamSpec * pspec)
1272 {
1273   GstOggMux *ogg_mux;
1274
1275   ogg_mux = GST_OGG_MUX (object);
1276
1277   switch (prop_id) {
1278     case ARG_MAX_DELAY:
1279       ogg_mux->max_delay = g_value_get_uint64 (value);
1280       break;
1281     case ARG_MAX_PAGE_DELAY:
1282       ogg_mux->max_page_delay = g_value_get_uint64 (value);
1283       break;
1284     default:
1285       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1286       break;
1287   }
1288 }
1289
1290 static void
1291 gst_ogg_mux_clear_collectpads (GstCollectPads * collect)
1292 {
1293   GSList *walk;
1294
1295   walk = collect->data;
1296   while (walk) {
1297     GstOggPad *pad = (GstOggPad *) walk->data;
1298     GstBuffer *buf;
1299
1300     ogg_stream_clear (&pad->stream);
1301
1302     while ((buf = g_queue_pop_head (pad->pagebuffers)) != NULL) {
1303       gst_buffer_unref (buf);
1304     }
1305     g_queue_free (pad->pagebuffers);
1306
1307     gst_collect_pads_remove_pad (collect, ((GstCollectData *) pad)->pad);
1308     walk = collect->data;
1309   }
1310 }
1311
1312 static GstStateChangeReturn
1313 gst_ogg_mux_change_state (GstElement * element, GstStateChange transition)
1314 {
1315   GstOggMux *ogg_mux;
1316   GstStateChangeReturn ret;
1317
1318   ogg_mux = GST_OGG_MUX (element);
1319
1320   switch (transition) {
1321     case GST_STATE_CHANGE_NULL_TO_READY:
1322       break;
1323     case GST_STATE_CHANGE_READY_TO_PAUSED:
1324       ogg_mux->next_ts = 0;
1325       ogg_mux->offset = 0;
1326       ogg_mux->pulling = NULL;
1327       gst_collect_pads_start (ogg_mux->collect);
1328       gst_ogg_mux_clear (ogg_mux);
1329       break;
1330     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1331       break;
1332     case GST_STATE_CHANGE_PAUSED_TO_READY:
1333       gst_collect_pads_stop (ogg_mux->collect);
1334       break;
1335     default:
1336       break;
1337   }
1338
1339   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1340
1341   switch (transition) {
1342     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1343       break;
1344     case GST_STATE_CHANGE_READY_TO_NULL:
1345       gst_ogg_mux_clear_collectpads (ogg_mux->collect);
1346       break;
1347     default:
1348       break;
1349   }
1350
1351   return ret;
1352 }
1353
1354 gboolean
1355 gst_ogg_mux_plugin_init (GstPlugin * plugin)
1356 {
1357   GST_DEBUG_CATEGORY_INIT (gst_ogg_mux_debug, "oggmux", 0, "ogg muxer");
1358
1359   return gst_element_register (plugin, "oggmux", GST_RANK_NONE,
1360       GST_TYPE_OGG_MUX);
1361 }