theoradec.c 24 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 20 21 22 23 24 25 26 27 28
/* 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.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <gst/gst.h>
#include <theora/theora.h>
#include <string.h>
#include <gst/tag/tag.h>

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
29 30
GST_DEBUG_CATEGORY (theoradec_debug);
#define GST_CAT_DEFAULT theoradec_debug
Benjamin Otte's avatar
Benjamin Otte committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

#define GST_TYPE_THEORA_DEC \
  (gst_theora_dec_get_type())
#define GST_THEORA_DEC(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_THEORA_DEC,GstTheoraDec))
#define GST_THEORA_DEC_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_THEORA_DEC,GstTheoraDec))
#define GST_IS_THEORA_DEC(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_THEORA_DEC))
#define GST_IS_THEORA_DEC_CLASS(obj) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_THEORA_DEC))

typedef struct _GstTheoraDec GstTheoraDec;
typedef struct _GstTheoraDecClass GstTheoraDecClass;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
46 47 48 49 50 51 52 53 54 55 56 57 58
struct _GstTheoraDec
{
  GstElement element;

  GstPad *sinkpad;
  GstPad *srcpad;

  theora_state state;
  theora_info info;
  theora_comment comment;

  guint packetno;
  guint64 granulepos;
59

60
  gboolean initialized;
61
  gboolean need_keyframe;
62 63
  gint width, height;
  gint offset_x, offset_y;
64 65

  gboolean crop;
Benjamin Otte's avatar
Benjamin Otte committed
66 67
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68 69
struct _GstTheoraDecClass
{
Benjamin Otte's avatar
Benjamin Otte committed
70 71 72
  GstElementClass parent_class;
};

73 74 75 76 77 78 79
#define THEORA_DEF_CROP		TRUE
enum
{
  ARG_0,
  ARG_CROP
};

Benjamin Otte's avatar
Benjamin Otte committed
80 81
static GstElementDetails theora_dec_details = {
  "TheoraDec",
82
  "Codec/Decoder/Video",
Benjamin Otte's avatar
Benjamin Otte committed
83
  "decode raw theora streams to raw YUV video",
84 85
  "Benjamin Otte <in7y118@public.uni-hamburg.de>, "
      "Wim Taymans <wim@fluendo.com>",
Benjamin Otte's avatar
Benjamin Otte committed
86 87 88
};

static GstStaticPadTemplate theora_dec_src_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
89 90 91 92
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-yuv, "
93 94 95
        "format = (fourcc) I420, "
        "framerate = (double) [0, MAX], "
        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
96
    );
Benjamin Otte's avatar
Benjamin Otte committed
97 98

static GstStaticPadTemplate theora_dec_sink_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
99 100 101 102 103
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-theora")
    );
Benjamin Otte's avatar
Benjamin Otte committed
104 105

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

107 108 109 110 111
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);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
112 113 114 115 116
static void theora_dec_chain (GstPad * pad, GstData * data);
static GstElementStateReturn theora_dec_change_state (GstElement * element);
static gboolean theora_dec_src_event (GstPad * pad, GstEvent * event);
static gboolean theora_dec_src_query (GstPad * pad,
    GstQueryType query, GstFormat * format, gint64 * value);
117 118 119 120 121 122 123 124 125
static gboolean theora_dec_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value);
static gboolean theora_dec_sink_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value);
static const GstFormat *theora_get_formats (GstPad * pad);
static const GstEventMask *theora_get_event_masks (GstPad * pad);
static const GstQueryType *theora_get_query_types (GstPad * pad);
Benjamin Otte's avatar
Benjamin Otte committed
126 127 128 129 130 131


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
132 133

  gst_element_class_add_pad_template (element_class,
Benjamin Otte's avatar
Benjamin Otte committed
134 135 136 137 138 139 140
      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));
  gst_element_class_set_details (element_class, &theora_dec_details);
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
141
gst_theora_dec_class_init (GstTheoraDecClass * klass)
Benjamin Otte's avatar
Benjamin Otte committed
142
{
143
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Benjamin Otte's avatar
Benjamin Otte committed
144 145
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);

146 147 148 149 150 151 152 153
  gobject_class->set_property = theora_dec_set_property;
  gobject_class->get_property = theora_dec_get_property;

  g_object_class_install_property (gobject_class, ARG_CROP,
      g_param_spec_boolean ("crop", "Crop",
          "Crop the image to the visible region", THEORA_DEF_CROP,
          (GParamFlags) G_PARAM_READWRITE));

Benjamin Otte's avatar
Benjamin Otte committed
154
  gstelement_class->change_state = theora_dec_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
155 156

  GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder");
Benjamin Otte's avatar
Benjamin Otte committed
157 158 159
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
160
gst_theora_dec_init (GstTheoraDec * dec)
Benjamin Otte's avatar
Benjamin Otte committed
161
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162 163 164
  dec->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get
      (&theora_dec_sink_factory), "sink");
165 166
  gst_pad_set_formats_function (dec->sinkpad, theora_get_formats);
  gst_pad_set_convert_function (dec->sinkpad, theora_dec_sink_convert);
Benjamin Otte's avatar
Benjamin Otte committed
167 168 169
  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
170 171 172
  dec->srcpad =
      gst_pad_new_from_template (gst_static_pad_template_get
      (&theora_dec_src_factory), "src");
Benjamin Otte's avatar
Benjamin Otte committed
173
  gst_pad_use_explicit_caps (dec->srcpad);
174
  gst_pad_set_event_mask_function (dec->srcpad, theora_get_event_masks);
Benjamin Otte's avatar
Benjamin Otte committed
175
  gst_pad_set_event_function (dec->srcpad, theora_dec_src_event);
176
  gst_pad_set_query_type_function (dec->srcpad, theora_get_query_types);
177
  gst_pad_set_query_function (dec->srcpad, theora_dec_src_query);
178 179 180
  gst_pad_set_formats_function (dec->srcpad, theora_get_formats);
  gst_pad_set_convert_function (dec->srcpad, theora_dec_src_convert);

Benjamin Otte's avatar
Benjamin Otte committed
181 182 183
  gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);

  GST_FLAG_SET (dec, GST_ELEMENT_EVENT_AWARE);
184 185

  dec->crop = THEORA_DEF_CROP;
Benjamin Otte's avatar
Benjamin Otte committed
186
}
187 188

/* FIXME: copy from libtheora, theora should somehow make this available for seeking */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
189 190 191 192 193 194
static int
_theora_ilog (unsigned int v)
{
  int ret = 0;

  while (v) {
195
    ret++;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
196
    v >>= 1;
197
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
198
  return (ret);
199 200
}

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
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);
}

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

