gsttheoradec.c 47.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>
Wim Taymans's avatar
Wim Taymans committed
47
#include <gst/video/gstvideometa.h>
48
#include <gst/video/gstvideopool.h>
Benjamin Otte's avatar
Benjamin Otte committed
49

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
53
#define THEORA_DEF_CROP         TRUE
54 55 56 57 58
#define THEORA_DEF_TELEMETRY_MV 0
#define THEORA_DEF_TELEMETRY_MBMODE 0
#define THEORA_DEF_TELEMETRY_QI 0
#define THEORA_DEF_TELEMETRY_BITS 0

59 60
enum
{
61
  PROP_0,
62 63 64 65 66
  PROP_CROP,
  PROP_TELEMETRY_MV,
  PROP_TELEMETRY_MBMODE,
  PROP_TELEMETRY_QI,
  PROP_TELEMETRY_BITS
67 68
};

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

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

86 87
#define gst_theora_dec_parent_class parent_class
G_DEFINE_TYPE (GstTheoraDec, gst_theora_dec, GST_TYPE_ELEMENT);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
88

89 90 91 92 93
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);

94
static gboolean theora_dec_setcaps (GstTheoraDec * dec, GstCaps * caps);
Wim Taymans's avatar
Wim Taymans committed
95 96 97 98
static gboolean theora_dec_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
static GstFlowReturn theora_dec_chain (GstPad * pad, GstObject * parent,
    GstBuffer * buffer);
99 100
static GstStateChangeReturn theora_dec_change_state (GstElement * element,
    GstStateChange transition);
Wim Taymans's avatar
Wim Taymans committed
101 102
static gboolean theora_dec_src_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
103 104 105 106
static gboolean theora_dec_src_query (GstPad * pad, GstObject * parent,
    GstQuery * query);
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
107 108

#if 0
109
static const GstFormat *theora_get_formats (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
110 111
#endif
#if 0
112
static const GstEventMask *theora_get_event_masks (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
113
#endif
Benjamin Otte's avatar
Benjamin Otte committed
114

115 116 117 118 119 120 121
static gboolean
gst_theora_dec_ctl_is_supported (int req)
{
  /* should return TH_EFAULT or TH_EINVAL if supported, and TH_EIMPL if not */
  return (th_decode_ctl (NULL, req, NULL, 0) != TH_EIMPL);
}

Benjamin Otte's avatar
Benjamin Otte committed
122
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
123
gst_theora_dec_class_init (GstTheoraDecClass * klass)
Benjamin Otte's avatar
Benjamin Otte committed
124
{
125
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Benjamin Otte's avatar
Benjamin Otte committed
126 127
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);

128 129 130
  gobject_class->set_property = theora_dec_set_property;
  gobject_class->get_property = theora_dec_get_property;

131
  g_object_class_install_property (gobject_class, PROP_CROP,
132 133
      g_param_spec_boolean ("crop", "Crop",
          "Crop the image to the visible region", THEORA_DEF_CROP,
134
          (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
135

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
  if (gst_theora_dec_ctl_is_supported (TH_DECCTL_SET_TELEMETRY_MV)) {
    g_object_class_install_property (gobject_class, PROP_TELEMETRY_MV,
        g_param_spec_int ("visualize-motion-vectors",
            "Visualize motion vectors",
            "Show motion vector selection overlaid on image. "
            "Value gives a mask for motion vector (MV) modes to show",
            0, 0xffff, THEORA_DEF_TELEMETRY_MV,
            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  }

  if (gst_theora_dec_ctl_is_supported (TH_DECCTL_SET_TELEMETRY_MBMODE)) {
    g_object_class_install_property (gobject_class, PROP_TELEMETRY_MBMODE,
        g_param_spec_int ("visualize-macroblock-modes",
            "Visualize macroblock modes",
            "Show macroblock mode selection overlaid on image. "
            "Value gives a mask for macroblock (MB) modes to show",
            0, 0xffff, THEORA_DEF_TELEMETRY_MBMODE,
            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  }

  if (gst_theora_dec_ctl_is_supported (TH_DECCTL_SET_TELEMETRY_QI)) {
    g_object_class_install_property (gobject_class, PROP_TELEMETRY_QI,
        g_param_spec_int ("visualize-quantization-modes",
            "Visualize adaptive quantization modes",
            "Show adaptive quantization mode selection overlaid on image. "
            "Value gives a mask for quantization (QI) modes to show",
            0, 0xffff, THEORA_DEF_TELEMETRY_QI,
            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  }

  if (gst_theora_dec_ctl_is_supported (TH_DECCTL_SET_TELEMETRY_BITS)) {
    /* FIXME: make this a boolean instead? The value scales the bars so
     * they're less wide. Default is to use full width, and anything else
     * doesn't seem particularly useful, since the smaller bars just disappear
     * then (they almost disappear for a value of 2 already). */
    g_object_class_install_property (gobject_class, PROP_TELEMETRY_BITS,
        g_param_spec_int ("visualize-bit-usage",
            "Visualize bitstream usage breakdown",
            "Sets the bitstream breakdown visualization mode. "
            "Values influence the width of the bit usage bars to show",
            0, 0xff, THEORA_DEF_TELEMETRY_BITS,
            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  }
179

180 181 182 183 184 185 186 187 188
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&theora_dec_src_factory));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&theora_dec_sink_factory));
  gst_element_class_set_details_simple (gstelement_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
189
  gstelement_class->change_state = theora_dec_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
190 191

  GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder");
Benjamin Otte's avatar
Benjamin Otte committed
192 193 194
}

static void
195
gst_theora_dec_init (GstTheoraDec * dec)
Benjamin Otte's avatar
Benjamin Otte committed
196
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
197
  dec->sinkpad =
198
      gst_pad_new_from_static_template (&theora_dec_sink_factory, "sink");
199
  gst_pad_set_event_function (dec->sinkpad, theora_dec_sink_event);
Benjamin Otte's avatar
Benjamin Otte committed
200 201 202
  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
203
  dec->srcpad =
204
      gst_pad_new_from_static_template (&theora_dec_src_factory, "src");
Benjamin Otte's avatar
Benjamin Otte committed
205
  gst_pad_set_event_function (dec->srcpad, theora_dec_src_event);
206
  gst_pad_set_query_function (dec->srcpad, theora_dec_src_query);
207
  gst_pad_use_fixed_caps (dec->srcpad);
208

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

211
  dec->crop = THEORA_DEF_CROP;
212 213 214 215
  dec->telemetry_mv = THEORA_DEF_TELEMETRY_MV;
  dec->telemetry_mbmode = THEORA_DEF_TELEMETRY_MBMODE;
  dec->telemetry_qi = THEORA_DEF_TELEMETRY_QI;
  dec->telemetry_bits = THEORA_DEF_TELEMETRY_BITS;
216 217
  dec->gather = NULL;
  dec->decode = NULL;
218
  dec->queued = NULL;
Wim Taymans's avatar
Wim Taymans committed
219
  dec->pendingevents = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
220
}
221

222 223 224 225 226
static void
gst_theora_dec_reset (GstTheoraDec * dec)
{
  dec->need_keyframe = TRUE;
  dec->last_timestamp = -1;
227 228
  dec->discont = TRUE;
  dec->frame_nr = -1;
Wim Taymans's avatar
Wim Taymans committed
229
  dec->seqnum = gst_util_seqnum_next ();
230 231
  dec->dropped = 0;
  dec->processed = 0;
232 233 234 235 236 237
  gst_segment_init (&dec->segment, GST_FORMAT_TIME);

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

239
  g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL);
240 241
  g_list_free (dec->queued);
  dec->queued = NULL;
242 243 244 245 246 247
  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
248 249 250
  g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL);
  g_list_free (dec->pendingevents);
  dec->pendingevents = NULL;
251 252 253 254 255

  if (dec->tags) {
    gst_tag_list_free (dec->tags);
    dec->tags = NULL;
  }
256 257
}

Wim Taymans's avatar
Wim Taymans committed
258
#if 0
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
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
276
#endif
277

Wim Taymans's avatar
Wim Taymans committed
278
#if 0
279 280 281 282 283 284 285 286 287 288
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
289
#endif
290

291
static gboolean
292 293 294
theora_dec_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
295
{
296 297 298 299
  gboolean res = TRUE;
  GstTheoraDec *dec;
  guint64 scale = 1;

Wim Taymans's avatar
Wim Taymans committed
300 301 302 303 304
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

305 306 307 308 309 310
  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;

311 312 313 314
  switch (src_format) {
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
315
          *dest_value = gst_util_uint64_scale_int (src_value, 8,
316
              dec->info.pic_height * dec->info.pic_width * dec->output_bpp);
317 318 319 320 321 322 323
          break;
        case GST_FORMAT_TIME:
          /* seems like a rather silly conversion, implement me if you like */
        default:
          res = FALSE;
      }
      break;
324
    case GST_FORMAT_TIME:
325 326
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
327 328 329
          scale =
              dec->output_bpp * (dec->info.pic_width * dec->info.pic_height) /
              8;
330
        case GST_FORMAT_DEFAULT:
331
          *dest_value = scale * gst_util_uint64_scale (src_value,
332
              dec->info.fps_numerator, dec->info.fps_denominator * GST_SECOND);
333 334 335 336
          break;
        default:
          res = FALSE;
      }
337 338
      break;
    case GST_FORMAT_DEFAULT:
339 340
      switch (*dest_format) {
        case GST_FORMAT_TIME:
341
          *dest_value = gst_util_uint64_scale (src_value,
342
              GST_SECOND * dec->info.fps_denominator, dec->info.fps_numerator);
343 344
          break;
        case GST_FORMAT_BYTES:
345
          *dest_value = gst_util_uint64_scale_int (src_value,
346
              dec->output_bpp * dec->info.pic_width * dec->info.pic_height, 8);
347 348 349 350
          break;
        default:
          res = FALSE;
      }
351 352
      break;
    default:
353
      res = FALSE;
354
  }
355 356
done:
  gst_object_unref (dec);
357
  return res;
358 359 360 361 362 363 364 365

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

Wim Taymans's avatar
Wim Taymans committed
368
#if 0
Benjamin Otte's avatar
Benjamin Otte committed
369
static gboolean
370 371 372
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
373
{
374 375
  gboolean res = TRUE;
  GstTheoraDec *dec;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
376

Wim Taymans's avatar
Wim Taymans committed
377 378 379 380 381
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

382 383 384 385 386 387
  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;

388
  switch (src_format) {
Benjamin Otte's avatar
Benjamin Otte committed
389
    case GST_FORMAT_DEFAULT:
390 391
      switch (*dest_format) {
        case GST_FORMAT_TIME:
392
          *dest_value = _theora_granule_start_time (dec, src_value);
393 394 395 396
          break;
        default:
          res = FALSE;
      }
397
      break;
398 399 400 401
    case GST_FORMAT_TIME:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
        {
402
          guint rest;
403

404
          /* framecount */
405
          *dest_value = gst_util_uint64_scale (src_value,
406
              dec->info.fps_numerator, GST_SECOND * dec->info.fps_denominator);
407 408

          /* funny way of calculating granulepos in theora */
409
          rest = *dest_value / dec->info.keyframe_granule_shift;
410
          *dest_value -= rest;
411
          *dest_value <<= dec->granule_shift;
412
          *dest_value += rest;
413 414 415 416 417 418 419
          break;
        }
        default:
          res = FALSE;
          break;
      }
      break;
Benjamin Otte's avatar
Benjamin Otte committed
420
    default:
421
      res = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
422
  }
423 424
done:
  gst_object_unref (dec);
425
  return res;
426 427 428 429 430 431 432 433

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

static gboolean
Wim Taymans's avatar
Wim Taymans committed
438
theora_dec_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
439
{
440
  GstTheoraDec *dec;
Wim Taymans's avatar
Wim Taymans committed
441 442
  gboolean res = FALSE;

Wim Taymans's avatar
Wim Taymans committed
443
  dec = GST_THEORA_DEC (parent);
444

445
  switch (GST_QUERY_TYPE (query)) {
Wim Taymans's avatar
Wim Taymans committed
446 447
    case GST_QUERY_POSITION:
    {
Wim Taymans's avatar
Wim Taymans committed
448
      gint64 value;
Wim Taymans's avatar
Wim Taymans committed
449
      GstFormat format;
Wim Taymans's avatar
Wim Taymans committed
450 451
      gint64 time;

Wim Taymans's avatar
Wim Taymans committed
452
      /* parse format */
453
      gst_query_parse_position (query, &format, NULL);
454

Wim Taymans's avatar
Wim Taymans committed
455
      time = dec->last_timestamp;
456
      time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
457

458
      GST_LOG_OBJECT (dec,
459
          "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));
460

Wim Taymans's avatar
Wim Taymans committed
461
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
462 463
              theora_dec_src_convert (pad, GST_FORMAT_TIME, time, &format,
                  &value)))
Wim Taymans's avatar
Wim Taymans committed
464 465
        goto error;

466
      gst_query_set_position (query, format, value);
Wim Taymans's avatar
Wim Taymans committed
467 468

      GST_LOG_OBJECT (dec,
469
          "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value,
470
          format);
Wim Taymans's avatar
Wim Taymans committed
471 472
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
473
    case GST_QUERY_DURATION:
474
    {
Wim Taymans's avatar
Wim Taymans committed
475
      /* forward to peer for total */
Wim Taymans's avatar
Wim Taymans committed
476
      res = gst_pad_peer_query (dec->sinkpad, query);
477
      if (!res)
Wim Taymans's avatar
Wim Taymans committed
478
        goto error;
479

Wim Taymans's avatar
Wim Taymans committed
480
      break;
481
    }
Wim Taymans's avatar
Wim Taymans committed
482 483 484 485 486
    case GST_QUERY_CONVERT:
    {
      GstFormat src_fmt, dest_fmt;
      gint64 src_val, dest_val;

487
      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
488
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
489 490
              theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
491 492
        goto error;

493
      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
Wim Taymans's avatar
Wim Taymans committed
494 495 496
      break;
    }
    default:
Wim Taymans's avatar
Wim Taymans committed
497
      res = gst_pad_query_default (pad, parent, query);
Wim Taymans's avatar
Wim Taymans committed
498
      break;
499
  }
500 501
done:

502
  return res;
503

504
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
505
error:
506 507 508 509
  {
    GST_DEBUG_OBJECT (dec, "query failed");
    goto done;
  }
Wim Taymans's avatar
Wim Taymans committed
510 511
}

Benjamin Otte's avatar
Benjamin Otte committed
512
static gboolean
Wim Taymans's avatar
Wim Taymans committed
513
theora_dec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
514 515 516 517
{
  gboolean res = TRUE;
  GstTheoraDec *dec;

Wim Taymans's avatar
Wim Taymans committed
518
  dec = GST_THEORA_DEC (parent);
Benjamin Otte's avatar
Benjamin Otte committed
519 520

  switch (GST_EVENT_TYPE (event)) {
521 522
    case GST_EVENT_SEEK:
    {
523 524
      GstFormat format, tformat;
      gdouble rate;
Scott Wheeler Wheeler's avatar
Scott Wheeler Wheeler committed
525
      GstEvent *real_seek;
526 527 528 529
      GstSeekFlags flags;
      GstSeekType cur_type, stop_type;
      gint64 cur, stop;
      gint64 tcur, tstop;
Wim Taymans's avatar
Wim Taymans committed
530
      guint32 seqnum;
531 532 533

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

537 538 539 540 541 542
      /* 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 
       */
543 544
      tformat = GST_FORMAT_TIME;
      if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur)))
545
        goto convert_error;
546
      if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop)))
547
        goto convert_error;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
548

549
      /* then seek with time on the peer */
550 551
      real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
          flags, cur_type, tcur, stop_type, tstop);
Wim Taymans's avatar
Wim Taymans committed
552
      gst_event_set_seqnum (real_seek, seqnum);
553

554
      res = gst_pad_push_event (dec->sinkpad, real_seek);
Benjamin Otte's avatar
Benjamin Otte committed
555 556
      break;
    }
