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

53 54 55 56 57
#define THEORA_DEF_TELEMETRY_MV 0
#define THEORA_DEF_TELEMETRY_MBMODE 0
#define THEORA_DEF_TELEMETRY_QI 0
#define THEORA_DEF_TELEMETRY_BITS 0

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

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

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

84 85
#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
86

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

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

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

113 114 115 116 117 118 119
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
120
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
121
gst_theora_dec_class_init (GstTheoraDecClass * klass)
Benjamin Otte's avatar
Benjamin Otte committed
122
{
123
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Benjamin Otte's avatar
Benjamin Otte committed
124 125
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);

126 127 128
  gobject_class->set_property = theora_dec_set_property;
  gobject_class->get_property = theora_dec_get_property;

129 130 131 132 133 134 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
  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));
  }
172

173 174 175 176 177 178 179 180 181
  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
182
  gstelement_class->change_state = theora_dec_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
183 184

  GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder");
Benjamin Otte's avatar
Benjamin Otte committed
185 186 187
}

static void
188
gst_theora_dec_init (GstTheoraDec * dec)
Benjamin Otte's avatar
Benjamin Otte committed
189
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
190
  dec->sinkpad =
191
      gst_pad_new_from_static_template (&theora_dec_sink_factory, "sink");
192
  gst_pad_set_event_function (dec->sinkpad, theora_dec_sink_event);
Benjamin Otte's avatar
Benjamin Otte committed
193 194 195
  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
196
  dec->srcpad =
197
      gst_pad_new_from_static_template (&theora_dec_src_factory, "src");
Benjamin Otte's avatar
Benjamin Otte committed
198
  gst_pad_set_event_function (dec->srcpad, theora_dec_src_event);
199
  gst_pad_set_query_function (dec->srcpad, theora_dec_src_query);
200
  gst_pad_use_fixed_caps (dec->srcpad);
201

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

204 205 206 207
  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;
208 209
  dec->gather = NULL;
  dec->decode = NULL;
210
  dec->queued = NULL;
Wim Taymans's avatar
Wim Taymans committed
211
  dec->pendingevents = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
212
}
213

214 215 216 217 218
static void
gst_theora_dec_reset (GstTheoraDec * dec)
{
  dec->need_keyframe = TRUE;
  dec->last_timestamp = -1;
219 220
  dec->discont = TRUE;
  dec->frame_nr = -1;
Wim Taymans's avatar
Wim Taymans committed
221
  dec->seqnum = gst_util_seqnum_next ();
222 223
  dec->dropped = 0;
  dec->processed = 0;
224 225 226 227 228 229
  gst_segment_init (&dec->segment, GST_FORMAT_TIME);

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

231
  g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL);
232 233
  g_list_free (dec->queued);
  dec->queued = NULL;
234 235 236 237 238 239
  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
240 241 242
  g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL);
  g_list_free (dec->pendingevents);
  dec->pendingevents = NULL;
243 244 245 246 247

  if (dec->tags) {
    gst_tag_list_free (dec->tags);
    dec->tags = NULL;
  }
248 249
}

Wim Taymans's avatar
Wim Taymans committed
250
#if 0
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
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
268
#endif
269

Wim Taymans's avatar
Wim Taymans committed
270
#if 0
271 272 273 274 275 276 277 278 279 280
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
281
#endif
282

