gstoggmux.c 55.4 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
2
/* OGG muxer plugin for GStreamer
 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
3
 * Copyright (C) 2006 Thomas Vander Stichele <thomas at apestaart dot org>
Wim Taymans's avatar
Wim Taymans committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

21
22
/**
 * SECTION:element-oggmux
Stefan Kost's avatar
Stefan Kost committed
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
34
35
36
37
 * </refsect2>
 *
 * Last reviewed on 2008-02-06 (0.10.17)
 */

Wim Taymans's avatar
Wim Taymans committed
38
39
40
41
42
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>
Wim Taymans's avatar
Wim Taymans committed
43
#include <gst/base/gstcollectpads.h>
44
#include <gst/tag/tag.h>
Wim Taymans's avatar
Wim Taymans committed
45

46
47
#include "gstoggmux.h"

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48
/* memcpy - if someone knows a way to get rid of it, please speak up
Wim Taymans's avatar
Wim Taymans committed
49
50
51
 * note: the ogg docs even say you need this... */
#include <string.h>
#include <time.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
52
#include <stdlib.h>             /* rand, srand, atoi */
Wim Taymans's avatar
Wim Taymans committed
53
54
55
56

GST_DEBUG_CATEGORY_STATIC (gst_ogg_mux_debug);
#define GST_CAT_DEFAULT gst_ogg_mux_debug

57
58
59
60
61
62
63
64
/* This isn't generally what you'd want with an end-time macro, because
   technically the end time of a buffer with invalid duration is invalid. But
   for sorting ogg pages this is what we want. */
#define GST_BUFFER_END_TIME(buf) \
    (GST_BUFFER_DURATION_IS_VALID (buf) \
    ? GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf) \
    : GST_BUFFER_TIMESTAMP (buf))

65
66
67
68
69
#define GST_BUFFER_RUNNING_TIME(buf, oggpad) \
    (GST_BUFFER_DURATION_IS_VALID (buf) \
    ? gst_segment_to_running_time ((oggpad)->segment, GST_FORMAT_TIME, \
    GST_BUFFER_TIMESTAMP (buf)) : 0)

70
#define GST_GP_FORMAT "[gp %8" G_GINT64_FORMAT "]"
71
#define GST_GP_CAST(_gp) ((gint64) _gp)
72

Wim Taymans's avatar
Wim Taymans committed
73
74
75
76
77
78
79
80
81
82
83
84
85
86
typedef enum
{
  GST_OGG_FLAG_BOS = GST_ELEMENT_FLAG_LAST,
  GST_OGG_FLAG_EOS
}
GstOggFlag;

/* OggMux signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

Wim Taymans's avatar
Wim Taymans committed
87
/* set to 0.5 seconds by default */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
88
89
#define DEFAULT_MAX_DELAY       G_GINT64_CONSTANT(500000000)
#define DEFAULT_MAX_PAGE_DELAY  G_GINT64_CONSTANT(500000000)
Wim Taymans's avatar
Wim Taymans committed
90
91
enum
{
92
93
  ARG_0,
  ARG_MAX_DELAY,
94
  ARG_MAX_PAGE_DELAY,
Wim Taymans's avatar
Wim Taymans committed
95
96
97
98
99
100
101
102
103
104
105
};

static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("application/ogg")
    );

static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
106
    GST_STATIC_CAPS ("video/x-theora; "
107
        "audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; "
108
        "application/x-ogm-video; application/x-ogm-audio; video/x-dirac; "
109
        "video/x-smoke; video/x-vp8; text/x-cmml, encoded = (boolean) TRUE; "
110
        "subtitle/x-kate; application/x-kate")
Wim Taymans's avatar
Wim Taymans committed
111
112
113
114
115
    );

static void gst_ogg_mux_base_init (gpointer g_class);
static void gst_ogg_mux_class_init (GstOggMuxClass * klass);
static void gst_ogg_mux_init (GstOggMux * ogg_mux);
116
static void gst_ogg_mux_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
117

Wim Taymans's avatar
Wim Taymans committed
118
119
static GstFlowReturn
gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux);
Wim Taymans's avatar
Wim Taymans committed
120
121
122
static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event);
static GstPad *gst_ogg_mux_request_new_pad (GstElement * element,
    GstPadTemplate * templ, const gchar * name);
123
124
static void gst_ogg_mux_release_pad (GstElement * element, GstPad * pad);

Wim Taymans's avatar
Wim Taymans committed
125
126
127
128
static void gst_ogg_mux_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_ogg_mux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);
129
130
static GstStateChangeReturn gst_ogg_mux_change_state (GstElement * element,
    GstStateChange transition);
Wim Taymans's avatar
Wim Taymans committed
131
132
133
134
135
136
137
138
139
140

static GstElementClass *parent_class = NULL;

/*static guint gst_ogg_mux_signals[LAST_SIGNAL] = { 0 }; */

GType
gst_ogg_mux_get_type (void)
{
  static GType ogg_mux_type = 0;

141
  if (G_UNLIKELY (ogg_mux_type == 0)) {
Wim Taymans's avatar
Wim Taymans committed
142
143
144
145
146
147
148
149
150
151
152
    static const GTypeInfo ogg_mux_info = {
      sizeof (GstOggMuxClass),
      gst_ogg_mux_base_init,
      NULL,
      (GClassInitFunc) gst_ogg_mux_class_init,
      NULL,
      NULL,
      sizeof (GstOggMux),
      0,
      (GInstanceInitFunc) gst_ogg_mux_init,
    };
153
154
155
156
157
    static const GInterfaceInfo preset_info = {
      NULL,
      NULL,
      NULL
    };
Wim Taymans's avatar
Wim Taymans committed
158
159
160
161

    ogg_mux_type =
        g_type_register_static (GST_TYPE_ELEMENT, "GstOggMux", &ogg_mux_info,
        0);
162
163

    g_type_add_interface_static (ogg_mux_type, GST_TYPE_PRESET, &preset_info);
Wim Taymans's avatar
Wim Taymans committed
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  }
  return ogg_mux_type;
}

