/*
 *  gstvaapidecoder_vp8.c - VP8 decoder
 *
 *  Copyright (C) 2013-2014 Intel Corporation
 *    Author: Halley Zhao <halley.zhao@intel.com>
 *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2.1
 *  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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301 USA
 */

/**
 * SECTION:gstvaapidecoder_vp8
 * @short_description: VP8 decoder
 */

#include "sysdeps.h"
#include <gst/codecparsers/gstvp8parser.h>
#include "gstvaapidecoder_vp8.h"
#include "gstvaapidecoder_objects.h"
#include "gstvaapidecoder_priv.h"
#include "gstvaapidisplay_priv.h"

#include "gstvaapicompat.h"

#define DEBUG 1
#include "gstvaapidebug.h"

#define GST_VAAPI_DECODER_VP8_CAST(decoder) \
  ((GstVaapiDecoderVp8 *)(decoder))

typedef struct _GstVaapiDecoderVp8Private GstVaapiDecoderVp8Private;
typedef struct _GstVaapiDecoderVp8Class GstVaapiDecoderVp8Class;

struct _GstVaapiDecoderVp8Private
{
  GstVaapiProfile profile;
  guint width;
  guint height;
  GstVp8Parser parser;
  GstVp8FrameHdr frame_hdr;
  GstVaapiPicture *last_picture;
  GstVaapiPicture *golden_ref_picture;
  GstVaapiPicture *alt_ref_picture;
  GstVaapiPicture *current_picture;
  guint size_changed:1;
};

/**
 * GstVaapiDecoderVp8:
 *
 * A decoder based on Vp8.
 */
struct _GstVaapiDecoderVp8
{
  /*< private > */
  GstVaapiDecoder parent_instance;

  GstVaapiDecoderVp8Private priv;
};

/**
 * GstVaapiDecoderVp8Class:
 *
 * A decoder class based on Vp8.
 */
struct _GstVaapiDecoderVp8Class
{
  /*< private > */
  GstVaapiDecoderClass parent_class;
};

G_DEFINE_TYPE (GstVaapiDecoderVp8, gst_vaapi_decoder_vp8,
    GST_TYPE_VAAPI_DECODER);

static GstVaapiDecoderStatus
get_status (GstVp8ParserResult result)
{
  GstVaapiDecoderStatus status;

  switch (result) {
    case GST_VP8_PARSER_OK:
      status = GST_VAAPI_DECODER_STATUS_SUCCESS;
      break;
    case GST_VP8_PARSER_ERROR:
      status = GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
      break;
    default:
      status = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
      break;
  }
  return status;
}

static void
gst_vaapi_decoder_vp8_close (GstVaapiDecoderVp8 * decoder)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;

  gst_vaapi_picture_replace (&priv->last_picture, NULL);
  gst_vaapi_picture_replace (&priv->golden_ref_picture, NULL);
  gst_vaapi_picture_replace (&priv->alt_ref_picture, NULL);
  gst_vaapi_picture_replace (&priv->current_picture, NULL);
}

static gboolean
gst_vaapi_decoder_vp8_open (GstVaapiDecoderVp8 * decoder)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;

  gst_vaapi_decoder_vp8_close (decoder);
  gst_vp8_parser_init (&priv->parser);
  return TRUE;
}

static void
gst_vaapi_decoder_vp8_destroy (GstVaapiDecoder * base_decoder)
{
  GstVaapiDecoderVp8 *const decoder = GST_VAAPI_DECODER_VP8_CAST (base_decoder);

  gst_vaapi_decoder_vp8_close (decoder);
}

static gboolean
gst_vaapi_decoder_vp8_create (GstVaapiDecoder * base_decoder)
{
  GstVaapiDecoderVp8 *const decoder = GST_VAAPI_DECODER_VP8_CAST (base_decoder);
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;

  if (!gst_vaapi_decoder_vp8_open (decoder))
    return FALSE;

  priv->profile = GST_VAAPI_PROFILE_UNKNOWN;
  return TRUE;
}

