]> git.sesse.net Git - vlc/commitdiff
avcodec: Mac OS X Video Acceleration (VDA) support (close #3558)
authorSebastien Zwickert <dilaroga@free.fr>
Thu, 29 Mar 2012 17:06:15 +0000 (19:06 +0200)
committerFelix Paul Kühne <fkuehne@videolan.org>
Tue, 31 Jul 2012 15:52:55 +0000 (17:52 +0200)
With minor updates by me.

Signed-off-by: Felix Paul Kühne <fkuehne@videolan.org>
NEWS
configure.ac
modules/codec/avcodec/Modules.am
modules/codec/avcodec/avcodec.c
modules/codec/avcodec/avcodec.h
modules/codec/avcodec/va.h
modules/codec/avcodec/vda.c [new file with mode: 0644]
modules/codec/avcodec/video.c

diff --git a/NEWS b/NEWS
index fca255b9752b8c1e0f6ec6bb3d8eb68fa9f792f7..9b0a6cabe8a1b6e172d5decbf22d8758bcf423a0 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,7 @@ Codecs:
  * Support for TechSmith Screen Codec 2, Microsoft Expression Encoder Screen,
    Microsoft Application Screen Decoder 1 (MSS1)
  * Support for Indeo Audio Coder, RealAudio Lossless
+ * Add Hardware Acceleration support on OS X for H.264 based upon VDADecoder
 
 Encoders:
  * high10, high422 and high444 encoding support in h264
index d58337072c6b6142529a5d33990b752c72fa37e6..702ae7d360fbb666bb847eb21b841ac681d5a9be 100644 (file)
@@ -2366,6 +2366,38 @@ AS_IF([test "${enable_dxva2}" != "no"], [
   fi
 ])
 
+dnl
+dnl vda needs avcodec
+dnl
+AC_ARG_ENABLE(vda,
+  [  --enable-vda          VDA  support (default auto)])
+
+AS_IF([test "${enable_vda}" != "no"], [
+  if test "${SYS}" = "darwin"; then
+  AS_IF([test "x${have_avcodec}" = "xyes"], [
+    AC_CHECK_HEADERS(VideoDecodeAcceleration/VDADecoder.h,
+      [
+        AC_CHECK_HEADERS(libavcodec/vda.h, [
+           VLC_ADD_LIBS([avcodec],[-Wl,-framework,CoreFoundation,-framework,VideoDecodeAcceleration,-framework,QuartzCore])
+           VLC_ADD_LDFLAGS([vda],[-Wl,-framework,CoreFoundation,-framework,VideoDecodeAcceleration,-framework,QuartzCore])
+           AC_DEFINE(HAVE_AVCODEC_VDA, 1, [Define if avcodec has to be built with VDA support.])
+        ],[
+       AS_IF([test "${enable_vda}" == "yes"],
+             [AC_MSG_ERROR([vda is present but libavcodec/vda.h is missing])],
+              [AC_MSG_WARN([vda is present but libavcodec/vda.h is missing ])])
+        ])
+  ],[
+       AS_IF([test "${enable_vda}" == "yes"],
+              [AC_MSG_ERROR([Could not find required VideoDecodeAcceleration/VDADecoder.h])],
+              [AC_MSG_WARN([VideoDecodeAcceleration/VDADecoder.h not found])])
+      ])
+  ],[
+    AS_IF([test "x${enable_vda}" != "x"], [
+      AC_MSG_ERROR([--enable-vda and --disable-avcodec options are mutually exclusive.])
+    ])
+ ])
+  fi
+])
 
 dnl
 dnl stream_out switcher needs libavcodec
index 9ea932fe56c84c2986abb2f5714dd6b58d35f961..4d32ac572c1600f752193539ca554c790e99da3b 100644 (file)
@@ -11,6 +11,7 @@ libavcodec_plugin_la_SOURCES = \
        chroma.c \
        vaapi.c \
        dxva2.c \
+       vda.c \
        copy.c \
        copy.h \
        va.h \
index 4a6849e57e67191037e468afbf95e929680c754c..a9d2dad6ebc75a43cfaee6dd780e44037a5ca04d 100644 (file)
@@ -70,6 +70,12 @@ static const int  nloopf_list[] = { 0, 1, 2, 3, 4 };
 static const char *const nloopf_list_text[] =
   { N_("None"), N_("Non-ref"), N_("Bidir"), N_("Non-key"), N_("All") };
 
+#if defined(HAVE_AVCODEC_VDA)
+static const int  nvda_pix_fmt_list[] = { 0, 1 };
+static const char *const nvda_pix_fmt_list_text[] =
+  { N_("420YpCbCr8Planar"), N_("422YpCbCr8") };
+#endif
+
 #ifdef ENABLE_SOUT
 static const char *const enc_hq_list[] = { "rd", "bits", "simple" };
 static const char *const enc_hq_list_text[] = {
@@ -140,9 +146,15 @@ vlc_module_begin ()
                  true )
     add_obsolete_string( "ffmpeg-codec" ) /* removed since 2.1.0 */
     add_string( "avcodec-codec", NULL, CODEC_TEXT, CODEC_LONGTEXT, true )
-#if defined(HAVE_AVCODEC_VAAPI) || defined(HAVE_AVCODEC_DXVA2)
+#if defined(HAVE_AVCODEC_VAAPI) || defined(HAVE_AVCODEC_DXVA2) || defined(HAVE_AVCODEC_VDA)
     add_obsolete_bool( "ffmpeg-hw" ) /* removed since 2.1.0 */
     add_bool( "avcodec-hw", false, HW_TEXT, HW_LONGTEXT, false )
+#if defined(HAVE_AVCODEC_VDA)
+    add_integer ( "avcodec-vda-pix-fmt", 0, VDA_PIX_FMT_TEXT,
+                  VDA_PIX_FMT_LONGTEXT, false)
+        change_safe ()
+        change_integer_list( nvda_pix_fmt_list, nvda_pix_fmt_list_text )
+#endif
 #endif
 #if defined(FF_THREAD_FRAME)
     add_obsolete_integer( "ffmpeg-threads" ) /* removed since 2.1.0 */
index dcb01d3d986766dba60d04de2328f387eded3233..b05db726c4f6e21ec7be5110a23f6c5f323267c1 100644 (file)
@@ -138,6 +138,9 @@ int ffmpeg_OpenCodec( decoder_t *p_dec );
 #define HW_TEXT N_("Hardware decoding")
 #define HW_LONGTEXT N_("This allows hardware decoding when available.")
 
+#define VDA_PIX_FMT_TEXT N_("VDA output pixel format")
+#define VDA_PIX_FMT_LONGTEXT N_("The pixel format for output image buffers.")
+
 #define THREADS_TEXT N_( "Threads" )
 #define THREADS_LONGTEXT N_( "Number of threads used for decoding, 0 meaning auto" )
 
@@ -273,10 +276,11 @@ int ffmpeg_OpenCodec( decoder_t *p_dec );
 #   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 */
index 1cc784430796c634cecf541c245a74e056dd70c9..8753212683ee4ff17814a6fea3b25d7a43fa9bfa 100644 (file)
@@ -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 (file)
index 0000000..fc68f31
--- /dev/null
@@ -0,0 +1,293 @@
+/*****************************************************************************
+ * vda.c: VDA helpers for the ffmpeg decoder
+ *****************************************************************************
+ * Copyright © 2012 VideoLAN
+ *
+ * Authors: Sebastien Zwickert <dilaroga@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <libavcodec/avcodec.h>
+
+#include "avcodec.h"
+#include "va.h"
+#include "copy.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;
+
+    vlc_fourcc_t        i_chroma;
+
+    copy_cache_t        image_cache;
+
+    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_Copy420YpCbCr8Planar: copy y420 CVPixelBuffer to picture_t
+ *****************************************************************************/
+static void vda_Copy420YpCbCr8Planar( picture_t *p_pic,
+                                      CVPixelBufferRef buffer,
+                                      unsigned i_width,
+                                      unsigned i_height,
+                                      copy_cache_t *cache )
+{
+    uint8_t *pp_plane[3];
+    size_t  pi_pitch[3];
+
+    CVPixelBufferLockBaseAddress( buffer, 0 );
+
+    for( int i = 0; i < 3; i++ )
+    {
+        pp_plane[i] = CVPixelBufferGetBaseAddressOfPlane( buffer, i );
+        pi_pitch[i] = CVPixelBufferGetBytesPerRowOfPlane( buffer, i );
+    }
+
+    CopyFromYv12( p_pic, pp_plane, pi_pitch,
+                  i_width, i_height, cache );
+
+    CVPixelBufferUnlockBaseAddress( buffer, 0 );
+}
+
+/*****************************************************************************
+ * vda_Copy422YpCbCr8: copy 2vuy CVPixelBuffer to picture_t
+ *****************************************************************************/
+static void vda_Copy422YpCbCr8( 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++ )
+        {
+            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.width == i_width
+        && p_va->hw_ctx.height == i_height
+        && p_va->hw_ctx.decoder )
+    {
+        *pp_hw_ctx = &p_va->hw_ctx;
+        *pi_chroma = p_va->i_chroma;
+        return VLC_SUCCESS;
+    }
+
+    if( p_va->hw_ctx.decoder )
+    {
+        ff_vda_destroy_decoder( &p_va->hw_ctx );
+        goto ok;
+    }
+
+    memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) );
+    p_va->hw_ctx.width = i_width;
+    p_va->hw_ctx.height = i_height;
+    p_va->hw_ctx.format = 'avc1';
+
+    int i_pix_fmt = var_CreateGetInteger( p_va->p_log, "avcodec-vda-pix-fmt" );
+
+    switch( i_pix_fmt )
+    {
+        case 1 :
+            p_va->hw_ctx.cv_pix_fmt_type = kCVPixelFormatType_422YpCbCr8;
+            p_va->i_chroma = VLC_CODEC_UYVY;
+            break;
+        case 0 :
+        default :
+            p_va->hw_ctx.cv_pix_fmt_type = kCVPixelFormatType_420YpCbCr8Planar;
+            p_va->i_chroma = VLC_CODEC_I420;
+            CopyInitCache( &p_va->image_cache, i_width );
+    }
+
+ok:
+    /* Setup the ffmpeg hardware context */
+    *pp_hw_ctx = &p_va->hw_ctx;
+    *pi_chroma = p_va->i_chroma;
+
+    /* 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 );
+
+    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; // dummy
+    }
+
+    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;
+
+    if( p_va->hw_ctx.cv_pix_fmt_type == kCVPixelFormatType_420YpCbCr8Planar )
+    {
+        if( !p_va->image_cache.buffer )
+            return VLC_EGENERIC;
+
+        vda_Copy420YpCbCr8Planar( p_picture,
+                                  cv_buffer,
+                                  p_va->hw_ctx.width,
+                                  p_va->hw_ctx.height,
+                                  &p_va->image_cache );
+    }
+    else
+        vda_Copy422YpCbCr8( p_picture, cv_buffer );
+
+    p_picture->date = p_va->top_frame->pts;
+
+    return VLC_SUCCESS;
+}
+
+static void Release( vlc_va_t *p_external, AVFrame *p_ff )
+{
+    VLC_UNUSED( p_ff );
+    VLC_UNUSED( p_external );
+}
+
+static void Close( vlc_va_t *p_external )
+{
+    vlc_va_vda_t *p_va = vlc_va_vda_Get( p_external );
+
+    ff_vda_destroy_decoder( &p_va->hw_ctx ) ;
+
+    if( p_va->top_frame )
+        ff_vda_release_vda_frame( p_va->top_frame );
+
+    if( p_va->hw_ctx.cv_pix_fmt_type == kCVPixelFormatType_420YpCbCr8Planar )
+        CopyCleanCache( &p_va->image_cache );
+
+    free( p_va );
+}
+
+vlc_va_t *vlc_va_NewVDA( vlc_object_t *p_log, int i_codec_id, void *p_extra, int i_extra )
+{
+    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;
+
+    p_va->p_log = p_log;
+    p_va->p_extradata = p_extra;
+    p_va->i_extradata = i_extra;
+    p_va->top_frame = NULL;
+
+    p_va->va.setup = Setup;
+    p_va->va.get = Get;
+    p_va->va.release = Release;
+    p_va->va.extract = Extract;
+    p_va->va.close = Close;
+
+    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
index a3465519b21647cbc656681892800a0413b452a0..b2e4271b7b9f226a8274ce7876513396caa001cd 100644 (file)
@@ -52,6 +52,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
@@ -60,7 +63,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
 
@@ -1166,6 +1169,9 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
             [PIX_FMT_VAAPI_MOCO] = "PIX_FMT_VAAPI_MOCO",
 #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",
@@ -1199,6 +1205,16 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
         }
 #endif
 
+#ifdef HAVE_AVCODEC_VDA
+        if( pi_fmt[i] == PIX_FMT_VDA_VLD )
+        {
+            msg_Dbg( p_dec, "Trying VDA" );
+            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 )
         {