static const GstQueryType *
theora_get_query_types (GstPad * pad)
{
  static const GstQueryType theora_src_query_types[] = {
    GST_QUERY_TOTAL,
    GST_QUERY_POSITION,
    0
  };

  return theora_src_query_types;
}


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

  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
253

254
  /* we need the info part before we can done something */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
255 256 257
  if (dec->packetno < 1)
    return FALSE;

258 259 260 261 262 263 264 265 266 267 268 269 270
  switch (src_format) {
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
          *dest_value =
              src_value * 2 / (dec->info.height * dec->info.width * 3);
          break;
        case GST_FORMAT_TIME:
          /* seems like a rather silly conversion, implement me if you like */
        default:
          res = FALSE;
      }
      break;
271
    case GST_FORMAT_TIME:
272 273
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
274
          scale = 3 * (dec->info.width * dec->info.height) / 2;
275
        case GST_FORMAT_DEFAULT:
276 277 278
          *dest_value =
              scale * (((guint64) src_value * dec->info.fps_numerator) /
              (dec->info.fps_denominator * GST_SECOND));
279 280 281 282
          break;
        default:
          res = FALSE;
      }
283 284
      break;
    case GST_FORMAT_DEFAULT:
285 286 287 288 289 290 291 292 293 294 295 296
      switch (*dest_format) {
        case GST_FORMAT_TIME:
          *dest_value = src_value * (GST_SECOND * dec->info.fps_denominator /
              dec->info.fps_numerator);
          break;
        case GST_FORMAT_BYTES:
          *dest_value =
              src_value * 3 * (dec->info.width * dec->info.height) / 2;
          break;
        default:
          res = FALSE;
      }
