Commit 300cf98e authored by Sebastien Zwickert's avatar Sebastien Zwickert Committed by Felix Paul Kühne

avcodec: Mac OS X Video Acceleration (VDA) support (close #3558)

With minor updates by me.
Signed-off-by: default avatarFelix Paul Kühne <fkuehne@videolan.org>
parent 72645d4f
...@@ -15,6 +15,7 @@ Codecs: ...@@ -15,6 +15,7 @@ Codecs:
* Support for TechSmith Screen Codec 2, Microsoft Expression Encoder Screen, * Support for TechSmith Screen Codec 2, Microsoft Expression Encoder Screen,
Microsoft Application Screen Decoder 1 (MSS1) Microsoft Application Screen Decoder 1 (MSS1)
* Support for Indeo Audio Coder, RealAudio Lossless * Support for Indeo Audio Coder, RealAudio Lossless
* Add Hardware Acceleration support on OS X for H.264 based upon VDADecoder
Encoders: Encoders:
* high10, high422 and high444 encoding support in h264 * high10, high422 and high444 encoding support in h264
......
...@@ -2366,6 +2366,38 @@ AS_IF([test "${enable_dxva2}" != "no"], [ ...@@ -2366,6 +2366,38 @@ AS_IF([test "${enable_dxva2}" != "no"], [
fi 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
dnl stream_out switcher needs libavcodec dnl stream_out switcher needs libavcodec
......
...@@ -11,6 +11,7 @@ libavcodec_plugin_la_SOURCES = \ ...@@ -11,6 +11,7 @@ libavcodec_plugin_la_SOURCES = \
chroma.c \ chroma.c \
vaapi.c \ vaapi.c \
dxva2.c \ dxva2.c \
vda.c \
copy.c \ copy.c \
copy.h \ copy.h \
va.h \ va.h \
......
...@@ -70,6 +70,12 @@ static const int nloopf_list[] = { 0, 1, 2, 3, 4 }; ...@@ -70,6 +70,12 @@ static const int nloopf_list[] = { 0, 1, 2, 3, 4 };
static const char *const nloopf_list_text[] = static const char *const nloopf_list_text[] =
{ N_("None"), N_("Non-ref"), N_("Bidir"), N_("Non-key"), N_("All") }; { 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 #ifdef ENABLE_SOUT
static const char *const enc_hq_list[] = { "rd", "bits", "simple" }; static const char *const enc_hq_list[] = { "rd", "bits", "simple" };
static const char *const enc_hq_list_text[] = { static const char *const enc_hq_list_text[] = {
...@@ -140,9 +146,15 @@ vlc_module_begin () ...@@ -140,9 +146,15 @@ vlc_module_begin ()
true ) true )
add_obsolete_string( "ffmpeg-codec" ) /* removed since 2.1.0 */ add_obsolete_string( "ffmpeg-codec" ) /* removed since 2.1.0 */
add_string( "avcodec-codec", NULL, CODEC_TEXT, CODEC_LONGTEXT, true ) 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_obsolete_bool( "ffmpeg-hw" ) /* removed since 2.1.0 */
add_bool( "avcodec-hw", false, HW_TEXT, HW_LONGTEXT, false ) 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 #endif
#if defined(FF_THREAD_FRAME) #if defined(FF_THREAD_FRAME)
add_obsolete_integer( "ffmpeg-threads" ) /* removed since 2.1.0 */ add_obsolete_integer( "ffmpeg-threads" ) /* removed since 2.1.0 */
......
...@@ -138,6 +138,9 @@ int ffmpeg_OpenCodec( decoder_t *p_dec ); ...@@ -138,6 +138,9 @@ int ffmpeg_OpenCodec( decoder_t *p_dec );
#define HW_TEXT N_("Hardware decoding") #define HW_TEXT N_("Hardware decoding")
#define HW_LONGTEXT N_("This allows hardware decoding when available.") #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_TEXT N_( "Threads" )
#define THREADS_LONGTEXT N_( "Number of threads used for decoding, 0 meaning auto" ) #define THREADS_LONGTEXT N_( "Number of threads used for decoding, 0 meaning auto" )
...@@ -273,10 +276,11 @@ int ffmpeg_OpenCodec( decoder_t *p_dec ); ...@@ -273,10 +276,11 @@ int ffmpeg_OpenCodec( decoder_t *p_dec );
# define HAVE_AVCODEC_MT # define HAVE_AVCODEC_MT
#endif #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) */ * system) */
//#define HAVE_AVCODEC_VAAPI 1 //#define HAVE_AVCODEC_VAAPI 1
//#define HAVE_AVCODEC_DXVA2 1 //#define HAVE_AVCODEC_DXVA2 1
//#define HAVE_AVCODEC_VDA 1
/* Ugly ifdefinitions to provide backwards compatibility with older ffmpeg/libav /* Ugly ifdefinitions to provide backwards compatibility with older ffmpeg/libav
* versions */ * versions */
......
...@@ -60,6 +60,7 @@ static inline void vlc_va_Delete(vlc_va_t *va) ...@@ -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_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_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 #endif
/*****************************************************************************
* 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
...@@ -52,6 +52,9 @@ ...@@ -52,6 +52,9 @@
# ifdef HAVE_AVCODEC_DXVA2 # ifdef HAVE_AVCODEC_DXVA2
# include <libavcodec/dxva2.h> # include <libavcodec/dxva2.h>
# endif # endif
# ifdef HAVE_AVCODEC_VDA
# include <libavcodec/vda.h>
# endif
#elif defined(HAVE_FFMPEG_AVCODEC_H) #elif defined(HAVE_FFMPEG_AVCODEC_H)
# include <ffmpeg/avcodec.h> # include <ffmpeg/avcodec.h>
#else #else
...@@ -60,7 +63,7 @@ ...@@ -60,7 +63,7 @@
#include "avcodec.h" #include "avcodec.h"
#include "va.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 # define HAVE_AVCODEC_VA
#endif #endif
...@@ -1166,6 +1169,9 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, ...@@ -1166,6 +1169,9 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
[PIX_FMT_VAAPI_MOCO] = "PIX_FMT_VAAPI_MOCO", [PIX_FMT_VAAPI_MOCO] = "PIX_FMT_VAAPI_MOCO",
#ifdef HAVE_AVCODEC_DXVA2 #ifdef HAVE_AVCODEC_DXVA2
[PIX_FMT_DXVA2_VLD] = "PIX_FMT_DXVA2_VLD", [PIX_FMT_DXVA2_VLD] = "PIX_FMT_DXVA2_VLD",
#endif
#ifdef HAVE_AVCODEC_VDA
[PIX_FMT_VDA_VLD] = "PIX_FMT_VDA_VLD",
#endif #endif
[PIX_FMT_YUYV422] = "PIX_FMT_YUYV422", [PIX_FMT_YUYV422] = "PIX_FMT_YUYV422",
[PIX_FMT_YUV420P] = "PIX_FMT_YUV420P", [PIX_FMT_YUV420P] = "PIX_FMT_YUV420P",
...@@ -1199,6 +1205,16 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, ...@@ -1199,6 +1205,16 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
} }
#endif #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 && if( p_sys->p_va &&
p_context->width > 0 && p_context->height > 0 ) p_context->width > 0 && p_context->height > 0 )
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment