gsttheoradec.c 48 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 (GstTheoraDec * dec)
Wim Taymans's avatar
Wim Taymans committed
772
{
773
  GstVideoFormat format;
Wim Taymans's avatar
Wim Taymans committed
774
  GstQuery *query;
775
  GstBufferPool *pool;
776
  guint size, min, max, prefix, alignment;
777
  GstStructure *config;
778
  GstCaps *caps;
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
  GstVideoInfo info, cinfo;

  /* theora has:
   *
   *  frame_width/frame_height : dimension of the encoded frame
   *  pic_width/pic_height : dimension of the visible part
   *  pic_x/pic_y : offset in encoded frame where visible part starts
   */
  GST_DEBUG_OBJECT (dec, "frame dimension %dx%d, PAR %d/%d, fps %d/%d",
      dec->info.frame_width, dec->info.frame_height,
      dec->info.aspect_numerator, dec->info.aspect_denominator,
      dec->info.fps_numerator, dec->info.fps_denominator);
  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);

  switch (dec->info.pixel_fmt) {
    case TH_PF_444:
      dec->output_bpp = 24;
      format = GST_VIDEO_FORMAT_Y444;
      break;
    case TH_PF_420:
      dec->output_bpp = 12;     /* Average bits per pixel. */
      format = GST_VIDEO_FORMAT_I420;
      break;
    case TH_PF_422:
      dec->output_bpp = 16;
      format = GST_VIDEO_FORMAT_Y42B;
      break;
    default:
      goto invalid_format;
  }

  if (dec->info.pic_width != dec->info.frame_width ||
      dec->info.pic_height != dec->info.frame_height ||
      dec->info.pic_x != 0 || dec->info.pic_y != 0) {
    GST_DEBUG_OBJECT (dec, "we need to crop");
    dec->need_cropping = TRUE;
  } else {
    GST_DEBUG_OBJECT (dec, "no cropping needed");
    dec->need_cropping = FALSE;
  }

  /* info contains the dimensions for the coded picture before cropping */
823
  gst_video_info_init (&info);
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
  gst_video_info_set_format (&info, format, dec->info.frame_width,
      dec->info.frame_height);
  info.fps_n = dec->info.fps_numerator;
  info.fps_d = dec->info.fps_denominator;
  /* calculate par
   * the info.aspect_* values reflect PAR;
   * 0:x and x:0 are allowed and can be interpreted as 1:1.
   */
  if (dec->have_par) {
    /* we had a par on the sink caps, override the encoded par */
    GST_DEBUG_OBJECT (dec, "overriding with input PAR %dx%d", dec->par_num,
        dec->par_den);
    info.par_n = dec->par_num;
    info.par_d = dec->par_den;
  } else {
    /* take encoded par */
    info.par_n = dec->info.aspect_numerator;
    info.par_d = dec->info.aspect_denominator;
  }
  if (info.par_n == 0 || info.par_d == 0) {
    info.par_n = info.par_d = 1;
  }

  /* these values are for all versions of the colorspace specified in the
   * theora info */
  info.chroma_site = GST_VIDEO_CHROMA_SITE_JPEG;
  info.colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235;
  info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
  info.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
  switch (dec->info.colorspace) {
    case TH_CS_ITU_REC_470M:
      info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470M;
      break;
    case TH_CS_ITU_REC_470BG:
      info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470BG;
      break;
    default:
      info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;
      break;
  }

  /* remove reconfigure flag now */
  gst_pad_check_reconfigure (dec->srcpad);
867

868
  /* for the output caps we always take the cropped dimensions */
869 870
  cinfo = info;
  gst_video_info_set_format (&cinfo, GST_VIDEO_INFO_FORMAT (&info),
871
      dec->info.pic_width, dec->info.pic_height);
872
  caps = gst_video_info_to_caps (&cinfo);
873
  gst_pad_set_caps (dec->srcpad, caps);
Wim Taymans's avatar
Wim Taymans committed
874 875 876 877

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

