gsttheoradec.c 45.1 KB
Newer Older
<
Benjamin Otte's avatar
Benjamin Otte committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* GStreamer
 * Copyright (C) 2004 Benjamin Otte <in7y118@public.uni-hamburg.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

Wim Taymans's avatar
Wim Taymans committed
20 21 22 23 24 25 26 27
/**
 * SECTION:element-theoradec
 * @see_also: theoraenc, oggdemux
 *
 * This element decodes theora streams into raw video
 * <ulink url="http://www.theora.org/">Theora</ulink> is a royalty-free
 * video codec maintained by the <ulink url="http://www.xiph.org/">Xiph.org
 * Foundation</ulink>, based on the VP3 codec.
28 29
 *
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
30
 * <title>Example pipeline</title>
31
 * |[
Wim Taymans's avatar
Wim Taymans committed
32
 * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! xvimagesink
33
 * ]| This example pipeline will decode an ogg stream and decodes the theora video. Refer to
Wim Taymans's avatar
Wim Taymans committed
34 35 36 37 38 39
 * the theoraenc example to create the ogg file.
 * </refsect2>
 *
 * Last reviewed on 2006-03-01 (0.10.4)
 */

Benjamin Otte's avatar
Benjamin Otte committed
40 41 42 43
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

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

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

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

57 58
enum
{
59
  PROP_0,
60 61 62 63 64
  PROP_CROP,
  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 71
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-yuv, "
72
        "format = (fourcc) { 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

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

86 87 88 89 90
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);

91
static gboolean theora_dec_sink_event (GstPad * pad, GstEvent * event);
92
static gboolean theora_dec_setcaps (GstPad * pad, GstCaps * caps);
93
static GstFlowReturn theora_dec_chain (GstPad * pad, GstBuffer * buffer);
94 95
static GstStateChangeReturn theora_dec_change_state (GstElement * element,
    GstStateChange transition);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
96
static gboolean theora_dec_src_event (GstPad * pad, GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
97
static gboolean theora_dec_src_query (GstPad * pad, GstQuery * query);
98 99 100
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
101 102

#if 0
103
static const GstFormat *theora_get_formats (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
104 105
#endif
#if 0
106
static const GstEventMask *theora_get_event_masks (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
107
#endif
108
static const GstQueryType *theora_get_query_types (GstPad * pad);
Benjamin Otte's avatar
Benjamin Otte committed
109 110 111 112 113 114


static void
gst_theora_dec_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
115 116

  gst_element_class_add_pad_template (element_class,
Benjamin Otte's avatar
Benjamin Otte committed
117 118 119
      gst_static_pad_template_get (&theora_dec_src_factory));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&theora_dec_sink_factory));
120 121 122 123
  gst_element_class_set_details_simple (element_class,
      "Theora video decoder", "Codec/Decoder/Video",
      "decode raw theora streams to raw YUV video",
      "Benjamin Otte <otte@gnome.org>, Wim Taymans <wim@fluendo.com>");
Benjamin Otte's avatar
Benjamin Otte committed
124 125
}

126 127 128 129 130 131 132
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
133
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134
gst_theora_dec_class_init (GstTheoraDecClass * klass)
Benjamin Otte's avatar
Benjamin Otte committed
135
{
136
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Benjamin Otte's avatar
Benjamin Otte committed
137 138
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);

139 140 141
  gobject_class->set_property = theora_dec_set_property;
  gobject_class->get_property = theora_dec_get_property;

142
  g_object_class_install_property (gobject_class, PROP_CROP,
143 144
      g_param_spec_boolean ("crop", "Crop",
          "Crop the image to the visible region", THEORA_DEF_CROP,
145
          (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
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 179 180 181 182 183 184 185 186 187 188 189
  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));
  }
190

Benjamin Otte's avatar
Benjamin Otte committed
191
  gstelement_class->change_state = theora_dec_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
192 193

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

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

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

215
  dec->crop = THEORA_DEF_CROP;
216 217 218 219
  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;
220 221
  dec->gather = NULL;
  dec->decode = NULL;
222
  dec->queued = NULL;
Wim Taymans's avatar
Wim Taymans committed
223
  dec->pendingevents = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
224
}
225

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

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

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

  if (dec->tags) {
    gst_tag_list_free (dec->tags);
    dec->tags = NULL;
  }