static GstVaapiDecoderStatus
gst_vaapi_decoder_vp8_reset (GstVaapiDecoder * base_decoder)
{
  gst_vaapi_decoder_vp8_destroy (base_decoder);
  if (gst_vaapi_decoder_vp8_create (base_decoder))
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
  return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
}

static GstVaapiDecoderStatus
ensure_context (GstVaapiDecoderVp8 * decoder)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  const GstVaapiProfile profile = GST_VAAPI_PROFILE_VP8;
  const GstVaapiEntrypoint entrypoint = GST_VAAPI_ENTRYPOINT_VLD;
  gboolean reset_context = FALSE;

  if (priv->profile != profile) {
    if (!gst_vaapi_display_has_decoder (GST_VAAPI_DECODER_DISPLAY (decoder),
            profile, entrypoint))
      return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE;

    priv->profile = profile;
    reset_context = TRUE;
  }

  if (priv->size_changed) {
    GST_DEBUG ("size changed");
    priv->size_changed = FALSE;
    reset_context = TRUE;
  }

  if (reset_context) {
    GstVaapiContextInfo info;
    /* *INDENT-OFF* */
    info = (GstVaapiContextInfo) {
      .profile = priv->profile,
      .entrypoint = entrypoint,
      .chroma_type = GST_VAAPI_CHROMA_TYPE_YUV420,
      .width = priv->width,
      .height = priv->height,
      .ref_frames = 3,
    };
    /* *INDENT-ON* */

    reset_context =
        gst_vaapi_decoder_ensure_context (GST_VAAPI_DECODER (decoder), &info);

    if (!reset_context)
      return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
  }
  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
ensure_quant_matrix (GstVaapiDecoderVp8 * decoder, GstVaapiPicture * picture)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr;
  GstVp8Segmentation *const seg = &priv->parser.segmentation;
  VAIQMatrixBufferVP8 *iq_matrix;
  const gint8 QI_MAX = 127;
  gint8 qi, qi_base;
  gint i;

  picture->iq_matrix = GST_VAAPI_IQ_MATRIX_NEW (VP8, decoder);
  if (!picture->iq_matrix) {
    GST_ERROR ("failed to allocate IQ matrix");
    return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
  }
  iq_matrix = picture->iq_matrix->param;

  /* Fill in VAIQMatrixBufferVP8 */
  for (i = 0; i < 4; i++) {
    if (seg->segmentation_enabled) {
      qi_base = seg->quantizer_update_value[i];
      if (!seg->segment_feature_mode)   // 0 means delta update
        qi_base += frame_hdr->quant_indices.y_ac_qi;
    } else
      qi_base = frame_hdr->quant_indices.y_ac_qi;

    qi = qi_base;
    iq_matrix->quantization_index[i][0] = CLAMP (qi, 0, QI_MAX);
    qi = qi_base + frame_hdr->quant_indices.y_dc_delta;
    iq_matrix->quantization_index[i][1] = CLAMP (qi, 0, QI_MAX);
    qi = qi_base + frame_hdr->quant_indices.y2_dc_delta;
    iq_matrix->quantization_index[i][2] = CLAMP (qi, 0, QI_MAX);
    qi = qi_base + frame_hdr->quant_indices.y2_ac_delta;
    iq_matrix->quantization_index[i][3] = CLAMP (qi, 0, QI_MAX);
    qi = qi_base + frame_hdr->quant_indices.uv_dc_delta;
    iq_matrix->quantization_index[i][4] = CLAMP (qi, 0, QI_MAX);
    qi = qi_base + frame_hdr->quant_indices.uv_ac_delta;
    iq_matrix->quantization_index[i][5] = CLAMP (qi, 0, QI_MAX);
  }
  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
ensure_probability_table (GstVaapiDecoderVp8 * decoder,
    GstVaapiPicture * picture)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr;
  VAProbabilityDataBufferVP8 *prob_table;

  picture->prob_table = GST_VAAPI_PROBABILITY_TABLE_NEW (VP8, decoder);
  if (!picture->prob_table) {
    GST_ERROR ("failed to allocate probality table");
    return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
  }
  prob_table = picture->prob_table->param;

  /* Fill in VAProbabilityDataBufferVP8 */
  memcpy (prob_table->dct_coeff_probs, frame_hdr->token_probs.prob,
      sizeof (frame_hdr->token_probs.prob));

  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static void
init_picture (GstVaapiDecoderVp8 * decoder, GstVaapiPicture * picture)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr;

  picture->structure = GST_VAAPI_PICTURE_STRUCTURE_FRAME;
  picture->type = frame_hdr->key_frame ? GST_VAAPI_PICTURE_TYPE_I :
      GST_VAAPI_PICTURE_TYPE_P;
  picture->pts = GST_VAAPI_DECODER_CODEC_FRAME (decoder)->pts;

  if (!frame_hdr->show_frame)
    GST_VAAPI_PICTURE_FLAG_SET (picture, GST_VAAPI_PICTURE_FLAG_SKIPPED);
}

static gboolean
fill_picture (GstVaapiDecoderVp8 * decoder, GstVaapiPicture * picture)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  VAPictureParameterBufferVP8 *const pic_param = picture->param;
  GstVp8Parser *const parser = &priv->parser;
  GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr;
  GstVp8Segmentation *const seg = &parser->segmentation;
  gint i;

  /* Fill in VAPictureParameterBufferVP8 */
  pic_param->frame_width = priv->width;
  pic_param->frame_height = priv->height;

  pic_param->last_ref_frame = VA_INVALID_SURFACE;
  pic_param->golden_ref_frame = VA_INVALID_SURFACE;
  pic_param->alt_ref_frame = VA_INVALID_SURFACE;
  if (!frame_hdr->key_frame) {
    if (priv->last_picture)
      pic_param->last_ref_frame = priv->last_picture->surface_id;
    if (priv->golden_ref_picture)
      pic_param->golden_ref_frame = priv->golden_ref_picture->surface_id;
    if (priv->alt_ref_picture)
      pic_param->alt_ref_frame = priv->alt_ref_picture->surface_id;
  }
  pic_param->out_of_loop_frame = VA_INVALID_SURFACE;    // not used currently

  pic_param->pic_fields.value = 0;
  pic_param->pic_fields.bits.key_frame = !frame_hdr->key_frame;
  pic_param->pic_fields.bits.version = frame_hdr->version;
  pic_param->pic_fields.bits.segmentation_enabled = seg->segmentation_enabled;
  pic_param->pic_fields.bits.update_mb_segmentation_map =
      seg->update_mb_segmentation_map;
  pic_param->pic_fields.bits.update_segment_feature_data =
      seg->update_segment_feature_data;
  pic_param->pic_fields.bits.filter_type = frame_hdr->filter_type;
  pic_param->pic_fields.bits.sharpness_level = frame_hdr->sharpness_level;
  pic_param->pic_fields.bits.loop_filter_adj_enable =
      parser->mb_lf_adjust.loop_filter_adj_enable;
  pic_param->pic_fields.bits.mode_ref_lf_delta_update =
      parser->mb_lf_adjust.mode_ref_lf_delta_update;
  pic_param->pic_fields.bits.sign_bias_golden = frame_hdr->sign_bias_golden;
  pic_param->pic_fields.bits.sign_bias_alternate =
      frame_hdr->sign_bias_alternate;
  pic_param->pic_fields.bits.mb_no_coeff_skip = frame_hdr->mb_no_skip_coeff;

  for (i = 0; i < 3; i++)
    pic_param->mb_segment_tree_probs[i] = seg->segment_prob[i];

  for (i = 0; i < 4; i++) {
    gint8 level;
    if (seg->segmentation_enabled) {
      level = seg->lf_update_value[i];
      if (!seg->segment_feature_mode)   // 0 means delta update
        level += frame_hdr->loop_filter_level;
    } else
      level = frame_hdr->loop_filter_level;
    pic_param->loop_filter_level[i] = CLAMP (level, 0, 63);

    pic_param->loop_filter_deltas_ref_frame[i] =
        parser->mb_lf_adjust.ref_frame_delta[i];
    pic_param->loop_filter_deltas_mode[i] =
        parser->mb_lf_adjust.mb_mode_delta[i];
  }

  /* In decoding, the only loop filter settings that matter are those
     in the frame header (9.1) */
  pic_param->pic_fields.bits.loop_filter_disable =
      frame_hdr->loop_filter_level == 0;

  pic_param->prob_skip_false = frame_hdr->prob_skip_false;
  pic_param->prob_intra = frame_hdr->prob_intra;
  pic_param->prob_last = frame_hdr->prob_last;
  pic_param->prob_gf = frame_hdr->prob_gf;

  memcpy (pic_param->y_mode_probs, frame_hdr->mode_probs.y_prob,
      sizeof (frame_hdr->mode_probs.y_prob));
  memcpy (pic_param->uv_mode_probs, frame_hdr->mode_probs.uv_prob,
      sizeof (frame_hdr->mode_probs.uv_prob));
  memcpy (pic_param->mv_probs, frame_hdr->mv_probs.prob,
      sizeof (frame_hdr->mv_probs));

  pic_param->bool_coder_ctx.range = frame_hdr->rd_range;
  pic_param->bool_coder_ctx.value = frame_hdr->rd_value;
  pic_param->bool_coder_ctx.count = frame_hdr->rd_count;

  return TRUE;
}