557 558 559 560 561 562
    case GST_EVENT_QOS:
    {
      gdouble proportion;
      GstClockTimeDiff diff;
      GstClockTime timestamp;

Wim Taymans's avatar
Wim Taymans committed
563
      gst_event_parse_qos (event, NULL, &proportion, &diff, &timestamp);
564 565 566

      /* we cannot randomly skip frame decoding since we don't have
       * B frames. we can however use the timestamp and diff to not
567 568
       * push late frames. This would at least save us the time to
       * crop/memcpy the data. */
569 570 571 572 573
      GST_OBJECT_LOCK (dec);
      dec->proportion = proportion;
      dec->earliest_time = timestamp + diff;
      GST_OBJECT_UNLOCK (dec);

574 575 576
      GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
          GST_TIME_ARGS (timestamp), diff);

577
      res = gst_pad_push_event (dec->sinkpad, event);
578 579
      break;
    }
Benjamin Otte's avatar
Benjamin Otte committed
580
    default:
581
      res = gst_pad_push_event (dec->sinkpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
582 583
      break;
  }
584
done:
Benjamin Otte's avatar
Benjamin Otte committed
585 586

  return res;
587

588 589 590 591 592 593
  /* ERRORS */
convert_error:
  {
    GST_DEBUG_OBJECT (dec, "could not convert format");
    goto done;
  }
594 595
}

596
static gboolean
Wim Taymans's avatar
Wim Taymans committed
597
theora_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
598
{
599
  gboolean ret = FALSE;
600 601
  GstTheoraDec *dec;

Wim Taymans's avatar
Wim Taymans committed
602
  dec = GST_THEORA_DEC (parent);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
603

Benjamin Otte's avatar
Benjamin Otte committed
604 605
  GST_LOG_OBJECT (dec, "handling event");
  switch (GST_EVENT_TYPE (event)) {
606 607 608 609 610 611 612
    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
613 614 615
    case GST_EVENT_EOS:
      ret = gst_pad_push_event (dec->srcpad, event);
      break;
Wim Taymans's avatar
Wim Taymans committed
616
    case GST_EVENT_SEGMENT:
617
    {
618
      const GstSegment *segment;
619

Wim Taymans's avatar
Wim Taymans committed
620
      gst_event_parse_segment (event, &segment);
621

622
      /* we need TIME format */
623
      if (segment->format != GST_FORMAT_TIME)
624 625
        goto newseg_wrong_format;

626
      GST_DEBUG_OBJECT (dec, "segment: %" GST_SEGMENT_FORMAT, segment);
627

628
      /* now configure the values */
629
      gst_segment_copy_into (segment, &dec->segment);
Wim Taymans's avatar
Wim Taymans committed
630
      dec->seqnum = gst_event_get_seqnum (event);
631

Wim Taymans's avatar
Wim Taymans committed
632
      /* We don't forward this unless/until the decoder is initialised */
633 634 635
      if (dec->have_header) {
        ret = gst_pad_push_event (dec->srcpad, event);
      } else {
Wim Taymans's avatar
Wim Taymans committed
636
        dec->pendingevents = g_list_append (dec->pendingevents, event);
637 638
        ret = TRUE;
      }
Benjamin Otte's avatar
Benjamin Otte committed
639
      break;
640
    }
641 642 643 644 645 646
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      ret = theora_dec_setcaps (dec, caps);
Wim Taymans's avatar
Wim Taymans committed
647
      gst_event_unref (event);
648 649
      break;
    }
650 651 652 653 654 655 656 657 658 659 660 661
    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
662
    default:
Wim Taymans's avatar
Wim Taymans committed
663
      ret = gst_pad_event_default (pad, parent, event);
Benjamin Otte's avatar
Benjamin Otte committed
664 665
      break;
  }
