gsttheoradec.c 40.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
enum
{
54 55
  PROP_0,
  PROP_CROP
56 57
};

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
  gobject_class->set_property = theora_dec_set_property;
  gobject_class->get_property = theora_dec_get_property;

126
  g_object_class_install_property (gobject_class, PROP_CROP,
127 128
      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
  dec->dropped = 0;
  dec->processed = 0;
172 173 174 175 176 177
  gst_segment_init (&dec->segment, GST_FORMAT_TIME);

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

179
  g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL);
180 181
  g_list_free (dec->queued);
  dec->queued = NULL;
182 183 184 185 186 187
  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
188 189 190
  g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL);
  g_list_free (dec->pendingevents);
  dec->pendingevents = NULL;
191 192 193 194 195

  if (dec->tags) {
    gst_tag_list_free (dec->tags);
    dec->tags = NULL;
  }
196 197
}

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

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

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

  return theora_src_query_types;
}


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

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

259 260 261 262 263 264
  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;

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

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

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

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

336 337 338 339 340 341
  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;

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

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

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

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

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

Wim Taymans's avatar
Wim Taymans committed
396 397
  gboolean res = FALSE;

398 399
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

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

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

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

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

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

Wim Taymans's avatar
Wim Taymans committed
421
      gst_query_set_position (query, format, value);
Wim Taymans's avatar
Wim Taymans committed
422 423

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

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

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

458
  return res;
459

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

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

474
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
Benjamin Otte's avatar
Benjamin Otte committed
475 476

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

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

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

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

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

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

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

  return res;
544

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

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

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

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

580 581
      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
          &start, &stop, &time);
582

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

587 588 589 590 591 592
      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));

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

Wim Taymans's avatar
Wim Taymans committed
598
      /* We don't forward this unless/until the decoder is initialised */
599 600 601
      if (dec->have_header) {
        ret = gst_pad_push_event (dec->srcpad, event);
      } else {
Wim Taymans's avatar
Wim Taymans committed
602
        dec->pendingevents = g_list_append (dec->pendingevents, event);
603 604
        ret = TRUE;
      }
Benjamin Otte's avatar
Benjamin Otte committed
605
      break;
606
    }
607 608 609 610 611 612 613 614 615 616 617 618
    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
619
    default:
Wim Taymans's avatar
Wim Taymans committed
620
      ret = gst_pad_push_event (dec->srcpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
621 622
      break;
  }
623
done:
Wim Taymans's avatar
Wim Taymans committed
624 625
  gst_object_unref (dec);

626
  return ret;
627 628 629 630

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

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

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

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 693 694
  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;
      }
    }
  }

695 696 697 698 699
  gst_object_unref (dec);

  return TRUE;
}

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

707
  GST_DEBUG_OBJECT (dec, "parsing comment packet");
708

709 710 711
  buf = gst_buffer_new ();
  GST_BUFFER_SIZE (buf) = packet->bytes;
  GST_BUFFER_DATA (buf) = packet->packet;
712

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

732 733 734 735 736 737
  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);
  }

738
  dec->tags = list;
739 740 741 742 743 744 745 746 747

  return GST_FLOW_OK;
}

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

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

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

  if (dec->crop) {
800 801
    dec->width = dec->info.pic_width;
    dec->height = dec->info.pic_height;
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
    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;
819 820 821 822 823 824 825 826
    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 */
827
  dec->decoder = th_decode_alloc (&dec->info, dec->setup);
828 829

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

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

  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;
847
  }
848

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

  return ret;
856 857 858 859 860 861
}

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

864
  GST_DEBUG_OBJECT (dec, "parsing header packet");
865

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

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

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

905 906 907
  in_ts = GST_BUFFER_TIMESTAMP (buf);
  in_dur = GST_BUFFER_DURATION (buf);

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

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

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

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

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

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

Wim Taymans's avatar
Wim Taymans committed
945
  if (clip_buffer (dec, buf)) {