297 298
      break;
    default:
299
      res = FALSE;
300
  }
301 302

  return res;
303 304
}

Benjamin Otte's avatar
Benjamin Otte committed
305
static gboolean
306 307 308
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
309
{
310 311
  gboolean res = TRUE;
  GstTheoraDec *dec;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
312

313 314 315
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

  /* we need the info part before we can done something */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
316 317 318
  if (dec->packetno < 1)
    return FALSE;

319
  switch (src_format) {
Benjamin Otte's avatar
Benjamin Otte committed
320
    case GST_FORMAT_DEFAULT:
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
    {
      guint64 framecount;
      guint ilog;

      ilog = _theora_ilog (dec->info.keyframe_frequency_force - 1);

      /* granulepos is last ilog bits for counting pframes since last iframe and 
       * bits in front of that for the framenumber of the last iframe. */
      framecount = src_value >> ilog;
      framecount += src_value - (framecount << ilog);

      switch (*dest_format) {
        case GST_FORMAT_TIME:
          *dest_value = framecount * (GST_SECOND * dec->info.fps_denominator /
              dec->info.fps_numerator);
          break;
        default:
          res = FALSE;
      }
340
      break;
341
    }
Benjamin Otte's avatar
Benjamin Otte committed
342
    default:
343
      res = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
344
  }
345 346

  return res;
Benjamin Otte's avatar
Benjamin Otte committed
347
}
348 349

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
350 351
theora_dec_src_query (GstPad * pad, GstQueryType query, GstFormat * format,
    gint64 * value)
352 353 354 355
{
  gint64 granulepos;
  GstTheoraDec *dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
  GstFormat my_format = GST_FORMAT_DEFAULT;
356
  guint64 time;
357

358
  if (query == GST_QUERY_POSITION) {
359
    /* this is easy, we can convert a granule position to everything */
360 361
    granulepos = dec->granulepos;
  } else {
362
    /* for the total, we just forward the query to the peer */
363 364 365 366
    if (!gst_pad_query (GST_PAD_PEER (dec->sinkpad), query, &my_format,
            &granulepos))
      return FALSE;
  }
367

368 369 370
  /* and convert to the final format in two steps with time as the 
   * intermediate step */
  my_format = GST_FORMAT_TIME;
371
  if (!theora_dec_sink_convert (dec->sinkpad, GST_FORMAT_DEFAULT, granulepos,
372 373
          &my_format, &time))
    return FALSE;
374
  if (!gst_pad_convert (pad, my_format, time, format, value))
375 376
    return FALSE;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
377
  GST_LOG_OBJECT (dec,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
378
      "query %u: peer returned granulepos: %llu - we return %llu (format %u)",
379 380 381 382
      query, granulepos, *value, *format);
  return TRUE;
}

Benjamin Otte's avatar
Benjamin Otte committed
383
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
384
theora_dec_src_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
385 386 387
{
  gboolean res = TRUE;
  GstTheoraDec *dec;
388
  GstFormat format;
Benjamin Otte's avatar
Benjamin Otte committed
389 390 391 392

  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

  switch (GST_EVENT_TYPE (event)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
393
    case GST_EVENT_SEEK:{
Benjamin Otte's avatar
Benjamin Otte committed
394
      guint64 value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
395

396 397 398 399 400 401 402 403
      /* 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 
       */
      format = GST_FORMAT_TIME;
      res = gst_pad_convert (pad, GST_EVENT_SEEK_FORMAT (event),
404
          GST_EVENT_SEEK_OFFSET (event), &format, &value);
405 406
      if (!res)
        goto error;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
407

408 409 410 411 412 413 414 415 416 417 418 419 420
      /* then seek with time on the peer */
      GstEvent *real_seek = gst_event_new_seek (
          (GST_EVENT_SEEK_TYPE (event) & ~GST_SEEK_FORMAT_MASK) |
          format, value);

      res = gst_pad_send_event (GST_PAD_PEER (dec->sinkpad), real_seek);
      if (!res)
        goto error;

      /* all worked, make sure we sync to keyframe */
      dec->need_keyframe = TRUE;

    error:
Benjamin Otte's avatar
Benjamin Otte committed
421 422 423 424 425 426 427 428 429 430 431 432
      gst_event_unref (event);
      break;
    }
    default:
      res = gst_pad_event_default (pad, event);
      break;
  }

  return res;
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
433
theora_dec_event (GstTheoraDec * dec, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
434
{
435
  guint64 value, time, bytes;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
436

Benjamin Otte's avatar
Benjamin Otte committed
437 438 439 440
  GST_LOG_OBJECT (dec, "handling event");
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_DISCONTINUOUS:
      if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT, &value)) {
441 442
        dec->granulepos = value;
        GST_DEBUG_OBJECT (dec,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
443
            "setting granuleposition to %" G_GUINT64_FORMAT " after discont",
444
            value);
Benjamin Otte's avatar
Benjamin Otte committed
445
      } else {
446 447
        GST_WARNING_OBJECT (dec,
            "discont event didn't include offset, we might set it wrong now");
Benjamin Otte's avatar
Benjamin Otte committed
448
      }
449
      if (dec->packetno < 3) {
450 451 452 453 454 455 456
        if (dec->granulepos != 0)
          GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL),
              ("can't handle discont before parsing first 3 packets"));
        dec->packetno = 0;
        gst_pad_push (dec->srcpad, GST_DATA (gst_event_new_discontinuous (FALSE,
                    GST_FORMAT_TIME, (guint64) 0, GST_FORMAT_DEFAULT,
                    (guint64) 0, GST_FORMAT_BYTES, (guint64) 0, 0)));
457
      } else {
458 459 460 461 462 463
        GstFormat time_format, default_format, bytes_format;

        time_format = GST_FORMAT_TIME;
        default_format = GST_FORMAT_DEFAULT;
        bytes_format = GST_FORMAT_BYTES;

464
        /* if one of them works, all of them work */
465 466 467 468 469 470
        if (theora_dec_sink_convert (dec->sinkpad, GST_FORMAT_DEFAULT,
                dec->granulepos, &time_format, &time)
            && theora_dec_src_convert (dec->srcpad, GST_FORMAT_TIME, time,
                &default_format, &value)
            && theora_dec_src_convert (dec->srcpad, GST_FORMAT_TIME, time,
                &bytes_format, &bytes)) {
471 472 473 474
          gst_pad_push (dec->srcpad,
              GST_DATA (gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
                      time, GST_FORMAT_DEFAULT, value, GST_FORMAT_BYTES, bytes,
                      0)));
475 476
          /* store new framenumber */
          dec->packetno = value + 3;
477 478 479 480
        } else {
          GST_ERROR_OBJECT (dec,
              "failed to parse data for DISCONT event, not sending any");
        }
481 482
        /* sync to keyframe */
        dec->need_keyframe = TRUE;
483
      }
Benjamin Otte's avatar
Benjamin Otte committed
484 485 486 487 488 489 490
      break;
    default:
      break;
  }
  gst_pad_event_default (dec->sinkpad, event);
}

491 492 493 494
#define ROUND_UP_2(x) (((x) + 1) & ~1)
#define ROUND_UP_4(x) (((x) + 3) & ~3)
#define ROUND_UP_8(x) (((x) + 7) & ~7)

Benjamin Otte's avatar
Benjamin Otte committed
495
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
496
theora_dec_chain (GstPad * pad, GstData * data)
Benjamin Otte's avatar
Benjamin Otte committed
497 498 499 500
{
  GstBuffer *buf;
  GstTheoraDec *dec;
  ogg_packet packet;
501
  guint64 offset_end;
502
  GstClockTime outtime;
Benjamin Otte's avatar
Benjamin Otte committed
503 504 505 506 507 508 509 510

  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
  if (GST_IS_EVENT (data)) {
    theora_dec_event (dec, GST_EVENT (data));
    return;
  }

  buf = GST_BUFFER (data);
511

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
  if (dec->packetno >= 3) {
    offset_end = GST_BUFFER_OFFSET_END (buf);
    if (offset_end != -1) {
      dec->granulepos = offset_end;
      /* granulepos to time */
      outtime = GST_SECOND * theora_granule_time (&dec->state, dec->granulepos);
    } else {
      GstFormat time_format = GST_FORMAT_TIME;

      /* framenumber to time */
      theora_dec_src_convert (dec->srcpad, GST_FORMAT_DEFAULT,
          dec->packetno - 3, &time_format, &outtime);
    }
  } else {
    /* we don't know yet */
    outtime = -1;
528 529
  }

Benjamin Otte's avatar
Benjamin Otte committed
530 531 532
  /* make ogg_packet out of the buffer */
  packet.packet = GST_BUFFER_DATA (buf);
  packet.bytes = GST_BUFFER_SIZE (buf);
533
  packet.granulepos = dec->granulepos;
534
  packet.packetno = dec->packetno;
535 536
  packet.b_o_s = (packet.packetno == 0) ? 1 : 0;
  packet.e_o_s = 0;
537

538 539 540
  GST_DEBUG_OBJECT (dec, "header=%d packetno=%d", packet.packet[0],
      packet.packetno);

Benjamin Otte's avatar
Benjamin Otte committed
541 542 543 544
  /* switch depending on packet type */
  if (packet.packet[0] & 0x80) {
    /* header packet */
    if (theora_decode_header (&dec->info, &dec->comment, &packet)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
545
      GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
546
          (NULL), ("couldn't read header packet"));
Benjamin Otte's avatar
Benjamin Otte committed
547 548 549
      gst_data_unref (data);
      return;
    }
550 551 552
    if (packet.packetno == 0) {
      dec->packetno++;
    } else if (packet.packetno == 1) {
553
      gchar *encoder = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
554
      GstTagList *list =
555 556
          gst_tag_list_from_vorbiscomment_buffer (buf, "\201theora", 7,
          &encoder);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
557

558
      if (!list) {
559 560
        GST_ERROR_OBJECT (dec, "failed to parse tags");
        list = gst_tag_list_new ();
561 562
      }
      if (encoder) {
563 564 565
        gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
            GST_TAG_ENCODER, encoder, NULL);
        g_free (encoder);
566
      }
Benjamin Otte's avatar
Benjamin Otte committed
567
      gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
568
          GST_TAG_ENCODER_VERSION, dec->info.version_major, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
569
      gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, 0, list);
570 571

      dec->packetno++;
