gstoggmux.c 32.7 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* OGG muxer plugin for GStreamer
 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>
Wim Taymans's avatar
Wim Taymans committed
25
26
#include <gst/base/gstcollectpads.h>

Wim Taymans's avatar
Wim Taymans committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <ogg/ogg.h>
/* memcpy - if someone knows a way to get rid of it, please speak up 
 * note: the ogg docs even say you need this... */
#include <string.h>
#include <time.h>

GST_DEBUG_CATEGORY_STATIC (gst_ogg_mux_debug);
#define GST_CAT_DEFAULT gst_ogg_mux_debug

#define GST_TYPE_OGG_MUX (gst_ogg_mux_get_type())
#define GST_OGG_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_MUX, GstOggMux))
#define GST_OGG_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_MUX, GstOggMux))
#define GST_IS_OGG_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_MUX))
#define GST_IS_OGG_MUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_MUX))

typedef struct _GstOggMux GstOggMux;
typedef struct _GstOggMuxClass GstOggMuxClass;

Wim Taymans's avatar
Wim Taymans committed
45
46
47
48
49
50
51
typedef enum
{
  GST_OGG_PAD_STATE_CONTROL = 0,
  GST_OGG_PAD_STATE_DATA = 1
}
GstOggPadState;

Wim Taymans's avatar
Wim Taymans committed
52
53
54
/* all information needed for one ogg stream */
typedef struct
{
Wim Taymans's avatar
Wim Taymans committed
55
  GstCollectData collect;       /* we extend the CollectData */
Wim Taymans's avatar
Wim Taymans committed
56
57
58
59
60
61
62

  GstBuffer *buffer;            /* the queued buffer for this pad */

  gint serial;
  ogg_stream_state stream;
  gint64 packetno;              /* number of next packet */
  gint64 pageno;                /* number of next page */
63
  guint64 duration;             /* duration of current page */
Wim Taymans's avatar
Wim Taymans committed
64
  gboolean eos;
65
  gint64 offset;
Wim Taymans's avatar
Wim Taymans committed
66

Wim Taymans's avatar
Wim Taymans committed
67
  GstOggPadState state;         /* state of the pad */
68
69

  GList *headers;
70
71
72
73

  gboolean new_page;            /* starting a new page */
  gboolean first_delta;         /* was the first packet in the page a delta */
  gboolean prev_delta;          /* was the previous buffer a delta frame */
Wim Taymans's avatar
Wim Taymans committed
74
75
76
77
78
79
80
}
GstOggPad;

struct _GstOggMux
{
  GstElement element;

Wim Taymans's avatar
Wim Taymans committed
81
  /* source pad */
Wim Taymans's avatar
Wim Taymans committed
82
83
  GstPad *srcpad;

Wim Taymans's avatar
Wim Taymans committed
84
85
  /* sinkpads */
  GstCollectPads *collect;
Wim Taymans's avatar
Wim Taymans committed
86

Wim Taymans's avatar
Wim Taymans committed
87
  /* the pad we are currently using to fill a page */
Wim Taymans's avatar
Wim Taymans committed
88
89
90
91
92
93
94
  GstOggPad *pulling;

  /* next timestamp for the page */
  GstClockTime next_ts;

  /* offset in stream */
  guint64 offset;
95
96
97

  /* need_headers */
  gboolean need_headers;
98
99

  guint64 max_delay;
100
  guint64 max_page_delay;
101

102
  GstOggPad *delta_pad;         /* when a delta frame is detected on a stream, we mark
103
104
                                   pages as delta frames up to the page that has the
                                   keyframe */
105

Wim Taymans's avatar
Wim Taymans committed
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
};

typedef enum
{
  GST_OGG_FLAG_BOS = GST_ELEMENT_FLAG_LAST,
  GST_OGG_FLAG_EOS
}
GstOggFlag;

struct _GstOggMuxClass
{
  GstElementClass parent_class;
};

/* elementfactory information */
static GstElementDetails gst_ogg_mux_details = GST_ELEMENT_DETAILS ("ogg muxer",
    "Codec/Muxer",
    "mux ogg streams (info about ogg: http://xiph.org)",
    "Wim Taymans <wim@fluendo.com>");

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

Wim Taymans's avatar
Wim Taymans committed
133
134
135
/* set to 0.5 seconds by default */
#define DEFAULT_MAX_DELAY	500000000LL
#define DEFAULT_MAX_PAGE_DELAY	500000000LL
Wim Taymans's avatar
Wim Taymans committed
136
137
enum
{
138
139
  ARG_0,
  ARG_MAX_DELAY,
140
  ARG_MAX_PAGE_DELAY,
Wim Taymans's avatar
Wim Taymans committed
141
142
143
144
145
146
147
148
149
150
151
};

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,
152
153
154
    GST_STATIC_CAPS ("video/x-theora; "
        "audio/x-vorbis; audio/x-flac; audio/x-speex; "
        "application/x-ogm-video; application/x-ogm-audio")
Wim Taymans's avatar
Wim Taymans committed
155
156
157
158
159
160
    );

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);

Wim Taymans's avatar
Wim Taymans committed
161
162
static GstFlowReturn
gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux);
Wim Taymans's avatar
Wim Taymans committed
163
164
165
166
167
168
169
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);
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);
170
171
static GstStateChangeReturn gst_ogg_mux_change_state (GstElement * element,
    GstStateChange transition);
