theoradec.c 38.8 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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/**
 * SECTION:element-theoradec
 * @see_also: theoraenc, oggdemux
 *
 * <refsect2>
 * <para>
 * 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.
 * </para>
 * <para>
 * </para>
 * <title>Example pipeline</title>
 * <programlisting>
 * gst-launch -v filesrc location=videotestsrc.ogg ! oggdemux ! theoradec ! xvimagesink
 * </programlisting>
 * This example pipeline will decode an ogg stream and decodes the theora video. Refer to
 * 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
44 45 46 47
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

Wim Taymans's avatar
Wim Taymans committed
48
#include "gsttheoradec.h"
Benjamin Otte's avatar
Benjamin Otte committed
49 50
#include <gst/tag/tag.h>

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
54
#define THEORA_DEF_CROP         TRUE
55 56 57 58 59 60
enum
{
  ARG_0,
  ARG_CROP
};

Stefan Kost's avatar
Stefan Kost committed
61
static const GstElementDetails theora_dec_details =
j^'s avatar
j^ committed
62
GST_ELEMENT_DETAILS ("Theora video decoder",
63 64 65 66
    "Codec/Decoder/Video",
    "decode raw theora streams to raw YUV video",
    "Benjamin Otte <in7y118@public.uni-hamburg.de>, "
    "Wim Taymans <wim@fluendo.com>");
Benjamin Otte's avatar
Benjamin Otte committed
67 68

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

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

GST_BOILERPLATE (GstTheoraDec, gst_theora_dec, GstElement, 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 93
static gboolean theora_dec_sink_event (GstPad * pad, GstEvent * event);
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 101 102 103
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);
Wim Taymans's avatar
Wim Taymans committed
104 105 106
static gboolean theora_dec_sink_query (GstPad * pad, GstQuery * query);

#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
112
static const GstQueryType *theora_get_query_types (GstPad * pad);
Benjamin Otte's avatar
Benjamin Otte committed
113 114 115 116 117 118


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
119 120

  gst_element_class_add_pad_template (element_class,
Benjamin Otte's avatar
Benjamin Otte committed
121 122 123 124 125 126 127
      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
128
gst_theora_dec_class_init (GstTheoraDecClass * klass)
Benjamin Otte's avatar
Benjamin Otte committed
129
{
130
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Benjamin Otte's avatar
Benjamin Otte committed
131 132
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);

133 134 135 136 137 138 139 140
  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
141
  gstelement_class->change_state = theora_dec_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
142 143

  GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder");
Benjamin Otte's avatar
Benjamin Otte committed
144 145 146
}

static void
147
gst_theora_dec_init (GstTheoraDec * dec, GstTheoraDecClass * g_class)
Benjamin Otte's avatar
Benjamin Otte committed
148
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
149
  dec->sinkpad =
150
      gst_pad_new_from_static_template (&theora_dec_sink_factory, "sink");
Wim Taymans's avatar
Wim Taymans committed
151
  gst_pad_set_query_function (dec->sinkpad, theora_dec_sink_query);
152
  gst_pad_set_event_function (dec->sinkpad, theora_dec_sink_event);
Benjamin Otte's avatar
Benjamin Otte committed
153 154 155
  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
156
  dec->srcpad =
157
      gst_pad_new_from_static_template (&theora_dec_src_factory, "src");
Benjamin Otte's avatar
Benjamin Otte committed
158
  gst_pad_set_event_function (dec->srcpad, theora_dec_src_event);
159
  gst_pad_set_query_type_function (dec->srcpad, theora_get_query_types);
160
  gst_pad_set_query_function (dec->srcpad, theora_dec_src_query);
161
  gst_pad_use_fixed_caps (dec->srcpad);
162

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

165
  dec->crop = THEORA_DEF_CROP;
166 167
  dec->gather = NULL;
  dec->decode = NULL;
168
  dec->queued = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
169
}
170

171 172 173
static void
gst_theora_dec_reset (GstTheoraDec * dec)
{
174 175
  GList *walk;

176 177 178
  dec->need_keyframe = TRUE;
  dec->last_timestamp = -1;
  dec->granulepos = -1;
179 180
  dec->discont = TRUE;
  dec->frame_nr = -1;
181 182 183 184 185 186
  gst_segment_init (&dec->segment, GST_FORMAT_TIME);

  GST_OBJECT_LOCK (dec);
  dec->proportion = 1.0;
  dec->earliest_time = -1;
  GST_OBJECT_UNLOCK (dec);
187 188 189 190 191 192

  for (walk = dec->queued; walk; walk = g_list_next (walk)) {
    gst_buffer_unref (GST_BUFFER_CAST (walk->data));
  }
  g_list_free (dec->queued);
  dec->queued = NULL;
193 194
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
195 196 197 198 199 200
static int
_theora_ilog (unsigned int v)
{
  int ret = 0;

  while (v) {
201
    ret++;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
202
    v >>= 1;
203
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
204
  return (ret);
205 206
}

207 208
static gint64
_theora_granule_frame (GstTheoraDec * dec, gint64 granulepos)
209 210 211 212
{
  guint ilog;
  gint framecount;

213 214
  if (granulepos == -1)
    return -1;
215

216
  ilog = dec->granule_shift;
217

218 219 220 221
  /* 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 = granulepos >> ilog;
  framecount += granulepos - (framecount << ilog);
222 223 224

  GST_DEBUG_OBJECT (dec, "framecount=%d, ilog=%u", framecount, ilog);

225 226 227 228 229 230 231 232
  return framecount;
}

static GstClockTime
_theora_granule_time (GstTheoraDec * dec, gint64 granulepos)
{
  gint framecount;

233
  /* invalid granule results in invalid time */
