Add initial (multithreaded) decoder based on FFmpeg.

There are tons of bugs left:
- Decoder API not nice enough with error conditions
- FFmpeg parser is sometimes broken
- Packets queue can be lost
This commit is contained in:
gb 2010-04-23 16:05:58 +00:00
parent 5fb146c298
commit a203d19a35
7 changed files with 1606 additions and 0 deletions

View file

@ -252,6 +252,14 @@ else
USE_VAAPISINK_GLX=0
fi
dnl Check for FFmpeg
PKG_CHECK_MODULES(LIBAVFORMAT, [libavformat])
AC_CHECK_HEADERS([libavformat/avformat.h])
PKG_CHECK_MODULES(LIBAVCODEC, [libavcodec])
AC_CHECK_HEADERS([libavcodec/avcodec.h])
AC_CHECK_HEADERS([libavcodec/vaapi.h])
AC_DEFINE_UNQUOTED(USE_GLX, $USE_GLX,
[Defined to 1 if GLX is enabled])
AM_CONDITIONAL(USE_GLX, test $USE_GLX -eq 1)

View file

@ -10,7 +10,20 @@ endif
libgstvaapi_includedir = \
$(includedir)/gstreamer-@GST_MAJORMINOR@/gst/vaapi
libgstvaapi_ffmpeg_source_c = \
gstvaapidecoder_ffmpeg.c \
$(NULL)
libgstvaapi_ffmpeg_source_h = \
gstvaapidecoder_ffmpeg.h \
$(NULL)
libgstvaapi_ffmpeg_source_priv_h = \
$(NULL)
libgstvaapi_source_c = \
gstvaapicontext.c \
gstvaapidecoder.c \
gstvaapidisplay.c \
gstvaapiimage.c \
gstvaapiimageformat.c \
@ -22,15 +35,19 @@ libgstvaapi_source_c = \
gstvaapisubpicture.c \
gstvaapisurface.c \
gstvaapisurfacepool.c \
gstvaapisurfaceproxy.c \
gstvaapiutils.c \
gstvaapivalue.c \
gstvaapivideobuffer.c \
gstvaapivideopool.c \
gstvaapivideosink.c \
gstvaapiwindow.c \
$(libgstvaapi_ffmpeg_source_c) \
$(NULL)
libgstvaapi_source_h = \
gstvaapicontext.h \
gstvaapidecoder.h \
gstvaapidisplay.h \
gstvaapiimage.h \
gstvaapiimageformat.h \
@ -41,21 +58,25 @@ libgstvaapi_source_h = \
gstvaapisubpicture.h \
gstvaapisurface.h \
gstvaapisurfacepool.h \
gstvaapisurfaceproxy.h \
gstvaapitypes.h \
gstvaapivalue.h \
gstvaapivideobuffer.h \
gstvaapivideopool.h \
gstvaapivideosink.h \
gstvaapiwindow.h \
$(libgstvaapi_ffmpeg_source_h) \
$(NULL)
libgstvaapi_source_priv_h = \
gstvaapicompat.h \
gstvaapidebug.h \
gstvaapidecoder_priv.h \
gstvaapidisplay_priv.h \
gstvaapiobject_priv.h \
gstvaapiutils.h \
gstvaapi_priv.h \
$(libgst_vaapi_ffmpeg_source_priv_h) \
$(NULL)
libgstvaapi_x11_source_c = \
@ -116,12 +137,16 @@ libgstvaapi_@GST_MAJORMINOR@_la_CFLAGS = \
-I$(top_srcdir)/gst-libs \
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(LIBAVCODEC_CFLAGS) \
$(LIBAVFORMAT_CFLAGS) \
$(LIBVA_CFLAGS) \
$(NULL)
libgstvaapi_@GST_MAJORMINOR@_la_LIBADD = \
$(GST_BASE_LIBS) \
$(GST_LIBS) \
$(LIBAVCODEC_LIBS) \
$(LIBAVFORMAT_LIBS) \
$(LIBVA_LIBS) \
$(NULL)

View file