Wim Taymans's avatar
Wim Taymans committed
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

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;

  if (!ogg_mux_type) {
    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,
    };

    ogg_mux_type =
        g_type_register_static (GST_TYPE_ELEMENT, "GstOggMux", &ogg_mux_info,
        0);
  }
  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));

  gst_element_class_set_details (element_class, &gst_ogg_mux_details);
}

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

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

  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);

226
227
228
  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
229
230
  gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad;

231
232
233
234
  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,
          DEFAULT_MAX_DELAY, (GParamFlags) G_PARAM_READWRITE));
235
236
237
238
  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,
          DEFAULT_MAX_PAGE_DELAY, (GParamFlags) G_PARAM_READWRITE));
239

Wim Taymans's avatar
Wim Taymans committed
240
241
242
243
  gstelement_class->change_state = gst_ogg_mux_change_state;

}

Wim Taymans's avatar
Wim Taymans committed
244
#if 0
Wim Taymans's avatar
Wim Taymans committed
245
246
247
248
249
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
250
    {GST_EVENT_DISCONTINUOUS, 0},
Wim Taymans's avatar
Wim Taymans committed
251
252
253
254
255
    {0,}
  };

  return gst_ogg_mux_sink_event_masks;
}
Wim Taymans's avatar
Wim Taymans committed
256
#endif
Wim Taymans's avatar
Wim Taymans committed
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

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);

  GST_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS);

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

Wim Taymans's avatar
Wim Taymans committed
274
275
276
  ogg_mux->collect = gst_collectpads_new ();
  gst_collectpads_set_function (ogg_mux->collect,
      (GstCollectPadsFunction) gst_ogg_mux_collected, ogg_mux);
Wim Taymans's avatar
Wim Taymans committed
277
  ogg_mux->pulling = NULL;
278
  ogg_mux->need_headers = TRUE;
279
  ogg_mux->max_delay = DEFAULT_MAX_DELAY;
280
  ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;
Wim Taymans's avatar
Wim Taymans committed
281

282
  ogg_mux->delta_pad = NULL;
Wim Taymans's avatar
Wim Taymans committed
283
284
285
}

static GstPadLinkReturn
286
gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer)
Wim Taymans's avatar
Wim Taymans committed
287
288
289
290
291
{
  GstOggMux *ogg_mux;

  ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
292
293
  GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s",
      gst_pad_get_name (pad));
Wim Taymans's avatar
Wim Taymans committed
294
295
296
297
298
299
300
301
302
303

  return GST_PAD_LINK_OK;
}

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
304
  GstElementClass *klass;
Wim Taymans's avatar
Wim Taymans committed
305
306
307

  g_return_val_if_fail (templ != NULL, NULL);

Wim Taymans's avatar
Wim Taymans committed
308
309
  if (templ->direction != GST_PAD_SINK)
    goto wrong_direction;
Wim Taymans's avatar
Wim Taymans committed
310
311
312
313

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

Wim Taymans's avatar
Wim Taymans committed
314
315
316
317
318
319
  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
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
    gint serial;
    gchar *name;

    if (req_name == NULL || strlen (req_name) < 6) {
      /* no name given when requesting the pad, use random serial number */
      serial = rand ();
    } else {
      /* parse serial number from requested padname */
      serial = atoi (&req_name[5]);
    }
    /* create new pad with the name */
    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 */
    {
Wim Taymans's avatar
Wim Taymans committed
338
339
340
341
342
      GstOggPad *oggpad;

      oggpad = (GstOggPad *)
          gst_collectpads_add_pad (ogg_mux->collect, newpad,
          sizeof (GstOggPad));
Wim Taymans's avatar
Wim Taymans committed
343
344
345
346
347
348
349
350

      oggpad->serial = serial;
      ogg_stream_init (&oggpad->stream, serial);
      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;
351
352
353
      oggpad->new_page = TRUE;
      oggpad->first_delta = FALSE;
      oggpad->prev_delta = FALSE;
Wim Taymans's avatar
Wim Taymans committed
354
355
356
357
358
359
360
361
362
    }
  }

  /* setup some pad functions */
  gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect);
  /* dd the pad to the element */
  gst_element_add_pad (element, newpad);

  return newpad;
Wim Taymans's avatar
Wim Taymans committed
363
364
365
366
367
368
369
370
371
372
373
374

  /* 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
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
}

/* handle events */
static gboolean
gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event)
{
  GstOggMux *ogg_mux;
  GstEventType type;

  ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));

  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);
}