234 235 236
  if (granulepos == -1)
    return -1;

237
  /* get framecount */
238
  framecount = _theora_granule_frame (dec, granulepos);
239 240 241 242 243 244 245 246 247 248 249 250

  return gst_util_uint64_scale_int (framecount * GST_SECOND,
      dec->info.fps_denominator, dec->info.fps_numerator);
}

static gint64
_inc_granulepos (GstTheoraDec * dec, gint64 granulepos)
{
  gint framecount;

  if (granulepos == -1)
    return -1;
251

252 253 254
  framecount = _theora_granule_frame (dec, granulepos);

  return (framecount + 1) << dec->granule_shift;
255 256
}

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

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

static const GstQueryType *
theora_get_query_types (GstPad * pad)
{
  static const GstQueryType theora_src_query_types[] = {
    GST_QUERY_POSITION,
295 296
    GST_QUERY_DURATION,
    GST_QUERY_CONVERT,
297 298 299 300 301 302 303
    0
  };

  return theora_src_query_types;
}


304
static gboolean
305 306 307
theora_dec_src_convert (GstPad * pad,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
308
{
309 310 311 312
  gboolean res = TRUE;
  GstTheoraDec *dec;
  guint64 scale = 1;

Wim Taymans's avatar
Wim Taymans committed
313 314 315 316 317
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

318 319 320 321 322 323
  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;

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

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

Benjamin Otte's avatar
Benjamin Otte committed
379
static gboolean
380 381 382
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
383
{
384 385
  gboolean res = TRUE;
  GstTheoraDec *dec;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386

Wim Taymans's avatar
Wim Taymans committed
387 388 389 390 391
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

392 393 394 395 396 397
  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;

398
  switch (src_format) {
Benjamin Otte's avatar
Benjamin Otte committed
399
    case GST_FORMAT_DEFAULT:
400 401
      switch (*dest_format) {
        case GST_FORMAT_TIME:
402
          *dest_value = _theora_granule_time (dec, src_value);
403 404 405 406
          break;
        default:
          res = FALSE;
      }
407
      break;
408 409 410 411
    case GST_FORMAT_TIME:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
        {
412
          guint rest;
413

414
          /* framecount */
415
          *dest_value = gst_util_uint64_scale (src_value,
416
              dec->info.fps_numerator, GST_SECOND * dec->info.fps_denominator);
417 418 419 420

          /* funny way of calculating granulepos in theora */
          rest = *dest_value / dec->info.keyframe_frequency_force;
          *dest_value -= rest;
421
          *dest_value <<= dec->granule_shift;
422
          *dest_value += rest;
423 424 425 426 427 428 429
          break;
        }
        default:
          res = FALSE;
          break;
      }
      break;
Benjamin Otte's avatar
Benjamin Otte committed
430
    default:
431
      res = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
432
  }
433 434
done:
  gst_object_unref (dec);
435
  return res;
436 437 438 439 440 441 442 443

  /* ERRORS */
no_header:
  {
    GST_DEBUG_OBJECT (dec, "no header yet, cannot convert");
    res = FALSE;
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
444
}
445 446

static gboolean
Wim Taymans's avatar
Wim Taymans committed
447
theora_dec_src_query (GstPad * pad, GstQuery * query)
448
{
449 450
  GstTheoraDec *dec;

Wim Taymans's avatar
Wim Taymans committed
451 452
  gboolean res = FALSE;

453 454
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

Wim Taymans's avatar
Wim Taymans committed
455 456 457
  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
    {
Wim Taymans's avatar
Wim Taymans committed
458
      gint64 granulepos, value;
Wim Taymans's avatar
Wim Taymans committed
459 460 461 462 463 464
      GstFormat my_format, format;
      gint64 time;

      /* we can convert a granule position to everything */
      granulepos = dec->granulepos;

465 466 467
      GST_LOG_OBJECT (dec,
          "query %p: we have current granule: %lld", query, granulepos);

Wim Taymans's avatar
Wim Taymans committed
468 469
      /* parse format */
      gst_query_parse_position (query, &format, NULL);
470

Wim Taymans's avatar
Wim Taymans committed
471 472 473 474 475 476 477 478
      /* and convert to the final format in two steps with time as the 
       * intermediate step */
      my_format = GST_FORMAT_TIME;
      if (!(res =
              theora_dec_sink_convert (dec->sinkpad, GST_FORMAT_DEFAULT,
                  granulepos, &my_format, &time)))
        goto error;

479
      time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
480

481 482 483
      GST_LOG_OBJECT (dec,
          "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));

Wim Taymans's avatar
Wim Taymans committed
484 485 486 487
      if (!(res =
              theora_dec_src_convert (pad, my_format, time, &format, &value)))
        goto error;

Wim Taymans's avatar
Wim Taymans committed
488
      gst_query_set_position (query, format, value);
Wim Taymans's avatar
Wim Taymans committed
489 490

      GST_LOG_OBJECT (dec,
Wim Taymans's avatar
Wim Taymans committed
491
          "query %p: we return %lld (format %u)", query, value, format);
Wim Taymans's avatar
Wim Taymans committed
492 493 494

      break;
    }
Wim Taymans's avatar
Wim Taymans committed
495
    case GST_QUERY_DURATION:
496 497 498 499 500 501
    {
      GstPad *peer;

      if (!(peer = gst_pad_get_peer (dec->sinkpad)))
        goto error;

Wim Taymans's avatar
Wim Taymans committed
502
      /* forward to peer for total */
503 504 505
      res = gst_pad_query (peer, query);
      gst_object_unref (peer);
      if (!res)
Wim Taymans's avatar
Wim Taymans committed
506
        goto error;
507

Wim Taymans's avatar
Wim Taymans committed
508
      break;
509
    }
Wim Taymans's avatar
Wim Taymans committed
510 511 512 513 514 515
    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);
516
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
517 518
              theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
519 520 521
        goto error;

      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
Wim Taymans's avatar
Wim Taymans committed
522 523 524
      break;
    }
    default:
525
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
526
      break;
527
  }
528 529 530
done:
  gst_object_unref (dec);

531
  return res;
532

533
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
534
error:
535 536 537 538
  {
    GST_DEBUG_OBJECT (dec, "query failed");
    goto done;
  }
Wim Taymans's avatar
Wim Taymans committed
539 540 541 542 543 544 545 546 547 548 549 550
}