static void
gst_ogg_mux_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&src_factory));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&sink_factory));

178
179
180
181
  gst_element_class_set_details_simple (element_class,
      "Ogg muxer", "Codec/Muxer",
      "mux ogg streams (info about ogg: http://xiph.org)",
      "Wim Taymans <wim@fluendo.com>");
Wim Taymans's avatar
Wim Taymans committed
182
183
184
185
186
187
188
189
190
191
192
}

static void
gst_ogg_mux_class_init (GstOggMuxClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

193
  parent_class = g_type_class_peek_parent (klass);
Wim Taymans's avatar
Wim Taymans committed
194

195
  gobject_class->finalize = gst_ogg_mux_finalize;
196
197
198
  gobject_class->get_property = gst_ogg_mux_get_property;
  gobject_class->set_property = gst_ogg_mux_set_property;

Wim Taymans's avatar
Wim Taymans committed
199
  gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad;
200
  gstelement_class->release_pad = gst_ogg_mux_release_pad;
Wim Taymans's avatar
Wim Taymans committed
201

202
203
204
  g_object_class_install_property (gobject_class, ARG_MAX_DELAY,
      g_param_spec_uint64 ("max-delay", "Max delay",
          "Maximum delay in multiplexing streams", 0, G_MAXUINT64,
205
206
          DEFAULT_MAX_DELAY,
          (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
207
208
209
  g_object_class_install_property (gobject_class, ARG_MAX_PAGE_DELAY,
      g_param_spec_uint64 ("max-page-delay", "Max page delay",
          "Maximum delay for sending out a page", 0, G_MAXUINT64,
210
211
          DEFAULT_MAX_PAGE_DELAY,
          (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
212

Wim Taymans's avatar
Wim Taymans committed
213
214
215
216
  gstelement_class->change_state = gst_ogg_mux_change_state;

}

Wim Taymans's avatar
Wim Taymans committed
217
#if 0
Wim Taymans's avatar
Wim Taymans committed
218
219
220
221
222
static const GstEventMask *
gst_ogg_mux_get_sink_event_masks (GstPad * pad)
{
  static const GstEventMask gst_ogg_mux_sink_event_masks[] = {
    {GST_EVENT_EOS, 0},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
223
    {GST_EVENT_DISCONTINUOUS, 0},
Wim Taymans's avatar
Wim Taymans committed
224
225
226
227
228
    {0,}
  };

  return gst_ogg_mux_sink_event_masks;
}
Wim Taymans's avatar
Wim Taymans committed
229
#endif
Wim Taymans's avatar
Wim Taymans committed
230

231
232
233
234
235
236
static void
gst_ogg_mux_clear (GstOggMux * ogg_mux)
{
  ogg_mux->pulling = NULL;
  ogg_mux->need_headers = TRUE;
  ogg_mux->delta_pad = NULL;
237
238
  ogg_mux->offset = 0;
  ogg_mux->next_ts = 0;
239
  ogg_mux->last_ts = GST_CLOCK_TIME_NONE;
240
241
}

Wim Taymans's avatar
Wim Taymans committed
242
243
244
245
246
247
248
249
250
251
252
static void
gst_ogg_mux_init (GstOggMux * ogg_mux)
{
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (ogg_mux);

  ogg_mux->srcpad =
      gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
          "src"), "src");
  gst_pad_set_event_function (ogg_mux->srcpad, gst_ogg_mux_handle_src_event);
  gst_element_add_pad (GST_ELEMENT (ogg_mux), ogg_mux->srcpad);

253
  GST_OBJECT_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS);
Wim Taymans's avatar
Wim Taymans committed
254
255
256
257

  /* seed random number generator for creation of serial numbers */
  srand (time (NULL));

258
259
  ogg_mux->collect = gst_collect_pads_new ();
  gst_collect_pads_set_function (ogg_mux->collect,
260
261
      (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_ogg_mux_collected),
      ogg_mux);
262

263
264
265
  ogg_mux->max_delay = DEFAULT_MAX_DELAY;
  ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;

266
  gst_ogg_mux_clear (ogg_mux);
Wim Taymans's avatar
Wim Taymans committed
267
268
}

269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
static void
gst_ogg_mux_finalize (GObject * object)
{
  GstOggMux *ogg_mux;

  ogg_mux = GST_OGG_MUX (object);

  if (ogg_mux->collect) {
    gst_object_unref (ogg_mux->collect);
    ogg_mux->collect = NULL;
  }

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

284
285
286
static void
gst_ogg_mux_ogg_pad_destroy_notify (GstCollectData * data)
{
287
  GstOggPadData *oggpad = (GstOggPadData *) data;
288
289
  GstBuffer *buf;

290
  ogg_stream_clear (&oggpad->map.stream);
291
  gst_caps_replace (&oggpad->map.caps, NULL);
292
293
294
295
296
297
298
299

  if (oggpad->pagebuffers) {
    while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
      gst_buffer_unref (buf);
    }
    g_queue_free (oggpad->pagebuffers);
    oggpad->pagebuffers = NULL;
  }
300
301
302
303
304

  if (oggpad->segment) {
    gst_segment_free (oggpad->segment);
    oggpad->segment = NULL;
  }
305
306
}

Wim Taymans's avatar
Wim Taymans committed
307
static GstPadLinkReturn
308
gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer)
Wim Taymans's avatar
Wim Taymans committed
309
310
311
312
313
{
  GstOggMux *ogg_mux;

  ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));

314
  GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
315

316
  gst_object_unref (ogg_mux);
Wim Taymans's avatar
Wim Taymans committed
317
318
319
320

  return GST_PAD_LINK_OK;
}