666
done:
Wim Taymans's avatar
Wim Taymans committed
667

668
  return ret;
669 670 671 672

  /* ERRORS */
newseg_wrong_format:
  {
673
    GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
674
    gst_event_unref (event);
675 676
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
677 678
}

679
static gboolean
680
theora_dec_setcaps (GstTheoraDec * dec, GstCaps * caps)
681 682
{
  GstStructure *s;
683
  const GValue *codec_data;
684 685 686 687 688 689 690

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

691 692 693
  if ((codec_data = gst_structure_get_value (s, "codec_data"))) {
    if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
      GstBuffer *buffer;
694 695
      guint8 *data, *ptr;
      gsize size, left;
696 697 698 699 700
      guint offset;

      buffer = gst_value_get_buffer (codec_data);

      offset = 0;
701
      data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
702

703 704 705 706
      ptr = data;
      left = size;

      while (left > 2) {
707 708 709
        guint psize;
        GstBuffer *buf;

710
        psize = (ptr[0] << 8) | ptr[1];
711
        /* skip header */
712 713
        ptr += 2;
        left -= 2;
714 715 716
        offset += 2;

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

Wim Taymans's avatar
Wim Taymans committed
719 720
        buf =
            gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, offset, psize);
721 722 723 724 725 726

        /* 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 */
Wim Taymans's avatar
Wim Taymans committed
727
        theora_dec_chain (dec->sinkpad, GST_OBJECT_CAST (dec), buf);
728 729

        /* skip the data */
730 731
        left -= psize;
        ptr += psize;
732 733
        offset += psize;
      }
734
      gst_buffer_unmap (buffer, data, size);
735 736 737
    }
  }

