DirectShow Source Filter for Video File Decoding Using Media Foundation

The filter leverages the Media Foundation Source Reader to parse and decode various multimedia containers (3GP, ASF, AVI, MKV, MOV, MP4, WMV). It outputs uncompressed RGB32 video and PCM audio through dedicated output pins. The primary filter class inherits from CSource, alongside IFileSourceFilter for file path specification and IMediaSeeking for playback position control.

Upon successfully loading a file via IFileSourceFilter::Load, the filter initializes an IMFSourceReader using MFCreateSourceReaderFromURL. By explicitly configuring the source reader's media types to MFVideoFormat_RGB32 and MFAudioFormat_PCM, the Media Foundation pipeline internally constructs the necessary decoders. The output pin classes, derived from CSourceStream, implement the FillBuffer method by invoking IMFSourceReader::ReadSample. The retrieved IMFSample is then mapped to a DirectShow IMediaSample and delivered downstream.

Media type translation is required for compatibility. The source reader exposes video formats as FORMAT_VideoInfo2, which must be manually converted to FORMAT_VideoInfo for the DirectShow video pin. Audio media types can be directly copied. Note that decoding capabilities depend on the Media Foundation Transforms (MFTs) registered on the host system; unsupported codecs will prevent file reading.

Filter Identification

  • Class ID: {B2A9C4D8-5E3F-4A1B-9012-3456789ABCDE}
  • Registration: DllRegisterServer / DllUnregisterServer
  • Output Pins:
    • Video: Major MEDIATYPE_Video, Sub MEDIASUBTYPE_RGB32, Format FORMAT_VideoInfo
    • Audio: Major MEDIATYPE_Audio, Sub MEDIASUBTYPE_PCM, Format FORMAT_WaveFormatEx

CoreDefs.h

#pragma once

#include <windows.h>
#include <dshow.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <initguid.h>
#include <strsafe.h>

// Base class definitions adapted from Windows SDK 7.1
#include "Strmbase10.h"
#pragma comment(lib, "Strmbase10")

#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfuuid")

template <class T> void ReleaseCOM(T** ppT) {
    if (*ppT) {
        (*ppT)->Release();
        *ppT = nullptr;
    }
}

// {B2A9C4D8-5E3F-4A1B-9012-3456789ABCDE}
DEFINE_GUID(CLSID_MFVideoReader,
    0xb2a9c4d8, 0x5e3f, 0x4a1b, 0x90, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde);

FilterDllEntry.cpp

#include "CoreDefs.h"
#include "MFSourceFilter.h"

const AMOVIESETUP_MEDIATYPE sudVideoPinTypes = {
    &MEDIATYPE_Video,
    &MEDIASUBTYPE_RGB32
};

const AMOVIESETUP_MEDIATYPE sudAudioPinTypes = {
    &MEDIATYPE_Audio,
    &MEDIASUBTYPE_PCM
};

const AMOVIESETUP_PIN sudOutputPins[] = {
    {
        L"Video Output",
        FALSE, TRUE, FALSE, FALSE,
        &CLSID_NULL, NULL,
        1, &sudVideoPinTypes
    },
    {
        L"Audio Output",
        FALSE, TRUE, FALSE, FALSE,
        &CLSID_NULL, NULL,
        1, &sudAudioPinTypes
    }
};

const AMOVIESETUP_FILTER MFVideoReaderSetup = {
    &CLSID_MFVideoReader,
    L"MF Video File Reader",
    MERIT_DO_NOT_USE,
    2,
    sudOutputPins
};

CFactoryTemplate g_Templates[] = {
    {
        L"MF Video File Reader",
        &CLSID_MFVideoReader,
        CMFSourceFilter::CreateInstance,
        NULL,
        &MFVideoReaderSetup
    }
};

int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

STDAPI DllRegisterServer() {
    return AMovieDllRegisterServer2(TRUE);
}

STDAPI DllUnregisterServer() {
    return AMovieDllRegisterServer2(FALSE);
}

extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) {
    return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}

MFSourceFilter.h

#pragma once
#include "CoreDefs.h"
#include "VideoOutputPin.h"
#include "AudioOutputPin.h"

class CMFSourceFilter : public CSource, public IFileSourceFilter, public IMediaSeeking {
    friend class CVideoOutputPin;
    friend class CAudioOutputPin;
public:
    CMFSourceFilter(LPUNKNOWN pUnk, HRESULT *phr);
    ~CMFSourceFilter();
    static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
    DECLARE_IUNKNOWN;

    STDMETHODIMP Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt);
    STDMETHODIMP GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt);

    IMFSourceReader* m_pSourceReader = nullptr;
    HRESULT InitializeSourceReader();

    CVideoOutputPin* m_pVideoPin;
    CAudioOutputPin* m_pAudioPin;
    LPWSTR m_pFilePath;

    HANDLE m_hSeekEvent;
    LONGLONG m_videoSeekOffset;
    LONGLONG m_audioSeekOffset;
    LONGLONG m_mediaDuration;
    LONGLONG m_currentAudioTime;

    IMFMediaType* m_pAudioMediaType;
    IMFMediaType* m_pVideoMediaType;

private:
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);

public:
    HRESULT STDMETHODCALLTYPE CheckCapabilities(DWORD *pCapabilities);
    HRESULT STDMETHODCALLTYPE ConvertTimeFormat(LONGLONG *pTarget, const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat);
    HRESULT STDMETHODCALLTYPE GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest);
    HRESULT STDMETHODCALLTYPE GetCapabilities(DWORD *pCapabilities);
    HRESULT STDMETHODCALLTYPE GetCurrentPosition(LONGLONG *pCurrent);
    HRESULT STDMETHODCALLTYPE GetDuration(LONGLONG *pDuration);
    HRESULT STDMETHODCALLTYPE GetPositions(LONGLONG *pCurrent, LONGLONG *pStop);
    HRESULT STDMETHODCALLTYPE GetPreroll(LONGLONG *pllPreroll);
    HRESULT STDMETHODCALLTYPE GetRate(double *pdRate);
    HRESULT STDMETHODCALLTYPE GetStopPosition(LONGLONG *pStop);
    HRESULT STDMETHODCALLTYPE GetTimeFormat(GUID *pFormat);
    HRESULT STDMETHODCALLTYPE IsFormatSupported(const GUID *pFormat);
    HRESULT STDMETHODCALLTYPE IsUsingTimeFormat(const GUID *pFormat);
    HRESULT STDMETHODCALLTYPE QueryPreferredFormat(GUID *pFormat);
    HRESULT STDMETHODCALLTYPE SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags);
    HRESULT STDMETHODCALLTYPE SetRate(double dRate);
    HRESULT STDMETHODCALLTYPE SetTimeFormat(const GUID *pFormat);
};

MFSourceFilter.cpp

#include "MFSourceFilter.h"
#include "VideoOutputPin.h"
#include "AudioOutputPin.h"

CMFSourceFilter::CMFSourceFilter(LPUNKNOWN pUnk, HRESULT *phr) 
    : CSource(NAME("MF Video File Reader"), pUnk, CLSID_MFVideoReader) {
    m_pVideoPin = new CVideoOutputPin(phr, this, L"Video Out");
    m_pAudioPin = new CAudioOutputPin(phr, this, L"Audio Out");
    m_hSeekEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    m_pFilePath = nullptr; 
    m_mediaDuration = 0; 
    m_currentAudioTime = 0;
    m_pVideoMediaType = nullptr; 
    m_pAudioMediaType = nullptr;
    m_videoSeekOffset = 0; 
    m_audioSeekOffset = 0;
    
    if (MFStartup(MF_VERSION) != S_OK) {
        MessageBox(NULL, L"Media Foundation initialization failed", L"Error", MB_OK);
    }
}

CMFSourceFilter::~CMFSourceFilter() {
    CloseHandle(m_hSeekEvent);
    ReleaseCOM(&m_pAudioMediaType);
    ReleaseCOM(&m_pVideoMediaType);
    ReleaseCOM(&m_pSourceReader);
    if (m_pFilePath) delete[] m_pFilePath;
    MFShutdown();
}

STDMETHODIMP CMFSourceFilter::NonDelegatingQueryInterface(REFIID riid, void ** ppv) {
    if (riid == IID_IMediaSeeking) {
        return GetInterface(static_cast<IMediaSeeking*>(this), ppv);
    } else if (riid == IID_IFileSourceFilter) {
        return GetInterface(static_cast<IFileSourceFilter*>(this), ppv);
    }
    return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
}

CUnknown * WINAPI CMFSourceFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) {
    return new CMFSourceFilter(pUnk, phr);
}

STDMETHODIMP CMFSourceFilter::Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt) {
    CheckPointer(lpwszFileName, E_POINTER);
    size_t pathLen = wcslen(lpwszFileName);
    if (pathLen > MAX_PATH || pathLen < 5) return ERROR_FILENAME_EXCED_RANGE;

    size_t allocLen = pathLen + 1;
    m_pFilePath = new WCHAR[allocLen];
    if (!m_pFilePath) return E_OUTOFMEMORY;
    StringCchCopyW(m_pFilePath, allocLen, lpwszFileName);

    // Extract extension
    WCHAR ext[5] = { 
        m_pFilePath[pathLen - 4], m_pFilePath[pathLen - 3], 
        m_pFilePath[pathLen - 2], m_pFilePath[pathLen - 1], 0 
    };
    
    const WCHAR* validExts[] = { L".3gp", L".asf", L".avi", L".mkv", L".mov", L".mp4", L".wmv" };
    bool isValid = false;
    for (int i = 0; i < _countof(validExts); ++i) {
        if (_wcsicmp(ext, validExts[i]) == 0) { isValid = true; break; }
    }

    if (isValid) {
        return InitializeSourceReader();
    }
    
    delete[] m_pFilePath; 
    m_pFilePath = nullptr;
    return VFW_E_INVALID_FILE_FORMAT;
}

STDMETHODIMP CMFSourceFilter::GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt) {
    CheckPointer(ppszFileName, E_POINTER);
    *ppszFileName = nullptr;
    if (m_pFilePath) {
        DWORD size = sizeof(WCHAR) * (wcslen(m_pFilePath) + 1);
        *ppszFileName = (LPOLESTR)CoTaskMemAlloc(size);
        if (*ppszFileName) CopyMemory(*ppszFileName, m_pFilePath, size);
    }
    return S_OK;
}

HRESULT CMFSourceFilter::InitializeSourceReader() {
    ReleaseCOM(&m_pSourceReader);
    IMFAttributes* pAttrs = nullptr;
    HRESULT hr = MFCreateAttributes(&pAttrs, 2);
    if (SUCCEEDED(hr)) {
        hr = pAttrs->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1);
    }
    if (SUCCEEDED(hr)) {
        hr = pAttrs->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, 1);
    }
    if (SUCCEEDED(hr)) {
        hr = MFCreateSourceReaderFromURL(m_pFilePath, pAttrs, &m_pSourceReader);
    }
    ReleaseCOM(&pAttrs);
    
    if (FAILED(hr)) {
        MessageBox(NULL, L"Source Reader creation failed", L"Error", MB_OK);
        return S_FALSE;
    }

    IMFMediaType* pTargetAudioType = nullptr;
    if (SUCCEEDED(hr)) hr = MFCreateMediaType(&pTargetAudioType);
    if (SUCCEEDED(hr)) hr = pTargetAudioType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
    if (SUCCEEDED(hr)) hr = pTargetAudioType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
    if (SUCCEEDED(hr)) hr = m_pSourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, nullptr, pTargetAudioType);
    ReleaseCOM(&pTargetAudioType);

    IMFMediaType* pTargetVideoType = nullptr;
    if (SUCCEEDED(hr)) hr = MFCreateMediaType(&pTargetVideoType);
    if (SUCCEEDED(hr)) hr = pTargetVideoType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    if (SUCCEEDED(hr)) hr = pTargetVideoType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
    if (SUCCEEDED(hr)) hr = m_pSourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr, pTargetVideoType);
    ReleaseCOM(&pTargetVideoType);

    if (SUCCEEDED(hr)) hr = m_pSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &m_pAudioMediaType);
    if (SUCCEEDED(hr)) hr = m_pSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, &m_pVideoMediaType);

    PROPVARIANT propVar;
    if (SUCCEEDED(hr)) {
        PropVariantInit(&propVar);
        hr = m_pSourceReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &propVar);
    }
    if (SUCCEEDED(hr)) {
        m_mediaDuration = propVar.uhVal.QuadPart;
        PropVariantClear(&propVar);
    }
    return hr;
}

