gsttheoradec.c 39.7 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
/**
 * SECTION:element-theoradec
 * @see_also: theoraenc, oggdemux
 *
 * 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.
28 29
 *
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
30
 * <title>Example pipeline</title>
31
 * |[
Wim Taymans's avatar
Wim Taymans committed
32
 * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! xvimagesink
33
 * ]| This example pipeline will decode an ogg stream and decodes the theora video. Refer to
Wim Taymans's avatar
Wim Taymans committed
34 35 36 37 38 39
 * 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
40 41 42 43
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

Wim Taymans's avatar
Wim Taymans committed
44
#include "gsttheoradec.h"
Benjamin Otte's avatar
Benjamin Otte committed
45
#include <gst/tag/tag.h>
46
#include <gst/video/video.h>
Benjamin Otte's avatar
Benjamin Otte committed
47

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48
#define GST_CAT_DEFAULT theoradec_debug
49
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
Benjamin Otte's avatar
Benjamin Otte committed
50

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
51
#define THEORA_DEF_CROP         TRUE
52 53 54 55 56 57
enum
{
  ARG_0,
  ARG_CROP
};

Benjamin Otte's avatar
Benjamin Otte committed
58
static GstStaticPadTemplate theora_dec_src_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
59 60 61 62
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-yuv, "
63
        "format = (fourcc) { I420, Y42B, Y444 }, "
64
        "framerate = (fraction) [0/1, MAX], "
65
        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
66
    );
Benjamin Otte's avatar
Benjamin Otte committed
67 68

static GstStaticPadTemplate theora_dec_sink_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
69 70 71 72 73
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-theora")
    );
Benjamin Otte's avatar
Benjamin Otte committed
74 75

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

77 78 79 80 81
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);

82
static gboolean theora_dec_sink_event (GstPad * pad, GstEvent * event);
83
static gboolean theora_dec_setcaps (GstPad * pad, GstCaps * caps);
84
static GstFlowReturn theora_dec_chain (GstPad * pad, GstBuffer * buffer);
85 86
static GstStateChangeReturn theora_dec_change_state (GstElement * element,
    GstStateChange transition);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
87
static gboolean theora_dec_src_event (GstPad * pad, GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
88
static gboolean theora_dec_src_query (GstPad * pad, GstQuery * query);
89 90 91
static gboolean theora_dec_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value);
Wim Taymans's avatar
Wim Taymans committed
92 93

#if 0
94
static const GstFormat *theora_get_formats (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
95 96
#endif
#if 0
97
static const GstEventMask *theora_get_event_masks (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
98
#endif
99
static const GstQueryType *theora_get_query_types (GstPad * pad);
Benjamin Otte's avatar
Benjamin Otte committed
100 101 102 103 104 105


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
106 107

  gst_element_class_add_pad_template (element_class,
Benjamin Otte's avatar
Benjamin Otte committed
108 109 110
      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));
111 112 113 114
  gst_element_class_set_details_simple (element_class,
      "Theora video decoder", "Codec/Decoder/Video",
      "decode raw theora streams to raw YUV video",
      "Benjamin Otte <otte@gnome.org>, Wim Taymans <wim@fluendo.com>");
Benjamin Otte's avatar
Benjamin Otte committed
115 116 117
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
118
gst_theora_dec_class_init (GstTheoraDecClass * klass)
Benjamin Otte's avatar
Benjamin Otte committed
119
{
120
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Benjamin Otte's avatar
Benjamin Otte committed
121 122
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);

123 124 125 126 127 128
  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,
129
          (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
130

Benjamin Otte's avatar
Benjamin Otte committed
131
  gstelement_class->change_state = theora_dec_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
132 133

  GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder");
Benjamin Otte's avatar
Benjamin Otte committed
134 135 136
}

static void
137
gst_theora_dec_init (GstTheoraDec * dec, GstTheoraDecClass * g_class)
Benjamin Otte's avatar
Benjamin Otte committed
138
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
139
  dec->sinkpad =
140
      gst_pad_new_from_static_template (&theora_dec_sink_factory, "sink");
141
  gst_pad_set_event_function (dec->sinkpad, theora_dec_sink_event);
142
  gst_pad_set_setcaps_function (dec->sinkpad, theora_dec_setcaps);
Benjamin Otte's avatar
Benjamin Otte committed
143 144 145
  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
146
  dec->srcpad =
147
      gst_pad_new_from_static_template (&theora_dec_src_factory, "src");
Benjamin Otte's avatar
Benjamin Otte committed
148
  gst_pad_set_event_function (dec->srcpad, theora_dec_src_event);
149
  gst_pad_set_query_type_function (dec->srcpad, theora_get_query_types);
150
  gst_pad_set_query_function (dec->srcpad, theora_dec_src_query);
151
  gst_pad_use_fixed_caps (dec->srcpad);
152

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

155
  dec->crop = THEORA_DEF_CROP;
156 157
  dec->gather = NULL;
  dec->decode = NULL;
158
  dec->queued = NULL;
Wim Taymans's avatar
Wim Taymans committed
159
  dec->pendingevents = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
160
}
161

162 163 164 165 166
static void
gst_theora_dec_reset (GstTheoraDec * dec)
{
  dec->need_keyframe = TRUE;
  dec->last_timestamp = -1;
167 168
  dec->discont = TRUE;
  dec->frame_nr = -1;
Wim Taymans's avatar
Wim Taymans committed
169
  dec->seqnum = gst_util_seqnum_next ();
170 171 172 173 174 175
  gst_segment_init (&dec->segment, GST_FORMAT_TIME);

  GST_OBJECT_LOCK (dec);
  dec->proportion = 1.0;
  dec->earliest_time = -1;
  GST_OBJECT_UNLOCK (dec);
176

177
  g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL);
178 179
  g_list_free (dec->queued);
  dec->queued = NULL;
180 181 182 183 184 185
  g_list_foreach (dec->gather, (GFunc) gst_mini_object_unref, NULL);
  g_list_free (dec->gather);
  dec->gather = NULL;
  g_list_foreach (dec->decode, (GFunc) gst_mini_object_unref, NULL);
  g_list_free (dec->decode);
  dec->decode = NULL;
Wim Taymans's avatar
Wim Taymans committed
186 187 188
  g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL);
  g_list_free (dec->pendingevents);
  dec->pendingevents = NULL;
189 190 191 192 193

  if (dec->tags) {
    gst_tag_list_free (dec->tags);
    dec->tags = NULL;
  }
194 195
}

Wim Taymans's avatar
Wim Taymans committed
196
#if 0
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
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
214
#endif
215

Wim Taymans's avatar
Wim Taymans committed
216
#if 0
217 218 219 220 221 222 223 224 225 226
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
227
#endif
228 229 230 231 232 233

static const GstQueryType *
theora_get_query_types (GstPad * pad)
{
  static const GstQueryType theora_src_query_types[] = {
    GST_QUERY_POSITION,
234 235
    GST_QUERY_DURATION,
    GST_QUERY_CONVERT,
236 237 238 239 240 241 242
    0
  };

  return theora_src_query_types;
}


243
static gboolean
244 245 246
theora_dec_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
247
{
248 249 250 251
  gboolean res = TRUE;
  GstTheoraDec *dec;
  guint64 scale = 1;

Wim Taymans's avatar
Wim Taymans committed
252 253 254 255 256
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

257 258 259 260 261 262
  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;

263 264 265 266
  switch (src_format) {
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
267
          *dest_value = gst_util_uint64_scale_int (src_value, 8,
268
              dec->info.pic_height * dec->info.pic_width * dec->output_bpp);
269 270 271 272 273 274 275
          break;
        case GST_FORMAT_TIME:
          /* seems like a rather silly conversion, implement me if you like */
        default:
          res = FALSE;
      }
      break;