static gboolean
theora_dec_sink_query (GstPad * pad, GstQuery * query)
{
  gboolean res = FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_CONVERT:
    {
      GstFormat src_fmt, dest_fmt;
      gint64 src_val, dest_val;
551

Wim Taymans's avatar
Wim Taymans committed
552 553 554 555 556 557 558 559 560 561
      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
      if (!(res =
              theora_dec_sink_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
        goto error;

      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
      break;
    }
    default:
562
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
563 564
      break;
  }
565

Wim Taymans's avatar
Wim Taymans committed
566 567
error:
  return res;
568 569
}

Benjamin Otte's avatar
Benjamin Otte committed
570
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
571
theora_dec_src_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
572 573 574 575
{
  gboolean res = TRUE;
  GstTheoraDec *dec;

576
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
Benjamin Otte's avatar
Benjamin Otte committed
577 578

  switch (GST_EVENT_TYPE (event)) {
579 580
    case GST_EVENT_SEEK:
    {
581 582
      GstFormat format, tformat;
      gdouble rate;
Scott Wheeler Wheeler's avatar
Scott Wheeler Wheeler committed
583
      GstEvent *real_seek;
584 585 586 587 588 589 590
      GstSeekFlags flags;
      GstSeekType cur_type, stop_type;
      gint64 cur, stop;
      gint64 tcur, tstop;

      gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
          &stop_type, &stop);
591
      gst_event_unref (event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
592

593 594 595 596 597 598
      /* 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 
       */
599 600
      tformat = GST_FORMAT_TIME;
      if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur)))
601
        goto convert_error;
602
      if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop)))