Benjamin Otte's avatar
Benjamin Otte committed
572 573
    } else if (packet.packetno == 2) {
      GstCaps *caps;
574 575 576 577 578 579 580 581 582 583 584 585 586
      gint par_num, par_den;

      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;
       * 0:0 is allowed and can be interpreted as 1:1, so correct for it */
      par_num = dec->info.aspect_numerator;
      par_den = dec->info.aspect_denominator;
      if (par_num == 0 && par_den == 0) {
        par_num = par_den = 1;
587

588
      }
589 590 591 592 593 594 595
      /* theora has:
       *
       *  width/height : dimension of the encoded frame 
       *  frame_width/frame_height : dimension of the visible part
       *  offset_x/offset_y : offset in encoded frame where visible part starts
       */
      GST_DEBUG_OBJECT (dec, "dimension %dx%d, PAR %d/%d", dec->info.width,
596
          dec->info.height, par_num, par_den);
597 598 599
      GST_DEBUG_OBJECT (dec, "frame dimension %dx%d, offset %d:%d",
          dec->info.frame_width, dec->info.frame_height,
          dec->info.offset_x, dec->info.offset_y);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
600

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
      if (dec->crop) {
        /* add black borders to make width/height/offsets even. we need this because
         * we cannot express an offset to the peer plugin. */
        dec->width =
            ROUND_UP_2 (dec->info.frame_width + (dec->info.offset_x & 1));
        dec->height =
            ROUND_UP_2 (dec->info.frame_height + (dec->info.offset_y & 1));
        dec->offset_x = dec->info.offset_x & ~1;
        dec->offset_y = dec->info.offset_y & ~1;
      } else {
        /* no cropping, use the encoded dimensions */
        dec->width = dec->info.width;
        dec->height = dec->info.height;
        dec->offset_x = 0;
        dec->offset_y = 0;
      }
617 618 619 620

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

Benjamin Otte's avatar
Benjamin Otte committed
621 622 623
      /* done */
      theora_decode_init (&dec->state, &dec->info);
      caps = gst_caps_new_simple ("video/x-raw-yuv",
624 625 626
          "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
          "framerate", G_TYPE_DOUBLE,
          ((gdouble) dec->info.fps_numerator) / dec->info.fps_denominator,
627
          "pixel-aspect-ratio", GST_TYPE_FRACTION, par_num, par_den,
628 629
          "width", G_TYPE_INT, dec->width, "height", G_TYPE_INT,
          dec->height, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
630 631
      gst_pad_set_explicit_caps (dec->srcpad, caps);
      gst_caps_free (caps);
632 633 634

      dec->initialized = TRUE;
      dec->packetno++;
Benjamin Otte's avatar
Benjamin Otte committed
635 636
    }
  } else {
637
    /* normal data packet */
Benjamin Otte's avatar
Benjamin Otte committed
638 639 640
    yuv_buffer yuv;
    GstBuffer *out;
    guint i;
641
    gboolean keyframe;
642 643 644 645
    gint out_size;
    gint stride_y, stride_uv;
    gint width, height;
    gint cwidth, cheight;
646

647 648 649 650 651 652 653
    dec->packetno++;

    if (!dec->initialized) {
      gst_data_unref (data);
      return;
    }

654 655 656 657 658 659
    /* the second most significant bit of the first data byte is cleared 
     * for keyframes */
    keyframe = (packet.packet[0] & 0x40) == 0;
    if (keyframe) {
      dec->need_keyframe = FALSE;
    } else if (dec->need_keyframe) {
660
      GST_WARNING_OBJECT (dec, "dropping frame because we need a keyframe");
661 662 663 664
      /* drop frames if we're looking for a keyframe */
      gst_data_unref (data);
      return;
    }
665 666 667 668 669
    if (theora_decode_packetin (&dec->state, &packet)) {
      GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
          (NULL), ("theora decoder did not read data packet"));
      gst_data_unref (data);
      return;
670
    }
671 672 673 674 675 676
    if (theora_decode_YUVout (&dec->state, &yuv) < 0) {
      GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
          (NULL), ("couldn't read out YUV image"));
      gst_data_unref (data);
      return;
    }
677

678 679
    g_return_if_fail (yuv.y_width == dec->info.width);
    g_return_if_fail (yuv.y_height == dec->info.height);
680