283
static gboolean
284 285 286
theora_dec_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
287
{
288 289 290 291
  gboolean res = TRUE;
  GstTheoraDec *dec;
  guint64 scale = 1;

Wim Taymans's avatar
Wim Taymans committed
292 293 294 295 296
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

297 298 299 300 301 302
  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;

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

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

Wim Taymans's avatar
Wim Taymans committed
360
#if 0
Benjamin Otte's avatar
Benjamin Otte committed
361
static gboolean
362 363 364
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
365
{
366 367
  gboolean res = TRUE;
  GstTheoraDec *dec;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
368

Wim Taymans's avatar
Wim Taymans committed
369 370 371 372 373
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

374 375 376 377 378 379
  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;

380
  switch (src_format) {
Benjamin Otte's avatar
Benjamin Otte committed
381
    case GST_FORMAT_DEFAULT:
382 383
      switch (*dest_format) {
        case GST_FORMAT_TIME:
384
          *dest_value = _theora_granule_start_time (dec, src_value);
385 386 387 388
          break;
        default:
          res = FALSE;
      }
389
      break;
390 391 392 393
    case GST_FORMAT_TIME:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
        {
394
          guint rest;
395

396
          /* framecount */
397
          *dest_value = gst_util_uint64_scale (src_value,
398
              dec->info.fps_numerator, GST_SECOND * dec->info.fps_denominator);
399 400

          /* funny way of calculating granulepos in theora */
401
          rest = *dest_value / dec->info.keyframe_granule_shift;
402
          *dest_value -= rest;
403
          *dest_value <<= dec->granule_shift;
404
          *dest_value += rest;
405 406 407 408 409 410 411
          break;
        }
        default:
          res = FALSE;
          break;
      }
      break;
Benjamin Otte's avatar
Benjamin Otte committed
412
    default:
413
      res = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
414
  }
415 416
done:
  gst_object_unref (dec);
417
  return res;
418 419 420 421 422 423 424 425

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

static gboolean
Wim Taymans's avatar
Wim Taymans committed
430
theora_dec_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
431
{
432
  GstTheoraDec *dec;
Wim Taymans's avatar
Wim Taymans committed
433 434
  gboolean res = FALSE;

Wim Taymans's avatar
Wim Taymans committed
435
  dec = GST_THEORA_DEC (parent);
436

437
  switch (GST_QUERY_TYPE (query)) {
Wim Taymans's avatar
Wim Taymans committed
438 439
    case GST_QUERY_POSITION:
    {
Wim Taymans's avatar
Wim Taymans committed
440
      gint64 value;
Wim Taymans's avatar
Wim Taymans committed
441
      GstFormat format;
Wim Taymans's avatar
Wim Taymans committed
442 443
      gint64 time;

Wim Taymans's avatar
Wim Taymans committed
444
      /* parse format */
445
      gst_query_parse_position (query, &format, NULL);
446

Wim Taymans's avatar
Wim Taymans committed
447
      time = dec->last_timestamp;
448
      time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
449

450
      GST_LOG_OBJECT (dec,
451
          "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));
452

Wim Taymans's avatar
Wim Taymans committed
453
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
454 455
              theora_dec_src_convert (pad, GST_FORMAT_TIME, time, &format,
                  &value)))
Wim Taymans's avatar
Wim Taymans committed
456 457
        goto error;

458
      gst_query_set_position (query, format, value);
Wim Taymans's avatar
Wim Taymans committed
459 460

      GST_LOG_OBJECT (dec,
461
          "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value,
462
          format);
Wim Taymans's avatar
Wim Taymans committed
463 464
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
465
    case GST_QUERY_DURATION:
466
    {
Wim Taymans's avatar
Wim Taymans committed
467
      /* forward to peer for total */
Wim Taymans's avatar
Wim Taymans committed
468
      res = gst_pad_peer_query (dec->sinkpad, query);
469
      if (!res)
Wim Taymans's avatar
Wim Taymans committed
470
        goto error;
471

Wim Taymans's avatar
Wim Taymans committed
472
      break;
473
    }
Wim Taymans's avatar
Wim Taymans committed
474 475 476 477 478
    case GST_QUERY_CONVERT:
    {
      GstFormat src_fmt, dest_fmt;
      gint64 src_val, dest_val;

479
      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
480
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
481 482
              theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
483 484
        goto error;

485
      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
Wim Taymans's avatar
Wim Taymans committed
486 487 488
      break;
    }
    default:
Wim Taymans's avatar
Wim Taymans committed
489
      res = gst_pad_query_default (pad, parent, query);
Wim Taymans's avatar
Wim Taymans committed
490
      break;
491
  }
492 493
done:

494
  return res;
495

496
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
497
error:
498 499 500 501
  {
    GST_DEBUG_OBJECT (dec, "query failed");
    goto done;
  }
Wim Taymans's avatar
Wim Taymans committed
502 503
}

Benjamin Otte's avatar
Benjamin Otte committed
504
static gboolean
Wim Taymans's avatar
Wim Taymans committed
505
theora_dec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
506 507 508 509
{
  gboolean res = TRUE;
  GstTheoraDec *dec;

Wim Taymans's avatar
Wim Taymans committed
510
  dec = GST_THEORA_DEC (parent);
Benjamin Otte's avatar
Benjamin Otte committed
511 512

  switch (GST_EVENT_TYPE (event)) {
513 514
    case GST_EVENT_SEEK:
    {
515 516
      GstFormat format, tformat;
      gdouble rate;
Scott Wheeler Wheeler's avatar
Scott Wheeler Wheeler committed
517
      GstEvent *real_seek;
518 519 520 521
      GstSeekFlags flags;
      GstSeekType cur_type, stop_type;
      gint64 cur, stop;
      gint64 tcur, tstop;
Wim Taymans's avatar
Wim Taymans committed
522
      guint32 seqnum;
523 524 525

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

529 530 531 532 533 534
      /* 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 
       */
535 536
      tformat = GST_FORMAT_TIME;
      if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur)))
537
        goto convert_error;
538
      if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop)))