321
322
323
324
static gboolean
gst_ogg_mux_sink_event (GstPad * pad, GstEvent * event)
{
  GstOggMux *ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
325
  GstOggPadData *ogg_pad = (GstOggPadData *) gst_pad_get_element_private (pad);
326
327
328
329
330
331
  gboolean ret;

  GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event),
      GST_DEBUG_PAD_NAME (pad));

  switch (GST_EVENT_TYPE (event)) {
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
    case GST_EVENT_NEWSEGMENT:{
      gboolean update;
      gdouble rate;
      gdouble applied_rate;
      GstFormat format;
      gint64 start, stop, position;

      g_return_val_if_fail (ogg_pad->segment != NULL, FALSE);

      gst_event_parse_new_segment_full (event, &update, &rate,
          &applied_rate, &format, &start, &stop, &position);

      /* We don't support non time NEWSEGMENT events */
      if (format != GST_FORMAT_TIME) {
        gst_event_unref (event);
        return FALSE;
      }
      gst_segment_set_newsegment_full (ogg_pad->segment, update, rate,
          applied_rate, format, start, stop, position);

352
      break;
353
    }
354
355
356
357
358
359
360
361
362
363
364
365
366
    default:
      ret = TRUE;
      break;
  }

  /* now GstCollectPads can take care of the rest, e.g. EOS */
  if (ret)
    ret = ogg_pad->collect_event (pad, event);

  gst_object_unref (ogg_mux);
  return ret;
}

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
static gboolean
gst_ogg_mux_is_serialno_present (GstOggMux * ogg_mux, guint32 serialno)
{
  GSList *walk;

  walk = ogg_mux->collect->data;
  while (walk) {
    GstOggPadData *pad = (GstOggPadData *) walk->data;
    if (pad->map.serialno == serialno)
      return TRUE;
    walk = walk->next;
  }

  return FALSE;
}

static guint32
gst_ogg_mux_generate_serialno (GstOggMux * ogg_mux)
{
  guint32 serialno;

  do {
    serialno = g_random_int_range (0, G_MAXINT32);
  } while (gst_ogg_mux_is_serialno_present (ogg_mux, serialno));

  return serialno;
}

Wim Taymans's avatar
Wim Taymans committed
395
396
397
398
399
400
static GstPad *
gst_ogg_mux_request_new_pad (GstElement * element,
    GstPadTemplate * templ, const gchar * req_name)
{
  GstOggMux *ogg_mux;
  GstPad *newpad;
Wim Taymans's avatar
Wim Taymans committed
401
  GstElementClass *klass;
Wim Taymans's avatar
Wim Taymans committed
402
403
404

  g_return_val_if_fail (templ != NULL, NULL);

Wim Taymans's avatar
Wim Taymans committed
405
406
  if (templ->direction != GST_PAD_SINK)
    goto wrong_direction;
Wim Taymans's avatar
Wim Taymans committed
407
408
409
410

  g_return_val_if_fail (GST_IS_OGG_MUX (element), NULL);
  ogg_mux = GST_OGG_MUX (element);

Wim Taymans's avatar
Wim Taymans committed
411
412
413
414
415
416
  klass = GST_ELEMENT_GET_CLASS (element);

  if (templ != gst_element_class_get_pad_template (klass, "sink_%d"))
    goto wrong_template;

  {
Wim Taymans's avatar
Wim Taymans committed
417
418
419
420
421
    gint serial;
    gchar *name;

    if (req_name == NULL || strlen (req_name) < 6) {
      /* no name given when requesting the pad, use random serial number */
422
      serial = gst_ogg_mux_generate_serialno (ogg_mux);
Wim Taymans's avatar
Wim Taymans committed
423
424
425
426
427
    } else {
      /* parse serial number from requested padname */
      serial = atoi (&req_name[5]);
    }
    /* create new pad with the name */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
428
    GST_DEBUG_OBJECT (ogg_mux, "Creating new pad for serial %d", serial);
Wim Taymans's avatar
Wim Taymans committed
429
430
431
432
433
434
435
    name = g_strdup_printf ("sink_%d", serial);
    newpad = gst_pad_new_from_template (templ, name);
    g_free (name);

    /* construct our own wrapper data structure for the pad to
     * keep track of its status */
    {
436
      GstOggPadData *oggpad;
Wim Taymans's avatar
Wim Taymans committed
437

438
      oggpad = (GstOggPadData *)
439
          gst_collect_pads_add_pad_full (ogg_mux->collect, newpad,
440
          sizeof (GstOggPadData), gst_ogg_mux_ogg_pad_destroy_notify);
441
      ogg_mux->active_pads++;
Wim Taymans's avatar
Wim Taymans committed
442

443
444
      oggpad->map.serialno = serial;
      ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
Wim Taymans's avatar
Wim Taymans committed
445
446
447
448
449
      oggpad->packetno = 0;
      oggpad->pageno = 0;
      oggpad->eos = FALSE;
      /* we assume there will be some control data first for this pad */
      oggpad->state = GST_OGG_PAD_STATE_CONTROL;
450
451
452
      oggpad->new_page = TRUE;
      oggpad->first_delta = FALSE;
      oggpad->prev_delta = FALSE;
453
      oggpad->data_pushed = FALSE;
454
      oggpad->pagebuffers = g_queue_new ();
455
456
      oggpad->map.headers = NULL;
      oggpad->map.queued = NULL;
457
458
459
      oggpad->segment = gst_segment_new ();
      gst_segment_set_newsegment (oggpad->segment, FALSE, 1, GST_FORMAT_TIME,
          0, -1, 0);
460
461
462
463

      oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
      gst_pad_set_event_function (newpad,
          GST_DEBUG_FUNCPTR (gst_ogg_mux_sink_event));
Wim Taymans's avatar
Wim Taymans committed
464
465
466
467
468
    }
  }

  /* setup some pad functions */
  gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect);