681 682 683 684
    width = dec->width;
    height = dec->height;
    cwidth = width / 2;
    cheight = height / 2;
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701

    /* should get the stride from the caps, for now we round up to the nearest
     * multiple of 4 because some element needs it. chroma needs special 
     * treatment, see videotestsrc. */
    stride_y = ROUND_UP_4 (width);
    stride_uv = ROUND_UP_8 (width) / 2;

    out_size = stride_y * height + stride_uv * cheight * 2;

    /* now copy over the area contained in offset_x,offset_y,
     * frame_width, frame_height */
    out = gst_pad_alloc_buffer (dec->srcpad, GST_BUFFER_OFFSET_NONE, out_size);

    /* copy the visible region to the destination. This is actually pretty
     * complicated and gstreamer doesn't support all the needed caps to do this
     * correctly. For example, when we have an odd offset, we should only combine
     * 1 row/column of luma samples with on chroma sample in colorspace conversion. 
702 703
     * We compensate for this by adding a block border around the image when the
     * offset of size is odd (see above).
704 705 706 707 708 709 710 711
     */
    {
      guint8 *dest_y, *src_y;
      guint8 *dest_u, *src_u;
      guint8 *dest_v, *src_v;

      dest_y = GST_BUFFER_DATA (out);
      dest_u = dest_y + stride_y * height;
712
      dest_v = dest_u + stride_uv * cheight;
713

714
      src_y = yuv.y + dec->offset_x + dec->offset_y * yuv.y_stride;
715 716 717 718 719 720 721 722

      for (i = 0; i < height; i++) {
        memcpy (dest_y, src_y, width);

        dest_y += stride_y;
        src_y += yuv.y_stride;
      }

723 724
      src_u = yuv.u + dec->offset_x / 2 + dec->offset_y / 2 * yuv.uv_stride;
      src_v = yuv.v + dec->offset_x / 2 + dec->offset_y / 2 * yuv.uv_stride;
725 726 727 728 729 730 731 732 733 734

      for (i = 0; i < cheight; i++) {
        memcpy (dest_u, src_u, cwidth);
        memcpy (dest_v, src_v, cwidth);

        dest_u += stride_uv;
        src_u += yuv.uv_stride;
        dest_v += stride_uv;
        src_v += yuv.uv_stride;
      }
735
    }
736

737 738 739 740 741 742 743
    GST_BUFFER_OFFSET (out) = dec->packetno - 4;
    GST_BUFFER_OFFSET_END (out) = dec->packetno - 3;
    GST_BUFFER_DURATION (out) =
        GST_SECOND * ((gdouble) dec->info.fps_denominator) /
        dec->info.fps_numerator;
    GST_BUFFER_TIMESTAMP (out) = outtime;

Benjamin Otte's avatar
Benjamin Otte committed
744 745 746 747 748 749
    gst_pad_push (dec->srcpad, GST_DATA (out));
  }
  gst_data_unref (data);
}

static GstElementStateReturn
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
750
theora_dec_change_state (GstElement * element)
Benjamin Otte's avatar
Benjamin Otte committed
751 752 753 754 755 756 757 758 759
{
  GstTheoraDec *dec = GST_THEORA_DEC (element);

  switch (GST_STATE_TRANSITION (element)) {
    case GST_STATE_NULL_TO_READY:
      break;
    case GST_STATE_READY_TO_PAUSED:
      theora_info_init (&dec->info);
      theora_comment_init (&dec->comment);
760
      dec->need_keyframe = TRUE;
761
      dec->initialized = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
      break;
    case GST_STATE_PAUSED_TO_PLAYING:
      break;
    case GST_STATE_PLAYING_TO_PAUSED:
      break;
    case GST_STATE_PAUSED_TO_READY:
      theora_clear (&dec->state);
      theora_comment_clear (&dec->comment);
      theora_info_clear (&dec->info);
      dec->packetno = 0;
      dec->granulepos = 0;
      break;
    case GST_STATE_READY_TO_NULL:
      break;
    default:
      break;
  }

  return parent_class->change_state (element);
}
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

static void
theora_dec_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstTheoraDec *dec = GST_THEORA_DEC (object);

  switch (prop_id) {
    case ARG_CROP:
      dec->crop = g_value_get_boolean (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
theora_dec_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstTheoraDec *dec = GST_THEORA_DEC (object);

  switch (prop_id) {
    case ARG_CROP:
      g_value_set_boolean (value, dec->crop);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}