Add initial (multithreaded) decoder based on FFmpeg.
authorgb <gb@5584edef-b1fe-4b99-b61b-dd2bab72e969>
Fri, 23 Apr 2010 16:05:58 +0000 (16:05 +0000)
committergb <gb@5584edef-b1fe-4b99-b61b-dd2bab72e969>
Fri, 23 Apr 2010 16:05:58 +0000 (16:05 +0000)
There are tons of bugs left:
- Decoder API not nice enough with error conditions
- FFmpeg parser is sometimes broken
- Packets queue can be lost

configure.ac
gst-libs/gst/vaapi/Makefile.am
gst-libs/gst/vaapi/gstvaapidecoder.c [new file with mode: 0644]
gst-libs/gst/vaapi/gstvaapidecoder.h [new file with mode: 0644]
gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c [new file with mode: 0644]
gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.h [new file with mode: 0644]
gst-libs/gst/vaapi/gstvaapidecoder_priv.h [new file with mode: 0644]

index df2a5e4..07b50ab 100644 (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)
index f8c59de..04a4547 100644 (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)
 
diff --git a/gst-libs/gst/vaapi/gstvaapidecoder.c b/gst-libs/gst/vaapi/gstvaapidecoder.c
new file mode 100644 (file)
index 0000000..fc80e6e
--- /dev/null
@@ -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;
+}
diff --git a/gst-libs/gst/vaapi/gstvaapidecoder.h b/gst-libs/gst/vaapi/gstvaapidecoder.h
new file mode 100644 (file)
index 0000000..4c2d4c0
--- /dev/null
@@ -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 */
diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c b/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c
new file mode 100644 (file)
index 0000000..f375a70
--- /dev/null
@@ -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);
+}
diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.h b/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.h
new file mode 100644 (file)
index 0000000..184759a
--- /dev/null
@@ -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 */
diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_priv.h b/gst-libs/gst/vaapi/gstvaapidecoder_priv.h
new file mode 100644 (file)
index 0000000..bfc05a7
--- /dev/null
@@ -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 */
+