gstvp8dec.c 23.2 KB
Newer Older
1
/* VP8
2 3
 * Copyright (C) 2006 David Schleef <ds@schleef.org>
 * Copyright (C) 2008,2009,2010 Entropy Wave Inc
4
 * Copyright (C) 2010-2012 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
18 19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
20 21
 *
 */
22 23 24 25 26 27 28 29 30 31 32 33 34
/**
 * SECTION:element-vp8dec
 * @see_also: vp8enc, matroskademux
 *
 * This element decodes VP8 streams into raw video.
 * <ulink url="http://www.webmproject.org">VP8</ulink> is a royalty-free
 * video codec maintained by <ulink url="http://www.google.com/">Google
 * </ulink>. It's the successor of On2 VP3, which was the base of the
 * Theora video codec.
 *
 * <refsect2>
 * <title>Example pipeline</title>
 * |[
35
 * gst-launch-1.0 -v filesrc location=videotestsrc.webm ! matroskademux ! vp8dec ! videoconvert ! videoscale ! autovideosink
36 37 38
 * ]| This example pipeline will decode a WebM stream and decodes the VP8 video.
 * </refsect2>
 */
39 40 41 42 43

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

44 45
#ifdef HAVE_VP8_DECODER

46 47
#include <string.h>

48
#include "gstvp8dec.h"
49
#include "gstvp8utils.h"
50

51 52 53
#include <gst/video/gstvideometa.h>
#include <gst/video/gstvideopool.h>

54
GST_DEBUG_CATEGORY_STATIC (gst_vp8dec_debug);
55 56
#define GST_CAT_DEFAULT gst_vp8dec_debug

57
#define DEFAULT_POST_PROCESSING FALSE
58
#define DEFAULT_POST_PROCESSING_FLAGS (VP8_DEBLOCK | VP8_DEMACROBLOCK | VP8_MFQE)
59 60
#define DEFAULT_DEBLOCKING_LEVEL 4
#define DEFAULT_NOISE_LEVEL 0
61
#define DEFAULT_THREADS 0
62

63 64
enum
{
65 66 67 68
  PROP_0,
  PROP_POST_PROCESSING,
  PROP_POST_PROCESSING_FLAGS,
  PROP_DEBLOCKING_LEVEL,
69 70
  PROP_NOISE_LEVEL,
  PROP_THREADS
71 72
};

73 74 75 76 77 78 79 80 81
#define C_FLAGS(v) ((guint) v)
#define GST_VP8_DEC_TYPE_POST_PROCESSING_FLAGS (gst_vp8_dec_post_processing_flags_get_type())
static GType
gst_vp8_dec_post_processing_flags_get_type (void)
{
  static const GFlagsValue values[] = {
    {C_FLAGS (VP8_DEBLOCK), "Deblock", "deblock"},
    {C_FLAGS (VP8_DEMACROBLOCK), "Demacroblock", "demacroblock"},
    {C_FLAGS (VP8_ADDNOISE), "Add noise", "addnoise"},
82
    {C_FLAGS (VP8_MFQE), "Multi-frame quality enhancement", "mfqe"},
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    {0, NULL, NULL}
  };
  static volatile GType id = 0;

  if (g_once_init_enter ((gsize *) & id)) {
    GType _id;

    _id = g_flags_register_static ("GstVP8DecPostProcessingFlags", values);

    g_once_init_leave ((gsize *) & id, _id);
  }

  return id;
}

#undef C_FLAGS

100 101 102 103 104
static void gst_vp8_dec_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_vp8_dec_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

105 106 107 108
static gboolean gst_vp8_dec_start (GstVideoDecoder * decoder);
static gboolean gst_vp8_dec_stop (GstVideoDecoder * decoder);
static gboolean gst_vp8_dec_set_format (GstVideoDecoder * decoder,
    GstVideoCodecState * state);
109
static gboolean gst_vp8_dec_flush (GstVideoDecoder * decoder);
110 111
static GstFlowReturn gst_vp8_dec_handle_frame (GstVideoDecoder * decoder,
    GstVideoCodecFrame * frame);
112 113
static gboolean gst_vp8_dec_decide_allocation (GstVideoDecoder * decoder,
    GstQuery * query);
114 115 116 117 118 119 120 121 122 123 124 125

static GstStaticPadTemplate gst_vp8_dec_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-vp8")
    );