HRESULT STDMETHODCALLTYPE CMFSourceFilter::CheckCapabilities(DWORD *pCapabilities) {
    if (!pCapabilities) return E_POINTER;
    if (*pCapabilities == (AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetDuration)) return S_OK;
    if (*pCapabilities & (AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetDuration)) return S_FALSE;
    return E_FAIL;
}

HRESULT STDMETHODCALLTYPE CMFSourceFilter::ConvertTimeFormat(LONGLONG *pTarget, const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE CMFSourceFilter::GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest) { return E_NOTIMPL; }

HRESULT STDMETHODCALLTYPE CMFSourceFilter::GetCapabilities(DWORD *pCapabilities) {
    *pCapabilities = AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetDuration;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CMFSourceFilter::GetCurrentPosition(LONGLONG *pCurrent) {
    *pCurrent = m_currentAudioTime;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CMFSourceFilter::GetDuration(LONGLONG *pDuration) {
    if (!m_pSourceReader) return S_FALSE;
    *pDuration = m_mediaDuration;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CMFSourceFilter::GetPositions(LONGLONG *pCurrent, LONGLONG *pStop) {
    if (!m_pSourceReader) return S_FALSE;
    *pCurrent = m_currentAudioTime;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CMFSourceFilter::GetPreroll(LONGLONG *pllPreroll) { *pllPreroll = 0; return S_OK; }
HRESULT STDMETHODCALLTYPE CMFSourceFilter::GetRate(double *pdRate) { *pdRate = 1.0; return S_OK; }
HRESULT STDMETHODCALLTYPE CMFSourceFilter::GetStopPosition(LONGLONG *pStop) { *pStop = m_mediaDuration - m_audioSeekOffset; return S_OK; }
HRESULT STDMETHODCALLTYPE CMFSourceFilter::GetTimeFormat(GUID *pFormat) { if(!pFormat) return E_POINTER; *pFormat = TIME_FORMAT_MEDIA_TIME; return S_OK; }
HRESULT STDMETHODCALLTYPE CMFSourceFilter::IsFormatSupported(const GUID *pFormat) { return (*pFormat == TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE; }
HRESULT STDMETHODCALLTYPE CMFSourceFilter::IsUsingTimeFormat(const GUID *pFormat) { return (*pFormat == TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE; }
HRESULT STDMETHODCALLTYPE CMFSourceFilter::QueryPreferredFormat(GUID *pFormat) { if(!pFormat) return E_POINTER; *pFormat = TIME_FORMAT_MEDIA_TIME; return S_OK; }

HRESULT STDMETHODCALLTYPE CMFSourceFilter::SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags) {
    if (!m_pSourceReader) return S_FALSE;
    if (dwCurrentFlags & AM_SEEKING_AbsolutePositioning) {
        m_videoSeekOffset = *pCurrent;
        m_audioSeekOffset = *pCurrent;
        *pStop = m_mediaDuration - *pCurrent;
        
        m_pVideoPin->DeliverBeginFlush();
        m_pVideoPin->Stop();
        m_pVideoPin->DeliverEndFlush();
        m_pVideoPin->Run();
        
        SetEvent(m_hSeekEvent);
        return S_OK;
    }
    return S_FALSE;
}

HRESULT STDMETHODCALLTYPE CMFSourceFilter::SetRate(double dRate) { return (dRate == 1.0) ? S_OK : S_FALSE; }
HRESULT STDMETHODCALLTYPE CMFSourceFilter::SetTimeFormat(const GUID *pFormat) { return (*pFormat == TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE; }

VideoOutputPin.h

#pragma once
#include "CoreDefs.h"
#include "MFSourceFilter.h"

class CVideoOutputPin : public CSourceStream {
    friend class CMFSourceFilter;
public:
    CVideoOutputPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName);
    ~CVideoOutputPin();

    HRESULT GetMediaType(CMediaType *pmt);
    HRESULT DecideBufferSize(IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pRequest);
    HRESULT FillBuffer(IMediaSample *pms);
    STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
    HRESULT OnThreadStartPlay(void);

    CMFSourceFilter* m_pParentFilter;
    LONG m_bufferSize;
};

VideoOutputPin.cpp

#include "VideoOutputPin.h"

CVideoOutputPin::CVideoOutputPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName) 
    : CSourceStream(NAME("Video Out"), phr, pParent, pPinName) {
    m_pParentFilter = (CMFSourceFilter*)pParent;
}

CVideoOutputPin::~CVideoOutputPin() {}

HRESULT CVideoOutputPin::GetMediaType(CMediaType *pmt) {
    if (!m_pParentFilter->m_pSourceReader) return S_FALSE;
    
    AM_MEDIA_TYPE* pAmMt = nullptr;
    m_pParentFilter->m_pVideoMediaType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pAmMt);
    
    if (pAmMt->formattype != FORMAT_VideoInfo2) {
        MessageBox(0, L"Source reader video format mismatch", 0, MB_OK);
        m_pParentFilter->m_pVideoMediaType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pAmMt);
        return S_FALSE;
    }

    VIDEOINFOHEADER2* pVi2 = (VIDEOINFOHEADER2*)(pAmMt->pbFormat);
    pmt->SetType(&MEDIATYPE_Video);
    pmt->SetSubtype(&MEDIASUBTYPE_RGB32);
    pmt->SetFormatType(&FORMAT_VideoInfo);
    pmt->SetTemporalCompressed(FALSE);

    VIDEOINFOHEADER* pVi = (VIDEOINFOHEADER*)pmt->AllocFormatBuffer(sizeof(VIDEOINFOHEADER));
    pVi->rcSource = pVi2->rcSource;
    pVi->rcTarget = pVi2->rcTarget;
    pVi->dwBitRate = pVi2->dwBitRate;
    pVi->dwBitErrorRate = pVi2->dwBitErrorRate;
    pVi->AvgTimePerFrame = pVi2->AvgTimePerFrame;

    pVi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pVi->bmiHeader.biWidth = pVi2->bmiHeader.biWidth;
    pVi->bmiHeader.biHeight = pVi2->bmiHeader.biHeight;
    pVi->bmiHeader.biPlanes = 1;
    pVi->bmiHeader.biBitCount = 32;
    pVi->bmiHeader.biCompression = BI_RGB;
    
    m_bufferSize = pVi2->bmiHeader.biWidth * abs(pVi2->bmiHeader.biHeight) * 4;
    pVi->bmiHeader.biSizeImage = m_bufferSize;
    pVi->bmiHeader.biXPelsPerMeter = 0;
    pVi->bmiHeader.biYPelsPerMeter = 0;
    pVi->bmiHeader.biClrUsed = 0;
    pVi->bmiHeader.biClrImportant = 0;

    pmt->SetSampleSize(m_bufferSize);
    m_pParentFilter->m_pVideoMediaType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pAmMt);
    return S_OK;
}

HRESULT CVideoOutputPin::DecideBufferSize(IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pRequest) {
    if (!m_pParentFilter->m_pSourceReader) return S_FALSE;
    pRequest->cBuffers = 1;
    pRequest->cbBuffer = m_bufferSize;
    
    ALLOCATOR_PROPERTIES actual;
    HRESULT hr = pAlloc->SetProperties(pRequest, &actual);
    if (FAILED(hr)) return hr;
    if (actual.cbBuffer < pRequest->cbBuffer) return E_FAIL;
    return NOERROR;
}

HRESULT CVideoOutputPin::FillBuffer(IMediaSample *pms) {
    BYTE *pBuffer = nullptr;
    pms->GetPointer(&pBuffer);
    long bufLen = pms->GetSize();
    pms->SetSyncPoint(TRUE);
    pms->SetDiscontinuity(FALSE);

    DWORD streamIndex, streamFlags;
    LONGLONG timestamp;
    IMFSample* pMfSample = nullptr;
    IMFMediaBuffer* pMfBuffer = nullptr;

    // Read loop replaces goto logic
    while (true) {
        HRESULT hr = m_pParentFilter->m_pSourceReader->ReadSample(
            MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &streamIndex, &streamFlags, &timestamp, &pMfSample);
        
        if (streamFlags & MF_SOURCE_READERF_ENDOFSTREAM) return S_FALSE;
        
        if (pMfSample) break;
        Sleep(1);
    }

    LONGLONG sampleTime, sampleDuration;
    pMfSample->GetSampleTime(&sampleTime);
    pMfSample->GetSampleDuration(&sampleDuration);
    
    LONGLONG endTime = sampleTime + sampleDuration;
    sampleTime -= m_pParentFilter->m_videoSeekOffset;
    endTime -= m_pParentFilter->m_videoSeekOffset;

    DWORD dataLength;
    pMfSample->GetTotalLength(&dataLength);
    pMfSample->GetBufferByIndex(0, &pMfBuffer);

    BYTE* pSrcData = nullptr;
    pMfBuffer->Lock(&pSrcData, nullptr, nullptr);
    CopyMemory(pBuffer, pSrcData, bufLen);
    
    pms->SetTime(&sampleTime, &endTime);
    pms->SetActualDataLength(bufLen);

    pMfBuffer->Unlock();
    ReleaseCOM(&pMfBuffer);
    ReleaseCOM(&pMfSample);
    return S_OK;
}

STDMETHODIMP CVideoOutputPin::Notify(IBaseFilter * pSender, Quality q) { return NOERROR; }

HRESULT CVideoOutputPin::OnThreadStartPlay(void) {
    PROPVARIANT posVar;
    PropVariantInit(&posVar);
    posVar.vt = VT_I8;
    posVar.hVal.QuadPart = m_pParentFilter->m_videoSeekOffset;
    
    m_pParentFilter->m_pSourceReader->SetCurrentPosition(GUID_NULL, posVar);
    PropVariantClear(&posVar);
    
    return DeliverNewSegment(0, m_pParentFilter->m_mediaDuration - m_pParentFilter->m_videoSeekOffset, 1.0);
}

AudioOutputPin.h

#pragma once
#include "CoreDefs.h"
#include "MFSourceFilter.h"

class CAudioOutputPin : public CSourceStream {
    friend class CMFSourceFilter;
public:
    CAudioOutputPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName);
    ~CAudioOutputPin();

    HRESULT GetMediaType(CMediaType *pmt);
    HRESULT DecideBufferSize(IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pRequest);
    HRESULT FillBuffer(IMediaSample *pms);
    STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);

    CMFSourceFilter* m_pParentFilter;
};

