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