static gboolean
fill_slice (GstVaapiDecoderVp8 * decoder, GstVaapiSlice * slice)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  VASliceParameterBufferVP8 *const slice_param = slice->param;
  GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr;
  gint i;

  /* Fill in VASliceParameterBufferVP8 */
  slice_param->slice_data_offset = frame_hdr->data_chunk_size;
  slice_param->macroblock_offset = frame_hdr->header_size;
  slice_param->num_of_partitions =
      (1 << frame_hdr->log2_nbr_of_dct_partitions) + 1;

  slice_param->partition_size[0] =
      frame_hdr->first_part_size - ((slice_param->macroblock_offset + 7) >> 3);
  for (i = 1; i < slice_param->num_of_partitions; i++)
    slice_param->partition_size[i] = frame_hdr->partition_size[i - 1];
  for (; i < G_N_ELEMENTS (slice_param->partition_size); i++)
    slice_param->partition_size[i] = 0;

  return TRUE;
}

static GstVaapiDecoderStatus
decode_slice (GstVaapiDecoderVp8 * decoder, GstVaapiPicture * picture,
    const guchar * buf, guint buf_size)
{
  GstVaapiSlice *slice;

  slice = GST_VAAPI_SLICE_NEW (VP8, decoder, buf, buf_size);
  if (!slice) {
    GST_ERROR ("failed to allocate slice");
    return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
  }

  if (!fill_slice (decoder, slice)) {
    gst_vaapi_mini_object_unref (GST_VAAPI_MINI_OBJECT (slice));
    return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
  }

  gst_vaapi_picture_add_slice (GST_VAAPI_PICTURE_CAST (picture), slice);
  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
decode_picture (GstVaapiDecoderVp8 * decoder, const guchar * buf,
    guint buf_size)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  GstVaapiPicture *picture;
  GstVaapiDecoderStatus status;

  status = ensure_context (decoder);
  if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
    return status;

  /* Create new picture */
  picture = GST_VAAPI_PICTURE_NEW (VP8, decoder);
  if (!picture) {
    GST_ERROR ("failed to allocate picture");
    return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
  }
  gst_vaapi_picture_replace (&priv->current_picture, picture);
  gst_vaapi_picture_unref (picture);

  status = ensure_quant_matrix (decoder, picture);
  if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
    return status;

  status = ensure_probability_table (decoder, picture);
  if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
    return status;

  init_picture (decoder, picture);
  if (!fill_picture (decoder, picture))
    return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;

  return decode_slice (decoder, picture, buf, buf_size);
}

