diff options
author | Joakim Plate <elupus@ecce.se> | 2013-06-08 11:52:10 +0200 |
---|---|---|
committer | Joakim Plate <elupus@ecce.se> | 2013-06-08 11:52:10 +0200 |
commit | d192ca3dc7122148d03292ff1ed37fc2fa2225b7 (patch) | |
tree | fffe1ef2b0be9030e5c7800676458fcdc1805eda | |
parent | 13a3215aed9b9d9d4a068a5da35ef376dcc0dc39 (diff) |
dvdplayer: support vda decoding using ffmpeg hwaccel infrastructure
This removes the requirment that width/height have to be known before
hand, as well as avoid need to extract extradata before hand.
-rw-r--r-- | XBMC.xcodeproj/project.pbxproj | 6 | ||||
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp | 17 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/VDA.cpp | 273 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/VDA.h | 54 |
5 files changed, 351 insertions, 1 deletions
diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj index d46800d3e5..4eda502c5b 100644 --- a/XBMC.xcodeproj/project.pbxproj +++ b/XBMC.xcodeproj/project.pbxproj @@ -234,6 +234,7 @@ 43BF09A21080D1E900E25290 /* AVTransportSCPD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43BF099E1080D1E900E25290 /* AVTransportSCPD.cpp */; settings = {COMPILER_FLAGS = "-I$SRCROOT/lib/libUPnP/Platinum/Source/Core -I$SRCROOT/lib/libUPnP/Platinum/Source/Platinum -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaConnect -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaRenderer -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaServer -I$SRCROOT/lib/libUPnP/Platinum/Source/Extras -I$SRCROOT/lib/libUPnP/Neptune/Source/System/Posix -I$SRCROOT/lib/libUPnP/Neptune/Source/Core"; }; }; 43BF09A31080D1E900E25290 /* RenderingControlSCPD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43BF099F1080D1E900E25290 /* RenderingControlSCPD.cpp */; settings = {COMPILER_FLAGS = "-I$SRCROOT/lib/libUPnP/Platinum/Source/Core -I$SRCROOT/lib/libUPnP/Platinum/Source/Platinum -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaConnect -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaRenderer -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaServer -I$SRCROOT/lib/libUPnP/Platinum/Source/Extras -I$SRCROOT/lib/libUPnP/Neptune/Source/System/Posix -I$SRCROOT/lib/libUPnP/Neptune/Source/Core"; }; }; 43BF09AB1080D2ED00E25290 /* RdrConnectionManagerSCPD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43BF09A81080D2ED00E25290 /* RdrConnectionManagerSCPD.cpp */; settings = {COMPILER_FLAGS = "-I$SRCROOT/lib/libUPnP/Platinum/Source/Core -I$SRCROOT/lib/libUPnP/Platinum/Source/Platinum -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaConnect -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaRenderer -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaServer -I$SRCROOT/lib/libUPnP/Platinum/Source/Extras -I$SRCROOT/lib/libUPnP/Neptune/Source/System/Posix -I$SRCROOT/lib/libUPnP/Neptune/Source/Core"; }; }; + 551C3A45175A12010051AAAD /* VDA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 551C3A43175A12010051AAAD /* VDA.cpp */; }; 552840CC1626163B00ED1333 /* UPnPPlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 552840CA1626163B00ED1333 /* UPnPPlayer.cpp */; settings = {COMPILER_FLAGS = "-I$SRCROOT/lib/libUPnP/Platinum/Source/Core -I$SRCROOT/lib/libUPnP/Platinum/Source/Platinum -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaConnect -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaRenderer -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaServer -I$SRCROOT/lib/libUPnP/Platinum/Source/Extras -I$SRCROOT/lib/libUPnP/Neptune/Source/System/Posix -I$SRCROOT/lib/libUPnP/Neptune/Source/Core"; }; }; 552A226915F7E14B0015C0D0 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 552A226815F7E14B0015C0D0 /* main.cpp */; }; 553840F215F360B400CE061B /* PltMimeType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 553840ED15F360B400CE061B /* PltMimeType.cpp */; settings = {COMPILER_FLAGS = "-I$SRCROOT/lib/libUPnP/Platinum/Source/Core -I$SRCROOT/lib/libUPnP/Platinum/Source/Platinum -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaConnect -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaRenderer -I$SRCROOT/lib/libUPnP/Platinum/Source/Devices/MediaServer -I$SRCROOT/lib/libUPnP/Platinum/Source/Extras -I$SRCROOT/lib/libUPnP/Neptune/Source/System/Posix -I$SRCROOT/lib/libUPnP/Neptune/Source/Core"; }; }; @@ -3603,6 +3604,8 @@ 43BF09A91080D2ED00E25290 /* RenderingControlSCPD_Full.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = RenderingControlSCPD_Full.xml; sourceTree = "<group>"; }; 43BF09DD1080D39300E25290 /* fastmemcpy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fastmemcpy.h; sourceTree = "<group>"; }; 43FAC87112D6349400F67914 /* IStorageProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IStorageProvider.h; sourceTree = "<group>"; }; + 551C3A43175A12010051AAAD /* VDA.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VDA.cpp; sourceTree = "<group>"; }; + 551C3A44175A12010051AAAD /* VDA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VDA.h; sourceTree = "<group>"; }; 552840CA1626163B00ED1333 /* UPnPPlayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UPnPPlayer.cpp; sourceTree = "<group>"; }; 552840CB1626163B00ED1333 /* UPnPPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UPnPPlayer.h; sourceTree = "<group>"; }; 552A226815F7E14B0015C0D0 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = main/main.cpp; sourceTree = "<group>"; }; @@ -7430,6 +7433,8 @@ E4991590174E6ABE00741B6D /* DVDVideoCodecVideoToolBox.h */, E38E15410D25F9F900618676 /* DVDVideoPPFFmpeg.cpp */, E38E15420D25F9F900618676 /* DVDVideoPPFFmpeg.h */, + 551C3A43175A12010051AAAD /* VDA.cpp */, + 551C3A44175A12010051AAAD /* VDA.h */, ); path = Video; sourceTree = "<group>"; @@ -10512,6 +10517,7 @@ DF529BAE1741697B00523FB4 /* Environment.cpp in Sources */, DFE4095B17417FDF00473BD9 /* LegacyPathTranslation.cpp in Sources */, 0E3036EC1760F68A00D93596 /* FavouritesDirectory.cpp in Sources */, + 551C3A45175A12010051AAAD /* VDA.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/configure.in b/configure.in index e5ae8d478b..72cfc71da9 100644 --- a/configure.in +++ b/configure.in @@ -2553,7 +2553,7 @@ XB_CONFIG_MODULE([lib/ffmpeg], [ ffmpg_config="$ffmpg_config --disable-devices --disable-doc" ffmpg_config="$ffmpg_config --disable-ffplay --disable-ffmpeg" ffmpg_config="$ffmpg_config --disable-ffprobe --disable-ffserver" - ffmpg_config="$ffmpg_config --disable-vda --disable-crystalhd" + ffmpg_config="$ffmpg_config --enable-vda --disable-crystalhd" ffmpg_config="$ffmpg_config --disable-decoder=mpeg_xvmc" # handle conditional enables/disables diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp index 310ee55ba6..e76feb3e0c 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp @@ -57,6 +57,9 @@ #ifdef HAVE_LIBVA #include "VAAPI.h" #endif +#ifdef TARGET_DARWIN_OSX +#include "VDA.h" +#endif using namespace boost; @@ -118,6 +121,20 @@ enum PixelFormat CDVDVideoCodecFFmpeg::GetFormat( struct AVCodecContext * avctx dec->Release(); } #endif + +#ifdef TARGET_DARWIN_OSX + if (*cur == AV_PIX_FMT_VDA_VLD && CSettings::Get().GetBool("videoplayer.usevda")) + { + VDA::CDecoder* dec = new VDA::CDecoder(); + if(dec->Open(avctx, *cur, ctx->m_uSurfacesCount)) + { + ctx->SetHardware(dec); + return *cur; + } + else + dec->Release(); + } +#endif cur++; } return ctx->m_dllAvCodec.avcodec_default_get_format(avctx, fmt); diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VDA.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDA.cpp new file mode 100644 index 0000000000..d6864cb2ff --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDA.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://www.xbmc.org + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include "system.h" +#ifdef TARGET_DARWIN_OSX +#include "osx/CocoaInterface.h" +#include "DVDVideoCodec.h" +#include "DVDCodecs/DVDCodecUtils.h" +#include "VDA.h" + +extern "C" { + #include <libavcodec/vda.h> +} + +using namespace std; +using namespace VDA; + + +static void RelBufferS(AVCodecContext *avctx, AVFrame *pic) +{ ((CDecoder*)((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetHardware())->RelBuffer(avctx, pic); } + +static int GetBufferS(AVCodecContext *avctx, AVFrame *pic) +{ return ((CDecoder*)((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetHardware())->GetBuffer(avctx, pic); } + +CDecoder::CDecoder() +: m_renderbuffers_count(0) +{ + m_ctx = (vda_context*)calloc(1, sizeof(vda_context)); +} + +CDecoder::~CDecoder() +{ + Close(); + free(m_ctx); +} + +void CDecoder::RelBuffer(AVCodecContext *avctx, AVFrame *pic) +{ + CVPixelBufferRef cv_buffer = (CVPixelBufferRef)pic->data[3]; + CVPixelBufferRelease(cv_buffer); + + for (int i = 0; i < 4; i++) + pic->data[i] = NULL; +} + +int CDecoder::GetBuffer(AVCodecContext *avctx, AVFrame *pic) +{ + pic->type = FF_BUFFER_TYPE_USER; + pic->data[0] = (uint8_t *)1; + return 0; +} + +static void vda_decoder_callback (void *vda_hw_ctx, + CFDictionaryRef user_info, + OSStatus status, + uint32_t infoFlags, + CVImageBufferRef image_buffer) +{ + struct vda_context *vda_ctx = (struct vda_context *)vda_hw_ctx; + + if (!image_buffer) + return; + + if (vda_ctx->cv_pix_fmt_type != CVPixelBufferGetPixelFormatType(image_buffer)) + return; + + vda_ctx->cv_buffer = CVPixelBufferRetain(image_buffer); +} + +bool CDecoder::Create(AVCodecContext *avctx) +{ + OSStatus status; + CFNumberRef height; + CFNumberRef width; + CFNumberRef format; + CFDataRef avc_data; + CFMutableDictionaryRef config_info; + CFMutableDictionaryRef buffer_attributes; + CFMutableDictionaryRef io_surface_properties; + CFNumberRef cv_pix_fmt; + + m_ctx->priv_bitstream = NULL; + m_ctx->priv_allocated_size = 0; + + /* Each VCL NAL in the bitstream sent to the decoder + * is preceded by a 4 bytes length header. + * Change the avcC atom header if needed, to signal headers of 4 bytes. */ + if (avctx->extradata_size >= 4 && (avctx->extradata[4] & 0x03) != 0x03) { + uint8_t *rw_extradata; + + if (!(rw_extradata = (uint8_t*)av_malloc(avctx->extradata_size))) + return false; + + memcpy(rw_extradata, avctx->extradata, avctx->extradata_size); + + rw_extradata[4] |= 0x03; + + avc_data = CFDataCreate(kCFAllocatorDefault, rw_extradata, avctx->extradata_size); + + av_freep(&rw_extradata); + } else { + avc_data = CFDataCreate(kCFAllocatorDefault, avctx->extradata, avctx->extradata_size); + } + + config_info = CFDictionaryCreateMutable(kCFAllocatorDefault, + 4, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + height = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &m_ctx->height); + width = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &m_ctx->width); + format = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &m_ctx->format); + + CFDictionarySetValue(config_info, kVDADecoderConfiguration_Height , height); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_Width , width); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_SourceFormat, format); + CFDictionarySetValue(config_info, kVDADecoderConfiguration_avcCData , avc_data); + + buffer_attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + io_surface_properties = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + cv_pix_fmt = CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, + &m_ctx->cv_pix_fmt_type); + CFDictionarySetValue(buffer_attributes, + kCVPixelBufferPixelFormatTypeKey, + cv_pix_fmt); + CFDictionarySetValue(buffer_attributes, + kCVPixelBufferIOSurfacePropertiesKey, + io_surface_properties); + + status = VDADecoderCreate(config_info, + buffer_attributes, + (VDADecoderOutputCallback*)vda_decoder_callback, + m_ctx, + &m_ctx->decoder); + + CFRelease(height); + CFRelease(width); + CFRelease(format); + CFRelease(avc_data); + CFRelease(config_info); + CFRelease(io_surface_properties); + CFRelease(cv_pix_fmt); + CFRelease(buffer_attributes); + + if(status != kVDADecoderNoErr) + { + CLog::Log(LOGERROR, "VDA::CDecoder - Failed to init VDA decoder: %d", status); + return false; + } + return true; +} + +void CDecoder::Close() +{ + OSStatus status = kVDADecoderNoErr; + + if (m_ctx->decoder) + status = VDADecoderDestroy(m_ctx->decoder); + m_ctx->decoder = NULL; + av_freep(&m_ctx->priv_bitstream); +} + +bool CDecoder::Open(AVCodecContext *avctx, enum PixelFormat fmt, unsigned int surfaces) +{ + Close(); + + if(fmt != AV_PIX_FMT_VDA_VLD) + return false; + + if(avctx->codec_id != AV_CODEC_ID_H264) + return false; + + switch(avctx->profile) + { + case FF_PROFILE_H264_HIGH_10: + case FF_PROFILE_H264_HIGH_10_INTRA: + case FF_PROFILE_H264_HIGH_422: + case FF_PROFILE_H264_HIGH_422_INTRA: + case FF_PROFILE_H264_HIGH_444_PREDICTIVE: + case FF_PROFILE_H264_HIGH_444_INTRA: + case FF_PROFILE_H264_CAVLC_444: + return false; + default: + break; + } + + if (Cocoa_GPUForDisplayIsNvidiaPureVideo3() && !CDVDCodecUtils::IsVP3CompatibleWidth(avctx->width)) + { + CLog::Log(LOGNOTICE, "%s - Nvidia 9400 GPU hardware limitation, cannot decode a width of %d", __FUNCTION__, avctx->width); + return false; + } + + if (avctx->profile == FF_PROFILE_H264_MAIN && avctx->level == 32 && avctx->refs > 4) + { + // Main@L3.2, VDA cannot handle greater than 4 reference frames + CLog::Log(LOGNOTICE, "%s - Main@L3.2 detected, VDA cannot decode.", __FUNCTION__); + return false; + } + + /* init vda */ + memset(m_ctx, 0, sizeof(struct vda_context)); + m_ctx->width = avctx->width; + m_ctx->height = avctx->height; + m_ctx->format = 'avc1'; + m_ctx->use_sync_decoding = 1; + m_ctx->cv_pix_fmt_type = kCVPixelFormatType_422YpCbCr8; + + if (!Create(avctx)) + return false; + + avctx->pix_fmt = fmt; + avctx->hwaccel_context = m_ctx; + avctx->get_buffer = GetBufferS; + avctx->release_buffer = RelBufferS; + + return true; +} + +int CDecoder::Decode(AVCodecContext* avctx, AVFrame* frame) +{ + int status = Check(avctx); + if(status) + return status; + + if(frame) + return VC_BUFFER | VC_PICTURE; + else + return VC_BUFFER; +} + +bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) +{ + ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(picture); + + picture->format = RENDER_FMT_CVBREF; + picture->cvBufferRef = (CVPixelBufferRef)frame->data[3]; + return true; +} + +int CDecoder::Check(AVCodecContext* avctx) +{ + return 0; +} + +unsigned CDecoder::GetAllowedReferences() +{ + return m_renderbuffers_count; +} + +#endif diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VDA.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDA.h new file mode 100644 index 0000000000..154457f855 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDA.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://www.xbmc.org + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#pragma once + +#include "system_gl.h" + +#include "DllAvCodec.h" +#include "DVDVideoCodecFFmpeg.h" + +struct vda_context; + +namespace VDA { + +class CDecoder + : public CDVDVideoCodecFFmpeg::IHardwareDecoder +{ +public: + CDecoder(); + ~CDecoder(); + virtual bool Open (AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces = 0); + virtual int Decode (AVCodecContext* avctx, AVFrame* frame); + virtual bool GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture); + virtual int Check (AVCodecContext* avctx); + virtual void Close(); + virtual const std::string Name() { return "vda"; } + virtual CCriticalSection* Section() { return NULL; } + virtual unsigned GetAllowedReferences(); + + int GetBuffer(AVCodecContext *avctx, AVFrame *pic); + void RelBuffer(AVCodecContext *avctx, AVFrame *pic); +protected: + bool Create(AVCodecContext* avctx); + unsigned m_renderbuffers_count; + vda_context* m_ctx; +}; + +} |