260 261
}

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

Wim Taymans's avatar
Wim Taymans committed
282
#if 0
283 284 285 286 287 288 289 290 291 292
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
293
#endif
294 295 296 297 298 299

static const GstQueryType *
theora_get_query_types (GstPad * pad)
{
  static const GstQueryType theora_src_query_types[] = {
    GST_QUERY_POSITION,
300 301
    GST_QUERY_DURATION,
    GST_QUERY_CONVERT,
302 303 304 305 306 307 308
    0
  };

  return theora_src_query_types;
}


309
static gboolean
310 311 312
theora_dec_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
313
{
314 315 316 317
  gboolean res = TRUE;
  GstTheoraDec *dec;
  guint64 scale = 1;

Wim Taymans's avatar
Wim Taymans committed
318 319 320 321 322
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

323 324 325 326 327 328
  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;

329 330 331 332
  switch (src_format) {
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
333
          *dest_value = gst_util_uint64_scale_int (src_value, 8,
334
              dec->info.pic_height * dec->info.pic_width * dec->output_bpp);
335 336 337 338 339 340 341
          break;
        case GST_FORMAT_TIME:
          /* seems like a rather silly conversion, implement me if you like */
        default:
          res = FALSE;
      }
      break;
342
    case GST_FORMAT_TIME:
343 344
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
345 346 347
          scale =
              dec->output_bpp * (dec->info.pic_width * dec->info.pic_height) /
              8;
348
        case GST_FORMAT_DEFAULT:
349
          *dest_value = scale * gst_util_uint64_scale (src_value,
350
              dec->info.fps_numerator, dec->info.fps_denominator * GST_SECOND);
351 352 353 354
          break;
        default:
          res = FALSE;
      }
355 356
      break;
    case GST_FORMAT_DEFAULT:
357 358
      switch (*dest_format) {
        case GST_FORMAT_TIME:
359
          *dest_value = gst_util_uint64_scale (src_value,
360
              GST_SECOND * dec->info.fps_denominator, dec->info.fps_numerator);
361 362
          break;
        case GST_FORMAT_BYTES:
363
          *dest_value = gst_util_uint64_scale_int (src_value,
364
              dec->output_bpp * dec->info.pic_width * dec->info.pic_height, 8);
365 366 367 368
          break;
        default:
          res = FALSE;
      }
369 370
      break;
    default:
371
      res = FALSE;
372
  }
373 374
done:
  gst_object_unref (dec);
375
  return res;
376 377 378 379 380 381 382 383

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

Wim Taymans's avatar
Wim Taymans committed
386
#if 0
Benjamin Otte's avatar
Benjamin Otte committed
387
static gboolean
388 389 390
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
391
{
392 393
  gboolean res = TRUE;
  GstTheoraDec *dec;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
394

Wim Taymans's avatar
Wim Taymans committed
395 396 397 398 399
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

400 401 402 403 404 405
  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;

406
  switch (src_format) {
Benjamin Otte's avatar
Benjamin Otte committed
407
    case GST_FORMAT_DEFAULT:
408 409
      switch (*dest_format) {
        case GST_FORMAT_TIME:
410
          *dest_value = _theora_granule_start_time (dec, src_value);
411 412 413 414
          break;
        default:
          res = FALSE;
      }
415
      break;
416 417 418 419
    case GST_FORMAT_TIME:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
        {
420
          guint rest;
421

422
          /* framecount */
423
          *dest_value = gst_util_uint64_scale (src_value,
424
              dec->info.fps_numerator, GST_SECOND * dec->info.fps_denominator);
425 426

          /* funny way of calculating granulepos in theora */
427
          rest = *dest_value / dec->info.keyframe_granule_shift;
428
          *dest_value -= rest;
429
          *dest_value <<= dec->granule_shift;
430
          *dest_value += rest;
431 432 433 434 435 436 437
          break;
        }
        default:
          res = FALSE;
          break;
      }
      break;
Benjamin Otte's avatar
Benjamin Otte committed
438
    default:
439
      res = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
440
  }
441 442
done:
  gst_object_unref (dec);
443
  return res;
444 445 446 447 448 449 450 451

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