AudioOutputPin.cpp

#include "AudioOutputPin.h"

CAudioOutputPin::CAudioOutputPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName) 
    : CSourceStream(NAME("Audio Out"), phr, pParent, pPinName) {
    m_pParentFilter = (CMFSourceFilter*)pParent;
}

CAudioOutputPin::~CAudioOutputPin() {}

HRESULT CAudioOutputPin::GetMediaType(CMediaType *pmt) {
    if (!m_pParentFilter->m_pSourceReader) return S_FALSE;
    
    AM_MEDIA_TYPE* pAmMt = nullptr;
    m_pParentFilter->m_pAudioMediaType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pAmMt);
    pmt->Set(*pAmMt);
    m_pParentFilter->m_pAudioMediaType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pAmMt);
    return S_OK;
}

HRESULT CAudioOutputPin::DecideBufferSize(IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pRequest) {
    if (!m_pParentFilter->m_pSourceReader) return S_FALSE;
    pRequest->cBuffers = 1;
    pRequest->cbBuffer = 1048576; // 1MB buffer
    
    ALLOCATOR_PROPERTIES actual;
    HRESULT hr = pAlloc->SetProperties(pRequest, &actual);
    if (FAILED(hr)) return hr;
    if (actual.cbBuffer < pRequest->cbBuffer) return E_FAIL;
    return NOERROR;
}