539
        goto convert_error;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
540

541
      /* then seek with time on the peer */
542 543
      real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
          flags, cur_type, tcur, stop_type, tstop);
Wim Taymans's avatar
Wim Taymans committed
544
      gst_event_set_seqnum (real_seek, seqnum);
545

546
      res = gst_pad_push_event (dec->sinkpad, real_seek);
Benjamin Otte's avatar
Benjamin Otte committed
547 548
      break;
    }
549 550 551 552 553 554
    case GST_EVENT_QOS:
    {
      gdouble proportion;
      GstClockTimeDiff diff;
      GstClockTime timestamp;

Wim Taymans's avatar
Wim Taymans committed
555
      gst_event_parse_qos (event, NULL, &proportion, &diff, &timestamp);
556 557 558

      /* we cannot randomly skip frame decoding since we don't have
       * B frames. we can however use the timestamp and diff to not
559 560
       * push late frames. This would at least save us the time to
       * crop/memcpy the data. */
561 562 563 564 565
      GST_OBJECT_LOCK (dec);
      dec->proportion = proportion;
      dec->earliest_time = timestamp + diff;
      GST_OBJECT_UNLOCK (dec);

566 567 568
      GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
          GST_TIME_ARGS (timestamp), diff);

569
      res = gst_pad_push_event (dec->sinkpad, event);
570 571
      break;
    }
Benjamin Otte's avatar
Benjamin Otte committed
572
    default:
573
      res = gst_pad_push_event (dec->sinkpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
574 575
      break;
  }
576
done:
Benjamin Otte's avatar
Benjamin Otte committed
577 578

  return res;
579

580 581 582 583 584 585
  /* ERRORS */
convert_error:
  {
    GST_DEBUG_OBJECT (dec, "could not convert format");
    goto done;
  }
586 587
}