603
        goto convert_error;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
604

605
      /* then seek with time on the peer */
606 607
      real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
          flags, cur_type, tcur, stop_type, tstop);
608

609
      res = gst_pad_push_event (dec->sinkpad, real_seek);
Benjamin Otte's avatar
Benjamin Otte committed
610 611
      break;
    }
612 613 614 615 616 617 618 619 620 621
    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
622 623
       * push late frames. This would at least save us the time to
       * crop/memcpy the data. */
624 625 626 627 628
      GST_OBJECT_LOCK (dec);
      dec->proportion = proportion;
      dec->earliest_time = timestamp + diff;
      GST_OBJECT_UNLOCK (dec);

629 630 631
      GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
          GST_TIME_ARGS (timestamp), diff);

632
      res = gst_pad_push_event (dec->sinkpad, event);
633 634
      break;
    }
Benjamin Otte's avatar
Benjamin Otte committed
635
    default:
636
      res = gst_pad_push_event (dec->sinkpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
637 638
      break;
  }
639 640
done:
  gst_object_unref (dec);
Benjamin Otte's avatar
Benjamin Otte committed
641 642

  return res;
643

644 645 646 647 648 649
  /* ERRORS */
convert_error:
  {
    GST_DEBUG_OBJECT (dec, "could not convert format");
    goto done;
  }
650 651
}

652 653
static gboolean
theora_dec_sink_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
654
{
655
  gboolean ret = FALSE;
656 657
  GstTheoraDec *dec;

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

Benjamin Otte's avatar
Benjamin Otte committed
660 661
  GST_LOG_OBJECT (dec, "handling event");
  switch (GST_EVENT_TYPE (event)) {
662 663 664 665 666 667 668
    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
669 670 671
    case GST_EVENT_EOS:
      ret = gst_pad_push_event (dec->srcpad, event);
      break;
672
    case GST_EVENT_NEWSEGMENT:
673
    {
674
      gboolean update;
675
      GstFormat format;
676
      gdouble rate, arate;
Wim Taymans's avatar
Wim Taymans committed
677
      gint64 start, stop, time;
678

679 680
      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
          &start, &stop, &time);
681 682 683 684 685

      /* we need TIME and a positive rate */
      if (format != GST_FORMAT_TIME)
        goto newseg_wrong_format;

686
      /* now configure the values */
687 688
      gst_segment_set_newsegment_full (&dec->segment, update,
          rate, arate, format, start, stop, time);
689

690
      /* and forward */
Wim Taymans's avatar
Wim Taymans committed
691
      ret = gst_pad_push_event (dec->srcpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
692
      break;
693
    }
Benjamin Otte's avatar
Benjamin Otte committed
694
    default:
Wim Taymans's avatar
Wim Taymans committed
695
      ret = gst_pad_push_event (dec->srcpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
696 697
      break;
  }
698
done:
Wim Taymans's avatar
Wim Taymans committed
699 700
  gst_object_unref (dec);

701
  return ret;
702 703 704 705

  /* ERRORS */
newseg_wrong_format:
  {
706
    GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
707
    gst_event_unref (event);
708 709
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
710 711
}

712
static GstFlowReturn
713
theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet)
Benjamin Otte's avatar
Benjamin Otte committed
714
{
715
  gchar *encoder = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
716
  GstBuffer *buf;
717 718
  GstTagList *list;

719
  GST_DEBUG_OBJECT (dec, "parsing comment packet");
720 721

  buf = gst_buffer_new_and_alloc (packet->bytes);
722
  memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
723

724 725
  list =
      gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\201theora", 7,
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
      &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);

743 744 745 746 747 748
  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);
  }

749
  gst_element_found_tags_for_pad (GST_ELEMENT_CAST (dec), dec->srcpad, list);
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786

  return GST_FLOW_OK;
}

static GstFlowReturn
theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstCaps *caps;
  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;
  }
  /* 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,
      dec->info.height, par_num, par_den);
  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);

  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. */
787 788
    dec->width =
        GST_ROUND_UP_2 (dec->info.frame_width + (dec->info.offset_x & 1));
789
    dec->height =
790
        GST_ROUND_UP_2 (dec->info.frame_height + (dec->info.offset_y & 1));
791 792 793 794 795 796 797 798 799 800
    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;
  }

801 802
  dec->granule_shift = _theora_ilog (dec->info.keyframe_frequency_force - 1);

803 804 805 806 807 808 809 810
  GST_DEBUG_OBJECT (dec, "after fixup frame dimension %dx%d, offset %d:%d",
      dec->width, dec->height, dec->offset_x, dec->offset_y);

  /* done */
  theora_decode_init (&dec->state, &dec->info);

  caps = gst_caps_new_simple ("video/x-raw-yuv",
      "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
811 812
      "framerate", GST_TYPE_FRACTION,
      dec->info.fps_numerator, dec->info.fps_denominator,
813 814 815 816 817
      "pixel-aspect-ratio", GST_TYPE_FRACTION, par_num, par_den,
      "width", G_TYPE_INT, dec->width, "height", G_TYPE_INT, dec->height, NULL);
  gst_pad_set_caps (dec->srcpad, caps);
  gst_caps_unref (caps);

Wim Taymans's avatar
Wim Taymans committed
818
  dec->have_header = TRUE;
819 820 821 822 823 824 825 826 827

  return GST_FLOW_OK;
}

static GstFlowReturn
theora_handle_header_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstFlowReturn res;

828
  GST_DEBUG_OBJECT (dec, "parsing header packet");
829 830 831 832

  if (theora_decode_header (&dec->info, &dec->comment, packet))
    goto header_read_error;

Wim Taymans's avatar
Wim Taymans committed
833 834
  switch (packet->packet[0]) {
    case 0x81:
835 836
      res = theora_handle_comment_packet (dec, packet);
      break;
Wim Taymans's avatar
Wim Taymans committed
837
    case 0x82:
838 839 840 841
      res = theora_handle_type_packet (dec, packet);
      break;
    default:
      /* ignore */
Wim Taymans's avatar
Wim Taymans committed
842 843 844
      g_warning ("unknown theora header packet found");
    case 0x80:
      /* nothing special, this is the identification header */
845 846 847 848 849 850 851 852 853 854 855 856 857 858
      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;
  }
}

