theoradec.c 38.8 KB
Newer Older
Benjamin Otte's avatar
Benjamin Otte committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* GStreamer
 * Copyright (C) 2004 Benjamin Otte <in7y118@public.uni-hamburg.de>
 *
 * 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.
 */

Wim Taymans's avatar
Wim Taymans committed
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
 * SECTION:element-theoradec
 * @see_also: theoraenc, oggdemux
 *
 * <refsect2>
 * <para>
 * This element decodes theora streams into raw video
 * <ulink url="http://www.theora.org/">Theora</ulink> is a royalty-free
 * video codec maintained by the <ulink url="http://www.xiph.org/">Xiph.org
 * Foundation</ulink>, based on the VP3 codec.
 * </para>
 * <para>
 * </para>
 * <title>Example pipeline</title>
 * <programlisting>
 * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! xvimagesink
 * </programlisting>
 * This example pipeline will decode an ogg stream and decodes the theora video. Refer to
 * the theoraenc example to create the ogg file.
 * </refsect2>
 *
 * Last reviewed on 2006-03-01 (0.10.4)
 */

Benjamin Otte's avatar
Benjamin Otte committed
44
45
46
47
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

Wim Taymans's avatar
Wim Taymans committed
48
#include "gsttheoradec.h"
Benjamin Otte's avatar
Benjamin Otte committed
49
50
#include <gst/tag/tag.h>

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
51
#define GST_CAT_DEFAULT theoradec_debug
52
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
Benjamin Otte's avatar
Benjamin Otte committed
53

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
54
#define THEORA_DEF_CROP         TRUE
55
56
57
58
59
60
enum
{
  ARG_0,
  ARG_CROP
};

Stefan Kost's avatar
Stefan Kost committed
61
static const GstElementDetails theora_dec_details =
j^'s avatar
j^ committed
62
GST_ELEMENT_DETAILS ("Theora video decoder",
63
64
65
66
    "Codec/Decoder/Video",
    "decode raw theora streams to raw YUV video",
    "Benjamin Otte <in7y118@public.uni-hamburg.de>, "
    "Wim Taymans <wim@fluendo.com>");
Benjamin Otte's avatar
Benjamin Otte committed
67
68

static GstStaticPadTemplate theora_dec_src_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
69
70
71
72
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-yuv, "
73
        "format = (fourcc) I420, "
74
        "framerate = (fraction) [0/1, MAX], "
75
        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
76
    );
Benjamin Otte's avatar
Benjamin Otte committed
77
78

static GstStaticPadTemplate theora_dec_sink_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
79
80
81
82
83
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-theora")
    );
Benjamin Otte's avatar
Benjamin Otte committed
84
85

GST_BOILERPLATE (GstTheoraDec, gst_theora_dec, GstElement, GST_TYPE_ELEMENT);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
86

87
88
89
90
91
static void theora_dec_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
static void theora_dec_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);

92
93
static gboolean theora_dec_sink_event (GstPad * pad, GstEvent * event);
static GstFlowReturn theora_dec_chain (GstPad * pad, GstBuffer * buffer);
94
95
static GstStateChangeReturn theora_dec_change_state (GstElement * element,
    GstStateChange transition);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
96
static gboolean theora_dec_src_event (GstPad * pad, GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
97
static gboolean theora_dec_src_query (GstPad * pad, GstQuery * query);
98
99
100
101
102
103
static gboolean theora_dec_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value);
static gboolean theora_dec_sink_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value);
Wim Taymans's avatar
Wim Taymans committed
104
105
106
static gboolean theora_dec_sink_query (GstPad * pad, GstQuery * query);

#if 0
107
static const GstFormat *theora_get_formats (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
108
109
#endif
#if 0
110
static const GstEventMask *theora_get_event_masks (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
111
#endif
112
static const GstQueryType *theora_get_query_types (GstPad * pad);
Benjamin Otte's avatar
Benjamin Otte committed
113
114
115
116
117
118


static void
gst_theora_dec_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
119
120

  gst_element_class_add_pad_template (element_class,
Benjamin Otte's avatar
Benjamin Otte committed
121
122
123
124
125
126
127
      gst_static_pad_template_get (&theora_dec_src_factory));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&theora_dec_sink_factory));
  gst_element_class_set_details (element_class, &theora_dec_details);
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
128
gst_theora_dec_class_init (GstTheoraDecClass * klass)
Benjamin Otte's avatar
Benjamin Otte committed
129
{
130
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Benjamin Otte's avatar
Benjamin Otte committed
131
132
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);

133
134
135
136
137
138
139
140
  gobject_class->set_property = theora_dec_set_property;
  gobject_class->get_property = theora_dec_get_property;

  g_object_class_install_property (gobject_class, ARG_CROP,
      g_param_spec_boolean ("crop", "Crop",
          "Crop the image to the visible region", THEORA_DEF_CROP,
          (GParamFlags) G_PARAM_READWRITE));

Benjamin Otte's avatar
Benjamin Otte committed
141
  gstelement_class->change_state = theora_dec_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
142
143

  GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder");