@ -0,0 +1,617 @@
/*
* gstvaapidecoder.c - VA decoder abstraction
*
* gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* SECTION:gstvaapidecoder
* @short_description: VA decoder abstraction
*/
#include "config.h"
#include <assert.h>
#include <string.h>
#include "gstvaapicompat.h"
#include "gstvaapidecoder.h"
#include "gstvaapidecoder_priv.h"
#include "gstvaapiutils.h"
#include "gstvaapi_priv.h"
#define DEBUG 1
#include "gstvaapidebug.h"
G_DEFINE_TYPE(GstVaapiDecoder, gst_vaapi_decoder, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_DISPLAY,
PROP_CODEC,
};
static gpointer
decoder_thread_cb(gpointer data)
{
GstVaapiDecoder * const decoder = data;
GstVaapiDecoderPrivate * const priv = decoder->priv;
GstVaapiDecoderClass * const klass = GST_VAAPI_DECODER_GET_CLASS(decoder);
GstVaapiDecoderStatus status = GST_VAAPI_DECODER_STATUS_SUCCESS;
if (!klass->decode) {
g_error("unimplemented GstVaapiDecoder::decode() function");
return NULL;
}
while (!priv->decoder_thread_cancel) {
g_mutex_lock(priv->adapter_mutex);
while (!gst_adapter_available(priv->adapter)) {
g_cond_wait(priv->adapter_cond, priv->adapter_mutex);
if (priv->decoder_thread_cancel)
break;
}
g_mutex_unlock(priv->adapter_mutex);
if (!priv->decoder_thread_cancel) {
if (status == GST_VAAPI_DECODER_STATUS_SUCCESS) {
g_object_ref(decoder);
status = klass->decode(decoder);
g_object_unref(decoder);
GST_DEBUG("decode frame (status = %d)", status);
}
else {
/* XXX: something went wrong, simply destroy any
buffer until this decoder is destroyed */
g_mutex_lock(priv->adapter_mutex);
gst_adapter_clear(priv->adapter);
g_mutex_unlock(priv->adapter_mutex);
/* Signal the main thread we got an error */
gst_vaapi_decoder_push_surface(decoder, NULL);
}
}
}
return NULL;
}
static GstBuffer *
create_buffer(const guchar *buf, guint buf_size, gboolean copy)
{
GstBuffer *buffer;
buffer = gst_buffer_new();
if (!buffer)
return NULL;
if (copy) {
buffer->malloc_data = g_malloc(buf_size);
if (!buffer->malloc_data) {
gst_buffer_unref(buffer);
return NULL;
}
memcpy(buffer->malloc_data, buf, buf_size);
GST_BUFFER_DATA(buffer) = buffer->malloc_data;
GST_BUFFER_SIZE(buffer) = buf_size;
}
else {
GST_BUFFER_DATA(buffer) = (guint8 *)buf;
GST_BUFFER_SIZE(buffer) = buf_size;
}
return buffer;
}
static gboolean
push_buffer(GstVaapiDecoder *decoder, GstBuffer *buffer)
{
GstVaapiDecoderPrivate * const priv = decoder->priv;
if (!buffer)
return FALSE;
g_return_val_if_fail(priv->adapter_mutex && priv->adapter_cond, FALSE);
GST_DEBUG("queue encoded data buffer %p (%d bytes)",
buffer, GST_BUFFER_SIZE(buffer));
/* XXX: add a mechanism to wait for enough buffer bytes to be consumed */
g_mutex_lock(priv->adapter_mutex);
gst_adapter_push(priv->adapter, buffer);
g_cond_signal(priv->adapter_cond);
g_mutex_unlock(priv->adapter_mutex);
if (!priv->decoder_thread) {
priv->decoder_thread = g_thread_create(
decoder_thread_cb, decoder,
TRUE,
NULL
);
if (!priv->decoder_thread)
return FALSE;
}
return TRUE;
}
static void
unref_surface_cb(gpointer surface, gpointer user_data)
{
if (surface)
g_object_unref(GST_VAAPI_SURFACE(surface));
}
static void
gst_vaapi_decoder_finalize(GObject *object)
{
GstVaapiDecoderPrivate * const priv = GST_VAAPI_DECODER(object)->priv;
if (priv->decoder_thread) {
priv->decoder_thread_cancel = TRUE;
if (priv->adapter_mutex && priv->adapter_cond) {
g_mutex_lock(priv->adapter_mutex);
g_cond_signal(priv->adapter_cond);
g_mutex_unlock(priv->adapter_mutex);
}
g_thread_join(priv->decoder_thread);
priv->decoder_thread = NULL;
}
if (priv->adapter) {
gst_adapter_clear(priv->adapter);
g_object_unref(priv->adapter);
priv->adapter = NULL;
}
if (priv->adapter_cond) {
g_cond_free(priv->adapter_cond);
priv->adapter_cond = NULL;
}
if (priv->adapter_mutex) {
g_mutex_free(priv->adapter_mutex);
priv->adapter_mutex = NULL;
}
if (priv->context) {
g_object_unref(priv->context);
priv->context = NULL;
}
g_queue_foreach(&priv->surfaces, unref_surface_cb, NULL);
if (priv->surfaces_cond) {
g_cond_free(priv->surfaces_cond);
priv->surfaces_cond = NULL;
}
if (priv->surfaces_mutex) {
g_mutex_free(priv->surfaces_mutex);
priv->surfaces_mutex = NULL;
}
if (priv->display) {
g_object_unref(priv->display);
priv->display = NULL;
}
G_OBJECT_CLASS(gst_vaapi_decoder_parent_class)->finalize(object);
}
static void
gst_vaapi_decoder_set_property(
GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec
)
{
GstVaapiDecoderPrivate * const priv = GST_VAAPI_DECODER(object)->priv;
switch (prop_id) {
case PROP_DISPLAY:
priv->display = g_object_ref(g_value_get_object(value));
break;
case PROP_CODEC:
priv->codec = g_value_get_uint(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
gst_vaapi_decoder_get_property(
GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec
)
{
GstVaapiDecoderPrivate * const priv = GST_VAAPI_DECODER(object)->priv;
switch (prop_id) {
case PROP_DISPLAY:
g_value_set_object(value, priv->display);
break;
case PROP_CODEC:
g_value_set_uint(value, priv->codec);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
gst_vaapi_decoder_class_init(GstVaapiDecoderClass *klass)
{
GObjectClass * const object_class = G_OBJECT_CLASS(klass);
g_type_class_add_private(klass, sizeof(GstVaapiDecoderPrivate));
object_class->finalize = gst_vaapi_decoder_finalize;
object_class->set_property = gst_vaapi_decoder_set_property;
object_class->get_property = gst_vaapi_decoder_get_property;
/**
* GstVaapiDecoder:display:
*
* The #GstVaapiDisplay this decoder is bound to.
*/
g_object_class_install_property
(object_class,
PROP_DISPLAY,
g_param_spec_object("display",
"Display",
"The GstVaapiDisplay this decoder is bound to",
GST_VAAPI_TYPE_DISPLAY,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class,
PROP_CODEC,
g_param_spec_uint("codec",
"Codec",
"The codec handled by the decoder",
0, G_MAXINT32, 0,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
}
static void
gst_vaapi_decoder_init(GstVaapiDecoder *decoder)
{
GstVaapiDecoderPrivate *priv = GST_VAAPI_DECODER_GET_PRIVATE(decoder);
decoder->priv = priv;
priv->context = NULL;
priv->codec = 0;
priv->adapter = gst_adapter_new();
priv->adapter_mutex = g_mutex_new();
priv->adapter_cond = g_cond_new();
priv->surfaces_mutex = g_mutex_new();
priv->surfaces_cond = g_cond_new();
priv->decoder_thread = NULL;
priv->decoder_thread_cancel = FALSE;
g_queue_init(&priv->surfaces);
}
/**
* gst_vaapi_decoder_put_buffer_data:
* @decoder: a #GstVaapiDecoder
* @buf: pointer to buffer data
* @buf_size: size of buffer data in bytes
*
* Queues @buf_size bytes from the data @buf to the HW decoder. The
* caller is responsible for making sure @buf is live beyond this
* function. So, this function is mostly useful with static data
* buffers. gst_vaapi_decoder_put_buffer_data_copy() does the same but
* copies the data.
*
* Caller can notify an End-Of-Stream with @buf set to %NULL and
* @buf_size set to zero.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_decoder_put_buffer_data(
GstVaapiDecoder *decoder,
const guchar *buf,
guint buf_size
)
{
g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), FALSE);
g_return_val_if_fail(buf, FALSE);
g_return_val_if_fail(buf_size > 0, FALSE);
return push_buffer(decoder, create_buffer(buf, buf_size, FALSE));
}
/**
* gst_vaapi_decoder_put_buffer_data_copy:
* @decoder: a #GstVaapiDecoder
* @buf: pointer to buffer data
* @buf_size: size of buffer data in bytes
*
* Queues a copy of @buf to the HW decoder.
*
* Caller can notify an End-Of-Stream with @buf set to %NULL and
* @buf_size set to zero.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_decoder_put_buffer_data_copy(
GstVaapiDecoder *decoder,
const guchar *buf,
guint buf_size
)
{
g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), FALSE);
g_return_val_if_fail(buf, FALSE);
g_return_val_if_fail(buf_size > 0, FALSE);
return push_buffer(decoder, create_buffer(buf, buf_size, TRUE));
}
/**
* gst_vaapi_decoder_put_buffer:
* @decoder: a #GstVaapiDecoder
* @buf: a #GstBuffer
*
* Queues a #GstBuffer to the HW decoder. The decoder holds a
* reference to @buf.
*
* Caller can notify an End-Of-Stream with @buf set to %NULL.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_decoder_put_buffer(GstVaapiDecoder *decoder, GstBuffer *buf)
{
g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), FALSE);
g_return_val_if_fail(GST_IS_BUFFER(buf), FALSE);
return push_buffer(decoder, gst_buffer_ref(buf));
}
/**
* gst_vaapi_decoder_get_surface:
* @decoder: a #GstVaapiDecoder
* @pstatus: return location for the decoder status, or %NULL
*
* Waits for a decoded surface to arrive. This functions blocks until
* the @decoder has a surface ready for the caller. @pstatus is
* optional but it can help to know what went wrong during the
* decoding process.
*
* Return value: a #GstVaapiSurfaceProxy holding the decoded surface,
* or %NULL if none is available (e.g. an error). Caller owns the
* returned object. g_object_unref() after usage.
*/
static GstVaapiSurface *
_gst_vaapi_decoder_get_surface(
GstVaapiDecoder *decoder,
GTimeVal *timeout,
GstVaapiDecoderStatus *pstatus
)
{
GstVaapiDecoderPrivate * const priv = decoder->priv;
GstVaapiSurface *surface;
g_mutex_lock(priv->surfaces_mutex);
while (g_queue_is_empty(&priv->surfaces))
if (!g_cond_timed_wait(priv->surfaces_cond, priv->surfaces_mutex, timeout))
break;
surface = g_queue_pop_head(&priv->surfaces);
g_mutex_unlock(priv->surfaces_mutex);
if (surface)
*pstatus = GST_VAAPI_DECODER_STATUS_SUCCESS;
else {
g_mutex_lock(priv->adapter_mutex);
if (gst_adapter_available(priv->adapter))
*pstatus = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
else if (timeout)
*pstatus = GST_VAAPI_DECODER_STATUS_TIMEOUT;
else
*pstatus = GST_VAAPI_DECODER_STATUS_END_OF_STREAM;
g_mutex_unlock(priv->adapter_mutex);
}
return surface;
}
GstVaapiSurfaceProxy *
gst_vaapi_decoder_get_surface(
GstVaapiDecoder *decoder,
GstVaapiDecoderStatus *pstatus
)
{
GstVaapiSurfaceProxy *proxy = NULL;
GstVaapiSurface *surface;
GstVaapiDecoderStatus status;
g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL);
surface = _gst_vaapi_decoder_get_surface(decoder, NULL, &status);
if (surface) {
proxy = gst_vaapi_surface_proxy_new(decoder->priv->context, surface);
if (!proxy)
status = GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
g_object_unref(surface);
}
if (pstatus)
*pstatus = status;
return proxy;
}
/**
* gst_vaapi_decoder_timed_get_surface:
* @decoder: a #GstVaapiDecoder
* @timeout: the number of microseconds to wait for the decoded surface
* @pstatus: return location for the decoder status, or %NULL
*
* Waits for a decoded surface to arrive. This function blocks for at
* least @timeout microseconds. @pstatus is optional but it can help
* to know what went wrong during the decoding process.
*
* Return value: a #GstVaapiSurfaceProxy holding the decoded surface,
* or %NULL if none is available (e.g. an error). Caller owns the
* returned object. g_object_unref() after usage.
*/
GstVaapiSurfaceProxy *
gst_vaapi_decoder_timed_get_surface(
GstVaapiDecoder *decoder,
guint32 timeout,
GstVaapiDecoderStatus *pstatus
)
{
GstVaapiSurfaceProxy *proxy = NULL;
GstVaapiSurface *surface;
GstVaapiDecoderStatus status;
GTimeVal end_time;
g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL);
g_get_current_time(&end_time);
g_time_val_add(&end_time, timeout);
surface = _gst_vaapi_decoder_get_surface(decoder, &end_time, &status);
if (surface) {
proxy = gst_vaapi_surface_proxy_new(decoder->priv->context, surface);
if (!proxy)
status = GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
g_object_unref(surface);
}
if (pstatus)
*pstatus = status;
return proxy;
}
gboolean
gst_vaapi_decoder_ensure_context(
GstVaapiDecoder *decoder,
GstVaapiProfile profile,
GstVaapiEntrypoint entrypoint,
guint width,
guint height
)
{
GstVaapiDecoderPrivate * const priv = decoder->priv;
if (priv->context)
return gst_vaapi_context_reset(priv->context,
profile, entrypoint, width, height);
priv->context = gst_vaapi_context_new(
priv->display,
profile,
entrypoint,
width,
height
);
return priv->context != NULL;
}
guint
gst_vaapi_decoder_copy(
GstVaapiDecoder *decoder,
guint offset,
guchar *buf,
guint buf_size
)
{
GstVaapiDecoderPrivate * const priv = decoder->priv;
guint avail;
if (!buf || !buf_size)
return 0;
avail = gst_vaapi_decoder_read_avail(decoder);
if (offset >= avail)
return 0;
if (buf_size > avail - offset)
buf_size = avail - offset;
if (buf_size > 0) {
g_mutex_lock(priv->adapter_mutex);
gst_adapter_copy(priv->adapter, buf, offset, buf_size);
g_mutex_unlock(priv->adapter_mutex);
}
return buf_size;
}
guint
gst_vaapi_decoder_read_avail(GstVaapiDecoder *decoder)
{
GstVaapiDecoderPrivate * const priv = decoder->priv;
guint avail;
g_mutex_lock(priv->adapter_mutex);
avail = gst_adapter_available(priv->adapter);
g_mutex_unlock(priv->adapter_mutex);
return avail;
}
guint
gst_vaapi_decoder_read(GstVaapiDecoder *decoder, guchar *buf, guint buf_size)
{
buf_size = gst_vaapi_decoder_copy(decoder, 0, buf, buf_size);
if (buf_size > 0)
gst_vaapi_decoder_flush(decoder, buf_size);
return buf_size;
}
void
gst_vaapi_decoder_flush(GstVaapiDecoder *decoder, guint buf_size)
{
GstVaapiDecoderPrivate * const priv = decoder->priv;
guint avail;
if (!buf_size)
return;
avail = gst_vaapi_decoder_read_avail(decoder);
if (buf_size > avail)
buf_size = avail;
g_mutex_lock(priv->adapter_mutex);
gst_adapter_flush(priv->adapter, buf_size);
g_mutex_unlock(priv->adapter_mutex);
}
gboolean
gst_vaapi_decoder_push_surface(
GstVaapiDecoder *decoder,
GstVaapiSurface *surface
)
{
GstVaapiDecoderPrivate * const priv = decoder->priv;
if (surface)
GST_DEBUG("queue decoded surface %" GST_VAAPI_ID_FORMAT,
GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(surface)));
else
GST_DEBUG("queue null surface to signal an error");
g_mutex_lock(priv->surfaces_mutex);
g_queue_push_tail(&priv->surfaces, surface ? g_object_ref(surface) : NULL);
g_cond_signal(priv->surfaces_cond);
g_mutex_unlock(priv->surfaces_mutex);
return TRUE;
}

View file

@ -0,0 +1,137 @@
/*
* gstvaapidecoder.h - VA decoder abstraction
*
* gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GST_VAAPI_DECODER_H
#define GST_VAAPI_DECODER_H
#include <gst/gstbuffer.h>
#include <gst/vaapi/gstvaapicontext.h>
#include <gst/vaapi/gstvaapisurfaceproxy.h>
G_BEGIN_DECLS
#define GST_VAAPI_TYPE_DECODER \
(gst_vaapi_decoder_get_type())
#define GST_VAAPI_DECODER(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), \
GST_VAAPI_TYPE_DECODER, \
GstVaapiDecoder))
#define GST_VAAPI_DECODER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), \
GST_VAAPI_TYPE_DECODER, \
GstVaapiDecoderClass))
#define GST_VAAPI_IS_DECODER(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_VAAPI_TYPE_DECODER))
#define GST_VAAPI_IS_DECODER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_VAAPI_TYPE_DECODER))
#define GST_VAAPI_DECODER_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), \
GST_VAAPI_TYPE_DECODER, \
GstVaapiDecoderClass))
typedef enum _GstVaapiDecoderStatus GstVaapiDecoderStatus;
typedef struct _GstVaapiDecoder GstVaapiDecoder;
typedef struct _GstVaapiDecoderPrivate GstVaapiDecoderPrivate;
typedef struct _GstVaapiDecoderClass GstVaapiDecoderClass;
/**
* GstVaapiDecoderStatus:
* @GST_VAAPI_DECODER_STATUS_SUCCESS: Success.
* @GST_VAAPI_DECODER_STATUS_TIMEOUT: Timeout. Try again later.
* @GST_VAAPI_DECODER_STATUS_EOS: End-Of-Stream.
* @GST_VAAPI_DECODER_STATUS_ERROR: Unknown error.
*
* Decoder status for gst_vaapi_decoder_get_surface().
*/
enum _GstVaapiDecoderStatus {
GST_VAAPI_DECODER_STATUS_SUCCESS = 0,
GST_VAAPI_DECODER_STATUS_TIMEOUT,
GST_VAAPI_DECODER_STATUS_END_OF_STREAM,
GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED,
GST_VAAPI_DECODER_STATUS_ERROR_INIT_FAILED,
GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN
};
/**
* GstVaapiDecoder:
*
* A VA decoder base instance.
*/
struct _GstVaapiDecoder {
/*< private >*/
GObject parent_instance;
GstVaapiDecoderPrivate *priv;
};
/**
* GstVaapiDecoderClass:
* @decode: decode one frame.
*
* A VA decoder base class.
*/
struct _GstVaapiDecoderClass {
/*< private >*/
GObjectClass parent_class;
GstVaapiDecoderStatus (*decode)(GstVaapiDecoder *decoder);
};
GType
gst_vaapi_decoder_get_type(void);
gboolean
gst_vaapi_decoder_put_buffer_data(
GstVaapiDecoder *decoder,
const guchar *buf,
guint buf_size
);
gboolean
gst_vaapi_decoder_put_buffer_data_copy(
GstVaapiDecoder *decoder,
const guchar *buf,
guint buf_size
);
gboolean
gst_vaapi_decoder_put_buffer(GstVaapiDecoder *decoder, GstBuffer *buf);
GstVaapiSurfaceProxy *
gst_vaapi_decoder_get_surface(
GstVaapiDecoder *decoder,
GstVaapiDecoderStatus *pstatus
);
GstVaapiSurfaceProxy *
gst_vaapi_decoder_timed_get_surface(
GstVaapiDecoder *decoder,
guint32 timeout,
GstVaapiDecoderStatus *pstatus
);
G_END_DECLS
#endif /* GST_VAAPI_DECODER_H */

View file

@ -0,0 +1,610 @@
/*
* gstvaapidecoder_ffmpeg.c - FFmpeg-based decoder
*
* gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* SECTION:gstvaapidecoder_ffmpeg
* @short_description: FFmpeg-based decoder
*/
#include "config.h"
#include <libavcodec/avcodec.h>
#include <libavcodec/vaapi.h>
#include <libavformat/avformat.h>
#include "gstvaapidecoder_ffmpeg.h"
#include "gstvaapidecoder_priv.h"
#include "gstvaapidisplay_priv.h"
#include "gstvaapiobject_priv.h"
#define DEBUG 1
#include "gstvaapidebug.h"
G_DEFINE_TYPE(GstVaapiDecoderFfmpeg,
gst_vaapi_decoder_ffmpeg,
GST_VAAPI_TYPE_DECODER);
#define GST_VAAPI_DECODER_FFMPEG_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), \
GST_VAAPI_TYPE_DECODER_FFMPEG, \
GstVaapiDecoderFfmpegPrivate))
/** Default I/O buffer size (32 KB) */
#define DEFAULT_IOBUF_SIZE (32 * 1024)
typedef struct _GstVaapiContextFfmpeg GstVaapiContextFfmpeg;
struct _GstVaapiContextFfmpeg {
struct vaapi_context base;
GstVaapiDecoderFfmpeg *decoder;
};
struct _GstVaapiDecoderFfmpegPrivate {
AVPacket packet;
AVFrame *frame;
guchar *iobuf;
guint iobuf_pos;
guint iobuf_size;
ByteIOContext ioctx;
AVFormatContext *fmtctx;
AVCodecContext *avctx;
GstVaapiContextFfmpeg *vactx;
guint video_stream_index;
guint is_constructed : 1;
};
/** Converts codec to FFmpeg codec id */
static enum CodecID
get_codec_id_from_codec(GstVaapiCodec codec)
{
switch (codec) {
case GST_VAAPI_CODEC_MPEG1: return CODEC_ID_MPEG1VIDEO;
case GST_VAAPI_CODEC_MPEG2: return CODEC_ID_MPEG2VIDEO;
case GST_VAAPI_CODEC_MPEG4: return CODEC_ID_MPEG4;
case GST_VAAPI_CODEC_H263: return CODEC_ID_H263;
case GST_VAAPI_CODEC_H264: return CODEC_ID_H264;
case GST_VAAPI_CODEC_VC1: return CODEC_ID_VC1;
}
return CODEC_ID_NONE;
}
/** Converts codec to FFmpeg raw bitstream format */
static const gchar *
get_raw_format_from_codec(GstVaapiCodec codec)
{
switch (codec) {
case GST_VAAPI_CODEC_MPEG1: return "mpegvideo";
case GST_VAAPI_CODEC_MPEG2: return "mpegvideo";
case GST_VAAPI_CODEC_MPEG4: return "m4v";
case GST_VAAPI_CODEC_H263: return "h263";
case GST_VAAPI_CODEC_H264: return "h264";
case GST_VAAPI_CODEC_VC1: return "vc1";
}
return NULL;
}
/** Finds a suitable profile from FFmpeg context */
static GstVaapiProfile
get_profile(AVCodecContext *avctx)
{
GstVaapiContextFfmpeg * const vactx = avctx->hwaccel_context;
GstVaapiDisplay *display;
GstVaapiProfile test_profiles[4];
guint i, n_profiles = 0;
#define ADD_PROFILE(profile) do { \
test_profiles[n_profiles++] = GST_VAAPI_PROFILE_##profile; \
} while (0)
switch (avctx->codec_id) {
case CODEC_ID_MPEG1VIDEO:
ADD_PROFILE(MPEG1);
break;
case CODEC_ID_MPEG2VIDEO:
ADD_PROFILE(MPEG2_MAIN);
ADD_PROFILE(MPEG2_SIMPLE);
break;
case CODEC_ID_H263:
ADD_PROFILE(H263_BASELINE);
/* fall-through */
case CODEC_ID_MPEG4:
ADD_PROFILE(MPEG4_MAIN);
ADD_PROFILE(MPEG4_ADVANCED_SIMPLE);
ADD_PROFILE(MPEG4_SIMPLE);
break;
case CODEC_ID_H264:
if (avctx->profile == 66) /* baseline */
ADD_PROFILE(H264_BASELINE);
else {
if (avctx->profile == 77) /* main */
ADD_PROFILE(H264_MAIN);
ADD_PROFILE(H264_HIGH);
}
break;
case CODEC_ID_WMV3:
if (avctx->profile == 0) /* simple */
ADD_PROFILE(VC1_SIMPLE);
ADD_PROFILE(VC1_MAIN);
break;
case CODEC_ID_VC1:
ADD_PROFILE(VC1_ADVANCED);
break;
default:
break;
}
#undef ADD_PROFILE
display = GST_VAAPI_DECODER_DISPLAY(vactx->decoder);
if (!display)
return 0;
for (i = 0; i < n_profiles; i++)
if (gst_vaapi_display_has_decoder(display, test_profiles[i]))
return test_profiles[i];
return 0;
}
/** Probes FFmpeg format from input stream */
static AVInputFormat *
get_probed_format(GstVaapiDecoder *decoder)
{
GstVaapiDecoderFfmpegPrivate * const priv =
GST_VAAPI_DECODER_FFMPEG(decoder)->priv;
AVProbeData pd;
pd.filename = "";
pd.buf = priv->iobuf;
pd.buf_size = MIN(gst_vaapi_decoder_read_avail(decoder), priv->iobuf_size);
if (!gst_vaapi_decoder_copy(decoder, 0, pd.buf, pd.buf_size))
return FALSE;
GST_DEBUG("probing format from buffer %p [%d bytes]", pd.buf, pd.buf_size);
return av_probe_input_format(&pd, 1);
}
/** Tries to get an FFmpeg format from the raw bitstream */
static AVInputFormat *
get_raw_format(GstVaapiDecoder *decoder)
{
const gchar *raw_format;
raw_format = get_raw_format_from_codec(GST_VAAPI_DECODER_CODEC(decoder));
if (!raw_format)
return NULL;
GST_DEBUG("trying raw format %s", raw_format);
return av_find_input_format(raw_format);
}
/** Reads one packet */
static int
stream_read(void *opaque, uint8_t *buf, int buf_size)
{
GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(opaque);
GstVaapiDecoderFfmpegPrivate * const priv =
GST_VAAPI_DECODER_FFMPEG(decoder)->priv;
if (buf_size > 0) {
if (priv->is_constructed) {
buf_size = gst_vaapi_decoder_read(decoder, buf, buf_size);
}
else {
buf_size = gst_vaapi_decoder_copy(
decoder,
priv->iobuf_pos,
buf, buf_size
);
priv->iobuf_pos += buf_size;
}
}
return buf_size;
}
/** Seeks into stream */
static int64_t
stream_seek(void *opaque, int64_t offset, int whence)
{
GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(opaque);
GstVaapiDecoderFfmpegPrivate * const priv =
GST_VAAPI_DECODER_FFMPEG(decoder)->priv;
/* If we parsed the headers (decoder is constructed), we can no
longer seek into the stream */
if (priv->is_constructed &&
!((whence == SEEK_SET || whence == SEEK_CUR) && offset == 0))
return -1;
switch (whence) {
case SEEK_SET:
priv->iobuf_pos = offset;
break;
case SEEK_CUR:
priv->iobuf_pos += offset;
break;
case SEEK_END:
priv->iobuf_pos = gst_vaapi_decoder_read_avail(decoder) + offset;
break;
default:
return -1;
}
return priv->iobuf_pos;
}
/** AVCodecContext.get_format() implementation */
static enum PixelFormat
gst_vaapi_decoder_ffmpeg_get_format(AVCodecContext *avctx, const enum PixelFormat *fmt)
{
GstVaapiContextFfmpeg * const vactx = avctx->hwaccel_context;
GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(vactx->decoder);
GstVaapiProfile profile;
gboolean success;
guint i;
profile = get_profile(avctx);
if (!profile)
return PIX_FMT_NONE;
/* XXX: only VLD entrypoint is supported at this time */
for (i = 0; fmt[i] != PIX_FMT_NONE; i++)
if (fmt[i] == PIX_FMT_VAAPI_VLD)
break;
success = gst_vaapi_decoder_ensure_context(
decoder,
profile,
GST_VAAPI_ENTRYPOINT_VLD,
avctx->width, avctx->height
);
if (success) {
GstVaapiDisplay * const display = GST_VAAPI_DECODER_DISPLAY(decoder);
GstVaapiContext * const context = GST_VAAPI_DECODER_CONTEXT(decoder);
vactx->base.display = GST_VAAPI_DISPLAY_VADISPLAY(display);
vactx->base.context_id = GST_VAAPI_OBJECT_ID(context);
return fmt[i];
}
return PIX_FMT_NONE;
}
/** AVCodecContext.get_buffer() implementation */
static int
gst_vaapi_decoder_ffmpeg_get_buffer(AVCodecContext *avctx, AVFrame *pic)
{
GstVaapiContextFfmpeg * const vactx = avctx->hwaccel_context;
GstVaapiContext * const context = GST_VAAPI_DECODER_CONTEXT(vactx->decoder);
GstVaapiSurface *surface;
GstVaapiID surface_id;
surface = gst_vaapi_context_get_surface(context);
if (!surface) {
GST_DEBUG("failed to get a free VA surface");
return -1;
}
surface_id = GST_VAAPI_OBJECT_ID(surface);
GST_DEBUG("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(surface_id));
pic->type = FF_BUFFER_TYPE_USER;
pic->age = 1;
pic->data[0] = (uint8_t *)surface;
pic->data[1] = NULL;
pic->data[2] = NULL;
pic->data[3] = (uint8_t *)(uintptr_t)surface_id;
pic->linesize[0] = 0;
pic->linesize[1] = 0;
pic->linesize[2] = 0;
pic->linesize[3] = 0;
return 0;
}
/** AVCodecContext.reget_buffer() implementation */
static int
gst_vaapi_decoder_ffmpeg_reget_buffer(AVCodecContext *avctx, AVFrame *pic)
{
GST_DEBUG("UNIMPLEMENTED");
return -1;
}
/** AVCodecContext.release_buffer() implementation */
static void
gst_vaapi_decoder_ffmpeg_release_buffer(AVCodecContext *avctx, AVFrame *pic)
{
GstVaapiID surface_id = GST_VAAPI_ID(GPOINTER_TO_UINT(pic->data[3]));
GST_DEBUG("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(surface_id));
pic->data[0] = NULL;
pic->data[1] = NULL;
pic->data[2] = NULL;
pic->data[3] = NULL;
}
static void
gst_vaapi_decoder_ffmpeg_destroy(GstVaapiDecoderFfmpeg *ffdecoder)
{
GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv;
if (priv->avctx) {
avcodec_close(priv->avctx);
priv->avctx = NULL;
}
if (priv->vactx) {
g_free(priv->vactx);
priv->vactx = NULL;
}
if (priv->fmtctx) {
av_close_input_stream(priv->fmtctx);
priv->fmtctx = NULL;
}
if (priv->iobuf) {
g_free(priv->iobuf);
priv->iobuf = NULL;
priv->iobuf_pos = 0;
}
av_freep(&priv->frame);
av_free_packet(&priv->packet);
}
static gboolean
gst_vaapi_decoder_ffmpeg_create(GstVaapiDecoderFfmpeg *ffdecoder)
{
GstVaapiDecoder * const decoder = GST_VAAPI_DECODER(ffdecoder);
GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv;
GstVaapiCodec codec = GST_VAAPI_DECODER_CODEC(decoder);
enum CodecID codec_id = get_codec_id_from_codec(codec);
typedef AVInputFormat *(*GetFormatFunc)(GstVaapiDecoder *);
GetFormatFunc get_format[2];
AVInputFormat *format;
AVStream *video_stream;
AVCodec *ffcodec;
guint i;
if (!priv->vactx) {
priv->vactx = g_new(GstVaapiContextFfmpeg, 1);
if (!priv->vactx)
return FALSE;
}
memset(&priv->vactx->base, 0, sizeof(priv->vactx->base));
priv->vactx->decoder = ffdecoder;
if (!priv->frame) {
priv->frame = avcodec_alloc_frame();
if (!priv->frame)
return FALSE;
}
if (!priv->iobuf) {
priv->iobuf = g_malloc0(priv->iobuf_size + FF_INPUT_BUFFER_PADDING_SIZE);
if (!priv->iobuf)
return FALSE;
}
get_format[ !codec] = get_raw_format;
get_format[!!codec] = get_probed_format;
for (i = 0; i < 2; i++) {
format = get_format[i](decoder);
if (!format)
continue;
priv->iobuf_pos = 0;
init_put_byte(
&priv->ioctx,
priv->iobuf,
priv->iobuf_size,
0, /* write flags */
ffdecoder,
stream_read,
NULL, /* no packet writer callback */
stream_seek
);
priv->ioctx.is_streamed = 1;
if (av_open_input_stream(&priv->fmtctx, &priv->ioctx, "", format, NULL) < 0)
continue;
if (av_find_stream_info(priv->fmtctx) >= 0)
break;
av_close_input_stream(priv->fmtctx);
priv->fmtctx = NULL;
}
if (!priv->fmtctx)
return FALSE;
if (av_find_stream_info(priv->fmtctx) < 0)
return FALSE;
dump_format(priv->fmtctx, 0, "", 0);
video_stream = NULL;
for (i = 0; i < priv->fmtctx->nb_streams; i++) {
AVStream * const stream = priv->fmtctx->streams[i];
if (!video_stream &&
stream->codec->codec_type == CODEC_TYPE_VIDEO &&
(codec ? (stream->codec->codec_id == codec_id) : 1)) {
video_stream = stream;
}
else
stream->discard = AVDISCARD_ALL;
}
if (!video_stream)
return FALSE;
priv->video_stream_index = video_stream->index;
priv->avctx = video_stream->codec;
priv->avctx->hwaccel_context = priv->vactx;
priv->avctx->get_format = gst_vaapi_decoder_ffmpeg_get_format;
priv->avctx->get_buffer = gst_vaapi_decoder_ffmpeg_get_buffer;
priv->avctx->reget_buffer = gst_vaapi_decoder_ffmpeg_reget_buffer;
priv->avctx->release_buffer = gst_vaapi_decoder_ffmpeg_release_buffer;
priv->avctx->thread_count = 1;
priv->avctx->draw_horiz_band = NULL;
priv->avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
ffcodec = avcodec_find_decoder(priv->avctx->codec_id);
if (!ffcodec || avcodec_open(priv->avctx, ffcodec) < 0)
return FALSE;
av_init_packet(&priv->packet);
return TRUE;
}
static GstVaapiSurface *
decode_frame(GstVaapiDecoderFfmpeg *ffdecoder, guchar *buf, guint buf_size)
{
GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv;
GstVaapiDisplay * const display = GST_VAAPI_DECODER_DISPLAY(ffdecoder);
GstVaapiSurface *surface = NULL;
int got_picture = 0;
GST_VAAPI_DISPLAY_LOCK(display);
avcodec_decode_video(
priv->avctx,
priv->frame,
&got_picture,
buf, buf_size
);
GST_VAAPI_DISPLAY_UNLOCK(display);
if (got_picture) {
surface = gst_vaapi_context_find_surface_by_id(
GST_VAAPI_DECODER_CONTEXT(ffdecoder),
GPOINTER_TO_UINT(priv->frame->data[3])
);
}
return surface;
}
GstVaapiDecoderStatus
gst_vaapi_decoder_ffmpeg_decode(GstVaapiDecoder *decoder)
{
GstVaapiDecoderFfmpeg * const ffdecoder = GST_VAAPI_DECODER_FFMPEG(decoder);
GstVaapiDecoderFfmpegPrivate * const priv = ffdecoder->priv;
GstVaapiSurface *surface = NULL;
AVPacket packet;
if (!priv->is_constructed) {
priv->is_constructed = gst_vaapi_decoder_ffmpeg_create(ffdecoder);
if (!priv->is_constructed) {
gst_vaapi_decoder_ffmpeg_destroy(ffdecoder);
return GST_VAAPI_DECODER_STATUS_ERROR_INIT_FAILED;
}
}
av_init_packet(&packet);
while (av_read_frame(priv->fmtctx, &packet) == 0) {
if (packet.stream_index != priv->video_stream_index)
continue;
surface = decode_frame(ffdecoder, packet.data, packet.size);
if (surface) /* decode a single frame only */
break;
}
if (!surface)
surface = decode_frame(ffdecoder, NULL, 0);
av_free_packet(&packet);
if (surface && gst_vaapi_decoder_push_surface(decoder, surface))
return GST_VAAPI_DECODER_STATUS_SUCCESS;
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
}
static void
gst_vaapi_decoder_ffmpeg_finalize(GObject *object)
{
GstVaapiDecoderFfmpeg * const ffdecoder = GST_VAAPI_DECODER_FFMPEG(object);
gst_vaapi_decoder_ffmpeg_destroy(ffdecoder);
G_OBJECT_CLASS(gst_vaapi_decoder_ffmpeg_parent_class)->finalize(object);
}
static void
gst_vaapi_decoder_ffmpeg_class_init(GstVaapiDecoderFfmpegClass *klass)
{
GObjectClass * const object_class = G_OBJECT_CLASS(klass);
GstVaapiDecoderClass * const decoder_class = GST_VAAPI_DECODER_CLASS(klass);
g_type_class_add_private(klass, sizeof(GstVaapiDecoderFfmpegPrivate));
object_class->finalize = gst_vaapi_decoder_ffmpeg_finalize;
decoder_class->decode = gst_vaapi_decoder_ffmpeg_decode;
}
static gpointer
gst_vaapi_decoder_ffmpeg_init_once_cb(gpointer user_data)
{
av_register_all();
return NULL;
}
static inline void
gst_vaapi_decoder_ffmpeg_init_once(void)
{
static GOnce once = G_ONCE_INIT;
g_once(&once, gst_vaapi_decoder_ffmpeg_init_once_cb, NULL);
}
static void
gst_vaapi_decoder_ffmpeg_init(GstVaapiDecoderFfmpeg *decoder)
{
GstVaapiDecoderFfmpegPrivate *priv;
gst_vaapi_decoder_ffmpeg_init_once();
priv = GST_VAAPI_DECODER_FFMPEG_GET_PRIVATE(decoder);
decoder->priv = priv;
priv->frame = NULL;
priv->iobuf = NULL;
priv->iobuf_pos = 0;
priv->iobuf_size = DEFAULT_IOBUF_SIZE;
priv->fmtctx = NULL;
priv->avctx = NULL;
priv->vactx = NULL;
priv->video_stream_index = 0;
priv->is_constructed = FALSE;
av_init_packet(&priv->packet);
}
/**
* gst_vaapi_decoder_ffmpeg_new:
* @display: a #GstVaapiDisplay
* @codec: a #GstVaapiCodec
*
* Creates a new #GstVaapiDecoder with the specified @codec bound to
* @display. If @codec is zero, the first video stream will be
* selected. Otherwise, the first video stream matching @codec is
* used, if any.
*
* Return value: the newly allocated #GstVaapiDecoder object
*/
GstVaapiDecoder *
gst_vaapi_decoder_ffmpeg_new(GstVaapiDisplay *display, GstVaapiCodec codec)
{
g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
return g_object_new(GST_VAAPI_TYPE_DECODER_FFMPEG,
"display", display,
"codec", codec,
NULL);
}

View file

@ -0,0 +1,86 @@
/*
* gstvaapidecoder_ffmpeg.h - FFmpeg-based decoder
*
* gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GST_VAAPI_DECODER_FFMPEG_H
#define GST_VAAPI_DECODER_FFMPEG_H
#include <gst/vaapi/gstvaapidecoder.h>
G_BEGIN_DECLS
#define GST_VAAPI_TYPE_DECODER_FFMPEG \
(gst_vaapi_decoder_ffmpeg_get_type())
#define GST_VAAPI_DECODER_FFMPEG(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), \
GST_VAAPI_TYPE_DECODER_FFMPEG, \
GstVaapiDecoderFfmpeg))
#define GST_VAAPI_DECODER_FFMPEG_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), \
GST_VAAPI_TYPE_DECODER_FFMPEG, \
GstVaapiDecoderFfmpegClass))
#define GST_VAAPI_IS_DECODER_FFMPEG(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_VAAPI_TYPE_DECODER_FFMPEG))
#define GST_VAAPI_IS_DECODER_FFMPEG_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_VAAPI_TYPE_DECODER_FFMPEG))
#define GST_VAAPI_DECODER_FFMPEG_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), \
GST_VAAPI_TYPE_DECODER_FFMPEG, \
GstVaapiDecoderFfmpegClass))
typedef struct _GstVaapiDecoderFfmpeg GstVaapiDecoderFfmpeg;
typedef struct _GstVaapiDecoderFfmpegPrivate GstVaapiDecoderFfmpegPrivate;
typedef struct _GstVaapiDecoderFfmpegClass GstVaapiDecoderFfmpegClass;
/**
* GstVaapiDecoderFfmpeg:
*
* A decoder based on FFmpeg.
*/
struct _GstVaapiDecoderFfmpeg {
/*< private >*/
GstVaapiDecoder parent_instance;
GstVaapiDecoderFfmpegPrivate *priv;
};
/**
* GstVaapiDecoderFfmpegClass:
*
* A decoder class based on FFmpeg.
*/
struct _GstVaapiDecoderFfmpegClass {
/*< private >*/
GstVaapiDecoderClass parent_class;
};
GType
gst_vaapi_decoder_ffmpeg_get_type(void);
GstVaapiDecoder *
gst_vaapi_decoder_ffmpeg_new(GstVaapiDisplay *display, GstVaapiCodec codec);
G_END_DECLS
#endif /* GST_VAAPI_DECODER_FFMPEG_H */

View file

@ -0,0 +1,123 @@
/*
* gstvaapidecoder_priv.h - VA decoder abstraction (private definitions)
*
* gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GST_VAAPI_DECODER_PRIV_H
#define GST_VAAPI_DECODER_PRIV_H
#include <glib.h>
#include <gst/base/gstadapter.h>
#include <gst/vaapi/gstvaapidecoder.h>
#include <gst/vaapi/gstvaapicontext.h>
G_BEGIN_DECLS
#define GST_VAAPI_DECODER_CAST(decoder) ((GstVaapiDecoder *)(decoder))
/**
* GST_VAAPI_DECODER_DISPLAY:
* @decoder: a #GstVaapiDecoder
*
* Macro that evaluates to the #GstVaapiDisplay of @decoder.
* This is an internal macro that does not do any run-time type check.
*/
#undef GST_VAAPI_DECODER_DISPLAY
#define GST_VAAPI_DECODER_DISPLAY(decoder) \
GST_VAAPI_DECODER_CAST(decoder)->priv->display
/**
* GST_VAAPI_DECODER_CONTEXT:
* @decoder: a #GstVaapiDecoder
*
* Macro that evaluates to the #GstVaapiContext of @decoder.
* This is an internal macro that does not do any run-time type check.
*/
#undef GST_VAAPI_DECODER_CONTEXT
#define GST_VAAPI_DECODER_CONTEXT(decoder) \
GST_VAAPI_DECODER_CAST(decoder)->priv->context
/**
* GST_VAAPI_DECODER_CODEC:
* @decoder: a #GstVaapiDecoder
*
* Macro that evaluates to the #GstVaapiCodec of @decoder.
* This is an internal macro that does not do any run-time type check.
*/
#undef GST_VAAPI_DECODER_CODEC
#define GST_VAAPI_DECODER_CODEC(decoder) \
GST_VAAPI_DECODER_CAST(decoder)->priv->codec
#define GST_VAAPI_DECODER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), \
GST_VAAPI_TYPE_DECODER, \
GstVaapiDecoderPrivate))
struct _GstVaapiDecoderPrivate {
GstVaapiDisplay *display;
GstVaapiContext *context;
GstVaapiCodec codec;
GstAdapter *adapter;
GMutex *adapter_mutex;
GCond *adapter_cond;
GQueue surfaces;
GMutex *surfaces_mutex;
GCond *surfaces_cond;
GThread *decoder_thread;
guint decoder_thread_cancel : 1;
};
gboolean
gst_vaapi_decoder_ensure_context(
GstVaapiDecoder *decoder,
GstVaapiProfile profile,
GstVaapiEntrypoint entrypoint,
guint width,
guint height
) attribute_hidden;
guint
gst_vaapi_decoder_copy(
GstVaapiDecoder *decoder,
guint offset,
guchar *buf,
guint buf_size
) attribute_hidden;
guint
gst_vaapi_decoder_read_avail(GstVaapiDecoder *decoder)
attribute_hidden;
guint
gst_vaapi_decoder_read(GstVaapiDecoder *decoder, guchar *buf, guint buf_size)
attribute_hidden;
void
gst_vaapi_decoder_flush(GstVaapiDecoder *decoder, guint buf_size)
attribute_hidden;
gboolean
gst_vaapi_decoder_push_surface(
GstVaapiDecoder *decoder,
GstVaapiSurface *surface
) attribute_hidden;
G_END_DECLS
#endif /* GST_VAAPI_DECODER_PRIV_H */