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