]> git.sesse.net Git - vlc/commitdiff
Added (but disabled at compilation time) VA-API support to our avcodec wrapper.
authorLaurent Aimar <fenrir@videolan.org>
Mon, 1 Jun 2009 09:49:05 +0000 (11:49 +0200)
committerLaurent Aimar <fenrir@videolan.org>
Mon, 1 Jun 2009 10:44:20 +0000 (12:44 +0200)
 I choosed to extract the decoded frame back to memory instead of developping
a new specific vaapi vout. It was easier, allows transcoding and does not break
our current OSD.

 Becarefull, you probably have to applied a pending patch on ffmpeg for h264
decoding.

 You still have to manually uncomment the HAVE_AVCODEC_VAAPI define in
avcodec.h and to fix the build system to test it.

modules/codec/avcodec/Modules.am
modules/codec/avcodec/avcodec.c
modules/codec/avcodec/avcodec.h
modules/codec/avcodec/vaapi.c [new file with mode: 0644]
modules/codec/avcodec/vaapi.h [new file with mode: 0644]
modules/codec/avcodec/video.c

index af45ff8028882d19d6af53e71323ccdf3384e092..2d707a064d00b50ebdf3afc29b449eb44e37873c 100644 (file)
@@ -7,6 +7,7 @@ SOURCES_avcodec = \
        avutil.h \
        fourcc.c \
        chroma.c \
+       vaapi.c \
        $(NULL)
 
 if ENABLE_SOUT
index 0d4026e275fb4cc9ae49a0dae44f61818d4cbce0..13524dd9e5df6f45182335735876dcbfb0cddabf 100644 (file)
@@ -129,6 +129,9 @@ vlc_module_begin ()
 
     add_integer( "ffmpeg-debug", 0, NULL, DEBUG_TEXT, DEBUG_LONGTEXT,
                  true )
+#ifdef HAVE_AVCODEC_VAAPI
+    add_bool( "ffmpeg-hw", true, NULL, HW_TEXT, HW_LONGTEXT, true )
+#endif
 
 #ifdef ENABLE_SOUT
     /* encoder submodule */
index 88ba3ebf99cb4444e5d37d995015f3825a878793..b027ae17e9252c12845003421f7e8d2a06b63b0c 100644 (file)
@@ -127,6 +127,9 @@ void EndAudioDec( decoder_t *p_dec );
     "usually has a detrimental effect on quality. However it provides a big " \
     "speedup for high definition streams." )
 
+#define HW_TEXT N_("Hardware decoding")
+#define HW_LONGTEXT N_("This allows hardware decoding when available.")
+
 /*
  * Encoder options
  */
@@ -257,3 +260,6 @@ void EndAudioDec( decoder_t *p_dec );
 #   define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
 #endif
 
