update for _get_caps() -> _query_caps()
[platform/upstream/gstreamer.git] / ext / ogg / gstoggmux.c
1 /* OGG muxer plugin for GStreamer
2  * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
3  * Copyright (C) 2006 Thomas Vander Stichele <thomas at apestaart dot org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:element-oggmux
23  * @see_also: <link linkend="gst-plugins-base-plugins-oggdemux">oggdemux</link>
24  *
25  * This element merges streams (audio and video) into ogg files.
26  *
27  * <refsect2>
28  * <title>Example pipelines</title>
29  * |[
30  * gst-launch v4l2src num-buffers=500 ! video/x-raw,width=320,height=240 ! videoconvert ! theoraenc ! oggmux ! filesink location=video.ogg
31  * ]| Encodes a video stream captured from a v4l2-compatible camera to Ogg/Theora
32  * (the encoding will stop automatically after 500 frames)
33  * </refsect2>
34  *
35  * Last reviewed on 2008-02-06 (0.10.17)
36  */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include <gst/gst.h>
43 #include <gst/base/gstbytewriter.h>
44 #include <gst/tag/tag.h>
45
46 #include "gstoggmux.h"
47
48 /* memcpy - if someone knows a way to get rid of it, please speak up
49  * note: the ogg docs even say you need this... */
50 #include <string.h>
51 #include <time.h>
52 #include <stdlib.h>             /* rand, srand, atoi */
53
54 GST_DEBUG_CATEGORY_STATIC (gst_ogg_mux_debug);
55 #define GST_CAT_DEFAULT gst_ogg_mux_debug
56
57 /* This isn't generally what you'd want with an end-time macro, because
58    technically the end time of a buffer with invalid duration is invalid. But
59    for sorting ogg pages this is what we want. */
60 #define GST_BUFFER_END_TIME(buf) \
61     (GST_BUFFER_DURATION_IS_VALID (buf) \
62     ? GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf) \
63     : GST_BUFFER_TIMESTAMP (buf))
64
65 #define GST_GP_FORMAT "[gp %8" G_GINT64_FORMAT "]"
66 #define GST_GP_CAST(_gp) ((gint64) _gp)
67
68 typedef enum
69 {
70   GST_OGG_FLAG_BOS = GST_ELEMENT_FLAG_LAST,
71   GST_OGG_FLAG_EOS
72 }
73 GstOggFlag;
74
75 /* OggMux signals and args */
76 enum
77 {
78   /* FILL ME */
79   LAST_SIGNAL
80 };
81
82 /* set to 0.5 seconds by default */
83 #define DEFAULT_MAX_DELAY       G_GINT64_CONSTANT(500000000)
84 #define DEFAULT_MAX_PAGE_DELAY  G_GINT64_CONSTANT(500000000)
85 #define DEFAULT_MAX_TOLERANCE   G_GINT64_CONSTANT(40000000)
86 #define DEFAULT_SKELETON        FALSE
87
88 enum
89 {
90   ARG_0,
91   ARG_MAX_DELAY,
92   ARG_MAX_PAGE_DELAY,
93   ARG_MAX_TOLERANCE,
94   ARG_SKELETON
95 };
96
97 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
98     GST_PAD_SRC,
99     GST_PAD_ALWAYS,
100     GST_STATIC_CAPS ("application/ogg")
101     );
102
103 static GstStaticPadTemplate video_sink_factory =
104     GST_STATIC_PAD_TEMPLATE ("video_%u",
105     GST_PAD_SINK,
106     GST_PAD_REQUEST,
107     GST_STATIC_CAPS ("video/x-theora; "
108         "application/x-ogm-video; video/x-dirac; "
109         "video/x-smoke; video/x-vp8; ")
110     );
111
112 static GstStaticPadTemplate audio_sink_factory =
113     GST_STATIC_PAD_TEMPLATE ("audio_%u",
114     GST_PAD_SINK,
115     GST_PAD_REQUEST,
116     GST_STATIC_CAPS
117     ("audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; "
118         "application/x-ogm-audio; ")
119     );
120
121 static GstStaticPadTemplate subtitle_sink_factory =
122     GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
123     GST_PAD_SINK,
124     GST_PAD_REQUEST,
125     GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) TRUE; "
126         "subtitle/x-kate; application/x-kate")
127     );
128
129 static void gst_ogg_mux_finalize (GObject * object);
130
131 static GstFlowReturn
132 gst_ogg_mux_collected (GstCollectPads2 * pads, GstOggMux * ogg_mux);
133 static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event);
134 static GstPad *gst_ogg_mux_request_new_pad (GstElement * element,
135     GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
136 static void gst_ogg_mux_release_pad (GstElement * element, GstPad * pad);
137
138 static void gst_ogg_mux_set_property (GObject * object,
139     guint prop_id, const GValue * value, GParamSpec * pspec);
140 static void gst_ogg_mux_get_property (GObject * object,
141     guint prop_id, GValue * value, GParamSpec * pspec);
142 static GstStateChangeReturn gst_ogg_mux_change_state (GstElement * element,
143     GstStateChange transition);
144
145 /*static guint gst_ogg_mux_signals[LAST_SIGNAL] = { 0 }; */
146 #define gst_ogg_mux_parent_class parent_class
147 G_DEFINE_TYPE_WITH_CODE (GstOggMux, gst_ogg_mux, GST_TYPE_ELEMENT,
148     G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
149
150 static void
151 gst_ogg_mux_class_init (GstOggMuxClass * klass)
152 {
153   GObjectClass *gobject_class;
154   GstElementClass *gstelement_class;
155
156   gobject_class = (GObjectClass *) klass;
157   gstelement_class = (GstElementClass *) klass;
158
159   gobject_class->finalize = gst_ogg_mux_finalize;
160   gobject_class->get_property = gst_ogg_mux_get_property;
161   gobject_class->set_property = gst_ogg_mux_set_property;
162
163   gst_element_class_add_pad_template (gstelement_class,
164       gst_static_pad_template_get (&src_factory));
165   gst_element_class_add_pad_template (gstelement_class,
166       gst_static_pad_template_get (&video_sink_factory));
167   gst_element_class_add_pad_template (gstelement_class,
168       gst_static_pad_template_get (&audio_sink_factory));
169   gst_element_class_add_pad_template (gstelement_class,
170       gst_static_pad_template_get (&subtitle_sink_factory));
171
172   gst_element_class_set_details_simple (gstelement_class,
173       "Ogg muxer", "Codec/Muxer",
174       "mux ogg streams (info about ogg: http://xiph.org)",
175       "Wim Taymans <wim@fluendo.com>");
176
177   gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad;
178   gstelement_class->release_pad = gst_ogg_mux_release_pad;
179
180   g_object_class_install_property (gobject_class, ARG_MAX_DELAY,
181       g_param_spec_uint64 ("max-delay", "Max delay",
182           "Maximum delay in multiplexing streams", 0, G_MAXUINT64,
183           DEFAULT_MAX_DELAY,
184           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
185   g_object_class_install_property (gobject_class, ARG_MAX_PAGE_DELAY,
186       g_param_spec_uint64 ("max-page-delay", "Max page delay",
187           "Maximum delay for sending out a page", 0, G_MAXUINT64,
188           DEFAULT_MAX_PAGE_DELAY,
189           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
190   g_object_class_install_property (gobject_class, ARG_MAX_TOLERANCE,
191       g_param_spec_uint64 ("max-tolerance", "Max time tolerance",
192           "Maximum timestamp difference for maintaining perfect granules",
193           0, G_MAXUINT64, DEFAULT_MAX_TOLERANCE,
194           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
195   g_object_class_install_property (gobject_class, ARG_SKELETON,
196       g_param_spec_boolean ("skeleton", "Skeleton",
197           "Whether to include a Skeleton track",
198           DEFAULT_SKELETON,
199           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
200
201   gstelement_class->change_state = gst_ogg_mux_change_state;
202
203 }
204
205 #if 0
206 static const GstEventMask *
207 gst_ogg_mux_get_sink_event_masks (GstPad * pad)
208 {
209   static const GstEventMask gst_ogg_mux_sink_event_masks[] = {
210     {GST_EVENT_EOS, 0},
211     {GST_EVENT_DISCONTINUOUS, 0},
212     {0,}
213   };
214
215   return gst_ogg_mux_sink_event_masks;
216 }
217 #endif
218
219 static void
220 gst_ogg_mux_clear (GstOggMux * ogg_mux)
221 {
222   ogg_mux->pulling = NULL;
223   ogg_mux->need_headers = TRUE;
224   ogg_mux->delta_pad = NULL;
225   ogg_mux->offset = 0;
226   ogg_mux->next_ts = 0;
227   ogg_mux->last_ts = GST_CLOCK_TIME_NONE;
228 }
229
230 static void
231 gst_ogg_mux_init (GstOggMux * ogg_mux)
232 {
233   GstElementClass *klass = GST_ELEMENT_GET_CLASS (ogg_mux);
234
235   ogg_mux->srcpad =
236       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
237           "src"), "src");
238   gst_pad_set_event_function (ogg_mux->srcpad, gst_ogg_mux_handle_src_event);
239   gst_element_add_pad (GST_ELEMENT (ogg_mux), ogg_mux->srcpad);
240
241   GST_OBJECT_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS);
242
243   /* seed random number generator for creation of serial numbers */
244   srand (time (NULL));
245
246   ogg_mux->collect = gst_collect_pads2_new ();
247   gst_collect_pads2_set_function (ogg_mux->collect,
248       (GstCollectPads2Function) GST_DEBUG_FUNCPTR (gst_ogg_mux_collected),
249       ogg_mux);
250
251   ogg_mux->max_delay = DEFAULT_MAX_DELAY;
252   ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;
253   ogg_mux->max_tolerance = DEFAULT_MAX_TOLERANCE;
254
255   gst_ogg_mux_clear (ogg_mux);
256 }
257
258 static void
259 gst_ogg_mux_finalize (GObject * object)
260 {
261   GstOggMux *ogg_mux;
262
263   ogg_mux = GST_OGG_MUX (object);
264
265   if (ogg_mux->collect) {
266     gst_object_unref (ogg_mux->collect);
267     ogg_mux->collect = NULL;
268   }
269
270   G_OBJECT_CLASS (parent_class)->finalize (object);
271 }
272
273 static void
274 gst_ogg_mux_ogg_pad_destroy_notify (GstCollectData2 * data)
275 {
276   GstOggPadData *oggpad = (GstOggPadData *) data;
277   GstBuffer *buf;
278
279   ogg_stream_clear (&oggpad->map.stream);
280   gst_caps_replace (&oggpad->map.caps, NULL);
281
282   if (oggpad->pagebuffers) {
283     while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
284       gst_buffer_unref (buf);
285     }
286     g_queue_free (oggpad->pagebuffers);
287     oggpad->pagebuffers = NULL;
288   }
289 }
290
291 static GstPadLinkReturn
292 gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer)
293 {
294   GstOggMux *ogg_mux;
295
296   ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
297
298   GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
299
300   gst_object_unref (ogg_mux);
301
302   return GST_PAD_LINK_OK;
303 }
304
305 static gboolean
306 gst_ogg_mux_sink_event (GstPad * pad, GstEvent * event)
307 {
308   GstOggMux *ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
309   GstOggPadData *ogg_pad = (GstOggPadData *) gst_pad_get_element_private (pad);
310   gboolean ret = FALSE;
311
312   GST_DEBUG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
313
314   switch (GST_EVENT_TYPE (event)) {
315     case GST_EVENT_SEGMENT:
316     {
317       const GstSegment *segment;
318
319       gst_event_parse_segment (event, &segment);
320
321       /* We don't support non time NEWSEGMENT events */
322       if (segment->format != GST_FORMAT_TIME) {
323         gst_event_unref (event);
324         event = NULL;
325         break;
326       }
327
328       gst_segment_copy_into (segment, &ogg_pad->segment);
329       break;
330     }
331     case GST_EVENT_FLUSH_STOP:{
332       gst_segment_init (&ogg_pad->segment, GST_FORMAT_TIME);
333       break;
334     }
335     case GST_EVENT_TAG:{
336       GstTagList *tags;
337
338       gst_event_parse_tag (event, &tags);
339       tags = gst_tag_list_merge (ogg_pad->tags, tags, GST_TAG_MERGE_APPEND);
340       if (ogg_pad->tags)
341         gst_tag_list_free (ogg_pad->tags);
342       ogg_pad->tags = tags;
343
344       GST_DEBUG_OBJECT (ogg_mux, "Got tags %" GST_PTR_FORMAT, ogg_pad->tags);
345       break;
346     }
347     default:
348       break;
349   }
350
351   /* now GstCollectPads can take care of the rest, e.g. EOS */
352   if (event != NULL)
353     ret = ogg_pad->collect_event (pad, event);
354
355   gst_object_unref (ogg_mux);
356   return ret;
357 }
358
359 static gboolean
360 gst_ogg_mux_is_serialno_present (GstOggMux * ogg_mux, guint32 serialno)
361 {
362   GSList *walk;
363
364   walk = ogg_mux->collect->data;
365   while (walk) {
366     GstOggPadData *pad = (GstOggPadData *) walk->data;
367     if (pad->map.serialno == serialno)
368       return TRUE;
369     walk = walk->next;
370   }
371
372   return FALSE;
373 }
374
375 static guint32
376 gst_ogg_mux_generate_serialno (GstOggMux * ogg_mux)
377 {
378   guint32 serialno;
379
380   do {
381     serialno = g_random_int_range (0, G_MAXINT32);
382   } while (gst_ogg_mux_is_serialno_present (ogg_mux, serialno));
383
384   return serialno;
385 }
386
387 static GstPad *
388 gst_ogg_mux_request_new_pad (GstElement * element,
389     GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
390 {
391   GstOggMux *ogg_mux;
392   GstPad *newpad;
393   GstElementClass *klass;
394
395   g_return_val_if_fail (templ != NULL, NULL);
396
397   if (templ->direction != GST_PAD_SINK)
398     goto wrong_direction;
399
400   g_return_val_if_fail (GST_IS_OGG_MUX (element), NULL);
401   ogg_mux = GST_OGG_MUX (element);
402
403   klass = GST_ELEMENT_GET_CLASS (element);
404
405   if (templ != gst_element_class_get_pad_template (klass, "video_%u") &&
406       templ != gst_element_class_get_pad_template (klass, "audio_%u") &&
407       templ != gst_element_class_get_pad_template (klass, "subtitle_%u")) {
408     goto wrong_template;
409   }
410
411   {
412     guint32 serial;
413     gchar *name = NULL;
414
415     if (req_name == NULL || strlen (req_name) < 6) {
416       /* no name given when requesting the pad, use random serial number */
417       serial = gst_ogg_mux_generate_serialno (ogg_mux);
418     } else {
419       /* parse serial number from requested padname */
420       unsigned long long_serial;
421       char *endptr = NULL;
422       long_serial = strtoul (&req_name[5], &endptr, 10);
423       if ((endptr && *endptr) || (long_serial & ~0xffffffff)) {
424         GST_WARNING_OBJECT (ogg_mux, "Invalid serial number specification: %s",
425             req_name + 5);
426         return NULL;
427       }
428       serial = (guint32) long_serial;
429     }
430     /* create new pad with the name */
431     GST_DEBUG_OBJECT (ogg_mux, "Creating new pad for serial %d", serial);
432
433     if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
434       name = g_strdup_printf ("video_%u", serial);
435     } else if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
436       name = g_strdup_printf ("audio_%u", serial);
437     } else if (templ == gst_element_class_get_pad_template (klass,
438             "subtitle_%u")) {
439       name = g_strdup_printf ("subtitle_%u", serial);
440     }
441     newpad = gst_pad_new_from_template (templ, name);
442     g_free (name);
443
444     /* construct our own wrapper data structure for the pad to
445      * keep track of its status */
446     {
447       GstOggPadData *oggpad;
448
449       oggpad = (GstOggPadData *)
450           gst_collect_pads2_add_pad_full (ogg_mux->collect, newpad,
451           sizeof (GstOggPadData), gst_ogg_mux_ogg_pad_destroy_notify, TRUE);
452       ogg_mux->active_pads++;
453
454       oggpad->map.serialno = serial;
455       ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
456       oggpad->packetno = 0;
457       oggpad->pageno = 0;
458       oggpad->eos = FALSE;
459       /* we assume there will be some control data first for this pad */
460       oggpad->state = GST_OGG_PAD_STATE_CONTROL;
461       oggpad->new_page = TRUE;
462       oggpad->first_delta = FALSE;
463       oggpad->prev_delta = FALSE;
464       oggpad->data_pushed = FALSE;
465       oggpad->pagebuffers = g_queue_new ();
466       oggpad->map.headers = NULL;
467       oggpad->map.queued = NULL;
468       oggpad->next_granule = 0;
469       oggpad->keyframe_granule = -1;
470
471       gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
472
473       oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
474       gst_pad_set_event_function (newpad,
475           GST_DEBUG_FUNCPTR (gst_ogg_mux_sink_event));
476     }
477   }
478
479   /* setup some pad functions */
480   gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect);
481
482   /* dd the pad to the element */
483   gst_element_add_pad (element, newpad);
484
485   return newpad;
486
487   /* ERRORS */
488 wrong_direction:
489   {
490     g_warning ("ogg_mux: request pad that is not a SINK pad\n");
491     return NULL;
492   }
493 wrong_template:
494   {
495     g_warning ("ogg_mux: this is not our template!\n");
496     return NULL;
497   }
498 }
499
500 static void
501 gst_ogg_mux_release_pad (GstElement * element, GstPad * pad)
502 {
503   GstOggMux *ogg_mux;
504
505   ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
506
507   gst_collect_pads2_remove_pad (ogg_mux->collect, pad);
508   gst_element_remove_pad (element, pad);
509
510   gst_object_unref (ogg_mux);
511 }
512
513 /* handle events */
514 static gboolean
515 gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event)
516 {
517   GstEventType type;
518
519   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
520
521   switch (type) {
522     case GST_EVENT_SEEK:
523       /* disable seeking for now */
524       return FALSE;
525     default:
526       break;
527   }
528
529   return gst_pad_event_default (pad, event);
530 }
531
532 static GstBuffer *
533 gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
534 {
535   GstBuffer *buffer;
536
537   /* allocate space for header and body */
538   buffer = gst_buffer_new_and_alloc (page->header_len + page->body_len);
539   gst_buffer_fill (buffer, 0, page->header, page->header_len);
540   gst_buffer_fill (buffer, page->header_len, page->body, page->body_len);
541
542   /* Here we set granulepos as our OFFSET_END to give easy direct access to
543    * this value later. Before we push it, we reset this to OFFSET + SIZE
544    * (see gst_ogg_mux_push_buffer). */
545   GST_BUFFER_OFFSET_END (buffer) = ogg_page_granulepos (page);
546   if (delta)
547     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
548
549   GST_LOG_OBJECT (mux, GST_GP_FORMAT
550       " created buffer %p from ogg page",
551       GST_GP_CAST (ogg_page_granulepos (page)), buffer);
552
553   return buffer;
554 }
555
556 static GstFlowReturn
557 gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer,
558     GstOggPadData * oggpad)
559 {
560   /* fix up OFFSET and OFFSET_END again */
561   GST_BUFFER_OFFSET (buffer) = mux->offset;
562   mux->offset += gst_buffer_get_size (buffer);
563   GST_BUFFER_OFFSET_END (buffer) = mux->offset;
564
565   /* Ensure we have monotonically increasing timestamps in the output. */
566   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
567     gint64 run_time = GST_BUFFER_TIMESTAMP (buffer);
568     if (mux->last_ts != GST_CLOCK_TIME_NONE && run_time < mux->last_ts)
569       GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
570     else
571       mux->last_ts = run_time;
572   }
573
574   return gst_pad_push (mux->srcpad, buffer);
575 }
576
577 /* if all queues have at least one page, dequeue the page with the lowest
578  * timestamp */
579 static gboolean
580 gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
581 {
582   GSList *walk;
583   GstOggPadData *opad = NULL;   /* "oldest" pad */
584   GstClockTime oldest = GST_CLOCK_TIME_NONE;
585   GstBuffer *buf = NULL;
586   gboolean ret = FALSE;
587
588   *flowret = GST_FLOW_OK;
589
590   walk = mux->collect->data;
591   while (walk) {
592     GstOggPadData *pad = (GstOggPadData *) walk->data;
593
594     /* We need each queue to either be at EOS, or have one or more pages
595      * available with a set granulepos (i.e. not -1), otherwise we don't have
596      * enough data yet to determine which stream needs to go next for correct
597      * time ordering. */
598     if (pad->pagebuffers->length == 0) {
599       if (pad->eos) {
600         GST_LOG_OBJECT (pad->collect.pad,
601             "pad is EOS, skipping for dequeue decision");
602       } else {
603         GST_LOG_OBJECT (pad->collect.pad,
604             "no pages in this queue, can't dequeue");
605         return FALSE;
606       }
607     } else {
608       /* We then need to check for a non-negative granulepos */
609       int i;
610       gboolean valid = FALSE;
611
612       for (i = 0; i < pad->pagebuffers->length; i++) {
613         buf = g_queue_peek_nth (pad->pagebuffers, i);
614         /* Here we check the OFFSET_END, which is actually temporarily the
615          * granulepos value for this buffer */
616         if (GST_BUFFER_OFFSET_END (buf) != -1) {
617           valid = TRUE;
618           break;
619         }
620       }
621       if (!valid) {
622         GST_LOG_OBJECT (pad->collect.pad,
623             "No page timestamps in queue, can't dequeue");
624         return FALSE;
625       }
626     }
627
628     walk = g_slist_next (walk);
629   }
630
631   walk = mux->collect->data;
632   while (walk) {
633     GstOggPadData *pad = (GstOggPadData *) walk->data;
634
635     /* any page with a granulepos of -1 can be pushed immediately.
636      * TODO: it CAN be, but it seems silly to do so? */
637     buf = g_queue_peek_head (pad->pagebuffers);
638     while (buf && GST_BUFFER_OFFSET_END (buf) == -1) {
639       GST_LOG_OBJECT (pad->collect.pad, "[gp        -1] pushing page");
640       g_queue_pop_head (pad->pagebuffers);
641       *flowret = gst_ogg_mux_push_buffer (mux, buf, pad);
642       buf = g_queue_peek_head (pad->pagebuffers);
643       ret = TRUE;
644     }
645
646     if (buf) {
647       /* if no oldest buffer yet, take this one */
648       if (oldest == GST_CLOCK_TIME_NONE) {
649         GST_LOG_OBJECT (mux, "no oldest yet, taking buffer %p from pad %"
650             GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
651             buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
652         oldest = GST_BUFFER_OFFSET (buf);
653         opad = pad;
654       } else {
655         /* if we have an oldest, compare with this one */
656         if (GST_BUFFER_OFFSET (buf) < oldest) {
657           GST_LOG_OBJECT (mux, "older buffer %p, taking from pad %"
658               GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
659               buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
660           oldest = GST_BUFFER_OFFSET (buf);
661           opad = pad;
662         }
663       }
664     }
665     walk = g_slist_next (walk);
666   }
667
668   if (oldest != GST_CLOCK_TIME_NONE) {
669     g_assert (opad);
670     buf = g_queue_pop_head (opad->pagebuffers);
671     GST_LOG_OBJECT (opad->collect.pad,
672         GST_GP_FORMAT " pushing oldest page buffer %p (granulepos time %"
673         GST_TIME_FORMAT ")", GST_BUFFER_OFFSET_END (buf), buf,
674         GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
675     *flowret = gst_ogg_mux_push_buffer (mux, buf, opad);
676     ret = TRUE;
677   }
678
679   return ret;
680 }
681
682 /* put the given ogg page on a per-pad queue, timestamping it correctly.
683  * after that, dequeue and push as many pages as possible.
684  * Caller should make sure:
685  * pad->timestamp     was set with the timestamp of the first packet put
686  *                    on the page
687  * pad->timestamp_end was set with the timestamp + duration of the last packet
688  *                    put on the page
689  * pad->gp_time       was set with the time matching the gp of the last
690  *                    packet put on the page
691  *
692  * will also reset timestamp and timestamp_end, so caller func can restart
693  * counting.
694  */
695 static GstFlowReturn
696 gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPadData * pad,
697     ogg_page * page, gboolean delta)
698 {
699   GstFlowReturn ret;
700   GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
701
702   /* take the timestamp of the first packet on this page */
703   GST_BUFFER_TIMESTAMP (buffer) = pad->timestamp;
704   GST_BUFFER_DURATION (buffer) = pad->timestamp_end - pad->timestamp;
705   /* take the gp time of the last completed packet on this page */
706   GST_BUFFER_OFFSET (buffer) = pad->gp_time;
707
708   /* the next page will start where the current page's end time leaves off */
709   pad->timestamp = pad->timestamp_end;
710
711   g_queue_push_tail (pad->pagebuffers, buffer);
712   GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
713       " queued buffer page %p (gp time %"
714       GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT
715       "), %d page buffers queued", GST_GP_CAST (ogg_page_granulepos (page)),
716       buffer, GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)),
717       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
718       g_queue_get_length (pad->pagebuffers));
719
720   while (gst_ogg_mux_dequeue_page (mux, &ret)) {
721     if (ret != GST_FLOW_OK)
722       break;
723   }
724
725   return ret;
726 }
727
728 /*
729  * Given two pads, compare the buffers queued on it.
730  * Returns:
731  *  0 if they have an equal priority
732  * -1 if the first is better
733  *  1 if the second is better
734  * Priority decided by: a) validity, b) older timestamp, c) smaller number
735  * of muxed pages
736  */
737 static gint
738 gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPadData * first,
739     GstOggPadData * second)
740 {
741   guint64 firsttime, secondtime;
742
743   /* if the first pad doesn't contain anything or is even NULL, return
744    * the second pad as best candidate and vice versa */
745   if (first == NULL)
746     return 1;
747   if (second == NULL)
748     return -1;
749
750   /* no timestamp on first buffer, it must go first */
751   firsttime = GST_BUFFER_TIMESTAMP (first->buffer);
752   if (firsttime == GST_CLOCK_TIME_NONE)
753     return -1;
754
755   /* no timestamp on second buffer, it must go first */
756   secondtime = GST_BUFFER_TIMESTAMP (second->buffer);
757   if (secondtime == GST_CLOCK_TIME_NONE)
758     return 1;
759
760   /* first buffer has higher timestamp, second one should go first */
761   if (secondtime < firsttime)
762     return 1;
763   /* second buffer has higher timestamp, first one should go first */
764   else if (secondtime > firsttime)
765     return -1;
766   else {
767     /* buffers with equal timestamps, prefer the pad that has the
768      * least number of pages muxed */
769     if (second->pageno < first->pageno)
770       return 1;
771     else if (second->pageno > first->pageno)
772       return -1;
773   }
774
775   /* same priority if all of the above failed */
776   return 0;
777 }
778
779 static GstBuffer *
780 gst_ogg_mux_decorate_buffer (GstOggMux * ogg_mux, GstOggPadData * pad,
781     GstBuffer * buf)
782 {
783   GstClockTime time;
784   gint64 duration, granule, limit;
785   GstClockTime next_time;
786   GstClockTimeDiff diff;
787   ogg_packet packet;
788   gsize size;
789
790   /* ensure messing with metadata is ok */
791   buf = gst_buffer_make_writable (buf);
792
793   /* convert time to running time, so we need no longer bother about that */
794   time = GST_BUFFER_TIMESTAMP (buf);
795   if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
796     time = gst_segment_to_running_time (&pad->segment, GST_FORMAT_TIME, time);
797     if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
798       gst_buffer_unref (buf);
799       return NULL;
800     } else {
801       GST_BUFFER_TIMESTAMP (buf) = time;
802     }
803   }
804
805   /* now come up with granulepos stuff corresponding to time */
806   if (!pad->have_type ||
807       pad->map.granulerate_n <= 0 || pad->map.granulerate_d <= 0)
808     goto no_granule;
809
810   packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
811   packet.bytes = size;
812   duration = gst_ogg_stream_get_packet_duration (&pad->map, &packet);
813   gst_buffer_unmap (buf, packet.packet, size);
814
815   /* give up if no duration can be determined, relying on upstream */
816   if (G_UNLIKELY (duration < 0)) {
817     /* well, if some day we really could handle sparse input ... */
818     if (pad->map.is_sparse) {
819       limit = 1;
820       diff = 2;
821       goto resync;
822     }
823     GST_WARNING_OBJECT (pad->collect.pad,
824         "failed to determine packet duration");
825     goto no_granule;
826   }
827
828   GST_LOG_OBJECT (pad->collect.pad, "buffer ts %" GST_TIME_FORMAT
829       ", duration %" GST_TIME_FORMAT ", granule duration %" G_GINT64_FORMAT,
830       GST_TIME_ARGS (time), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
831       duration);
832
833   /* determine granule corresponding to time,
834    * using the inverse of oggdemux' granule -> time */
835
836   /* see if interpolated granule matches good enough */
837   granule = pad->next_granule;
838   next_time = gst_ogg_stream_granule_to_time (&pad->map, pad->next_granule);
839   diff = GST_CLOCK_DIFF (next_time, time);
840
841   /* we tolerate deviation up to configured or within granule granularity */
842   limit = gst_ogg_stream_granule_to_time (&pad->map, 1) / 2;
843   limit = MAX (limit, ogg_mux->max_tolerance);
844
845   GST_LOG_OBJECT (pad->collect.pad, "expected granule %" G_GINT64_FORMAT " == "
846       "time %" GST_TIME_FORMAT " --> ts diff %" GST_TIME_FORMAT
847       " < tolerance %" GST_TIME_FORMAT " (?)",
848       granule, GST_TIME_ARGS (next_time), GST_TIME_ARGS (ABS (diff)),
849       GST_TIME_ARGS (limit));
850
851 resync:
852   /* if not good enough, determine granule based on time */
853   if (diff > limit || diff < -limit) {
854     granule = gst_util_uint64_scale_round (time, pad->map.granulerate_n,
855         GST_SECOND * pad->map.granulerate_d);
856     GST_DEBUG_OBJECT (pad->collect.pad,
857         "resyncing to determined granule %" G_GINT64_FORMAT, granule);
858   }
859
860   if (pad->map.is_ogm || pad->map.is_sparse) {
861     pad->next_granule = granule;
862   } else {
863     granule += duration;
864     pad->next_granule = granule;
865   }
866
867   /* track previous keyframe */
868   if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
869     pad->keyframe_granule = granule;
870
871   /* determine corresponding time and granulepos */
872   GST_BUFFER_OFFSET (buf) = gst_ogg_stream_granule_to_time (&pad->map, granule);
873   GST_BUFFER_OFFSET_END (buf) =
874       gst_ogg_stream_granule_to_granulepos (&pad->map, granule,
875       pad->keyframe_granule);
876
877   return buf;
878
879   /* ERRORS */
880 no_granule:
881   {
882     GST_DEBUG_OBJECT (pad->collect.pad, "could not determine granulepos, "
883         "falling back to upstream provided metadata");
884     return buf;
885   }
886 }
887
888
889 /* make sure at least one buffer is queued on all pads, two if possible
890  * 
891  * if pad->buffer == NULL, pad->next_buffer !=  NULL, then
892  *   we do not know if the buffer is the last or not
893  * if pad->buffer != NULL, pad->next_buffer != NULL, then
894  *   pad->buffer is not the last buffer for the pad
895  * if pad->buffer != NULL, pad->next_buffer == NULL, then
896  *   pad->buffer if the last buffer for the pad
897  * 
898  * returns a pointer to an oggpad that holds the best buffer, or
899  * NULL when no pad was usable. "best" means the buffer marked
900  * with the lowest timestamp. If best->buffer == NULL then either
901  * we're at EOS (popped = FALSE), or a buffer got dropped, so retry. */
902 static GstOggPadData *
903 gst_ogg_mux_queue_pads (GstOggMux * ogg_mux, gboolean * popped)
904 {
905   GstOggPadData *bestpad = NULL;
906   GSList *walk;
907
908   *popped = FALSE;
909
910   /* try to make sure we have a buffer from each usable pad first */
911   walk = ogg_mux->collect->data;
912   while (walk) {
913     GstOggPadData *pad;
914     GstCollectData2 *data;
915
916     data = (GstCollectData2 *) walk->data;
917     pad = (GstOggPadData *) data;
918
919     walk = g_slist_next (walk);
920
921     GST_LOG_OBJECT (data->pad, "looking at pad for buffer");
922
923     /* try to get a new buffer for this pad if needed and possible */
924     if (pad->buffer == NULL) {
925       GstBuffer *buf;
926
927       buf = gst_collect_pads2_pop (ogg_mux->collect, data);
928       GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
929
930       /* On EOS we get a NULL buffer */
931       if (buf != NULL) {
932         *popped = TRUE;
933
934         if (ogg_mux->delta_pad == NULL &&
935             GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
936           ogg_mux->delta_pad = pad;
937
938         /* if we need headers */
939         if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
940           /* and we have one */
941           ogg_packet packet;
942           gboolean is_header;
943           gsize size;
944
945           packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
946           packet.bytes = size;
947
948           /* if we're not yet in data mode, ensure we're setup on the first packet */
949           if (!pad->have_type) {
950             GstCaps *caps;
951
952             /* Use headers in caps, if any; this will allow us to be resilient
953              * to starting streams on the fly, and some streams (like VP8
954              * at least) do not send headers packets, as other muxers don't
955              * expect/need them. */
956             caps = gst_pad_get_current_caps (GST_PAD_CAST (data->pad));
957             pad->have_type =
958                 gst_ogg_stream_setup_map_from_caps_headers (&pad->map, caps);
959
960             if (!pad->have_type) {
961               /* fallback on the packet */
962               pad->have_type = gst_ogg_stream_setup_map (&pad->map, &packet);
963             }
964             if (!pad->have_type) {
965               GST_ERROR_OBJECT (pad, "mapper didn't recognise input stream "
966                   "(pad caps: %" GST_PTR_FORMAT ")", caps);
967             } else {
968               GST_DEBUG_OBJECT (pad, "caps detected: %" GST_PTR_FORMAT,
969                   pad->map.caps);
970             }
971             if (caps)
972               gst_caps_unref (caps);
973           }
974
975           if (pad->have_type)
976             is_header = gst_ogg_stream_packet_is_header (&pad->map, &packet);
977           else                  /* fallback (FIXME 0.11: remove IN_CAPS hack) */
978             is_header = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
979
980           gst_buffer_unmap (buf, packet.packet, size);
981
982           if (is_header) {
983             GST_DEBUG_OBJECT (ogg_mux,
984                 "got header buffer in control state, ignoring");
985             /* just ignore */
986             pad->map.n_header_packets_seen++;
987             gst_buffer_unref (buf);
988             buf = NULL;
989           } else {
990             GST_DEBUG_OBJECT (ogg_mux,
991                 "got data buffer in control state, switching to data mode");
992             /* this is a data buffer so switch to data state */
993             pad->state = GST_OGG_PAD_STATE_DATA;
994
995             /* check if this type of stream allows generating granulepos
996              * metadata here, if not, upstream will have to provide */
997             if (gst_ogg_stream_granule_to_granulepos (&pad->map, 1, 1) < 0) {
998               GST_WARNING_OBJECT (data->pad, "can not generate metadata; "
999                   "relying on upstream");
1000               /* disable metadata code path, otherwise not used anyway */
1001               pad->map.granulerate_n = 0;
1002             }
1003           }
1004         }
1005
1006         /* so now we should have a real data packet;
1007          * see that it is properly decorated */
1008         if (G_LIKELY (buf)) {
1009           buf = gst_ogg_mux_decorate_buffer (ogg_mux, pad, buf);
1010           if (G_UNLIKELY (!buf))
1011             GST_DEBUG_OBJECT (data->pad, "buffer clipped");
1012         }
1013       }
1014
1015       pad->buffer = buf;
1016     }
1017
1018     /* we should have a buffer now, see if it is the best pad to
1019      * pull on */
1020     if (pad->buffer) {
1021       if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) {
1022         GST_LOG_OBJECT (data->pad,
1023             "new best pad, with buffer %" GST_PTR_FORMAT, pad->buffer);
1024
1025         bestpad = pad;
1026       }
1027     }
1028   }
1029
1030   return bestpad;
1031 }
1032
1033 static GList *
1034 gst_ogg_mux_get_headers (GstOggPadData * pad)
1035 {
1036   GList *res = NULL;
1037   GstStructure *structure;
1038   GstCaps *caps;
1039   GstPad *thepad;
1040
1041   thepad = pad->collect.pad;
1042
1043   GST_LOG_OBJECT (thepad, "getting headers");
1044
1045   caps = gst_pad_get_current_caps (thepad);
1046   if (caps != NULL) {
1047     const GValue *streamheader;
1048
1049     structure = gst_caps_get_structure (caps, 0);
1050     streamheader = gst_structure_get_value (structure, "streamheader");
1051     if (streamheader != NULL) {
1052       GST_LOG_OBJECT (thepad, "got header");
1053       if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
1054         GArray *bufarr = g_value_peek_pointer (streamheader);
1055         gint i;
1056
1057         GST_LOG_OBJECT (thepad, "got fixed list");
1058
1059         for (i = 0; i < bufarr->len; i++) {
1060           GValue *bufval = &g_array_index (bufarr, GValue, i);
1061
1062           GST_LOG_OBJECT (thepad, "item %d", i);
1063           if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
1064             GstBuffer *buf = g_value_peek_pointer (bufval);
1065
1066             GST_LOG_OBJECT (thepad, "adding item %d to header list", i);
1067
1068             gst_buffer_ref (buf);
1069             res = g_list_append (res, buf);
1070           }
1071         }
1072       } else {
1073         GST_LOG_OBJECT (thepad, "streamheader is not fixed list");
1074       }
1075
1076     } else if (gst_structure_has_name (structure, "video/x-dirac")) {
1077       res = g_list_append (res, pad->buffer);
1078       pad->buffer = NULL;
1079     } else {
1080       GST_LOG_OBJECT (thepad, "caps don't have streamheader");
1081     }
1082     gst_caps_unref (caps);
1083   } else {
1084     GST_LOG_OBJECT (thepad, "got empty caps as negotiated format");
1085   }
1086   return res;
1087 }
1088
1089 static GstCaps *
1090 gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
1091 {
1092   GstStructure *structure;
1093   GValue array = { 0 };
1094   GList *walk = buffers;
1095
1096   caps = gst_caps_make_writable (caps);
1097
1098   structure = gst_caps_get_structure (caps, 0);
1099
1100   /* put buffers in a fixed list */
1101   g_value_init (&array, GST_TYPE_ARRAY);
1102
1103   while (walk) {
1104     GstBuffer *buf = GST_BUFFER (walk->data);
1105     GstBuffer *copy;
1106     GValue value = { 0 };
1107
1108     walk = walk->next;
1109
1110     /* mark buffer */
1111     GST_LOG ("Setting IN_CAPS on buffer of length %" G_GSIZE_FORMAT,
1112         gst_buffer_get_size (buf));
1113     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
1114
1115     g_value_init (&value, GST_TYPE_BUFFER);
1116     copy = gst_buffer_copy (buf);
1117     gst_value_set_buffer (&value, copy);
1118     gst_buffer_unref (copy);
1119     gst_value_array_append_value (&array, &value);
1120     g_value_unset (&value);
1121   }
1122   gst_structure_set_value (structure, "streamheader", &array);
1123   g_value_unset (&array);
1124
1125   return caps;
1126 }
1127
1128 static void
1129 gst_ogg_mux_create_header_packet_with_flags (ogg_packet * packet,
1130     gboolean bos, gboolean eos)
1131 {
1132   packet->granulepos = 0;
1133   /* mark BOS and packet number */
1134   packet->b_o_s = bos;
1135   /* mark EOS */
1136   packet->e_o_s = eos;
1137 }
1138
1139 static void
1140 gst_ogg_mux_create_header_packet (ogg_packet * packet, GstOggPadData * pad)
1141 {
1142   gst_ogg_mux_create_header_packet_with_flags (packet, pad->packetno == 0, 0);
1143   packet->packetno = pad->packetno++;
1144 }
1145
1146 static void
1147 gst_ogg_mux_submit_skeleton_header_packet (GstOggMux * mux,
1148     ogg_stream_state * os, GstBuffer * buf, gboolean bos, gboolean eos)
1149 {
1150   ogg_packet packet;
1151   gsize size;
1152
1153   packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1154   packet.bytes = size;
1155   gst_ogg_mux_create_header_packet_with_flags (&packet, bos, eos);
1156   ogg_stream_packetin (os, &packet);
1157   gst_buffer_unref (buf);
1158 }
1159
1160 static void
1161 gst_ogg_mux_make_fishead (GstOggMux * mux, ogg_stream_state * os)
1162 {
1163   GstByteWriter bw;
1164   GstBuffer *fishead;
1165
1166   GST_DEBUG_OBJECT (mux, "Creating fishead");
1167
1168   gst_byte_writer_init_with_size (&bw, 64, TRUE);
1169   gst_byte_writer_put_string_utf8 (&bw, "fishead");
1170   gst_byte_writer_put_int16_le (&bw, 3);        /* version major */
1171   gst_byte_writer_put_int16_le (&bw, 0);        /* version minor */
1172   gst_byte_writer_put_int64_le (&bw, 0);        /* presentation time numerator */
1173   gst_byte_writer_put_int64_le (&bw, 1000);     /* ...and denominator */
1174   gst_byte_writer_put_int64_le (&bw, 0);        /* base time numerator */
1175   gst_byte_writer_put_int64_le (&bw, 1000);     /* ...and denominator */
1176   gst_byte_writer_fill (&bw, ' ', 20);  /* UTC time */
1177   g_assert (gst_byte_writer_get_pos (&bw) == 64);
1178   fishead = gst_byte_writer_reset_and_get_buffer (&bw);
1179   gst_ogg_mux_submit_skeleton_header_packet (mux, os, fishead, 1, 0);
1180 }
1181
1182 static void
1183 gst_ogg_mux_byte_writer_put_string_utf8 (GstByteWriter * bw, const char *s)
1184 {
1185   gst_byte_writer_put_data (bw, (const guint8 *) s, strlen (s));
1186 }
1187
1188 static void
1189 gst_ogg_mux_add_fisbone_message_header (GstOggMux * mux, GstByteWriter * bw,
1190     const char *tag, const char *value)
1191 {
1192   /* It is valid to pass NULL as the value to omit the tag */
1193   if (!value)
1194     return;
1195   GST_DEBUG_OBJECT (mux, "Adding fisbone message header %s: %s", tag, value);
1196   gst_ogg_mux_byte_writer_put_string_utf8 (bw, tag);
1197   gst_ogg_mux_byte_writer_put_string_utf8 (bw, ": ");
1198   gst_ogg_mux_byte_writer_put_string_utf8 (bw, value);
1199   gst_ogg_mux_byte_writer_put_string_utf8 (bw, "\r\n");
1200 }
1201
1202 static void
1203 gst_ogg_mux_add_fisbone_message_header_from_tags (GstOggMux * mux,
1204     GstByteWriter * bw, const char *header, const char *tag,
1205     const GstTagList * tags)
1206 {
1207   GString *s;
1208   guint size = gst_tag_list_get_tag_size (tags, tag), n;
1209   GST_DEBUG_OBJECT (mux, "Found %u tags for name %s", size, tag);
1210   if (size == 0)
1211     return;
1212   s = g_string_new ("");
1213   for (n = 0; n < size; ++n) {
1214     gchar *tmp;
1215     if (n)
1216       g_string_append (s, ", ");
1217     gst_tag_list_get_string_index (tags, tag, n, &tmp);
1218     g_string_append (s, tmp);
1219     g_free (tmp);
1220   }
1221   gst_ogg_mux_add_fisbone_message_header (mux, bw, header, s->str);
1222   g_string_free (s, TRUE);
1223 }
1224
1225 /* This is a basic placeholder to generate roles for the tracks.
1226    For tracks with more than one video, both video tracks will get
1227    tagged with a "video/main" role, but we have no way of knowing
1228    which one is the main one, if any. We could just pick one. For
1229    audio, it's more complicated as we don't know which is music,
1230    which is dubbing, etc. For kate, we could take a pretty good
1231    guess based on the category, as role essentially is category.
1232    For now, leave this as is. */
1233 static const char *
1234 gst_ogg_mux_get_default_role (GstOggPadData * pad)
1235 {
1236   const char *type = gst_ogg_stream_get_media_type (&pad->map);
1237   if (type) {
1238     if (!strncmp (type, "video/", strlen ("video/")))
1239       return "video/main";
1240     if (!strncmp (type, "audio/", strlen ("audio/")))
1241       return "audio/main";
1242     if (!strcmp (type + strlen (type) - strlen ("kate"), "kate"))
1243       return "text/caption";
1244   }
1245   return NULL;
1246 }
1247
1248 static void
1249 gst_ogg_mux_make_fisbone (GstOggMux * mux, ogg_stream_state * os,
1250     GstOggPadData * pad)
1251 {
1252   GstByteWriter bw;
1253
1254   GST_DEBUG_OBJECT (mux,
1255       "Creating %s fisbone for serial %08x",
1256       gst_ogg_stream_get_media_type (&pad->map), pad->map.serialno);
1257
1258   gst_byte_writer_init (&bw);
1259   gst_byte_writer_put_string_utf8 (&bw, "fisbone");
1260   gst_byte_writer_put_int32_le (&bw, 44);       /* offset to message headers */
1261   gst_byte_writer_put_uint32_le (&bw, pad->map.serialno);
1262   gst_byte_writer_put_uint32_le (&bw, pad->map.n_header_packets);
1263   gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_n);
1264   gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_d);
1265   gst_byte_writer_put_uint64_le (&bw, 0);       /* base granule */
1266   gst_byte_writer_put_uint32_le (&bw, pad->map.preroll);
1267   gst_byte_writer_put_uint8 (&bw, pad->map.granuleshift);
1268   gst_byte_writer_fill (&bw, 0, 3);     /* padding */
1269   /* message header fields - MIME type for now */
1270   gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Content-Type",
1271       gst_ogg_stream_get_media_type (&pad->map));
1272   gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Role",
1273       gst_ogg_mux_get_default_role (pad));
1274   gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Language",
1275       GST_TAG_LANGUAGE_CODE, pad->tags);
1276   gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Title",
1277       GST_TAG_TITLE, pad->tags);
1278
1279   gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1280       gst_byte_writer_reset_and_get_buffer (&bw), 0, 0);
1281 }
1282
1283 static void
1284 gst_ogg_mux_make_fistail (GstOggMux * mux, ogg_stream_state * os)
1285 {
1286   GST_DEBUG_OBJECT (mux, "Creating fistail");
1287
1288   gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1289       gst_buffer_new_and_alloc (0), 0, 1);
1290 }
1291
1292 /*
1293  * For each pad we need to write out one (small) header in one
1294  * page that allows decoders to identify the type of the stream.
1295  * After that we need to write out all extra info for the decoders.
1296  * In the case of a codec that also needs data as configuration, we can
1297  * find that info in the streamcaps. 
1298  * After writing the headers we must start a new page for the data.
1299  */
1300 static GstFlowReturn
1301 gst_ogg_mux_send_headers (GstOggMux * mux)
1302 {
1303   GSList *walk;
1304   GList *hbufs, *hwalk;
1305   GstCaps *caps;
1306   GstFlowReturn ret;
1307   ogg_page page;
1308   ogg_stream_state skeleton_stream;
1309
1310   hbufs = NULL;
1311   ret = GST_FLOW_OK;
1312
1313   GST_LOG_OBJECT (mux, "collecting headers");
1314
1315   walk = mux->collect->data;
1316   while (walk) {
1317     GstOggPadData *pad;
1318     GstPad *thepad;
1319
1320     pad = (GstOggPadData *) walk->data;
1321     thepad = pad->collect.pad;
1322
1323     walk = g_slist_next (walk);
1324
1325     GST_LOG_OBJECT (mux, "looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
1326
1327     /* if the pad has no buffer, we don't care */
1328     if (pad->buffer == NULL)
1329       continue;
1330
1331     /* now figure out the headers */
1332     pad->map.headers = gst_ogg_mux_get_headers (pad);
1333   }
1334
1335   GST_LOG_OBJECT (mux, "creating BOS pages");
1336   walk = mux->collect->data;
1337   while (walk) {
1338     GstOggPadData *pad;
1339     GstBuffer *buf;
1340     ogg_packet packet;
1341     GstPad *thepad;
1342     GstCaps *caps;
1343     GstStructure *structure;
1344     GstBuffer *hbuf;
1345     gsize size;
1346
1347     pad = (GstOggPadData *) walk->data;
1348     thepad = pad->collect.pad;
1349     caps = gst_pad_get_current_caps (thepad);
1350     structure = gst_caps_get_structure (caps, 0);
1351
1352     walk = walk->next;
1353
1354     pad->packetno = 0;
1355
1356     GST_LOG_OBJECT (thepad, "looping over headers");
1357
1358     if (pad->map.headers) {
1359       buf = GST_BUFFER (pad->map.headers->data);
1360       pad->map.headers = g_list_remove (pad->map.headers, buf);
1361     } else if (pad->buffer) {
1362       buf = pad->buffer;
1363       gst_buffer_ref (buf);
1364     } else {
1365       /* fixme -- should be caught in the previous list traversal. */
1366       GST_OBJECT_LOCK (thepad);
1367       g_critical ("No headers or buffers on pad %s:%s",
1368           GST_DEBUG_PAD_NAME (thepad));
1369       GST_OBJECT_UNLOCK (thepad);
1370       continue;
1371     }
1372
1373     /* create a packet from the buffer */
1374     packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1375     packet.bytes = size;
1376
1377     gst_ogg_mux_create_header_packet (&packet, pad);
1378
1379     /* swap the packet in */
1380     ogg_stream_packetin (&pad->map.stream, &packet);
1381
1382     gst_buffer_unmap (buf, packet.packet, size);
1383     gst_buffer_unref (buf);
1384
1385     GST_LOG_OBJECT (thepad, "flushing out BOS page");
1386     if (!ogg_stream_flush (&pad->map.stream, &page))
1387       g_critical ("Could not flush BOS page");
1388
1389     hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1390
1391     GST_LOG_OBJECT (mux, "swapped out page with mime type %s",
1392         gst_structure_get_name (structure));
1393
1394     /* quick hack: put video pages at the front.
1395      * Ideally, we would have a settable enum for which Ogg
1396      * profile we work with, and order based on that.
1397      * (FIXME: if there is more than one video stream, shouldn't we only put
1398      * one's BOS into the first page, followed by an audio stream's BOS, and
1399      * only then followed by the remaining video and audio streams?) */
1400     if (pad->map.is_video) {
1401       GST_DEBUG_OBJECT (thepad, "putting %s page at the front",
1402           gst_structure_get_name (structure));
1403       hbufs = g_list_prepend (hbufs, hbuf);
1404     } else {
1405       hbufs = g_list_append (hbufs, hbuf);
1406     }
1407
1408     gst_caps_unref (caps);
1409   }
1410
1411   /* The Skeleton BOS goes first - even before the video that went first before */
1412   if (mux->use_skeleton) {
1413     ogg_stream_init (&skeleton_stream, gst_ogg_mux_generate_serialno (mux));
1414     gst_ogg_mux_make_fishead (mux, &skeleton_stream);
1415     while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1416       GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1417       hbufs = g_list_append (hbufs, hbuf);
1418     }
1419   }
1420
1421   GST_LOG_OBJECT (mux, "creating next headers");
1422   walk = mux->collect->data;
1423   while (walk) {
1424     GstOggPadData *pad;
1425     GstPad *thepad;
1426
1427     pad = (GstOggPadData *) walk->data;
1428     thepad = pad->collect.pad;
1429
1430     walk = walk->next;
1431
1432     if (mux->use_skeleton)
1433       gst_ogg_mux_make_fisbone (mux, &skeleton_stream, pad);
1434
1435     GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
1436         GST_DEBUG_PAD_NAME (thepad));
1437
1438     hwalk = pad->map.headers;
1439     while (hwalk) {
1440       GstBuffer *buf = GST_BUFFER (hwalk->data);
1441       ogg_packet packet;
1442       ogg_page page;
1443       gsize size;
1444
1445       hwalk = hwalk->next;
1446
1447       /* create a packet from the buffer */
1448       packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1449       packet.bytes = size;
1450
1451       gst_ogg_mux_create_header_packet (&packet, pad);
1452
1453       /* swap the packet in */
1454       ogg_stream_packetin (&pad->map.stream, &packet);
1455       gst_buffer_unmap (buf, packet.packet, size);
1456       gst_buffer_unref (buf);
1457
1458       /* if last header, flush page */
1459       if (hwalk == NULL) {
1460         GST_LOG_OBJECT (mux,
1461             "flushing page as packet %" G_GUINT64_FORMAT " is first or "
1462             "last packet", (guint64) packet.packetno);
1463         while (ogg_stream_flush (&pad->map.stream, &page)) {
1464           GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1465
1466           GST_LOG_OBJECT (mux, "swapped out page");
1467           hbufs = g_list_append (hbufs, hbuf);
1468         }
1469       } else {
1470         GST_LOG_OBJECT (mux, "try to swap out page");
1471         /* just try to swap out a page then */
1472         while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1473           GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1474
1475           GST_LOG_OBJECT (mux, "swapped out page");
1476           hbufs = g_list_append (hbufs, hbuf);
1477         }
1478       }
1479     }
1480     g_list_free (pad->map.headers);
1481     pad->map.headers = NULL;
1482   }
1483
1484   if (mux->use_skeleton) {
1485     /* flush accumulated fisbones, the fistail must be on a separate page */
1486     while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1487       GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1488       hbufs = g_list_append (hbufs, hbuf);
1489     }
1490     gst_ogg_mux_make_fistail (mux, &skeleton_stream);
1491     while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1492       GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1493       hbufs = g_list_append (hbufs, hbuf);
1494     }
1495     ogg_stream_clear (&skeleton_stream);
1496   }
1497
1498   /* hbufs holds all buffers for the headers now */
1499
1500   /* create caps with the buffers */
1501   caps = gst_pad_query_caps (mux->srcpad, NULL);
1502   if (caps) {
1503     caps = gst_ogg_mux_set_header_on_caps (caps, hbufs);
1504     gst_pad_set_caps (mux->srcpad, caps);
1505     gst_caps_unref (caps);
1506   }
1507   /* and send the buffers */
1508   while (hbufs != NULL) {
1509     GstBuffer *buf = GST_BUFFER (hbufs->data);
1510
1511     hbufs = g_list_delete_link (hbufs, hbufs);
1512
1513     if ((ret = gst_ogg_mux_push_buffer (mux, buf, NULL)) != GST_FLOW_OK)
1514       break;
1515   }
1516   /* free any remaining nodes/buffers in case we couldn't push them */
1517   g_list_foreach (hbufs, (GFunc) gst_mini_object_unref, NULL);
1518   g_list_free (hbufs);
1519
1520   return ret;
1521 }
1522
1523 /* this function is called to process data on the best pending pad.
1524  *
1525  * basic idea:
1526  *
1527  * 1) store the selected pad and keep on pulling until we fill a
1528  *    complete ogg page or the ogg page is filled above the max-delay
1529  *    threshold. This is needed because the ogg spec says that
1530  *    you should fill a complete page with data from the same logical
1531  *    stream. When the page is filled, go back to 1).
1532  * 2) before filling a page, read ahead one more buffer to see if this
1533  *    packet is the last of the stream. We need to do this because the ogg
1534  *    spec mandates that the last packet should have the EOS flag set before
1535  *    sending it to ogg. if pad->buffer is NULL we need to wait to find out
1536  *    whether there are any more buffers.
1537  * 3) pages get queued on a per-pad queue. Every time a page is queued, a
1538  *    dequeue is called, which will dequeue the oldest page on any pad, provided
1539  *    that ALL pads have at least one marked page in the queue (or remaining
1540  *    pads are at EOS)
1541  */
1542 static GstFlowReturn
1543 gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
1544 {
1545   GstFlowReturn ret = GST_FLOW_OK;
1546   gboolean delta_unit;
1547   gint64 granulepos = 0;
1548   GstClockTime timestamp, gp_time;
1549   GstBuffer *next_buf;
1550
1551   GST_LOG_OBJECT (ogg_mux, "best pad %" GST_PTR_FORMAT
1552       ", currently pulling from %" GST_PTR_FORMAT, best->collect.pad,
1553       ogg_mux->pulling ? ogg_mux->pulling->collect.pad : NULL);
1554
1555   if (ogg_mux->pulling) {
1556     next_buf = gst_collect_pads2_peek (ogg_mux->collect,
1557         &ogg_mux->pulling->collect);
1558     if (next_buf) {
1559       ogg_mux->pulling->eos = FALSE;
1560       gst_buffer_unref (next_buf);
1561     } else {
1562       GST_DEBUG_OBJECT (ogg_mux->pulling->collect.pad, "setting eos to true");
1563       ogg_mux->pulling->eos = TRUE;
1564     }
1565   }
1566
1567   /* We could end up pushing from the best pad instead, so check that
1568    * as well */
1569   if (best && best != ogg_mux->pulling) {
1570     next_buf = gst_collect_pads2_peek (ogg_mux->collect, &best->collect);
1571     if (next_buf) {
1572       best->eos = FALSE;
1573       gst_buffer_unref (next_buf);
1574     } else {
1575       GST_DEBUG_OBJECT (best->collect.pad, "setting eos to true");
1576       best->eos = TRUE;
1577     }
1578   }
1579
1580   /* if we were already pulling from one pad, but the new "best" buffer is
1581    * from another pad, we need to check if we have reason to flush a page
1582    * for the pad we were pulling from before */
1583   if (ogg_mux->pulling && best &&
1584       ogg_mux->pulling != best && ogg_mux->pulling->buffer) {
1585     GstOggPadData *pad = ogg_mux->pulling;
1586     GstClockTime last_ts = GST_BUFFER_END_TIME (pad->buffer);
1587
1588     /* if the next packet in the current page is going to make the page
1589      * too long, we need to flush */
1590     if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) {
1591       ogg_page page;
1592
1593       GST_LOG_OBJECT (pad->collect.pad,
1594           GST_GP_FORMAT " stored packet %" G_GINT64_FORMAT
1595           " will make page too long, flushing",
1596           GST_BUFFER_OFFSET_END (pad->buffer),
1597           (gint64) pad->map.stream.packetno);
1598
1599       while (ogg_stream_flush (&pad->map.stream, &page)) {
1600         /* end time of this page is the timestamp of the next buffer */
1601         ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1602         /* Place page into the per-pad queue */
1603         ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1604             pad->first_delta);
1605         /* increment the page number counter */
1606         pad->pageno++;
1607         /* mark other pages as delta */
1608         pad->first_delta = TRUE;
1609       }
1610       pad->new_page = TRUE;
1611       ogg_mux->pulling = NULL;
1612     }
1613   }
1614
1615   /* if we don't know which pad to pull on, use the best one */
1616   if (ogg_mux->pulling == NULL) {
1617     ogg_mux->pulling = best;
1618     GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from best pad");
1619
1620     /* remember timestamp and gp time of first buffer for this new pad */
1621     if (ogg_mux->pulling != NULL) {
1622       ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer);
1623       GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "updated times, next ts %"
1624           GST_TIME_FORMAT, GST_TIME_ARGS (ogg_mux->next_ts));
1625     } else {
1626       /* no pad to pull on, send EOS */
1627       gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1628       return GST_FLOW_WRONG_STATE;
1629     }
1630   }
1631
1632   if (ogg_mux->need_headers) {
1633     ret = gst_ogg_mux_send_headers (ogg_mux);
1634     ogg_mux->need_headers = FALSE;
1635   }
1636
1637   /* we are pulling from a pad, continue to do so until a page
1638    * has been filled and queued */
1639   if (ogg_mux->pulling != NULL) {
1640     ogg_packet packet;
1641     ogg_page page;
1642     GstBuffer *buf, *tmpbuf;
1643     GstOggPadData *pad = ogg_mux->pulling;
1644     gint64 duration;
1645     gboolean force_flush;
1646     gsize size;
1647
1648     GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from pad");
1649
1650     /* now see if we have a buffer */
1651     buf = pad->buffer;
1652     if (buf == NULL) {
1653       GST_DEBUG_OBJECT (ogg_mux, "pad was EOS");
1654       ogg_mux->pulling = NULL;
1655       return GST_FLOW_OK;
1656     }
1657
1658     delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
1659     duration = GST_BUFFER_DURATION (buf);
1660
1661     /* if the current "next timestamp" on the pad is unset, then this is the
1662      * first packet on the new page.  Update our pad's page timestamp */
1663     if (ogg_mux->pulling->timestamp == GST_CLOCK_TIME_NONE) {
1664       ogg_mux->pulling->timestamp = GST_BUFFER_TIMESTAMP (buf);
1665       GST_LOG_OBJECT (ogg_mux->pulling->collect.pad,
1666           "updated pad timestamp to %" GST_TIME_FORMAT,
1667           GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1668     }
1669     /* create a packet from the buffer */
1670     packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1671     packet.bytes = size;
1672     packet.granulepos = GST_BUFFER_OFFSET_END (buf);
1673     if (packet.granulepos == -1)
1674       packet.granulepos = 0;
1675     /* mark BOS and packet number */
1676     packet.b_o_s = (pad->packetno == 0);
1677     packet.packetno = pad->packetno++;
1678     GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
1679         " packet %" G_GINT64_FORMAT " (%ld bytes) created from buffer",
1680         GST_GP_CAST (packet.granulepos), (gint64) packet.packetno,
1681         packet.bytes);
1682
1683     packet.e_o_s = ogg_mux->pulling->eos ? 1 : 0;
1684     tmpbuf = NULL;
1685
1686     /* we flush when we see a new keyframe */
1687     force_flush = (pad->prev_delta && !delta_unit)
1688         || pad->map.always_flush_page;
1689     if (duration != -1) {
1690       pad->duration += duration;
1691       /* if page duration exceeds max, flush page */
1692       if (pad->duration > ogg_mux->max_page_delay) {
1693         force_flush = TRUE;
1694         pad->duration = 0;
1695       }
1696     }
1697
1698     if (GST_BUFFER_IS_DISCONT (buf)) {
1699       if (pad->data_pushed) {
1700         GST_LOG_OBJECT (pad->collect.pad, "got discont");
1701         packet.packetno++;
1702         /* No public API for this; hack things in */
1703         pad->map.stream.pageno++;
1704         force_flush = TRUE;
1705       } else {
1706         GST_LOG_OBJECT (pad->collect.pad, "discont at stream start");
1707       }
1708     }
1709
1710     /* flush the currently built page if necessary */
1711     if (force_flush) {
1712       GST_LOG_OBJECT (pad->collect.pad,
1713           GST_GP_FORMAT " forced flush of page before this packet",
1714           GST_BUFFER_OFFSET_END (pad->buffer));
1715       while (ogg_stream_flush (&pad->map.stream, &page)) {
1716         /* end time of this page is the timestamp of the next buffer */
1717         ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1718         ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1719             pad->first_delta);
1720
1721         /* increment the page number counter */
1722         pad->pageno++;
1723         /* mark other pages as delta */
1724         pad->first_delta = TRUE;
1725       }
1726       pad->new_page = TRUE;
1727     }
1728
1729     /* if this is the first packet of a new page figure out the delta flag */
1730     if (pad->new_page) {
1731       if (delta_unit) {
1732         /* mark the page as delta */
1733         pad->first_delta = TRUE;
1734       } else {
1735         /* got a keyframe */
1736         if (ogg_mux->delta_pad == pad) {
1737           /* if we get it on the pad with deltaunits,
1738            * we mark the page as non delta */
1739           pad->first_delta = FALSE;
1740         } else if (ogg_mux->delta_pad != NULL) {
1741           /* if there are pads with delta frames, we
1742            * must mark this one as delta */
1743           pad->first_delta = TRUE;
1744         } else {
1745           pad->first_delta = FALSE;
1746         }
1747       }
1748       pad->new_page = FALSE;
1749     }
1750
1751     /* save key unit to track delta->key unit transitions */
1752     pad->prev_delta = delta_unit;
1753
1754     /* swap the packet in */
1755     if (packet.e_o_s == 1)
1756       GST_DEBUG_OBJECT (pad->collect.pad, "swapping in EOS packet");
1757     if (packet.b_o_s == 1)
1758       GST_DEBUG_OBJECT (pad->collect.pad, "swapping in BOS packet");
1759
1760     ogg_stream_packetin (&pad->map.stream, &packet);
1761     gst_buffer_unmap (buf, packet.packet, size);
1762     pad->data_pushed = TRUE;
1763
1764     gp_time = GST_BUFFER_OFFSET (pad->buffer);
1765     granulepos = GST_BUFFER_OFFSET_END (pad->buffer);
1766     timestamp = GST_BUFFER_TIMESTAMP (pad->buffer);
1767
1768     GST_LOG_OBJECT (pad->collect.pad,
1769         GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", gp time %"
1770         GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT " packetin'd",
1771         granulepos, (gint64) packet.packetno, GST_TIME_ARGS (gp_time),
1772         GST_TIME_ARGS (timestamp));
1773     /* don't need the old buffer anymore */
1774     gst_buffer_unref (pad->buffer);
1775     /* store new readahead buffer */
1776     pad->buffer = tmpbuf;
1777
1778     /* let ogg write out the pages now. The packet we got could end
1779      * up in more than one page so we need to write them all */
1780     if (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1781       /* we have a new page, so we need to timestamp it correctly.
1782        * if this fresh packet ends on this page, then the page's granulepos
1783        * comes from that packet, and we should set this buffer's timestamp */
1784
1785       GST_LOG_OBJECT (pad->collect.pad,
1786           GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", time %"
1787           GST_TIME_FORMAT ") caused new page",
1788           granulepos, (gint64) packet.packetno, GST_TIME_ARGS (timestamp));
1789       GST_LOG_OBJECT (pad->collect.pad,
1790           GST_GP_FORMAT " new page %ld",
1791           GST_GP_CAST (ogg_page_granulepos (&page)), pad->map.stream.pageno);
1792
1793       if (ogg_page_granulepos (&page) == granulepos) {
1794         /* the packet we streamed in finishes on the current page,
1795          * because the page's granulepos is the granulepos of the last
1796          * packet completed on that page,
1797          * so update the timestamp that we will give to the page */
1798         GST_LOG_OBJECT (pad->collect.pad,
1799             GST_GP_FORMAT
1800             " packet finishes on current page, updating gp time to %"
1801             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (gp_time));
1802         pad->gp_time = gp_time;
1803       } else {
1804         GST_LOG_OBJECT (pad->collect.pad,
1805             GST_GP_FORMAT
1806             " packet spans beyond current page, keeping old gp time %"
1807             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (pad->gp_time));
1808       }
1809
1810       /* push the page */
1811       /* end time of this page is the timestamp of the next buffer */
1812       pad->timestamp_end = timestamp;
1813       ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, pad->first_delta);
1814       pad->pageno++;
1815       /* mark next pages as delta */
1816       pad->first_delta = TRUE;
1817
1818       /* use an inner loop here to flush the remaining pages and
1819        * mark them as delta frames as well */
1820       while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1821         if (ogg_page_granulepos (&page) == granulepos) {
1822           /* the page has taken up the new packet completely, which means
1823            * the packet ends the page and we can update the gp time
1824            * before pushing out */
1825           pad->gp_time = gp_time;
1826         }
1827
1828         /* we have a complete page now, we can push the page
1829          * and make sure to pull on a new pad the next time around */
1830         ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1831             pad->first_delta);
1832         /* increment the page number counter */
1833         pad->pageno++;
1834       }
1835       /* need a new page as well */
1836       pad->new_page = TRUE;
1837       pad->duration = 0;
1838       /* we're done pulling on this pad, make sure to choose a new
1839        * pad for pulling in the next iteration */
1840       ogg_mux->pulling = NULL;
1841     }
1842
1843     /* Update the gp time, if necessary, since any future page will have at
1844      * least this gp time.
1845      */
1846     if (pad->gp_time < gp_time) {
1847       pad->gp_time = gp_time;
1848       GST_LOG_OBJECT (pad->collect.pad,
1849           "Updated running gp time of pad %" GST_PTR_FORMAT
1850           " to %" GST_TIME_FORMAT, pad->collect.pad, GST_TIME_ARGS (gp_time));
1851     }
1852   }
1853
1854   return ret;
1855 }
1856
1857 /* all_pads_eos:
1858  *
1859  * Checks if all pads are EOS'd by peeking.
1860  *
1861  * Returns TRUE if all pads are EOS.
1862  */
1863 static gboolean
1864 all_pads_eos (GstCollectPads2 * pads)
1865 {
1866   GSList *walk;
1867
1868   walk = pads->data;
1869   while (walk) {
1870     GstOggPadData *oggpad = (GstOggPadData *) walk->data;
1871
1872     GST_DEBUG_OBJECT (oggpad->collect.pad,
1873         "oggpad %p eos %d", oggpad, oggpad->eos);
1874
1875     if (oggpad->eos == FALSE)
1876       return FALSE;
1877
1878     walk = g_slist_next (walk);
1879   }
1880
1881   return TRUE;
1882 }
1883
1884 /* This function is called when there is data on all pads.
1885  * 
1886  * It finds a pad to pull on, this is done by looking at the buffers
1887  * to decide which one to use, and using the 'oldest' one first. It then calls
1888  * gst_ogg_mux_process_best_pad() to process as much data as possible.
1889  * 
1890  * If all the pads have received EOS, it flushes out all data by continually
1891  * getting the best pad and calling gst_ogg_mux_process_best_pad() until they
1892  * are all empty, and then sends EOS.
1893  */
1894 static GstFlowReturn
1895 gst_ogg_mux_collected (GstCollectPads2 * pads, GstOggMux * ogg_mux)
1896 {
1897   GstOggPadData *best;
1898   GstFlowReturn ret;
1899   gboolean popped;
1900
1901   GST_LOG_OBJECT (ogg_mux, "collected");
1902
1903   /* queue buffers on all pads; find a buffer with the lowest timestamp */
1904   best = gst_ogg_mux_queue_pads (ogg_mux, &popped);
1905
1906   if (popped)
1907     return GST_FLOW_OK;
1908
1909   if (best == NULL || best->buffer == NULL) {
1910     /* This is not supposed to happen */
1911     return GST_FLOW_ERROR;
1912   }
1913
1914   ret = gst_ogg_mux_process_best_pad (ogg_mux, best);
1915
1916   if (best->eos && all_pads_eos (pads)) {
1917     gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1918     return GST_FLOW_EOS;
1919   }
1920
1921   return ret;
1922 }
1923
1924 static void
1925 gst_ogg_mux_get_property (GObject * object,
1926     guint prop_id, GValue * value, GParamSpec * pspec)
1927 {
1928   GstOggMux *ogg_mux;
1929
1930   ogg_mux = GST_OGG_MUX (object);
1931
1932   switch (prop_id) {
1933     case ARG_MAX_DELAY:
1934       g_value_set_uint64 (value, ogg_mux->max_delay);
1935       break;
1936     case ARG_MAX_PAGE_DELAY:
1937       g_value_set_uint64 (value, ogg_mux->max_page_delay);
1938       break;
1939     case ARG_MAX_TOLERANCE:
1940       g_value_set_uint64 (value, ogg_mux->max_tolerance);
1941       break;
1942     case ARG_SKELETON:
1943       g_value_set_boolean (value, ogg_mux->use_skeleton);
1944       break;
1945     default:
1946       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1947       break;
1948   }
1949 }
1950
1951 static void
1952 gst_ogg_mux_set_property (GObject * object,
1953     guint prop_id, const GValue * value, GParamSpec * pspec)
1954 {
1955   GstOggMux *ogg_mux;
1956
1957   ogg_mux = GST_OGG_MUX (object);
1958
1959   switch (prop_id) {
1960     case ARG_MAX_DELAY:
1961       ogg_mux->max_delay = g_value_get_uint64 (value);
1962       break;
1963     case ARG_MAX_PAGE_DELAY:
1964       ogg_mux->max_page_delay = g_value_get_uint64 (value);
1965       break;
1966     case ARG_MAX_TOLERANCE:
1967       ogg_mux->max_tolerance = g_value_get_uint64 (value);
1968       break;
1969     case ARG_SKELETON:
1970       ogg_mux->use_skeleton = g_value_get_boolean (value);
1971       break;
1972     default:
1973       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1974       break;
1975   }
1976 }
1977
1978 /* reset all variables in the ogg pads. */
1979 static void
1980 gst_ogg_mux_init_collectpads (GstCollectPads2 * collect)
1981 {
1982   GSList *walk;
1983
1984   walk = collect->data;
1985   while (walk) {
1986     GstOggPadData *oggpad = (GstOggPadData *) walk->data;
1987
1988     ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
1989     oggpad->packetno = 0;
1990     oggpad->pageno = 0;
1991     oggpad->eos = FALSE;
1992     /* we assume there will be some control data first for this pad */
1993     oggpad->state = GST_OGG_PAD_STATE_CONTROL;
1994     oggpad->new_page = TRUE;
1995     oggpad->first_delta = FALSE;
1996     oggpad->prev_delta = FALSE;
1997     oggpad->data_pushed = FALSE;
1998     oggpad->pagebuffers = g_queue_new ();
1999
2000     gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
2001
2002     walk = g_slist_next (walk);
2003   }
2004 }
2005
2006 /* Clear all buffers from the collectpads object */
2007 static void
2008 gst_ogg_mux_clear_collectpads (GstCollectPads2 * collect)
2009 {
2010   GSList *walk;
2011
2012   for (walk = collect->data; walk; walk = g_slist_next (walk)) {
2013     GstOggPadData *oggpad = (GstOggPadData *) walk->data;
2014     GstBuffer *buf;
2015
2016     ogg_stream_clear (&oggpad->map.stream);
2017
2018     while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
2019       gst_buffer_unref (buf);
2020     }
2021     g_queue_free (oggpad->pagebuffers);
2022     oggpad->pagebuffers = NULL;
2023
2024     if (oggpad->buffer) {
2025       gst_buffer_unref (oggpad->buffer);
2026       oggpad->buffer = NULL;
2027     }
2028
2029     if (oggpad->tags) {
2030       gst_tag_list_free (oggpad->tags);
2031       oggpad->tags = NULL;
2032     }
2033
2034     gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
2035   }
2036 }
2037
2038 static GstStateChangeReturn
2039 gst_ogg_mux_change_state (GstElement * element, GstStateChange transition)
2040 {
2041   GstOggMux *ogg_mux;
2042   GstStateChangeReturn ret;
2043
2044   ogg_mux = GST_OGG_MUX (element);
2045
2046   switch (transition) {
2047     case GST_STATE_CHANGE_NULL_TO_READY:
2048       break;
2049     case GST_STATE_CHANGE_READY_TO_PAUSED:
2050       gst_ogg_mux_clear (ogg_mux);
2051       gst_ogg_mux_init_collectpads (ogg_mux->collect);
2052       gst_collect_pads2_start (ogg_mux->collect);
2053       break;
2054     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2055       break;
2056     case GST_STATE_CHANGE_PAUSED_TO_READY:
2057       gst_collect_pads2_stop (ogg_mux->collect);
2058       break;
2059     default:
2060       break;
2061   }
2062
2063   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2064
2065   switch (transition) {
2066     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2067       break;
2068     case GST_STATE_CHANGE_PAUSED_TO_READY:
2069       gst_ogg_mux_clear_collectpads (ogg_mux->collect);
2070       break;
2071     case GST_STATE_CHANGE_READY_TO_NULL:
2072       break;
2073     default:
2074       break;
2075   }
2076
2077   return ret;
2078 }
2079
2080 gboolean
2081 gst_ogg_mux_plugin_init (GstPlugin * plugin)
2082 {
2083   GST_DEBUG_CATEGORY_INIT (gst_ogg_mux_debug, "oggmux", 0, "ogg muxer");
2084
2085   return gst_element_register (plugin, "oggmux", GST_RANK_PRIMARY,
2086       GST_TYPE_OGG_MUX);
2087 }