gsttheoradec.c 39.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
/**
 * 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 57
enum
{
  ARG_0,
  ARG_CROP
};

Stefan Kost's avatar
Stefan Kost committed
58
static const GstElementDetails theora_dec_details =
j^'s avatar
j^ committed
59
GST_ELEMENT_DETAILS ("Theora video decoder",
60 61 62 63
    "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
64 65

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

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

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

84 85 86 87 88
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);

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

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


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
113 114

  gst_element_class_add_pad_template (element_class,
Benjamin Otte's avatar
Benjamin Otte committed
115 116 117 118 119 120 121
      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
122
gst_theora_dec_class_init (GstTheoraDecClass * klass)
Benjamin Otte's avatar
Benjamin Otte committed
123
{
124
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Benjamin Otte's avatar
Benjamin Otte committed
125 126
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);

127 128 129 130 131 132
  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,
133
          (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
134

Benjamin Otte's avatar
Benjamin Otte committed
135
  gstelement_class->change_state = theora_dec_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
136 137

  GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder");
Benjamin Otte's avatar
Benjamin Otte committed
138 139 140
}

static void
141
gst_theora_dec_init (GstTheoraDec * dec, GstTheoraDecClass * g_class)
Benjamin Otte's avatar
Benjamin Otte committed
142
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
143
  dec->sinkpad =
144
      gst_pad_new_from_static_template (&theora_dec_sink_factory, "sink");
145
  gst_pad_set_event_function (dec->sinkpad, theora_dec_sink_event);
146
  gst_pad_set_setcaps_function (dec->sinkpad, theora_dec_setcaps);
Benjamin Otte's avatar
Benjamin Otte committed
147 148 149
  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
150
  dec->srcpad =
151
      gst_pad_new_from_static_template (&theora_dec_src_factory, "src");
Benjamin Otte's avatar
Benjamin Otte committed
152
  gst_pad_set_event_function (dec->srcpad, theora_dec_src_event);
153
  gst_pad_set_query_type_function (dec->srcpad, theora_get_query_types);
154
  gst_pad_set_query_function (dec->srcpad, theora_dec_src_query);
155
  gst_pad_use_fixed_caps (dec->srcpad);
156

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

159
  dec->crop = THEORA_DEF_CROP;
160 161
  dec->gather = NULL;
  dec->decode = NULL;
162
  dec->queued = NULL;
Wim Taymans's avatar
Wim Taymans committed
163
  dec->pendingevents = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
164
}
165

166 167 168 169 170
static void
gst_theora_dec_reset (GstTheoraDec * dec)
{
  dec->need_keyframe = TRUE;
  dec->last_timestamp = -1;
171 172
  dec->discont = TRUE;
  dec->frame_nr = -1;
Wim Taymans's avatar
Wim Taymans committed
173
  dec->seqnum = gst_util_seqnum_next ();
174 175 176 177 178 179
  gst_segment_init (&dec->segment, GST_FORMAT_TIME);

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

181
  g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL);
182 183
  g_list_free (dec->queued);
  dec->queued = NULL;
184 185 186 187 188 189
  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
190 191 192
  g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL);
  g_list_free (dec->pendingevents);
  dec->pendingevents = NULL;
193 194 195 196 197

  if (dec->tags) {
    gst_tag_list_free (dec->tags);
    dec->tags = NULL;
  }
198 199
}

Wim Taymans's avatar
Wim Taymans committed
200
#if 0
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
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
218
#endif
219

Wim Taymans's avatar
Wim Taymans committed
220
#if 0
221 222 223 224 225 226 227 228 229 230
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
231
#endif
232 233 234 235 236 237

static const GstQueryType *
theora_get_query_types (GstPad * pad)
{
  static const GstQueryType theora_src_query_types[] = {
    GST_QUERY_POSITION,
238 239
    GST_QUERY_DURATION,
    GST_QUERY_CONVERT,
240 241 242 243 244 245 246
    0
  };

  return theora_src_query_types;
}


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

Wim Taymans's avatar
Wim Taymans committed
256 257 258 259 260
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

261 262 263 264 265 266
  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;

267 268 269 270
  switch (src_format) {
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
271
          *dest_value = gst_util_uint64_scale_int (src_value, 8,
272
              dec->info.pic_height * dec->info.pic_width * dec->output_bpp);
273 274 275 276 277 278 279
          break;
        case GST_FORMAT_TIME:
          /* seems like a rather silly conversion, implement me if you like */
        default:
          res = FALSE;
      }
      break;
280
    case GST_FORMAT_TIME:
281 282
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
283 284 285
          scale =
              dec->output_bpp * (dec->info.pic_width * dec->info.pic_height) /
              8;
286
        case GST_FORMAT_DEFAULT:
287
          *dest_value = scale * gst_util_uint64_scale (src_value,
288
              dec->info.fps_numerator, dec->info.fps_denominator * GST_SECOND);
289 290 291 292
          break;
        default:
          res = FALSE;
      }
293 294
      break;
    case GST_FORMAT_DEFAULT:
295 296
      switch (*dest_format) {
        case GST_FORMAT_TIME:
297
          *dest_value = gst_util_uint64_scale (src_value,
298
              GST_SECOND * dec->info.fps_denominator, dec->info.fps_numerator);
299 300
          break;
        case GST_FORMAT_BYTES:
301
          *dest_value = gst_util_uint64_scale_int (src_value,
302
              dec->output_bpp * dec->info.pic_width * dec->info.pic_height, 8);
303 304 305 306
          break;
        default:
          res = FALSE;
      }
307 308
      break;
    default:
309
      res = FALSE;
310
  }
311 312
done:
  gst_object_unref (dec);
313
  return res;
314 315 316 317 318 319 320 321

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

Wim Taymans's avatar
Wim Taymans committed
324
#if 0
Benjamin Otte's avatar
Benjamin Otte committed
325
static gboolean
326 327 328
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
329
{
330 331
  gboolean res = TRUE;
  GstTheoraDec *dec;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
332

Wim Taymans's avatar
Wim Taymans committed
333 334 335 336 337
  if (src_format == *dest_format) {
    *dest_value = src_value;
    return TRUE;
  }

338 339 340 341 342 343
  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;

344
  switch (src_format) {
Benjamin Otte's avatar
Benjamin Otte committed
345
    case GST_FORMAT_DEFAULT:
346 347
      switch (*dest_format) {
        case GST_FORMAT_TIME:
348
          *dest_value = _theora_granule_start_time (dec, src_value);
349 350 351 352
          break;
        default:
          res = FALSE;
      }
353
      break;
354 355 356 357
    case GST_FORMAT_TIME:
      switch (*dest_format) {
        case GST_FORMAT_DEFAULT:
        {
358
          guint rest;
359

360
          /* framecount */
361
          *dest_value = gst_util_uint64_scale (src_value,
362
              dec->info.fps_numerator, GST_SECOND * dec->info.fps_denominator);
363 364

          /* funny way of calculating granulepos in theora */
365
          rest = *dest_value / dec->info.keyframe_granule_shift;
366
          *dest_value -= rest;
367
          *dest_value <<= dec->granule_shift;
368
          *dest_value += rest;
369 370 371 372 373 374 375
          break;
        }
        default:
          res = FALSE;
          break;
      }
      break;
Benjamin Otte's avatar
Benjamin Otte committed
376
    default:
377
      res = FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
378
  }
379 380
done:
  gst_object_unref (dec);
381
  return res;
382 383 384 385 386 387 388 389

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

static gboolean
Wim Taymans's avatar
Wim Taymans committed
394
theora_dec_src_query (GstPad * pad, GstQuery * query)
395
{
396 397
  GstTheoraDec *dec;

Wim Taymans's avatar
Wim Taymans committed
398 399
  gboolean res = FALSE;

400 401
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));

Wim Taymans's avatar
Wim Taymans committed
402 403 404
  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
    {
Wim Taymans's avatar
Wim Taymans committed
405
      gint64 value;
Wim Taymans's avatar
Wim Taymans committed
406
      GstFormat format;
Wim Taymans's avatar
Wim Taymans committed
407 408
      gint64 time;

Wim Taymans's avatar
Wim Taymans committed
409 410
      /* parse format */
      gst_query_parse_position (query, &format, NULL);
411

Wim Taymans's avatar
Wim Taymans committed
412
      time = dec->last_timestamp;
413
      time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
414

415 416 417
      GST_LOG_OBJECT (dec,
          "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));

Wim Taymans's avatar
Wim Taymans committed
418
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
419 420
              theora_dec_src_convert (pad, GST_FORMAT_TIME, time, &format,
                  &value)))
Wim Taymans's avatar
Wim Taymans committed
421 422
        goto error;

Wim Taymans's avatar
Wim Taymans committed
423
      gst_query_set_position (query, format, value);
Wim Taymans's avatar
Wim Taymans committed
424 425

      GST_LOG_OBJECT (dec,
426 427
          "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value,
          format);