859 860 861 862 863
/* 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)
{
864 865
  gboolean res = TRUE;
  GstClockTime in_ts, in_dur, stop;
866 867
  gint64 cstart, cstop;

868 869 870
  in_ts = GST_BUFFER_TIMESTAMP (buf);
  in_dur = GST_BUFFER_DURATION (buf);

871 872
  GST_LOG_OBJECT (dec,
      "timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT,
873
      GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur));
874

875 876
  /* can't clip without TIME segment */
  if (dec->segment.format != GST_FORMAT_TIME)
877 878
    goto beach;

879 880 881 882 883 884 885 886 887 888 889
  /* we need a start time */
  if (!GST_CLOCK_TIME_IS_VALID (in_ts))
    goto beach;

  /* generate valid stop, if duration unknown, we have unknown stop */
  stop =
      GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE;

  /* now clip */
  if (!(res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME,
              in_ts, stop, &cstart, &cstop)))
890 891
    goto beach;

892 893
  /* update timestamp and possibly duration if the clipped stop time is
   * valid */
894
  GST_BUFFER_TIMESTAMP (buf) = cstart;
895 896
  if (GST_CLOCK_TIME_IS_VALID (cstop))
    GST_BUFFER_DURATION (buf) = cstop - cstart;
897 898

beach:
899
  GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
900 901 902
  return res;
}

903
/* FIXME, this needs to be moved to the demuxer */
904 905 906
static GstFlowReturn
theora_dec_push (GstTheoraDec * dec, GstBuffer * buf)
{
907
  GstFlowReturn result = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
908
  GstClockTime outtime = GST_BUFFER_TIMESTAMP (buf);
909

Wim Taymans's avatar
Wim Taymans committed
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
  if (outtime == GST_CLOCK_TIME_NONE) {
    dec->queued = g_list_append (dec->queued, buf);
    GST_DEBUG_OBJECT (dec, "queued buffer");
  } else {
    if (dec->queued) {
      gint64 size;
      GList *walk;

      GST_DEBUG_OBJECT (dec, "first buffer with time %" GST_TIME_FORMAT,
          GST_TIME_ARGS (outtime));

      size = g_list_length (dec->queued);
      for (walk = dec->queued; walk; walk = g_list_next (walk)) {
        GstBuffer *buffer = GST_BUFFER (walk->data);
        GstClockTime time;

926 927
        time = outtime - gst_util_uint64_scale_int (size * GST_SECOND,
            dec->info.fps_denominator, dec->info.fps_numerator);
Wim Taymans's avatar
Wim Taymans committed
928 929 930

        GST_DEBUG_OBJECT (dec, "patch buffer %lld %lld", size, time);
        GST_BUFFER_TIMESTAMP (buffer) = time;
931 932 933 934 935

        if (dec->discont) {
          GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
          dec->discont = FALSE;
        }
936
        /* ignore the result.. */
937 938 939 940
        if (clip_buffer (dec, buffer))
          gst_pad_push (dec->srcpad, buffer);
        else
          gst_buffer_unref (buffer);
Wim Taymans's avatar
Wim Taymans committed
941 942 943 944 945
        size--;
      }
      g_list_free (dec->queued);
      dec->queued = NULL;
    }
946 947 948 949
    if (dec->discont) {
      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
      dec->discont = FALSE;
    }
950 951 952 953
    if (clip_buffer (dec, buf))
      result = gst_pad_push (dec->srcpad, buf);
    else
      gst_buffer_unref (buf);
Wim Taymans's avatar
Wim Taymans committed
954
  }
955 956 957 958

  return result;
}

959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
static GstFlowReturn
theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
    GstClockTime outtime)
{
  /* normal data packet */
  yuv_buffer yuv;
  GstBuffer *out;
  guint i;
  gboolean keyframe;
  gint out_size;
  gint stride_y, stride_uv;
  gint width, height;
  gint cwidth, cheight;
  GstFlowReturn result;

974
  if (G_UNLIKELY (!dec->have_header))
975 976 977 978 979
    goto not_initialized;

  /* the second most significant bit of the first data byte is cleared 
   * for keyframes */
  keyframe = (packet->packet[0] & 0x40) == 0;
980
  if (G_UNLIKELY (keyframe)) {
981
    GST_DEBUG_OBJECT (dec, "we have a keyframe");