static gboolean
Wim Taymans's avatar
Wim Taymans committed
456
theora_dec_src_query (GstPad * pad, GstQuery * query)
457
{
458 459
  GstTheoraDec *dec;

Wim Taymans's avatar
Wim Taymans committed
460 461
  gboolean res = FALSE;

462 463
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

Wim Taymans's avatar
Wim Taymans committed
464 465 466
  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
    {
Wim Taymans's avatar
Wim Taymans committed
467
      gint64 value;
Wim Taymans's avatar
Wim Taymans committed
468
      GstFormat format;
Wim Taymans's avatar
Wim Taymans committed
469 470
      gint64 time;

Wim Taymans's avatar
Wim Taymans committed
471 472
      /* parse format */
      gst_query_parse_position (query, &format, NULL);
473

Wim Taymans's avatar
Wim Taymans committed
474
      time = dec->last_timestamp;
475
      time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
476

477 478 479
      GST_LOG_OBJECT (dec,
          "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));

Wim Taymans's avatar
Wim Taymans committed
480
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
481 482
              theora_dec_src_convert (pad, GST_FORMAT_TIME, time, &format,
                  &value)))
Wim Taymans's avatar
Wim Taymans committed
483 484
        goto error;

Wim Taymans's avatar
Wim Taymans committed
485
      gst_query_set_position (query, format, value);
Wim Taymans's avatar
Wim Taymans committed
486 487

      GST_LOG_OBJECT (dec,
488 489
          "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value,
          format);
Wim Taymans's avatar
Wim Taymans committed
490 491
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
492
    case GST_QUERY_DURATION:
493
    {
Wim Taymans's avatar
Wim Taymans committed
494
      /* forward to peer for total */
Wim Taymans's avatar
Wim Taymans committed
495
      res = gst_pad_peer_query (dec->sinkpad, query);
496
      if (!res)
Wim Taymans's avatar
Wim Taymans committed
497
        goto error;
498

Wim Taymans's avatar
Wim Taymans committed
499
      break;
500
    }
Wim Taymans's avatar
Wim Taymans committed
501 502 503 504 505 506
    case GST_QUERY_CONVERT:
    {
      GstFormat src_fmt, dest_fmt;
      gint64 src_val, dest_val;

      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
507
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
508 509
              theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
510 511 512
        goto error;

      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
Wim Taymans's avatar
Wim Taymans committed
513 514 515
      break;
    }
    default:
516
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
517
      break;
518
  }
519 520 521
done:
  gst_object_unref (dec);

522
  return res;
523

524
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
525
error:
526 527 528 529
  {
    GST_DEBUG_OBJECT (dec, "query failed");
    goto done;
  }
Wim Taymans's avatar
Wim Taymans committed
530 531
}

Benjamin Otte's avatar
Benjamin Otte committed
532
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
533
theora_dec_src_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
534 535 536 537
{
  gboolean res = TRUE;
  GstTheoraDec *dec;

538
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
Benjamin Otte's avatar
Benjamin Otte committed
539 540

  switch (GST_EVENT_TYPE (event)) {
541 542
    case GST_EVENT_SEEK:
    {
543 544
      GstFormat format, tformat;
      gdouble rate;
Scott Wheeler Wheeler's avatar
Scott Wheeler Wheeler committed
545
      GstEvent *real_seek;
546 547 548 549
      GstSeekFlags flags;
      GstSeekType cur_type, stop_type;
      gint64 cur, stop;
      gint64 tcur, tstop;
Wim Taymans's avatar
Wim Taymans committed
550
      guint32 seqnum;
551 552 553

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

557 558 559 560 561 562
      /* 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 
       */
563 564
      tformat = GST_FORMAT_TIME;
      if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur)))
565
        goto convert_error;
566
      if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop)))
567
        goto convert_error;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
568

569
      /* then seek with time on the peer */
570 571
      real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
          flags, cur_type, tcur, stop_type, tstop);
Wim Taymans's avatar
Wim Taymans committed
572
      gst_event_set_seqnum (real_seek, seqnum);
573

574
      res = gst_pad_push_event (dec->sinkpad, real_seek);
Benjamin Otte's avatar
Benjamin Otte committed
575 576
      break;
    }