Wim Taymans's avatar
Wim Taymans committed
428 429
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
430
    case GST_QUERY_DURATION:
431
    {
Wim Taymans's avatar
Wim Taymans committed
432
      /* forward to peer for total */
Wim Taymans's avatar
Wim Taymans committed
433
      res = gst_pad_peer_query (dec->sinkpad, query);
434
      if (!res)
Wim Taymans's avatar
Wim Taymans committed
435
        goto error;
436

Wim Taymans's avatar
Wim Taymans committed
437
      break;
438
    }
Wim Taymans's avatar
Wim Taymans committed
439 440 441 442 443 444
    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);
445
      if (!(res =
Wim Taymans's avatar
Wim Taymans committed
446 447
              theora_dec_src_convert (pad, src_fmt, src_val, &dest_fmt,
                  &dest_val)))
448 449 450
        goto error;

      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
Wim Taymans's avatar
Wim Taymans committed
451 452 453
      break;
    }
    default:
454
      res = gst_pad_query_default (pad, query);
Wim Taymans's avatar
Wim Taymans committed
455
      break;
456
  }
457 458 459
done:
  gst_object_unref (dec);

460
  return res;
461

462
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
463
error:
464 465 466 467
  {
    GST_DEBUG_OBJECT (dec, "query failed");
    goto done;
  }
Wim Taymans's avatar
Wim Taymans committed
468 469
}

Benjamin Otte's avatar
Benjamin Otte committed
470
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
471
theora_dec_src_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
472 473 474 475
{
  gboolean res = TRUE;
  GstTheoraDec *dec;

476
  dec = GST_THEORA_DEC (gst_pad_get_parent (pad));
Benjamin Otte's avatar
Benjamin Otte committed
477 478

  switch (GST_EVENT_TYPE (event)) {
479 480
    case GST_EVENT_SEEK:
    {
481 482
      GstFormat format, tformat;
      gdouble rate;
Scott Wheeler Wheeler's avatar
Scott Wheeler Wheeler committed
483
      GstEvent *real_seek;
484 485 486 487
      GstSeekFlags flags;
      GstSeekType cur_type, stop_type;
      gint64 cur, stop;
      gint64 tcur, tstop;
Wim Taymans's avatar
Wim Taymans committed
488
      guint32 seqnum;
489 490 491

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

495 496 497 498 499 500
      /* 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 
       */
501 502
      tformat = GST_FORMAT_TIME;
      if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur)))
503
        goto convert_error;
504
      if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop)))
505
        goto convert_error;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
506

507
      /* then seek with time on the peer */
508 509
      real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
          flags, cur_type, tcur, stop_type, tstop);
Wim Taymans's avatar
Wim Taymans committed
510
      gst_event_set_seqnum (real_seek, seqnum);
511

512
      res = gst_pad_push_event (dec->sinkpad, real_seek);
Benjamin Otte's avatar
Benjamin Otte committed
513 514
      break;
    }
515 516 517 518 519 520 521 522 523 524
    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
525 526
       * push late frames. This would at least save us the time to
       * crop/memcpy the data. */
527 528 529 530 531
      GST_OBJECT_LOCK (dec);
      dec->proportion = proportion;
      dec->earliest_time = timestamp + diff;
      GST_OBJECT_UNLOCK (dec);

532 533 534
      GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
          GST_TIME_ARGS (timestamp), diff);

535
      res = gst_pad_push_event (dec->sinkpad, event);
536 537
      break;
    }
Benjamin Otte's avatar
Benjamin Otte committed
538
    default:
539
      res = gst_pad_push_event (dec->sinkpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
540 541
      break;
  }
542 543
done:
  gst_object_unref (dec);
Benjamin Otte's avatar
Benjamin Otte committed
544 545

  return res;
546

547 548 549 550 551 552
  /* ERRORS */
convert_error:
  {
    GST_DEBUG_OBJECT (dec, "could not convert format");
    goto done;
  }
553 554
}

555 556
static gboolean
theora_dec_sink_event (GstPad * pad, GstEvent * event)
Benjamin Otte's avatar
Benjamin Otte committed
557
{
558
  gboolean ret = FALSE;
559 560
  GstTheoraDec *dec;

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

Benjamin Otte's avatar
Benjamin Otte committed
563 564
  GST_LOG_OBJECT (dec, "handling event");
  switch (GST_EVENT_TYPE (event)) {
565 566 567 568 569 570 571
    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
572 573 574
    case GST_EVENT_EOS:
      ret = gst_pad_push_event (dec->srcpad, event);
      break;
575
    case GST_EVENT_NEWSEGMENT:
576
    {
577
      gboolean update;
578
      GstFormat format;
579
      gdouble rate, arate;
Wim Taymans's avatar
Wim Taymans committed
580
      gint64 start, stop, time;
581

582 583
      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
          &start, &stop, &time);
584

585
      /* we need TIME format */
586 587 588
      if (format != GST_FORMAT_TIME)
        goto newseg_wrong_format;

589 590 591 592 593 594
      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));

595
      /* now configure the values */
596 597
      gst_segment_set_newsegment_full (&dec->segment, update,
          rate, arate, format, start, stop, time);
Wim Taymans's avatar
Wim Taymans committed
598
      dec->seqnum = gst_event_get_seqnum (event);
599

Wim Taymans's avatar
Wim Taymans committed
600
      /* We don't forward this unless/until the decoder is initialised */
601 602 603
      if (dec->have_header) {
        ret = gst_pad_push_event (dec->srcpad, event);
      } else {
Wim Taymans's avatar
Wim Taymans committed
604
        dec->pendingevents = g_list_append (dec->pendingevents, event);
605 606
        ret = TRUE;
      }
Benjamin Otte's avatar
Benjamin Otte committed
607
      break;
608
    }
609 610 611 612 613 614 615 616 617 618 619 620
    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
621
    default:
Wim Taymans's avatar
Wim Taymans committed
622
      ret = gst_pad_push_event (dec->srcpad, event);
Benjamin Otte's avatar
Benjamin Otte committed
623 624
      break;
  }
625
done:
Wim Taymans's avatar
Wim Taymans committed
626 627
  gst_object_unref (dec);

628
  return ret;
629 630 631 632

  /* ERRORS */
newseg_wrong_format:
  {
633
    GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
634
    gst_event_unref (event);
635 636
    goto done;
  }
Benjamin Otte's avatar
Benjamin Otte committed
637 638
}

639 640 641 642 643
static gboolean
theora_dec_setcaps (GstPad * pad, GstCaps * caps)
{
  GstTheoraDec *dec;
  GstStructure *s;
644
  const GValue *codec_data;
645 646 647 648 649 650 651 652 653

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

654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
  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;
      }
    }
  }

697 698 699 700 701
  gst_object_unref (dec);

  return TRUE;
}

702
static GstFlowReturn
703
theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet)
Benjamin Otte's avatar
Benjamin Otte committed
704
{
705
  gchar *encoder = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
706
  GstBuffer *buf;
707 708
  GstTagList *list;

709
  GST_DEBUG_OBJECT (dec, "parsing comment packet");
710

711 712 713
  buf = gst_buffer_new ();
  GST_BUFFER_SIZE (buf) = packet->bytes;
  GST_BUFFER_DATA (buf) = packet->packet;
714

715 716
  list =
      gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\201theora", 7,
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
      &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);

734 735 736 737 738 739
  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);
  }

740
  dec->tags = list;
741 742 743 744 745 746 747 748 749

  return GST_FLOW_OK;
}

static GstFlowReturn
theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstCaps *caps;
  gint par_num, par_den;
750
  GstFlowReturn ret = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
751
  GList *walk;
752
  guint32 fourcc;
753 754 755 756 757 758 759

  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;
760 761 762 763
   * 0:0 is allowed and can be interpreted as 1:1, so correct for it.
   * x:0 for other x isn't technically allowed, but it's seen in the wild and
   * is reasonable to treat the same. 
   */
764 765 766 767 768 769 770 771 772 773
  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;
  }
774
  if (par_den == 0) {
775 776 777 778 779
    par_num = par_den = 1;
  }
  /* theora has:
   *
   *  width/height : dimension of the encoded frame 
780 781
   *  pic_width/pic_height : dimension of the visible part
   *  pic_x/pic_y : offset in encoded frame where visible part starts
782
   */
783 784
  GST_DEBUG_OBJECT (dec, "dimension %dx%d, PAR %d/%d", dec->info.pic_width,
      dec->info.pic_height, par_num, par_den);
785
  GST_DEBUG_OBJECT (dec, "frame dimension %dx%d, offset %d:%d",
786 787
      dec->info.pic_width, dec->info.pic_height,
      dec->info.pic_x, dec->info.pic_y);
788