276
    case GST_FORMAT_TIME:
277 278
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
279 280 281
          scale =
              dec->output_bpp * (dec->info.pic_width * dec->info.pic_height) /
              8;
282
        case GST_FORMAT_DEFAULT:
283
          *dest_value = scale * gst_util_uint64_scale (src_value,
284
              dec->info.fps_numerator, dec->info.fps_denominator * GST_SECOND);
285 286 287 288
          break;
        default:
          res = FALSE;
      }
289 290
      break;
    case GST_FORMAT_DEFAULT:
291 292
      switch (*dest_format) {
        case GST_FORMAT_TIME:
293
          *dest_value = gst_util_uint64_scale (src_value,
294
              GST_SECOND * dec->info.fps_denominator, dec->info.fps_numerator);
295 296
          break;
        case GST_FORMAT_BYTES:
297
          *dest_value = gst_util_uint64_scale_int (src_value,
298
              dec->output_bpp * dec->info.pic_width * dec->info.pic_height, 8);
299 300 301 302
          break;
        default:
          res = FALSE;
      }
303 304
      break;
    default:
305
      res = FALSE;
306
  }
307 308
done:
  gst_object_unref (dec);
309
  return res;
310 311 312 313 314 315 316 317

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

Wim Taymans's avatar
Wim Taymans committed
320
#if 0
Benjamin Otte's avatar
Benjamin Otte committed
321
static gboolean
322 323 324
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
325
{
326 327
  gboolean res = TRUE;
  GstTheoraDec *dec;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
328

Wim Taymans's avatar
Wim Taymans committed
329 330 331 332 333
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

334 335 336 337 338 339
  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;

340
  switch (src_format) {
Benjamin Otte's avatar
Benjamin Otte committed
341
    case GST_FORMAT_DEFAULT:
342 343
      switch (*dest_format) {
        case GST_FORMAT_TIME:
344
          *dest_value = _theora_granule_start_time (dec, src_value);
345 346 347 348
          break;
        default:
          res = FALSE;
      }
349
      break;
350 351 352 353
    case GST_FORMAT_TIME:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
        {
354
          guint rest;
355

356
          /* framecount */
357
          *dest_value = gst_util_uint64_scale (src_value,
358
              dec->info.fps_numerator, GST_SECOND * dec->info.fps_denominator);
359 360

          /* funny way of calculating granulepos in theora */
361
          rest = *dest_value / dec->info.keyframe_granule_shift;
362
          *dest_value -= rest;
363
          *dest_value <<= dec->granule_shift;
364
          *dest_value += rest;
365 366 367 368 369 370 371
          break;
        }
        default:
          res = FALSE;
          break;
      }
      break;
Benjamin Otte's avatar
Benjamin Otte committed
372
    default:
373
      res = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
374
  }