Wim Taymans's avatar
Wim Taymans committed
399
#if 0
Wim Taymans's avatar
Wim Taymans committed
400
static GstBuffer *
401
gst_ogg_mux_next_buffer (GstOggPad * pad, gboolean * interrupt)
Wim Taymans's avatar
Wim Taymans committed
402
403
{
  GstData *data = NULL;
404
  GstBuffer *buffer = NULL;
Wim Taymans's avatar
Wim Taymans committed
405

406
407
  while (buffer == NULL) {
    //gst_pad_pull (pad->pad, &buffer);
Wim Taymans's avatar
Wim Taymans committed
408
    GST_DEBUG ("muxer: pulled %s:%s %p", GST_DEBUG_PAD_NAME (pad->pad), data);
Wim Taymans's avatar
Wim Taymans committed
409
410
411
412
413
414
415
416
417
418
419
    /* if it's an event, handle it */
    if (GST_IS_EVENT (data)) {
      GstEventType type;
      GstOggMux *ogg_mux;
      GstEvent *event = GST_EVENT (data);

      ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad->pad));
      type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;

      switch (type) {
        case GST_EVENT_EOS:
420
          pad->eos = TRUE;
421
          gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
422
          return NULL;
423
        case GST_EVENT_DISCONTINUOUS:
424
        {
425
          guint64 start_value, end_value;
426

427
428
          if (gst_event_discont_get_value (event, GST_FORMAT_TIME,
                  &start_value, &end_value)) {
429
            GST_DEBUG_OBJECT (ogg_mux,
430
431
432
                "got discont of %" G_GUINT64_FORMAT " and %" G_GUINT64_FORMAT
                " on pad %s:%s", start_value, end_value,
                GST_DEBUG_PAD_NAME (pad->pad));
433
          }
434
          pad->offset = start_value;
435
          gst_event_unref (event);
436

437
        }
438
          break;
Wim Taymans's avatar
Wim Taymans committed
439
440
441
442
443
        default:
          gst_pad_event_default (pad->pad, event);
          break;
      }
      data = NULL;
444
445
446
447
448
449
450
451
452
    } else {
      GstBuffer *buf = GST_BUFFER (data);
      gboolean incaps;

      incaps = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_IN_CAPS);
      /* if we need headers */
      if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
        /* and we have one */
        if (incaps) {
Wim Taymans's avatar
Wim Taymans committed
453
          GST_DEBUG ("muxer: got incaps buffer in control state, ignoring");
454
455
          /* just ignore */
          gst_buffer_unref (buf);
456
          /* pull next one in next iteration */
457
458
          data = NULL;
        } else {
Wim Taymans's avatar
Wim Taymans committed
459
          GST_DEBUG
460
461
462
463
464
              ("muxer: got data buffer in control state, switching to data mode");
          /* this is a data buffer so switch to data state */
          pad->state = GST_OGG_PAD_STATE_DATA;
        }
      }
Wim Taymans's avatar
Wim Taymans committed
465
466
467
468
    }
  }
  return GST_BUFFER (data);
}
Wim Taymans's avatar
Wim Taymans committed
469
#endif
Wim Taymans's avatar
Wim Taymans committed
470

471
static GstBuffer *
472
gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
Wim Taymans's avatar
Wim Taymans committed
473
474
475
476
{
  GstBuffer *buffer;

  /* allocate space for header and body */
Wim Taymans's avatar
Wim Taymans committed
477
  buffer = gst_buffer_new_and_alloc (page->header_len + page->body_len);
Wim Taymans's avatar
Wim Taymans committed
478
479
480
481
482
483
484
485
486
  memcpy (GST_BUFFER_DATA (buffer), page->header, page->header_len);
  memcpy (GST_BUFFER_DATA (buffer) + page->header_len,
      page->body, page->body_len);

  /* next_ts was the timestamp of the first buffer put in this page */
  GST_BUFFER_TIMESTAMP (buffer) = mux->next_ts;
  GST_BUFFER_OFFSET (buffer) = mux->offset;
  mux->offset += GST_BUFFER_SIZE (buffer);
  GST_BUFFER_OFFSET_END (buffer) = mux->offset;
487
  if (delta)
488
    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
Wim Taymans's avatar
Wim Taymans committed
489

490
491
492
  return buffer;
}

Wim Taymans's avatar
Wim Taymans committed
493
static GstFlowReturn
494
gst_ogg_mux_push_page (GstOggMux * mux, ogg_page * page, gboolean delta)
495
{
Wim Taymans's avatar
Wim Taymans committed
496
497
  GstBuffer *buffer;
  GstFlowReturn ret;
498

Wim Taymans's avatar
Wim Taymans committed
499
500
501
502
503
  buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);

  ret = gst_pad_push (mux->srcpad, buffer);

  return ret;
Wim Taymans's avatar
Wim Taymans committed
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
}

/*
 * Given two pads, compare the buffers queued on it and return 0 if they have
 * an equal priority, 1 if the new pad is better, -1 if the old pad is better 
 */
