From a203d19a35f2d5f64f4f239e1c6519fb21424094 Mon Sep 17 00:00:00 2001 From: gb Date: Fri, 23 Apr 2010 16:05:58 +0000 Subject: [PATCH] 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 --- configure.ac | 8 + gst-libs/gst/vaapi/Makefile.am | 25 + gst-libs/gst/vaapi/gstvaapidecoder.c | 617 ++++++++++++++++++++ gst-libs/gst/vaapi/gstvaapidecoder.h | 137 +++++ gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c | 610 +++++++++++++++++++ gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.h | 86 +++ gst-libs/gst/vaapi/gstvaapidecoder_priv.h | 123 ++++ 7 files changed, 1606 insertions(+) create mode 100644 gst-libs/gst/vaapi/gstvaapidecoder.c create mode 100644 gst-libs/gst/vaapi/gstvaapidecoder.h create mode 100644 gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c create mode 100644 gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.h create mode 100644 gst-libs/gst/vaapi/gstvaapidecoder_priv.h diff --git a/configure.ac b/configure.ac index df2a5e4d0b..07b50abac2 100644 --- a/configure.ac +++ b/configure.ac @@ -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) diff --git a/gst-libs/gst/vaapi/Makefile.am b/gst-libs/gst/vaapi/Makefile.am index f8c59de9e2..04a4547b43 100644 --- a/gst-libs/gst/vaapi/Makefile.am +++ b/gst-libs/gst/vaapi/Makefile.am @@ -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 index 0000000000..fc80e6ec10 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapidecoder.c @@ -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 +#include +#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 index 0000000000..4c2d4c0728 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapidecoder.h @@ -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 +#include +#include + +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 index 0000000000..f375a70b67 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.c @@ -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 +#include +#include +#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 index 0000000000..184759a687 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapidecoder_ffmpeg.h @@ -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 + +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 index 0000000000..bfc05a7b84 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapidecoder_priv.h @@ -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 +#include +#include +#include + +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 */ +