static GstStaticPadTemplate gst_vp8_dec_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
126
    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420"))
127 128
    );

129 130
#define parent_class gst_vp8_dec_parent_class
G_DEFINE_TYPE (GstVP8Dec, gst_vp8_dec, GST_TYPE_VIDEO_DECODER);
131 132 133 134 135

static void
gst_vp8_dec_class_init (GstVP8DecClass * klass)
{
  GObjectClass *gobject_class;
136
  GstElementClass *element_class;
137
  GstVideoDecoderClass *base_video_decoder_class;
138 139

  gobject_class = G_OBJECT_CLASS (klass);
140
  element_class = GST_ELEMENT_CLASS (klass);
141
  base_video_decoder_class = GST_VIDEO_DECODER_CLASS (klass);
142 143 144 145

  gobject_class->set_property = gst_vp8_dec_set_property;
  gobject_class->get_property = gst_vp8_dec_get_property;

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
  g_object_class_install_property (gobject_class, PROP_POST_PROCESSING,
      g_param_spec_boolean ("post-processing", "Post Processing",
          "Enable post processing", DEFAULT_POST_PROCESSING,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_POST_PROCESSING_FLAGS,
      g_param_spec_flags ("post-processing-flags", "Post Processing Flags",
          "Flags to control post processing",
          GST_VP8_DEC_TYPE_POST_PROCESSING_FLAGS, DEFAULT_POST_PROCESSING_FLAGS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_DEBLOCKING_LEVEL,
      g_param_spec_uint ("deblocking-level", "Deblocking Level",
          "Deblocking level",
          0, 16, DEFAULT_DEBLOCKING_LEVEL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_class, PROP_NOISE_LEVEL,
      g_param_spec_uint ("noise-level", "Noise Level",
          "Noise level",
          0, 16, DEFAULT_NOISE_LEVEL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

169 170
  g_object_class_install_property (gobject_class, PROP_THREADS,
      g_param_spec_uint ("threads", "Max Threads",
171 172
          "Maximum number of decoding threads (0 = automatic)",
          0, 16, DEFAULT_THREADS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
173

174 175 176 177 178
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_vp8_dec_src_template));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_vp8_dec_sink_template));

179
  gst_element_class_set_static_metadata (element_class,
180 181
      "On2 VP8 Decoder",
      "Codec/Decoder/Video",
182 183
      "Decode VP8 video streams", "David Schleef <ds@entropywave.com>, "
      "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
184

185 186
  base_video_decoder_class->start = GST_DEBUG_FUNCPTR (gst_vp8_dec_start);
  base_video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_vp8_dec_stop);
187
  base_video_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_vp8_dec_flush);
188 189 190 191
  base_video_decoder_class->set_format =
      GST_DEBUG_FUNCPTR (gst_vp8_dec_set_format);
  base_video_decoder_class->handle_frame =
      GST_DEBUG_FUNCPTR (gst_vp8_dec_handle_frame);
192
  base_video_decoder_class->decide_allocation = gst_vp8_dec_decide_allocation;
193 194

  GST_DEBUG_CATEGORY_INIT (gst_vp8dec_debug, "vp8dec", 0, "VP8 Decoder");
195 196 197
}

static void
198
gst_vp8_dec_init (GstVP8Dec * gst_vp8_dec)
199
{
200
  GstVideoDecoder *decoder = (GstVideoDecoder *) gst_vp8_dec;
201

202
  GST_DEBUG_OBJECT (gst_vp8_dec, "gst_vp8_dec_init");
203
  gst_video_decoder_set_packetized (decoder, TRUE);
204 205 206 207
  gst_vp8_dec->post_processing = DEFAULT_POST_PROCESSING;
  gst_vp8_dec->post_processing_flags = DEFAULT_POST_PROCESSING_FLAGS;
  gst_vp8_dec->deblocking_level = DEFAULT_DEBLOCKING_LEVEL;
  gst_vp8_dec->noise_level = DEFAULT_NOISE_LEVEL;
208 209

  gst_video_decoder_set_needs_format (decoder, TRUE);
210 211
  gst_video_decoder_set_use_default_pad_acceptcaps (decoder, TRUE);
  GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (decoder));
212 213 214 215 216 217
}

static void
gst_vp8_dec_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
218
  GstVP8Dec *dec;
219

220
  g_return_if_fail (GST_IS_VP8_DEC (object));
221
  dec = GST_VP8_DEC (object);
222

223
  GST_DEBUG_OBJECT (object, "gst_vp8_dec_set_property");
224
  switch (prop_id) {
225 226 227 228 229 230 231 232 233 234 235 236
    case PROP_POST_PROCESSING:
      dec->post_processing = g_value_get_boolean (value);
      break;
    case PROP_POST_PROCESSING_FLAGS:
      dec->post_processing_flags = g_value_get_flags (value);
      break;
    case PROP_DEBLOCKING_LEVEL:
      dec->deblocking_level = g_value_get_uint (value);
      break;
    case PROP_NOISE_LEVEL:
      dec->noise_level = g_value_get_uint (value);
      break;
237 238 239
    case PROP_THREADS:
      dec->threads = g_value_get_uint (value);
      break;
240
    default:
241
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
242 243 244 245 246 247 248 249
      break;
  }
}

static void
gst_vp8_dec_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
250
  GstVP8Dec *dec;
251

252
  g_return_if_fail (GST_IS_VP8_DEC (object));
253
  dec = GST_VP8_DEC (object);
254 255

  switch (prop_id) {
256 257 258 259 260 261 262 263 264 265 266 267
    case PROP_POST_PROCESSING:
      g_value_set_boolean (value, dec->post_processing);
      break;
    case PROP_POST_PROCESSING_FLAGS:
      g_value_set_flags (value, dec->post_processing_flags);
      break;
    case PROP_DEBLOCKING_LEVEL:
      g_value_set_uint (value, dec->deblocking_level);
      break;
    case PROP_NOISE_LEVEL:
      g_value_set_uint (value, dec->noise_level);
      break;
268 269 270
    case PROP_THREADS:
      g_value_set_uint (value, dec->threads);
      break;
271 272 273 274 275 276 277
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static gboolean
278
gst_vp8_dec_start (GstVideoDecoder * decoder)
279 280 281
{
  GstVP8Dec *gst_vp8_dec = GST_VP8_DEC (decoder);

282
  GST_DEBUG_OBJECT (gst_vp8_dec, "start");
283
  gst_vp8_dec->decoder_inited = FALSE;
284 285 286 287 288

  return TRUE;
}

static gboolean
289
gst_vp8_dec_stop (GstVideoDecoder * base_video_decoder)
290
{
291
  GstVP8Dec *gst_vp8_dec = GST_VP8_DEC (base_video_decoder);
292

293
  GST_DEBUG_OBJECT (gst_vp8_dec, "stop");
294 295 296 297 298

  if (gst_vp8_dec->output_state) {
    gst_video_codec_state_unref (gst_vp8_dec->output_state);
    gst_vp8_dec->output_state = NULL;
  }
299

300 301 302 303
  if (gst_vp8_dec->input_state) {
    gst_video_codec_state_unref (gst_vp8_dec->input_state);
    gst_vp8_dec->input_state = NULL;
  }
304

305 306 307
  if (gst_vp8_dec->decoder_inited)
    vpx_codec_destroy (&gst_vp8_dec->decoder);
  gst_vp8_dec->decoder_inited = FALSE;
308

309 310 311 312 313 314 315
  if (gst_vp8_dec->pool) {
    gst_buffer_pool_set_active (gst_vp8_dec->pool, FALSE);
    gst_object_unref (gst_vp8_dec->pool);
    gst_vp8_dec->pool = NULL;
    gst_vp8_dec->buf_size = 0;
  }

316 317 318
  return TRUE;
}

319
static gboolean
320
gst_vp8_dec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
321 322 323 324
{
  GstVP8Dec *gst_vp8_dec = GST_VP8_DEC (decoder);

  GST_DEBUG_OBJECT (gst_vp8_dec, "set_format");
325

Sebastian Dröge's avatar
Sebastian Dröge committed
326 327
  if (gst_vp8_dec->decoder_inited)
    vpx_codec_destroy (&gst_vp8_dec->decoder);
328 329
  gst_vp8_dec->decoder_inited = FALSE;

330 331 332 333 334 335
  if (gst_vp8_dec->output_state) {
    gst_video_codec_state_unref (gst_vp8_dec->output_state);
    gst_vp8_dec->output_state = NULL;
  }

  if (gst_vp8_dec->input_state) {
336
    gst_video_codec_state_unref (gst_vp8_dec->input_state);
337 338 339
    gst_vp8_dec->input_state = NULL;
  }

340 341
  gst_vp8_dec->input_state = gst_video_codec_state_ref (state);

342 343 344
  return TRUE;
}

345
static gboolean
346
gst_vp8_dec_flush (GstVideoDecoder * base_video_decoder)
347 348 349
{
  GstVP8Dec *decoder;

350
  GST_DEBUG_OBJECT (base_video_decoder, "flush");
351 352 353

  decoder = GST_VP8_DEC (base_video_decoder);

354 355 356 357
  if (decoder->output_state) {
    gst_video_codec_state_unref (decoder->output_state);
    decoder->output_state = NULL;
  }
358

359 360
  if (decoder->decoder_inited)
    vpx_codec_destroy (&decoder->decoder);
361 362 363 364 365 366 367 368 369 370
  decoder->decoder_inited = FALSE;

  return TRUE;
}

static void
gst_vp8_dec_send_tags (GstVP8Dec * dec)
{
  GstTagList *list;

371
  list = gst_tag_list_new_empty ();
372 373 374
  gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
      GST_TAG_VIDEO_CODEC, "VP8 video", NULL);

375
  gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (dec),
376
      gst_event_new_tag (list));
377 378
}

379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
#ifdef HAVE_VPX_1_4
struct Frame
{
  GstMapInfo info;
  GstBuffer *buffer;
};

static GstBuffer *
gst_vp8_dec_prepare_image (GstVP8Dec * dec, const vpx_image_t * img)
{
  gint comp;
  GstVideoMeta *vmeta;
  GstBuffer *buffer;
  struct Frame *frame = img->fb_priv;
  GstVideoInfo *info = &dec->output_state->info;

  buffer = gst_buffer_ref (frame->buffer);

  vmeta = gst_buffer_get_video_meta (buffer);
  vmeta->format = GST_VIDEO_INFO_FORMAT (info);
  vmeta->width = GST_VIDEO_INFO_WIDTH (info);
  vmeta->height = GST_VIDEO_INFO_HEIGHT (info);
  vmeta->n_planes = GST_VIDEO_INFO_N_PLANES (info);

  for (comp = 0; comp < 4; comp++) {
    vmeta->stride[comp] = img->stride[comp];
    vmeta->offset[comp] =
        img->planes[comp] ? img->planes[comp] - frame->info.data : 0;
  }

  /* FIXME This is a READ/WRITE mapped buffer see bug #754826 */

  return buffer;
}

static int
gst_vp8_dec_get_buffer_cb (gpointer priv, gsize min_size,
    vpx_codec_frame_buffer_t * fb)
{
  GstVP8Dec *dec = priv;
  GstBuffer *buffer;
  struct Frame *frame;
  GstFlowReturn ret;

  if (!dec->pool || dec->buf_size != min_size) {
    GstBufferPool *pool;
    GstStructure *config;
    GstCaps *caps;
    GstAllocator *allocator;
    GstAllocationParams params;

    if (dec->pool) {
      gst_buffer_pool_set_active (dec->pool, FALSE);
      gst_object_unref (dec->pool);
      dec->pool = NULL;
      dec->buf_size = 0;
    }

    gst_video_decoder_get_allocator (GST_VIDEO_DECODER (dec), &allocator,
        &params);

    if (allocator &&
        GST_OBJECT_FLAG_IS_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC)) {
      gst_object_unref (allocator);
      allocator = NULL;
    }

    pool = gst_buffer_pool_new ();
    config = gst_buffer_pool_get_config (pool);
    gst_buffer_pool_config_set_allocator (config, allocator, &params);
    caps = gst_caps_from_string ("video/internal");
    gst_buffer_pool_config_set_params (config, caps, min_size, 2, 0);
    gst_caps_unref (caps);
    gst_buffer_pool_set_config (pool, config);

    if (allocator)
      gst_object_unref (allocator);

    if (!gst_buffer_pool_set_active (pool, TRUE)) {
      GST_WARNING ("Failed to create internal pool");
      gst_object_unref (pool);
      return -1;
    }

    dec->pool = pool;
    dec->buf_size = min_size;
  }

  ret = gst_buffer_pool_acquire_buffer (dec->pool, &buffer, NULL);
  if (ret != GST_FLOW_OK) {
    GST_WARNING ("Failed to acquire buffer from internal pool.");
    return -1;
  }

  frame = g_new0 (struct Frame, 1);
  if (!gst_buffer_map (buffer, &frame->info, GST_MAP_READWRITE)) {
    gst_buffer_unref (buffer);
    g_free (frame);
    GST_WARNING ("Failed to map buffer from internal pool.");
    return -1;
  }

  fb->size = frame->info.size;
  fb->data = frame->info.data;
  frame->buffer = buffer;
  fb->priv = frame;

  GST_TRACE_OBJECT (priv, "Allocated buffer %p", frame->buffer);

  return 0;
}

static int
gst_vp8_dec_release_buffer_cb (gpointer priv, vpx_codec_frame_buffer_t * fb)
{
  struct Frame *frame = fb->priv;

  GST_TRACE_OBJECT (priv, "Release buffer %p", frame->buffer);

  g_assert (frame);
  gst_buffer_unmap (frame->buffer, &frame->info);
  gst_buffer_unref (frame->buffer);
  g_free (frame);

  return 0;
}
#endif

507 508 509 510
static void
gst_vp8_dec_image_to_buffer (GstVP8Dec * dec, const vpx_image_t * img,
    GstBuffer * buffer)
{
511 512 513 514 515 516 517
  int deststride, srcstride, height, width, line, comp;
  guint8 *dest, *src;
  GstVideoFrame frame;
  GstVideoInfo *info = &dec->output_state->info;

  if (!gst_video_frame_map (&frame, info, buffer, GST_MAP_WRITE)) {
    GST_ERROR_OBJECT (dec, "Could not map video buffer");
518
    return;
519 520 521 522 523
  }

  for (comp = 0; comp < 3; comp++) {
    dest = GST_VIDEO_FRAME_COMP_DATA (&frame, comp);
    src = img->planes[comp];
524 525
    width = GST_VIDEO_FRAME_COMP_WIDTH (&frame, comp)
        * GST_VIDEO_FRAME_COMP_PSTRIDE (&frame, comp);
526 527 528 529
    height = GST_VIDEO_FRAME_COMP_HEIGHT (&frame, comp);
    deststride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, comp);
    srcstride = img->stride[comp];

530 531 532 533 534 535 536 537 538 539 540 541
    if (srcstride == deststride) {
      GST_TRACE_OBJECT (dec, "Stride matches. Comp %d: %d, copying full plane",
          comp, srcstride);
      memcpy (dest, src, srcstride * height);
    } else {
      GST_TRACE_OBJECT (dec, "Stride mismatch. Comp %d: %d != %d, copying "
          "line by line.", comp, srcstride, deststride);
      for (line = 0; line < height; line++) {
        memcpy (dest, src, width);
        dest += deststride;
        src += srcstride;
      }
542 543 544 545
    }
  }

  gst_video_frame_unmap (&frame);
546 547
}

548
static GstFlowReturn
549
open_codec (GstVP8Dec * dec, GstVideoCodecFrame * frame)
550
{
551 552 553
  int flags = 0;
  vpx_codec_stream_info_t stream_info;
  vpx_codec_caps_t caps;
554
  vpx_codec_dec_cfg_t cfg;
555
  GstVideoCodecState *state = dec->input_state;
556
  vpx_codec_err_t status;
557
  GstMapInfo minfo;
558

559
  memset (&stream_info, 0, sizeof (stream_info));
560
  memset (&cfg, 0, sizeof (cfg));
561
  stream_info.sz = sizeof (stream_info);
562

563 564 565 566 567
  if (!gst_buffer_map (frame->input_buffer, &minfo, GST_MAP_READ)) {
    GST_ERROR_OBJECT (dec, "Failed to map input buffer");
    return GST_FLOW_ERROR;
  }

568
  status = vpx_codec_peek_stream_info (&vpx_codec_vp8_dx_algo,
569 570 571
      minfo.data, minfo.size, &stream_info);

  gst_buffer_unmap (frame->input_buffer, &minfo);
572

573 574 575
  if (status != VPX_CODEC_OK) {
    GST_WARNING_OBJECT (dec, "VPX preprocessing error: %s",
        gst_vpx_error_name (status));
576
    gst_video_decoder_drop_frame (GST_VIDEO_DECODER (dec), frame);
577 578 579
    return GST_FLOW_CUSTOM_SUCCESS_1;
  }
  if (!stream_info.is_kf) {
580
    GST_WARNING_OBJECT (dec, "No keyframe, skipping");
581
    gst_video_decoder_drop_frame (GST_VIDEO_DECODER (dec), frame);
582
    return GST_FLOW_CUSTOM_SUCCESS_1;
583
  }
584

585 586 587
  g_assert (dec->output_state == NULL);
  dec->output_state =
      gst_video_decoder_set_output_state (GST_VIDEO_DECODER (dec),
588
      GST_VIDEO_FORMAT_I420, stream_info.w, stream_info.h, state);
589
  gst_video_decoder_negotiate (GST_VIDEO_DECODER (dec));
590
  gst_vp8_dec_send_tags (dec);
591

592 593
  cfg.w = stream_info.w;
  cfg.h = stream_info.h;
594 595 596 597 598

  if (dec->threads > 0)
    cfg.threads = dec->threads;
  else
    cfg.threads = g_get_num_processors ();
599

600
  caps = vpx_codec_get_caps (&vpx_codec_vp8_dx_algo);
601

602 603 604 605 606
  if (dec->post_processing) {
    if (!(caps & VPX_CODEC_CAP_POSTPROC)) {
      GST_WARNING_OBJECT (dec, "Decoder does not support post processing");
    } else {
      flags |= VPX_CODEC_USE_POSTPROC;
607
    }
608
  }
609

610
  status =
611
      vpx_codec_dec_init (&dec->decoder, &vpx_codec_vp8_dx_algo, &cfg, flags);
612 613 614 615 616 617
  if (status != VPX_CODEC_OK) {
    GST_ELEMENT_ERROR (dec, LIBRARY, INIT,
        ("Failed to initialize VP8 decoder"), ("%s",
            gst_vpx_error_name (status)));
    return GST_FLOW_ERROR;
  }
618

619 620
  if ((caps & VPX_CODEC_CAP_POSTPROC) && dec->post_processing) {
    vp8_postproc_cfg_t pp_cfg = { 0, };
621

622 623 624
    pp_cfg.post_proc_flag = dec->post_processing_flags;
    pp_cfg.deblocking_level = dec->deblocking_level;
    pp_cfg.noise_level = dec->noise_level;
625

626
    status = vpx_codec_control (&dec->decoder, VP8_SET_POSTPROC, &pp_cfg);
627
    if (status != VPX_CODEC_OK) {
628 629
      GST_WARNING_OBJECT (dec, "Couldn't set postprocessing settings: %s",
          gst_vpx_error_name (status));
630
    }
631
  }
632 633 634 635
#ifdef HAVE_VPX_1_4
  vpx_codec_set_frame_buffer_functions (&dec->decoder,
      gst_vp8_dec_get_buffer_cb, gst_vp8_dec_release_buffer_cb, dec);
#endif
636

637
  dec->decoder_inited = TRUE;
638

639 640
  return GST_FLOW_OK;
}
641