577 578 579 580 581 582 583 584 585 586
    case GST_EVENT_QOS:
    {
      gdouble proportion;
      GstClockTimeDiff diff;
      GstClockTime timestamp;

      gst_event_parse_qos (event, &proportion, &diff, &timestamp);

      /* we cannot randomly skip frame decoding since we don't have
       * B frames. we can however use the timestamp and diff to not
587 588
       * push late frames. This would at least save us the time to
       * crop/memcpy the data. */
589 590 591 592 593
      GST_OBJECT_LOCK (dec);
      dec->proportion = proportion;
      dec->earliest_time = timestamp + diff;
      GST_OBJECT_UNLOCK (dec);

594 595 596
      GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
          GST_TIME_ARGS (timestamp), diff);

597
      res = gst_pad_push_event (dec->sinkpad, event);
598 599
      break;
    }
Benjamin Otte's avatar
Benjamin Otte committed
600
    default:
601
      res = gst_pad_push_event (dec->sinkpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
602 603
      break;
  }
604 605
done:
  gst_object_unref (dec);
Benjamin Otte's avatar
Benjamin Otte committed
606 607

  return res;
608

609 610 611 612 613 614
  /* ERRORS */
convert_error:
  {
    GST_DEBUG_OBJECT (dec, "could not convert format");
    goto done;
  }
615 616
}

617 618
static gboolean
theora_dec_sink_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
619
{
620
  gboolean ret = FALSE;
621 622
  GstTheoraDec *dec;

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

Benjamin Otte's avatar
Benjamin Otte committed
625 626
  GST_LOG_OBJECT (dec, "handling event");
  switch (GST_EVENT_TYPE (event)) {
627 628 629 630 631 632 633
    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
634 635 636
    case GST_EVENT_EOS:
      ret = gst_pad_push_event (dec->srcpad, event);
      break;
637
    case GST_EVENT_NEWSEGMENT:
638
    {
639
      gboolean update;
640
      GstFormat format;
641
      gdouble rate, arate;
Wim Taymans's avatar
Wim Taymans committed
642
      gint64 start, stop, time;
643

644 645
      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
          &start, &stop, &time);
646

647
      /* we need TIME format */
648 649 650
      if (format != GST_FORMAT_TIME)
        goto newseg_wrong_format;

651 652 653 654 655 656
      GST_DEBUG_OBJECT (dec,
          "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
          ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
          update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
          GST_TIME_ARGS (time));

657
      /* now configure the values */
658 659
      gst_segment_set_newsegment_full (&dec->segment, update,
          rate, arate, format, start, stop, time);
Wim Taymans's avatar
Wim Taymans committed
660
      dec->seqnum = gst_event_get_seqnum (event);
661

Wim Taymans's avatar
Wim Taymans committed
662
      /* We don't forward this unless/until the decoder is initialised */
663 664 665
      if (dec->have_header) {
        ret = gst_pad_push_event (dec->srcpad, event);
      } else {
Wim Taymans's avatar
Wim Taymans committed
666
        dec->pendingevents = g_list_append (dec->pendingevents, event);
667 668
        ret = TRUE;
      }
Benjamin Otte's avatar
Benjamin Otte committed
669
      break;
670
    }
671 672 673 674 675 676 677 678 679 680 681 682
    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
683
    default:
Wim Taymans's avatar
Wim Taymans committed
684
      ret = gst_pad_push_event (dec->srcpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
685 686
      break;
  }
687
done:
Wim Taymans's avatar
Wim Taymans committed
688 689
  gst_object_unref (dec);

690
  return ret;
691 692 693 694

  /* ERRORS */
newseg_wrong_format:
  {
695
    GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
696
    gst_event_unref (event);
697 698
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
699 700
}

701 702 703 704 705
static gboolean
theora_dec_setcaps (GstPad * pad, GstCaps * caps)
{
  GstTheoraDec *dec;
  GstStructure *s;
706
  const GValue *codec_data;
707 708 709 710 711 712 713 714 715

  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

  s = gst_caps_get_structure (caps, 0);

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

716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
  if ((codec_data = gst_structure_get_value (s, "codec_data"))) {
    if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
      GstBuffer *buffer;
      guint8 *data;
      guint size;
      guint offset;

      buffer = gst_value_get_buffer (codec_data);

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

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

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

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

        buf = gst_buffer_create_sub (buffer, offset, psize);

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

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

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

759 760 761 762 763
  gst_object_unref (dec);

  return TRUE;
}

764
static GstFlowReturn
765
theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet)
Benjamin Otte's avatar
Benjamin Otte committed
766
{
767
  gchar *encoder = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
768
  GstBuffer *buf;
769 770
  GstTagList *list;

771
  GST_DEBUG_OBJECT (dec, "parsing comment packet");
772

773 774 775
  buf = gst_buffer_new ();
  GST_BUFFER_SIZE (buf) = packet->bytes;
  GST_BUFFER_DATA (buf) = packet->packet;
776

777 778
  list =
      gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\201theora", 7,
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
      &encoder);

  gst_buffer_unref (buf);

  if (!list) {
    GST_ERROR_OBJECT (dec, "couldn't decode comments");
    list = gst_tag_list_new ();
  }
  if (encoder) {
    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
        GST_TAG_ENCODER, encoder, NULL);
    g_free (encoder);
  }
  gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
      GST_TAG_ENCODER_VERSION, dec->info.version_major,
      GST_TAG_VIDEO_CODEC, "Theora", NULL);

796 797 798 799 800 801
  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);
  }

802
  dec->tags = list;
803 804 805 806 807 808 809 810 811

  return GST_FLOW_OK;
}

static GstFlowReturn
theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstCaps *caps;
  gint par_num, par_den;
812
  GstFlowReturn ret = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
813
  GList *walk;
814
  guint32 fourcc;
815 816 817 818 819 820 821

  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;
822
   * 0:x and x:0 are allowed and can be interpreted as 1:1.
823
   */
824 825 826 827 828 829 830 831 832 833
  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;
  }
834
  if (par_num == 0 || par_den == 0) {
835 836 837 838 839
    par_num = par_den = 1;
  }
  /* theora has:
   *
   *  width/height : dimension of the encoded frame 
840 841
   *  pic_width/pic_height : dimension of the visible part
   *  pic_x/pic_y : offset in encoded frame where visible part starts
842
   */
843 844
  GST_DEBUG_OBJECT (dec, "dimension %dx%d, PAR %d/%d", dec->info.pic_width,
      dec->info.pic_height, par_num, par_den);
845
  GST_DEBUG_OBJECT (dec, "frame dimension %dx%d, offset %d:%d",
846 847
      dec->info.pic_width, dec->info.pic_height,
      dec->info.pic_x, dec->info.pic_y);
848

849
  if (dec->info.pixel_fmt == TH_PF_420) {
850 851
    dec->output_bpp = 12;       /* Average bits per pixel. */
    fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0');
852
  } else if (dec->info.pixel_fmt == TH_PF_422) {
853
    dec->output_bpp = 16;
854
    fourcc = GST_MAKE_FOURCC ('Y', '4', '2', 'B');
855
  } else if (dec->info.pixel_fmt == TH_PF_444) {
856 857 858
    dec->output_bpp = 24;
    fourcc = GST_MAKE_FOURCC ('Y', '4', '4', '4');
  } else {
859
    GST_ERROR_OBJECT (dec, "Invalid pixel format %d", dec->info.pixel_fmt);
860 861
    return GST_FLOW_ERROR;
  }
862 863

  if (dec->crop) {
864 865
    dec->width = dec->info.pic_width;
    dec->height = dec->info.pic_height;
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
    dec->offset_x = dec->info.pic_x;
    dec->offset_y = dec->info.pic_y;
    /* Ensure correct offsets in chroma for formats that need it
     * by rounding the offset. libtheora will add proper pixels,
     * so no need to handle them ourselves. */
    if (dec->offset_x & 1 && dec->info.pixel_fmt != TH_PF_444) {
      dec->offset_x--;
      dec->width++;
    }
    if (dec->offset_y & 1 && dec->info.pixel_fmt == TH_PF_420) {
      dec->offset_y--;
      dec->height++;
    }
  } else {
    /* no cropping, use the encoded dimensions */
    dec->width = dec->info.frame_width;
    dec->height = dec->info.frame_height;
883 884 885 886 887 888 889 890
    dec->offset_x = 0;
    dec->offset_y = 0;
  }

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

  /* done */
891
  dec->decoder = th_decode_alloc (&dec->info, dec->setup);
892