Benjamin Otte's avatar
Benjamin Otte committed
144
145
146
}

static void
147
gst_theora_dec_init (GstTheoraDec * dec, GstTheoraDecClass * g_class)
Benjamin Otte's avatar
Benjamin Otte committed
148
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
149
  dec->sinkpad =
150
      gst_pad_new_from_static_template (&theora_dec_sink_factory, "sink");
Wim Taymans's avatar
Wim Taymans committed
151
  gst_pad_set_query_function (dec->sinkpad, theora_dec_sink_query);
152
  gst_pad_set_event_function (dec->sinkpad, theora_dec_sink_event);
Benjamin Otte's avatar
Benjamin Otte committed
153
154
155
  gst_pad_set_chain_function (dec->sinkpad, theora_dec_chain);
  gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
156
  dec->srcpad =
157
      gst_pad_new_from_static_template (&theora_dec_src_factory, "src");
Benjamin Otte's avatar
Benjamin Otte committed
158
  gst_pad_set_event_function (dec->srcpad, theora_dec_src_event);
159
  gst_pad_set_query_type_function (dec->srcpad, theora_get_query_types);
160
  gst_pad_set_query_function (dec->srcpad, theora_dec_src_query);
161
  gst_pad_use_fixed_caps (dec->srcpad);
162

Benjamin Otte's avatar
Benjamin Otte committed
163
164
  gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);

165
  dec->crop = THEORA_DEF_CROP;
166
167
  dec->gather = NULL;
  dec->decode = NULL;
168
  dec->queued = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
169
}
170

171
172
173
static void
gst_theora_dec_reset (GstTheoraDec * dec)
{
174
175
  GList *walk;

176
177
178
  dec->need_keyframe = TRUE;
  dec->last_timestamp = -1;
  dec->granulepos = -1;
179
180
  dec->discont = TRUE;
  dec->frame_nr = -1;
181
182
183
184
185
186
  gst_segment_init (&dec->segment, GST_FORMAT_TIME);

  GST_OBJECT_LOCK (dec);
  dec->proportion = 1.0;
  dec->earliest_time = -1;
  GST_OBJECT_UNLOCK (dec);
187
188
189
190
191
192

  for (walk = dec->queued; walk; walk = g_list_next (walk)) {
    gst_buffer_unref (GST_BUFFER_CAST (walk->data));
  }
  g_list_free (dec->queued);
  dec->queued = NULL;
193
194
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
195
196
197
198
199
200
static int
_theora_ilog (unsigned int v)
{
  int ret = 0;

  while (v) {
201
    ret++;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
202
    v >>= 1;
203
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
204
  return (ret);
205
206
}

207
208
static gint64
_theora_granule_frame (GstTheoraDec * dec, gint64 granulepos)
209
210
211
212
{
  guint ilog;
  gint framecount;

213
214
  if (granulepos == -1)
    return -1;
215

216
  ilog = dec->granule_shift;
217

218
219
220
221
  /* granulepos is last ilog bits for counting pframes since last iframe and 
   * bits in front of that for the framenumber of the last iframe. */
  framecount = granulepos >> ilog;
  framecount += granulepos - (framecount << ilog);
222
223
224

  GST_DEBUG_OBJECT (dec, "framecount=%d, ilog=%u", framecount, ilog);

225
226
227
228
229
230
231
232
  return framecount;
}

static GstClockTime
_theora_granule_time (GstTheoraDec * dec, gint64 granulepos)
{
  gint framecount;

233
  /* invalid granule results in invalid time */
234
235
236
  if (granulepos == -1)
    return -1;

237
  /* get framecount */
238
  framecount = _theora_granule_frame (dec, granulepos);
239
240
241
242
243
244
245
246
247
248
249
250

  return gst_util_uint64_scale_int (framecount * GST_SECOND,
      dec->info.fps_denominator, dec->info.fps_numerator);
}

static gint64
_inc_granulepos (GstTheoraDec * dec, gint64 granulepos)
{
  gint framecount;

  if (granulepos == -1)
    return -1;
251

252
253
254
  framecount = _theora_granule_frame (dec, granulepos);

  return (framecount + 1) << dec->granule_shift;
255
256
}

Wim Taymans's avatar
Wim Taymans committed
257
#if 0
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
static const GstFormat *
theora_get_formats (GstPad * pad)
{
  static GstFormat src_formats[] = {
    GST_FORMAT_DEFAULT,         /* frames in this case */
    GST_FORMAT_TIME,
    GST_FORMAT_BYTES,
    0
  };
  static GstFormat sink_formats[] = {
    GST_FORMAT_DEFAULT,
    GST_FORMAT_TIME,
    0
  };

  return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
}
Wim Taymans's avatar
Wim Taymans committed
275
#endif
276

Wim Taymans's avatar
Wim Taymans committed
277
#if 0
278
279
280
281
282
283
284
285
286
287
static const GstEventMask *
theora_get_event_masks (GstPad * pad)
{
  static const GstEventMask theora_src_event_masks[] = {
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
    {0,}
  };

  return theora_src_event_masks;
}
Wim Taymans's avatar
Wim Taymans committed
288
#endif
289
290
291
292
293
294

static const GstQueryType *
theora_get_query_types (GstPad * pad)
{
  static const GstQueryType theora_src_query_types[] = {
    GST_QUERY_POSITION,
295
296
    GST_QUERY_DURATION,
    GST_QUERY_CONVERT,
297
298
299
300
301
302
303
    0
  };

  return theora_src_query_types;
}


304
static gboolean
305
306
307
theora_dec_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
308
{
309
310
311
312
  gboolean res = TRUE;
  GstTheoraDec *dec;
  guint64 scale = 1;

Wim Taymans's avatar
Wim Taymans committed
313
314
315
316
317
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

318
319
320
321
322
323
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

  /* we need the info part before we can done something */
  if (!dec->have_header)
    goto no_header;

324
325
326
327
  switch (src_format) {
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
328
329
          *dest_value = gst_util_uint64_scale_int (src_value, 2,
              dec->info.height * dec->info.width * 3);
330
331
332
333
334
335
336
          break;
        case GST_FORMAT_TIME:
          /* seems like a rather silly conversion, implement me if you like */
        default:
          res = FALSE;
      }
      break;
337
    case GST_FORMAT_TIME:
338
339
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
340
          scale = 3 * (dec->info.width * dec->info.height) / 2;
341
        case GST_FORMAT_DEFAULT:
342
          *dest_value = scale * gst_util_uint64_scale (src_value,
343
              dec->info.fps_numerator, dec->info.fps_denominator * GST_SECOND);
344
345
346
347
          break;
        default:
          res = FALSE;
      }
348
349
      break;
    case GST_FORMAT_DEFAULT:
350
351
      switch (*dest_format) {
        case GST_FORMAT_TIME:
352
          *dest_value = gst_util_uint64_scale (src_value,
353
              GST_SECOND * dec->info.fps_denominator, dec->info.fps_numerator);
354
355
          break;
        case GST_FORMAT_BYTES:
356
357
          *dest_value = gst_util_uint64_scale_int (src_value,
              3 * dec->info.width * dec->info.height, 2);
358
359
360
361
          break;
        default:
          res = FALSE;
      }
362
363
      break;
    default:
364
      res = FALSE;
365
  }
366
367
done:
  gst_object_unref (dec);
368
  return res;
369
370
371
372
373
374
375
376

  /* ERRORS */
no_header:
  {
    GST_DEBUG_OBJECT (dec, "no header yet, cannot convert");
    res = FALSE;
    goto done;
  }
377
378
}