+/* Uncomment it to enable compilation with vaapi (you also must change the build
+ * system) */
+//#define HAVE_AVCODEC_VAAPI 1
diff --git a/modules/codec/avcodec/vaapi.c b/modules/codec/avcodec/vaapi.c
new file mode 100644 (file)
index 0000000..5b2d995
--- /dev/null
@@ -0,0 +1,487 @@
+/*****************************************************************************
+ * vaapi.c: VAAPI helpers for the ffmpeg decoder
+ *****************************************************************************
+ * Copyright (C) 2009 Laurent Aimar
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir_AT_ videolan _DOT_ org>
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_vout.h>
+#include <assert.h>
+
+#ifdef HAVE_LIBAVCODEC_AVCODEC_H
+#   include <libavcodec/avcodec.h>
+#   include <libavcodec/vaapi.h>
+#elif defined(HAVE_FFMPEG_AVCODEC_H)
+#   include <ffmpeg/avcodec.h>
+#else
+#   include <avcodec.h>
+#endif
+
+#include "avcodec.h"
+#include "vaapi.h"
+
+#ifdef HAVE_AVCODEC_VAAPI
+
+#include <X11/Xlib.h>
+#include <va/va_x11.h>
+
+
+typedef struct
+{
+    VASurfaceID  i_id;
+    int          i_refcount;
+    unsigned int i_order;
+
+} vlc_va_surface_t;
+
+struct vlc_va_t
+{
+    /* */
+    Display      *p_display_x11;
+    VADisplay     p_display;
+
+    VAConfigID    i_config_id;
+    VAContextID   i_context_id;
+
+    struct vaapi_context hw_ctx;
+
+    /* */
+    int i_version_major;
+    int i_version_minor;
+
+    /* */
+    int          i_surface_count;
+    unsigned int i_surface_order;
+    int          i_surface_width;
+    int          i_surface_height;
+    vlc_fourcc_t i_surface_chroma;
+
+    vlc_va_surface_t *p_surface;
+
+    VAImage      image;
+
+};
+
+static int VaOpen( vlc_va_t *p_va, int i_codec_id );
+static void VaClose( vlc_va_t *p_va );
+
+static int VaCreateSurfaces( vlc_va_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
+                             int i_width, int i_height );
+static void VaDestroySurfaces( vlc_va_t *p_va );
+
+vlc_va_t *VaNew( int i_codec_id )
+{
+    vlc_va_t *p_va = calloc( 1, sizeof(*p_va) );
+    if( !p_va )
+        return NULL;
+
+    if( VaOpen( p_va, i_codec_id ) )
+    {
+        free( p_va );
+        return NULL;
+    }
+    return p_va;
+}
+void VaDelete( vlc_va_t *p_va )
+{
+    VaClose( p_va );
+    free( p_va );
+}
+int VaSetup( vlc_va_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
+             int i_width, int i_height )
+{
+    if( p_va->i_surface_width == i_width &&
+        p_va->i_surface_height == i_height )
+        return VLC_SUCCESS;
+
+    *pp_hw_ctx = NULL;
+    *pi_chroma = 0;
+    if( p_va->i_surface_width || p_va->i_surface_height )
+        VaDestroySurfaces( p_va );
+
+    if( i_width > 0 && i_height > 0 )
+        return VaCreateSurfaces( p_va, pp_hw_ctx, pi_chroma, i_width, i_height );
+
+    return VLC_EGENERIC;
+}
+void VaVersion( vlc_va_t *p_va, char *psz_version, size_t i_version )
+{
+    snprintf( psz_version, i_version, "%d.%d", p_va->i_version_major, p_va->i_version_minor );
+}
+
+static int VaOpen( vlc_va_t *p_va, int i_codec_id )
+{
+    VAProfile i_profile;
+    int i_surface_count;
+
+    /* */
+    switch( i_codec_id )
+    {
+    case CODEC_ID_MPEG1VIDEO:
+    case CODEC_ID_MPEG2VIDEO:
+        i_profile = VAProfileMPEG2Main;
+        i_surface_count = 2+1;
+        break;
+    case CODEC_ID_MPEG4:
+        i_profile = VAProfileMPEG4AdvancedSimple;
+        i_surface_count = 2+1;
+        break;
+    case CODEC_ID_WMV3:
+        i_profile = VAProfileVC1Main;
+        i_surface_count = 2+1;
+        break;
+    case CODEC_ID_VC1:
+        i_profile = VAProfileVC1Advanced;
+        i_surface_count = 2+1;
+        break;
+    case CODEC_ID_H264:
+        i_profile = VAProfileH264High;
+        i_surface_count = 16+1;
+        break;
+    default:
+        return VLC_EGENERIC;
+    }
+
+    /* */
+    memset( p_va, 0, sizeof(*p_va) );
+
+    /* Create a VA display */
+    p_va->p_display_x11 = XOpenDisplay(NULL);
+    if( !p_va->p_display_x11 )
+        goto error;
+
+    p_va->p_display = vaGetDisplay( p_va->p_display_x11 );
+    if( !p_va->p_display )
+        goto error;
+
+    if( vaInitialize( p_va->p_display, &p_va->i_version_major, &p_va->i_version_minor ) )
+        goto error;
+
+    /* Create a VA configuration */
+    VAConfigAttrib attrib;
+    memset( &attrib, 0, sizeof(attrib) );
+    attrib.type = VAConfigAttribRTFormat;
+    if( vaGetConfigAttributes( p_va->p_display,
+                               i_profile, VAEntrypointVLD, &attrib, 1 ) )
+        goto error;
+
+    /* Not sure what to do if not, I don't have a way to test */
+    if( (attrib.value & VA_RT_FORMAT_YUV420) == 0 )
+        goto error;
+    if( vaCreateConfig( p_va->p_display,
+                        i_profile, VAEntrypointVLD, &attrib, 1, &p_va->i_config_id ) )
+    {
+        p_va->i_config_id = 0;
+        goto error;
+    }
+
+    p_va->i_surface_count = i_surface_count;
+
+    return VLC_SUCCESS;
+
+error:
+    return VLC_EGENERIC;
+}
+static void VaClose( vlc_va_t *p_va )
+{
+    if( p_va->i_surface_width || p_va->i_surface_height )
+        VaDestroySurfaces( p_va );
+
+    if( p_va->i_config_id )
+        vaDestroyConfig( p_va->p_display, p_va->i_config_id );
+    if( p_va->p_display )
+        vaTerminate( p_va->p_display );
+    if( p_va->p_display_x11 )
+        XCloseDisplay( p_va->p_display_x11 );
+}
+
+static int VaCreateSurfaces( vlc_va_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
+                             int i_width, int i_height )
+{
+    assert( i_width > 0 && i_height > 0 );
+
+    /* */
+    p_va->p_surface = calloc( p_va->i_surface_count, sizeof(*p_va->p_surface) );
+    if( !p_va->p_surface )
+        return VLC_EGENERIC;
+
+    /* Create surfaces */
+    VASurfaceID pi_surface_id[p_va->i_surface_count];
+    if( vaCreateSurfaces( p_va->p_display, i_width, i_height, VA_RT_FORMAT_YUV420,
+                          p_va->i_surface_count, pi_surface_id ) )
+    {
+        for( int i = 0; i < p_va->i_surface_count; i++ )
+            p_va->p_surface[i].i_id = VA_INVALID_SURFACE;
+        goto error;
+    }
+
+    for( int i = 0; i < p_va->i_surface_count; i++ )
+    {
+        vlc_va_surface_t *p_surface = &p_va->p_surface[i];
+
+        p_surface->i_id = pi_surface_id[i];
+        p_surface->i_refcount = 0;
+        p_surface->i_order = 0;
+    }
+
+    /* Create a context */
+    if( vaCreateContext( p_va->p_display, p_va->i_config_id,
+                         i_width, i_height, VA_PROGRESSIVE,
+                         pi_surface_id, p_va->i_surface_count, &p_va->i_context_id ) )
+    {
+        p_va->i_context_id = 0;
+        goto error;
+    }
+
+    /* Find a supported image chroma */
+    int i_fmt_count = vaMaxNumImageFormats( p_va->p_display );
+    VAImageFormat *p_fmt = calloc( i_fmt_count, sizeof(*p_fmt) );
+    if( !p_fmt )
+        goto error;
+
+    if( vaQueryImageFormats( p_va->p_display, p_fmt, &i_fmt_count ) )
+    {
+        free( p_fmt );
+        goto error;
+    }
+
+    vlc_fourcc_t  i_chroma = 0;
+    VAImageFormat fmt;
+    for( int i = 0; i < i_fmt_count; i++ )
+    {
+        if( p_fmt[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) ||
+            p_fmt[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) )
+        {
+            i_chroma = VLC_FOURCC( 'I', '4', '2', '0' );
+            fmt = p_fmt[i];
+        }
+        /* TODO: It seems that these may also be available (but not
+         * with my setup):
+         * VA_FOURCC( 'N', 'V', '1', '2')
+         * VA_FOURCC( 'U', 'Y', 'V', 'Y')
+         * VA_FOURCC( 'Y', 'U', 'Y', 'V')
+         */
+    }
+    free( p_fmt );
+    if( !i_chroma )
+        goto error;
+    *pi_chroma = i_chroma;
+
+    /* Create an image for surface extraction */
+    if( vaCreateImage(  p_va->p_display, &fmt, i_width, i_height, &p_va->image ) )
+    {
+        p_va->image.image_id = 0;
+        goto error;
+    }
+
+    /* Setup the ffmpeg hardware context */
+    *pp_hw_ctx = &p_va->hw_ctx;
+
+    memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) );
+    p_va->hw_ctx.display    = p_va->p_display;
+    p_va->hw_ctx.config_id  = p_va->i_config_id;
+    p_va->hw_ctx.context_id = p_va->i_context_id;
+
+    /* */
+    p_va->i_surface_chroma = i_chroma;
+    p_va->i_surface_width = i_width;
+    p_va->i_surface_height = i_height;
+    return VLC_SUCCESS;
+
+error:
+    VaDestroySurfaces( p_va );
+    return VLC_EGENERIC;
+}
+static void VaDestroySurfaces( vlc_va_t *p_va )
+{
+    if( p_va->image.image_id )
+        vaDestroyImage( p_va->p_display, p_va->image.image_id );
+
+    if( p_va->i_context_id )
+        vaDestroyContext( p_va->p_display, p_va->i_context_id );
+
+    for( int i = 0; i < p_va->i_surface_count && p_va->p_surface; i++ )
+    {
+        vlc_va_surface_t *p_surface = &p_va->p_surface[i];
+
+        if( p_surface->i_id != VA_INVALID_SURFACE )
+            vaDestroySurfaces( p_va->p_display, &p_surface->i_id, 1 );
+    }
+    free( p_va->p_surface );
+
+    /* */
+    p_va->image.image_id = 0;
+    p_va->i_context_id = 0;
+    p_va->p_surface = NULL;
+    p_va->i_surface_width = 0;
+    p_va->i_surface_height = 0;
+}
+
+int VaExtract( vlc_va_t *p_va, picture_t *p_picture, AVFrame *p_ff )
+{
+    VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
+
+    if( vaSyncSurface( p_va->p_display, p_va->i_context_id, i_surface_id ) )
+        return VLC_EGENERIC;
+
+    /* XXX vaDeriveImage may be better but it is not supported by
+     * my setup.
+     */
+
+    if( vaGetImage( p_va->p_display, i_surface_id,
+                    0, 0, p_va->i_surface_width, p_va->i_surface_height,
+                    p_va->image.image_id) )
+        return VLC_EGENERIC;
+
+    void *p_base;
+    if( vaMapBuffer( p_va->p_display, p_va->image.buf, &p_base ) )
+        return VLC_EGENERIC;
+
+    for( int i_plane = 0; i_plane < p_picture->i_planes; i_plane++ )
+    {
+        const int i_src_plane = ((p_va->image.format.fourcc == VA_FOURCC('Y','V','1','2' )) && i_plane != 0) ?  (3 - i_plane) : i_plane;
+        const uint8_t *p_src = (uint8_t*)p_base + p_va->image.offsets[i_src_plane];
+        const int i_src_stride = p_va->image.pitches[i_src_plane];
+
+        uint8_t *p_dst = p_picture->p[i_plane].p_pixels;
+        const int i_dst_stride = p_picture->p[i_plane].i_pitch;
+
+        if( i_src_stride != i_dst_stride )
+        {
+            for( int i = 0; i < p_picture->p[i_plane].i_visible_lines; i++ )
+            {
+                vlc_memcpy( p_dst, p_src, __MIN( i_src_stride, i_dst_stride ) );
+                p_src += i_src_stride;
+                p_dst += i_dst_stride;
+            }
+        }
+        else
+        {
+            vlc_memcpy( p_dst, p_src, p_picture->p[i_plane].i_visible_lines * i_src_stride );
+        }
+    }
+
+    if( vaUnmapBuffer( p_va->p_display, p_va->image.buf ) )
+        return VLC_EGENERIC;
+
+    return VLC_SUCCESS;
+}
+int VaGrabSurface( vlc_va_t *p_va, AVFrame *p_ff )
+{
+    int i_old;
+    int i;
+
+    /* Grab an unused surface, in case none are, try the oldest
+     * XXX using the oldest is a workaround in case a problem happens with ffmpeg */
+    for( i = 0, i_old = 0; i < p_va->i_surface_count; i++ )
+    {
+        vlc_va_surface_t *p_surface = &p_va->p_surface[i];
+
+        if( !p_surface->i_refcount )
+            break;
+
+        if( p_surface->i_order < p_va->p_surface[i_old].i_order )
+            i_old = i;
+    }
+    if( i >= p_va->i_surface_count )
+        i = i_old;
+
+    vlc_va_surface_t *p_surface = &p_va->p_surface[i];
+
+    p_surface->i_refcount = 1;
+    p_surface->i_order = p_va->i_surface_order++;
+
+    /* */
+    for( int i = 0; i < 4; i++ )
+    {
+        p_ff->data[i] = NULL;
+        p_ff->linesize[i] = 0;
+
+        if( i == 0 || i == 3 )
+            p_ff->data[i] = (void*)(uintptr_t)p_surface->i_id;/* Yummie */
+    }
+    return VLC_SUCCESS;
+}
+void VaUngrabSurface( vlc_va_t *p_va, AVFrame *p_ff )
+{
+    VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
+
+    for( int i = 0; i < p_va->i_surface_count; i++ )
+    {
+        vlc_va_surface_t *p_surface = &p_va->p_surface[i];
+
+        if( p_surface->i_id == i_surface_id )
+            p_surface->i_refcount--;
+    }
+}
+
+#else
+
+vlc_va_t *VaNew( int i_codec_id )
+{
+    VLC_UNUSED(i_codec_id);
+    return NULL;
+}
+void VaDelete( vlc_va_t *p_va )
+{
+    VLC_UNUSED(p_va);
+    assert( 0 );
+}
+
+void VaVersion( vlc_va_t *p_va, char *psz_version, size_t i_version )
+{
+    VLC_UNUSED(p_va); VLC_UNUSED(psz_version); VLC_UNUSED(i_version);
+    assert(0);
+}
+
+int VaSetup( vlc_va_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
+             int i_width, int i_height )
+{
+    VLC_UNUSED(p_va); VLC_UNUSED(pp_hw_ctx); VLC_UNUSED(pi_chroma);
+    VLC_UNUSED(i_width); VLC_UNUSED(i_height);
+    assert(0);
+    return -1;
+}
+
+int VaExtract( vlc_va_t *p_va, picture_t *p_picture, AVFrame *p_ff )
+{
+    VLC_UNUSED(p_va); VLC_UNUSED(p_picture); VLC_UNUSED(p_ff);
+    assert(0);
+    return -1;
+}
+
+int VaGrabSurface( vlc_va_t *p_va, AVFrame *p_ff )
+{
+    VLC_UNUSED(p_va); VLC_UNUSED(p_ff);
+    assert(0);
+    return -1;
+}
+
+void VaUngrabSurface( vlc_va_t *p_va, AVFrame *p_ff )
+{
+    VLC_UNUSED(p_va); VLC_UNUSED(p_ff);
+    assert(0);
+}
+
+#endif
diff --git a/modules/codec/avcodec/vaapi.h b/modules/codec/avcodec/vaapi.h
new file mode 100644 (file)
index 0000000..b8c6985
--- /dev/null
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ * vaapi.h: VAAPI helpers for the ffmpeg decoder
+ *****************************************************************************
+ * Copyright (C) 2009 Laurent Aimar
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir_AT_ videolan _DOT_ org>
+ *
+ * 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 _VLC_VAAPI_H
+#define _VLC_VAAPI_H 1
+
+typedef struct vlc_va_t vlc_va_t;
+
+vlc_va_t *VaNew( int i_codec_id );
+void VaDelete( vlc_va_t *p_va );
+
+void VaVersion( vlc_va_t *p_va, char *psz_version, size_t i_version );
+
+int VaSetup( vlc_va_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
+             int i_width, int i_height );
+
+int VaExtract( vlc_va_t *p_va, picture_t *p_picture, AVFrame *p_ff );
+
+int VaGrabSurface( vlc_va_t *p_va, AVFrame *p_ff );
+
+void VaUngrabSurface( vlc_va_t *p_va, AVFrame *p_ff );
+
+#endif
index c57b0d30fe4ab376ffdd95ebade8d56a00fd35b7..879f99518b0f8ea7242fc2839ef4e7dbb5a7bb54 100644 (file)
 #include <vlc_codec.h>
 #include <vlc_codecs.h>                               /* BITMAPINFOHEADER */
 #include <vlc_avcodec.h>
+#include <assert.h>
 
 /* ffmpeg header */
 #ifdef HAVE_LIBAVCODEC_AVCODEC_H
 #   include <libavcodec/avcodec.h>
+#   ifdef HAVE_AVCODEC_VAAPI
+#       include <libavcodec/vaapi.h>
+#   endif
 #elif defined(HAVE_FFMPEG_AVCODEC_H)
 #   include <ffmpeg/avcodec.h>
 #else
@@ -44,6 +48,7 @@
 #endif
 
 #include "avcodec.h"
+#include "vaapi.h"
 
 /*****************************************************************************
  * decoder_sys_t : decoder descriptor
@@ -84,6 +89,9 @@ struct decoder_sys_t
 
     /* */
     bool b_flush;
+
+    /* VA API */
+    vlc_va_t *p_va;
 };
 
 /* FIXME (dummy palette for now) */
@@ -100,6 +108,10 @@ static int  ffmpeg_ReGetFrameBuf( struct AVCodecContext *, AVFrame * );
 static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *, AVFrame * );
 static void ffmpeg_NextPts( decoder_t * );
 
+static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *,
+                                          const enum PixelFormat * );
+
+
 static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc )
 {
     uint8_t *p = (uint8_t*)&fcc;
@@ -114,7 +126,7 @@ static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc )
 static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec,
                                             AVCodecContext *p_context )
 {
-    picture_t *p_pic;
+    decoder_sys_t *p_sys = p_dec->p_sys;
 
     p_dec->fmt_out.video.i_width = p_context->width;
     p_dec->fmt_out.video.i_height = p_context->height;
@@ -124,10 +136,12 @@ static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec,
         return NULL; /* invalid display size */
     }
 
-    if( GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS )
+    if( !p_sys->p_va && GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) )
     {
-        /* we are doomed, but not really, because most codecs set their pix_fmt much later */
-        p_dec->fmt_out.i_codec = VLC_CODEC_I420;
+        /* we are doomed, but not really, because most codecs set their pix_fmt
+         * much later
+         * FIXME does it make sense here ? */
+        p_dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
     }
     p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma;
 
@@ -165,9 +179,7 @@ static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec,
         p_dec->fmt_out.video.i_frame_rate_base = p_context->time_base.num;
     }
 
-    p_pic = decoder_NewPicture( p_dec );
-
-    return p_pic;
+    return decoder_NewPicture( p_dec );
 }
 
 /*****************************************************************************
@@ -192,6 +204,7 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context,
     p_sys->psz_namecodec = psz_namecodec;
     p_sys->p_ff_pic = avcodec_alloc_frame();
     p_sys->b_delayed_open = true;
+    p_sys->p_va = NULL;
 
     /* ***** Fill p_context with init values ***** */
     p_sys->p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ?: p_dec->fmt_in.i_codec );
@@ -318,6 +331,9 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context,
     p_sys->p_context->release_buffer = ffmpeg_ReleaseFrameBuf;
     p_sys->p_context->opaque = p_dec;
 
+    if( var_CreateGetBool( p_dec, "ffmpeg-hw" ) )
+        p_sys->p_context->get_format = ffmpeg_GetFormat;
+
     /* ***** misc init ***** */
     p_sys->input_pts = p_sys->input_dts = 0;
     p_sys->i_pts = 0;
@@ -618,7 +634,7 @@ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block )
             p_sys->i_late_frames = 0;
         }
 
-        if( !b_drawpicture || !p_sys->p_ff_pic->linesize[0] )
+        if( !b_drawpicture || ( !p_sys->p_va && !p_sys->p_ff_pic->linesize[0] ) )
         {
             /* Do not display the picture */
             p_pic = (picture_t *)p_sys->p_ff_pic->opaque;
@@ -735,6 +751,9 @@ void EndVideoDec( decoder_t *p_dec )
 
     if( p_sys->p_ff_pic ) av_free( p_sys->p_ff_pic );
     free( p_sys->p_buffer_orig );
+
+    if( p_sys->p_va )
+        VaDelete( p_sys->p_va );
 }
 
 /*****************************************************************************
@@ -837,6 +856,14 @@ static int ffmpeg_OpenCodec( decoder_t *p_dec )
 
     p_sys->b_delayed_open = false;
 
+    if( p_sys->p_va )
+    {
+        char psz_version[128];
+
+        VaVersion( p_sys->p_va, psz_version, sizeof(psz_version) );
+        msg_Info( p_dec, "Using VA API version %s for hardware decoding.", psz_version );
+    }
+
     return VLC_SUCCESS;
 }
 /*****************************************************************************
@@ -848,7 +875,11 @@ static void ffmpeg_CopyPicture( decoder_t *p_dec,
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
 
-    if( TestFfmpegChroma( p_sys->p_context->pix_fmt, -1 ) == VLC_SUCCESS )
+    if( p_sys->p_va )
+    {
+        VaExtract( p_sys->p_va, p_pic, p_ff_pic );
+    }
+    else if( TestFfmpegChroma( p_sys->p_context->pix_fmt, -1 ) == VLC_SUCCESS )
     {
         int i_plane, i_size, i_line;
         uint8_t *p_dst, *p_src;
@@ -898,11 +929,33 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context,
     ffmpeg_SetFrameBufferPts( p_dec, p_ff_pic );
 
     /* */
-    p_ff_pic->opaque = 0;
+    p_ff_pic->opaque = NULL;
+
+    if( p_sys->p_va )
+    {
+        if( VaSetup( p_sys->p_va,
+                     &p_sys->p_context->hwaccel_context, &p_dec->fmt_out.video.i_chroma,
+                     p_sys->p_context->width, p_sys->p_context->height ) )
+        {
+            msg_Err( p_dec, "VaSetup failed" );
+            return -1;
+        }
+
+        /* */
+        p_ff_pic->type = FF_BUFFER_TYPE_USER;
+        /* FIXME what is that, should give good value */
+        p_ff_pic->age = 256*256*256*64; // FIXME FIXME from ffmpeg
 
-    /* Not much to do in indirect rendering mode */
-    if( !p_sys->b_direct_rendering )
+        if( VaGrabSurface( p_sys->p_va, p_ff_pic ) )
+        {
+            msg_Err( p_dec, "VaGrabSurface failed" );
+            return -1;
+        }
+        return 0;
+    }
+    else if( !p_sys->b_direct_rendering )
     {
+        /* Not much to do in indirect rendering mode */
         return avcodec_default_get_buffer( p_context, p_ff_pic );
     }
 
@@ -1011,20 +1064,30 @@ static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *p_context,
                                     AVFrame *p_ff_pic )
 {
     decoder_t *p_dec = (decoder_t *)p_context->opaque;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if( p_sys->p_va )
+    {
+        VaUngrabSurface( p_sys->p_va, p_ff_pic );
 
-    if( !p_ff_pic->opaque )
+        /* */
+        for( int i = 0; i < 4; i++ )
+            p_ff_pic->data[i] = NULL;
+    }
+    else if( !p_ff_pic->opaque )
     {
         avcodec_default_release_buffer( p_context, p_ff_pic );
-        return;
     }
+    else
+    {
+        picture_t *p_pic = (picture_t*)p_ff_pic->opaque;
 
-    picture_t *p_pic = (picture_t*)p_ff_pic->opaque;
-    decoder_UnlinkPicture( p_dec, p_pic );
+        decoder_UnlinkPicture( p_dec, p_pic );
 
-    p_ff_pic->data[0] = NULL;
-    p_ff_pic->data[1] = NULL;
-    p_ff_pic->data[2] = NULL;
-    p_ff_pic->data[3] = NULL;
+        /* */
+        for( int i = 0; i < 4; i++ )
+            p_ff_pic->data[i] = NULL;
+    }
 }
 
 static void ffmpeg_NextPts( decoder_t *p_dec )