469

Wim Taymans's avatar
Wim Taymans committed
470
471
472
473
  /* dd the pad to the element */
  gst_element_add_pad (element, newpad);

  return newpad;
Wim Taymans's avatar
Wim Taymans committed
474
475
476
477
478
479
480
481
482
483
484
485

  /* ERRORS */
wrong_direction:
  {
    g_warning ("ogg_mux: request pad that is not a SINK pad\n");
    return NULL;
  }
wrong_template:
  {
    g_warning ("ogg_mux: this is not our template!\n");
    return NULL;
  }
Wim Taymans's avatar
Wim Taymans committed
486
487
}

488
489
490
491
492
493
494
static void
gst_ogg_mux_release_pad (GstElement * element, GstPad * pad)
{
  GstOggMux *ogg_mux;

  ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));

495
  gst_collect_pads_remove_pad (ogg_mux->collect, pad);
496
  gst_element_remove_pad (element, pad);
497
498

  gst_object_unref (ogg_mux);
499
500
}

Wim Taymans's avatar
Wim Taymans committed
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
/* handle events */
static gboolean
gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event)
{
  GstEventType type;

  type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;

  switch (type) {
    case GST_EVENT_SEEK:
      /* disable seeking for now */
      return FALSE;
    default:
      break;
  }

  return gst_pad_event_default (pad, event);
}

520
static GstBuffer *
521
gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
Wim Taymans's avatar
Wim Taymans committed
522
523
524
525
{
  GstBuffer *buffer;

  /* allocate space for header and body */
Wim Taymans's avatar
Wim Taymans committed
526
  buffer = gst_buffer_new_and_alloc (page->header_len + page->body_len);
Wim Taymans's avatar
Wim Taymans committed
527
528
529
530
  memcpy (GST_BUFFER_DATA (buffer), page->header, page->header_len);
  memcpy (GST_BUFFER_DATA (buffer) + page->header_len,
      page->body, page->body_len);

531
532
533
534
  /* Here we set granulepos as our OFFSET_END to give easy direct access to
   * this value later. Before we push it, we reset this to OFFSET + SIZE
   * (see gst_ogg_mux_push_buffer). */
  GST_BUFFER_OFFSET_END (buffer) = ogg_page_granulepos (page);
535
  if (delta)
536
    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
Wim Taymans's avatar
Wim Taymans committed
537

538
  GST_LOG_OBJECT (mux, GST_GP_FORMAT
539
540
      " created buffer %p from ogg page",
      GST_GP_CAST (ogg_page_granulepos (page)), buffer);
541

542
543
544
  return buffer;
}

Wim Taymans's avatar
Wim Taymans committed
545
static GstFlowReturn
546
547
gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer,
    GstOggPadData * oggpad)
548
{
549
550
  GstCaps *caps;

551
552
553
554
  /* fix up OFFSET and OFFSET_END again */
  GST_BUFFER_OFFSET (buffer) = mux->offset;
  mux->offset += GST_BUFFER_SIZE (buffer);
  GST_BUFFER_OFFSET_END (buffer) = mux->offset;
555

556
557
  /* Ensure we have monotonically increasing timestamps in the output. */
  if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
558
559
    gint64 run_time = GST_BUFFER_RUNNING_TIME (buffer, oggpad);
    if (mux->last_ts != GST_CLOCK_TIME_NONE && run_time < mux->last_ts)
560
561
      GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
    else
562
      mux->last_ts = run_time;
563
564
  }

565
  caps = gst_pad_get_negotiated_caps (mux->srcpad);
566
  gst_buffer_set_caps (buffer, caps);
567
568
  if (caps)
    gst_caps_unref (caps);
569

570
571
572
573
574
575
576
577
578
  return gst_pad_push (mux->srcpad, buffer);
}

/* if all queues have at least one page, dequeue the page with the lowest
 * timestamp */
static gboolean
gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
{
  GSList *walk;
579
  GstOggPadData *opad = NULL;   /* "oldest" pad */
580
581
582
583
584
585
586
587
  GstClockTime oldest = GST_CLOCK_TIME_NONE;
  GstBuffer *buf = NULL;
  gboolean ret = FALSE;

  *flowret = GST_FLOW_OK;

  walk = mux->collect->data;
  while (walk) {
588
    GstOggPadData *pad = (GstOggPadData *) walk->data;
589
590
591
592
593
594
595

    /* We need each queue to either be at EOS, or have one or more pages
     * available with a set granulepos (i.e. not -1), otherwise we don't have
     * enough data yet to determine which stream needs to go next for correct
     * time ordering. */
    if (pad->pagebuffers->length == 0) {
      if (pad->eos) {
596
597
        GST_LOG_OBJECT (pad->collect.pad,
            "pad is EOS, skipping for dequeue decision");
598
      } else {
599
600
        GST_LOG_OBJECT (pad->collect.pad,
            "no pages in this queue, can't dequeue");
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
        return FALSE;
      }
    } else {
      /* We then need to check for a non-negative granulepos */
      int i;
      gboolean valid = FALSE;

      for (i = 0; i < pad->pagebuffers->length; i++) {
        buf = g_queue_peek_nth (pad->pagebuffers, i);
        /* Here we check the OFFSET_END, which is actually temporarily the
         * granulepos value for this buffer */
        if (GST_BUFFER_OFFSET_END (buf) != -1) {
          valid = TRUE;
          break;
        }
      }
      if (!valid) {
618
619
        GST_LOG_OBJECT (pad->collect.pad,
            "No page timestamps in queue, can't dequeue");
620
621
622
623
624
625
626
627
628
        return FALSE;
      }
    }

    walk = g_slist_next (walk);
  }

  walk = mux->collect->data;
  while (walk) {
629
    GstOggPadData *pad = (GstOggPadData *) walk->data;
630

631
    /* any page with a granulepos of -1 can be pushed immediately.
632
633
634
     * TODO: it CAN be, but it seems silly to do so? */
    buf = g_queue_peek_head (pad->pagebuffers);
    while (buf && GST_BUFFER_OFFSET_END (buf) == -1) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
635
      GST_LOG_OBJECT (pad->collect.pad, "[gp        -1] pushing page");
636
      g_queue_pop_head (pad->pagebuffers);
637
      *flowret = gst_ogg_mux_push_buffer (mux, buf, pad);
638
639
640
641
642
643
644
      buf = g_queue_peek_head (pad->pagebuffers);
      ret = TRUE;
    }

    if (buf) {
      /* if no oldest buffer yet, take this one */
      if (oldest == GST_CLOCK_TIME_NONE) {
645
646
647
648
        GST_LOG_OBJECT (mux, "no oldest yet, taking buffer %p from pad %"
            GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
            buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
        oldest = GST_BUFFER_OFFSET (buf);
649
650
651
        opad = pad;
      } else {
        /* if we have an oldest, compare with this one */
652
653
654
655
656
        if (GST_BUFFER_OFFSET (buf) < oldest) {
          GST_LOG_OBJECT (mux, "older buffer %p, taking from pad %"
              GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
              buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
          oldest = GST_BUFFER_OFFSET (buf);
657
658
659
660
661
662
663
664
665
666
          opad = pad;
        }
      }
    }
    walk = g_slist_next (walk);
  }

  if (oldest != GST_CLOCK_TIME_NONE) {
    g_assert (opad);
    buf = g_queue_pop_head (opad->pagebuffers);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
667
    GST_LOG_OBJECT (opad->collect.pad,
668
669
670
        GST_GP_FORMAT " pushing oldest page buffer %p (granulepos time %"
        GST_TIME_FORMAT ")", GST_BUFFER_OFFSET_END (buf), buf,
        GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
671
    *flowret = gst_ogg_mux_push_buffer (mux, buf, opad);
672
673
674
675
676
677
    ret = TRUE;
  }

  return ret;
}

678
679
680
681
682
683
684
685
686
687
688
689
690
/* put the given ogg page on a per-pad queue, timestamping it correctly.
 * after that, dequeue and push as many pages as possible.
 * Caller should make sure:
 * pad->timestamp     was set with the timestamp of the first packet put
 *                    on the page
 * pad->timestamp_end was set with the timestamp + duration of the last packet
 *                    put on the page
 * pad->gp_time       was set with the time matching the gp of the last
 *                    packet put on the page
 *
 * will also reset timestamp and timestamp_end, so caller func can restart
 * counting.
 */
691
static GstFlowReturn
692
693
gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPadData * pad,
    ogg_page * page, gboolean delta)
694
{
Wim Taymans's avatar
Wim Taymans committed
695
  GstFlowReturn ret;
696
  GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
697

698
  /* take the timestamp of the first packet on this page */
699
  GST_BUFFER_TIMESTAMP (buffer) = pad->timestamp;
700
  GST_BUFFER_DURATION (buffer) = pad->timestamp_end - pad->timestamp;
701
702
  /* take the gp time of the last completed packet on this page */
  GST_BUFFER_OFFSET (buffer) = pad->gp_time;
703

704
  /* the next page will start where the current page's end time leaves off */
705
706
  pad->timestamp = pad->timestamp_end;

707
  g_queue_push_tail (pad->pagebuffers, buffer);
708
709
710
  GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
      " queued buffer page %p (gp time %"
      GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT
711
      "), %d page buffers queued", GST_GP_CAST (ogg_page_granulepos (page)),
712
      buffer, GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)),
713
714
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
      g_queue_get_length (pad->pagebuffers));
Wim Taymans's avatar
Wim Taymans committed
715

716
717
718
719
  while (gst_ogg_mux_dequeue_page (mux, &ret)) {
    if (ret != GST_FLOW_OK)
      break;
  }
Wim Taymans's avatar
Wim Taymans committed
720
721

  return ret;
Wim Taymans's avatar
Wim Taymans committed
722
723
724
}

/*
725
726
727
728
729
730
731
 * Given two pads, compare the buffers queued on it.
 * Returns:
 *  0 if they have an equal priority
 * -1 if the first is better
 *  1 if the second is better
 * Priority decided by: a) validity, b) older timestamp, c) smaller number
 * of muxed pages
Wim Taymans's avatar
Wim Taymans committed
732
733
 */
static gint
734
735
gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPadData * first,
    GstOggPadData * second)