878
  if (gst_pad_peer_query (dec->srcpad, query)) {
Wim Taymans's avatar
Wim Taymans committed
879 880
    GST_DEBUG_OBJECT (dec, "got downstream ALLOCATION hints");
    /* we got configuration from our peer, parse them */
881 882
    gst_query_parse_allocation_params (query, &size, &min, &max, &prefix,
        &alignment, &pool);
883 884 885 886

    /* 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
887 888
  } else {
    GST_DEBUG_OBJECT (dec, "didn't get downstream ALLOCATION hints");
889
    size = 0;
890 891
    min = max = 0;
    prefix = 0;
Wim Taymans's avatar
Wim Taymans committed
892
    alignment = 0;
893
    pool = NULL;
894
    dec->has_cropping = FALSE;
Wim Taymans's avatar
Wim Taymans committed
895
  }
896
  GST_DEBUG_OBJECT (dec, "downstream cropping %d", dec->has_cropping);
Wim Taymans's avatar
Wim Taymans committed
897 898 899

  if (pool == NULL) {
    /* we did not get a pool, make one ourselves then */
900
    pool = gst_video_buffer_pool_new ();
Wim Taymans's avatar
Wim Taymans committed
901 902 903 904 905 906
  }

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

907
  if (dec->has_cropping) {
908
    dec->vinfo = info;
909 910 911
    /* 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);
912
    caps = gst_video_info_to_caps (&info);
913 914
  } else {
    /* no cropping, use cropped videoinfo */
915
    dec->vinfo = cinfo;
916
  }
917
  size = MAX (size, GST_VIDEO_INFO_SIZE (&dec->vinfo));
918

919 920
  config = gst_buffer_pool_get_config (pool);
  gst_buffer_pool_config_set (config, caps, size, min, max, prefix, alignment);
921 922
  gst_caps_unref (caps);

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

Wim Taymans's avatar
Wim Taymans committed
928
  gst_buffer_pool_set_config (pool, config);
Wim Taymans's avatar
Wim Taymans committed
929 930 931
  /* and activate */
  gst_buffer_pool_set_active (pool, TRUE);

Wim Taymans's avatar
Wim Taymans committed
932 933
  gst_query_unref (query);

Wim Taymans's avatar
Wim Taymans committed
934
  return GST_FLOW_OK;
935 936 937 938 939 940 941

  /* ERRORS */
invalid_format:
  {
    GST_ERROR_OBJECT (dec, "Invalid pixel format %d", dec->info.pixel_fmt);
    return GST_FLOW_ERROR;
  }
Wim Taymans's avatar
Wim Taymans committed
942 943
}

944 945 946
static GstFlowReturn
theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
{
947
  GstFlowReturn ret = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
948
  GList *walk;
949

950 951
  if ((ret = theora_negotiate (dec)) != GST_FLOW_OK)
    goto negotiate_failed;
Wim Taymans's avatar
Wim Taymans committed
952

Wim Taymans's avatar
Wim Taymans committed
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972
  /* done */
  dec->decoder = th_decode_alloc (&dec->info, dec->setup);

  if (th_decode_ctl (dec->decoder, TH_DECCTL_SET_TELEMETRY_MV,
          &dec->telemetry_mv, sizeof (dec->telemetry_mv)) != TH_EIMPL) {
    GST_WARNING_OBJECT (dec, "Could not enable MV visualisation");
  }
  if (th_decode_ctl (dec->decoder, TH_DECCTL_SET_TELEMETRY_MBMODE,
          &dec->telemetry_mbmode, sizeof (dec->telemetry_mbmode)) != TH_EIMPL) {
    GST_WARNING_OBJECT (dec, "Could not enable MB mode visualisation");
  }
  if (th_decode_ctl (dec->decoder, TH_DECCTL_SET_TELEMETRY_QI,
          &dec->telemetry_qi, sizeof (dec->telemetry_qi)) != TH_EIMPL) {
    GST_WARNING_OBJECT (dec, "Could not enable QI mode visualisation");
  }
  if (th_decode_ctl (dec->decoder, TH_DECCTL_SET_TELEMETRY_BITS,
          &dec->telemetry_bits, sizeof (dec->telemetry_bits)) != TH_EIMPL) {
    GST_WARNING_OBJECT (dec, "Could not enable BITS mode visualisation");
  }

Wim Taymans's avatar
Wim Taymans committed