@@ -1051,3 +1114,53 @@ static void ffmpeg_NextPts( decoder_t *p_dec )
             (2 * p_sys->p_context->time_base.den);
     }
 }
+
+static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_codec,
+                                          const enum PixelFormat *pi_fmt )
+{
+    decoder_t *p_dec = p_codec->opaque;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if( p_sys->p_va )
+    {
+        VaDelete( p_sys->p_va );
+        p_sys->p_va = NULL;
+    }
+
+    /* Try too look for a supported hw acceleration */
+    for( int i = 0; pi_fmt[i] != PIX_FMT_NONE; i++ )
+    {
+        static const char *ppsz_name[PIX_FMT_NB] = {
+            [PIX_FMT_VDPAU_H264] = "PIX_FMT_VDPAU_H264",
+            [PIX_FMT_VAAPI_IDCT] = "PIX_FMT_VAAPI_IDCT",
+            [PIX_FMT_VAAPI_VLD] = "PIX_FMT_VAAPI_VLD",
+            [PIX_FMT_VAAPI_MOCO] = "PIX_FMT_VAAPI_MOCO",
+            [PIX_FMT_YUYV422] = "PIX_FMT_YUYV422",
+            [PIX_FMT_YUV420P] = "PIX_FMT_YUV420P",
+        };
+        msg_Dbg( p_dec, "Available decoder output format %d (%s)", pi_fmt[i], ppsz_name[pi_fmt[i]] ?: "Unknown" );
+
+        /* Only VLD supported */
+        if( pi_fmt[i] == PIX_FMT_VAAPI_VLD )
+        {
+            msg_Dbg( p_dec, "Trying VA API" );
+            p_sys->p_va = VaNew( p_sys->i_codec_id );
+            if( p_sys->p_va )
+            {
+                /* FIXME this will disabled direct rendering
+                 * even if a new pixel format is renegociated
+                 *
+                 * FIXME Try to call VaSetup when possible
+                 * to detect errors when possible (later is too late) */
+                p_sys->b_direct_rendering = false;
+                p_sys->p_context->draw_horiz_band = NULL;
+                return pi_fmt[i];
+            }
+            msg_Warn( p_dec, "Failed to open VA API" );
+        }
+    }
+
+    /* Fallback to default behaviour */
+    return avcodec_default_get_format( p_codec, pi_fmt );
+}
+