Wim Taymans's avatar
Wim Taymans committed
736
{
737
  guint64 firsttime, secondtime;
Wim Taymans's avatar
Wim Taymans committed
738

739
740
  /* if the first pad doesn't contain anything or is even NULL, return
   * the second pad as best candidate and vice versa */
741
  if (first == NULL || (first->buffer == NULL && first->next_buffer == NULL))
Wim Taymans's avatar
Wim Taymans committed
742
    return 1;
743
  if (second == NULL || (second->buffer == NULL && second->next_buffer == NULL))
Wim Taymans's avatar
Wim Taymans committed
744
745
    return -1;

746
  /* no timestamp on first buffer, it must go first */
747
748
749
750
  if (first->buffer)
    firsttime = GST_BUFFER_TIMESTAMP (first->buffer);
  else
    firsttime = GST_BUFFER_TIMESTAMP (first->next_buffer);
751
  if (firsttime == GST_CLOCK_TIME_NONE)
Wim Taymans's avatar
Wim Taymans committed
752
753
    return -1;

754
  /* no timestamp on second buffer, it must go first */
755
756
757
758
  if (second->buffer)
    secondtime = GST_BUFFER_TIMESTAMP (second->buffer);
  else
    secondtime = GST_BUFFER_TIMESTAMP (second->next_buffer);
759
  if (secondtime == GST_CLOCK_TIME_NONE)
Wim Taymans's avatar
Wim Taymans committed
760
761
    return 1;

762
763
764
765
766
  firsttime = gst_segment_to_running_time (first->segment, GST_FORMAT_TIME,
      firsttime);
  secondtime = gst_segment_to_running_time (second->segment, GST_FORMAT_TIME,
      secondtime);

767
768
  /* first buffer has higher timestamp, second one should go first */
  if (secondtime < firsttime)
Wim Taymans's avatar
Wim Taymans committed
769
    return 1;
770
771
  /* second buffer has higher timestamp, first one should go first */
  else if (secondtime > firsttime)
Wim Taymans's avatar
Wim Taymans committed
772
773
774
775
    return -1;
  else {
    /* buffers with equal timestamps, prefer the pad that has the
     * least number of pages muxed */
776
    if (second->pageno < first->pageno)
Wim Taymans's avatar
Wim Taymans committed
777
      return 1;
778
    else if (second->pageno > first->pageno)
Wim Taymans's avatar
Wim Taymans committed
779
780
781
782
783
784
785
      return -1;
  }

  /* same priority if all of the above failed */
  return 0;
}

786
787
788
789
790
791
792
793
794
795
796
797
798
/* make sure at least one buffer is queued on all pads, two if possible
 * 
 * if pad->buffer == NULL, pad->next_buffer !=  NULL, then
 *   we do not know if the buffer is the last or not
 * if pad->buffer != NULL, pad->next_buffer != NULL, then
 *   pad->buffer is not the last buffer for the pad
 * if pad->buffer != NULL, pad->next_buffer == NULL, then
 *   pad->buffer if the last buffer for the pad
 * 
 * returns a pointer to an oggpad that holds the best buffer, or
 * NULL when no pad was usable. "best" means the buffer marked
 * with the lowest timestamp. If best->buffer == NULL then nothing
 * should be done until more data arrives */
799
static GstOggPadData *
Wim Taymans's avatar
Wim Taymans committed
800
gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
Wim Taymans's avatar
Wim Taymans committed
801
{
802
  GstOggPadData *bestpad = NULL, *still_hungry = NULL;
Wim Taymans's avatar
Wim Taymans committed
803
804
805
  GSList *walk;

  /* try to make sure we have a buffer from each usable pad first */
Wim Taymans's avatar
Wim Taymans committed
806
  walk = ogg_mux->collect->data;
Wim Taymans's avatar
Wim Taymans committed
807
  while (walk) {
808
    GstOggPadData *pad;
Wim Taymans's avatar
Wim Taymans committed
809
    GstCollectData *data;
Wim Taymans's avatar
Wim Taymans committed
810

Wim Taymans's avatar
Wim Taymans committed
811
    data = (GstCollectData *) walk->data;
812
    pad = (GstOggPadData *) data;
Wim Taymans's avatar
Wim Taymans committed
813

Wim Taymans's avatar
Wim Taymans committed
814
815
    walk = g_slist_next (walk);

816
    GST_LOG_OBJECT (data->pad, "looking at pad for buffer");
817

Wim Taymans's avatar
Wim Taymans committed
818
    /* try to get a new buffer for this pad if needed and possible */
Wim Taymans's avatar
Wim Taymans committed
819
820
    if (pad->buffer == NULL) {
      GstBuffer *buf;
Wim Taymans's avatar
Wim Taymans committed
821

822
823
824
825
826
827
828
829
      /* shift the buffer along if needed (it's okay if next_buffer is NULL) */
      if (pad->buffer == NULL) {
        GST_LOG_OBJECT (data->pad, "shifting buffer %" GST_PTR_FORMAT,
            pad->next_buffer);
        pad->buffer = pad->next_buffer;
        pad->next_buffer = NULL;
      }

830
      buf = gst_collect_pads_pop (ogg_mux->collect, data);
831
      GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
Wim Taymans's avatar
Wim Taymans committed
832

833
834
      /* On EOS we get a NULL buffer */
      if (buf != NULL) {
835
836
837
838
        if (ogg_mux->delta_pad == NULL &&
            GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
          ogg_mux->delta_pad = pad;

839
840
841
        /* if we need headers */
        if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
          /* and we have one */
842
          ogg_packet packet;
843
844
          gboolean is_header;

845
846
          packet.packet = GST_BUFFER_DATA (buf);
          packet.bytes = GST_BUFFER_SIZE (buf);
847
848
849
850

          if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
            packet.granulepos = GST_BUFFER_OFFSET_END (buf);
          else
851
852
853
854
855
856
            packet.granulepos = 0;

          /* if we're not yet in data mode, ensure we're setup on the first packet */
          if (!pad->have_type) {
            pad->have_type = gst_ogg_stream_setup_map (&pad->map, &packet);
            if (!pad->have_type) {
857
858
859
860
861
              GST_ERROR_OBJECT (pad, "mapper didn't recognise input stream "
                  "(pad caps: %" GST_PTR_FORMAT ")", GST_PAD_CAPS (pad));
            } else {
              GST_DEBUG_OBJECT (pad, "caps detected: %" GST_PTR_FORMAT,
                  pad->map.caps);
862
863
864
            }
          }

865
866
867
868
          if (pad->have_type)
            is_header = gst_ogg_stream_packet_is_header (&pad->map, &packet);
          else                  /* fallback (FIXME 0.11: remove IN_CAPS hack) */
            is_header = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
869

870
          if (is_header) {
871
            GST_DEBUG_OBJECT (ogg_mux,
872
                "got header buffer in control state, ignoring");
873
            /* just ignore */
874
            pad->map.n_header_packets_seen++;
875
876
877
            gst_buffer_unref (buf);
            buf = NULL;
          } else {
878
            GST_DEBUG_OBJECT (ogg_mux,
Vincent Penquerc'h's avatar
Vincent Penquerc'h committed
879
                "got data buffer in control state, switching to data mode");
880
881
882
            /* this is a data buffer so switch to data state */
            pad->state = GST_OGG_PAD_STATE_DATA;
          }
Wim Taymans's avatar
Wim Taymans committed
883
        }
884
      } else {
885
        GST_DEBUG_OBJECT (data->pad, "EOS on pad");
886
887
888
889
        if (!pad->eos) {
          ogg_page page;
          GstFlowReturn ret;

890
891
892
          /* it's no longer active */
          ogg_mux->active_pads--;

893
894
895
          /* Just gone to EOS. Flush existing page(s) */
          pad->eos = TRUE;

896
          while (ogg_stream_flush (&pad->map.stream, &page)) {
897
898
899
900
901
902
903
904
905
            /* Place page into the per-pad queue */
            ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
                pad->first_delta);
            /* increment the page number counter */
            pad->pageno++;
            /* mark other pages as delta */
            pad->first_delta = TRUE;
          }
        }
Wim Taymans's avatar
Wim Taymans committed
906
      }
907

908
      pad->next_buffer = buf;
Wim Taymans's avatar
Wim Taymans committed
909
    }
Wim Taymans's avatar
Wim Taymans committed
910
911
912

    /* we should have a buffer now, see if it is the best pad to
     * pull on */
913
    if (pad->buffer || pad->next_buffer) {
914
      if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) {
915
916
917
        GST_LOG_OBJECT (data->pad,
            "new best pad, with buffers %" GST_PTR_FORMAT
            " and %" GST_PTR_FORMAT, pad->buffer, pad->next_buffer);
918

Wim Taymans's avatar
Wim Taymans committed
919
        bestpad = pad;
920
      }
921
    } else if (!pad->eos) {
922
      GST_LOG_OBJECT (data->pad, "hungry pad");
923
      still_hungry = pad;
Wim Taymans's avatar
Wim Taymans committed
924
925
    }
  }
926
927
928
929
930
931

  if (still_hungry)
    /* drop back into collectpads... */
    return still_hungry;
  else
    return bestpad;
Wim Taymans's avatar
Wim Taymans committed
932
933
}

934
static GList *
935
gst_ogg_mux_get_headers (GstOggPadData * pad)
936
937
938
{
  GList *res = NULL;
  GstStructure *structure;
939
  GstCaps *caps;
Wim Taymans's avatar
Wim Taymans committed
940
  GstPad *thepad;
941

Wim Taymans's avatar
Wim Taymans committed
942
  thepad = pad->collect.pad;
943

944
  GST_LOG_OBJECT (thepad, "getting headers");
Wim Taymans's avatar
Wim Taymans committed
945
946

  caps = gst_pad_get_negotiated_caps (thepad);
947
948
949
950
  if (caps != NULL) {
    const GValue *streamheader;

    structure = gst_caps_get_structure (caps, 0);
951
952
953
954
955
956
    streamheader = gst_structure_get_value (structure, "streamheader");
    if (streamheader != NULL) {
      GST_LOG_OBJECT (thepad, "got header");
      if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
        GArray *bufarr = g_value_peek_pointer (streamheader);
        gint i;
957

958
        GST_LOG_OBJECT (thepad, "got fixed list");
Wim Taymans's avatar
Wim Taymans committed
959

960
961
        for (i = 0; i < bufarr->len; i++) {
          GValue *bufval = &g_array_index (bufarr, GValue, i);
962

963
964
965
          GST_LOG_OBJECT (thepad, "item %d", i);
          if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
            GstBuffer *buf = g_value_peek_pointer (bufval);
966

967
            GST_LOG_OBJECT (thepad, "adding item %d to header list", i);
Wim Taymans's avatar
Wim Taymans committed
968

969
970
            gst_buffer_ref (buf);
            res = g_list_append (res, buf);
971
972
          }
        }
Wim Taymans's avatar
Wim Taymans committed
973
      } else {
974
        GST_LOG_OBJECT (thepad, "streamheader is not fixed list");
975
      }
976
977
978
979

      /* Start a new page for every CMML buffer */
      if (gst_structure_has_name (structure, "text/x-cmml"))
        pad->always_flush_page = TRUE;
980
981
982
983
    } else if (gst_structure_has_name (structure, "video/x-dirac")) {
      res = g_list_append (res, pad->buffer);
      pad->buffer = pad->next_buffer;
      pad->next_buffer = NULL;
984
      pad->always_flush_page = TRUE;
985
986
    } else {
      GST_LOG_OBJECT (thepad, "caps don't have streamheader");
987
    }
988
    gst_caps_unref (caps);
Wim Taymans's avatar
Wim Taymans committed
989
  } else {
990
    GST_LOG_OBJECT (thepad, "got empty caps as negotiated format");
991
992
993
994
  }
  return res;
}

Wim Taymans's avatar
Wim Taymans committed
995
static GstCaps *
996
997
gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
{
Wim Taymans's avatar
Wim Taymans committed
998
  GstStructure *structure;
999
  GValue array = { 0 };
1000
1001
  GList *walk = buffers;

Wim Taymans's avatar
Wim Taymans committed
1002
1003
1004
1005
  caps = gst_caps_make_writable (caps);

  structure = gst_caps_get_structure (caps, 0);

1006
  /* put buffers in a fixed list */
1007
  g_value_init (&array, GST_TYPE_ARRAY);
1008
1009
1010

  while (walk) {
    GstBuffer *buf = GST_BUFFER (walk->data);
1011
    GstBuffer *copy;
1012
1013
1014
1015
1016
    GValue value = { 0 };

    walk = walk->next;

    /* mark buffer */
1017
    GST_LOG ("Setting IN_CAPS on buffer of length %d", GST_BUFFER_SIZE (buf));
1018
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
1019
1020

    g_value_init (&value, GST_TYPE_BUFFER);
1021
1022
1023
    copy = gst_buffer_copy (buf);
    gst_value_set_buffer (&value, copy);
    gst_buffer_unref (copy);
1024
    gst_value_array_append_value (&array, &value);
1025
1026
    g_value_unset (&value);
  }
1027
1028
  gst_structure_set_value (structure, "streamheader", &array);
  g_value_unset (&array);
Wim Taymans's avatar
Wim Taymans committed
1029
1030

  return caps;
1031
1032
}

1033
/*
1034
1035
1036
1037
1038
1039
1040
 * For each pad we need to write out one (small) header in one
 * page that allows decoders to identify the type of the stream.
 * After that we need to write out all extra info for the decoders.
 * In the case of a codec that also needs data as configuration, we can
 * find that info in the streamcaps. 
 * After writing the headers we must start a new page for the data.
 */
Wim Taymans's avatar
Wim Taymans committed
1041
static GstFlowReturn
1042
1043
1044
1045
1046
gst_ogg_mux_send_headers (GstOggMux * mux)
{
  GSList *walk;
  GList *hbufs, *hwalk;
  GstCaps *caps;
Wim Taymans's avatar
Wim Taymans committed
1047
  GstFlowReturn ret;
1048
1049

  hbufs = NULL;
Wim Taymans's avatar
Wim Taymans committed
1050
  ret = GST_FLOW_OK;
1051

1052
  GST_LOG_OBJECT (mux, "collecting headers");
1053

Wim Taymans's avatar
Wim Taymans committed
1054
  walk = mux->collect->data;
1055
  while (walk) {
1056
    GstOggPadData *pad;
Wim Taymans's avatar
Wim Taymans committed
1057
    GstPad *thepad;
1058

1059
    pad = (GstOggPadData *) walk->data;
Wim Taymans's avatar
Wim Taymans committed
1060
    thepad = pad->collect.pad;
1061

Wim Taymans's avatar
Wim Taymans committed
1062
1063
    walk = g_slist_next (walk);

1064
    GST_LOG_OBJECT (mux, "looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
1065
1066

    /* if the pad has no buffer, we don't care */
1067
    if (pad->buffer == NULL && pad->next_buffer == NULL)
1068
1069
1070
      continue;

    /* now figure out the headers */
1071
    pad->map.headers = gst_ogg_mux_get_headers (pad);
1072
1073
  }

1074
  GST_LOG_OBJECT (mux, "creating BOS pages");
Wim Taymans's avatar
Wim Taymans committed
1075
  walk = mux->collect->data;
1076
  while (walk) {
1077
    GstOggPadData *pad;
1078
1079
1080
    GstBuffer *buf;
    ogg_packet packet;
    ogg_page page;
Wim Taymans's avatar
Wim Taymans committed
1081
    GstPad *thepad;
1082
1083
1084
    GstCaps *caps;
    GstStructure *structure;
    GstBuffer *hbuf;
Wim Taymans's avatar
Wim Taymans committed
1085

1086
    pad = (GstOggPadData *) walk->data;
Wim Taymans's avatar
Wim Taymans committed
1087
    thepad = pad->collect.pad;
1088
1089
    caps = gst_pad_get_negotiated_caps (thepad);
    structure = gst_caps_get_structure (caps, 0);
1090
1091
1092
1093
1094

    walk = walk->next;

    pad->packetno = 0;

1095
    GST_LOG_OBJECT (thepad, "looping over headers");
1096

1097
1098
1099
    if (pad->map.headers) {
      buf = GST_BUFFER (pad->map.headers->data);
      pad->map.headers = g_list_remove (pad->map.headers, buf);
1100
    } else if (pad->buffer) {
1101
1102
      buf = pad->buffer;
      gst_buffer_ref (buf);
James Doc Livingston's avatar