Benjamin Otte's avatar
Benjamin Otte committed
379
static gboolean
380
381
382
theora_dec_sink_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
Benjamin Otte's avatar
Benjamin Otte committed
383
{
384
385
  gboolean res = TRUE;
  GstTheoraDec *dec;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386

Wim Taymans's avatar
Wim Taymans committed
387
388
389
390
391
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

392
393
394
395
396
397
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

  /* we need the info part before we can done something */
  if (!dec->have_header)
    goto no_header;

398
  switch (src_format) {
Benjamin Otte's avatar
Benjamin Otte committed
399
    case GST_FORMAT_DEFAULT:
400
401
      switch (*dest_format) {
        case GST_FORMAT_TIME:
402
          *dest_value = _theora_granule_time (dec, src_value);
403
404
405
406
          break;
        default:
          res = FALSE;
      }
407
      break;
408
409
410
411
    case GST_FORMAT_TIME:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
        {
412
          guint rest;
413

414
          /* framecount */
415
          *dest_value = gst_util_uint64_scale (src_value,
416
              dec->info.fps_numerator, GST_SECOND * dec->info.fps_denominator);
417
418
419
420

          /* funny way of calculating granulepos in theora */
          rest = *dest_value / dec->info.keyframe_frequency_force;
          *dest_value -= rest;
421
          *dest_value <<= dec->granule_shift;
422
          *dest_value += rest;
423
424
425
426
427
428
429
          break;
        }
        default:
          res = FALSE;
          break;
      }
      break;
Benjamin Otte's avatar
Benjamin Otte committed
430
    default:
431
      res = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
432
  }
433
434
done:
  gst_object_unref (dec);
435
  return res;
436
437
438
439
440
441
442
443

  /* ERRORS */
no_header:
  {
    GST_DEBUG_OBJECT (dec, "no header yet, cannot convert");
    res = FALSE;
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
444
}
445
446

static gboolean
Wim Taymans's avatar
Wim Taymans committed
447
theora_dec_src_query (GstPad * pad, GstQuery * query)
448
{
449
450
  GstTheoraDec *dec;

Wim Taymans's avatar
Wim Taymans committed
451
452
  gboolean res = FALSE;

453
454
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

Wim Taymans's avatar
Wim Taymans committed
455
456
457
  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
    {
Wim Taymans's avatar
Wim Taymans committed
458
      gint64 granulepos, value;
Wim Taymans's avatar
Wim Taymans committed
459
460
461
462
463
464
      GstFormat my_format, format;
      gint64 time;

      /* we can convert a granule position to everything */
      granulepos = dec->granulepos;

465
466
467
      GST_LOG_OBJECT (dec,
          "query %p: we have current granule: %lld", query, granulepos);

Wim Taymans's avatar
Wim Taymans committed
468
469
      /* parse format */
      gst_query_parse_position (query, &format, NULL);
470

Wim Taymans's avatar
Wim Taymans committed
471
472
473
474
475
476
477
478
      /* and convert to the final format in two steps with time as the 
       * intermediate step */
      my_format = GST_FORMAT_TIME;
      if (!(res =
              theora_dec_sink_convert (dec->sinkpad, GST_FORMAT_DEFAULT,
                  granulepos, &my_format, &time)))
        goto error;

479
      time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
480

481
482
483
      GST_LOG_OBJECT (dec,
          "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));

Wim Taymans's avatar
Wim Taymans committed
484
485
486
487
      if (!(res =
              theora_dec_src_convert (pad, my_format, time, &format, &value)))
        goto error;

Wim Taymans's avatar
Wim Taymans committed
488
      gst_query_set_position (query, format, value);
Wim Taymans's avatar
Wim Taymans committed
489
490

      GST_LOG_OBJECT (dec,
Wim Taymans's avatar
Wim Taymans committed
491
          "query %p: we return %lld (format %u)", query, value, format);
Wim Taymans's avatar
Wim Taymans committed
492
493
494

      break;
    }
Wim Taymans's avatar
Wim Taymans committed
495
    case GST_QUERY_DURATION:
496
497
498
499
500
501
    {
      GstPad *peer;

      if (!(peer = gst_pad_get_peer (dec->sinkpad)))
        goto error;

Wim Taymans's avatar
Wim Taymans committed
502
      /* forward to peer for total */
503
504
505
      res = gst_pad_query (peer, query);
      gst_object_unref (peer);
      if (!res)
Wim Taymans's avatar
Wim Taymans committed
506
        goto error;
507

Wim Taymans's avatar
Wim Taymans committed
508
      break;
509
    }
Wim Taymans's avatar
Wim Taymans committed
510
511
512
513
514
515
    case GST_QUERY_CONVERT:
    {
      GstFormat src_fmt, dest_fmt;
      gint64 src_val, dest_val;

      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
516
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
517
518
              theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
519
520
521
        goto error;

      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
Wim Taymans's avatar
Wim Taymans committed
522
523
524
      break;
    }
    default:
525
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
526
      break;
527
  }
528
529
530
done:
  gst_object_unref (dec);

531
  return res;
532

533
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
534
error:
535
536
537
538
  {
    GST_DEBUG_OBJECT (dec, "query failed");
    goto done;
  }
Wim Taymans's avatar
Wim Taymans committed
539
540
541
542
543
544
545
546
547
548
549
550
}

static gboolean
theora_dec_sink_query (GstPad * pad, GstQuery * query)
{
  gboolean res = FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_CONVERT:
    {
      GstFormat src_fmt, dest_fmt;
      gint64 src_val, dest_val;
551

Wim Taymans's avatar
Wim Taymans committed
552
553
554
555
556
557
558
559
560
561
      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
      if (!(res =
              theora_dec_sink_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
        goto error;

      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
      break;
    }
    default:
562
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
563
564
      break;
  }
565

Wim Taymans's avatar
Wim Taymans committed
566
567
error:
  return res;
568
569
}

Benjamin Otte's avatar
Benjamin Otte committed
570
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
571
theora_dec_src_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
572
573
574
575
{
  gboolean res = TRUE;
  GstTheoraDec *dec;

576
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
Benjamin Otte's avatar
Benjamin Otte committed
577
578

  switch (GST_EVENT_TYPE (event)) {
579
580
    case GST_EVENT_SEEK:
    {
581
582
      GstFormat format, tformat;
      gdouble rate;
Scott Wheeler's avatar
Scott Wheeler committed
583
      GstEvent *real_seek;
584
585
586
587
588
589
590
      GstSeekFlags flags;
      GstSeekType cur_type, stop_type;
      gint64 cur, stop;
      gint64 tcur, tstop;

      gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
          &stop_type, &stop);
591
      gst_event_unref (event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
592

593
594
595
596
597
598
      /* we have to ask our peer to seek to time here as we know
       * nothing about how to generate a granulepos from the src
       * formats or anything.
       * 
       * First bring the requested format to time 
       */
599
600
      tformat = GST_FORMAT_TIME;
      if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur)))
601
        goto convert_error;
602
      if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop)))
603
        goto convert_error;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
604

605
      /* then seek with time on the peer */
606
607
      real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
          flags, cur_type, tcur, stop_type, tstop);
608

609
      res = gst_pad_push_event (dec->sinkpad, real_seek);
Benjamin Otte's avatar
Benjamin Otte committed
610
611
      break;
    }
612
613
614
615
616
617
618
619
620
621
    case GST_EVENT_QOS:
    {
      gdouble proportion;
      GstClockTimeDiff diff;
      GstClockTime timestamp;

      gst_event_parse_qos (event, &proportion, &diff, &timestamp);

      /* we cannot randomly skip frame decoding since we don't have
       * B frames. we can however use the timestamp and diff to not
622
623
       * push late frames. This would at least save us the time to
       * crop/memcpy the data. */
624
625
626
627
628
      GST_OBJECT_LOCK (dec);
      dec->proportion = proportion;
      dec->earliest_time = timestamp + diff;
      GST_OBJECT_UNLOCK (dec);

629
630
631
      GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
          GST_TIME_ARGS (timestamp), diff);

632
      res = gst_pad_push_event (dec->sinkpad, event);
633
634
      break;
    }
Benjamin Otte's avatar
Benjamin Otte committed
635
    default:
636
      res = gst_pad_push_event (dec->sinkpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
637
638
      break;
  }
639
640
done:
  gst_object_unref (dec);
Benjamin Otte's avatar
Benjamin Otte committed
641
642

  return res;
643

644
645
646
647
648
649
  /* ERRORS */
convert_error:
  {
    GST_DEBUG_OBJECT (dec, "could not convert format");
    goto done;
  }
650
651
}

652
653
static gboolean
theora_dec_sink_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
654
{
655
  gboolean ret = FALSE;
656
657
  GstTheoraDec *dec;

Wim Taymans's avatar
Wim Taymans committed
658
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
659

Benjamin Otte's avatar
Benjamin Otte committed
660
661
  GST_LOG_OBJECT (dec, "handling event");
  switch (GST_EVENT_TYPE (event)) {
662
663
664
665
666
667
668
    case GST_EVENT_FLUSH_START:
      ret = gst_pad_push_event (dec->srcpad, event);
      break;
    case GST_EVENT_FLUSH_STOP:
      gst_theora_dec_reset (dec);
      ret = gst_pad_push_event (dec->srcpad, event);
      break;
Wim Taymans's avatar
Wim Taymans committed
669
670
671
    case GST_EVENT_EOS:
      ret = gst_pad_push_event (dec->srcpad, event);
      break;
672
    case GST_EVENT_NEWSEGMENT:
673
    {
674
      gboolean update;
675
      GstFormat format;
676
      gdouble rate, arate;
Wim Taymans's avatar
Wim Taymans committed
677
      gint64 start, stop, time;
678

679
680
      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
          &start, &stop, &time);
681
682
683
684
685

      /* we need TIME and a positive rate */
      if (format != GST_FORMAT_TIME)
        goto newseg_wrong_format;

686
      /* now configure the values */
687
688
      gst_segment_set_newsegment_full (&dec->segment, update,
          rate, arate, format, start, stop, time);
689

690
      /* and forward */
Wim Taymans's avatar
Wim Taymans committed
691
      ret = gst_pad_push_event (dec->srcpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
692
      break;
693
    }
Benjamin Otte's avatar
Benjamin Otte committed
694
    default:
Wim Taymans's avatar
Wim Taymans committed
695
      ret = gst_pad_push_event (dec->srcpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
696
697
      break;
  }
698
done:
Wim Taymans's avatar
Wim Taymans committed
699
700
  gst_object_unref (dec);

701
  return ret;
702
703
704
705

  /* ERRORS */
newseg_wrong_format:
  {
706
    GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
707
    gst_event_unref (event);
708
709
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
710
711
}

712
static GstFlowReturn
713
theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet)
Benjamin Otte's avatar
Benjamin Otte committed
714
{
715
  gchar *encoder = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
716
  GstBuffer *buf;
717
718
  GstTagList *list;

719
  GST_DEBUG_OBJECT (dec, "parsing comment packet");
720
721

  buf = gst_buffer_new_and_alloc (packet->bytes);
722
  memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
723

724
725
  list =
      gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\201theora", 7,
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
      &encoder);

  gst_buffer_unref (buf);

  if (!list) {
    GST_ERROR_OBJECT (dec, "couldn't decode comments");
    list = gst_tag_list_new ();
  }
  if (encoder) {
    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
        GST_TAG_ENCODER, encoder, NULL);
    g_free (encoder);
  }
  gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
      GST_TAG_ENCODER_VERSION, dec->info.version_major,
      GST_TAG_VIDEO_CODEC, "Theora", NULL);

743
744
745
746
747
748
  if (dec->info.target_bitrate > 0) {
    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
        GST_TAG_BITRATE, dec->info.target_bitrate,
        GST_TAG_NOMINAL_BITRATE, dec->info.target_bitrate, NULL);
  }

749
  gst_element_found_tags_for_pad (GST_ELEMENT_CAST (dec), dec->srcpad, list);
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786

  return GST_FLOW_OK;
}