588
static gboolean
Wim Taymans's avatar
Wim Taymans committed
589
theora_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
590
{
591
  gboolean ret = FALSE;
592 593
  GstTheoraDec *dec;

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

Benjamin Otte's avatar
Benjamin Otte committed
596 597
  GST_LOG_OBJECT (dec, "handling event");
  switch (GST_EVENT_TYPE (event)) {
598 599 600 601 602 603 604
    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
605 606 607
    case GST_EVENT_EOS:
      ret = gst_pad_push_event (dec->srcpad, event);
      break;
Wim Taymans's avatar
Wim Taymans committed
608
    case GST_EVENT_SEGMENT:
609
    {
610
      const GstSegment *segment;
611

Wim Taymans's avatar
Wim Taymans committed
612
      gst_event_parse_segment (event, &segment);
613

614
      /* we need TIME format */
615
      if (segment->format != GST_FORMAT_TIME)
616 617
        goto newseg_wrong_format;

618
      GST_DEBUG_OBJECT (dec, "segment: %" GST_SEGMENT_FORMAT, segment);
619

620
      /* now configure the values */
621
      gst_segment_copy_into (segment, &dec->segment);
Wim Taymans's avatar
Wim Taymans committed
622
      dec->seqnum = gst_event_get_seqnum (event);
623

Wim Taymans's avatar
Wim Taymans committed
624
      /* We don't forward this unless/until the decoder is initialised */
625 626 627
      if (dec->have_header) {
        ret = gst_pad_push_event (dec->srcpad, event);
      } else {
Wim Taymans's avatar
Wim Taymans committed
628
        dec->pendingevents = g_list_append (dec->pendingevents, event);
629 630
        ret = TRUE;
      }
Benjamin Otte's avatar
Benjamin Otte committed
631
      break;
632
    }
633 634 635 636 637 638
    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
639
      gst_event_unref (event);
640 641
      break;
    }
642 643 644 645 646 647 648 649 650 651 652 653
    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
654
    default:
Wim Taymans's avatar
Wim Taymans committed
655
      ret = gst_pad_event_default (pad, parent, event);
Benjamin Otte's avatar
Benjamin Otte committed
656 657
      break;
  }
658
done:
Wim Taymans's avatar
Wim Taymans committed
659

660
  return ret;
661 662 663 664

  /* ERRORS */
newseg_wrong_format:
  {
665
    GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
666
    gst_event_unref (event);
667 668
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
669 670
}

671
static gboolean
672
theora_dec_setcaps (GstTheoraDec * dec, GstCaps * caps)
673 674
{
  GstStructure *s;
675
  const GValue *codec_data;
676 677 678 679 680 681 682

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

683 684 685
  if ((codec_data = gst_structure_get_value (s, "codec_data"))) {
    if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
      GstBuffer *buffer;
Wim Taymans's avatar
Wim Taymans committed
686 687 688
      GstMapInfo map;
      guint8 *ptr;
      gsize left;
689 690 691 692 693
      guint offset;

      buffer = gst_value_get_buffer (codec_data);

      offset = 0;
Wim Taymans's avatar
Wim Taymans committed
694
      gst_buffer_map (buffer, &map, GST_MAP_READ);
695

Wim Taymans's avatar
Wim Taymans committed
696 697
      ptr = map.data;
      left = map.size;
698 699

      while (left > 2) {
700 701 702
        guint psize;
        GstBuffer *buf;

703
        psize = (ptr[0] << 8) | ptr[1];
704
        /* skip header */
705 706
        ptr += 2;
        left -= 2;
707 708 709
        offset += 2;

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

Wim Taymans's avatar
Wim Taymans committed
712 713
        buf =
            gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, offset, psize);
714 715 716 717 718 719

        /* 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
720
        theora_dec_chain (dec->sinkpad, GST_OBJECT_CAST (dec), buf);
721 722

        /* skip the data */
723 724
        left -= psize;
        ptr += psize;
725 726
        offset += psize;
      }
Wim Taymans's avatar
Wim Taymans committed
727
      gst_buffer_unmap (buffer, &map);
728 729 730
    }
  }

731 732 733
  return TRUE;
}