static gint
gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPad * old, GstOggPad * new)
{
  guint64 oldtime, newtime;

  /* if the old pad doesn't contain anything or is even NULL, return 
   * the new pad as best candidate and vice versa */
  if (old == NULL || old->buffer == NULL)
    return 1;
  if (new == NULL || new->buffer == NULL)
    return -1;

  /* no timestamp on old buffer, it must go first */
  oldtime = GST_BUFFER_TIMESTAMP (old->buffer);
  if (oldtime == GST_CLOCK_TIME_NONE)
    return -1;

  /* no timestamp on new buffer, it must go first */
  newtime = GST_BUFFER_TIMESTAMP (new->buffer);
  if (newtime == GST_CLOCK_TIME_NONE)
    return 1;

  /* old buffer has higher timestamp, new one should go first */
  if (newtime < oldtime)
    return 1;
  /* new buffer has higher timestamp, old one should go first */
  else if (newtime > oldtime)
    return -1;
  else {
    /* buffers with equal timestamps, prefer the pad that has the
     * least number of pages muxed */
    if (new->pageno < old->pageno)
      return 1;
    else if (new->pageno > old->pageno)
      return -1;
  }

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

/* make sure a buffer is queued on all pads, returns a pointer to an oggpad
 * that holds the best buffer or NULL when no pad was usable */
static GstOggPad *
Wim Taymans's avatar
Wim Taymans committed
554
gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
Wim Taymans's avatar
Wim Taymans committed
555
556
557
558
559
{
  GstOggPad *bestpad = NULL;
  GSList *walk;

  /* try to make sure we have a buffer from each usable pad first */
Wim Taymans's avatar
Wim Taymans committed
560
  walk = ogg_mux->collect->data;
Wim Taymans's avatar
Wim Taymans committed
561
  while (walk) {
Wim Taymans's avatar
Wim Taymans committed
562
563
    GstOggPad *pad;
    GstCollectData *data;
Wim Taymans's avatar
Wim Taymans committed
564

Wim Taymans's avatar
Wim Taymans committed
565
566
    data = (GstCollectData *) walk->data;
    pad = (GstOggPad *) data;
Wim Taymans's avatar
Wim Taymans committed
567

Wim Taymans's avatar
Wim Taymans committed
568
569
    walk = g_slist_next (walk);

Wim Taymans's avatar
Wim Taymans committed
570
    /* try to get a new buffer for this pad if needed and possible */
Wim Taymans's avatar
Wim Taymans committed
571
572
573
    if (pad->buffer == NULL) {
      GstBuffer *buf;
      gboolean incaps;
Wim Taymans's avatar
Wim Taymans committed
574

Wim Taymans's avatar
Wim Taymans committed
575
576
      buf = gst_collectpads_pop (ogg_mux->collect, data);

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
      /* On EOS we get a NULL buffer */
      if (buf != NULL) {
        incaps = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
        /* if we need headers */
        if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
          /* and we have one */
          if (incaps) {
            GST_DEBUG ("muxer: got incaps buffer in control state, ignoring");
            /* just ignore */
            gst_buffer_unref (buf);
            buf = NULL;
            /* We discarded the data of this pad, so it's not EOS. If no bestpad
               selected so far then use this one */
            if (!bestpad) {
              bestpad = pad;
            }
          } else {
            GST_DEBUG ("muxer: got data buffer in control state, switching "
                "to data mode");
            /* this is a data buffer so switch to data state */
            pad->state = GST_OGG_PAD_STATE_DATA;
          }
Wim Taymans's avatar
Wim Taymans committed
599
600
601
602
        }
      }
      pad->buffer = buf;
    }
Wim Taymans's avatar
Wim Taymans committed
603
604
605
606
607
608
609
610
611
612
613
614

    /* we should have a buffer now, see if it is the best pad to
     * pull on */
    if (pad->buffer != NULL) {
      if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) {
        bestpad = pad;
      }
    }
  }
  return bestpad;
}

615
616
617
618
619
620
621
static GList *
gst_ogg_mux_get_headers (GstOggPad * pad)
{
  GList *res = NULL;
  GstOggMux *ogg_mux;
  GstStructure *structure;
  const GstCaps *caps;
Wim Taymans's avatar
Wim Taymans committed
622
  GstPad *thepad;
623

Wim Taymans's avatar
Wim Taymans committed
624
  thepad = pad->collect.pad;
625

Wim Taymans's avatar
Wim Taymans committed
626
  ogg_mux = GST_OGG_MUX (GST_PAD_PARENT (thepad));
627

Wim Taymans's avatar
Wim Taymans committed
628
629
630
  GST_LOG ("getting headers from pad %s:%s", GST_DEBUG_PAD_NAME (thepad));

  caps = gst_pad_get_negotiated_caps (thepad);
631
632
633
634
635
636
  if (caps != NULL) {
    const GValue *streamheader;

    structure = gst_caps_get_structure (caps, 0);
    streamheader = gst_structure_get_value (structure, "streamheader");
    if (streamheader != NULL) {
Wim Taymans's avatar
Wim Taymans committed
637
      GST_LOG ("got header");
638
      if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
639
640
641
        GArray *bufarr = g_value_peek_pointer (streamheader);
        gint i;

Wim Taymans's avatar
Wim Taymans committed
642
643
        GST_LOG ("got fixed list");

644
645
646
        for (i = 0; i < bufarr->len; i++) {
          GValue *bufval = &g_array_index (bufarr, GValue, i);

Wim Taymans's avatar
Wim Taymans committed
647
          GST_LOG ("item %d", i);
648
649
650
          if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
            GstBuffer *buf = g_value_peek_pointer (bufval);

Wim Taymans's avatar
Wim Taymans committed
651
652
            GST_LOG ("adding item %d to header list", i);

Wim Taymans's avatar
Wim Taymans committed
653
            gst_buffer_ref (buf);
654
655
656
            res = g_list_append (res, buf);
          }
        }
Wim Taymans's avatar
Wim Taymans committed
657
658
      } else {
        GST_LOG ("streamheader is not fixed list");
659
      }
Wim Taymans's avatar
Wim Taymans committed
660
661
    } else {
      GST_LOG ("caps done have streamheader");
662
    }
Wim Taymans's avatar
Wim Taymans committed
663
664
  } else {
    GST_LOG ("got empty caps as negotiated format");
665
666
667
668
  }
  return res;
}

