--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gstvp8decoder
+ * @title: GstVP8Decoder
+ * @short_description: Base class to implement stateless VP8 decoders
+ * @sources:
+ * - gstvp8picture.h
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstvp8decoder.h"
+
+GST_DEBUG_CATEGORY (gst_vp8_decoder_debug);
+#define GST_CAT_DEFAULT gst_vp8_decoder_debug
+
+struct _GstVp8DecoderPrivate
+{
+ gint width;
+ gint height;
+
+ gboolean had_sequence;
+ GstVp8Parser parser;
+ gboolean wait_keyframe;
+};
+
+#define parent_class gst_vp8_decoder_parent_class
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstVp8Decoder, gst_vp8_decoder,
+ GST_TYPE_VIDEO_DECODER,
+ G_ADD_PRIVATE (GstVp8Decoder);
+ GST_DEBUG_CATEGORY_INIT (gst_vp8_decoder_debug, "vp8decoder", 0,
+ "VP8 Video Decoder"));
+
+static gboolean gst_vp8_decoder_start (GstVideoDecoder * decoder);
+static gboolean gst_vp8_decoder_stop (GstVideoDecoder * decoder);
+static gboolean gst_vp8_decoder_set_format (GstVideoDecoder * decoder,
+ GstVideoCodecState * state);
+static GstFlowReturn gst_vp8_decoder_finish (GstVideoDecoder * decoder);
+static gboolean gst_vp8_decoder_flush (GstVideoDecoder * decoder);
+static GstFlowReturn gst_vp8_decoder_drain (GstVideoDecoder * decoder);
+static GstFlowReturn gst_vp8_decoder_handle_frame (GstVideoDecoder * decoder,
+ GstVideoCodecFrame * frame);
+
+static void
+gst_vp8_decoder_class_init (GstVp8DecoderClass * klass)
+{
+ GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
+
+ decoder_class->start = GST_DEBUG_FUNCPTR (gst_vp8_decoder_start);
+ decoder_class->stop = GST_DEBUG_FUNCPTR (gst_vp8_decoder_stop);
+ decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_vp8_decoder_set_format);
+ decoder_class->finish = GST_DEBUG_FUNCPTR (gst_vp8_decoder_finish);
+ decoder_class->flush = GST_DEBUG_FUNCPTR (gst_vp8_decoder_flush);
+ decoder_class->drain = GST_DEBUG_FUNCPTR (gst_vp8_decoder_drain);
+ decoder_class->handle_frame =
+ GST_DEBUG_FUNCPTR (gst_vp8_decoder_handle_frame);
+}
+
+static void
+gst_vp8_decoder_init (GstVp8Decoder * self)
+{
+ gst_video_decoder_set_packetized (GST_VIDEO_DECODER (self), TRUE);
+
+ self->priv = gst_vp8_decoder_get_instance_private (self);
+}
+
+static gboolean
+gst_vp8_decoder_start (GstVideoDecoder * decoder)
+{
+ GstVp8Decoder *self = GST_VP8_DECODER (decoder);
+ GstVp8DecoderPrivate *priv = self->priv;
+
+ gst_vp8_parser_init (&priv->parser);
+ priv->wait_keyframe = TRUE;
+
+ return TRUE;
+}
+
+static void
+gst_vp8_decoder_reset (GstVp8Decoder * self)
+{
+ GstVp8DecoderPrivate *priv = self->priv;
+
+ gst_vp8_picture_clear (&self->last_picture);
+ gst_vp8_picture_clear (&self->golden_ref_picture);
+ gst_vp8_picture_clear (&self->alt_ref_picture);
+
+ priv->wait_keyframe = TRUE;
+}
+
+static gboolean
+gst_vp8_decoder_stop (GstVideoDecoder * decoder)
+{
+ GstVp8Decoder *self = GST_VP8_DECODER (decoder);
+
+ if (self->input_state) {
+ gst_video_codec_state_unref (self->input_state);
+ self->input_state = NULL;
+ }
+
+ gst_vp8_decoder_reset (self);
+
+ return TRUE;
+}
+
+static gboolean
+gst_vp8_decoder_check_codec_change (GstVp8Decoder * self,
+ const GstVp8FrameHdr * frame_hdr)
+{
+ GstVp8DecoderPrivate *priv = self->priv;
+ gboolean ret = TRUE;
+ gboolean changed = FALSE;
+
+ if (priv->width != frame_hdr->width || priv->height != frame_hdr->height) {
+ GST_INFO_OBJECT (self, "resolution changed %dx%d", frame_hdr->width,
+ frame_hdr->height);
+ priv->width = frame_hdr->width;
+ priv->height = frame_hdr->height;
+ changed = TRUE;
+ }
+
+ if (changed || !priv->had_sequence) {
+ GstVp8DecoderClass *klass = GST_VP8_DECODER_GET_CLASS (self);
+
+ priv->had_sequence = TRUE;
+
+ if (klass->new_sequence)
+ ret = klass->new_sequence (self, frame_hdr);
+ }
+
+ return ret;
+}
+
+static gboolean
+gst_vp8_decoder_set_format (GstVideoDecoder * decoder,
+ GstVideoCodecState * state)
+{
+ GstVp8Decoder *self = GST_VP8_DECODER (decoder);
+ GstVp8DecoderPrivate *priv = self->priv;
+
+ GST_DEBUG_OBJECT (decoder, "Set format");
+
+ if (self->input_state)
+ gst_video_codec_state_unref (self->input_state);
+
+ self->input_state = gst_video_codec_state_ref (state);
+
+ priv->width = GST_VIDEO_INFO_WIDTH (&state->info);
+ priv->height = GST_VIDEO_INFO_HEIGHT (&state->info);
+
+ return TRUE;
+}
+
+static gboolean
+gst_vp8_decoder_update_reference (GstVp8Decoder * self, GstVp8Picture * picture)
+{
+ GstVp8FrameHdr *frame_hdr = &picture->frame_hdr;
+
+ if (frame_hdr->key_frame) {
+ gst_vp8_picture_replace (&self->last_picture, picture);
+ gst_vp8_picture_replace (&self->golden_ref_picture, picture);
+ gst_vp8_picture_replace (&self->alt_ref_picture, picture);
+
+ goto done;
+ }
+
+ if (frame_hdr->refresh_alternate_frame) {
+ gst_vp8_picture_replace (&self->alt_ref_picture, picture);
+ } else {
+ switch (frame_hdr->copy_buffer_to_alternate) {
+ case 0:
+ /* do nothing */
+ break;
+ case 1:
+ gst_vp8_picture_replace (&self->alt_ref_picture, self->last_picture);
+ break;
+ case 2:
+ gst_vp8_picture_replace (&self->alt_ref_picture,
+ self->golden_ref_picture);
+ break;
+ default:
+ GST_WARNING_OBJECT (self, "unrecognized copy_buffer_to_alternate %d",
+ frame_hdr->copy_buffer_to_alternate);
+ break;
+ }
+ }
+
+ if (frame_hdr->refresh_golden_frame) {
+ gst_vp8_picture_replace (&self->golden_ref_picture, picture);
+ } else {
+ switch (frame_hdr->copy_buffer_to_golden) {
+ case 0:
+ /* do nothing */
+ break;
+ case 1:
+ gst_vp8_picture_replace (&self->golden_ref_picture, self->last_picture);
+ break;
+ case 2:
+ gst_vp8_picture_replace (&self->golden_ref_picture,
+ self->alt_ref_picture);
+ break;
+ default:
+ GST_WARNING_OBJECT (self, "unrecognized copy_buffer_to_golden %d",
+ frame_hdr->copy_buffer_to_alternate);
+ break;
+ }
+ }
+
+ if (frame_hdr->refresh_last)
+ gst_vp8_picture_replace (&self->last_picture, picture);
+
+done:
+ gst_vp8_picture_unref (picture);
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_vp8_decoder_finish (GstVideoDecoder * decoder)
+{
+ GstVp8Decoder *self = GST_VP8_DECODER (decoder);
+
+ GST_DEBUG_OBJECT (self, "finish");
+
+ gst_vp8_decoder_reset (self);
+
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_vp8_decoder_flush (GstVideoDecoder * decoder)
+{
+ GstVp8Decoder *self = GST_VP8_DECODER (decoder);
+
+ GST_DEBUG_OBJECT (self, "flush");
+
+ gst_vp8_decoder_reset (self);
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_vp8_decoder_drain (GstVideoDecoder * decoder)
+{
+ GstVp8Decoder *self = GST_VP8_DECODER (decoder);
+
+ GST_DEBUG_OBJECT (self, "drain");
+
+ gst_vp8_decoder_reset (self);
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_vp8_decoder_handle_frame (GstVideoDecoder * decoder,
+ GstVideoCodecFrame * frame)
+{
+ GstVp8Decoder *self = GST_VP8_DECODER (decoder);
+ GstVp8DecoderClass *klass = GST_VP8_DECODER_GET_CLASS (self);
+ GstVp8DecoderPrivate *priv = self->priv;
+ GstBuffer *in_buf = frame->input_buffer;
+ GstMapInfo map;
+ GstVp8FrameHdr frame_hdr;
+ GstVp8ParserResult pres;
+ GstVp8Picture *picture = NULL;
+
+ GST_LOG_OBJECT (self,
+ "handle frame, PTS: %" GST_TIME_FORMAT ", DTS: %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (in_buf)),
+ GST_TIME_ARGS (GST_BUFFER_DTS (in_buf)));
+
+ if (!gst_buffer_map (in_buf, &map, GST_MAP_READ)) {
+ GST_ERROR_OBJECT (self, "Cannot map buffer");
+
+ goto error;
+ }
+
+ pres = gst_vp8_parser_parse_frame_header (&priv->parser,
+ &frame_hdr, map.data, map.size);
+
+ if (pres != GST_VP8_PARSER_OK) {
+ GST_ERROR_OBJECT (self, "Cannot parser frame header");
+
+ goto unmap_and_error;
+ }
+
+ if (priv->wait_keyframe) {
+ if (!frame_hdr.key_frame) {
+ GST_DEBUG_OBJECT (self, "Waiting initial keyframe, drop buffer %"
+ GST_PTR_FORMAT, in_buf);
+
+ gst_buffer_unmap (in_buf, &map);
+ gst_video_decoder_drop_frame (decoder, frame);
+
+ return GST_FLOW_OK;
+ }
+ }
+
+ priv->wait_keyframe = FALSE;
+
+ if (frame_hdr.key_frame &&
+ !gst_vp8_decoder_check_codec_change (self, &frame_hdr)) {
+ GST_ERROR_OBJECT (self, "Subclass cannot handle codec change");
+ goto unmap_and_error;
+ }
+
+ picture = gst_vp8_picture_new ();
+ picture->frame_hdr = frame_hdr;
+ picture->pts = GST_BUFFER_PTS (in_buf);
+ picture->data = map.data;
+ picture->size = map.size;
+ picture->system_frame_number = frame->system_frame_number;
+
+ if (klass->new_picture) {
+ if (!klass->new_picture (self, picture)) {
+ GST_ERROR_OBJECT (self, "subclass cannot handle new picture");
+ goto unmap_and_error;
+ }
+ }
+
+ if (klass->start_picture) {
+ if (!klass->start_picture (self, picture)) {
+ GST_ERROR_OBJECT (self, "subclass cannot handle start picture");
+ goto unmap_and_error;
+ }
+ }
+
+ if (klass->decode_picture) {
+ if (!klass->decode_picture (self, picture, &priv->parser)) {
+ GST_ERROR_OBJECT (self, "subclass cannot decode current picture");
+ goto unmap_and_error;
+ }
+ }
+
+ if (klass->end_picture) {
+ if (!klass->end_picture (self, picture)) {
+ GST_ERROR_OBJECT (self, "subclass cannot handle end picture");
+ goto unmap_and_error;
+ }
+ }
+
+ gst_buffer_unmap (in_buf, &map);
+
+ gst_video_codec_frame_set_user_data (frame, gst_vp8_picture_ref (picture),
+ (GDestroyNotify) gst_vp8_picture_unref);
+ gst_video_codec_frame_unref (frame);
+
+ /* transfer ownership of picture */
+ gst_vp8_decoder_update_reference (self, picture);
+
+ g_assert (klass->output_picture);
+ return klass->output_picture (self, picture);
+
+unmap_and_error:
+ {
+ gst_buffer_unmap (in_buf, &map);
+ goto error;
+ }
+
+error:
+ {
+ GstFlowReturn ret;
+
+ if (picture)
+ gst_vp8_picture_unref (picture);
+
+ gst_video_decoder_drop_frame (decoder, frame);
+ GST_VIDEO_DECODER_ERROR (self, 1, STREAM, DECODE,
+ ("Failed to decode data"), (NULL), ret);
+
+ return ret;
+ }
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_VP8_DECODER_H__
+#define __GST_VP8_DECODER_H__
+
+#include <gst/codecs/codecs-prelude.h>
+
+#include <gst/video/video.h>
+#include <gst/codecparsers/gstvp8parser.h>
+#include <gst/codecs/gstvp8picture.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VP8_DECODER (gst_vp8_decoder_get_type())
+#define GST_VP8_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VP8_DECODER,GstVp8Decoder))
+#define GST_VP8_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VP8_DECODER,GstVp8DecoderClass))
+#define GST_VP8_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_VP8_DECODER,GstVp8DecoderClass))
+#define GST_IS_VP8_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VP8_DECODER))
+#define GST_IS_VP8_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VP8_DECODER))
+#define GST_VP8_DECODER_CAST(obj) ((GstVP8Decoder*)obj)
+
+typedef struct _GstVp8Decoder GstVp8Decoder;
+typedef struct _GstVp8DecoderClass GstVp8DecoderClass;
+typedef struct _GstVp8DecoderPrivate GstVp8DecoderPrivate;
+
+/**
+ * GstVp8Decoder:
+ *
+ * The opaque #GstVp8Decoder data structure.
+ */
+struct _GstVp8Decoder
+{
+ /*< private >*/
+ GstVideoDecoder parent;
+
+ /*< protected >*/
+ GstVideoCodecState * input_state;
+
+ /* reference frames */
+ GstVp8Picture *last_picture;
+ GstVp8Picture *golden_ref_picture;
+ GstVp8Picture *alt_ref_picture;
+
+ /*< private >*/
+ GstVp8DecoderPrivate *priv;
+ gpointer padding[GST_PADDING_LARGE];
+};
+
+/**
+ * GstVp8DecoderClass:
+ * @new_sequence: Notifies subclass of SPS update
+ * @new_picture: Optional.
+ * Called whenever new #GstVp8Picture is created.
+ * Subclass can set implementation specific user data
+ * on the #GstVp8Picture via gst_vp8_picture_set_user_data()
+ * @start_picture: Optional.
+ * Called per one #GstVp8Picture to notify subclass to prepare
+ * decoding process for the #GstVp8Picture
+ * @decode_slice: Provides per slice data with parsed slice header and
+ * required raw bitstream for subclass to decode it
+ * @end_picture: Optional.
+ * Called per one #GstVp8Picture to notify subclass to finish
+ * decoding process for the #GstVp8Picture
+ * @output_picture: Called with a #GstVp8Picture which is required to be outputted.
+ * Subclass can retrieve parent #GstVideoCodecFrame by using
+ * gst_video_decoder_get_frame() with system_frame_number
+ * and the #GstVideoCodecFrame must be consumed by subclass via
+ * gst_video_decoder_{finish,drop,release}_frame().
+ */
+struct _GstVp8DecoderClass
+{
+ GstVideoDecoderClass parent_class;
+
+ gboolean (*new_sequence) (GstVp8Decoder * decoder,
+ const GstVp8FrameHdr * frame_hdr);
+
+ gboolean (*new_picture) (GstVp8Decoder * decoder,
+ GstVp8Picture * picture);
+
+ gboolean (*start_picture) (GstVp8Decoder * decoder,
+ GstVp8Picture * picture);
+
+ gboolean (*decode_picture) (GstVp8Decoder * decoder,
+ GstVp8Picture * picture,
+ GstVp8Parser * parser);
+
+ gboolean (*end_picture) (GstVp8Decoder * decoder,
+ GstVp8Picture * picture);
+
+ GstFlowReturn (*output_picture) (GstVp8Decoder * decoder,
+ GstVp8Picture * picture);
+
+ /*< private >*/
+ gpointer padding[GST_PADDING_LARGE];
+};
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstVp8Decoder, gst_object_unref)
+
+GST_CODECS_API
+GType gst_vp8_decoder_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_VP8_DECODER_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstvp8picture.h"
+
+GST_DEBUG_CATEGORY_EXTERN (gst_vp8_decoder_debug);
+#define GST_CAT_DEFAULT gst_vp8_decoder_debug
+
+GST_DEFINE_MINI_OBJECT_TYPE (GstVp8Picture, gst_vp8_picture);
+
+static void
+_gst_vp8_picture_free (GstVp8Picture * picture)
+{
+ GST_TRACE ("Free picture %p", picture);
+
+ if (picture->notify)
+ picture->notify (picture->user_data);
+
+ g_free (picture);
+}
+
+/**
+ * gst_vp8_picture_new:
+ *
+ * Create new #GstVp8Picture
+ *
+ * Returns: a new #GstVp8Picture
+ */
+GstVp8Picture *
+gst_vp8_picture_new (void)
+{
+ GstVp8Picture *pic;
+
+ pic = g_new0 (GstVp8Picture, 1);
+ pic->pts = GST_CLOCK_TIME_NONE;
+
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (pic), 0,
+ GST_TYPE_VP8_PICTURE, NULL, NULL,
+ (GstMiniObjectFreeFunction) _gst_vp8_picture_free);
+
+ GST_TRACE ("New picture %p", pic);
+
+ return pic;
+}
+
+/**
+ * gst_vp8_picture_set_user_data:
+ * @picture: a #GstVp8Picture
+ * @user_data: private data
+ * @notify: (closure user_data): a #GDestroyNotify
+ *
+ * Sets @user_data on the picture and the #GDestroyNotify that will be called when
+ * the picture is freed.
+ *
+ * If a @user_data was previously set, then the previous set @notify will be called
+ * before the @user_data is replaced.
+ */
+void
+gst_vp8_picture_set_user_data (GstVp8Picture * picture, gpointer user_data,
+ GDestroyNotify notify)
+{
+ g_return_if_fail (GST_IS_VP8_PICTURE (picture));
+
+ if (picture->notify)
+ picture->notify (picture->user_data);
+
+ picture->user_data = user_data;
+ picture->notify = notify;
+}
+
+/**
+ * gst_vp8_picture_get_user_data:
+ * @picture: a #GstVp8Picture
+ *
+ * Gets private data set on the picture via
+ * gst_vp8_picture_set_user_data() previously.
+ *
+ * Returns: (transfer none): The previously set user_data
+ */
+gpointer
+gst_vp8_picture_get_user_data (GstVp8Picture * picture)
+{
+ return picture->user_data;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_VP8_PICTURE_H__
+#define __GST_VP8_PICTURE_H__
+
+#include <gst/codecs/codecs-prelude.h>
+#include <gst/codecparsers/gstvp8parser.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VP8_PICTURE (gst_vp8_picture_get_type())
+#define GST_IS_VP8_PICTURE(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_VP8_PICTURE))
+#define GST_VP8_PICTURE(obj) ((GstVp8Picture *)obj)
+#define GST_VP8_PICTURE_CAST(obj) (GST_VP8_PICTURE(obj))
+
+typedef struct _GstVp8Picture GstVp8Picture;
+
+struct _GstVp8Picture
+{
+ GstMiniObject parent;
+
+ GstClockTime pts;
+ /* From GstVideoCodecFrame */
+ guint32 system_frame_number;
+
+ GstVp8FrameHdr frame_hdr;
+
+ /* raw data and size (does not have ownership) */
+ const guint8 * data;
+ gsize size;
+
+ gpointer user_data;
+ GDestroyNotify notify;
+};
+
+GST_CODECS_API
+GType gst_vp8_picture_get_type (void);
+
+GST_CODECS_API
+GstVp8Picture * gst_vp8_picture_new (void);
+
+static inline GstVp8Picture *
+gst_vp8_picture_ref (GstVp8Picture * picture)
+{
+ return (GstVp8Picture *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (picture));
+}
+
+static inline void
+gst_vp8_picture_unref (GstVp8Picture * picture)
+{
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (picture));
+}
+
+static inline gboolean
+gst_vp8_picture_replace (GstVp8Picture ** old_picture,
+ GstVp8Picture * new_picture)
+{
+ return gst_mini_object_replace ((GstMiniObject **) old_picture,
+ (GstMiniObject *) new_picture);
+}
+
+static inline void
+gst_vp8_picture_clear (GstVp8Picture ** picture)
+{
+ if (picture && *picture) {
+ gst_vp8_picture_unref (*picture);
+ *picture = NULL;
+ }
+}
+
+GST_CODECS_API
+void gst_vp8_picture_set_user_data (GstVp8Picture * picture,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+GST_CODECS_API
+gpointer gst_vp8_picture_get_user_data (GstVp8Picture * picture);
+
+G_END_DECLS
+
+#endif /* __GST_VP8_PICTURE_H__ */