From be1155eab07f237abdab5b693ee6a13e5cc4e688 Mon Sep 17 00:00:00 2001 From: Hendrik Leppkes Date: Sat, 22 Apr 2017 11:35:39 +0200 Subject: Allow the PacketAllocator to allocate new packets on the fly This allows downstream filters to hang on to them for longer without deadlocking the allocator. --- common/includes/ILAVDynamicAllocator.h | 30 ++++++ demuxer/LAVSplitter/LAVSplitter.vcxproj | 1 + demuxer/LAVSplitter/LAVSplitter.vcxproj.filters | 3 + demuxer/LAVSplitter/OutputPin.cpp | 2 +- demuxer/LAVSplitter/PacketAllocator.cpp | 116 +++++++++++++++++------- demuxer/LAVSplitter/PacketAllocator.h | 13 ++- demuxer/LAVSplitter/dllmain.cpp | 1 + 7 files changed, 129 insertions(+), 37 deletions(-) create mode 100644 common/includes/ILAVDynamicAllocator.h diff --git a/common/includes/ILAVDynamicAllocator.h b/common/includes/ILAVDynamicAllocator.h new file mode 100644 index 00000000..2002156c --- /dev/null +++ b/common/includes/ILAVDynamicAllocator.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010-2017 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 + +// {8FBB906B-D1DB-4528-9498-563241CCD43D} +DEFINE_GUID(IID_ILAVDynamicAllocator, +0x8fbb906b, 0xd1db, 0x4528, 0x94, 0x98, 0x56, 0x32, 0x41, 0xcc, 0xd4, 0x3d); + +interface __declspec(uuid("8FBB906B-D1DB-4528-9498-563241CCD43D")) ILAVDynamicAllocator : public IUnknown +{ + // Query wether this allocator is using dynamic allocation of samples and will not run out of samples + STDMETHOD_(BOOL,IsDynamicAllocator)() PURE; +}; diff --git a/demuxer/LAVSplitter/LAVSplitter.vcxproj b/demuxer/LAVSplitter/LAVSplitter.vcxproj index 30768bd9..b64034b8 100644 --- a/demuxer/LAVSplitter/LAVSplitter.vcxproj +++ b/demuxer/LAVSplitter/LAVSplitter.vcxproj @@ -114,6 +114,7 @@ + diff --git a/demuxer/LAVSplitter/LAVSplitter.vcxproj.filters b/demuxer/LAVSplitter/LAVSplitter.vcxproj.filters index 3689aaa4..8121654b 100644 --- a/demuxer/LAVSplitter/LAVSplitter.vcxproj.filters +++ b/demuxer/LAVSplitter/LAVSplitter.vcxproj.filters @@ -119,6 +119,9 @@ Header Files\common + + Header Files\common + diff --git a/demuxer/LAVSplitter/OutputPin.cpp b/demuxer/LAVSplitter/OutputPin.cpp index e6b36a5c..66f0aa25 100644 --- a/demuxer/LAVSplitter/OutputPin.cpp +++ b/demuxer/LAVSplitter/OutputPin.cpp @@ -164,7 +164,7 @@ HRESULT CLAVOutputPin::DecideBufferSize(IMemAllocator* pAlloc, ALLOCATOR_PROPERT HRESULT hr = S_OK; - pProperties->cBuffers = max(pProperties->cBuffers, (m_bPacketAllocator ? 10 : m_nBuffers)); + pProperties->cBuffers = max(pProperties->cBuffers, (m_bPacketAllocator ? 20 : m_nBuffers)); pProperties->cbBuffer = max(max(m_mt.lSampleSize, 256000), (ULONG)pProperties->cbBuffer); // Vorbis requires at least 2 buffers diff --git a/demuxer/LAVSplitter/PacketAllocator.cpp b/demuxer/LAVSplitter/PacketAllocator.cpp index 30456e5c..4ae27f7b 100644 --- a/demuxer/LAVSplitter/PacketAllocator.cpp +++ b/demuxer/LAVSplitter/PacketAllocator.cpp @@ -127,6 +127,16 @@ CPacketAllocator::~CPacketAllocator(void) ReallyFree(); } +STDMETHODIMP CPacketAllocator::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv) +{ + if (riid == IID_ILAVDynamicAllocator) { + return GetInterface((ILAVDynamicAllocator *) this, ppv); + } + else { + return __super::NonDelegatingQueryInterface(riid, ppv); + } +} + STDMETHODIMP CPacketAllocator::SetProperties(ALLOCATOR_PROPERTIES* pRequest, ALLOCATOR_PROPERTIES* pActual) { CheckPointer(pActual,E_POINTER); @@ -215,47 +225,13 @@ HRESULT CPacketAllocator::Alloc(void) return E_OUTOFMEMORY; } - /* Compute the aligned size */ - LONG lAlignedSize = m_lSize + m_lPrefix; - - /* Check overflow */ - if (lAlignedSize < m_lSize) { - return E_OUTOFMEMORY; - } - - if (m_lAlignment > 1) { - LONG lRemainder = lAlignedSize % m_lAlignment; - if (lRemainder != 0) { - LONG lNewSize = lAlignedSize + m_lAlignment - lRemainder; - if (lNewSize < lAlignedSize) { - return E_OUTOFMEMORY; - } - lAlignedSize = lNewSize; - } - } - - /* Create the contiguous memory block for the samples - making sure it's properly aligned (64K should be enough!) - */ - ASSERT(lAlignedSize % m_lAlignment == 0); - - LONGLONG lToAllocate = m_lCount * (LONGLONG)lAlignedSize; - - /* Check overflow */ - if (lToAllocate > MAXLONG) { - return E_OUTOFMEMORY; - } - m_bAllocated = TRUE; CMediaPacketSample *pSample = nullptr; ASSERT(m_lAllocated == 0); - // Create the new samples - we have allocated m_lSize bytes for each sample - // plus m_lPrefix bytes per sample as a prefix. We set the pointer to - // the memory after the prefix - so that GetPointer() will return a pointer - // to m_lSize bytes. + // Create the initial set of samples for (; m_lAllocated < m_lCount; m_lAllocated++) { pSample = new CMediaPacketSample(NAME("LAV Package media sample"), this, &hr); @@ -272,6 +248,76 @@ HRESULT CPacketAllocator::Alloc(void) return NOERROR; } +// get container for a sample. Blocking, synchronous call to get the +// next free buffer (as represented by an IMediaSample interface). +// on return, the time etc properties will be invalid, but the buffer +// pointer and size will be correct. + +HRESULT CPacketAllocator::GetBuffer(__deref_out IMediaSample **ppBuffer, + __in_opt REFERENCE_TIME *pStartTime, + __in_opt REFERENCE_TIME *pEndTime, + DWORD dwFlags +) +{ + UNREFERENCED_PARAMETER(pStartTime); + UNREFERENCED_PARAMETER(pEndTime); + UNREFERENCED_PARAMETER(dwFlags); + CMediaSample *pSample; + + *ppBuffer = NULL; + for (;;) + { + { // scope for lock + CAutoLock cObjectLock(this); + + /* Check we are committed */ + if (!m_bCommitted) { + return VFW_E_NOT_COMMITTED; + } + pSample = (CMediaSample *)m_lFree.RemoveHead(); + + /* if no sample was available, allocate a new one */ + if (pSample == NULL) { + HRESULT hr = S_OK; + pSample = new CMediaPacketSample(NAME("LAV Package media sample"), this, &hr); + ASSERT(SUCCEEDED(hr)); + + if (pSample) { + m_lAllocated++; + DbgLog((LOG_TRACE, 10, "Allocated new sample, %d total", m_lAllocated)); + } + } + } + + /* If we didn't get a sample then wait for the list to signal */ + + if (pSample) { + break; + } + if (dwFlags & AM_GBF_NOWAIT) { + return VFW_E_TIMEOUT; + } + ASSERT(m_hSem != NULL); + WaitForSingleObject(m_hSem, INFINITE); + } + + /* Addref the buffer up to one. On release + back to zero instead of being deleted, it will requeue itself by + calling the ReleaseBuffer member function. NOTE the owner of a + media sample must always be derived from CBaseAllocator */ + + + ASSERT(pSample->m_cRef == 0); + pSample->m_cRef = 1; + *ppBuffer = pSample; + +#ifdef DXMPERF + PERFLOG_GETBUFFER((IMemAllocator *) this, pSample); +#endif // DXMPERF + + return NOERROR; +} + // override this to free up any resources we have allocated. // called from the base class on Decommit when all buffers have been diff --git a/demuxer/LAVSplitter/PacketAllocator.h b/demuxer/LAVSplitter/PacketAllocator.h index 1de13118..8f3b03b1 100644 --- a/demuxer/LAVSplitter/PacketAllocator.h +++ b/demuxer/LAVSplitter/PacketAllocator.h @@ -25,6 +25,7 @@ #include "Packet.h" #include "IMediaSideData.h" #include "IMediaSideDataFFmpeg.h" +#include "ILAVDynamicAllocator.h" interface __declspec(uuid("0B2EE323-0ED8-452D-B31E-B9B4DE2C0C39")) ILAVMediaSample : public IUnknown { @@ -54,7 +55,7 @@ protected: MediaSideDataFFMpeg *m_pSideData = nullptr; }; -class CPacketAllocator : public CBaseAllocator +class CPacketAllocator : public CBaseAllocator, public ILAVDynamicAllocator { protected: BOOL m_bAllocated = FALSE; @@ -69,9 +70,19 @@ protected: // overriden to allocate the memory when commit called HRESULT Alloc(void); + public: CPacketAllocator(LPCTSTR pName, LPUNKNOWN pUnk, HRESULT *phr); virtual ~CPacketAllocator(void); + // CUnknown support + DECLARE_IUNKNOWN; + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv); + + // CBaseAllocator overrides STDMETHODIMP SetProperties(ALLOCATOR_PROPERTIES* pRequest, ALLOCATOR_PROPERTIES* pActual); + STDMETHODIMP GetBuffer(IMediaSample **ppBuffer, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags); + + // ILAVDynamicAllocator + STDMETHODIMP_(BOOL) IsDynamicAllocator() { return TRUE; } }; diff --git a/demuxer/LAVSplitter/dllmain.cpp b/demuxer/LAVSplitter/dllmain.cpp index f65df0d3..1d6e0a74 100644 --- a/demuxer/LAVSplitter/dllmain.cpp +++ b/demuxer/LAVSplitter/dllmain.cpp @@ -37,6 +37,7 @@ #include "registry.h" #include "IGraphRebuildDelegate.h" #include "IMediaSideDataFFmpeg.h" +#include "ILAVDynamicAllocator.h" // The GUID we use to register the splitter media types DEFINE_GUID(MEDIATYPE_LAVSplitter, -- cgit v1.2.3