HRESULT CAudioOutputPin::FillBuffer(IMediaSample *pms) {
    BYTE *pBuffer = nullptr;
    pms->GetPointer(&pBuffer);
    long bufLen = pms->GetSize();

    if (WaitForSingleObject(m_pParentFilter->m_hSeekEvent, 0) == WAIT_OBJECT_0) {
        DeliverBeginFlush();
        Sleep(1);
        DeliverEndFlush();
        DeliverNewSegment(0, m_pParentFilter->m_mediaDuration - m_pParentFilter->m_audioSeekOffset, 1.0);
        
        PROPVARIANT seekVar;
        PropVariantInit(&seekVar);
        seekVar.vt = VT_I8;
        seekVar.hVal.QuadPart = m_pParentFilter->m_audioSeekOffset;
        m_pParentFilter->m_pSourceReader->SetCurrentPosition(GUID_NULL, seekVar);
        PropVariantClear(&seekVar);
        
        pms->SetDiscontinuity(TRUE);
    }

    DWORD streamIndex, streamFlags;
    LONGLONG timestamp;
    IMFSample* pMfSample = nullptr;
    IMFMediaBuffer* pMfBuffer = nullptr;

    // Read loop replaces goto logic
    while (true) {
        HRESULT hr = m_pParentFilter->m_pSourceReader->ReadSample(
            MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &streamIndex, &streamFlags, &timestamp, &pMfSample);
        
        if (streamFlags & MF_SOURCE_READERF_ENDOFSTREAM) return S_FALSE;
        
        if (pMfSample) break;
        Sleep(1);
    }

    LONGLONG sampleTime, sampleDuration;
    pMfSample->GetSampleTime(&sampleTime);
    m_pParentFilter->m_currentAudioTime = sampleTime;
    
    pMfSample->GetSampleDuration(&sampleDuration);
    LONGLONG endTime = sampleTime + sampleDuration;
    
    sampleTime -= m_pParentFilter->m_audioSeekOffset;
    endTime -= m_pParentFilter->m_audioSeekOffset;

    DWORD dataLength;
    pMfSample->GetTotalLength(&dataLength);
    pMfSample->GetBufferByIndex(0, &pMfBuffer);

    BYTE* pSrcData = nullptr;
    pMfBuffer->Lock(&pSrcData, nullptr, nullptr);
    
    if (dataLength <= (DWORD)bufLen) {
        CopyMemory(pBuffer, pSrcData, dataLength);
    }

    pms->SetTime(&sampleTime, &endTime);
    pms->SetActualDataLength(dataLength);
    pms->SetSyncPoint(TRUE);

    pMfBuffer->Unlock();
    ReleaseCOM(&pMfBuffer);
    ReleaseCOM(&pMfSample);
    return S_OK;
}

STDMETHODIMP CAudioOutputPin::Notify(IBaseFilter * pSender, Quality q) { return NOERROR; }

Tags: DirectShow Media Foundation Source Filter C++ Video Decoding

Posted on Sat, 05 Sep 2026 16:44:49 +0000 by mona02