738 739 740
  return TRUE;
}

741
static GstFlowReturn
742
theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet)
Benjamin Otte's avatar
Benjamin Otte committed
743
{
744 745 746
  gchar *encoder = NULL;
  GstTagList *list;

747
  GST_DEBUG_OBJECT (dec, "parsing comment packet");
748

749
  list =
750 751
      gst_tag_list_from_vorbiscomment (packet->packet, packet->bytes,
      (guint8 *) "\201theora", 7, &encoder);
752 753 754

  if (!list) {
    GST_ERROR_OBJECT (dec, "couldn't decode comments");
755
    list = gst_tag_list_new_empty ();
756 757 758 759 760 761 762 763 764 765
  }
  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);

766 767 768 769 770 771
  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);
  }

772
  dec->tags = list;
773 774 775 776

  return GST_FLOW_OK;
}

Wim Taymans's avatar
Wim Taymans committed
777
static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
778
theora_negotiate_pool (GstTheoraDec * dec, GstCaps * caps, GstVideoInfo * info)
Wim Taymans's avatar
Wim Taymans committed
779 780 781
{
  GstQuery *query;
  GstBufferPool *pool = NULL;
782
  guint size, min, max, prefix, alignment;
783
  GstStructure *config;
Wim Taymans's avatar
Wim Taymans committed
784 785 786 787

  /* find a pool for the negotiated caps now */
  query = gst_query_new_allocation (caps, TRUE);

788
  if (gst_pad_peer_query (dec->srcpad, query)) {
Wim Taymans's avatar
Wim Taymans committed
789 790
    GST_DEBUG_OBJECT (dec, "got downstream ALLOCATION hints");
    /* we got configuration from our peer, parse them */
791 792
    gst_query_parse_allocation_params (query, &size, &min, &max, &prefix,
        &alignment, &pool);
Wim Taymans's avatar
the  
Wim Taymans committed
793
    size = MAX (size, info->size);
Wim Taymans's avatar
Wim Taymans committed
794 795
  } else {
    GST_DEBUG_OBJECT (dec, "didn't get downstream ALLOCATION hints");
Wim Taymans's avatar
the  
Wim Taymans committed
796
    size = info->size;
797 798
    min = max = 0;
    prefix = 0;
Wim Taymans's avatar
Wim Taymans committed
799
    alignment = 0;
Wim Taymans's avatar
Wim Taymans committed
800 801 802 803 804 805 806 807 808 809 810
  }

  if (pool == NULL) {
    /* we did not get a pool, make one ourselves then */
    pool = gst_buffer_pool_new ();
  }

  if (dec->pool)
    gst_object_unref (dec->pool);
  dec->pool = pool;

811 812
  config = gst_buffer_pool_get_config (pool);
  gst_buffer_pool_config_set (config, caps, size, min, max, prefix, alignment);
813
  /* just set the option, if the pool can support it we will transparently use
814
   * it through the video info API. We could also see if the pool support this
815
   * option and only activate it then. */
Wim Taymans's avatar
Wim Taymans committed
816
  gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
817

Wim Taymans's avatar
Wim Taymans committed
818
  /* check if downstream supports cropping */
Wim Taymans's avatar
Wim Taymans committed
819
  dec->has_cropping =
Wim Taymans's avatar
Wim Taymans committed
820
      gst_query_has_allocation_meta (query, GST_VIDEO_CROP_META_API);
Wim Taymans's avatar
Wim Taymans committed
821

Wim Taymans's avatar
Wim Taymans committed
822
  gst_buffer_pool_set_config (pool, config);
Wim Taymans's avatar
Wim Taymans committed
823 824 825
  /* and activate */
  gst_buffer_pool_set_active (pool, TRUE);

Wim Taymans's avatar
Wim Taymans committed
826 827
  gst_query_unref (query);

Wim Taymans's avatar
Wim Taymans committed
828 829 830
  return GST_FLOW_OK;
}

831 832 833 834
static GstFlowReturn
theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstCaps *caps;
Wim Taymans's avatar
Wim Taymans committed
835 836
  GstVideoFormat format;
  gint width, height;
837
  gint par_num, par_den;
838
  GstFlowReturn ret = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
839
  GList *walk;
840 841 842 843 844 845 846

  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;
847
   * 0:x and x:0 are allowed and can be interpreted as 1:1.
848
   */
849 850 851 852 853 854 855 856 857 858
  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;
  }
859
  if (par_num == 0 || par_den == 0) {
860 861 862 863
    par_num = par_den = 1;
  }
  /* theora has:
   *
Wim Taymans's avatar
Wim Taymans committed
864
   *  frame_width/frame_height : dimension of the encoded frame
865 866
   *  pic_width/pic_height : dimension of the visible part
   *  pic_x/pic_y : offset in encoded frame where visible part starts
867
   */
Wim Taymans's avatar
Wim Taymans committed
868 869 870 871 872
  GST_DEBUG_OBJECT (dec, "frame dimension %dx%d, PAR %d/%d",
      dec->info.frame_width, dec->info.frame_height, par_num, par_den);
  GST_DEBUG_OBJECT (dec, "picture dimension %dx%d, offset %d:%d",
      dec->info.pic_width, dec->info.pic_height, dec->info.pic_x,
      dec->info.pic_y);
873

Wim Taymans's avatar
Wim Taymans committed
874 875 876
  switch (dec->info.pixel_fmt) {
    case TH_PF_444:
      dec->output_bpp = 24;
Wim Taymans's avatar
Wim Taymans committed
877
      format = GST_VIDEO_FORMAT_Y444;
Wim Taymans's avatar
Wim Taymans committed
878 879 880
      break;
    case TH_PF_420:
      dec->output_bpp = 12;     /* Average bits per pixel. */
Wim Taymans's avatar
Wim Taymans committed
881
      format = GST_VIDEO_FORMAT_I420;
Wim Taymans's avatar
Wim Taymans committed
882 883 884
      break;
    case TH_PF_422:
      dec->output_bpp = 16;
Wim Taymans's avatar
Wim Taymans committed
885
      format = GST_VIDEO_FORMAT_Y42B;
Wim Taymans's avatar
Wim Taymans committed
886 887 888
      break;
    default:
      goto invalid_format;
889
  }
890 891

  if (dec->crop) {
Wim Taymans's avatar
Wim Taymans committed
892 893
    width = dec->info.pic_width;
    height = dec->info.pic_height;
894 895
  } else {
    /* no cropping, use the encoded dimensions */
Wim Taymans's avatar
Wim Taymans committed
896 897
    width = dec->info.frame_width;
    height = dec->info.frame_height;
898
  }