Wim Taymans's avatar
Wim Taymans committed
669
static GstCaps *
670
671
gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
{
Wim Taymans's avatar
Wim Taymans committed
672
  GstStructure *structure;
673
674
675
  GValue list = { 0 };
  GList *walk = buffers;

Wim Taymans's avatar
Wim Taymans committed
676
677
678
679
  caps = gst_caps_make_writable (caps);

  structure = gst_caps_get_structure (caps, 0);

680
  /* put buffers in a fixed list */
681
  g_value_init (&list, GST_TYPE_ARRAY);
682
683
684
685
686
687
688
689

  while (walk) {
    GstBuffer *buf = GST_BUFFER (walk->data);
    GValue value = { 0 };

    walk = walk->next;

    /* mark buffer */
690
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
691
692

    g_value_init (&value, GST_TYPE_BUFFER);
693
    gst_value_set_buffer (&value, buf);
694
695
696
697
698
    gst_value_list_append_value (&list, &value);
    g_value_unset (&value);
  }
  gst_structure_set_value (structure, "streamheader", &list);
  g_value_unset (&list);
Wim Taymans's avatar
Wim Taymans committed
699
700

  return caps;
701
702
}

703
/*
704
705
706
707
708
709
710
 * 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
711
static GstFlowReturn
712
713
714
715
716
gst_ogg_mux_send_headers (GstOggMux * mux)
{
  GSList *walk;
  GList *hbufs, *hwalk;
  GstCaps *caps;
Wim Taymans's avatar
Wim Taymans committed
717
  GstFlowReturn ret;
718
719

  hbufs = NULL;
Wim Taymans's avatar
Wim Taymans committed
720
  ret = GST_FLOW_OK;
721
722
723

  GST_LOG ("collecting headers");

Wim Taymans's avatar
Wim Taymans committed
724
  walk = mux->collect->data;
725
  while (walk) {
Wim Taymans's avatar
Wim Taymans committed
726
727
    GstOggPad *pad;
    GstPad *thepad;
728

Wim Taymans's avatar
Wim Taymans committed
729
730
    pad = (GstOggPad *) walk->data;
    thepad = pad->collect.pad;
731

Wim Taymans's avatar
Wim Taymans committed
732
733
734
    walk = g_slist_next (walk);

    GST_LOG ("looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
735
736
737
738
739
740
741
742
743
744

    /* if the pad has no buffer, we don't care */
    if (pad->buffer == NULL)
      continue;

    /* now figure out the headers */
    pad->headers = gst_ogg_mux_get_headers (pad);
  }

  GST_LOG ("creating first headers");
Wim Taymans's avatar
Wim Taymans committed
745
  walk = mux->collect->data;
