[vlc-devel] [Patches]: Video Decoder Acceleration (VDA) avcodec module for Mac OS X

Laurent Aimar fenrir at elivagar.org
Wed Nov 2 20:51:34 CET 2011


Hi,

On Tue, Nov 01, 2011 at 04:07:58PM +0100, Sebastien Zwickert wrote:
> The patch in attachment implements an avcodec module that uses a patched version of ffmpeg which takes benefit of VDA HWAccel module (patch sent on ffmpeg-devel mailing list).
> The set of patches contains some hacks to disable ffmpeg-mt because HWAccel is not working with ffmpeg-mt.
> 
> I tested the module on Lion and Snow Leopard and I've done some perf tests on Lion. It results of a gain of CPU usage : comparing the time profiler sample of a nightly build and a VDA build shows that VDA build reduces the usage of CPU of more than 30% on my mac book pro with an Intel HD graphic. The samples have been done when VLC played the first minute of Big Buck Bunny 1080p.
> 
> Notes that i've to adds LDFLAGS manually to compile the patched build. These LDFLAGS should be added in configure.ac but i did not succeed in doing that.
> export LDFLAGS="-framework CoreFoundation -framework VideoDecodeAcceleration -framework QuartzCore"


> From dd0e0498f293687de58aee6a4a3286c5d53395e7 Mon Sep 17 00:00:00 2001
> From: Sebastien Zwickert <dilaroga at free.fr>
> Date: Sun, 30 Oct 2011 01:06:04 +0200
> Subject: [PATCH 1/5] avcodec: Video Decoder Acceleration (VDA) for Mac OS X.
> 
> ---
>  modules/codec/avcodec/Modules.am |    1 +
>  modules/codec/avcodec/va.h       |    1 +
>  modules/codec/avcodec/vda.c      |  246 ++++++++++++++++++++++++++++++++++++++
>  modules/codec/avcodec/video.c    |   17 +++-
>  4 files changed, 263 insertions(+), 2 deletions(-)
>  create mode 100644 modules/codec/avcodec/vda.c
> 
> diff --git a/modules/codec/avcodec/Modules.am b/modules/codec/avcodec/Modules.am
> index 2dee687..33d157c 100644
> --- a/modules/codec/avcodec/Modules.am
> +++ b/modules/codec/avcodec/Modules.am
> @@ -11,6 +11,7 @@ libavcodec_plugin_la_SOURCES = \
>  	chroma.c \
>  	vaapi.c \
>  	dxva2.c \
> +	vda.c \
>  	copy.c \
>  	copy.h \
>  	va.h \
> diff --git a/modules/codec/avcodec/va.h b/modules/codec/avcodec/va.h
> index 1cc7844..8753212 100644
> --- a/modules/codec/avcodec/va.h
> +++ b/modules/codec/avcodec/va.h
> @@ -60,6 +60,7 @@ static inline void vlc_va_Delete(vlc_va_t *va)
>  
>  vlc_va_t *vlc_va_NewVaapi(vlc_object_t *obj, int codec_id);
>  vlc_va_t *vlc_va_NewDxva2(vlc_object_t *log, int codec_id);
> +vlc_va_t *vlc_va_NewVDA( vlc_object_t *log, int i_codec_id,void *p_extra, int i_extra);
>  
>  #endif
>  
> diff --git a/modules/codec/avcodec/vda.c b/modules/codec/avcodec/vda.c
> new file mode 100644
> index 0000000..fc7db16
> --- /dev/null
> +++ b/modules/codec/avcodec/vda.c
> @@ -0,0 +1,246 @@
> +/*****************************************************************************
> + * vda.c: VDA helpers for the ffmpeg decoder
> + *****************************************************************************
> + * Copyright (c) 2011 Sebastien Zwickert
> + * $Id$
> + *
> + * Authors: Sebastien Zwickert <dilaroga_AT_ free _DOT_ fr>
> + *
> + * 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>
> +
> +#include <libavformat/avformat.h>
> +#include <libavformat/avio.h>
> +
> +#ifdef HAVE_LIBAVCODEC_AVCODEC_H
> +#   include <libavcodec/avcodec.h>
> +#elif defined(HAVE_FFMPEG_AVCODEC_H)
> +#   include <ffmpeg/avcodec.h>
> +#else
> +#   include <avcodec.h>
> +#endif
> +
> +#include "avcodec.h"
> +#include "va.h"
> +
> +#ifdef HAVE_AVCODEC_VDA
> +
> +#include <libavcodec/vda.h>
> +#include <VideoDecodeAcceleration/VDADecoder.h>
> +
> +typedef struct
> +{
> +    vlc_va_t            va;
> +    struct vda_context  hw_ctx;
> +
> +    uint8_t             *p_extradata;
> +    int                 i_extradata;
> +
> +    vda_frame           *top_frame;
> +
> +    vlc_object_t        *p_log;
> +
> +} vlc_va_vda_t;
> +
> +static vlc_va_vda_t *vlc_va_vda_Get( void *p_va )
> +{
> +    return p_va;
> +}
> +
> +/*****************************************************************************
> + * vda_CopyPicture: copy CVPixelBuffer to picture_t
> + *****************************************************************************/
> +static void vda_CopyCVBuffer( picture_t *p_pic,
> +                              CVPixelBufferRef buffer )
> +{
> +    int i_plane, i_line, i_dst_stride, i_src_stride;
> +    uint8_t *p_dst, *p_src;
> +
> +    CVPixelBufferLockBaseAddress( buffer, 0 );
> +
> +    for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
> +    {
> +        p_dst = p_pic->p[i_plane].p_pixels;
> +        p_src = CVPixelBufferGetBaseAddressOfPlane( buffer, i_plane );
> +        i_dst_stride  = p_pic->p[i_plane].i_pitch;
> +        i_src_stride  = CVPixelBufferGetBytesPerRowOfPlane( buffer, i_plane );
> +
> +        for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines ; i_line++ )
> +        {
> +            vlc_memcpy( p_dst, p_src, i_src_stride );
> +
> +            p_src += i_src_stride;
> +            p_dst += i_dst_stride;
> +        }
> +    }
> +
> +    CVPixelBufferUnlockBaseAddress( buffer, 0 );
> +}
> +
> +static int Setup( vlc_va_t *p_external, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
> +                  int i_width, int i_height )
> +{
> +
> +    vlc_va_vda_t *p_va = vlc_va_vda_Get(p_external);
> +
> +    if ( p_va->hw_ctx.decoder != NULL )
> +    {
> +        *pp_hw_ctx = &p_va->hw_ctx;
> +        *pi_chroma = VLC_CODEC_UYVY;
> +        return VLC_SUCCESS;
> +    }
 This won't work. You must reinitialize when Setup() is called as some
parameters may have change (like the width/height).

> +    if ( pi_chroma == VLC_FOURCC( 'a', 'v', 'c', '1') )
 pi_chroma is a pointer...

> +    {
> +        msg_Warn( p_va->p_log, "Not avc." );
> +        return VLC_EGENERIC;
> +    }
> +
> +    *pp_hw_ctx = NULL;
> +    *pi_chroma = VLC_CODEC_UYVY;
 Can't YV12/NV12/I420 be used?

> +    /* Setup the ffmpeg hardware context */
> +    *pp_hw_ctx = &p_va->hw_ctx;
> +
> +    p_va->top_frame = NULL;
> +
> +    memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) );
> +    p_va->hw_ctx.queue = NULL;
> +    p_va->hw_ctx.queue_mutex = NULL;
> +    p_va->hw_ctx.width = i_width;
> +    p_va->hw_ctx.height = i_height;
> +    p_va->hw_ctx.format = 'avc1';
> +
> +    /* create the decoder */
> +    int status = ff_vda_create_decoder(&p_va->hw_ctx, p_va->p_extradata, p_va->i_extradata);
> +    if ( status )
> +    {
> +        msg_Err( p_va->p_log, "Failed to create the decoder : %i", status );
> +        return VLC_EGENERIC;
> +    }
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static int Get( vlc_va_t *p_external, AVFrame *p_ff )
> +{
> +    vlc_va_vda_t *p_va = vlc_va_vda_Get(p_external);
> +
> +    /* FIXME: release previous frame buffer if needed. The same part of code in Release causes memory leaks. */
> +    if ( p_va->top_frame )
> +        ff_vda_release_vda_frame( p_va->top_frame );
> +
> +    p_va->top_frame = ff_vda_queue_pop( &p_va->hw_ctx );
> +
> +    /* */
> +    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] = 1; // FIXME: a better fake value ?
> +    }
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static int Extract( vlc_va_t *p_external, picture_t *p_picture, AVFrame *p_ff )
> +{
> +    VLC_UNUSED( p_ff );
> +    vlc_va_vda_t *p_va = vlc_va_vda_Get( p_external );
> +
> +    if ( !p_va->top_frame )
> +    {
> +        msg_Dbg( p_va->p_log, "Decoder is buffering...");
> +        return VLC_EGENERIC;
> +    }
> +
> +    CVPixelBufferRef cv_buffer = p_va->top_frame->cv_buffer;
> +    vda_CopyCVBuffer( p_picture, cv_buffer );
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static void Release( vlc_va_t *p_external, AVFrame *p_ff )
> +{
> +    VLC_UNUSED( p_ff );
> +    VLC_UNUSED( p_external );
> +}

 I am a bit dubious about the way you implemented the surface
attachment to the picture.
 To have it correctly working you should attach the correct surface to its
associated AVFrame buffer. Otherwise I don't think the picture order will
be the correct one.

> +
> +static void Close( vlc_va_vda_t *p_va )
> +{
> +    ff_vda_destroy_decoder( &p_va->hw_ctx ) ;
> +
> +    if ( p_va->top_frame )
> +        ff_vda_release_vda_frame( p_va->top_frame );
> +
> +    free( p_va );
> +}
> +
> +static void Delete( vlc_va_t *p_external )
> +{
> +    vlc_va_vda_t *p_va = vlc_va_vda_Get( p_external );
> +    Close( p_va );
> +}
You could merge Delete() and Close().

> +vlc_va_t *vlc_va_NewVDA( vlc_object_t *p_log, int i_codec_id, void *p_extra, int i_extra )
 Do you really need the extra data at this point?
You won't always have them at that point.
> +{
> +    if ( i_codec_id != CODEC_ID_H264 )
> +        return NULL;
> +
> +    if ( !p_extra || i_extra < 7 )
> +    {
> +        msg_Warn( p_log, "VDA requires extradata." );
> +        return NULL;
> +    }
> +
> +    vlc_va_vda_t *p_va = calloc( 1, sizeof(*p_va) );
> +    if( !p_va )
> +        return NULL;
> +
> +    msg_Dbg( p_log, "Trying VDA module" );
 I don't think this log is helpful.
At this point you should also try as much as possible to ensure that VDA
module has a chance to succeed, and if any initialized the context
parameters that will remain constants.
> +
> +    p_va->p_log = p_log;
> +    p_va->p_extradata = p_extra;
> +    p_va->i_extradata = i_extra;
> +
> +    p_va->va.setup = Setup;
> +    p_va->va.get = Get;
> +    p_va->va.release = Release;
> +    p_va->va.extract = Extract;
> +    p_va->va.close = Delete;
> +
> +    return &p_va->va;
> +}
> +
> +#else
> +vlc_va_t *vlc_va_NewVDA( vlc_object_t *p_log, int i_codec_id, void *p_extra, int i_extra )
> +{
> +    VLC_UNUSED( p_log );
> +    VLC_UNUSED( i_codec_id );
> +    VLC_UNUSED( p_extra );
> +    VLC_UNUSED( i_extra );
> +    return NULL;
> +}
> +#endif
> diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c
> index 6c9b865..7fdeb71 100644
> --- a/modules/codec/avcodec/video.c
> +++ b/modules/codec/avcodec/video.c
> @@ -44,6 +44,9 @@
>  #   ifdef HAVE_AVCODEC_DXVA2
>  #       include <libavcodec/dxva2.h>
>  #   endif
> +#   ifdef HAVE_AVCODEC_VDA
> +#       include <libavcodec/vda.h>
> +#   endif
>  #elif defined(HAVE_FFMPEG_AVCODEC_H)
>  #   include <ffmpeg/avcodec.h>
>  #else
> @@ -52,7 +55,7 @@
>  
>  #include "avcodec.h"
>  #include "va.h"
> -#if defined(HAVE_AVCODEC_VAAPI) || defined(HAVE_AVCODEC_DXVA2)
> +#if defined(HAVE_AVCODEC_VAAPI) || defined(HAVE_AVCODEC_DXVA2) || defined(HAVE_AVCODEC_VDA)
>  #   define HAVE_AVCODEC_VA
>  #endif
>  
> @@ -1126,6 +1129,9 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
>  #ifdef HAVE_AVCODEC_DXVA2
>              [PIX_FMT_DXVA2_VLD] = "PIX_FMT_DXVA2_VLD",
>  #endif
> +#ifdef HAVE_AVCODEC_VDA
> +            [PIX_FMT_VDA_VLD] = "PIX_FMT_VDA_VLD",
> +#endif
>              [PIX_FMT_YUYV422] = "PIX_FMT_YUYV422",
>              [PIX_FMT_YUV420P] = "PIX_FMT_YUV420P",
>          };
> @@ -1157,7 +1163,14 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
>                  msg_Warn( p_dec, "Failed to open DXVA2" );
>          }
>  #endif
> -
> +#ifdef HAVE_AVCODEC_VDA
> +        if ( pi_fmt[i] == PIX_FMT_VDA_VLD )
> +        {
> +            p_sys->p_va = vlc_va_NewVDA( VLC_OBJECT(p_dec), p_sys->i_codec_id, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra );
> +            if( !p_sys->p_va )
> +                msg_Warn( p_dec, "Failed to open VDA" );
> +        }
> +#endif
>          if( p_sys->p_va &&
>              p_context->width > 0 && p_context->height > 0 )
>          {
> -- 
> 1.7.5.4


> From 5ec71aeead01341cbd5292fba68d1b730421432d Mon Sep 17 00:00:00 2001
> From: Sebastien Zwickert <dilaroga at free.fr>
> Date: Sun, 30 Oct 2011 01:08:20 +0200
> Subject: [PATCH 3/5] avcodec: hack set ffmpeg-hw to true.
> 
> ---
>  modules/codec/avcodec/avcodec.c |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/modules/codec/avcodec/avcodec.c b/modules/codec/avcodec/avcodec.c
> index 0d98564..b1a0128 100644
> --- a/modules/codec/avcodec/avcodec.c
> +++ b/modules/codec/avcodec/avcodec.c
> @@ -127,8 +127,8 @@ vlc_module_begin ()
>  
>      add_integer( "ffmpeg-debug", 0, DEBUG_TEXT, DEBUG_LONGTEXT,
>                   true )
> -#if defined(HAVE_AVCODEC_VAAPI) || defined(HAVE_AVCODEC_DXVA2)
> -    add_bool( "ffmpeg-hw", false, HW_TEXT, HW_LONGTEXT, false )
> +#if defined(HAVE_AVCODEC_VAAPI) || defined(HAVE_AVCODEC_DXVA2) || defined(HAVE_AVCODEC_VDA)
> +    add_bool( "ffmpeg-hw", true, HW_TEXT, HW_LONGTEXT, true )
>  #endif
>  #if defined(FF_THREAD_FRAME)
>      add_integer( "ffmpeg-threads", 0, THREADS_TEXT, THREADS_LONGTEXT, true );
> -- 
> 1.7.5.4
 The check against HAVE_AVCODEC_VDA should be merged in one of the
previous patch.
 I don't think changing the default value is a good idea, at least for now.

> From 30fd305cc3910b9f3f70565a4ef23b7121f4d26a Mon Sep 17 00:00:00 2001
> From: Sebastien Zwickert <dilaroga at free.fr>
> Date: Sun, 30 Oct 2011 01:08:45 +0200
> Subject: [PATCH 4/5] avcodec: adds HAVE_AVCODEC_VDA.
> 
> ---
>  modules/codec/avcodec/avcodec.h |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/modules/codec/avcodec/avcodec.h b/modules/codec/avcodec/avcodec.h
> index e9dbf18..48f52b8 100644
> --- a/modules/codec/avcodec/avcodec.h
> +++ b/modules/codec/avcodec/avcodec.h
> @@ -267,15 +267,15 @@ int ffmpeg_OpenCodec( decoder_t *p_dec );
>  #ifndef AV_VERSION_INT
>  #   define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
>  #endif
> -
Cosmetic.
>  #if defined(FF_THREAD_FRAME)
>  #   define HAVE_AVCODEC_MT
>  #endif
>  
> -/* Uncomment it to enable compilation with vaapi/dxva2 (you also must change the build
> +/* Uncomment it to enable compilation with vaapi/dxva2/vda (you also must change the build
>   * system) */
>  //#define HAVE_AVCODEC_VAAPI 1
>  //#define HAVE_AVCODEC_DXVA2 1
> +//#define HAVE_AVCODEC_VDA 1
>  
>  /* Ugly ifdefinitions to provide backwards compatibility with older ffmpeg/libav
>   * versions */
> -- 
> 1.7.5.4
 It should be merged in one of the previous patch.


> From b87cbbf7fbd44b3b9d4ef16dfcddbab307763a0d Mon Sep 17 00:00:00 2001
> From: Sebastien Zwickert <dilaroga at free.fr>
> Date: Sun, 30 Oct 2011 01:09:17 +0200
> Subject: [PATCH 5/5] avcodec: hack FF_THREAD_FRAME disabled.
> 
> ---
>  modules/codec/avcodec/avcodec.c |    4 ++--
>  modules/codec/avcodec/avcodec.h |    4 ++--
>  2 files changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/modules/codec/avcodec/avcodec.c b/modules/codec/avcodec/avcodec.c
> index b1a0128..5ecd631 100644
> --- a/modules/codec/avcodec/avcodec.c
> +++ b/modules/codec/avcodec/avcodec.c
> @@ -130,9 +130,9 @@ vlc_module_begin ()
>  #if defined(HAVE_AVCODEC_VAAPI) || defined(HAVE_AVCODEC_DXVA2) || defined(HAVE_AVCODEC_VDA)
>      add_bool( "ffmpeg-hw", true, HW_TEXT, HW_LONGTEXT, true )
>  #endif
> -#if defined(FF_THREAD_FRAME)
> +/*#if defined(FF_THREAD_FRAME)
>      add_integer( "ffmpeg-threads", 0, THREADS_TEXT, THREADS_LONGTEXT, true );
> -#endif
> +#endif*/
>  
>  
>  #ifdef ENABLE_SOUT
> diff --git a/modules/codec/avcodec/avcodec.h b/modules/codec/avcodec/avcodec.h
> index 48f52b8..d705abe 100644
> --- a/modules/codec/avcodec/avcodec.h
> +++ b/modules/codec/avcodec/avcodec.h
> @@ -267,9 +267,9 @@ int ffmpeg_OpenCodec( decoder_t *p_dec );
>  #ifndef AV_VERSION_INT
>  #   define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
>  #endif
> -#if defined(FF_THREAD_FRAME)
> +/*#if defined(FF_THREAD_FRAME)
>  #   define HAVE_AVCODEC_MT
> -#endif
> +#endif*/
>  
>  /* Uncomment it to enable compilation with vaapi/dxva2/vda (you also must change the build
>   * system) */
> -- 
> 1.7.5.4

 This patch should not be needed as ffmpeg-mt is disabled when hw is used.
(look for msg_Warn( p_dec, "threaded frame decoding is not compatible with ffmpeg-hw, disabled" )).


Regards,

-- 
fenrir



More information about the vlc-devel mailing list