diff options
author | Hendrik Leppkes <h.leppkes@gmail.com> | 2012-02-14 23:38:35 +0400 |
---|---|---|
committer | Hendrik Leppkes <h.leppkes@gmail.com> | 2012-02-16 19:59:34 +0400 |
commit | cd28fb752cf4e35ca9cd49bba0e3305766da14d9 (patch) | |
tree | 6fd62bd50f08d99f8e26175494ae05f16fe3e65c | |
parent | 565be07c7eb82bf5429a04bc7a616c331e89602a (diff) |
Implement native DXVA2 decoding
-rw-r--r-- | decoder/LAVVideo/LAVPixFmtConverter.cpp | 2 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVVideo.cpp | 118 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVVideo.h | 4 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVVideo.vcxproj | 4 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVVideo.vcxproj.filters | 18 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVVideoSettings.h | 4 | ||||
-rw-r--r-- | decoder/LAVVideo/VideoOutputPin.cpp | 45 | ||||
-rw-r--r-- | decoder/LAVVideo/VideoOutputPin.h | 33 | ||||
-rw-r--r-- | decoder/LAVVideo/VideoSettingsProp.cpp | 6 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/DecBase.h | 2 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/ILAVDecoder.h | 17 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.cpp | 239 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.h | 90 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/dxva2dec.cpp | 387 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/dxva2dec.h | 20 |
15 files changed, 874 insertions, 115 deletions
diff --git a/decoder/LAVVideo/LAVPixFmtConverter.cpp b/decoder/LAVVideo/LAVPixFmtConverter.cpp index abd9e625..fe257dff 100644 --- a/decoder/LAVVideo/LAVPixFmtConverter.cpp +++ b/decoder/LAVVideo/LAVPixFmtConverter.cpp @@ -85,6 +85,8 @@ static LAV_INOUT_PIXFMT_MAP lav_pixfmt_map[] = { { LAVPixFmt_RGB24, 8, 6, { LAVOutPixFmt_RGB24, LAVOutPixFmt_RGB32, LAVOutPixFmt_YUY2, LAVOutPixFmt_UYVY, LAVOutPixFmt_NV12, LAVOutPixFmt_YV12 } }, { LAVPixFmt_RGB32, 8, 6, { LAVOutPixFmt_RGB32, LAVOutPixFmt_RGB24, LAVOutPixFmt_YUY2, LAVOutPixFmt_UYVY, LAVOutPixFmt_NV12, LAVOutPixFmt_YV12 } }, { LAVPixFmt_ARGB32, 8, 6, { LAVOutPixFmt_RGB32, LAVOutPixFmt_RGB24, LAVOutPixFmt_YUY2, LAVOutPixFmt_UYVY, LAVOutPixFmt_NV12, LAVOutPixFmt_YV12 } }, + + { LAVPixFmt_DXVA2, 7, 1, { LAVOutPixFmt_NV12 } }, }; LAVOutPixFmtDesc lav_pixfmt_desc[] = { diff --git a/decoder/LAVVideo/LAVVideo.cpp b/decoder/LAVVideo/LAVVideo.cpp index a6a24f3b..a4e67620 100644 --- a/decoder/LAVVideo/LAVVideo.cpp +++ b/decoder/LAVVideo/LAVVideo.cpp @@ -23,6 +23,8 @@ #include "Media.h" #include <dvdmedia.h> +#include "VideoOutputPin.h" + #include "moreuuids.h" #include "registry.h" @@ -77,6 +79,23 @@ CLAVVideo::CLAVVideo(LPUNKNOWN pUnk, HRESULT* phr) GetModuleFileName(NULL, fileName, 1024); m_processName = PathFindFileName (fileName); + m_pInput = new CTransformInputPin(TEXT("CTransformInputPin"), this, phr, L"Input"); + if(!m_pInput) { + *phr = E_OUTOFMEMORY; + } + if (FAILED(*phr)) { + return; + } + + m_pOutput = new CVideoOutputPin(TEXT("CVideoOutputPin"), this, phr, L"Output"); + if(!m_pOutput) { + *phr = E_OUTOFMEMORY; + } + if(FAILED(*phr)) { + SAFE_DELETE(m_pInput); + return; + } + memset(&m_LAVPinInfo, 0, sizeof(m_LAVPinInfo)); avcodec_register_all(); @@ -513,8 +532,10 @@ HRESULT CLAVVideo::CreateDecoder(const CMediaType *pmt) m_pDecoder = CreateDecoderCUVID(); else if (m_settings.HWAccel == HWAccel_QuickSync) m_pDecoder = CreateDecoderQuickSync(); - else if (m_settings.HWAccel == HWAccel_DXVA2) + else if (m_settings.HWAccel == HWAccel_DXVA2CopyBack) m_pDecoder = CreateDecoderDXVA2(); + else if (m_settings.HWAccel == HWAccel_DXVA2Native) + m_pDecoder = CreateDecoderDXVA2Native(); m_bHWDecoder = TRUE; } @@ -619,6 +640,23 @@ HRESULT CLAVVideo::BreakConnect(PIN_DIRECTION dir) return __super::BreakConnect(dir); } +HRESULT CLAVVideo::CompleteConnect(PIN_DIRECTION dir, IPin *pReceivePin) +{ + DbgLog((LOG_TRACE, 10, L"::CompleteConnect")); + HRESULT hr = S_OK; + if (dir == PINDIR_OUTPUT) { + if (m_pDecoder) { + hr = m_pDecoder->PostConnect(pReceivePin); + if (FAILED(hr)) { + m_bHWDecoderFailed = TRUE; + CMediaType &mt = m_pInput->CurrentMediaType(); + hr = CreateDecoder(&mt); + } + } + } + return S_OK; +} + HRESULT CLAVVideo::GetDeliveryBuffer(IMediaSample** ppOut, int width, int height, AVRational ar, DXVA2_ExtendedFormat dxvaExtFlags, REFERENCE_TIME avgFrameDuration) { CheckPointer(ppOut, E_POINTER); @@ -1005,7 +1043,10 @@ STDMETHODIMP CLAVVideo::Deliver(LAVFrame *pFrame) return S_OK; } - return Filter(pFrame); + if (pFrame->format == LAVPixFmt_DXVA2) + return DeliverToRenderer(pFrame); + else + return Filter(pFrame); } STDMETHODIMP CLAVVideo::DeliverToRenderer(LAVFrame *pFrame) @@ -1046,9 +1087,14 @@ STDMETHODIMP CLAVVideo::DeliverToRenderer(LAVFrame *pFrame) if (avgDuration == 0) avgDuration = AV_NOPTS_VALUE; - if(FAILED(hr = GetDeliveryBuffer(&pSampleOut, width, height, pFrame->aspect_ratio, pFrame->ext_format, avgDuration)) || FAILED(hr = pSampleOut->GetPointer(&pDataOut))) { - ReleaseFrame(&pFrame); - return hr; + if (pFrame->format == LAVPixFmt_DXVA2) { + pSampleOut = (IMediaSample *)pFrame->data[0]; + pSampleOut->AddRef(); + } else { + if(FAILED(hr = GetDeliveryBuffer(&pSampleOut, width, height, pFrame->aspect_ratio, pFrame->ext_format, avgDuration)) || FAILED(hr = pSampleOut->GetPointer(&pDataOut))) { + ReleaseFrame(&pFrame); + return hr; + } } pSampleOut->SetTime(&pFrame->rtStart, &pFrame->rtStop); @@ -1058,34 +1104,35 @@ STDMETHODIMP CLAVVideo::DeliverToRenderer(LAVFrame *pFrame) BITMAPINFOHEADER *pBIH = NULL; videoFormatTypeHandler(mt.Format(), mt.FormatType(), &pBIH); - long required = pBIH->biSizeImage; - - long lSampleSize = pSampleOut->GetSize(); - if (lSampleSize < required) { - DbgLog((LOG_ERROR, 10, L"::Decode(): Buffer is too small! Actual: %d, Required: %d", lSampleSize, required)); - SafeRelease(&pSampleOut); - ReleaseFrame(&pFrame); - return E_FAIL; - } - - -#if defined(DEBUG) && DEBUG_PIXELCONV_TIMINGS - LARGE_INTEGER frequency, start, end; - QueryPerformanceFrequency(&frequency); - QueryPerformanceCounter(&start); -#endif - m_PixFmtConverter.Convert(pFrame, pDataOut, width, height, pBIH->biWidth); -#if defined(DEBUG) && DEBUG_PIXELCONV_TIMINGS - QueryPerformanceCounter(&end); - double diff = (end.QuadPart - start.QuadPart) * 1000.0 / frequency.QuadPart; - m_pixFmtTimingAvg.Sample(diff); + if (pFrame->format != LAVPixFmt_DXVA2) { + long required = pBIH->biSizeImage; - DbgLog((LOG_TRACE, 10, L"Pixel Mapping took %2.3fms in avg", m_pixFmtTimingAvg.Average())); -#endif + long lSampleSize = pSampleOut->GetSize(); + if (lSampleSize < required) { + DbgLog((LOG_ERROR, 10, L"::Decode(): Buffer is too small! Actual: %d, Required: %d", lSampleSize, required)); + SafeRelease(&pSampleOut); + ReleaseFrame(&pFrame); + return E_FAIL; + } - if ((mt.subtype == MEDIASUBTYPE_RGB32 || mt.subtype == MEDIASUBTYPE_RGB24) && pBIH->biHeight > 0) { - int bpp = (mt.subtype == MEDIASUBTYPE_RGB32) ? 4 : 3; - flip_plane(pDataOut, pBIH->biWidth * bpp, height); + #if defined(DEBUG) && DEBUG_PIXELCONV_TIMINGS + LARGE_INTEGER frequency, start, end; + QueryPerformanceFrequency(&frequency); + QueryPerformanceCounter(&start); + #endif + m_PixFmtConverter.Convert(pFrame, pDataOut, width, height, pBIH->biWidth); + #if defined(DEBUG) && DEBUG_PIXELCONV_TIMINGS + QueryPerformanceCounter(&end); + double diff = (end.QuadPart - start.QuadPart) * 1000.0 / frequency.QuadPart; + m_pixFmtTimingAvg.Sample(diff); + + DbgLog((LOG_TRACE, 10, L"Pixel Mapping took %2.3fms in avg", m_pixFmtTimingAvg.Average())); + #endif + + if ((mt.subtype == MEDIASUBTYPE_RGB32 || mt.subtype == MEDIASUBTYPE_RGB24) && pBIH->biHeight > 0) { + int bpp = (mt.subtype == MEDIASUBTYPE_RGB32) ? 4 : 3; + flip_plane(pDataOut, pBIH->biWidth * bpp, height); + } } if (m_bSendMediaType) { @@ -1273,13 +1320,20 @@ STDMETHODIMP_(DWORD) CLAVVideo::CheckHWAccelSupport(LAVHWAccel hwAccel) SAFE_DELETE(pDecoder); } break; - case HWAccel_DXVA2: + case HWAccel_DXVA2CopyBack: { ILAVDecoder *pDecoder = CreateDecoderDXVA2(); hr = pDecoder->InitInterfaces(this, this); SAFE_DELETE(pDecoder); } break; + case HWAccel_DXVA2Native: + { + ILAVDecoder *pDecoder = CreateDecoderDXVA2Native(); + hr = pDecoder->InitInterfaces(this, this); + SAFE_DELETE(pDecoder); + } + break; } return SUCCEEDED(hr) ? 1 : 0; diff --git a/decoder/LAVVideo/LAVVideo.h b/decoder/LAVVideo/LAVVideo.h index c16110ef..71ec2b62 100644 --- a/decoder/LAVVideo/LAVVideo.h +++ b/decoder/LAVVideo/LAVVideo.h @@ -112,6 +112,7 @@ public: HRESULT Receive(IMediaSample *pIn); HRESULT BreakConnect(PIN_DIRECTION dir); + HRESULT CompleteConnect(PIN_DIRECTION dir, IPin *pReceivePin); // ILAVVideoCallback STDMETHODIMP AllocateFrame(LAVFrame **ppFrame); @@ -125,6 +126,7 @@ public: STDMETHODIMP_(BOOL) IsVistaOrNewer(); STDMETHODIMP_(CMediaType&) GetInputMediaType() { return m_pInput->CurrentMediaType(); } STDMETHODIMP GetLAVPinInfo(LAVPinInfo &info) { if (m_LAVPinInfoValid) { info = m_LAVPinInfo; return S_OK; } return E_FAIL; } + STDMETHODIMP_(CBasePin*) GetOutputPin() { return m_pOutput; } public: // Pin Configuration @@ -152,6 +154,8 @@ private: STDMETHODIMP DeliverToRenderer(LAVFrame *pFrame); private: + friend class CVideoOutputPin; + ILAVDecoder *m_pDecoder; REFERENCE_TIME m_rtPrevStart; diff --git a/decoder/LAVVideo/LAVVideo.vcxproj b/decoder/LAVVideo/LAVVideo.vcxproj index e40aeb25..8e7bd56a 100644 --- a/decoder/LAVVideo/LAVVideo.vcxproj +++ b/decoder/LAVVideo/LAVVideo.vcxproj @@ -147,6 +147,7 @@ <ClCompile Include="decoders\avcodec.cpp" /> <ClCompile Include="decoders\cuvid.cpp" /> <ClCompile Include="decoders\dxva2dec.cpp" /> + <ClCompile Include="decoders\dxva2\DXVA2SurfaceAllocator.cpp" /> <ClCompile Include="decoders\pixfmt.cpp" /> <ClCompile Include="decoders\quicksync.cpp" /> <ClCompile Include="dllmain.cpp" /> @@ -169,6 +170,7 @@ <ClCompile Include="stdafx.cpp"> <PrecompiledHeader>Create</PrecompiledHeader> </ClCompile> + <ClCompile Include="VideoOutputPin.cpp" /> <ClCompile Include="VideoSettingsProp.cpp" /> </ItemGroup> <ItemGroup> @@ -176,6 +178,7 @@ <ClInclude Include="decoders\cuvid.h" /> <ClInclude Include="decoders\DecBase.h" /> <ClInclude Include="decoders\dxva2dec.h" /> + <ClInclude Include="decoders\dxva2\DXVA2SurfaceAllocator.h" /> <ClInclude Include="decoders\ILAVDecoder.h" /> <ClInclude Include="decoders\quicksync.h" /> <ClInclude Include="H264RandomAccess.h" /> @@ -191,6 +194,7 @@ <ClInclude Include="pixconv\pixconv_sse2_templates.h" /> <ClInclude Include="resource.h" /> <ClInclude Include="stdafx.h" /> + <ClInclude Include="VideoOutputPin.h" /> <ClInclude Include="VideoSettingsProp.h" /> </ItemGroup> <ItemGroup> diff --git a/decoder/LAVVideo/LAVVideo.vcxproj.filters b/decoder/LAVVideo/LAVVideo.vcxproj.filters index 02737b4d..ef11eb0d 100644 --- a/decoder/LAVVideo/LAVVideo.vcxproj.filters +++ b/decoder/LAVVideo/LAVVideo.vcxproj.filters @@ -31,6 +31,12 @@ <Filter Include="Header Files\decoders"> <UniqueIdentifier>{c5cd3602-30d6-48ff-8100-230758b36fb4}</UniqueIdentifier> </Filter> + <Filter Include="Source Files\decoders\dxva2"> + <UniqueIdentifier>{2417dd22-d693-40ad-a3f7-a6e3b4e22b9d}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\decoders\dxva2"> + <UniqueIdentifier>{d3bbf10b-cfb1-45d9-8cb4-6323349e6b22}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="stdafx.cpp"> @@ -105,6 +111,12 @@ <ClCompile Include="decoders\dxva2dec.cpp"> <Filter>Source Files\decoders</Filter> </ClCompile> + <ClCompile Include="VideoOutputPin.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="decoders\dxva2\DXVA2SurfaceAllocator.cpp"> + <Filter>Source Files\decoders\dxva2</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="stdafx.h"> @@ -167,6 +179,12 @@ <ClInclude Include="decoders\dxva2dec.h"> <Filter>Header Files\decoders</Filter> </ClInclude> + <ClInclude Include="VideoOutputPin.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="decoders\dxva2\DXVA2SurfaceAllocator.h"> + <Filter>Header Files\decoders\dxva2</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="LAVVideo.rc"> diff --git a/decoder/LAVVideo/LAVVideoSettings.h b/decoder/LAVVideo/LAVVideoSettings.h index 3715cb4d..a73ff613 100644 --- a/decoder/LAVVideo/LAVVideoSettings.h +++ b/decoder/LAVVideo/LAVVideoSettings.h @@ -91,7 +91,9 @@ typedef enum LAVHWAccel { HWAccel_None, HWAccel_CUDA, HWAccel_QuickSync, - HWAccel_DXVA2 + HWAccel_DXVA2, + HWAccel_DXVA2CopyBack = HWAccel_DXVA2, + HWAccel_DXVA2Native }; // Deinterlace algorithms offered by the hardware decoders diff --git a/decoder/LAVVideo/VideoOutputPin.cpp b/decoder/LAVVideo/VideoOutputPin.cpp new file mode 100644 index 00000000..42f59225 --- /dev/null +++ b/decoder/LAVVideo/VideoOutputPin.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010-2012 Hendrik Leppkes + * http://www.1f0.de + * + * 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. + */ + +#include "stdafx.h" +#include "LAVVideo.h" +#include "VideoOutputPin.h" + +CVideoOutputPin::CVideoOutputPin(LPCTSTR pObjectName, CLAVVideo *pFilter, HRESULT * phr, LPCWSTR pName) + : CTransformOutputPin(pObjectName, (CTransformFilter *)pFilter, phr, pName) + , m_pFilter(pFilter) +{ +} + +CVideoOutputPin::~CVideoOutputPin() +{ +} + +HRESULT CVideoOutputPin::InitAllocator(IMemAllocator **ppAlloc) +{ + HRESULT hr = S_FALSE; + if (m_pFilter->m_pDecoder) { + hr = m_pFilter->m_pDecoder->InitAllocator(ppAlloc); + } + + if (hr != S_OK) + hr = __super::InitAllocator(ppAlloc); + + return hr; +} diff --git a/decoder/LAVVideo/VideoOutputPin.h b/decoder/LAVVideo/VideoOutputPin.h new file mode 100644 index 00000000..52867773 --- /dev/null +++ b/decoder/LAVVideo/VideoOutputPin.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010-2012 Hendrik Leppkes + * http://www.1f0.de + * + * 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. + */ + +#pragma once + +class CVideoOutputPin : public CTransformOutputPin +{ +public: + CVideoOutputPin(LPCTSTR pObjectName, CLAVVideo *pFilter, HRESULT * phr, LPCWSTR pName); + virtual ~CVideoOutputPin(); + + HRESULT InitAllocator(IMemAllocator **ppAlloc); + +private: + CLAVVideo *m_pFilter; +}; + diff --git a/decoder/LAVVideo/VideoSettingsProp.cpp b/decoder/LAVVideo/VideoSettingsProp.cpp index df8fb040..bf05bf08 100644 --- a/decoder/LAVVideo/VideoSettingsProp.cpp +++ b/decoder/LAVVideo/VideoSettingsProp.cpp @@ -179,11 +179,13 @@ HRESULT CLAVVideoSettingsProp::OnActivate() WCHAR hwAccelNone[] = L"None"; WCHAR hwAccelCUDA[] = L"NVIDIA CUVID"; WCHAR hwAccelQuickSync[] = L"Intel\xae QuickSync"; - WCHAR hwAccelDXVA2[] = L"DXVA2 (copy-back)"; + WCHAR hwAccelDXVA2CB[] = L"DXVA2 (copy-back)"; + WCHAR hwAccelDXVA2N[] = L"DXVA2 (native)"; SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelNone); SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelCUDA); SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelQuickSync); - SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelDXVA2); + SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelDXVA2CB); + SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelDXVA2N); // Init the fieldorder Combo Box SendDlgItemMessage(m_Dlg, IDC_DEINT_FIELDORDER, CB_RESETCONTENT, 0, 0); diff --git a/decoder/LAVVideo/decoders/DecBase.h b/decoder/LAVVideo/decoders/DecBase.h index 0d4c989b..6b062322 100644 --- a/decoder/LAVVideo/decoders/DecBase.h +++ b/decoder/LAVVideo/decoders/DecBase.h @@ -34,6 +34,8 @@ public: STDMETHODIMP InitInterfaces(ILAVVideoSettings *pSettings, ILAVVideoCallback *pCallback) { m_pSettings = pSettings; m_pCallback = pCallback; return Init(); } STDMETHOD_(REFERENCE_TIME, GetFrameDuration)() { return 0; } STDMETHOD_(BOOL, IsInterlaced)() { return TRUE; } + STDMETHODIMP InitAllocator(IMemAllocator **ppAlloc) { return E_NOTIMPL; } + STDMETHODIMP PostConnect(IPin *pPin) { return S_FALSE; } STDMETHODIMP Decode(IMediaSample *pSample) { HRESULT hr; diff --git a/decoder/LAVVideo/decoders/ILAVDecoder.h b/decoder/LAVVideo/decoders/ILAVDecoder.h index 49b29c01..2f5dde21 100644 --- a/decoder/LAVVideo/decoders/ILAVDecoder.h +++ b/decoder/LAVVideo/decoders/ILAVDecoder.h @@ -219,6 +219,11 @@ interface ILAVVideoCallback * Query the LAVPinInfo */ STDMETHOD(GetLAVPinInfo)(LAVPinInfo &info) PURE; + + /** + * Get a reference to the output pin + */ + STDMETHOD_(CBasePin*, GetOutputPin)() PURE; }; /** @@ -299,6 +304,17 @@ interface ILAVDecoder * This function should return false if the format can 100% not be interlaced, and true if it can be interlaced (but also progressive). */ STDMETHOD_(BOOL, IsInterlaced)() PURE; + + /** + * Allows the decoder to handle an allocator. + * Used by DXVA2 decoding + */ + STDMETHOD(InitAllocator)(IMemAllocator **ppAlloc) PURE; + + /** + * Function called after connection is established, with the pin as argument + */ + STDMETHOD(PostConnect)(IPin *pPin) PURE; }; /** @@ -310,3 +326,4 @@ ILAVDecoder *CreateDecoderAVCodec(); ILAVDecoder *CreateDecoderCUVID(); ILAVDecoder *CreateDecoderQuickSync(); ILAVDecoder *CreateDecoderDXVA2(); +ILAVDecoder *CreateDecoderDXVA2Native(); diff --git a/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.cpp b/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.cpp new file mode 100644 index 00000000..aae155f2 --- /dev/null +++ b/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2010-2012 Hendrik Leppkes + * http://www.1f0.de + * + * 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. + * + * Initial Design and Concept taken from MPC-HC, licensed under GPLv2 + */ + +#include "stdafx.h" +#include "decoders/dxva2dec.h" +#include "DXVA2SurfaceAllocator.h" + +#include "moreuuids.h" +#include <evr.h> +#include <Mferror.h> + +CDXVA2Sample::CDXVA2Sample(CDXVA2SurfaceAllocator *pAlloc, HRESULT *phr) + : CMediaSample(NAME("CDXVA2Sample"), (CBaseAllocator*)pAlloc, phr, NULL, 0) + , m_dwSurfaceId(0), m_pSurface(NULL) +{ +} + +CDXVA2Sample::~CDXVA2Sample() +{ + SafeRelease(&m_pSurface); +} + +//Note: CMediaSample does not derive from CUnknown, so we cannot use the +// DECLARE_IUNKNOWN macro that is used by most of the filter classes. + +STDMETHODIMP CDXVA2Sample::QueryInterface(REFIID riid, __deref_out void **ppv) +{ + CheckPointer(ppv,E_POINTER); + ValidateReadWritePtr(ppv,sizeof(PVOID)); + + if (riid == __uuidof(IMFGetService)) { + return GetInterface((IMFGetService*) this, ppv); + } + if (riid == __uuidof(ILAVDXVA2Sample)) { + return GetInterface((ILAVDXVA2Sample*) this, ppv); + } else { + return CMediaSample::QueryInterface(riid, ppv); + } +} + +STDMETHODIMP_(ULONG) CDXVA2Sample::AddRef() +{ + return __super::AddRef(); +} + +STDMETHODIMP_(ULONG) CDXVA2Sample::Release() +{ + // Return a temporary variable for thread safety. + ULONG cRef = __super::Release(); + return cRef; +} + +// IMFGetService::GetService +STDMETHODIMP CDXVA2Sample::GetService(REFGUID guidService, REFIID riid, LPVOID *ppv) +{ + if (guidService != MR_BUFFER_SERVICE) { + return MF_E_UNSUPPORTED_SERVICE; + } else if (m_pSurface == NULL) { + return E_NOINTERFACE; + } else { + return m_pSurface->QueryInterface(riid, ppv); + } +} + +// Override GetPointer because this class does not manage a system memory buffer. +// The EVR uses the MR_BUFFER_SERVICE service to get the Direct3D surface. +STDMETHODIMP CDXVA2Sample::GetPointer(BYTE ** ppBuffer) +{ + return E_NOTIMPL; +} + +// Sets the pointer to the Direct3D surface. +void CDXVA2Sample::SetSurface(DWORD surfaceId, IDirect3DSurface9 *pSurf) +{ + SafeRelease(&m_pSurface); + m_pSurface = pSurf; + m_dwSurfaceId = surfaceId; + if (m_pSurface) + m_pSurface->AddRef(); +} + +STDMETHODIMP_(int) CDXVA2Sample::GetDXSurfaceId() +{ + return m_dwSurfaceId; +} + +CDXVA2SurfaceAllocator::CDXVA2SurfaceAllocator(CDecDXVA2 *m_pDXVA2Dec, HRESULT* phr) + : CBaseAllocator(NAME("CDXVA2SurfaceAllocator"), NULL, phr) + , m_pDec(m_pDXVA2Dec) + , m_ppRTSurfaceArray(NULL) + , m_nSurfaceArrayCount(0) +{ +} + +CDXVA2SurfaceAllocator::~CDXVA2SurfaceAllocator(void) +{ + if (m_pDec) + m_pDec->m_pDXVA2Allocator = NULL; +} + +// IUnknown +STDMETHODIMP CDXVA2SurfaceAllocator::NonDelegatingQueryInterface(REFIID riid, void** ppv) +{ + CheckPointer(ppv, E_POINTER); + + *ppv = NULL; + + return + QI(ILAVDXVA2SurfaceAllocator) + __super::NonDelegatingQueryInterface(riid, ppv); +} + +HRESULT CDXVA2SurfaceAllocator::Alloc() +{ + DbgLog((LOG_TRACE, 10, L"CDXVA2SurfaceAllocator::Alloc()")); + HRESULT hr = S_OK; + IDirectXVideoDecoderService *pDXVA2Service = NULL; + + if (!m_pDec) + return E_FAIL; + + CheckPointer(m_pDec->m_pD3DDevMngr, E_UNEXPECTED); + hr = m_pDec->m_pD3DDevMngr->GetVideoService (m_pDec->m_hDevice, IID_IDirectXVideoDecoderService, (void**)&pDXVA2Service); + CheckPointer (pDXVA2Service, E_UNEXPECTED); + CAutoLock lock(this); + + hr = __super::Alloc(); + + if (SUCCEEDED(hr)) { + DbgLog((LOG_TRACE, 10, L"-> Releasing old resources")); + // Free the old resources. + Free(); + + m_nSurfaceArrayCount = m_lCount; + + // Allocate a new array of pointers. + m_ppRTSurfaceArray = new IDirect3DSurface9*[m_lCount]; + if (m_ppRTSurfaceArray == NULL) { + hr = E_OUTOFMEMORY; + } else { + ZeroMemory(m_ppRTSurfaceArray, sizeof(IDirect3DSurface9*) * m_lCount); + } + } + + // Allocate the surfaces. + D3DFORMAT d3dFormat = (D3DFORMAT)FOURCC_NV12; + if (SUCCEEDED(hr)) { + DbgLog((LOG_TRACE, 10, L"-> Allocating surfaces")); + hr = pDXVA2Service->CreateSurface( + m_pDec->m_dwSurfaceWidth, + m_pDec->m_dwSurfaceHeight, + m_lCount - 1, + d3dFormat, + D3DPOOL_DEFAULT, + 0, + DXVA2_VideoDecoderRenderTarget, + m_ppRTSurfaceArray, + NULL + ); + } + + if (SUCCEEDED(hr)) { + DbgLog((LOG_TRACE, 10, L"-> Creating samples")); + // Important : create samples in reverse order ! + for (m_lAllocated = m_lCount-1; m_lAllocated >= 0; m_lAllocated--) { + CDXVA2Sample *pSample = new CDXVA2Sample(this, &hr); + if (pSample == NULL) { + hr = E_OUTOFMEMORY; + break; + } + if (FAILED(hr)) { + break; + } + // Assign the Direct3D surface pointer and the index. + pSample->SetSurface(m_lAllocated, m_ppRTSurfaceArray[m_lAllocated]); + + // Add to the sample list. + m_lFree.Add(pSample); + } + + hr = m_pDec->CreateDXVA2Decoder(m_lCount, m_ppRTSurfaceArray); + if (FAILED (hr)) { + Free(); + } + } + + if (SUCCEEDED(hr)) { + m_bChanged = FALSE; + } + SafeRelease(&pDXVA2Service); + return hr; +} + +void CDXVA2SurfaceAllocator::Free() +{ + DbgLog((LOG_TRACE, 10, L"CDXVA2SurfaceAllocator::Free()")); + CMediaSample *pSample = NULL; + + if (m_pDec) + m_pDec->Flush(); + + do { + pSample = m_lFree.RemoveHead(); + if (pSample) { + delete pSample; + } + } while (pSample); + + if (m_ppRTSurfaceArray) { + for (UINT i = 0; i < m_nSurfaceArrayCount; i++) { + if (m_ppRTSurfaceArray[i] != NULL) { + m_ppRTSurfaceArray[i]->Release(); + } + } + + delete [] m_ppRTSurfaceArray; + m_ppRTSurfaceArray = NULL; + } + m_lAllocated = 0; + m_nSurfaceArrayCount = 0; +} diff --git a/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.h b/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.h new file mode 100644 index 00000000..022c6caf --- /dev/null +++ b/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.h @@ -0,0 +1,90 @@ +/* +* Copyright (C) 2010-2012 Hendrik Leppkes +* http://www.1f0.de +* +* 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. +* +* Initial Design and Concept taken from MPC-HC, licensed under GPLv2 +*/ + +#pragma once + +#include <Mfidl.h> + +class CDecDXVA2; + +interface __declspec(uuid("50A8A9A1-FF44-45C1-9DC2-79066ED1E576")) +ILAVDXVA2Sample : +public IUnknown { + STDMETHOD_(int, GetDXSurfaceId()) = 0; +}; + +class CDXVA2Sample : public CMediaSample, public IMFGetService, public ILAVDXVA2Sample +{ + friend class CDXVA2SurfaceAllocator; + +public: + CDXVA2Sample(CDXVA2SurfaceAllocator *pAlloc, HRESULT *phr); + virtual ~CDXVA2Sample(); + + STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IMFGetService::GetService + STDMETHODIMP GetService(REFGUID guidService, REFIID riid, LPVOID *ppv); + + // ILAVDXVA2Sample + STDMETHODIMP_(int) GetDXSurfaceId(); + + // Override GetPointer because this class does not manage a system memory buffer. + // The EVR uses the MR_BUFFER_SERVICE service to get the Direct3D surface. + STDMETHODIMP GetPointer(BYTE ** ppBuffer); + +private: + + // Sets the pointer to the Direct3D surface. + void SetSurface(DWORD surfaceId, IDirect3DSurface9 *pSurf); + + IDirect3DSurface9 *m_pSurface; + DWORD m_dwSurfaceId; +}; + +interface __declspec(uuid("23F80BD8-2654-4F74-B7CC-621868D0A850")) +ILAVDXVA2SurfaceAllocator : +public IUnknown { + STDMETHOD_(void,DecoderDestruct)() = 0; +}; + +class CDXVA2SurfaceAllocator : public CBaseAllocator, public ILAVDXVA2SurfaceAllocator +{ +public: + CDXVA2SurfaceAllocator(CDecDXVA2 *m_pDXVA2Dec, HRESULT* phr); + virtual ~CDXVA2SurfaceAllocator(void); + + DECLARE_IUNKNOWN; + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv); + + HRESULT Alloc(); + void Free(); + + STDMETHODIMP_(void) DecoderDestruct() { m_pDec = NULL; } + +private: + CDecDXVA2 *m_pDec; + + IDirect3DSurface9 **m_ppRTSurfaceArray; + UINT m_nSurfaceArrayCount; +}; diff --git a/decoder/LAVVideo/decoders/dxva2dec.cpp b/decoder/LAVVideo/decoders/dxva2dec.cpp index c2adecfc..bc6b476a 100644 --- a/decoder/LAVVideo/decoders/dxva2dec.cpp +++ b/decoder/LAVVideo/decoders/dxva2dec.cpp @@ -21,12 +21,14 @@ #include "stdafx.h" #include "dxva2dec.h" +#include "dxva2/DXVA2SurfaceAllocator.h" #include "moreuuids.h" #include "Media.h" #include <Shlwapi.h> #include <dxva2api.h> +#include <evr.h> #include "libavcodec/dxva2.h" #include "gpu_memcpy_sse4.h" @@ -39,6 +41,12 @@ ILAVDecoder *CreateDecoderDXVA2() { return new CDecDXVA2(); } +ILAVDecoder *CreateDecoderDXVA2Native() { + CDecDXVA2 *dec = new CDecDXVA2(); + dec->SetNativeMode(TRUE); + return dec; +} + //////////////////////////////////////////////////////////////////////////////// // Codec Maps //////////////////////////////////////////////////////////////////////////////// @@ -144,6 +152,7 @@ static void CopyFrameNV12_SSE4(const BYTE *pSourceData, BYTE *pY, BYTE *pUV, int CDecDXVA2::CDecDXVA2(void) : CDecAvcodec() + , m_bNative(FALSE) , m_pD3D(NULL) , m_pD3DDev(NULL) , m_pD3DDevMngr(NULL) @@ -157,6 +166,8 @@ CDecDXVA2::CDecDXVA2(void) , m_dwSurfaceWidth(0) , m_dwSurfaceHeight(0) , m_dwVendorId(0) + , m_pDXVA2Allocator(NULL) + , m_hDevice(INVALID_HANDLE_VALUE) { ZeroMemory(&dx, sizeof(dx)); ZeroMemory(&m_DXVAExtendedFormat, sizeof(m_DXVAExtendedFormat)); @@ -167,6 +178,8 @@ CDecDXVA2::CDecDXVA2(void) CDecDXVA2::~CDecDXVA2(void) { DestroyDecoder(true); + if (m_pDXVA2Allocator) + m_pDXVA2Allocator->DecoderDestruct(); } STDMETHODIMP CDecDXVA2::DestroyDecoder(bool bFull, bool bNoAVCodec) @@ -192,6 +205,9 @@ STDMETHODIMP CDecDXVA2::DestroyDecoder(bool bFull, bool bNoAVCodec) if (bFull) { SafeRelease(&m_pDXVADecoderService); + if (m_hDevice != INVALID_HANDLE_VALUE) + m_pD3DDevMngr->CloseDeviceHandle(m_hDevice); + m_hDevice = INVALID_HANDLE_VALUE; SafeRelease(&m_pD3DDevMngr); SafeRelease(&m_pD3DDev); SafeRelease(&m_pD3D); @@ -203,6 +219,103 @@ STDMETHODIMP CDecDXVA2::DestroyDecoder(bool bFull, bool bNoAVCodec) return S_OK; } +STDMETHODIMP CDecDXVA2::InitAllocator(IMemAllocator **ppAlloc) +{ + HRESULT hr = S_OK; + if (!m_bNative) + return E_NOTIMPL; + + m_pDXVA2Allocator = new CDXVA2SurfaceAllocator(this, &hr); + if (!m_pDXVA2Allocator) { + return E_OUTOFMEMORY; + } + if (FAILED(hr)) { + SAFE_DELETE(m_pDXVA2Allocator); + return hr; + } + + return m_pDXVA2Allocator->QueryInterface(__uuidof(IMemAllocator), (void **)ppAlloc); +} + +STDMETHODIMP CDecDXVA2::PostConnect(IPin *pPin) +{ + HRESULT hr = S_OK; + if (m_pD3DDevMngr) + return S_FALSE; + + DbgLog((LOG_TRACE, 10, L"CDecDXVA2::PostConnect()")); + + IMFGetService *pGetService = NULL; + IDirect3DDeviceManager9 *pDeviceManager = NULL; + + hr = pPin->QueryInterface(__uuidof(IMFGetService), (void**)&pGetService); + if (FAILED(hr)) { + DbgLog((LOG_ERROR, 10, L"-> IMFGetService not available")); + goto done; + } + + // Get the Direct3D device manager. + hr = pGetService->GetService(MR_VIDEO_ACCELERATION_SERVICE, __uuidof(IDirect3DDeviceManager9), (void**)&pDeviceManager); + if (FAILED(hr)) { + DbgLog((LOG_ERROR, 10, L"-> D3D Device Manager not available")); + goto done; + } + + hr = SetD3DDeviceManager(pDeviceManager); + if (FAILED(hr)) { + DbgLog((LOG_ERROR, 10, L"-> Setting D3D Device Manager faield")); + goto done; + } + + hr = DXVA2NotifyEVR(); + +done: + SafeRelease(&pGetService); + if (FAILED(hr)) { + SafeRelease(&pDeviceManager); + } + return hr; +} + +HRESULT CDecDXVA2::DXVA2NotifyEVR() +{ + HRESULT hr = S_OK; + IMFGetService *pGetService = NULL; + IDirectXVideoMemoryConfiguration *pVideoConfig = NULL; + + hr = m_pCallback->GetOutputPin()->GetConnected()->QueryInterface(&pGetService); + if (FAILED(hr)) { + DbgLog((LOG_ERROR, 10, L"-> IMFGetService not available")); + goto done; + } + + // Configure EVR for receiving DXVA2 samples + hr = pGetService->GetService(MR_VIDEO_ACCELERATION_SERVICE, __uuidof(IDirectXVideoMemoryConfiguration), (void**)&pVideoConfig); + if (FAILED(hr)) { + DbgLog((LOG_ERROR, 10, L"-> IDirectXVideoMemoryConfiguration not available")); + goto done; + } + + // Notify the EVR about the format we're sending + DXVA2_SurfaceType surfaceType; + for (DWORD iTypeIndex = 0; ; iTypeIndex++) { + hr = pVideoConfig->GetAvailableSurfaceTypeByIndex(iTypeIndex, &surfaceType); + if (FAILED(hr)) { + hr = S_OK; + break; + } + + if (surfaceType == DXVA2_SurfaceType_DecoderRenderTarget) { + hr = pVideoConfig->SetSurfaceType(DXVA2_SurfaceType_DecoderRenderTarget); + break; + } + } +done: + SafeRelease(&pGetService); + SafeRelease(&pVideoConfig); + return hr; +} + STDMETHODIMP CDecDXVA2::LoadDXVA2Functions() { // Load DXVA2 library @@ -256,23 +369,19 @@ done: return hr; } -HRESULT CDecDXVA2::CreateDXVAVideoService(IDirect3DDevice9 *pDevice, IDirect3DDeviceManager9 *pManager, IDirectXVideoDecoderService **ppService) +HRESULT CDecDXVA2::CreateDXVAVideoService(IDirect3DDeviceManager9 *pManager, IDirectXVideoDecoderService **ppService) { HRESULT hr = S_OK; IDirectXVideoDecoderService *pService; - HANDLE hDevice; - hr = pManager->OpenDeviceHandle(&hDevice); + hr = pManager->OpenDeviceHandle(&m_hDevice); if (FAILED(hr)) { DbgLog((LOG_ERROR, 10, L"-> OpenDeviceHandle failed")); goto done; } - HRESULT hr2 = pManager->GetVideoService(hDevice, IID_IDirectXVideoDecoderService, (void**)&pService); - - // Close the device handle. - hr = pManager->CloseDeviceHandle(hDevice); + HRESULT hr2 = pManager->GetVideoService(m_hDevice, IID_IDirectXVideoDecoderService, (void**)&pService); if (FAILED(hr2)) { DbgLog((LOG_ERROR, 10, L"-> Acquiring VideoDecoderService failed")); @@ -366,10 +475,13 @@ done: return E_FAIL; } -// ILAVDecoder -STDMETHODIMP CDecDXVA2::Init() +/** + * This function is only called in non-native mode + * Its responsibility is to initialize D3D, create a device and a device manager + * and call SetD3DDeviceManager with it. + */ +HRESULT CDecDXVA2::InitD3D() { - DbgLog((LOG_TRACE, 10, L"CDecDXVA2::Init(): Trying to open DXVA2 decoder")); HRESULT hr = S_OK; if (FAILED(hr = LoadDXVA2Functions())) { @@ -425,30 +537,78 @@ STDMETHODIMP CDecDXVA2::Init() return E_FAIL; } - hr = CreateDXVAVideoService(m_pD3DDev, m_pD3DDevMngr, &m_pDXVADecoderService); + hr = SetD3DDeviceManager(m_pD3DDevMngr); + if (FAILED(hr)) { + DbgLog((LOG_TRACE, 10, L"-> SetD3DDeviceManager failed with hr: %X", hr)); + return E_FAIL; + } + + return S_OK; +} + +/** + * Called from both native and non-native mode + * Initialize all the common DXVA2 interfaces and device handles + */ +HRESULT CDecDXVA2::SetD3DDeviceManager(IDirect3DDeviceManager9 *pDevManager) +{ + HRESULT hr = S_OK; + ASSERT(pDevManager); + + m_pD3DDevMngr = pDevManager; + hr = CreateDXVAVideoService(m_pD3DDevMngr, &m_pDXVADecoderService); if (FAILED(hr)) { DbgLog((LOG_TRACE, 10, L"-> Creation of DXVA2 Decoder Service failed with hr: %X", hr)); return E_FAIL; } - // Init the ffmpeg parts - // This is our main software decoder, unable to fail! - CDecAvcodec::Init(); + // If the decoder was initialized already, check if we can use this device + if (m_pAVCtx) { + GUID input = GUID_NULL; + D3DFORMAT output; + hr = FindVideoServiceConversion(m_pAVCtx->codec_id, &input, &output); + if (FAILED(hr)) { + DbgLog((LOG_TRACE, 10, L"-> No decoder device available that can decode codec '%S' to NV12", avcodec_get_name(m_pAVCtx->codec_id))); + return E_FAIL; + } + } - if (CopyFrameNV12 == NULL) { - int cpu_flags = av_get_cpu_flags(); - if (cpu_flags & AV_CPU_FLAG_SSE4) { - DbgLog((LOG_TRACE, 10, L"-> Using SSE4 frame copy")); - CopyFrameNV12 = CopyFrameNV12_SSE4; - } else if (FALSE && cpu_flags & AV_CPU_FLAG_SSE2) { - DbgLog((LOG_TRACE, 10, L"-> Using SSE2 frame copy")); - //CopyFrameNV12 = CopyFrameNV12_SSE2; - } else { - DbgLog((LOG_TRACE, 10, L"-> Using fallback frame copy")); - CopyFrameNV12 = CopyFrameNV12_fallback; + return S_OK; +} + +// ILAVDecoder +STDMETHODIMP CDecDXVA2::Init() +{ + DbgLog((LOG_TRACE, 10, L"CDecDXVA2::Init(): Trying to open DXVA2 decoder")); + HRESULT hr = S_OK; + + // Initialize all D3D interfaces in non-native mode + if (!m_bNative) { + hr = InitD3D(); + if (FAILED(hr)) { + DbgLog((LOG_TRACE, 10, L"-> D3D Initialization failed with hr: %X", hr)); + return hr; + } + + if (CopyFrameNV12 == NULL) { + int cpu_flags = av_get_cpu_flags(); + if (cpu_flags & AV_CPU_FLAG_SSE4) { + DbgLog((LOG_TRACE, 10, L"-> Using SSE4 frame copy")); + CopyFrameNV12 = CopyFrameNV12_SSE4; + } else if (FALSE && cpu_flags & AV_CPU_FLAG_SSE2) { + DbgLog((LOG_TRACE, 10, L"-> Using SSE2 frame copy")); + //CopyFrameNV12 = CopyFrameNV12_SSE2; + } else { + DbgLog((LOG_TRACE, 10, L"-> Using fallback frame copy")); + CopyFrameNV12 = CopyFrameNV12_fallback; + } } } + // Init the ffmpeg parts + // This is our main software decoder, unable to fail! + CDecAvcodec::Init(); + return S_OK; } @@ -459,12 +619,16 @@ STDMETHODIMP CDecDXVA2::InitDecoder(CodecID codec, const CMediaType *pmt) DestroyDecoder(false); - GUID input = GUID_NULL; - D3DFORMAT output; - hr = FindVideoServiceConversion(codec, &input, &output); - if (FAILED(hr)) { - DbgLog((LOG_TRACE, 10, L"-> No decoder device available that can decode codec '%S' to NV12", avcodec_get_name(codec))); - return E_FAIL; + // If we have a DXVA Decoder, check if its capable + // If we don't have one yet, it may be handed to us later, and compat is checked at that point + if (m_pDXVADecoderService) { + GUID input = GUID_NULL; + D3DFORMAT output; + hr = FindVideoServiceConversion(codec, &input, &output); + if (FAILED(hr)) { + DbgLog((LOG_TRACE, 10, L"-> No decoder device available that can decode codec '%S' to NV12", avcodec_get_name(codec))); + return E_FAIL; + } } m_bFailHWDecode = FALSE; @@ -480,12 +644,20 @@ STDMETHODIMP CDecDXVA2::InitDecoder(CodecID codec, const CMediaType *pmt) return E_FAIL; } + m_dwSurfaceWidth = FFALIGN(m_pAVCtx->coded_width, 16); + m_dwSurfaceHeight = FFALIGN(m_pAVCtx->coded_height, 16); + return S_OK; } -HRESULT CDecDXVA2::CreateDXVA2Decoder() +HRESULT CDecDXVA2::CreateDXVA2Decoder(int nSurfaces, IDirect3DSurface9 **ppSurfaces) { + DbgLog((LOG_TRACE, 10, L"-> CDecDXVA2::CreateDXVA2Decoder")); HRESULT hr = S_OK; + LPDIRECT3DSURFACE9 pSurfaces[DXVA2_MAX_SURFACES]; + + if (!m_pDXVADecoderService) + return E_FAIL; DestroyDecoder(false, true); @@ -493,22 +665,27 @@ HRESULT CDecDXVA2::CreateDXVA2Decoder() D3DFORMAT output; FindVideoServiceConversion(m_pAVCtx->codec_id, &input, &output); - m_dwSurfaceWidth = FFALIGN(m_pAVCtx->coded_width, 16); - m_dwSurfaceHeight = FFALIGN(m_pAVCtx->coded_height, 16); - - m_NumSurfaces = (m_pAVCtx->codec_id == CODEC_ID_H264) ? 16 + DXVA2_QUEUE_SURFACES + 2 : 2 + DXVA2_QUEUE_SURFACES + 2; - LPDIRECT3DSURFACE9 pSurfaces[DXVA2_MAX_SURFACES]; - hr = m_pDXVADecoderService->CreateSurface(m_dwSurfaceWidth, m_dwSurfaceHeight, m_NumSurfaces - 1, output, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, pSurfaces, NULL); - if (FAILED(hr)) { - DbgLog((LOG_TRACE, 10, L"-> Creation of surfaces failed with hr: %X", hr)); - m_NumSurfaces = 0; - return E_FAIL; + if (!nSurfaces) { + m_NumSurfaces = (m_pAVCtx->codec_id == CODEC_ID_H264) ? 16 + DXVA2_QUEUE_SURFACES + 2 : 2 + DXVA2_QUEUE_SURFACES + 2; + hr = m_pDXVADecoderService->CreateSurface(m_dwSurfaceWidth, m_dwSurfaceHeight, m_NumSurfaces - 1, output, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, pSurfaces, NULL); + if (FAILED(hr)) { + DbgLog((LOG_TRACE, 10, L"-> Creation of surfaces failed with hr: %X", hr)); + m_NumSurfaces = 0; + return E_FAIL; + } + ppSurfaces = pSurfaces; + } else { + m_NumSurfaces = nSurfaces; + for (int i = 0; i < m_NumSurfaces; i++) { + ppSurfaces[i]->AddRef(); + } } for (int i = 0; i < m_NumSurfaces; i++) { - m_pSurfaces[i].d3d = pSurfaces[i]; + m_pSurfaces[i].d3d = ppSurfaces[i]; m_pSurfaces[i].ref = 0; m_pSurfaces[i].age = 0; + m_pSurfaces[i].sample = NULL; } DbgLog((LOG_TRACE, 10, L"-> Successfully created %d surfaces (%dx%d)", m_NumSurfaces, m_dwSurfaceWidth, m_dwSurfaceHeight)); @@ -556,7 +733,7 @@ HRESULT CDecDXVA2::CreateDXVA2Decoder() m_DXVAVideoDecoderConfig = best_cfg; IDirectXVideoDecoder *decoder; - hr = m_pDXVADecoderService->CreateVideoDecoder(input, &desc, &m_DXVAVideoDecoderConfig, pSurfaces, m_NumSurfaces, &decoder); + hr = m_pDXVADecoderService->CreateVideoDecoder(input, &desc, &m_DXVAVideoDecoderConfig, ppSurfaces, m_NumSurfaces, &decoder); if (FAILED(hr)) { DbgLog((LOG_TRACE, 10, L"-> CreateVideoDecoder failed with hr: %X", hr)); return E_FAIL; @@ -592,36 +769,66 @@ int CDecDXVA2::get_dxva2_buffer(struct AVCodecContext *c, AVFrame *pic) HRESULT hr = S_OK; + if (c->pix_fmt != PIX_FMT_DXVA2_VLD || c->profile > FF_PROFILE_H264_HIGH) { + DbgLog((LOG_ERROR, 10, L"DXVA2 buffer request, but not dxva2 pixfmt or unsupported profile")); + pDec->m_bFailHWDecode = TRUE; + return -1; + } + if (!pDec->m_pDecoder || FFALIGN(c->coded_width, 16) != pDec->m_dwSurfaceWidth || FFALIGN(c->coded_height, 16) != pDec->m_dwSurfaceHeight) { - hr = pDec->CreateDXVA2Decoder(); + if (!pDec->m_pDecoder && pDec->m_bNative) { + ASSERT(0); + } else if (!pDec->m_bNative) { + hr = pDec->CreateDXVA2Decoder(); + } if (FAILED(hr)) { pDec->m_bFailHWDecode = TRUE; return -1; } } - if (c->pix_fmt != PIX_FMT_DXVA2_VLD || c->profile > FF_PROFILE_H264_HIGH) { - DbgLog((LOG_ERROR, 10, L"DXVA2 buffer request, but not dxva2 pixfmt or unsupported profile")); - pDec->m_bFailHWDecode = TRUE; - return -1; - } - pic->type = FF_BUFFER_TYPE_USER; //pDec->m_pD3DDevMngr->TestDevice( - int i, old, old_unused; - for (i = 0, old = 0, old_unused = -1; i < pDec->m_NumSurfaces; i++) { - d3d_surface_t *surface = &pDec->m_pSurfaces[i]; - if (!surface->ref && (old_unused == -1 || surface->age < pDec->m_pSurfaces[old_unused].age)) - old_unused = i; - if (surface->age < pDec->m_pSurfaces[old].age) - old = i; - } - if (old_unused == -1) { - DbgLog((LOG_TRACE, 10, L"No free surface, using oldest")); - i = old; + + int i; + if (pDec->m_bNative) { + if (!pDec->m_pDXVA2Allocator) + return -1; + + IMediaSample *pSample = NULL; + hr = pDec->m_pDXVA2Allocator->GetBuffer(&pSample, NULL, NULL, 0); + if (FAILED(hr)) { + DbgLog((LOG_ERROR, 10, L"DXVA2Allocator returned error")); + return -1; + } + ILAVDXVA2Sample *pLavDXVA2 = NULL; + hr = pSample->QueryInterface(&pLavDXVA2); + if (FAILED(hr)) { + DbgLog((LOG_ERROR, 10, L"Sample is no LAV DXVA2 sample?????")); + return -1; + } + i = pLavDXVA2->GetDXSurfaceId(); + SafeRelease(&pLavDXVA2); + + pDec->m_pSurfaces[i].sample = pSample; + + DbgLog((LOG_TRACE, 10, L"get_dxva2_buffer on dxva2 buffer %i", i)); } else { - i = old_unused; + int old, old_unused; + for (i = 0, old = 0, old_unused = -1; i < pDec->m_NumSurfaces; i++) { + d3d_surface_t *surface = &pDec->m_pSurfaces[i]; + if (!surface->ref && (old_unused == -1 || surface->age < pDec->m_pSurfaces[old_unused].age)) + old_unused = i; + if (surface->age < pDec->m_pSurfaces[old].age) + old = i; + } + if (old_unused == -1) { + DbgLog((LOG_TRACE, 10, L"No free surface, using oldest")); + i = old; + } else { + i = old_unused; + } } LPDIRECT3DSURFACE9 pSurface = pDec->m_pSurfaces[i].d3d; @@ -643,8 +850,13 @@ void CDecDXVA2::release_dxva2_buffer(struct AVCodecContext *c, AVFrame *pic) LPDIRECT3DSURFACE9 pSurface = (LPDIRECT3DSURFACE9)pic->data[3]; for (int i = 0; i < pDec->m_NumSurfaces; i++) { - if (pDec->m_pSurfaces[i].d3d == pSurface) + if (pDec->m_pSurfaces[i].d3d == pSurface) { pDec->m_pSurfaces[i].ref--; + if (pDec->m_pSurfaces[i].ref == 0) { + DbgLog((LOG_TRACE, 10, L"release_dxva2_buffer on dxva2 buffer %d", i)); + SafeRelease(&pDec->m_pSurfaces[i].sample); + } + } } memset(pic->data, 0, sizeof(pic->data)); @@ -693,6 +905,7 @@ STDMETHODIMP CDecDXVA2::Flush() for (int i = 0; i < m_NumSurfaces; i++) { d3d_surface_t *s = &m_pSurfaces[i]; + SafeRelease(&s->sample); s->ref = 0; //s->age = 0; } @@ -726,18 +939,22 @@ STDMETHODIMP CDecDXVA2::EndOfStream() HRESULT CDecDXVA2::HandleDXVA2Frame(LAVFrame *pFrame) { - LAVFrame *pQueuedFrame = m_FrameQueue[m_FrameQueuePosition]; - m_FrameQueue[m_FrameQueuePosition] = pFrame; + if (m_bNative) { + DeliverDXVA2Frame(pFrame); + } else { + LAVFrame *pQueuedFrame = m_FrameQueue[m_FrameQueuePosition]; + m_FrameQueue[m_FrameQueuePosition] = pFrame; - d3d_surface_t *s = FindSurface((LPDIRECT3DSURFACE9)pFrame->data[3]); - s->ref++; + d3d_surface_t *s = FindSurface((LPDIRECT3DSURFACE9)pFrame->data[3]); + s->ref++; - m_FrameQueuePosition = (m_FrameQueuePosition + 1) % DXVA2_QUEUE_SURFACES; + m_FrameQueuePosition = (m_FrameQueuePosition + 1) % DXVA2_QUEUE_SURFACES; - if (pQueuedFrame) { - s = FindSurface((LPDIRECT3DSURFACE9)pQueuedFrame->data[3]); - s->ref--; - DeliverDXVA2Frame(pQueuedFrame); + if (pQueuedFrame) { + s = FindSurface((LPDIRECT3DSURFACE9)pQueuedFrame->data[3]); + s->ref--; + DeliverDXVA2Frame(pQueuedFrame); + } } return S_OK; @@ -745,10 +962,20 @@ HRESULT CDecDXVA2::HandleDXVA2Frame(LAVFrame *pFrame) HRESULT CDecDXVA2::DeliverDXVA2Frame(LAVFrame *pFrame) { - if (CopyFrame(pFrame)) + if (m_bNative) { + d3d_surface_t *s = FindSurface((LPDIRECT3DSURFACE9)pFrame->data[3]); + + ASSERT(s->sample); + pFrame->data[0] = (uint8_t *)s->sample; + + pFrame->format = LAVPixFmt_DXVA2; Deliver(pFrame); - else - ReleaseFrame(&pFrame); + } else { + if (CopyFrame(pFrame)) + Deliver(pFrame); + else + ReleaseFrame(&pFrame); + } return S_OK; } @@ -756,8 +983,12 @@ HRESULT CDecDXVA2::DeliverDXVA2Frame(LAVFrame *pFrame) STDMETHODIMP CDecDXVA2::GetPixelFormat(LAVPixelFormat *pPix, int *pBpp) { // Output is always NV12 - if (pPix) - *pPix = LAVPixFmt_NV12; + if (pPix) { + if (m_bNative) + *pPix = LAVPixFmt_DXVA2; + else + *pPix = LAVPixFmt_NV12; + } if (pBpp) *pBpp = 8; return S_OK; diff --git a/decoder/LAVVideo/decoders/dxva2dec.h b/decoder/LAVVideo/decoders/dxva2dec.h index 63f622a9..7bc56b46 100644 --- a/decoder/LAVVideo/decoders/dxva2dec.h +++ b/decoder/LAVVideo/decoders/dxva2dec.h @@ -30,8 +30,11 @@ typedef struct { LPDIRECT3DSURFACE9 d3d; uint64_t age; long ref; + IMediaSample *sample; } d3d_surface_t; +class CDXVA2SurfaceAllocator; + class CDecDXVA2 : public CDecAvcodec { public: @@ -44,9 +47,14 @@ public: STDMETHODIMP Flush(); STDMETHODIMP EndOfStream(); + STDMETHODIMP InitAllocator(IMemAllocator **ppAlloc); + STDMETHODIMP PostConnect(IPin *pPin); + // CDecBase STDMETHODIMP Init(); + HRESULT SetNativeMode(BOOL bNative) { m_bNative = bNative; return S_OK; } + protected: HRESULT AdditionaDecoderInit(); HRESULT PostDecode(); @@ -56,20 +64,27 @@ protected: bool CopyFrame(LAVFrame *pFrame); private: + HRESULT InitD3D(); STDMETHODIMP DestroyDecoder(bool bFull, bool bNoAVCodec = false); STDMETHODIMP LoadDXVA2Functions(); HRESULT CreateD3DDeviceManager(IDirect3DDevice9 *pDevice, UINT *pReset, IDirect3DDeviceManager9 **ppManager); - HRESULT CreateDXVAVideoService(IDirect3DDevice9 *pDevice, IDirect3DDeviceManager9 *pManager, IDirectXVideoDecoderService **ppService); + HRESULT CreateDXVAVideoService(IDirect3DDeviceManager9 *pManager, IDirectXVideoDecoderService **ppService); HRESULT FindVideoServiceConversion(CodecID codec, GUID *input, D3DFORMAT *output); - HRESULT CreateDXVA2Decoder(); + HRESULT CreateDXVA2Decoder(int nSurfaces = 0, IDirect3DSurface9 **ppSurfaces = NULL); + HRESULT SetD3DDeviceManager(IDirect3DDeviceManager9 *pDevManager); + HRESULT DXVA2NotifyEVR(); static int get_dxva2_buffer(struct AVCodecContext *c, AVFrame *pic); static void release_dxva2_buffer(struct AVCodecContext *c, AVFrame *pic); d3d_surface_t *FindSurface(LPDIRECT3DSURFACE9 pSurface); private: + friend class CDXVA2SurfaceAllocator; + BOOL m_bNative; + CDXVA2SurfaceAllocator *m_pDXVA2Allocator; + struct { HMODULE dxva2lib; pCreateDeviceManager9 *createDeviceManager; @@ -79,6 +94,7 @@ private: IDirect3DDevice9 *m_pD3DDev; IDirect3DDeviceManager9 *m_pD3DDevMngr; UINT m_pD3DResetToken; + HANDLE m_hDevice; IDirectXVideoDecoderService *m_pDXVADecoderService; IDirectXVideoDecoder *m_pDecoder; |