746
  while (walk) {
Wim Taymans's avatar
Wim Taymans committed
747
    GstOggPad *pad;
748
749
750
    GstBuffer *buf;
    ogg_packet packet;
    ogg_page page;
Wim Taymans's avatar
Wim Taymans committed
751
752
753
754
    GstPad *thepad;

    pad = (GstOggPad *) walk->data;
    thepad = pad->collect.pad;
755
756
757
758
759

    walk = walk->next;

    pad->packetno = 0;

Wim Taymans's avatar
Wim Taymans committed
760
    GST_LOG ("looping over headers for pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
761
762
763
764
765

    if (pad->headers) {
      buf = GST_BUFFER (pad->headers->data);
      pad->headers = g_list_remove (pad->headers, buf);
    } else {
766
767
      buf = pad->buffer;
      gst_buffer_ref (buf);
768
769
770
771
772
773
    }

    /* create a packet from the buffer */
    packet.packet = GST_BUFFER_DATA (buf);
    packet.bytes = GST_BUFFER_SIZE (buf);
    packet.granulepos = GST_BUFFER_OFFSET_END (buf);
774
775
    if (packet.granulepos == -1)
      packet.granulepos = 0;
776
777
778
779
780
781
782
783
784
785
786
787
    /* mark BOS and packet number */
    packet.b_o_s = (pad->packetno == 0);
    packet.packetno = pad->packetno++;
    /* mark EOS */
    packet.e_o_s = 0;

    /* swap the packet in */
    ogg_stream_packetin (&pad->stream, &packet);
    gst_buffer_unref (buf);

    GST_LOG ("flushing page with first packet");
    while (ogg_stream_flush (&pad->stream, &page)) {
788
      GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
789
790
791
792
793
794
795

      GST_LOG ("swapped out page");
      hbufs = g_list_append (hbufs, hbuf);
    }
  }

  GST_LOG ("creating next headers");
Wim Taymans's avatar
Wim Taymans committed
796
  walk = mux->collect->data;
797
  while (walk) {
Wim Taymans's avatar
Wim Taymans committed
798
799
800
801
802
    GstOggPad *pad;
    GstPad *thepad;

    pad = (GstOggPad *) walk->data;
    thepad = pad->collect.pad;
803
804
805

    walk = walk->next;

Wim Taymans's avatar
Wim Taymans committed
806
    GST_LOG ("looping over headers for pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
807
808
809
810
811
812
813
814
815
816
817
818
819

    hwalk = pad->headers;
    while (hwalk) {
      GstBuffer *buf = GST_BUFFER (hwalk->data);
      ogg_packet packet;
      ogg_page page;

      hwalk = hwalk->next;

      /* create a packet from the buffer */
      packet.packet = GST_BUFFER_DATA (buf);
      packet.bytes = GST_BUFFER_SIZE (buf);
      packet.granulepos = GST_BUFFER_OFFSET_END (buf);
820
821
      if (packet.granulepos == -1)
        packet.granulepos = 0;
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
      /* mark BOS and packet number */
      packet.b_o_s = (pad->packetno == 0);
      packet.packetno = pad->packetno++;
      /* mark EOS */
      packet.e_o_s = 0;

      /* swap the packet in */
      ogg_stream_packetin (&pad->stream, &packet);
      gst_buffer_unref (buf);

      /* if last header, flush page */
      if (hwalk == NULL) {
        GST_LOG ("flushing page as packet %d is first or last packet",
            pad->packetno);
        while (ogg_stream_flush (&pad->stream, &page)) {
837
          GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
838
839
840
841
842
843
844
845

          GST_LOG ("swapped out page");
          hbufs = g_list_append (hbufs, hbuf);
        }
      } else {
        GST_LOG ("try to swap out page");
        /* just try to swap out a page then */
        while (ogg_stream_pageout (&pad->stream, &page) > 0) {
846
          GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
847
848
849
850
851
852

          GST_LOG ("swapped out page");
          hbufs = g_list_append (hbufs, hbuf);
        }
      }
    }
Wim Taymans's avatar
Wim Taymans committed
853
854
    g_list_free (pad->headers);
    pad->headers = NULL;
855
856
857
858
859
860
  }
  /* hbufs holds all buffers for the headers now */

  /* create caps with the buffers */
  caps = gst_pad_get_caps (mux->srcpad);
  if (caps) {
Wim Taymans's avatar
Wim Taymans committed
861
862
    caps = gst_ogg_mux_set_header_on_caps (caps, hbufs);
    gst_pad_set_caps (mux->srcpad, caps);
863
864
865
866
867
868
869
870
  }
  /* and send the buffers */
  hwalk = hbufs;
  while (hwalk) {
    GstBuffer *buf = GST_BUFFER (hwalk->data);

    hwalk = hwalk->next;

Wim Taymans's avatar
Wim Taymans committed
871
872
    if ((ret = gst_pad_push (mux->srcpad, buf)) != GST_FLOW_OK)
      break;
873
  }
Wim Taymans's avatar
Wim Taymans committed
874
  g_list_free (hbufs);
Wim Taymans's avatar
Wim Taymans committed
875
876

  return ret;
877
878
}

Wim Taymans's avatar
Wim Taymans committed
879
880
881
/* this function is called when there is data on all pads.
 *
 * basic idea:
Wim Taymans's avatar
Wim Taymans committed
882
 *
Wim Taymans's avatar
Wim Taymans committed
883
884
 * 1) find a pad to pull on, this is done by looking at the buffers 
 *    to decide which one should be muxed first.
Wim Taymans's avatar
Wim Taymans committed
885
 * 2) store the selected pad and keep on pulling until we fill a 
886
887
 *    complete ogg page or the ogg page is filled above the max-delay 
 *    threshold. This is needed because the ogg spec says that
Wim Taymans's avatar
Wim Taymans committed
888
889
890
 *    you should fill a complete page with data from the same logical
 *    stream. When the page is filled, go back to 1).
 * 3) before filling a packet, read ahead one more buffer to see if this
891
892
893
 *    packet is the last of the stream. We need to do this because the ogg
 *    spec mandates that the last packet should have the EOS flag set before
 *    sending it to ogg.
Wim Taymans's avatar
Wim Taymans committed
894
 */
Wim Taymans's avatar
Wim Taymans committed
895
896
static GstFlowReturn
gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
Wim Taymans's avatar
Wim Taymans committed
897
{
898
  GstOggPad *best;
899
  gboolean delta_unit;
Wim Taymans's avatar
Wim Taymans committed
900
  GstFlowReturn ret;
Wim Taymans's avatar
Wim Taymans committed
901

Wim Taymans's avatar
Wim Taymans committed
902
903
904
  GST_DEBUG ("collected");

  best = gst_ogg_mux_queue_pads (ogg_mux);
905
  if (best && !best->buffer)
Wim Taymans's avatar
Wim Taymans committed
906
    return GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
907

Wim Taymans's avatar
Wim Taymans committed
908
  GST_DEBUG ("best pad %p", best);
909

910
911
912
913
914
  if (!best) {                  /* EOS : FIXME !! We need to handle EOS correctly */
    gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
    return GST_FLOW_WRONG_STATE;
  }

915
916
917
918
  /* we're pulling a pad and there is a better one, see if we need
   * to flush the current page */
  if (ogg_mux->pulling && best &&
      ogg_mux->pulling != best && ogg_mux->pulling->buffer) {
919
920
    GstOggPad *pad = ogg_mux->pulling;

921
    GstClockTime last_ts =
922
        GST_BUFFER_TIMESTAMP (pad->buffer) + GST_BUFFER_DURATION (pad->buffer);
Wim Taymans's avatar
Wim Taymans committed
923

924
925
926
927
928
    /* if the next packet in the current page is going to make the page 
     * too long, we need to flush */
    if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) {
      ogg_page page;

929
      while (ogg_stream_flush (&pad->stream, &page)) {
Wim Taymans's avatar
Wim Taymans committed
930
        ret = gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta);
931
        /* increment the page number counter */
932
933
934
        pad->pageno++;
        /* mark other pages as delta */
        pad->first_delta = TRUE;
935
      }
936
      pad->new_page = TRUE;
937
938
939
940
941
      ogg_mux->pulling = NULL;
    }
  }

  /* if we don't know which pad to pull on, use the best one */
