Click here to Skip to main content
15,876,879 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
How can I read an additional video stream from wmv and avi file?
For wmv file I use my own directshow source filter based on the source of an existing wmv source firter.
I have tried to use IWMReaderCallbackAdvanced, IWMReaderAdvanced2.
But, I do not know, how to connect IWMReaderCallbackAdvanced and IWMReaderAdvanced2.
When using IWMReader in the function OnSample can not be reached samples from additional video streams.
The source code of existing working modules (3dtv.at filters are unavailable).
Can you help me?


Here is the code:
C++
#include "stdafx.h"
#include <initguid.h>
using namespace std;
//#define INFORMATION_MESSAGE
#undef INFORMATION_MESSAGE
#define TRY(hr, X) {hr = X; if(!SUCCEEDED(hr)) throw hr;}
// Setup data for filter registration
const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
{ 
    &MEDIATYPE_Stream,
    &MEDIASUBTYPE_NULL 
};
const AMOVIESETUP_PIN sudOpPin =
{ 
    L"Output",
    FALSE,
    TRUE,
    FALSE,
    TRUE,
    &CLSID_NULL,
    0,
    1,
    &sudOpPinTypes 
};
const AMOVIESETUP_FILTER sudAsync =
{ 
    &CLSID_ASFStereoFilter,
    L"ASF WMVStereoFilter",
    MERIT_UNLIKELY,
    1,
    &sudOpPin
};
CFactoryTemplate g_Templates[1] = 
{
    {
        L"ASF WMVStereoFilter",
        &CLSID_ASFStereoFilter,
        CEncStereoFilter::CreateInstance,
        NULL,
        &sudAsync
    }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
////////////////////////////////////////////////////////////////////////
// Exported entry points 
////////////////////////////////////////////////////////////////////////
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
STDAPI DllRegisterServer()
{
    return AMovieDllRegisterServer2(TRUE);
}
//////////////////////////////////////////////////////////////////////////
STDAPI DllUnregisterServer()
{
    return AMovieDllRegisterServer2(FALSE);
}
//////////////////////////////////////////////////////////////////////////
BOOL APIENTRY DllMain(HANDLE hModule, DWORD  dwReason, LPVOID lpReserved)
{
    return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// CEncStereoFilter
//////////////////////////////////////////////////////////////////////////
// Constructor
CEncStereoFilter::CEncStereoFilter(LPUNKNOWN pUnk, HRESULT *phr) :
    CSource(NAME("Encrypted File Source"), pUnk, CLSID_ASFStereoFilter), 
    CSourceSeeking(NAME("Seek object"), pUnk, phr, &m_cStateLock), 
    m_bActive(FALSE), m_bPaused(FALSE), m_bSeek(FALSE), m_bEOF(FALSE), m_Helper(this)
{
    CAutoLock cAutoLock(&m_cStateLock);
    m_hAsyncEvent = CreateEvent(NULL, FALSE, FALSE, 0);     // Create an event for handling asynchronous calls
    *phr = WMCreateReader(NULL, 0, &m_pReader);             // Create WM reader
	*phr = m_pReader.QueryInterface<IWMReaderAdvanced2>(&m_pReaderAdvanced);             // Create WM advanced reader
}
//////////////////////////////////////////////////////////////////////////
// Dtor
CEncStereoFilter::~CEncStereoFilter()
{
    Cleanup();
    CloseHandle(m_hAsyncEvent);                     // Free the event 
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// IUnknown
//////////////////////////////////////////////////////////////////////////
CUnknown* WINAPI CEncStereoFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
    ASSERT(phr);
    return (CUnknown*)(CSource*)(new CEncStereoFilter(pUnk, phr));
}
//////////////////////////////////////////////////////////////////////////
STDMETHODIMP CEncStereoFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
    CHECK_GET_INTERFACE(IFileSourceFilter);
    CHECK_GET_INTERFACE(IMediaSeeking);
    return CSource::NonDelegatingQueryInterface(riid, ppv);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// IFileSourceFilter
//////////////////////////////////////////////////////////////////////////
STDMETHODIMP CEncStereoFilter::Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt)
{
    CAutoLock lck(&m_cStateLock);
    USES_CONVERSION;
    CheckPointer(lpwszFileName, E_POINTER);
    HRESULT hRet = S_OK;
    
    try
    {
        IWMReaderCallback* pWMCallback = (IWMReaderCallback*)&m_Helper;
        m_pReader->Open(lpwszFileName, pWMCallback, 0);
		IWMReaderCallbackAdvanced* pWMCallbackAdvanced = (IWMReaderCallbackAdvanced*)&m_Helper;
        
        // Wait for 5 seconds at most to open 
        if(WaitForSingleObject(m_hAsyncEvent, INFINITE) == WAIT_TIMEOUT)
            throw RPC_E_TIMEOUT;
        TRY(hRet, m_hrAsync);           // Test for error
        GetWMStreamsAndMediaTypes();    // Get the media types for each stream
        CreateWMPins();                 // Create a pin for each stream
        GetWMVDuration(lpwszFileName);  // Get duration and store it
        m_File = lpwszFileName;
    }
    catch(HRESULT h)
    {
        hRet = h;
    }
    return hRet;
}
//////////////////////////////////////////////////////////////////////////
STDMETHODIMP CEncStereoFilter::GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt)
{
    CheckPointer(ppszFileName, E_POINTER);
    *ppszFileName = NULL;
    wstring wsName = m_File;
    if(!wsName.empty()) 
    {
        DWORD n = sizeof(WCHAR)*(1 + wsName.size());
        *ppszFileName = (LPOLESTR) CoTaskMemAlloc( n );
        CopyMemory(*ppszFileName, wsName.c_str(), n);
    }
    return S_OK;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//IMediaFilter
//////////////////////////////////////////////////////////////////////
STDMETHODIMP CEncStereoFilter::Run(REFERENCE_TIME tStart)
{
    CAutoLock lock(&m_cStateLock);
    if(m_bPaused)
        m_pReader->Resume();            // If we are running after a pause, resume the reader
    return CSource::Run(tStart);
}
//////////////////////////////////////////////////////////////////////
STDMETHODIMP CEncStereoFilter::Pause()
{
    CAutoLock lock(&m_cStateLock);
    if(m_bActive)
    {
        m_pReader->Pause();     // If we are paused while running, pause the reader
        m_bPaused = TRUE;
    }
    return CSource::Pause();
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// IWMReaderCallback 
//////////////////////////////////////////////////////////////////////////
STDMETHODIMP CEncStereoFilter::OnSample(DWORD dwOutputNum, QWORD qwSampleTime, QWORD qwSampleDuration, DWORD dwFlags, INSSBuffer *pSample, void *pvContext)        
{
    // Get the pin which is associated with this stream and queue the sample
    CEncWMVPin *pPin = (CEncWMVPin *)m_paStreams[dwOutputNum];
    if(pPin->IsConnected())
    {
        CAutoLock lock(&pPin->m_cLock);
        pSample->AddRef();                                      // Add a reference to the sample so its alive till we use it
        pPin->m_Samples.push(WMSample(dwFlags, pSample));  // Queue the sample
    }
    return S_OK;
}
STDMETHODIMP CEncStereoFilter::OnStreamSample(WORD wStreamNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer *pSample, void *pvContext)
{
    // Get the pin which is associated with this stream and queue the sample
    CEncWMVPin *pPin = (CEncWMVPin *)m_paStreams[wStreamNum];
    if(pPin->IsConnected())
    {
        CAutoLock lock(&pPin->m_cLock);
        pSample->AddRef();                                      // Add a reference to the sample so its alive till we use it
        pPin->m_Samples.push(WMSample(dwFlags, pSample));  // Queue the sample
    }
    return S_OK;
}
STDMETHODIMP CEncStereoFilter::OnTime(QWORD cnsCurrentTime, void *pvContext)
{
	return S_OK;
}
STDMETHODIMP CEncStereoFilter::OnStreamSelection(WORD wStreamCount, WORD *pStreamNumbers, WMT_STREAM_SELECTION *pSelections, void *pvContext)
{
	return S_OK;
}
STDMETHODIMP CEncStereoFilter::OnOutputPropsChanged( DWORD dwOutputNum, WM_MEDIA_TYPE *pMediaType, void *pvContext)
{
	return S_OK;
}
STDMETHODIMP CEncStereoFilter::AllocateForStream( WORD wStreamNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext)
{
	return S_OK;
}
STDMETHODIMP CEncStereoFilter::AllocateForOutput(DWORD dwOutputNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext)
{
	return S_OK;
}
//////////////////////////////////////////////////////////////////////////
STDMETHODIMP CEncStereoFilter::OnStatus(WMT_STATUS Status, HRESULT hr, WMT_ATTR_DATATYPE dwType, BYTE *pValue, void *pvContext) 
{
    // Flag to indicate whether we fire the event
    bool bFireEvent = false;
    switch(Status)
    {
    case WMT_CLOSED:            // On open and close and stop , just fire the event
    case WMT_OPENED:
    case WMT_STOPPED:
        bFireEvent = true;
        break;
    case WMT_STARTED:           // On start reset EOF flag
        m_bEOF = FALSE;
        break;
    case WMT_EOF:               // On error or EOF fire the event
        if(!m_bEOF)
            m_bEOF = TRUE;
        bFireEvent = true;
        break;
    }
    if(bFireEvent)
    {
        m_hrAsync = hr;
        SetEvent(m_hAsyncEvent);
    }
    return S_OK;
}
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// CSourceSeeking
//////////////////////////////////////////////////////////////////////////
HRESULT CEncStereoFilter::ChangeStart()
{
    if(m_State == State_Paused)         // If we are streaming
    {
        m_pReader->Pause();             // Pause the reader object
        m_bSeek = TRUE;                 // Signal that we are in a seek
        // Flush the pins ( as per CSourceseeking help in Dshow docs )
        for(size_t i = 0; i < m_mtArray.size(); ++i)
        {
            CEncWMVPin* pPin = (CEncWMVPin*)m_paStreams[i];
            if(pPin->ThreadExists()) 
            {
                pPin->DeliverBeginFlush();      // Send flush request downstream
                pPin->Stop();                   // Stop streaming
                pPin->DeliverEndFlush();        // Send flush stop request
                pPin->Run();                    // Resume streaming
            }
        }
        m_pReader->Start(m_rtStart, 0, 1.0, 0); // Start the reader at the new position   
        m_bSeek = FALSE;                        // Signal that we are done seeking
    }
    return S_OK;
}
//////////////////////////////////////////////////////////////////////////

// Delete the pins and free media types
void CEncStereoFilter::Cleanup()
{
    for(size_t i = 0; i < m_mtArray.size(); ++i) 
        CoTaskMemFree(m_mtArray[i]);                // Free all the pin media types
}
//////////////////////////////////////////////////////////////////////////
// Wmv specific methods 
//////////////////////////////////////////////////////////////////////////
// Get file duration from meta data
void CEncStereoFilter::GetWMVDuration(LPCOLESTR pwszFileName)
{
    HRESULT hr;
    CComQIPtr<IWMHeaderInfo3> pHdrInfo(m_pReader);   // Get header info 3 interface
    WORD wStream = 0;                          // Get the duration
    WMT_ATTR_DATATYPE attrType = WMT_TYPE_QWORD;    
    WORD wSize = sizeof(QWORD);
    TRY(hr, pHdrInfo->GetAttributeByName(&wStream, g_wszWMDuration, &attrType, (BYTE*)&m_rtDuration, &wSize));
}
//////////////////////////////////////////////////////////////////////////
// Get the WM media types for each stream
void CEncStereoFilter::GetWMStreamsAndMediaTypes()
{
    CAutoLock cAutoLock(&m_cStateLock);
    CComQIPtr<IWMProfile> pProf(m_pReader);
    HRESULT hr;
    DWORD dwNumStreams;
    pProf->GetStreamCount(&dwNumStreams);           // Get streams count
#ifdef INFORMATION_MESSAGE
	wchar_t local_message[500];
	const wchar_t const *local_caption = L"Information";
	swprintf(local_message,sizeof(local_message)/sizeof(wchar_t),L"Streams count: %d",dwNumStreams);
	::MessageBoxW(0,local_message,local_caption,MB_ICONINFORMATION);
#endif
	CComPtr<IWMOutputMediaProps> local_pOutProps_Video;
	CComPtr<IWMOutputMediaProps> local_pOutProps_Audio;
	CComPtr<IWMOutputMediaProps> local_pOutProps_Text;
	CComPtr<IWMOutputMediaProps> local_pOutProps_ScriptCommand;
	CComPtr<IWMOutputMediaProps> local_pOutProps_Timecode;
    for(size_t i = 0; i < dwNumStreams; ++i)        // Iterate over each stream
    {
        CComPtr<IWMStreamConfig> pStreamConfig;     // Get IWMStreamConfig interface
        pProf->GetStream(i, &pStreamConfig);
        CComPtr<IWMOutputMediaProps> pOutProps;     // Save the media type
        m_pReader->GetOutputProps(i, &pOutProps);
		GUID local_stream_GUID;
		pStreamConfig->GetStreamType(&local_stream_GUID);
		if(pOutProps==NULL)
		{
			CComPtr<IWMMediaProps> local_pProps;
			pStreamConfig.QueryInterface<IWMMediaProps>(&local_pProps);
			local_pProps.CopyTo((IWMMediaProps **)&pOutProps);
		}
		if(pOutProps)
		{
			if
				(
				local_stream_GUID == MEDIATYPE_Video
				)
			{
				pOutProps.CopyTo(&local_pOutProps_Video);
#ifdef INFORMATION_MESSAGE
				::MessageBoxW(0,L"Stream type MEDIATYPE_Video",local_caption,MB_ICONINFORMATION);
#endif
			}
			else
				if
					(
					local_stream_GUID == MEDIATYPE_Audio
					)
				{
					pOutProps.CopyTo(&local_pOutProps_Audio);
#ifdef INFORMATION_MESSAGE
					::MessageBoxW(0,L"Stream type MEDIATYPE_Audio",local_caption,MB_ICONINFORMATION);
#endif
				}
				else
					if
						(
						local_stream_GUID == MEDIATYPE_Text
						)
					{
						pOutProps.CopyTo(&local_pOutProps_Text);
#ifdef INFORMATION_MESSAGE
						::MessageBoxW(0,L"Stream type MEDIATYPE_Text",local_caption,MB_ICONINFORMATION);
#endif
					}
					else
						if
							(
							local_stream_GUID == MEDIATYPE_ScriptCommand
							)
						{
							pOutProps.CopyTo(&local_pOutProps_ScriptCommand);
#ifdef INFORMATION_MESSAGE
							::MessageBoxW(0,L"Stream type MEDIATYPE_ScriptCommand",local_caption,MB_ICONINFORMATION);
#endif
						}
						else
							if
								(
								local_stream_GUID == MEDIATYPE_Timecode
								)
							{
								pOutProps.CopyTo(&local_pOutProps_Timecode);
#ifdef INFORMATION_MESSAGE
								::MessageBoxW(0,L"Stream type MEDIATYPE_Timecode",local_caption,MB_ICONINFORMATION);
#endif
							}
		}
		else
		{
			if
				(
				local_stream_GUID == MEDIATYPE_Video
				)
			{
				local_pOutProps_Video.CopyTo(&pOutProps);
#ifdef INFORMATION_MESSAGE
				::MessageBoxW(0,L"Set Properties for Stream type MEDIATYPE_Video",local_caption,MB_ICONINFORMATION);
#endif
			}
			else
				if
					(
					local_stream_GUID == MEDIATYPE_Audio
					)
				{
					local_pOutProps_Audio.CopyTo(&pOutProps);
#ifdef INFORMATION_MESSAGE
					::MessageBoxW(0,L"Set Properties for Stream type MEDIATYPE_Audio",local_caption,MB_ICONINFORMATION);
#endif
				}
				else
					if
						(
						local_stream_GUID == MEDIATYPE_Text
						)
					{
						local_pOutProps_Text.CopyTo(&pOutProps);
#ifdef INFORMATION_MESSAGE
						::MessageBoxW(0,L"Set Properties for Stream type MEDIATYPE_Text",local_caption,MB_ICONINFORMATION);
#endif
					}
					else
						if
							(
							local_stream_GUID == MEDIATYPE_ScriptCommand
							)
						{
							local_pOutProps_ScriptCommand.CopyTo(&pOutProps);
#ifdef INFORMATION_MESSAGE
							::MessageBoxW(0,L"Set Properties for Stream type MEDIATYPE_ScriptCommand",local_caption,MB_ICONINFORMATION);
#endif
						}
						else
							if
								(
								local_stream_GUID == MEDIATYPE_Timecode
								)
							{
								local_pOutProps_Timecode.CopyTo(&pOutProps);
#ifdef INFORMATION_MESSAGE
								::MessageBoxW(0,L"Set Properties for Stream type MEDIATYPE_Timecode",local_caption,MB_ICONINFORMATION);
#endif
							}
			
			m_pReader->SetOutputProps(i, pOutProps);
		}
        if(pOutProps)
        {
            WORD cbName;                                // Get the name of the stream and save it
            wstring wsName;
            TRY(hr, pStreamConfig->GetStreamName(NULL, &cbName));
            wsName.resize(cbName);
            pStreamConfig->GetStreamName((WCHAR*)wsName.data(), &cbName);
            m_PinNames.push_back(wsName);
#ifdef INFORMATION_MESSAGE
			wchar_t local_message[500];
			swprintf(local_message,sizeof(local_message)/sizeof(wchar_t),L"Stream: %d\nStream name: %s",i,(WCHAR*)wsName.data());
			::MessageBoxW(0,local_message,local_caption,MB_ICONINFORMATION);
#endif
            DWORD cbSize;
            AM_MEDIA_TYPE *pmt;
            TRY(hr, pOutProps->GetMediaType(NULL, &cbSize));
            pmt = (AM_MEDIA_TYPE *)CoTaskMemAlloc(cbSize);
            TRY(hr, pOutProps->GetMediaType((WM_MEDIA_TYPE*)pmt, &cbSize));
            m_mtArray.push_back(pmt);
        }
    }
}
//////////////////////////////////////////////////////////////////////////
// Create a pin for each stream
void CEncStereoFilter::CreateWMPins()
{
    CAutoLock cAutoLock(&m_cStateLock);
    m_paStreams = (CSourceStream **) new CEncWMVPin*[m_mtArray.size()];      // Allocate pin array
    CComQIPtr<IWMReaderAdvanced> pAdv(m_pReader);
    for(size_t i = 0; i < m_mtArray.size(); ++i)
    {
        HRESULT hr;
        // Create a pin, (its added automatically to the array)
        CEncWMVPin *pPin = new CEncWMVPin(&hr, this, (LPCWSTR)m_PinNames[i].c_str(), m_mtArray[i], i == 0);
		if(hr==S_OK)
		{
	        if(i == 0)
				m_pFirstPin = pPin;
			// Get the maximum possible sample size 
		    hr = pAdv->GetMaxOutputSampleSize(i, &pPin->m_dwSampleSize);
			if(hr!=S_OK)
			{
				hr = pAdv->GetMaxStreamSampleSize(i,&pPin->m_dwSampleSize);
				if(hr!=S_OK)
				{
					pPin->m_dwSampleSize = 2000*1500*4;
				}
			}
		}
    }
}
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// CEncWMVPin
//////////////////////////////////////////////////////////////////////////
CEncWMVPin::CEncWMVPin(HRESULT *phr, CEncStereoFilter *pParent, LPCWSTR pPinName, AM_MEDIA_TYPE *pmt, BOOL bMaster) :
CSourceStream(NAME("WMV Source pin"),phr, pParent, pPinName), m_bMaster(bMaster)
{
    CAutoLock lock(&m_cLock);
    m_pParent = pParent;
    m_mt = *pmt;
}
//////////////////////////////////////////////////////////////////////////
// Dtor
CEncWMVPin::~CEncWMVPin()
{
    CAutoLock lock(&m_cLock);
    while(!m_Samples.empty())               // Discard any samples left in the queue
    {
        WMSample s = m_Samples.front();
        m_Samples.pop();
        s.pINSSBuffer->Release();
    }
}
//////////////////////////////////////////////////////////////////////////
// Pump the data 
HRESULT CEncWMVPin::FillBuffer(IMediaSample *pms)
{
    // If there are no queued samples and the graph is running , then wait for them 
    // unless its EOF or a seek
    while(m_Samples.empty() && m_pParent->m_bActive && !m_pParent->m_bSeek &&  !m_pParent->m_bEOF) 
        Sleep(1);
    // If its a seek then exit
    if(m_pParent->m_bSeek) 
    {
        pms->SetActualDataLength(0);
        pms->SetDiscontinuity(TRUE);
        return S_OK;
    }
    // If graph is inactive stop cueing samples
    if(!m_pParent->m_bActive || m_pParent->m_bEOF) 
        return S_FALSE;
    // Get the critical section
    WMSample s;
    {
        CAutoLock lock(&m_cLock);
        // Dequeue the sample
        s = m_Samples.front();
        m_Samples.pop();
    }
    DWORD dwLen;
    BYTE *pDataDest, *pDataSrc;
    // Get the buffer and size 
    if(SUCCEEDED(s.pINSSBuffer->GetBufferAndLength(&pDataSrc, &dwLen)))
    {
        // Copy the sample
        pms->GetPointer(&pDataDest);
        memcpy(pDataDest, pDataSrc, dwLen);
        // Set length, flags and timestamp ( no timestamp works best!! )
        pms->SetActualDataLength(dwLen);
        pms->SetDiscontinuity((s.dwFlags & WM_SF_DISCONTINUITY));
        pms->SetSyncPoint(s.dwFlags & WM_SF_CLEANPOINT);
        pms->SetTime(NULL, NULL);
    }
    // Release the WM sample (we had AddRef'd it in OnSample())
    s.pINSSBuffer->Release();
    return S_OK;
}
//////////////////////////////////////////////////////////////////////////
// See Directshow help topic for IAMStreamConfig for details on this method
HRESULT CEncWMVPin::GetMediaType(int iPosition, CMediaType *pmt)
{
    if(!iPosition) 
    {
        *pmt = m_mt;
        return S_OK;
    }
    return VFW_S_NO_MORE_ITEMS;
}
//////////////////////////////////////////////////////////////////////////
// This method is called to see if a given output format is supported
HRESULT CEncWMVPin::CheckMediaType(const CMediaType *pmt)
{
    if(*pmt == m_mt) // we accept the one and only media type
        return S_OK;
    else
        return S_FALSE;
}
//////////////////////////////////////////////////////////////////////////
// This method is called after the pins are connected to allocate buffers to stream data
HRESULT CEncWMVPin::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties)
{
    CAutoLock cAutoLock(m_pFilter->pStateLock());
    HRESULT hr = S_OK;
    pProperties->cBuffers = 5;
    pProperties->cbBuffer = m_dwSampleSize;
    ALLOCATOR_PROPERTIES Actual;
    hr = pAlloc->SetProperties(pProperties,&Actual);
    if(FAILED(hr)) return hr;
    if(Actual.cbBuffer < pProperties->cbBuffer) return E_FAIL;
    return S_OK;
}
//////////////////////////////////////////////////////////////////////////
HRESULT CEncWMVPin::Active()
{
    CAutoLock lock(&m_cLock);        // Grab the critical section
    m_pParent->m_bActive = TRUE;            // Signal that we are active and streaming
    if(m_bMaster)                           // If we are the master pin then start the reader object
        m_pParent->m_pReader->Start(m_pParent->m_rtStart, 0, 1.0, 0);
    return CSourceStream::Active();         // Call the baseclass 
}
//////////////////////////////////////////////////////////////////////////
HRESULT CEncWMVPin::Inactive()
{
    if(m_bMaster)                       // If we are the master pin then stop the reader
        m_pParent->m_pReader->Stop();
    m_pParent->m_bActive = FALSE;       // Signal that we are inactive
    return CSourceStream::Inactive();   // Call the baseclass 
}
//////////////////////////////////////////////////////////////////////////
// Deliver a new segment after seek ( as explained in Dshow docs )
HRESULT CEncWMVPin::OnThreadStartPlay()
{
    return DeliverNewSegment(m_pParent->m_rtStart, m_pParent->m_rtDuration, 1.0);
}




#pragma once
// {20FFACF7-BC0F-4AD5-86EF-5384EF96A3D7}
DEFINE_GUID(CLSID_ASFStereoFilter, 0x20FFACF7, 0xBC0F, 0x4AD5, 0x86, 0xEF, 0x53, 0x84, 0xEF, 0x96, 0xA3, 0xD7);
class CEncWMVPin;
// Struct for queued WMV samples
struct WMSample
{
    DWORD dwFlags;
    INSSBuffer *pINSSBuffer;
    WMSample(DWORD adwFlags, INSSBuffer *apINSSBuffer) :dwFlags(adwFlags), pINSSBuffer(apINSSBuffer) {}
    WMSample() {}
};
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Helpers to prevent circular reference between wm reader and filter
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Class which implements IWMReaderCallback forwarding the calls to the filter 
// Filter holds a reference to the WM reader and the WM reader holds a reference to IWMReaderCallback
// This causes a circular reference and objects leak on exit
// Using this class as a proxy avoids that problem
class CWMVStereoFilterHelper : public CUnknown, public IWMReaderCallback, public IWMReaderCallbackAdvanced
{
    CComPtr<IWMReaderCallback> m_pFilter;
	CComPtr<IWMReaderCallbackAdvanced> m_pFilter_advanced;
public :
    CWMVStereoFilterHelper(IWMReaderCallback *pFilter):
	   CUnknown(NAME("WMV StereoFilter Helper object"), 0),
		   m_pFilter(pFilter)
    {
        AddRef();       // AddRef once
		m_pFilter->QueryInterface<IWMReaderCallbackAdvanced>(&m_pFilter_advanced);
    }
    //////////////////////////////////////////////////////////////////////////
    // IUnknown
    //////////////////////////////////////////////////////////////////////////
    DECLARE_IUNKNOWN;
    // Expose IWMReaderCallback
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv) 
    {
        CHECK_GET_INTERFACE(IWMReaderCallback);
		CHECK_GET_INTERFACE(IWMReaderCallbackAdvanced);
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
    //////////////////////////////////////////////////////////////////////////
    // IWMReaderCallback - Forward calls to the filter
    //////////////////////////////////////////////////////////////////////////
    HRESULT STDMETHODCALLTYPE FORWARD6(OnSample, m_pFilter, DWORD, dwOutputNum, QWORD, cnsSampleTime, QWORD, cnsSampleDuration, DWORD, dwFlags, INSSBuffer*, pSample, void*, pvContext);
    HRESULT STDMETHODCALLTYPE FORWARD5(OnStatus, m_pFilter, WMT_STATUS, Status, HRESULT, hr, WMT_ATTR_DATATYPE, dwType, BYTE*, pValue, void*, pvContext);
    
	//////////////////////////////////////////////////////////////////////////
    // IWMReaderCallbackAdvanced - Forward calls to the filter
    //////////////////////////////////////////////////////////////////////////
	HRESULT STDMETHODCALLTYPE FORWARD6(OnStreamSample, m_pFilter_advanced, WORD ,wStreamNum, QWORD, cnsSampleTime, QWORD, cnsSampleDuration, DWORD, dwFlags, INSSBuffer *,pSample, void *,pvContext);
	HRESULT STDMETHODCALLTYPE FORWARD2(OnTime, m_pFilter_advanced, QWORD, cnsCurrentTime, void *,pvContext);
	HRESULT STDMETHODCALLTYPE FORWARD4(OnStreamSelection, m_pFilter_advanced, WORD, wStreamCount, WORD *,pStreamNumbers, WMT_STREAM_SELECTION *,pSelections, void *,pvContext);
	HRESULT STDMETHODCALLTYPE FORWARD3(OnOutputPropsChanged, m_pFilter_advanced, DWORD, dwOutputNum, WM_MEDIA_TYPE *,pMediaType, void *,pvContext);
	HRESULT STDMETHODCALLTYPE FORWARD4(AllocateForStream, m_pFilter_advanced, WORD, wStreamNum, DWORD, cbBuffer, INSSBuffer **,ppBuffer, void *,pvContext);
	HRESULT STDMETHODCALLTYPE FORWARD4(AllocateForOutput, m_pFilter_advanced, DWORD, dwOutputNum, DWORD, cbBuffer, INSSBuffer **,ppBuffer, void *,pvContext);
};
//////////////////////////////////////////////////////////////////////////

// Source filter implementing push source for WMV 
class CEncStereoFilter : virtual public CSource, virtual public CSourceSeeking, public IFileSourceFilter, public IWMReaderCallback, public IWMReaderCallbackAdvanced
{
    friend class CEncWMVPin;
	wstring m_File;
    void Cleanup();
    //////////////////////////////////////////////////////////////////////////
    // WMV source specific stuff
    //////////////////////////////////////////////////////////////////////////
    HANDLE m_hAsyncEvent;                       // Event signaled when the WM reader completes opening
    HRESULT m_hrAsync;                          // Return value from the WM reader 
    BOOL m_bEOF, m_bActive, m_bPaused, m_bSeek; // Flags to indicate EOF, whether the filter is streaming or paused and whether we are in a seek
    vector<AM_MEDIA_TYPE*> m_mtArray;           // Array of Media types 
    vector<wstring> m_PinNames;                 // Name for each stream
    CEncWMVPin *m_pFirstPin;                    // Pointer to the first pin which we call the master pin
    CWMVStereoFilterHelper m_Helper;                     // Callback helper object to prevent circular reference
    CComPtr<IWMReader> m_pReader;               // WMV reader object
	CComPtr<IWMReaderAdvanced2> m_pReaderAdvanced;               // WMV advanced reader object
    void GetWMVDuration(LPCOLESTR pwszFile);    // Gets the duration of the file 
    void GetWMStreamsAndMediaTypes();           // Gets the media types in the WMV
    void CreateWMPins();                        // Creates a pin for each stream
public:
     CEncStereoFilter(LPUNKNOWN pUnk, HRESULT *phr);
    ~CEncStereoFilter();
    //////////////////////////////////////////////////////////////////////////
    //  IUnknown
    //////////////////////////////////////////////////////////////////////////
    // We cant use DECLARE_IUNKNOWN since we have 2 CUnknown parent classes from CSource and CSourceSeeking
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { return CSource::GetOwner()->QueryInterface(riid,ppv); }
    STDMETHODIMP_(ULONG) AddRef() { return CSource::GetOwner()->AddRef(); }
    STDMETHODIMP_(ULONG) Release() { return CSource::GetOwner()->Release(); }
    static CUnknown* WINAPI CreateInstance(LPUNKNOWN, HRESULT *);
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
    //////////////////////////////////////////////////////////////////////////
    //  IFileSourceFilter 
    //////////////////////////////////////////////////////////////////////////
    STDMETHODIMP Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt);
    STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName, AM_MEDIA_TYPE *pmt);
    //////////////////////////////////////////////////////////////////////////
    // IMediaFilter 
    //////////////////////////////////////////////////////////////////////////
    STDMETHODIMP Run(REFERENCE_TIME rtStart);
    STDMETHODIMP Pause();
    //////////////////////////////////////////////////////////////////////////
    // IWMReaderCallback called by the helper object for push mode
    //////////////////////////////////////////////////////////////////////////
    STDMETHODIMP OnSample(DWORD dwOutputNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer *pSample, void *pvContext);
    STDMETHODIMP OnStatus(WMT_STATUS Status, HRESULT hr, WMT_ATTR_DATATYPE dwType, BYTE *pValue, void *pvContext);
    //////////////////////////////////////////////////////////////////////////
    // IWMReaderCallbackAdvanced called by the helper object for push mode
    //////////////////////////////////////////////////////////////////////////
	STDMETHODIMP OnStreamSample(WORD wStreamNum, QWORD cnsSampleTime, QWORD cnsSampleDuration, DWORD dwFlags, INSSBuffer *pSample, void *pvContext);
	STDMETHODIMP OnTime(QWORD cnsCurrentTime, void *pvContext);
	STDMETHODIMP OnStreamSelection(WORD wStreamCount, WORD *pStreamNumbers, WMT_STREAM_SELECTION *pSelections, void *pvContext);
	STDMETHODIMP OnOutputPropsChanged( DWORD dwOutputNum, WM_MEDIA_TYPE *pMediaType, void *pvContext);
	STDMETHODIMP AllocateForStream( WORD wStreamNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext);
	STDMETHODIMP AllocateForOutput(DWORD dwOutputNum, DWORD cbBuffer, INSSBuffer **ppBuffer, void *pvContext);
    //////////////////////////////////////////////////////////////////////////
    // CSourceSeeking 
    //////////////////////////////////////////////////////////////////////////
    HRESULT ChangeStart();
    HRESULT ChangeStop() DUMMYIMPLEMENT;
    HRESULT ChangeRate() DUMMYIMPLEMENT;
    
};
//////////////////////////////////////////////////////////////////////////
// WMV Push pin implementation
class CEncWMVPin : public CSourceStream
{
public:
    CCritSec m_cLock;           // Critical section
    CEncStereoFilter *m_pParent;         // parent filter
    queue<WMSample> m_Samples;  // queue of samples
    BOOL m_bMaster;             // Flag indicating if its the master pin 
    DWORD m_dwSampleSize;       // Maximum sample size, used to decide buffer size
    CEncWMVPin(HRESULT *phr, CEncStereoFilter *pParent, LPCWSTR pPinName, AM_MEDIA_TYPE *pmt, BOOL bMaster);
    ~CEncWMVPin();
    //////////////////////////////////////////////////////////////////////////
    //  CSourceStream overrides
    //////////////////////////////////////////////////////////////////////////
    HRESULT FillBuffer(IMediaSample *pms);
    HRESULT DecideBufferSize(IMemAllocator *pIMemAlloc, ALLOCATOR_PROPERTIES *pProperties);
    HRESULT CheckMediaType(const CMediaType *pMediaType);
    HRESULT GetMediaType(int iPosition, CMediaType *pmt);
    HRESULT Active(void); 
    HRESULT Inactive();
    HRESULT OnThreadStartPlay(void);
};
//////////////////////////////////////////////////////////////////////////
Posted
Updated 17-Dec-10 4:47am
v4
Comments
[no name] 17-Dec-10 9:37am    
Function OnStreamSample is never called.
[no name] 17-Dec-10 12:20pm    
But interface was given using CHECK_GET_INTERFACE(IWMReaderCallbackAdvanced);.
[no name] 20-Dec-10 9:00am    
Does anybody know, if all video file streams can be read by asynchrone wmv reader (IWMReader)?
(*phr = WMCreateReader(NULL, 0, &m_pReader); // Create WM reader)

1 solution

 
Share this answer
 
v2
Comments
[no name] 17-Dec-10 9:35am    
I did not find there about several video streams.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900