/*
* (C) 2003-2006 Gabest
* (C) 2006-2013 see Authors.txt
*
* This file is part of MPC-HC.
*
* MPC-HC 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 3 of the License, or
* (at your option) any later version.
*
* MPC-HC 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, see .
*
*/
#include "stdafx.h"
#include "../../../DSUtil/DSUtil.h"
#include
#include "moreuuids.h"
#include "../../switcher/AudioSwitcher/AudioSwitcher.h"
#include "BaseSplitter.h"
//
// CPacketQueue
//
CPacketQueue::CPacketQueue() : m_size(0)
{
}
void CPacketQueue::Add(CAutoPtr p)
{
CAutoLock cAutoLock(this);
if (p) {
m_size += p->GetDataSize();
if (p->bAppendable && !p->bDiscontinuity && !p->pmt
&& p->rtStart == Packet::INVALID_TIME
&& !IsEmpty() && GetTail()->rtStart != Packet::INVALID_TIME) {
Packet* tail = GetTail();
size_t oldsize = tail->GetCount();
size_t newsize = tail->GetCount() + p->GetCount();
tail->SetCount(newsize, max(1024, (int)newsize)); // doubles the reserved buffer size
memcpy(tail->GetData() + oldsize, p->GetData(), p->GetCount());
/*
GetTail()->Append(*p); // too slow
*/
return;
}
}
AddTail(p);
}
CAutoPtr CPacketQueue::Remove()
{
CAutoLock cAutoLock(this);
ASSERT(__super::GetCount() > 0);
CAutoPtr p = RemoveHead();
if (p) {
m_size -= p->GetDataSize();
}
return p;
}
void CPacketQueue::RemoveAll()
{
CAutoLock cAutoLock(this);
m_size = 0;
__super::RemoveAll();
}
int CPacketQueue::GetCount()
{
CAutoLock cAutoLock(this);
return (int)__super::GetCount();
}
int CPacketQueue::GetSize()
{
CAutoLock cAutoLock(this);
return m_size;
}
//
// CBaseSplitterInputPin
//
CBaseSplitterInputPin::CBaseSplitterInputPin(TCHAR* pName, CBaseSplitterFilter* pFilter, CCritSec* pLock, HRESULT* phr)
: CBasePin(pName, pFilter, pLock, phr, L"Input", PINDIR_INPUT)
{
}
CBaseSplitterInputPin::~CBaseSplitterInputPin()
{
}
HRESULT CBaseSplitterInputPin::GetAsyncReader(IAsyncReader** ppAsyncReader)
{
CheckPointer(ppAsyncReader, E_POINTER);
*ppAsyncReader = nullptr;
CheckPointer(m_pAsyncReader, VFW_E_NOT_CONNECTED);
(*ppAsyncReader = m_pAsyncReader)->AddRef();
return S_OK;
}
STDMETHODIMP CBaseSplitterInputPin::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
CheckPointer(ppv, E_POINTER);
return
__super::NonDelegatingQueryInterface(riid, ppv);
}
HRESULT CBaseSplitterInputPin::CheckMediaType(const CMediaType* pmt)
{
return S_OK;
/*
return pmt->majortype == MEDIATYPE_Stream
? S_OK
: E_INVALIDARG;
*/
}
HRESULT CBaseSplitterInputPin::CheckConnect(IPin* pPin)
{
HRESULT hr;
if (FAILED(hr = __super::CheckConnect(pPin))) {
return hr;
}
return CComQIPtr(pPin) ? S_OK : E_NOINTERFACE;
}
HRESULT CBaseSplitterInputPin::BreakConnect()
{
HRESULT hr;
if (FAILED(hr = __super::BreakConnect())) {
return hr;
}
if (FAILED(hr = (static_cast(m_pFilter))->BreakConnect(PINDIR_INPUT, this))) {
return hr;
}
m_pAsyncReader.Release();
return S_OK;
}
HRESULT CBaseSplitterInputPin::CompleteConnect(IPin* pPin)
{
HRESULT hr;
if (FAILED(hr = __super::CompleteConnect(pPin))) {
return hr;
}
CheckPointer(pPin, E_POINTER);
m_pAsyncReader = pPin;
CheckPointer(m_pAsyncReader, E_NOINTERFACE);
if (FAILED(hr = (static_cast(m_pFilter))->CompleteConnect(PINDIR_INPUT, this))) {
return hr;
}
return S_OK;
}
STDMETHODIMP CBaseSplitterInputPin::BeginFlush()
{
return E_UNEXPECTED;
}
STDMETHODIMP CBaseSplitterInputPin::EndFlush()
{
return E_UNEXPECTED;
}
//
// CBaseSplitterOutputPin
//
CBaseSplitterOutputPin::CBaseSplitterOutputPin(CAtlArray& mts, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr, int nBuffers, int QueueMaxPackets)
: CBaseOutputPin(NAME("CBaseSplitterOutputPin"), pFilter, pLock, phr, pName)
, m_hrDeliver(S_OK) // just in case it were asked before the worker thread could be created and reset it
, m_fFlushing(false)
, m_eEndFlush(TRUE)
, m_QueueMaxPackets(QueueMaxPackets)
{
m_mts.Copy(mts);
m_nBuffers = max(nBuffers, 1);
memset(&m_brs, 0, sizeof(m_brs));
m_brs.rtLastDeliverTime = Packet::INVALID_TIME;
}
CBaseSplitterOutputPin::CBaseSplitterOutputPin(LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr, int nBuffers, int QueueMaxPackets)
: CBaseOutputPin(NAME("CBaseSplitterOutputPin"), pFilter, pLock, phr, pName)
, m_hrDeliver(S_OK) // just in case it were asked before the worker thread could be created and reset it
, m_fFlushing(false)
, m_eEndFlush(TRUE)
, m_QueueMaxPackets(QueueMaxPackets)
{
m_nBuffers = max(nBuffers, 1);
memset(&m_brs, 0, sizeof(m_brs));
m_brs.rtLastDeliverTime = Packet::INVALID_TIME;
}
CBaseSplitterOutputPin::~CBaseSplitterOutputPin()
{
}
STDMETHODIMP CBaseSplitterOutputPin::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
CheckPointer(ppv, E_POINTER);
return
// riid == __uuidof(IMediaSeeking) ? m_pFilter->QueryInterface(riid, ppv) :
QI(IMediaSeeking)
QI(IPropertyBag)
QI(IPropertyBag2)
QI(IDSMPropertyBag)
QI(IBitRateInfo)
__super::NonDelegatingQueryInterface(riid, ppv);
}
HRESULT CBaseSplitterOutputPin::SetName(LPCWSTR pName)
{
CheckPointer(pName, E_POINTER);
if (m_pName) {
delete [] m_pName;
}
m_pName = DEBUG_NEW WCHAR[wcslen(pName) + 1];
CheckPointer(m_pName, E_OUTOFMEMORY);
wcscpy_s(m_pName, wcslen(pName) + 1, pName);
return S_OK;
}
HRESULT CBaseSplitterOutputPin::DecideBufferSize(IMemAllocator* pAlloc, ALLOCATOR_PROPERTIES* pProperties)
{
ASSERT(pAlloc);
ASSERT(pProperties);
HRESULT hr = NOERROR;
pProperties->cBuffers = max(pProperties->cBuffers, m_nBuffers);
pProperties->cbBuffer = max(m_mt.lSampleSize, 1);
// TODO: is this still needed ?
if (m_mt.subtype == MEDIASUBTYPE_Vorbis && m_mt.formattype == FORMAT_VorbisFormat) {
// oh great, the oggds vorbis decoder assumes there will be two at least, stupid thing...
pProperties->cBuffers = max(pProperties->cBuffers, 2);
}
ALLOCATOR_PROPERTIES Actual;
if (FAILED(hr = pAlloc->SetProperties(pProperties, &Actual))) {
return hr;
}
if (Actual.cbBuffer < pProperties->cbBuffer) {
return E_FAIL;
}
ASSERT(Actual.cBuffers == pProperties->cBuffers);
return NOERROR;
}
HRESULT CBaseSplitterOutputPin::CheckMediaType(const CMediaType* pmt)
{
for (size_t i = 0; i < m_mts.GetCount(); i++) {
if (*pmt == m_mts[i]) {
return S_OK;
}
}
return E_INVALIDARG;
}
HRESULT CBaseSplitterOutputPin::GetMediaType(int iPosition, CMediaType* pmt)
{
CAutoLock cAutoLock(m_pLock);
if (iPosition < 0) {
return E_INVALIDARG;
}
if ((size_t)iPosition >= m_mts.GetCount()) {
return VFW_S_NO_MORE_ITEMS;
}
*pmt = m_mts[iPosition];
return S_OK;
}
STDMETHODIMP CBaseSplitterOutputPin::Notify(IBaseFilter* pSender, Quality q)
{
return E_NOTIMPL;
}
//
HRESULT CBaseSplitterOutputPin::Active()
{
CAutoLock cAutoLock(m_pLock);
if (m_Connected) {
Create();
}
return __super::Active();
}
HRESULT CBaseSplitterOutputPin::Inactive()
{
CAutoLock cAutoLock(m_pLock);
if (ThreadExists()) {
CallWorker(CMD_EXIT);
}
return __super::Inactive();
}
HRESULT CBaseSplitterOutputPin::DeliverBeginFlush()
{
m_eEndFlush.Reset();
m_fFlushed = false;
m_fFlushing = true;
m_hrDeliver = S_FALSE;
m_queue.RemoveAll();
HRESULT hr = IsConnected() ? GetConnected()->BeginFlush() : S_OK;
if (S_OK != hr) {
m_eEndFlush.Set();
}
return hr;
}
HRESULT CBaseSplitterOutputPin::DeliverEndFlush()
{
if (!ThreadExists()) {
return S_FALSE;
}
HRESULT hr = IsConnected() ? GetConnected()->EndFlush() : S_OK;
m_hrDeliver = S_OK;
m_fFlushing = false;
m_fFlushed = true;
m_eEndFlush.Set();
return hr;
}
HRESULT CBaseSplitterOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
m_brs.rtLastDeliverTime = Packet::INVALID_TIME;
if (m_fFlushing) {
return S_FALSE;
}
m_rtStart = tStart;
if (!ThreadExists()) {
return S_FALSE;
}
HRESULT hr = __super::DeliverNewSegment(tStart, tStop, dRate);
if (S_OK != hr) {
return hr;
}
MakeISCRHappy();
return hr;
}
int CBaseSplitterOutputPin::QueueCount()
{
return m_queue.GetCount();
}
int CBaseSplitterOutputPin::QueueSize()
{
return m_queue.GetSize();
}
HRESULT CBaseSplitterOutputPin::QueueEndOfStream()
{
return QueuePacket(CAutoPtr()); // NULL means EndOfStream
}
HRESULT CBaseSplitterOutputPin::QueuePacket(CAutoPtr p)
{
if (!ThreadExists()) {
return S_FALSE;
}
while (S_OK == m_hrDeliver
&& ((m_queue.GetCount() > (m_QueueMaxPackets * 2) || m_queue.GetSize() > (MAXPACKETSIZE * 3 / 2))
|| ((m_queue.GetCount() > m_QueueMaxPackets || m_queue.GetSize() > MAXPACKETSIZE) && !(static_cast(m_pFilter))->IsAnyPinDrying()))) {
Sleep(10);
}
if (S_OK != m_hrDeliver) {
return m_hrDeliver;
}
m_queue.Add(p);
return m_hrDeliver;
}
bool CBaseSplitterOutputPin::IsDiscontinuous()
{
return m_mt.majortype == MEDIATYPE_Text
|| m_mt.majortype == MEDIATYPE_ScriptCommand
|| m_mt.majortype == MEDIATYPE_Subtitle
|| m_mt.subtype == MEDIASUBTYPE_DVD_SUBPICTURE
|| m_mt.subtype == MEDIASUBTYPE_CVD_SUBPICTURE
|| m_mt.subtype == MEDIASUBTYPE_SVCD_SUBPICTURE;
}
bool CBaseSplitterOutputPin::IsActive()
{
CComPtr pPin = this;
do {
CComPtr pPinTo;
CComQIPtr pSSIP;
if (S_OK == pPin->ConnectedTo(&pPinTo) && (pSSIP = pPinTo) && !pSSIP->IsActive()) {
return false;
}
pPin = GetFirstPin(GetFilterFromPin(pPinTo), PINDIR_OUTPUT);
} while (pPin);
return true;
}
DWORD CBaseSplitterOutputPin::ThreadProc()
{
SetThreadName((DWORD) - 1, "CBaseSplitterOutputPin");
m_hrDeliver = S_OK;
m_fFlushing = m_fFlushed = false;
m_eEndFlush.Set();
// fix for Microsoft DTV-DVD Video Decoder - video freeze after STOP/PLAY
bool iHaaliRenderConnect = false;
CComPtr pPinTo = this, pTmp;
while (pPinTo && SUCCEEDED(pPinTo->ConnectedTo(&pTmp)) && (pPinTo = pTmp)) {
pTmp = nullptr;
CComPtr pBF = GetFilterFromPin(pPinTo);
if (GetCLSID(pBF) == CLSID_DXR) { // Haali Renderer
iHaaliRenderConnect = true;
break;
}
pPinTo = GetFirstPin(pBF, PINDIR_OUTPUT);
}
if (IsConnected() && !iHaaliRenderConnect) {
GetConnected()->BeginFlush();
GetConnected()->EndFlush();
}
for (;;) {
Sleep(1);
DWORD cmd;
if (CheckRequest(&cmd)) {
m_hThread = nullptr;
cmd = GetRequest();
Reply(S_OK);
ASSERT(cmd == CMD_EXIT);
return 0;
}
int cnt = 0;
do {
CAutoPtr p;
{
CAutoLock cAutoLock(&m_queue);
if ((cnt = m_queue.GetCount()) > 0) {
p = m_queue.Remove();
}
}
if (S_OK == m_hrDeliver && cnt > 0) {
ASSERT(!m_fFlushing);
m_fFlushed = false;
// flushing can still start here, to release a blocked deliver call
HRESULT hr = p
? DeliverPacket(p)
: DeliverEndOfStream();
m_eEndFlush.Wait(); // .. so we have to wait until it is done
if (hr != S_OK && !m_fFlushed) { // and only report the error in m_hrDeliver if we didn't flush the stream
// CAutoLock cAutoLock(&m_csQueueLock);
m_hrDeliver = hr;
break;
}
}
} while (--cnt > 0);
}
}
HRESULT CBaseSplitterOutputPin::DeliverPacket(CAutoPtr p)
{
HRESULT hr;
long nBytes = (long)p->GetCount();
if (nBytes == 0) {
return S_OK;
}
m_brs.nBytesSinceLastDeliverTime += nBytes;
if (p->rtStart != Packet::INVALID_TIME) {
if (m_brs.rtLastDeliverTime == Packet::INVALID_TIME) {
m_brs.rtLastDeliverTime = p->rtStart;
m_brs.nBytesSinceLastDeliverTime = 0;
}
if (m_brs.rtLastDeliverTime + 10000000 < p->rtStart) {
REFERENCE_TIME rtDiff = p->rtStart - m_brs.rtLastDeliverTime;
double secs, bits;
secs = (double)rtDiff / 10000000;
bits = 8.0 * m_brs.nBytesSinceLastDeliverTime;
m_brs.nCurrentBitRate = (DWORD)(bits / secs);
m_brs.rtTotalTimeDelivered += rtDiff;
m_brs.nTotalBytesDelivered += m_brs.nBytesSinceLastDeliverTime;
secs = (double)m_brs.rtTotalTimeDelivered / 10000000;
bits = 8.0 * m_brs.nTotalBytesDelivered;
m_brs.nAverageBitRate = (DWORD)(bits / secs);
m_brs.rtLastDeliverTime = p->rtStart;
m_brs.nBytesSinceLastDeliverTime = 0;
/*
TRACE(_T("[%d] c: %d kbps, a: %d kbps\n"),
p->TrackNumber,
(m_brs.nCurrentBitRate+500)/1000,
(m_brs.nAverageBitRate+500)/1000);
*/
}
double dRate = 1.0;
if (SUCCEEDED((static_cast(m_pFilter))->GetRate(&dRate))) {
p->rtStart = (REFERENCE_TIME)((double)p->rtStart / dRate);
p->rtStop = (REFERENCE_TIME)((double)p->rtStop / dRate);
}
}
do {
CComPtr pSample;
if (S_OK != (hr = GetDeliveryBuffer(&pSample, nullptr, nullptr, 0))) {
break;
}
if (nBytes > pSample->GetSize()) {
pSample.Release();
ALLOCATOR_PROPERTIES props, actual;
if (S_OK != (hr = m_pAllocator->GetProperties(&props))) {
break;
}
props.cbBuffer = nBytes * 3 / 2;
if (props.cBuffers > 1) {
if (S_OK != (hr = __super::DeliverBeginFlush())) {
break;
}
if (S_OK != (hr = __super::DeliverEndFlush())) {
break;
}
}
if (S_OK != (hr = m_pAllocator->Decommit())) {
break;
}
if (S_OK != (hr = m_pAllocator->SetProperties(&props, &actual))) {
break;
}
if (S_OK != (hr = m_pAllocator->Commit())) {
break;
}
if (S_OK != (hr = GetDeliveryBuffer(&pSample, nullptr, nullptr, 0))) {
break;
}
}
if (p->pmt) {
pSample->SetMediaType(p->pmt);
p->bDiscontinuity = true;
CAutoLock cAutoLock(m_pLock);
m_mts.RemoveAll();
m_mts.Add(*p->pmt);
}
bool fTimeValid = p->rtStart != Packet::INVALID_TIME;
#if defined(_DEBUG) && 0
TRACE(_T("[%d]: d%d s%d p%d, b=%d, [%20I64d - %20I64d]\n"),
p->TrackNumber,
p->bDiscontinuity, p->bSyncPoint, fTimeValid && p->rtStart < 0,
nBytes, p->rtStart, p->rtStop);
#endif
ASSERT(!p->bSyncPoint || fTimeValid);
BYTE* pData = nullptr;
if (S_OK != (hr = pSample->GetPointer(&pData)) || !pData) {
break;
}
memcpy(pData, p->GetData(), nBytes);
if (S_OK != (hr = pSample->SetActualDataLength(nBytes))) {
break;
}
if (S_OK != (hr = pSample->SetTime(fTimeValid ? &p->rtStart : nullptr, fTimeValid ? &p->rtStop : nullptr))) {
break;
}
if (S_OK != (hr = pSample->SetMediaTime(nullptr, nullptr))) {
break;
}
if (S_OK != (hr = pSample->SetDiscontinuity(p->bDiscontinuity))) {
break;
}
if (S_OK != (hr = pSample->SetSyncPoint(p->bSyncPoint))) {
break;
}
if (S_OK != (hr = pSample->SetPreroll(fTimeValid && p->rtStart < 0))) {
break;
}
if (S_OK != (hr = Deliver(pSample))) {
break;
}
} while (false);
return hr;
}
void CBaseSplitterOutputPin::MakeISCRHappy()
{
CComPtr pPinTo = this, pTmp;
while (pPinTo && SUCCEEDED(pPinTo->ConnectedTo(&pTmp)) && (pPinTo = pTmp)) {
pTmp = nullptr;
CComPtr pBF = GetFilterFromPin(pPinTo);
if (GetCLSID(pBF) == GUIDFromCString(_T("{48025243-2D39-11CE-875D-00608CB78066}"))) { // ISCR
CAutoPtr p(DEBUG_NEW Packet());
p->TrackNumber = (DWORD) - 1;
p->rtStart = -1;
p->rtStop = 0;
p->bSyncPoint = FALSE;
p->SetData(" ", 2);
QueuePacket(p);
break;
}
pPinTo = GetFirstPin(pBF, PINDIR_OUTPUT);
}
}
HRESULT CBaseSplitterOutputPin::GetDeliveryBuffer(IMediaSample** ppSample, REFERENCE_TIME* pStartTime, REFERENCE_TIME* pEndTime, DWORD dwFlags)
{
return __super::GetDeliveryBuffer(ppSample, pStartTime, pEndTime, dwFlags);
}
HRESULT CBaseSplitterOutputPin::Deliver(IMediaSample* pSample)
{
return __super::Deliver(pSample);
}
// IMediaSeeking
STDMETHODIMP CBaseSplitterOutputPin::GetCapabilities(DWORD* pCapabilities)
{
return (static_cast(m_pFilter))->GetCapabilities(pCapabilities);
}
STDMETHODIMP CBaseSplitterOutputPin::CheckCapabilities(DWORD* pCapabilities)
{
return (static_cast(m_pFilter))->CheckCapabilities(pCapabilities);
}
STDMETHODIMP CBaseSplitterOutputPin::IsFormatSupported(const GUID* pFormat)
{
return (static_cast(m_pFilter))->IsFormatSupported(pFormat);
}
STDMETHODIMP CBaseSplitterOutputPin::QueryPreferredFormat(GUID* pFormat)
{
return (static_cast(m_pFilter))->QueryPreferredFormat(pFormat);
}
STDMETHODIMP CBaseSplitterOutputPin::GetTimeFormat(GUID* pFormat)
{
return (static_cast(m_pFilter))->GetTimeFormat(pFormat);
}
STDMETHODIMP CBaseSplitterOutputPin::IsUsingTimeFormat(const GUID* pFormat)
{
return (static_cast(m_pFilter))->IsUsingTimeFormat(pFormat);
}
STDMETHODIMP CBaseSplitterOutputPin::SetTimeFormat(const GUID* pFormat)
{
return (static_cast(m_pFilter))->SetTimeFormat(pFormat);
}
STDMETHODIMP CBaseSplitterOutputPin::GetDuration(LONGLONG* pDuration)
{
return (static_cast(m_pFilter))->GetDuration(pDuration);
}
STDMETHODIMP CBaseSplitterOutputPin::GetStopPosition(LONGLONG* pStop)
{
return (static_cast(m_pFilter))->GetStopPosition(pStop);
}
STDMETHODIMP CBaseSplitterOutputPin::GetCurrentPosition(LONGLONG* pCurrent)
{
return (static_cast(m_pFilter))->GetCurrentPosition(pCurrent);
}
STDMETHODIMP CBaseSplitterOutputPin::ConvertTimeFormat(LONGLONG* pTarget, const GUID* pTargetFormat, LONGLONG Source, const GUID* pSourceFormat)
{
return (static_cast(m_pFilter))->ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat);
}
STDMETHODIMP CBaseSplitterOutputPin::SetPositions(LONGLONG* pCurrent, DWORD dwCurrentFlags, LONGLONG* pStop, DWORD dwStopFlags)
{
return (static_cast(m_pFilter))->SetPositionsInternal(this, pCurrent, dwCurrentFlags, pStop, dwStopFlags);
}
STDMETHODIMP CBaseSplitterOutputPin::GetPositions(LONGLONG* pCurrent, LONGLONG* pStop)
{
return (static_cast(m_pFilter))->GetPositions(pCurrent, pStop);
}
STDMETHODIMP CBaseSplitterOutputPin::GetAvailable(LONGLONG* pEarliest, LONGLONG* pLatest)
{
return (static_cast(m_pFilter))->GetAvailable(pEarliest, pLatest);
}
STDMETHODIMP CBaseSplitterOutputPin::SetRate(double dRate)
{
return (static_cast(m_pFilter))->SetRate(dRate);
}
STDMETHODIMP CBaseSplitterOutputPin::GetRate(double* pdRate)
{
return (static_cast(m_pFilter))->GetRate(pdRate);
}
STDMETHODIMP CBaseSplitterOutputPin::GetPreroll(LONGLONG* pllPreroll)
{
return (static_cast(m_pFilter))->GetPreroll(pllPreroll);
}
//
// CBaseSplitterFilter
//
CBaseSplitterFilter::CBaseSplitterFilter(LPCTSTR pName, LPUNKNOWN pUnk, HRESULT* phr, const CLSID& clsid, int QueueMaxPackets)
: CBaseFilter(pName, pUnk, this, clsid)
, m_rtDuration(0), m_rtStart(0), m_rtStop(0), m_rtCurrent(0)
, m_dRate(1.0)
, m_nOpenProgress(100)
, m_fAbort(false)
, m_rtLastStart(_I64_MIN)
, m_rtLastStop(_I64_MIN)
, m_priority(THREAD_PRIORITY_NORMAL)
, m_QueueMaxPackets(QueueMaxPackets)
{
if (phr) {
*phr = S_OK;
}
m_pInput.Attach(DEBUG_NEW CBaseSplitterInputPin(NAME("CBaseSplitterInputPin"), this, this, phr));
}
CBaseSplitterFilter::~CBaseSplitterFilter()
{
CAutoLock cAutoLock(this);
CAMThread::CallWorker(CMD_EXIT);
CAMThread::Close();
}
STDMETHODIMP CBaseSplitterFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
CheckPointer(ppv, E_POINTER);
*ppv = nullptr;
if (m_pInput && riid == __uuidof(IFileSourceFilter)) {
return E_NOINTERFACE;
}
return
QI(IFileSourceFilter)
QI(IMediaSeeking)
QI(IAMOpenProgress)
QI2(IAMMediaContent)
QI2(IAMExtendedSeeking)
QI(IKeyFrameInfo)
QI(IBufferInfo)
QI(IPropertyBag)
QI(IPropertyBag2)
QI(IDSMPropertyBag)
QI(IDSMResourceBag)
QI(IDSMChapterBag)
__super::NonDelegatingQueryInterface(riid, ppv);
}
CBaseSplitterOutputPin* CBaseSplitterFilter::GetOutputPin(DWORD TrackNum)
{
CAutoLock cAutoLock(&m_csPinMap);
CBaseSplitterOutputPin* pPin = nullptr;
m_pPinMap.Lookup(TrackNum, pPin);
return pPin;
}
DWORD CBaseSplitterFilter::GetOutputTrackNum(CBaseSplitterOutputPin* pPin)
{
CAutoLock cAutoLock(&m_csPinMap);
POSITION pos = m_pPinMap.GetStartPosition();
while (pos) {
DWORD TrackNum;
CBaseSplitterOutputPin* pPinTmp;
m_pPinMap.GetNextAssoc(pos, TrackNum, pPinTmp);
if (pPinTmp == pPin) {
return TrackNum;
}
}
return (DWORD) - 1;
}
HRESULT CBaseSplitterFilter::RenameOutputPin(DWORD TrackNumSrc, DWORD TrackNumDst, const AM_MEDIA_TYPE* pmt)
{
CAutoLock cAutoLock(&m_csPinMap);
CBaseSplitterOutputPin* pPin;
if (m_pPinMap.Lookup(TrackNumSrc, pPin)) {
if (CComQIPtr pPinTo = pPin->GetConnected()) {
if (pmt && S_OK != pPinTo->QueryAccept(pmt)) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
}
m_pPinMap.RemoveKey(TrackNumSrc);
m_pPinMap[TrackNumDst] = pPin;
if (pmt) {
CAutoLock cAutoLock2(&m_csmtnew);
m_mtnew[TrackNumDst] = *pmt;
}
return S_OK;
}
return E_FAIL;
}
HRESULT CBaseSplitterFilter::AddOutputPin(DWORD TrackNum, CAutoPtr pPin)
{
CAutoLock cAutoLock(&m_csPinMap);
if (!pPin) {
return E_INVALIDARG;
}
m_pPinMap[TrackNum] = pPin;
m_pOutputs.AddTail(pPin);
return S_OK;
}
HRESULT CBaseSplitterFilter::DeleteOutputs()
{
m_rtDuration = 0;
m_pRetiredOutputs.RemoveAll();
CAutoLock cAutoLockF(this);
if (m_State != State_Stopped) {
return VFW_E_NOT_STOPPED;
}
while (m_pOutputs.GetCount()) {
CAutoPtr pPin = m_pOutputs.RemoveHead();
if (IPin* pPinTo = pPin->GetConnected()) {
pPinTo->Disconnect();
}
pPin->Disconnect();
// we can't just let it be deleted now, something might have AddRefed on it (graphedit...)
m_pRetiredOutputs.AddTail(pPin);
}
CAutoLock cAutoLockPM(&m_csPinMap);
m_pPinMap.RemoveAll();
CAutoLock cAutoLockMT(&m_csmtnew);
m_mtnew.RemoveAll();
RemoveAll();
ResRemoveAll();
ChapRemoveAll();
m_fontinst.UninstallFonts();
m_pSyncReader.Release();
return S_OK;
}
void CBaseSplitterFilter::DeliverBeginFlush()
{
m_fFlushing = true;
POSITION pos = m_pOutputs.GetHeadPosition();
while (pos) {
m_pOutputs.GetNext(pos)->DeliverBeginFlush();
}
}
void CBaseSplitterFilter::DeliverEndFlush()
{
POSITION pos = m_pOutputs.GetHeadPosition();
while (pos) {
m_pOutputs.GetNext(pos)->DeliverEndFlush();
}
m_fFlushing = false;
m_eEndFlush.Set();
}
DWORD CBaseSplitterFilter::ThreadProc()
{
if (m_pSyncReader) {
m_pSyncReader->SetBreakEvent(GetRequestHandle());
}
if (!DemuxInit()) {
for (;;) {
DWORD cmd = GetRequest();
if (cmd == CMD_EXIT) {
CAMThread::m_hThread = nullptr;
}
Reply(S_OK);
if (cmd == CMD_EXIT) {
return 0;
}
}
}
m_eEndFlush.Set();
m_fFlushing = false;
for (DWORD cmd = (DWORD) - 1; ; cmd = GetRequest()) {
if (cmd == CMD_EXIT) {
m_hThread = nullptr;
Reply(S_OK);
return 0;
}
SetThreadPriority(m_hThread, m_priority = THREAD_PRIORITY_NORMAL);
m_rtStart = m_rtNewStart;
m_rtStop = m_rtNewStop;
DemuxSeek(m_rtStart);
if (cmd != (DWORD) - 1) {
Reply(S_OK);
}
m_eEndFlush.Wait();
m_pActivePins.RemoveAll();
POSITION pos = m_pOutputs.GetHeadPosition();
while (pos && !m_fFlushing) {
CBaseSplitterOutputPin* pPin = m_pOutputs.GetNext(pos);
if (pPin->IsConnected() && pPin->IsActive()) {
m_pActivePins.AddTail(pPin);
pPin->DeliverNewSegment(m_rtStart, m_rtStop, m_dRate);
}
}
do {
m_bDiscontinuitySent.RemoveAll();
} while (!DemuxLoop());
pos = m_pActivePins.GetHeadPosition();
while (pos && !CheckRequest(&cmd)) {
m_pActivePins.GetNext(pos)->QueueEndOfStream();
}
}
ASSERT(0); // we should only exit via CMD_EXIT
m_hThread = nullptr;
return 0;
}
HRESULT CBaseSplitterFilter::DeliverPacket(CAutoPtr p)
{
HRESULT hr = S_FALSE;
CBaseSplitterOutputPin* pPin = GetOutputPin(p->TrackNumber);
if (!pPin || !pPin->IsConnected() || !m_pActivePins.Find(pPin)) {
return S_FALSE;
}
if (p->rtStart != Packet::INVALID_TIME) {
m_rtCurrent = p->rtStart;
p->rtStart -= m_rtStart;
p->rtStop -= m_rtStart;
ASSERT(p->rtStart <= p->rtStop);
}
{
CAutoLock cAutoLock(&m_csmtnew);
CMediaType mt;
if (m_mtnew.Lookup(p->TrackNumber, mt)) {
p->pmt = CreateMediaType(&mt);
m_mtnew.RemoveKey(p->TrackNumber);
}
}
if (!m_bDiscontinuitySent.Find(p->TrackNumber)) {
p->bDiscontinuity = TRUE;
}
DWORD TrackNumber = p->TrackNumber;
BOOL bDiscontinuity = p->bDiscontinuity;
#if defined(_DEBUG) && 0
TRACE(_T("[%d]: d%d s%d p%d, b=%d, [%20I64d - %20I64d]\n"),
p->TrackNumber,
p->bDiscontinuity, p->bSyncPoint, p->rtStart != Packet::INVALID_TIME && p->rtStart < 0,
p->GetCount(), p->rtStart, p->rtStop);
#endif
hr = pPin->QueuePacket(p);
if (S_OK != hr) {
if (POSITION pos = m_pActivePins.Find(pPin)) {
m_pActivePins.RemoveAt(pos);
}
if (!m_pActivePins.IsEmpty()) { // only die when all pins are down
hr = S_OK;
}
return hr;
}
if (bDiscontinuity) {
m_bDiscontinuitySent.AddTail(TrackNumber);
}
return hr;
}
bool CBaseSplitterFilter::IsAnyPinDrying()
{
int totalcount = 0, totalsize = 0;
POSITION pos = m_pActivePins.GetHeadPosition();
while (pos) {
CBaseSplitterOutputPin* pPin = m_pActivePins.GetNext(pos);
int count = pPin->QueueCount();
int size = pPin->QueueSize();
if (!pPin->IsDiscontinuous() && (count < MINPACKETS || size < MINPACKETSIZE)) {
// if (m_priority != THREAD_PRIORITY_ABOVE_NORMAL && (count < MINPACKETS/3 || size < MINPACKETSIZE/3))
if (m_priority != THREAD_PRIORITY_BELOW_NORMAL && (count < MINPACKETS / 3 || size < MINPACKETSIZE / 3)) {
// SetThreadPriority(m_hThread, m_priority = THREAD_PRIORITY_ABOVE_NORMAL);
POSITION pos = m_pOutputs.GetHeadPosition();
while (pos) {
m_pOutputs.GetNext(pos)->SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
}
m_priority = THREAD_PRIORITY_BELOW_NORMAL;
}
return true;
}
totalcount += count;
totalsize += size;
}
if (m_priority != THREAD_PRIORITY_NORMAL && (totalcount > m_QueueMaxPackets * 2 / 3 || totalsize > MAXPACKETSIZE * 2 / 3)) {
// SetThreadPriority(m_hThread, m_priority = THREAD_PRIORITY_NORMAL);
POSITION pos = m_pOutputs.GetHeadPosition();
while (pos) {
m_pOutputs.GetNext(pos)->SetThreadPriority(THREAD_PRIORITY_NORMAL);
}
m_priority = THREAD_PRIORITY_NORMAL;
}
if (totalcount < m_QueueMaxPackets && totalsize < MAXPACKETSIZE) {
return true;
}
return false;
}
HRESULT CBaseSplitterFilter::BreakConnect(PIN_DIRECTION dir, CBasePin* pPin)
{
CheckPointer(pPin, E_POINTER);
if (dir == PINDIR_INPUT) {
DeleteOutputs();
} else if (dir == PINDIR_OUTPUT) {
} else {
return E_UNEXPECTED;
}
return S_OK;
}
HRESULT CBaseSplitterFilter::CompleteConnect(PIN_DIRECTION dir, CBasePin* pPin)
{
CheckPointer(pPin, E_POINTER);
if (dir == PINDIR_INPUT) {
CBaseSplitterInputPin* pIn = static_cast(pPin);
HRESULT hr;
CComPtr pAsyncReader;
if (FAILED(hr = pIn->GetAsyncReader(&pAsyncReader))
|| FAILED(hr = DeleteOutputs())
|| FAILED(hr = CreateOutputs(pAsyncReader))) {
return hr;
}
ChapSort();
m_pSyncReader = pAsyncReader;
} else if (dir == PINDIR_OUTPUT) {
m_pRetiredOutputs.RemoveAll();
} else {
return E_UNEXPECTED;
}
return S_OK;
}
int CBaseSplitterFilter::GetPinCount()
{
return (m_pInput ? 1 : 0) + (int)m_pOutputs.GetCount();
}
CBasePin* CBaseSplitterFilter::GetPin(int n)
{
CAutoLock cAutoLock(this);
if (n >= 0 && n < (int)m_pOutputs.GetCount()) {
if (POSITION pos = m_pOutputs.FindIndex(n)) {
return m_pOutputs.GetAt(pos);
}
}
if (n == (int)m_pOutputs.GetCount() && m_pInput) {
return m_pInput;
}
return nullptr;
}
STDMETHODIMP CBaseSplitterFilter::Stop()
{
CAutoLock cAutoLock(this);
DeliverBeginFlush();
CallWorker(CMD_EXIT);
DeliverEndFlush();
HRESULT hr;
if (FAILED(hr = __super::Stop())) {
return hr;
}
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::Pause()
{
CAutoLock cAutoLock(this);
FILTER_STATE fs = m_State;
HRESULT hr;
if (FAILED(hr = __super::Pause())) {
return hr;
}
if (fs == State_Stopped) {
Create();
}
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::Run(REFERENCE_TIME tStart)
{
CAutoLock cAutoLock(this);
HRESULT hr;
if (FAILED(hr = __super::Run(tStart))) {
return hr;
}
return S_OK;
}
// IFileSourceFilter
STDMETHODIMP CBaseSplitterFilter::Load(LPCOLESTR pszFileName, const AM_MEDIA_TYPE* pmt)
{
CheckPointer(pszFileName, E_POINTER);
m_fn = pszFileName;
HRESULT hr = E_FAIL;
CComPtr pAsyncReader;
CAtlList Items;
CAtlList Chapters;
if (BuildPlaylist(pszFileName, Items)) {
pAsyncReader = (IAsyncReader*)DEBUG_NEW CAsyncFileReader(Items, hr);
} else {
pAsyncReader = (IAsyncReader*)DEBUG_NEW CAsyncFileReader(CString(pszFileName), hr);
}
if (FAILED(hr)
|| FAILED(hr = DeleteOutputs())
|| FAILED(hr = CreateOutputs(pAsyncReader))) {
m_fn = "";
return hr;
}
if (BuildChapters(pszFileName, Items, Chapters)) {
POSITION pos = Chapters.GetHeadPosition();
int i = 1;
while (pos) {
CString str;
CHdmvClipInfo::PlaylistChapter& chap = Chapters.GetNext(pos);
if (chap.m_nMarkType == CHdmvClipInfo::EntryMark) {
str.Format(_T("Chapter %d"), i);
ChapAppend(chap.m_rtTimestamp, str);
i++;
}
}
}
ChapSort();
m_pSyncReader = pAsyncReader;
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::GetCurFile(LPOLESTR* ppszFileName, AM_MEDIA_TYPE* pmt)
{
CheckPointer(ppszFileName, E_POINTER);
*ppszFileName = (LPOLESTR)CoTaskMemAlloc((m_fn.GetLength() + 1) * sizeof(WCHAR));
if (!(*ppszFileName)) {
return E_OUTOFMEMORY;
}
wcscpy_s(*ppszFileName, m_fn.GetLength() + 1, m_fn);
return S_OK;
}
LPCTSTR CBaseSplitterFilter::GetPartFilename(IAsyncReader* pAsyncReader)
{
CComQIPtr pFH = pAsyncReader;
return pFH ? pFH->GetFileName() : (LPCWSTR)m_fn;
}
// IMediaSeeking
STDMETHODIMP CBaseSplitterFilter::GetCapabilities(DWORD* pCapabilities)
{
return pCapabilities ? *pCapabilities =
AM_SEEKING_CanGetStopPos |
AM_SEEKING_CanGetDuration |
AM_SEEKING_CanSeekAbsolute |
AM_SEEKING_CanSeekForwards |
AM_SEEKING_CanSeekBackwards, S_OK : E_POINTER;
}
STDMETHODIMP CBaseSplitterFilter::CheckCapabilities(DWORD* pCapabilities)
{
CheckPointer(pCapabilities, E_POINTER);
if (*pCapabilities == 0) {
return S_OK;
}
DWORD caps;
GetCapabilities(&caps);
if ((caps&*pCapabilities) == 0) {
return E_FAIL;
}
if (caps == *pCapabilities) {
return S_OK;
}
return S_FALSE;
}
STDMETHODIMP CBaseSplitterFilter::IsFormatSupported(const GUID* pFormat)
{
return !pFormat ? E_POINTER : *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
}
STDMETHODIMP CBaseSplitterFilter::QueryPreferredFormat(GUID* pFormat)
{
return GetTimeFormat(pFormat);
}
STDMETHODIMP CBaseSplitterFilter::GetTimeFormat(GUID* pFormat)
{
return pFormat ? *pFormat = TIME_FORMAT_MEDIA_TIME, S_OK : E_POINTER;
}
STDMETHODIMP CBaseSplitterFilter::IsUsingTimeFormat(const GUID* pFormat)
{
return IsFormatSupported(pFormat);
}
STDMETHODIMP CBaseSplitterFilter::SetTimeFormat(const GUID* pFormat)
{
return S_OK == IsFormatSupported(pFormat) ? S_OK : E_INVALIDARG;
}
STDMETHODIMP CBaseSplitterFilter::GetDuration(LONGLONG* pDuration)
{
CheckPointer(pDuration, E_POINTER);
*pDuration = m_rtDuration;
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::GetStopPosition(LONGLONG* pStop)
{
return GetDuration(pStop);
}
STDMETHODIMP CBaseSplitterFilter::GetCurrentPosition(LONGLONG* pCurrent)
{
return E_NOTIMPL;
}
STDMETHODIMP CBaseSplitterFilter::ConvertTimeFormat(LONGLONG* pTarget, const GUID* pTargetFormat, LONGLONG Source, const GUID* pSourceFormat)
{
return E_NOTIMPL;
}
STDMETHODIMP CBaseSplitterFilter::SetPositions(LONGLONG* pCurrent, DWORD dwCurrentFlags, LONGLONG* pStop, DWORD dwStopFlags)
{
return SetPositionsInternal(this, pCurrent, dwCurrentFlags, pStop, dwStopFlags);
}
STDMETHODIMP CBaseSplitterFilter::GetPositions(LONGLONG* pCurrent, LONGLONG* pStop)
{
if (pCurrent) {
*pCurrent = m_rtCurrent;
}
if (pStop) {
*pStop = m_rtStop;
}
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::GetAvailable(LONGLONG* pEarliest, LONGLONG* pLatest)
{
if (pEarliest) {
*pEarliest = 0;
}
return GetDuration(pLatest);
}
STDMETHODIMP CBaseSplitterFilter::SetRate(double dRate)
{
return dRate > 0 ? m_dRate = dRate, S_OK : E_INVALIDARG;
}
STDMETHODIMP CBaseSplitterFilter::GetRate(double* pdRate)
{
return pdRate ? *pdRate = m_dRate, S_OK : E_POINTER;
}
STDMETHODIMP CBaseSplitterFilter::GetPreroll(LONGLONG* pllPreroll)
{
return pllPreroll ? *pllPreroll = 0, S_OK : E_POINTER;
}
HRESULT CBaseSplitterFilter::SetPositionsInternal(void* id, LONGLONG* pCurrent, DWORD dwCurrentFlags, LONGLONG* pStop, DWORD dwStopFlags)
{
CAutoLock cAutoLock(this);
if (!pCurrent && !pStop
|| (dwCurrentFlags & AM_SEEKING_PositioningBitsMask) == AM_SEEKING_NoPositioning
&& (dwStopFlags & AM_SEEKING_PositioningBitsMask) == AM_SEEKING_NoPositioning) {
return S_OK;
}
REFERENCE_TIME rtCurrent = m_rtCurrent;
REFERENCE_TIME rtStop = m_rtStop;
if (pCurrent)
switch (dwCurrentFlags & AM_SEEKING_PositioningBitsMask) {
case AM_SEEKING_NoPositioning:
break;
case AM_SEEKING_AbsolutePositioning:
rtCurrent = *pCurrent;
break;
case AM_SEEKING_RelativePositioning:
rtCurrent = rtCurrent + *pCurrent;
break;
case AM_SEEKING_IncrementalPositioning:
rtCurrent = rtCurrent + *pCurrent;
break;
}
if (pStop)
switch (dwStopFlags & AM_SEEKING_PositioningBitsMask) {
case AM_SEEKING_NoPositioning:
break;
case AM_SEEKING_AbsolutePositioning:
rtStop = *pStop;
break;
case AM_SEEKING_RelativePositioning:
rtStop += *pStop;
break;
case AM_SEEKING_IncrementalPositioning:
rtStop = rtCurrent + *pStop;
break;
}
if (m_rtCurrent == rtCurrent && m_rtStop == rtStop) {
return S_OK;
}
if (m_rtLastStart == rtCurrent && m_rtLastStop == rtStop && !m_LastSeekers.Find(id)) {
m_LastSeekers.AddTail(id);
return S_OK;
}
m_rtLastStart = rtCurrent;
m_rtLastStop = rtStop;
m_LastSeekers.RemoveAll();
m_LastSeekers.AddTail(id);
DbgLog((LOG_TRACE, 0, _T("Seek Started %I64d"), rtCurrent));
m_rtNewStart = m_rtCurrent = rtCurrent;
m_rtNewStop = rtStop;
if (ThreadExists()) {
DeliverBeginFlush();
CallWorker(CMD_SEEK);
DeliverEndFlush();
}
DbgLog((LOG_TRACE, 0, _T("Seek Ended")));
return S_OK;
}
// IAMOpenProgress
STDMETHODIMP CBaseSplitterFilter::QueryProgress(LONGLONG* pllTotal, LONGLONG* pllCurrent)
{
CheckPointer(pllTotal, E_POINTER);
CheckPointer(pllCurrent, E_POINTER);
*pllTotal = 100;
*pllCurrent = m_nOpenProgress;
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::AbortOperation()
{
m_fAbort = true;
return S_OK;
}
// IAMMediaContent
STDMETHODIMP CBaseSplitterFilter::get_AuthorName(BSTR* pbstrAuthorName)
{
return GetProperty(L"AUTH", pbstrAuthorName);
}
STDMETHODIMP CBaseSplitterFilter::get_Title(BSTR* pbstrTitle)
{
return GetProperty(L"TITL", pbstrTitle);
}
STDMETHODIMP CBaseSplitterFilter::get_Rating(BSTR* pbstrRating)
{
return GetProperty(L"RTNG", pbstrRating);
}
STDMETHODIMP CBaseSplitterFilter::get_Description(BSTR* pbstrDescription)
{
return GetProperty(L"DESC", pbstrDescription);
}
STDMETHODIMP CBaseSplitterFilter::get_Copyright(BSTR* pbstrCopyright)
{
return GetProperty(L"CPYR", pbstrCopyright);
}
// IAMExtendedSeeking
STDMETHODIMP CBaseSplitterFilter::get_ExSeekCapabilities(long* pExCapabilities)
{
CheckPointer(pExCapabilities, E_POINTER);
*pExCapabilities = AM_EXSEEK_CANSEEK;
if (ChapGetCount()) {
*pExCapabilities |= AM_EXSEEK_MARKERSEEK;
}
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::get_MarkerCount(long* pMarkerCount)
{
CheckPointer(pMarkerCount, E_POINTER);
*pMarkerCount = (long)ChapGetCount();
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::get_CurrentMarker(long* pCurrentMarker)
{
CheckPointer(pCurrentMarker, E_POINTER);
REFERENCE_TIME rt = m_rtCurrent;
long i = ChapLookup(&rt);
if (i < 0) {
return E_FAIL;
}
*pCurrentMarker = i + 1;
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::GetMarkerTime(long MarkerNum, double* pMarkerTime)
{
CheckPointer(pMarkerTime, E_POINTER);
REFERENCE_TIME rt;
if (FAILED(ChapGet((int)MarkerNum - 1, &rt))) {
return E_FAIL;
}
*pMarkerTime = (double)rt / 10000000;
return S_OK;
}
STDMETHODIMP CBaseSplitterFilter::GetMarkerName(long MarkerNum, BSTR* pbstrMarkerName)
{
return ChapGet((int)MarkerNum - 1, nullptr, pbstrMarkerName);
}
// IKeyFrameInfo
STDMETHODIMP CBaseSplitterFilter::GetKeyFrameCount(UINT& nKFs)
{
return E_NOTIMPL;
}
STDMETHODIMP CBaseSplitterFilter::GetKeyFrames(const GUID* pFormat, REFERENCE_TIME* pKFs, UINT& nKFs)
{
return E_NOTIMPL;
}
// IBufferInfo
STDMETHODIMP_(int) CBaseSplitterFilter::GetCount()
{
CAutoLock cAutoLock(m_pLock);
return (int)m_pOutputs.GetCount();
}
STDMETHODIMP CBaseSplitterFilter::GetStatus(int i, int& samples, int& size)
{
CAutoLock cAutoLock(m_pLock);
if (POSITION pos = m_pOutputs.FindIndex(i)) {
CBaseSplitterOutputPin* pPin = m_pOutputs.GetAt(pos);
samples = pPin->QueueCount();
size = pPin->QueueSize();
return pPin->IsConnected() ? S_OK : S_FALSE;
}
return E_INVALIDARG;
}
STDMETHODIMP_(DWORD) CBaseSplitterFilter::GetPriority()
{
return m_priority;
}