Wim Taymans's avatar
Wim Taymans committed
942
  if (ogg_mux->pulling == NULL) {
943
    ogg_mux->pulling = best;
Wim Taymans's avatar
Wim Taymans committed
944
945
946
947
948
    /* remember timestamp of first buffer for this new pad */
    if (ogg_mux->pulling != NULL) {
      ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer);
    } else {
      /* no pad to pull on, send EOS */
949
      gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
Wim Taymans's avatar
Wim Taymans committed
950
      return GST_FLOW_WRONG_STATE;
Wim Taymans's avatar
Wim Taymans committed
951
952
    }
  }
953

954
  if (ogg_mux->need_headers) {
Wim Taymans's avatar
Wim Taymans committed
955
    ret = gst_ogg_mux_send_headers (ogg_mux);
956
957
    ogg_mux->need_headers = FALSE;
  }
Wim Taymans's avatar
Wim Taymans committed
958
959
960
961
962
963
964
965

  /* we are pulling from a pad, continue to do so until a page
   * has been filled and pushed */
  if (ogg_mux->pulling != NULL) {
    ogg_packet packet;
    ogg_page page;
    GstBuffer *buf, *tmpbuf;
    GstOggPad *pad = ogg_mux->pulling;
966
    gint64 duration;
967
    gboolean force_flush;
Wim Taymans's avatar
Wim Taymans committed
968
969
970
971

    /* now see if we have a buffer */
    buf = pad->buffer;

972
    delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
973
974
    duration = GST_BUFFER_DURATION (buf);

Wim Taymans's avatar
Wim Taymans committed
975
976
977
978
    /* create a packet from the buffer */
    packet.packet = GST_BUFFER_DATA (buf);
    packet.bytes = GST_BUFFER_SIZE (buf);
    packet.granulepos = GST_BUFFER_OFFSET_END (buf);
979
980
    if (packet.granulepos == -1)
      packet.granulepos = 0;
Wim Taymans's avatar
Wim Taymans committed
981
982
983
984
    /* mark BOS and packet number */
    packet.b_o_s = (pad->packetno == 0);
    packet.packetno = pad->packetno++;

Wim Taymans's avatar
Wim Taymans committed
985
#if 0
Wim Taymans's avatar
Wim Taymans committed
986
    /* read ahead one more buffer to find EOS */
987
988
989
    tmpbuf = gst_ogg_mux_next_buffer (pad, &interrupt);
    if (interrupt)
      return;
Wim Taymans's avatar
Wim Taymans committed
990
991
992
993
994
995
996
    /* data exhausted on this pad */
    if (tmpbuf == NULL) {
      /* stop pulling from the pad */
      ogg_mux->pulling = NULL;
    }
    /* mark EOS */
    packet.e_o_s = (tmpbuf == NULL ? 1 : 0);
Wim Taymans's avatar
Wim Taymans committed
997
998
999
1000
#else
    packet.e_o_s = 0;
    tmpbuf = NULL;
#endif
Wim Taymans's avatar
Wim Taymans committed
1001

1002
1003
    /* we flush when we see a new keyframe */
    force_flush = (pad->prev_delta && !delta_unit);
1004
1005
1006
1007
    if (duration != -1) {
      pad->duration += duration;
      /* if page duration exceeds max, flush page */
      if (pad->duration > ogg_mux->max_page_delay) {
1008
        force_flush = TRUE;
1009
1010
1011
1012
        pad->duration = 0;
      }
    }

1013
1014
1015
    /* force flush */
    if (force_flush) {
      while (ogg_stream_flush (&pad->stream, &page)) {
Wim Taymans's avatar
Wim Taymans committed
1016
        ret = gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta);
1017
1018
        /* increment the page number counter */
        pad->pageno++;
1019
        /* mark other pages as delta */
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
        pad->first_delta = TRUE;
      }
      pad->new_page = TRUE;
    }

    /* if this is the first packet of a new page figure out the delta flag */
    if (pad->new_page) {
      if (delta_unit) {
        /* This page is a delta frame */
        if (ogg_mux->delta_pad == NULL) {
          /* we got a delta unit on this pad */
          ogg_mux->delta_pad = pad;
        }
        /* mark the page as delta */
        pad->first_delta = TRUE;
      } else {
        /* got a keyframe */
        if (ogg_mux->delta_pad == pad) {
          /* if we get it on the pad with deltaunits,
           * we mark the page as non delta */
          pad->first_delta = FALSE;
1041
1042
1043
1044
1045
1046
        } else if (ogg_mux->delta_pad != NULL) {
          /* if there are pads with delta frames, we
           * must mark this one as delta */
          pad->first_delta = TRUE;
        } else {
          pad->first_delta = FALSE;
1047
1048
1049
1050
1051
1052
1053
1054
1055
        }
      }
      pad->new_page = FALSE;
    }

    /* save key unit to track delta->key unit transitions */
    pad->prev_delta = delta_unit;

    /* swap the packet in */
Wim Taymans's avatar
Wim Taymans committed
1056
1057
1058
1059
1060
    if (packet.e_o_s == 1)
      GST_DEBUG_OBJECT (pad, "swapping in EOS packet");
    if (packet.b_o_s == 1)
      GST_DEBUG_OBJECT (pad, "swapping in BOS packet");

1061
1062
1063
1064
1065
1066
1067
1068
1069
    ogg_stream_packetin (&pad->stream, &packet);

    /* don't need the old buffer anymore */
    gst_buffer_unref (pad->buffer);
    /* store new readahead buffer */
    pad->buffer = tmpbuf;

    /* let ogg write out the pages now. The packet we got could end 
     * up in more than one page so we need to write them all */
1070
1071
    if (ogg_stream_pageout (&pad->stream, &page) > 0) {
      /* push the page */
Wim Taymans's avatar
Wim Taymans committed
1072
      ret = gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta);
Wim Taymans's avatar
Wim Taymans committed
1073
      pad->pageno++;
1074
1075
1076
1077
1078
1079
1080
1081
      /* mark next pages as delta */
      pad->first_delta = TRUE;

      /* use an inner loop her to flush the remaining pages and
       * mark them as delta frames as well */
      while (ogg_stream_pageout (&pad->stream, &page) > 0) {
        /* we have a complete page now, we can push the page 
         * and make sure to pull on a new pad the next time around */
Wim Taymans's avatar
Wim Taymans committed
1082
        ret = gst_ogg_mux_push_page (ogg_mux, &page, pad->first_delta);
1083
1084
1085
1086
1087
1088
        /* increment the page number counter */
        pad->pageno++;
      }
      /* need a new page as well */
      pad->new_page = TRUE;
      pad->duration = 0;
Wim Taymans's avatar
Wim Taymans committed
1089
1090
1091
1092
1093
      /* we're done pulling on this pad, make sure to choose a new 
       * pad for pulling in the next iteration */
      ogg_mux->pulling = NULL;
    }
  }
Wim Taymans's avatar
Wim Taymans committed
1094
1095

  return GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
1096
1097
1098
1099
1100
1101
}

static void
gst_ogg_mux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
{
1102
1103
1104
1105
  GstOggMux *ogg_mux;

  ogg_mux = GST_OGG_MUX (object);

Wim Taymans's avatar
Wim Taymans committed
1106
  switch (prop_id) {
1107
1108
1109
    case ARG_MAX_DELAY:
      g_value_set_uint64 (value, ogg_mux->max_delay);
      break;
1110
1111
1112
    case ARG_MAX_PAGE_DELAY:
      g_value_set_uint64 (value, ogg_mux->max_page_delay);
      break;
Wim Taymans's avatar
Wim Taymans committed
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_ogg_mux_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec)
{
1123
1124
1125
1126
  GstOggMux *ogg_mux;

  ogg_mux = GST_OGG_MUX (object);

Wim Taymans's avatar
Wim Taymans committed
1127
  switch (prop_id) {
1128
1129
1130
    case ARG_MAX_DELAY:
      ogg_mux->max_delay = g_value_get_uint64 (value);
      break;
1131
1132
1133
    case ARG_MAX_PAGE_DELAY:
      ogg_mux->max_page_delay = g_value_get_uint64 (value);
      break;
Wim Taymans's avatar
Wim Taymans committed
1134
1135
1136
1137
1138
1139
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

1140
1141
static GstStateChangeReturn
gst_ogg_mux_change_state (GstElement * element, GstStateChange transition)
Wim Taymans's avatar
Wim Taymans committed
1142
1143
{
  GstOggMux *ogg_mux;
1144
  GstStateChangeReturn ret;
Wim Taymans's avatar
Wim Taymans committed
1145
1146
1147
1148

  ogg_mux = GST_OGG_MUX (element);

  switch (transition) {
1149
1150
    case GST_STATE_CHANGE_NULL_TO_READY:
    case GST_STATE_CHANGE_READY_TO_PAUSED:
Wim Taymans's avatar
Wim Taymans committed
1151
1152
1153
      ogg_mux->next_ts = 0;
      ogg_mux->offset = 0;
      ogg_mux->pulling = NULL;
Wim Taymans's avatar
Wim Taymans committed
1154
      gst_collectpads_start (ogg_mux->collect);
Wim Taymans's avatar
Wim Taymans committed
1155
      break;
1156
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
Wim Taymans's avatar
Wim Taymans committed
1157
      break;
1158
    case GST_STATE_CHANGE_PAUSED_TO_READY:
Wim Taymans's avatar
Wim Taymans committed
1159
1160
1161
1162
      gst_collectpads_stop (ogg_mux->collect);
      break;
    default:
      break;
Wim Taymans's avatar
Wim Taymans committed
1163
  }
1164

1165
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1166

Wim Taymans's avatar
Wim Taymans committed
1167
  return ret;
Wim Taymans's avatar
Wim Taymans committed
1168
1169
1170
1171
1172
1173
1174
}

gboolean
gst_ogg_mux_plugin_init (GstPlugin * plugin)
{
  GST_DEBUG_CATEGORY_INIT (gst_ogg_mux_debug, "oggmux", 0, "ogg muxer");

1175
  return gst_element_register (plugin, "oggmux", GST_RANK_NONE,
Wim Taymans's avatar
Wim Taymans committed
1176
1177
      GST_TYPE_OGG_MUX);
}