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