734
static GstFlowReturn
735
theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet)
Benjamin Otte's avatar
Benjamin Otte committed
736
{
737 738 739
  gchar *encoder = NULL;
  GstTagList *list;

740
  GST_DEBUG_OBJECT (dec, "parsing comment packet");
741

742
  list =
743 744
      gst_tag_list_from_vorbiscomment (packet->packet, packet->bytes,
      (guint8 *) "\201theora", 7, &encoder);
745 746 747

  if (!list) {
    GST_ERROR_OBJECT (dec, "couldn't decode comments");
748
    list = gst_tag_list_new_empty ();
749 750 751 752 753 754 755 756 757 758
  }
  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);

759 760 761 762 763 764
  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);
  }

765
  dec->tags = list;
766 767 768 769

  return GST_FLOW_OK;
}

Wim Taymans's avatar
Wim Taymans committed
770
static GstFlowReturn
771
theora_negotiate_pool (GstTheoraDec * dec)
Wim Taymans's avatar
Wim Taymans committed
772 773
{
  GstQuery *query;
774
  GstBufferPool *pool;
775
  guint size, min, max, prefix, alignment;
776
  GstStructure *config;
777
  GstCaps *caps;
778
  GstVideoInfo info;
779

780 781 782 783 784 785
  /* for the output caps we always take the cropped dimensions */
  info = dec->vinfo;
  gst_video_info_set_format (&info, GST_VIDEO_INFO_FORMAT (&info),
      dec->info.pic_width, dec->info.pic_height);
  caps = gst_video_info_to_caps (&info);
  gst_pad_set_caps (dec->srcpad, caps);
Wim Taymans's avatar
Wim Taymans committed
786 787 788 789

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

790
  if (gst_pad_peer_query (dec->srcpad, query)) {
Wim Taymans's avatar
Wim Taymans committed
791 792
    GST_DEBUG_OBJECT (dec, "got downstream ALLOCATION hints");
    /* we got configuration from our peer, parse them */
793 794
    gst_query_parse_allocation_params (query, &size, &min, &max, &prefix,
        &alignment, &pool);
795 796 797 798

    /* check if downstream supports cropping */
    dec->has_cropping =
        gst_query_has_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE);
Wim Taymans's avatar
Wim Taymans committed
799 800
  } else {
    GST_DEBUG_OBJECT (dec, "didn't get downstream ALLOCATION hints");
801
    size = 0;
802 803
    min = max = 0;
    prefix = 0;
Wim Taymans's avatar
Wim Taymans committed
804
    alignment = 0;
805
    pool = NULL;
806
    dec->has_cropping = FALSE;
Wim Taymans's avatar
Wim Taymans committed
807 808 809 810
  }

  if (pool == NULL) {
    /* we did not get a pool, make one ourselves then */
811
    pool = gst_video_buffer_pool_new ();
Wim Taymans's avatar
Wim Taymans committed
812 813 814 815 816 817
  }

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

818
  if (dec->has_cropping) {
819
    dec->outinfo = dec->vinfo;
820 821 822 823
    /* we can crop, configure the pool with buffers of caps and size of the
     * decoded picture size and then crop them with metadata */
    gst_caps_unref (caps);
    caps = gst_video_info_to_caps (&dec->vinfo);
824 825 826
  } else {
    /* no cropping, use cropped videoinfo */
    dec->outinfo = info;
827
  }
828
  size = MAX (size, GST_VIDEO_INFO_SIZE (&dec->outinfo));
829

830 831
  config = gst_buffer_pool_get_config (pool);
  gst_buffer_pool_config_set (config, caps, size, min, max, prefix, alignment);
832 833
  gst_caps_unref (caps);

834
  /* just set the option, if the pool can support it we will transparently use
835
   * it through the video info API. We could also see if the pool support this
836
   * option and only activate it then. */
Wim Taymans's avatar
Wim Taymans committed
837
  gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
838