642 643 644 645 646 647 648 649 650 651
static GstFlowReturn
gst_vp8_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
{
  GstVP8Dec *dec;
  GstFlowReturn ret = GST_FLOW_OK;
  vpx_codec_err_t status;
  vpx_codec_iter_t iter = NULL;
  vpx_image_t *img;
  long decoder_deadline = 0;
  GstClockTimeDiff deadline;
652
  GstMapInfo minfo;
653 654
  GstVideoInfo *info;
  GstVideoCodecState *new_output_state;
655

656
  GST_LOG_OBJECT (decoder, "handle_frame");
657

658 659
  dec = GST_VP8_DEC (decoder);

660
  if (!dec->decoder_inited) {
661
    ret = open_codec (dec, frame);
662 663
    if (ret == GST_FLOW_CUSTOM_SUCCESS_1)
      return GST_FLOW_OK;
664 665
    else if (ret != GST_FLOW_OK)
      return ret;
666
  }
667

668
  deadline = gst_video_decoder_get_max_decode_time (decoder, frame);
669 670 671 672 673 674 675 676
  if (deadline < 0) {
    decoder_deadline = 1;
  } else if (deadline == G_MAXINT64) {
    decoder_deadline = 0;
  } else {
    decoder_deadline = MAX (1, deadline / GST_MSECOND);
  }

677 678 679 680 681
  if (!gst_buffer_map (frame->input_buffer, &minfo, GST_MAP_READ)) {
    GST_ERROR_OBJECT (dec, "Failed to map input buffer");
    return GST_FLOW_ERROR;
  }

682
  status = vpx_codec_decode (&dec->decoder,
683 684 685 686
      minfo.data, minfo.size, NULL, decoder_deadline);

  gst_buffer_unmap (frame->input_buffer, &minfo);

687
  if (status) {
688 689 690
    GST_VIDEO_DECODER_ERROR (decoder, 1, LIBRARY, ENCODE,
        ("Failed to decode frame"), ("%s", gst_vpx_error_name (status)), ret);
    return ret;
691 692
  }

693 694
  img = vpx_codec_get_frame (&dec->decoder, &iter);
  if (img) {
695 696 697 698 699 700 701 702
    if (img->fmt != VPX_IMG_FMT_I420) {
      vpx_img_free (img);
      GST_ELEMENT_ERROR (decoder, LIBRARY, ENCODE,
          ("Failed to decode frame"), ("Unsupported color format %d",
              img->fmt));
      return GST_FLOW_ERROR;
    }

703 704 705
    if (deadline < 0) {
      GST_LOG_OBJECT (dec, "Skipping late frame (%f s past deadline)",
          (double) -deadline / GST_SECOND);
706
      gst_video_decoder_drop_frame (decoder, frame);
707
    } else {
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
      info = &dec->output_state->info;
      if (GST_VIDEO_INFO_WIDTH (info) != img->d_w
          || GST_VIDEO_INFO_HEIGHT (info) != img->d_h) {
        GST_DEBUG_OBJECT (dec,
            "Changed output resolution was %d x %d now is got %u x %u (display %u x %u)",
            GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), img->w,
            img->h, img->d_w, img->d_h);

        new_output_state =
            gst_video_decoder_set_output_state (GST_VIDEO_DECODER (dec),
            GST_VIDEO_FORMAT_I420, img->d_w, img->d_h, dec->output_state);
        if (dec->output_state) {
          gst_video_codec_state_unref (dec->output_state);
        }
        dec->output_state = new_output_state;
        /* No need to call negotiate() here, it will be automatically called
         * by allocate_output_frame() below */
      }
726 727 728
#ifdef HAVE_VPX_1_4
      if (img->fb_priv && dec->have_video_meta) {
        frame->output_buffer = gst_vp8_dec_prepare_image (dec, img);
729
        ret = gst_video_decoder_finish_frame (decoder, frame);
730 731 732 733 734 735 736 737 738 739 740
      } else
#endif
      {
        ret = gst_video_decoder_allocate_output_frame (decoder, frame);

        if (ret == GST_FLOW_OK) {
          gst_vp8_dec_image_to_buffer (dec, img, frame->output_buffer);
          ret = gst_video_decoder_finish_frame (decoder, frame);
        } else {
          gst_video_decoder_drop_frame (decoder, frame);
        }
741
      }
742
    }
743

744 745 746 747
    vpx_img_free (img);

    while ((img = vpx_codec_get_frame (&dec->decoder, &iter))) {
      GST_WARNING_OBJECT (decoder, "Multiple decoded frames... dropping");
748
      vpx_img_free (img);
749
    }
750 751
  } else {
    /* Invisible frame */
752 753
    GST_VIDEO_CODEC_FRAME_SET_DECODE_ONLY (frame);
    gst_video_decoder_finish_frame (decoder, frame);
754
  }
755 756 757

  return ret;
}
758

759 760 761
static gboolean
gst_vp8_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
{
762
  GstVP8Dec *dec = GST_VP8_DEC (bdec);
763 764 765 766 767 768 769 770 771 772 773
  GstBufferPool *pool;
  GstStructure *config;

  if (!GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (bdec, query))
    return FALSE;

  g_assert (gst_query_get_n_allocation_pools (query) > 0);
  gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
  g_assert (pool != NULL);

  config = gst_buffer_pool_get_config (pool);
774
  if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
775 776
    gst_buffer_pool_config_add_option (config,
        GST_BUFFER_POOL_OPTION_VIDEO_META);
777
    dec->have_video_meta = TRUE;
778 779 780 781 782 783 784
  }
  gst_buffer_pool_set_config (pool, config);
  gst_object_unref (pool);

  return TRUE;
}

785
#endif /* HAVE_VP8_DECODER */