static void
update_ref_frames (GstVaapiDecoderVp8 * decoder)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  GstVaapiPicture *picture = priv->current_picture;
  GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr;

  // update picture reference
  if (frame_hdr->key_frame) {
    gst_vaapi_picture_replace (&priv->golden_ref_picture, picture);
    gst_vaapi_picture_replace (&priv->alt_ref_picture, picture);
  } else {
    // process refresh_alternate_frame/copy_buffer_to_alternate first
    if (frame_hdr->refresh_alternate_frame) {
      gst_vaapi_picture_replace (&priv->alt_ref_picture, picture);
    } else {
      switch (frame_hdr->copy_buffer_to_alternate) {
        case 0:
          // do nothing
          break;
        case 1:
          gst_vaapi_picture_replace (&priv->alt_ref_picture,
              priv->last_picture);
          break;
        case 2:
          gst_vaapi_picture_replace (&priv->alt_ref_picture,
              priv->golden_ref_picture);
          break;
        default:
          GST_WARNING
              ("WARNING: VP8 decoder: unrecognized copy_buffer_to_alternate");
      }
    }

    if (frame_hdr->refresh_golden_frame) {
      gst_vaapi_picture_replace (&priv->golden_ref_picture, picture);
    } else {
      switch (frame_hdr->copy_buffer_to_golden) {
        case 0:
          // do nothing
          break;
        case 1:
          gst_vaapi_picture_replace (&priv->golden_ref_picture,
              priv->last_picture);
          break;
        case 2:
          gst_vaapi_picture_replace (&priv->golden_ref_picture,
              priv->alt_ref_picture);
          break;
        default:
          GST_WARNING
              ("WARNING: VP8 decoder: unrecognized copy_buffer_to_golden");
      }
    }
  }
  if (frame_hdr->key_frame || frame_hdr->refresh_last)
    gst_vaapi_picture_replace (&priv->last_picture, picture);
}

static GstVaapiDecoderStatus
decode_current_picture (GstVaapiDecoderVp8 * decoder)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  GstVaapiPicture *const picture = priv->current_picture;

  if (!picture)
    return GST_VAAPI_DECODER_STATUS_SUCCESS;

  update_ref_frames (decoder);
  if (!gst_vaapi_picture_decode (picture))
    goto error;
  if (!gst_vaapi_picture_output (picture))
    goto error;
  gst_vaapi_picture_replace (&priv->current_picture, NULL);
  return GST_VAAPI_DECODER_STATUS_SUCCESS;

  /* ERRORS */
error:
  {
    /* XXX: fix for cases where first field failed to be decoded */
    gst_vaapi_picture_replace (&priv->current_picture, NULL);
    return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
  }
}

static GstVaapiDecoderStatus
parse_frame_header (GstVaapiDecoderVp8 * decoder, const guchar * buf,
    guint buf_size, GstVp8FrameHdr * frame_hdr)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  GstVp8ParserResult result;

  memset (frame_hdr, 0, sizeof (*frame_hdr));
  result = gst_vp8_parser_parse_frame_header (&priv->parser, frame_hdr,
      buf, buf_size);
  if (result != GST_VP8_PARSER_OK)
    return get_status (result);

  if (frame_hdr->key_frame &&
      (frame_hdr->width != priv->width || frame_hdr->height != priv->height)) {
    priv->width = frame_hdr->width;
    priv->height = frame_hdr->height;
    priv->size_changed = TRUE;
  }
  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
gst_vaapi_decoder_vp8_parse (GstVaapiDecoder * base_decoder,
    GstAdapter * adapter, gboolean at_eos, GstVaapiDecoderUnit * unit)
{
  guint flags = 0;

  unit->size = gst_adapter_available (adapter);

  /* The whole frame is available */
  flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START;
  flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
  flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END;
  GST_VAAPI_DECODER_UNIT_FLAG_SET (unit, flags);
  return GST_VAAPI_DECODER_STATUS_SUCCESS;

}

static GstVaapiDecoderStatus
decode_buffer (GstVaapiDecoderVp8 * decoder, const guchar * buf, guint buf_size)
{
  GstVaapiDecoderVp8Private *const priv = &decoder->priv;
  GstVaapiDecoderStatus status;

  status = parse_frame_header (decoder, buf, buf_size, &priv->frame_hdr);
  if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
    return status;

  return decode_picture (decoder, buf, buf_size);
}

static GstVaapiDecoderStatus
gst_vaapi_decoder_vp8_decode (GstVaapiDecoder * base_decoder,
    GstVaapiDecoderUnit * unit)
{
  GstVaapiDecoderVp8 *const decoder = GST_VAAPI_DECODER_VP8_CAST (base_decoder);
  GstVaapiDecoderStatus status;
  GstBuffer *const buffer =
      GST_VAAPI_DECODER_CODEC_FRAME (decoder)->input_buffer;
  GstMapInfo map_info;

  if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ)) {
    GST_ERROR ("failed to map buffer");
    return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
  }

  status = decode_buffer (decoder, map_info.data + unit->offset, unit->size);
  gst_buffer_unmap (buffer, &map_info);
  if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
    return status;
  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
gst_vaapi_decoder_vp8_start_frame (GstVaapiDecoder * base_decoder,
    GstVaapiDecoderUnit * base_unit)
{
  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
gst_vaapi_decoder_vp8_end_frame (GstVaapiDecoder * base_decoder)
{
  GstVaapiDecoderVp8 *const decoder = GST_VAAPI_DECODER_VP8_CAST (base_decoder);

  return decode_current_picture (decoder);
}

static GstVaapiDecoderStatus
gst_vaapi_decoder_vp8_flush (GstVaapiDecoder * base_decoder)
{
  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static void
gst_vaapi_decoder_vp8_finalize (GObject * object)
{
  GstVaapiDecoder *const base_decoder = GST_VAAPI_DECODER (object);

  gst_vaapi_decoder_vp8_destroy (base_decoder);
  G_OBJECT_CLASS (gst_vaapi_decoder_vp8_parent_class)->finalize (object);
}

static void
gst_vaapi_decoder_vp8_class_init (GstVaapiDecoderVp8Class * klass)
{
  GObjectClass *const object_class = G_OBJECT_CLASS (klass);
  GstVaapiDecoderClass *const decoder_class = GST_VAAPI_DECODER_CLASS (klass);

  object_class->finalize = gst_vaapi_decoder_vp8_finalize;

  decoder_class->reset = gst_vaapi_decoder_vp8_reset;
  decoder_class->parse = gst_vaapi_decoder_vp8_parse;
  decoder_class->decode = gst_vaapi_decoder_vp8_decode;
  decoder_class->start_frame = gst_vaapi_decoder_vp8_start_frame;
  decoder_class->end_frame = gst_vaapi_decoder_vp8_end_frame;
  decoder_class->flush = gst_vaapi_decoder_vp8_flush;
}

static void
gst_vaapi_decoder_vp8_init (GstVaapiDecoderVp8 * decoder)
{
  GstVaapiDecoder *const base_decoder = GST_VAAPI_DECODER (decoder);

  gst_vaapi_decoder_vp8_create (base_decoder);
}

/**
 * gst_vaapi_decoder_vp8_new:
 * @display: a #GstVaapiDisplay
 * @caps: a #GstCaps holding codec information
 *
 * Creates a new #GstVaapiDecoder for VP8 decoding.  The @caps can
 * hold extra information like codec-data and pictured coded size.
 *
 * Return value: the newly allocated #GstVaapiDecoder object
 */
GstVaapiDecoder *
gst_vaapi_decoder_vp8_new (GstVaapiDisplay * display, GstCaps * caps)
{
  return g_object_new (GST_TYPE_VAAPI_DECODER_VP8, "display", display,
      "caps", caps, NULL);
}