375 376
done:
  gst_object_unref (dec);
377
  return res;
378 379 380 381 382 383 384 385

  /* ERRORS */
no_header:
  {
    GST_DEBUG_OBJECT (dec, "no header yet, cannot convert");
    res = FALSE;
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
386
}
Wim Taymans's avatar
Wim Taymans committed
387
#endif
388 389

static gboolean
Wim Taymans's avatar
Wim Taymans committed
390
theora_dec_src_query (GstPad * pad, GstQuery * query)
391
{
392 393
  GstTheoraDec *dec;

Wim Taymans's avatar
Wim Taymans committed
394 395
  gboolean res = FALSE;

396 397
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

Wim Taymans's avatar
Wim Taymans committed
398 399 400
  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
    {
Wim Taymans's avatar
Wim Taymans committed
401
      gint64 value;
Wim Taymans's avatar
Wim Taymans committed
402
      GstFormat format;
Wim Taymans's avatar
Wim Taymans committed
403 404
      gint64 time;

Wim Taymans's avatar
Wim Taymans committed
405 406
      /* parse format */
      gst_query_parse_position (query, &format, NULL);
407

Wim Taymans's avatar
Wim Taymans committed
408
      time = dec->last_timestamp;
409
      time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
410

411 412 413
      GST_LOG_OBJECT (dec,
          "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));

Wim Taymans's avatar
Wim Taymans committed
414
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
415 416
              theora_dec_src_convert (pad, GST_FORMAT_TIME, time, &format,
                  &value)))
Wim Taymans's avatar
Wim Taymans committed
417 418
        goto error;

Wim Taymans's avatar
Wim Taymans committed
419
      gst_query_set_position (query, format, value);
Wim Taymans's avatar
Wim Taymans committed
420 421

      GST_LOG_OBJECT (dec,
422 423
          "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value,
          format);
Wim Taymans's avatar
Wim Taymans committed
424 425
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
426
    case GST_QUERY_DURATION:
427
    {
Wim Taymans's avatar
Wim Taymans committed
428
      /* forward to peer for total */
Wim Taymans's avatar
Wim Taymans committed
429
      res = gst_pad_peer_query (dec->sinkpad, query);
430
      if (!res)
Wim Taymans's avatar
Wim Taymans committed
431
        goto error;
432

Wim Taymans's avatar
Wim Taymans committed
433
      break;
434
    }
Wim Taymans's avatar
Wim Taymans committed
435 436 437 438 439 440
    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);