789
  if (dec->info.pixel_fmt == TH_PF_420) {
790 791
    dec->output_bpp = 12;       /* Average bits per pixel. */
    fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0');
792
  } else if (dec->info.pixel_fmt == TH_PF_422) {
793
    dec->output_bpp = 16;
794
    fourcc = GST_MAKE_FOURCC ('Y', '4', '2', 'B');
795
  } else if (dec->info.pixel_fmt == TH_PF_444) {
796 797 798
    dec->output_bpp = 24;
    fourcc = GST_MAKE_FOURCC ('Y', '4', '4', '4');
  } else {
799
    GST_ERROR_OBJECT (dec, "Invalid pixel format %d", dec->info.pixel_fmt);
800 801
    return GST_FLOW_ERROR;
  }
802 803

  if (dec->crop) {
804 805
    dec->width = dec->info.pic_width;
    dec->height = dec->info.pic_height;
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
    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;
823 824 825 826 827 828 829 830
    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 */
831
  dec->decoder = th_decode_alloc (&dec->info, dec->setup);
832 833

  caps = gst_caps_new_simple ("video/x-raw-yuv",
834
      "format", GST_TYPE_FOURCC, fourcc,
835 836
      "framerate", GST_TYPE_FRACTION,
      dec->info.fps_numerator, dec->info.fps_denominator,
837 838 839 840 841
      "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
842
  dec->have_header = TRUE;
Wim Taymans's avatar
Wim Taymans committed
843 844 845 846 847 848

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

851 852 853 854 855 856 857
  if (dec->tags) {
    gst_element_found_tags_for_pad (GST_ELEMENT_CAST (dec), dec->srcpad,
        dec->tags);
    dec->tags = NULL;
  }

  return ret;
858 859 860 861 862 863
}

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

866
  GST_DEBUG_OBJECT (dec, "parsing header packet");
867

868 869
  ret = th_decode_headerin (&dec->info, &dec->comment, &dec->setup, packet);
  if (ret < 0)
870 871
    goto header_read_error;

Wim Taymans's avatar
Wim Taymans committed
872 873
  switch (packet->packet[0]) {
    case 0x81:
874 875
      res = theora_handle_comment_packet (dec, packet);
      break;
Wim Taymans's avatar
Wim Taymans committed
876
    case 0x82:
877 878 879 880
      res = theora_handle_type_packet (dec, packet);
      break;
    default:
      /* ignore */
Wim Taymans's avatar
Wim Taymans committed
881 882 883
      g_warning ("unknown theora header packet found");
    case 0x80:
      /* nothing special, this is the identification header */
884 885 886 887 888 889 890 891 892 893 894 895 896 897
      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;
  }
}

898 899 900 901 902
/* 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)
{
903 904
  gboolean res = TRUE;
  GstClockTime in_ts, in_dur, stop;
905 906
  gint64 cstart, cstop;

907 908 909
  in_ts = GST_BUFFER_TIMESTAMP (buf);
  in_dur = GST_BUFFER_DURATION (buf);

910 911
  GST_LOG_OBJECT (dec,
      "timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT,
912
      GST_TIME_ARGS (in_ts), GST_TIME_ARGS (in_dur));
913

914 915
  /* can't clip without TIME segment */
  if (dec->segment.format != GST_FORMAT_TIME)
916 917
    goto beach;

918 919 920 921 922 923 924 925 926 927 928
  /* 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)))
929 930
    goto beach;

931 932
  /* update timestamp and possibly duration if the clipped stop time is
   * valid */
933
  GST_BUFFER_TIMESTAMP (buf) = cstart;
934 935
  if (GST_CLOCK_TIME_IS_VALID (cstop))
    GST_BUFFER_DURATION (buf) = cstop - cstart;
936 937

beach:
938
  GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
939 940 941
  return res;
}

942
static GstFlowReturn
943
theora_dec_push_forward (GstTheoraDec * dec, GstBuffer * buf)
944
{
945
  GstFlowReturn result = GST_FLOW_OK;
946

Wim Taymans's avatar
Wim Taymans committed
947
  if (clip_buffer (dec, buf)) {
948
    if (dec->discont) {
Wim Taymans's avatar
Wim Taymans committed
949
      GST_LOG_OBJECT (dec, "setting DISCONT");
950 951 952
      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
      dec->discont = FALSE;
    }
Wim Taymans's avatar
Wim Taymans committed
953 954 955
    result = gst_pad_push (dec->srcpad, buf);
  } else {
    gst_buffer_unref (buf);
Wim Taymans's avatar
Wim Taymans committed
956
  }