Wim Taymans's avatar
Wim Taymans committed
839 840
  GST_DEBUG_OBJECT (dec, "downstream cropping %d", dec->has_cropping);

Wim Taymans's avatar
Wim Taymans committed
841
  gst_buffer_pool_set_config (pool, config);
Wim Taymans's avatar
Wim Taymans committed
842 843 844
  /* and activate */
  gst_buffer_pool_set_active (pool, TRUE);

Wim Taymans's avatar
Wim Taymans committed
845 846
  gst_query_unref (query);

Wim Taymans's avatar
Wim Taymans committed
847 848 849
  return GST_FLOW_OK;
}

850 851 852
static GstFlowReturn
theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
{
Wim Taymans's avatar
Wim Taymans committed
853
  GstVideoFormat format;
854
  gint par_num, par_den;
855
  GstFlowReturn ret = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
856
  GList *walk;
857 858 859 860 861 862 863

  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;
864
   * 0:x and x:0 are allowed and can be interpreted as 1:1.
865
   */
866 867 868 869 870 871 872 873 874 875
  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;
  }
876
  if (par_num == 0 || par_den == 0) {
877 878 879 880
    par_num = par_den = 1;
  }
  /* theora has:
   *
Wim Taymans's avatar
Wim Taymans committed
881
   *  frame_width/frame_height : dimension of the encoded frame
882 883
   *  pic_width/pic_height : dimension of the visible part
   *  pic_x/pic_y : offset in encoded frame where visible part starts
884
   */
Wim Taymans's avatar
Wim Taymans committed
885 886 887 888 889
  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);
890

Wim Taymans's avatar
Wim Taymans committed
891 892 893
  switch (dec->info.pixel_fmt) {
    case TH_PF_444:
      dec->output_bpp = 24;
Wim Taymans's avatar
Wim Taymans committed
894
      format = GST_VIDEO_FORMAT_Y444;
Wim Taymans's avatar
Wim Taymans committed
895 896 897
      break;
    case TH_PF_420:
      dec->output_bpp = 12;     /* Average bits per pixel. */
Wim Taymans's avatar
Wim Taymans committed
898
      format = GST_VIDEO_FORMAT_I420;
Wim Taymans's avatar
Wim Taymans committed
899 900 901
      break;
    case TH_PF_422:
      dec->output_bpp = 16;
Wim Taymans's avatar
Wim Taymans committed
902
      format = GST_VIDEO_FORMAT_Y42B;
Wim Taymans's avatar
Wim Taymans committed
903 904 905
      break;
    default:
      goto invalid_format;
906
  }
907

Wim Taymans's avatar
Wim Taymans committed
908 909
  if (dec->info.pic_width != dec->info.frame_width ||
      dec->info.pic_height != dec->info.frame_height ||
Wim Taymans's avatar
Wim Taymans committed
910 911
      dec->info.pic_x != 0 || dec->info.pic_y != 0) {
    GST_DEBUG_OBJECT (dec, "we need to crop");
Wim Taymans's avatar
Wim Taymans committed
912
    dec->need_cropping = TRUE;
Wim Taymans's avatar
Wim Taymans committed
913 914
  } else {
    GST_DEBUG_OBJECT (dec, "no cropping needed");
Wim Taymans's avatar
Wim Taymans committed
915
    dec->need_cropping = FALSE;
Wim Taymans's avatar
Wim Taymans committed
916
  }
917

918 919 920
  /* our info contains the dimensions for the coded picture before cropping */
  gst_video_info_set_format (&dec->vinfo, format, dec->info.frame_width,
      dec->info.frame_height);
Wim Taymans's avatar
Wim Taymans committed
921 922 923 924
  dec->vinfo.fps_n = dec->info.fps_numerator;
  dec->vinfo.fps_d = dec->info.fps_denominator;
  dec->vinfo.par_n = par_num;
  dec->vinfo.par_d = par_den;
Wim Taymans's avatar
Wim Taymans committed
</