441
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
442 443
              theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
444 445 446
        goto error;

      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
Wim Taymans's avatar
Wim Taymans committed
447 448 449
      break;
    }
    default:
450
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
451
      break;
452
  }
453 454 455
done:
  gst_object_unref (dec);

456
  return res;
457

458
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
459
error:
460 461 462 463
  {
    GST_DEBUG_OBJECT (dec, "query failed");
    goto done;
  }
Wim Taymans's avatar
Wim Taymans committed
464 465
}

Benjamin Otte's avatar
Benjamin Otte committed
466
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
467
theora_dec_src_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
468 469 470 471
{
  gboolean res = TRUE;
  GstTheoraDec *dec;

472
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
Benjamin Otte's avatar
Benjamin Otte committed
473 474

  switch (GST_EVENT_TYPE (event)) {
475 476
    case GST_EVENT_SEEK:
    {
477 478
      GstFormat format, tformat;
      gdouble rate;
Scott Wheeler Wheeler's avatar
Scott Wheeler Wheeler committed
479
      GstEvent *real_seek;
480 481 482 483
      GstSeekFlags flags;
      GstSeekType cur_type, stop_type;
      gint64 cur, stop;
      gint64 tcur, tstop;
Wim Taymans's avatar
Wim Taymans committed
484
      guint32 seqnum;
485 486 487

      gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
          &stop_type, &stop);
Wim Taymans's avatar
Wim Taymans committed
488
      seqnum = gst_event_get_seqnum (event);
489
      gst_event_unref (event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
490

491 492 493 494 495 496
      /* 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 
       */
497 498
      tformat = GST_FORMAT_TIME;
      if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur)))
499
        goto convert_error;
500
      if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop)))
501
        goto convert_error;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
502

503
      /* then seek with time on the peer */
504 505
      real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
          flags, cur_type, tcur, stop_type, tstop);
Wim Taymans's avatar
Wim Taymans committed
506
      gst_event_set_seqnum (real_seek, seqnum);
507

508
      res = gst_pad_push_event (dec->sinkpad, real_seek);
Benjamin Otte's avatar
Benjamin Otte committed
509 510
      break;
    }
511 512 513 514 515 516 517 518 519 520
    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
521 522
       * push late frames. This would at least save us the time to
       * crop/memcpy the data. */
523 524 525 526 527
      GST_OBJECT_LOCK (dec);
      dec->proportion = proportion;
      dec->earliest_time = timestamp + diff;
      GST_OBJECT_UNLOCK (dec);

528 529 530
      GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
          GST_TIME_ARGS (timestamp), diff);

531
      res = gst_pad_push_event (dec->sinkpad, event);
532 533
      break;
    }
Benjamin Otte's avatar
Benjamin Otte committed
534
    default:
535
      res = gst_pad_push_event (dec->sinkpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
536 537
      break;
  }
538 539
done:
  gst_object_unref (dec);
Benjamin Otte's avatar
Benjamin Otte committed
540 541

  return res;
542

543 544 545 546 547 548
  /* ERRORS */
convert_error:
  {
    GST_DEBUG_OBJECT (dec, "could not convert format");
    goto done;
  }
549 550
}

551 552
static gboolean
theora_dec_sink_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
553
{
554
  gboolean ret = FALSE;
555 556
  GstTheoraDec *dec;

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

Benjamin Otte's avatar
Benjamin Otte committed
559 560
  GST_LOG_OBJECT (dec, "handling event");
  switch (GST_EVENT_TYPE (event)) {
561 562 563 564 565 566 567
    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
568 569 570
    case GST_EVENT_EOS:
      ret = gst_pad_push_event (dec->srcpad, event);
      break;
571
    case GST_EVENT_NEWSEGMENT:
572
    {
573
      gboolean update;
574
      GstFormat format;
575
      gdouble rate, arate;
Wim Taymans's avatar
Wim Taymans committed
576
      gint64 start, stop, time;
577

578 579
      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
          &start, &stop, &time);
580

581
      /* we need TIME format */
582 583 584
      if (format != GST_FORMAT_TIME)
        goto newseg_wrong_format;

585 586 587 588 589 590
      GST_DEBUG_OBJECT (dec,
          "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
          ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
          update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
          GST_TIME_ARGS (time));

591
      /* now configure the values */
592 593
      gst_segment_set_newsegment_full (&dec->segment, update,
          rate, arate, format, start, stop, time);
Wim Taymans's avatar
Wim Taymans committed
594
      dec->seqnum = gst_event_get_seqnum (event);
595

Wim Taymans's avatar
Wim Taymans committed
596
      /* We don't forward this unless/until the decoder is initialised */
597 598 599
      if (dec->have_header) {
        ret = gst_pad_push_event (dec->srcpad, event);
      } else {
Wim Taymans's avatar
Wim Taymans committed
600
        dec->pendingevents = g_list_append (dec->pendingevents, event);
601 602
        ret = TRUE;
      }
Benjamin Otte's avatar
Benjamin Otte committed
603
      break;
604
    }
605 606 607 608 609 610 611 612 613 614 615 616
    case GST_EVENT_TAG:
    {
      if (dec->have_header)
        /* and forward */
        ret = gst_pad_push_event (dec->srcpad, event);
      else {
        /* store it to send once we're initialized */
        dec->pendingevents = g_list_append (dec->pendingevents, event);
        ret = TRUE;
      }
      break;
    }
Benjamin Otte's avatar
Benjamin Otte committed
617
    default:
Wim Taymans's avatar
Wim Taymans committed
618
      ret = gst_pad_push_event (dec->srcpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
619 620
      break;
  }
621
done:
Wim Taymans's avatar
Wim Taymans committed
622 623
  gst_object_unref (dec);

624
  return ret;
625 626 627 628

  /* ERRORS */
newseg_wrong_format:
  {
629
    GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
630
    gst_event_unref (event);
631 632
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
633 634
}

635 636 637 638 639
static gboolean
theora_dec_setcaps (GstPad * pad, GstCaps * caps)
{
  GstTheoraDec *dec;
  GstStructure *s;
640
  const GValue *codec_data;
641 642 643 644 645 646 647 648 649

  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

  s = gst_caps_get_structure (caps, 0);

  /* parse the par, this overrides the encoded par */
  dec->have_par = gst_structure_get_fraction (s, "pixel-aspect-ratio",
      &dec->par_num, &dec->par_den);

650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
  if ((codec_data = gst_structure_get_value (s, "codec_data"))) {
    if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
      GstBuffer *buffer;
      guint8 *data;
      guint size;
      guint offset;

      buffer = gst_value_get_buffer (codec_data);

      offset = 0;
      size = GST_BUFFER_SIZE (buffer);
      data = GST_BUFFER_DATA (buffer);

      while (size > 2) {
        guint psize;
        GstBuffer *buf;

        psize = (data[0] << 8) | data[1];
        /* skip header */
        data += 2;
        size -= 2;
        offset += 2;

        /* make sure we don't read too much */
        psize = MIN (psize, size);

        buf = gst_buffer_create_sub (buffer, offset, psize);

        /* first buffer is a discont buffer */
        if (offset == 2)
          GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);

        /* now feed it to the decoder we can ignore the error */
        theora_dec_chain (pad, buf);

        /* skip the data */
        size -= psize;
        data += psize;
        offset += psize;
      }
    }
  }

693 694 695 696 697
  gst_object_unref (dec);

  return TRUE;
}

698
static GstFlowReturn
699
theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet)
Benjamin Otte's avatar
Benjamin Otte committed
700
{
701
  gchar *encoder = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
702
  GstBuffer *buf;
703 704
  GstTagList *list;

705
  GST_DEBUG_OBJECT (dec, "parsing comment packet");
706

707 708 709
  buf = gst_buffer_new ();
  GST_BUFFER_SIZE (buf) = packet->bytes;
  GST_BUFFER_DATA (buf) = packet->packet;
710

711 712
  list =
      gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\201theora", 7,
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
      &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);

730 731 732 733 734 735
  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);
  }

736
  dec->tags = list;
737 738 739 740 741 742 743 744 745

  return GST_FLOW_OK;
}

static GstFlowReturn
theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstCaps *caps;
  gint par_num, par_den;
746
  GstFlowReturn ret = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
747
  GList *walk;
748
  guint32 fourcc;
749 750 751 752 753 754 755

  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;
756
   * 0:x and x:0 are allowed and can be interpreted as 1:1.
757
   */
758 759 760 761 762 763 764 765 766 767
  if (dec->have_par) {
    /* we had a par on the sink caps, override the encoded par */
    GST_DEBUG_OBJECT (dec, "overriding with input PAR");
    par_num = dec->par_num;
    par_den = dec->par_den;
  } else {
    /* take encoded par */
    par_num = dec->info.aspect_numerator;
    par_den = dec->info.aspect_denominator;
  }
768
  if (par_num == 0 || par_den == 0) {
769 770 771 772 773
    par_num = par_den = 1;
  }
  /* theora has:
   *
   *  width/height : dimension of the encoded frame 
774 775
   *  pic_width/pic_height : dimension of the visible part
   *  pic_x/pic_y : offset in encoded frame where visible part starts
776
   */
777 778
  GST_DEBUG_OBJECT (dec, "dimension %dx%d, PAR %d/%d", dec->info.pic_width,
      dec->info.pic_height, par_num, par_den);
779
  GST_DEBUG_OBJECT (dec, "frame dimension %dx%d, offset %d:%d",
780 781
      dec->info.pic_width, dec->info.pic_height,
      dec->info.pic_x, dec->info.pic_y);
782

783
  if (dec->info.pixel_fmt == TH_PF_420) {
784 785
    dec->output_bpp = 12;       /* Average bits per pixel. */
    fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0');
786
  } else if (dec->info.pixel_fmt == TH_PF_422) {
787
    dec->output_bpp = 16;
788
    fourcc = GST_MAKE_FOURCC ('Y', '4', '2', 'B');
789
  } else if (dec->info.pixel_fmt == TH_PF_444) {
790 791 792
    dec->output_bpp = 24;
    fourcc = GST_MAKE_FOURCC ('Y', '4', '4', '4');
  } else {
793
    GST_ERROR_OBJECT (dec, "Invalid pixel format %d", dec->info.pixel_fmt);
794 795
    return GST_FLOW_ERROR;
  }
796 797

  if (dec->crop) {
798 799
    dec->width = dec->info.pic_width;
    dec->height = dec->info.pic_height;
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
    dec->offset_x = dec->info.pic_x;
    dec->offset_y = dec->info.pic_y;
    /* Ensure correct offsets in chroma for formats that need it
     * by rounding the offset. libtheora will add proper pixels,
     * so no need to handle them ourselves. */
    if (dec->offset_x & 1 && dec->info.pixel_fmt != TH_PF_444) {
      dec->offset_x--;
      dec->width++;
    }
    if (dec->offset_y & 1 && dec->info.pixel_fmt == TH_PF_420) {
      dec->offset_y--;
      dec->height++;
    }
  } else {
    /* no cropping, use the encoded dimensions */
    dec->width = dec->info.frame_width;
    dec->height = dec->info.frame_height;
817 818 819 820 821 822 823 824
    dec->offset_x = 0;
    dec->offset_y = 0;
  }

  GST_DEBUG_OBJECT (dec, "after fixup frame dimension %dx%d, offset %d:%d",
      dec->width, dec->height, dec->offset_x, dec->offset_y);

  /* done */
825
  dec->decoder = th_decode_alloc (&dec->info, dec->setup);
826 827

  caps = gst_caps_new_simple ("video/x-raw-yuv",
828
      "format", GST_TYPE_FOURCC, fourcc,
829 830
      "framerate", GST_TYPE_FRACTION,
      dec->info.fps_numerator, dec->info.fps_denominator,
831
      "pixel-aspect-ratio", GST_TYPE_FRACTION, par_num, par_den,
832
      "width", G_TYPE_INT, dec->width, "height", G_TYPE_INT, dec->height,
833 834
      "color-matrix", G_TYPE_STRING, "sdtv",
      "chroma-site", G_TYPE_STRING, "jpeg", NULL);
835 836 837
  gst_pad_set_caps (dec->srcpad, caps);
  gst_caps_unref (caps);

Wim Taymans's avatar
Wim Taymans committed
838
  dec->have_header = TRUE;
Wim Taymans's avatar
Wim Taymans committed
839 840 841 842 843 844

  if (dec->pendingevents) {
    for (walk = dec->pendingevents; walk; walk = g_list_next (walk))
      gst_pad_push_event (dec->srcpad, GST_EVENT_CAST (walk->data));
    g_list_free (dec->pendingevents);
    dec->pendingevents = NULL;
845
  }
846

847 848 849 850 851 852 853
  if (dec->tags) {
    gst_element_found_tags_for_pad (GST_ELEMENT_CAST (dec), dec->srcpad,
        dec->tags);
    dec->tags = NULL;
  }

  return ret;
854 855 856 857 858 859
}

static GstFlowReturn
theora_handle_header_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstFlowReturn res;
860
  int ret;
861

862
  GST_DEBUG_OBJECT (dec, "parsing header packet");
863

864 865
  ret = th_decode_headerin (&dec->info, &dec->comment, &dec->setup, packet);
  if (ret < 0)
866 867
    goto header_read_error;

Wim Taymans's avatar
Wim Taymans committed
868 869
  switch (packet->packet[0]) {
    case 0x81:
870 871
      res = theora_handle_comment_packet (dec, packet);
      break;
Wim Taymans's avatar
Wim Taymans committed
872
    case 0x82:
873 874 875 876
      res = theora_handle_type_packet (dec, packet);
      break;
    default:
      /* ignore */
Wim Taymans's avatar
Wim Taymans committed
877 878 879
      g_warning ("unknown theora header packet found");
    case 0x80:
      /* nothing special, this is the identification header */
880 881 882 883 884 885 886 887 888 889 890 891 892 893
      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;
  }
}

894 895 896 897 898
/* 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)
{
899 900
  gboolean res = TRUE;
  GstClockTime in_ts, in_dur, stop;
901 902
  gint64 cstart, cstop;

903 904 905
  in_ts = GST_BUFFER_TIMESTAMP (buf);
  in_dur = GST_BUFFER_DURATION (buf);

906 907
  GST_LOG_OBJECT (dec,
      "timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT,
908
      GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur));
909

910 911
  /* can't clip without TIME segment */
  if (dec->segment.format != GST_FORMAT_TIME)
912 913
    goto beach;

914 915 916 917 918 919 920 921 922 923 924
  /* 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)))
925 926
    goto beach;

927 928
  /* update timestamp and possibly duration if the clipped stop time is
   * valid */
929
  GST_BUFFER_TIMESTAMP (buf) = cstart;
930 931
  if (GST_CLOCK_TIME_IS_VALID (cstop))
    GST_BUFFER_DURATION (buf) = cstop - cstart;
932 933

beach:
934
  GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
935 936 937
  return res;
}

938
static GstFlowReturn
939
theora_dec_push_forward (GstTheoraDec * dec, GstBuffer * buf)
940
{
941
  GstFlowReturn result = GST_FLOW_OK;
942

Wim Taymans's avatar
Wim Taymans committed
943
  if (clip_buffer (dec, buf)) {
944
    if (dec->discont) {
Wim Taymans's avatar
Wim Taymans committed
945
      GST_LOG_OBJECT (dec, "setting DISCONT");
946 947 948
      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
      dec->discont = FALSE;
    }
Wim Taymans's avatar
Wim Taymans committed
949 950 951
    result = gst_pad_push (dec