static GstFlowReturn
theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstCaps *caps;
  gint par_num, par_den;

  GST_DEBUG_OBJECT (dec, "fps %d/%d, PAR %d/%d",
      dec->info.fps_numerator, dec->info.fps_denominator,
      dec->info.aspect_numerator, dec->info.aspect_denominator);

  /* calculate par
   * the info.aspect_* values reflect PAR;
   * 0:0 is allowed and can be interpreted as 1:1, so correct for it */
  par_num = dec->info.aspect_numerator;
  par_den = dec->info.aspect_denominator;
  if (par_num == 0 && par_den == 0) {
    par_num = par_den = 1;
  }
  /* theora has:
   *
   *  width/height : dimension of the encoded frame 
   *  frame_width/frame_height : dimension of the visible part
   *  offset_x/offset_y : offset in encoded frame where visible part starts
   */
  GST_DEBUG_OBJECT (dec, "dimension %dx%d, PAR %d/%d", dec->info.width,
      dec->info.height, par_num, par_den);
  GST_DEBUG_OBJECT (dec, "frame dimension %dx%d, offset %d:%d",
      dec->info.frame_width, dec->info.frame_height,
      dec->info.offset_x, dec->info.offset_y);

  if (dec->crop) {
    /* add black borders to make width/height/offsets even. we need this because
     * we cannot express an offset to the peer plugin. */
787
788
    dec->width =
        GST_ROUND_UP_2 (dec->info.frame_width + (dec->info.offset_x & 1));
789
    dec->height =
790
        GST_ROUND_UP_2 (dec->info.frame_height + (dec->info.offset_y & 1));
791
792
793
794
795
796
797
798
799
800
    dec->offset_x = dec->info.offset_x & ~1;
    dec->offset_y = dec->info.offset_y & ~1;
  } else {
    /* no cropping, use the encoded dimensions */
    dec->width = dec->info.width;
    dec->height = dec->info.height;
    dec->offset_x = 0;
    dec->offset_y = 0;
  }

801
802
  dec->granule_shift = _theora_ilog (dec->info.keyframe_frequency_force - 1);

803
804
805
806
807
808
809
810
  GST_DEBUG_OBJECT (dec, "after fixup frame dimension %dx%d, offset %d:%d",
      dec->width, dec->height, dec->offset_x, dec->offset_y);

  /* done */
  theora_decode_init (&dec->state, &dec->info);

  caps = gst_caps_new_simple ("video/x-raw-yuv",
      "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
811
812
      "framerate", GST_TYPE_FRACTION,
      dec->info.fps_numerator, dec->info.fps_denominator,
813
814
815
816
817
      "pixel-aspect-ratio", GST_TYPE_FRACTION, par_num, par_den,
      "width", G_TYPE_INT, dec->width, "height", G_TYPE_INT, dec->height, NULL);
  gst_pad_set_caps (dec->srcpad, caps);
  gst_caps_unref (caps);

Wim Taymans's avatar
Wim Taymans committed
818
  dec->have_header = TRUE;
819
820
821
822
823
824
825
826
827

  return GST_FLOW_OK;
}

static GstFlowReturn
theora_handle_header_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstFlowReturn res;

828
  GST_DEBUG_OBJECT (dec, "parsing header packet");
829
830
831
832

  if (theora_decode_header (&dec->info, &dec->comment, packet))
    goto header_read_error;

Wim Taymans's avatar
Wim Taymans committed
833
834
  switch (packet->packet[0]) {
    case 0x81:
835
836
      res = theora_handle_comment_packet (dec, packet);
      break;
Wim Taymans's avatar
Wim Taymans committed
837
    case 0x82:
838
839
840
841
      res = theora_handle_type_packet (dec, packet);
      break;
    default:
      /* ignore */
Wim Taymans's avatar
Wim Taymans committed
842
843
844
      g_warning ("unknown theora header packet found");
    case 0x80:
      /* nothing special, this is the identification header */
845
846
847
848
849
850
851
852
853
854
855
856
857
858
      res = GST_FLOW_OK;
      break;
  }
  return res;

  /* ERRORS */
header_read_error:
  {
    GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
        (NULL), ("couldn't read header packet"));
    return GST_FLOW_ERROR;
  }
}

859
860
861
862
863
/* returns TRUE if buffer is within segment, else FALSE.
 * if Buffer is on segment border, it's timestamp and duration will be clipped */
static gboolean
clip_buffer (GstTheoraDec * dec, GstBuffer * buf)
{
864
865
  gboolean res = TRUE;
  GstClockTime in_ts, in_dur, stop;
866
867
  gint64 cstart, cstop;

868
869
870
  in_ts = GST_BUFFER_TIMESTAMP (buf);
  in_dur = GST_BUFFER_DURATION (buf);

871
872
  GST_LOG_OBJECT (dec,
      "timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT,
873
      GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur));
874

875
876
  /* can't clip without TIME segment */
  if (dec->segment.format != GST_FORMAT_TIME)
877
878
    goto beach;

879
880
881
882
883
884
885
886
887
888
889
  /* we need a start time */
  if (!GST_CLOCK_TIME_IS_VALID (in_ts))
    goto beach;

  /* generate valid stop, if duration unknown, we have unknown stop */
  stop =
      GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE;

  /* now clip */
  if (!(res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME,
              in_ts, stop, &cstart, &cstop)))
890
891
    goto beach;

892
893
  /* update timestamp and possibly duration if the clipped stop time is
   * valid */
894
  GST_BUFFER_TIMESTAMP (buf) = cstart;
895
896
  if (GST_CLOCK_TIME_IS_VALID (cstop))
    GST_BUFFER_DURATION (buf) = cstop - cstart;
897
898

beach:
899
  GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
900
901
902
  return res;
}

903
/* FIXME, this needs to be moved to the demuxer */
904
905
906
static GstFlowReturn
theora_dec_push (GstTheoraDec * dec, GstBuffer * buf)
{
907
  GstFlowReturn result = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
908
  GstClockTime outtime = GST_BUFFER_TIMESTAMP (buf);
909

Wim Taymans's avatar
Wim Taymans committed
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
  if (outtime == GST_CLOCK_TIME_NONE) {
    dec->queued = g_list_append (dec->queued, buf);
    GST_DEBUG_OBJECT (dec, "queued buffer");
  } else {
    if (dec->queued) {
      gint64 size;
      GList *walk;

      GST_DEBUG_OBJECT (dec, "first buffer with time %" GST_TIME_FORMAT,
          GST_TIME_ARGS (outtime));

      size = g_list_length (dec->queued);
      for (walk = dec->queued; walk; walk = g_list_next (walk)) {
        GstBuffer *buffer = GST_BUFFER (walk->data);
        GstClockTime time;

926
927
        time = outtime - gst_util_uint64_scale_int (size * GST_SECOND,
            dec->info.fps_denominator, dec->info.fps_numerator);
Wim Taymans's avatar
Wim Taymans committed
928
929
930

        GST_DEBUG_OBJECT (dec, "patch buffer %lld %lld", size, time);
        GST_BUFFER_TIMESTAMP (buffer) = time;
931
932
933
934
935

        if (dec->discont) {
          GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
          dec->discont = FALSE;
        }
936
        /* ignore the result.. */
937
938
939
940
        if (clip_buffer (dec, buffer))
          gst_pad_push (dec->srcpad, buffer);
        else
          gst_buffer_unref (buffer);
Wim Taymans's avatar
Wim Taymans committed
941
942
943
944
945
        size--;
      }
      g_list_free (dec->queued);
      dec->queued = NULL;
    }
946
947
948
949
    if (dec->discont) {
      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
      dec->discont = FALSE;
    }
950
951
952
953
    if (clip_buffer (dec, buf))
      result = gst_pad_push (dec->srcpad, buf);
    else
      gst_buffer_unref (buf);
Wim Taymans's avatar
Wim Taymans committed
954
  }
955
956
957
958

  return result;
}

959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
static GstFlowReturn
theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
    GstClockTime outtime)
{
  /* normal data packet */
  yuv_buffer yuv;
  GstBuffer *out;
  guint i;
  gboolean keyframe;
  gint out_size;
  gint stride_y, stride_uv;
  gint width, height;
  gint cwidth, cheight;
  GstFlowReturn result;

974
  if (G_UNLIKELY (!dec->have_header))
975
976
977
978
979
    goto not_initialized;

  /* the second most significant bit of the first data byte is cleared 
   * for keyframes */
  keyframe = (packet->packet[0] & 0x40) == 0;
980
  if (G_UNLIKELY (keyframe)) {
981
    GST_DEBUG_OBJECT (dec, "we have a keyframe");
982
    dec->need_keyframe = FALSE;
983
  } else if (G_UNLIKELY (dec->need_keyframe)) {
984
985
986
    goto dropping;
  }

987
988
  GST_DEBUG_OBJECT (dec, "parsing data packet");

989
  /* this does the decoding */
990
  if (G_UNLIKELY (theora_decode_packetin (&dec->state, packet)))
991
992
993
994
    goto decode_error;

  if (outtime != -1) {
    gboolean need_skip;
995
996
997
998
999
    GstClockTime qostime;

    /* qos needs to be done on running time */
    qostime = gst_segment_to_running_time (&dec->segment, GST_FORMAT_TIME,
        outtime);
1000
1001
1002
1003
1004
1005

    GST_OBJECT_LOCK (dec);
    /* check for QoS, don't perform the last steps of getting and
     * pushing the buffers that are known to be late. */
    /* FIXME, we can also entirely skip decoding if the next valid buffer is 
     * known to be after a keyframe (using the granule_shift) */
1006
    need_skip = dec->earliest_time != -1 && qostime <= dec->earliest_time;
1007
    GST_OBJECT_UNLOCK (dec);
1008

1009
1010
1011
1012
1013
1014
    if (need_skip)
      goto dropping_qos;
  }

  /* this does postprocessing and set up the decoded frame
   * pointers in our yuv variable */
1015
  if (G_UNLIKELY (theora_decode_YUVout (&dec->state, &yuv) < 0))
1016
    goto no_yuv;
1017

1018
1019
  if (G_UNLIKELY ((yuv.y_width != dec->info.width)
          || (yuv.y_height != dec->info.height)))
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
    goto wrong_dimensions;

  width = dec->width;
  height = dec->height;
  cwidth = width / 2;
  cheight = height / 2;

  /* should get the stride from the caps, for now we round up to the nearest
   * multiple of 4 because some element needs it. chroma needs special 
   * treatment, see videotestsrc. */
1030
1031
  stride_y = GST_ROUND_UP_4 (width);
  stride_uv = GST_ROUND_UP_8 (width) / 2;
1032
1033
1034
1035
1036

  out_size = stride_y * height + stride_uv * cheight * 2;

  /* now copy over the area contained in offset_x,offset_y,
   * frame_width, frame_height */
Andy Wingo's avatar
Andy Wingo committed
1037
1038
1039
  result =
      gst_pad_alloc_buffer_and_set_caps (dec->srcpad, GST_BUFFER_OFFSET_NONE,
      out_size, GST_PAD_CAPS (dec->srcpad), &out);
1040
  if (G_UNLIKELY (result != GST_FLOW_OK))
1041
1042
1043
1044
1045
    goto no_buffer;

  /* copy the visible region to the destination. This is actually pretty
   * complicated and gstreamer doesn't support all the needed caps to do this
   * correctly. For example, when we have an odd offset, we should only combine
1046
   * 1 row/column of luma samples with one chroma sample in colorspace conversion. 
Wim Taymans's avatar
Wim Taymans committed
1047
   * We compensate for this by adding a black border around the image when the
1048
   * offset or size is odd (see above).
1049
1050
   */
  {
Andy Wingo's avatar
Andy Wingo committed
1051
1052
1053
    guchar *dest_y, *src_y;
    guchar *dest_u, *src_u;
    guchar *dest_v, *src_v;
1054
    gint offset;
1055

Andy Wingo's avatar
Andy Wingo committed
1056
    dest_y = GST_BUFFER_DATA (out);
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
    dest_u = dest_y + stride_y * height;
    dest_v = dest_u + stride_uv * cheight;

    src_y = yuv.y + dec->offset_x + dec->offset_y * yuv.y_stride;

    for (i = 0; i < height; i++) {
      memcpy (dest_y, src_y, width);

      dest_y += stride_y;
      src_y += yuv.y_stride;
    }

1069
1070
1071
1072
    offset = dec->offset_x / 2 + dec->offset_y / 2 * yuv.uv_stride;

    src_u = yuv.u + offset;
    src_v = yuv.v + offset;
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084

    for (i = 0; i < cheight; i++) {
      memcpy (dest_u, src_u, cwidth);
      memcpy (dest_v, src_v, cwidth);

      dest_u += stride_uv;
      src_u += yuv.uv_stride;
      dest_v += stride_uv;
      src_v += yuv.uv_stride;
    }
  }

Wim Taymans's avatar
Wim Taymans committed
1085
  GST_BUFFER_OFFSET (out) = dec->frame_nr;
1086
1087
  if (dec->frame_nr != -1)
    dec->frame_nr++;
Wim Taymans's avatar
Wim Taymans committed
1088
  GST_BUFFER_OFFSET_END (out) = dec->frame_nr;
1089
  GST_BUFFER_DURATION (out) =
1090
1091
      gst_util_uint64_scale_int (GST_SECOND, dec->info.fps_denominator,
      dec->info.fps_numerator);
1092
1093
  GST_BUFFER_TIMESTAMP (out) = outtime;

1094
  result = theora_dec_push (dec, out);
1095
1096
1097
1098
1099
1100
1101

  return result;

  /* ERRORS */
not_initialized:
  {
    GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
Wim Taymans's avatar
Wim Taymans committed
1102
        (NULL), ("no header sent yet"));
1103
1104
1105
1106
1107
    return GST_FLOW_ERROR;
  }
dropping:
  {
    GST_WARNING_OBJECT (dec, "dropping frame because we need a keyframe");
1108
    dec->discont = TRUE;
1109
1110
    return GST_FLOW_OK;
  }
1111
1112
dropping_qos:
  {
1113
1114
1115
    if (dec->frame_nr != -1)
      dec->frame_nr++;
    dec->discont = TRUE;
1116
1117
1118
1119
    GST_WARNING_OBJECT (dec, "dropping frame because of QoS");
    return GST_FLOW_OK;
  }
decode_error:
1120
1121
  {
    GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
1122
        (NULL), ("theora decoder did not decode data packet"));
1123
1124
    return GST_FLOW_ERROR;
  }
1125
no_yuv:
1126
1127
1128
1129
1130
1131
1132
  {
    GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
        (NULL), ("couldn't read out YUV image"));
    return GST_FLOW_ERROR;
  }
wrong_dimensions:
  {
1133
    GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, FORMAT,
1134
1135
1136
1137
1138
        (NULL), ("dimensions of image do not match header"));
    return GST_FLOW_ERROR;
  }
no_buffer:
  {
1139
1140
    GST_DEBUG_OBJECT (dec, "could not get buffer, reason: %s",
        gst_flow_get_name (result));
1141
    return result;
1142
1143
1144
1145
  }
}

static GstFlowReturn
1146
theora_dec_chain_forward (GstTheoraDec * dec, gboolean discont, GstBuffer * buf)
1147
{
Benjamin Otte's avatar
Benjamin Otte committed
1148
  ogg_packet packet;
1149
  GstFlowReturn result = GST_FLOW_OK;
Benjamin Otte's avatar
Benjamin Otte committed
1150

1151
  /* resync on DISCONT */