893
  if (th_decode_ctl (dec->decoder, TH_DECCTL_SET_TELEMETRY_MV,
894
          &dec->telemetry_mv, sizeof (dec->telemetry_mv)) != TH_EIMPL) {
895
    GST_WARNING_OBJECT (dec, "Could not enable MV visualisation");
896
  }
897
  if (th_decode_ctl (dec->decoder, TH_DECCTL_SET_TELEMETRY_MBMODE,
898
          &dec->telemetry_mbmode, sizeof (dec->telemetry_mbmode)) != TH_EIMPL) {
899
    GST_WARNING_OBJECT (dec, "Could not enable MB mode visualisation");
900
  }
901
  if (th_decode_ctl (dec->decoder, TH_DECCTL_SET_TELEMETRY_QI,
902
          &dec->telemetry_qi, sizeof (dec->telemetry_qi)) != TH_EIMPL) {
903
    GST_WARNING_OBJECT (dec, "Could not enable QI mode visualisation");
904
  }
905
  if (th_decode_ctl (dec->decoder, TH_DECCTL_SET_TELEMETRY_BITS,
906
          &dec->telemetry_bits, sizeof (dec->telemetry_bits)) != TH_EIMPL) {
907
    GST_WARNING_OBJECT (dec, "Could not enable BITS mode visualisation");
908
  }
909

910
  caps = gst_caps_new_simple ("video/x-raw-yuv",
911
      "format", GST_TYPE_FOURCC, fourcc,
912 913
      "framerate", GST_TYPE_FRACTION,
      dec->info.fps_numerator, dec->info.fps_denominator,
914
      "pixel-aspect-ratio", GST_TYPE_FRACTION, par_num, par_den,
915
      "width", G_TYPE_INT, dec->width, "height", G_TYPE_INT, dec->height,
916 917
      "color-matrix", G_TYPE_STRING, "sdtv",
      "chroma-site", G_TYPE_STRING, "jpeg", NULL);
918 919 920
  gst_pad_set_caps (dec->srcpad, caps);
  gst_caps_unref (caps);

Wim Taymans's avatar
Wim Taymans committed
921
  dec->have_header = TRUE;
Wim Taymans's avatar
Wim Taymans committed
922 923 924 925 926 927

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

930 931 932 933 934 935 936
  if (dec->tags) {
    gst_element_found_tags_for_pad (GST_ELEMENT_CAST (dec), dec->srcpad,
        dec->tags);
    dec->tags = NULL;
  }

  return ret;
937 938 939 940 941 942
}

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

945
  GST_DEBUG_OBJECT (dec, "parsing header packet");
946

947 948
  ret = th_decode_headerin (&dec->info, &dec->comment, &dec->setup, packet);
  if (ret < 0)
949 950
    goto header_read_error;

Wim Taymans's avatar
Wim Taymans committed
951 952
  switch (packet->packet[0]) {
    case 0x81:
953 954
      res = theora_handle_comment_packet (dec, packet);
      break;
Wim Taymans's avatar
Wim Taymans committed
955
    case 0x82:
956 957 958 959
      res = theora_handle_type_packet (dec, packet);
      break;
    default:
      /* ignore */
Wim Taymans's avatar
Wim Taymans committed
960 961 962
      g_warning ("unknown theora header packet found");
    case 0x80:
      /* nothing special, this is the identification header */
963 964 965 966 967 968 969 970 971 972 973 974 975 976
      res = GST_FLOW_OK;
      break;
  }
  return res;

  /* ERRORS */
header_read_error:
  {
    GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
        (NULL), ("couldn't read header packet"));
    return GST_FLOW_ERROR;
  }
}

977 978 979 980 981
/* returns TRUE if buffer is within segment, else FALSE.
 * if Buffer is on segment border, it's timestamp and duration will be clipped */
static gboolean
clip_buffer (GstTheoraDec * dec, GstBuffer * buf)
{
982 983
  gboolean res = TRUE;
  GstClockTime in_ts, in_dur, stop;
984 985
  gint64 cstart, cstop;

986 987 988
  in_ts = GST_BUFFER_TIMESTAMP (buf);
  in_dur = GST_BUFFER_DURATION (buf);

989 990
  GST_LOG_OBJECT (dec,
      "timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT,
991
      GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur));