Windows Media Foundation - adding Customer MFT Transform to Topology

15 hours ago 3
ARTICLE AD BOX

I have a WMF media session Project for displaying avi, MP4 Videos
I want to make Play/Pause backforward forforward and seek to specific frame. for making SeekFrame I want a Filter to know the time for this frame. so I create a through-pass MFT.
before adding MFT I could see the Video, but after that I could not.

My MFT.cpp

#include "Stdafx.h" #include "WmfTransformer.h" #include <mfplay.h> #include <mfidl.h> #include <atomic> using namespace MMLibrary; // Video FOURCC codes. const FOURCC FOURCC_YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'); const FOURCC FOURCC_UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'); const FOURCC FOURCC_NV12 = MAKEFOURCC('N', 'V', '1', '2'); // Static array of media types (preferred and accepted). const GUID* g_MediaSubtypes[] = { & MFVideoFormat_I420, & MFVideoFormat_NV12, & MFVideoFormat_YUY2, & MFVideoFormat_UYVY, & MFVideoFormat_RGB32 }; // Number of media types in the aray. DWORD g_cNumSubtypes = ARRAYSIZE(g_MediaSubtypes); //------------------------------------------------------------------- // Name: CreateInstance // Description: Static method to create an instance of the source. // // pUnkOuter: Aggregating object or NULL. // iid: IID of the requested interface on the source. // ppSource: Receives a ref-counted pointer to the source. //------------------------------------------------------------------- HRESULT WmfTransformer::CreateInstance(HWND hWnd, UINT stopMsg,IUnknown* pUnkOuter, REFIID iid, void** ppMFT) { if (ppMFT == NULL) { return E_POINTER; } // This object does not support aggregation. if (pUnkOuter != NULL) { return CLASS_E_NOAGGREGATION; } HRESULT hr = S_OK; WmfTransformer* pMFT = new WmfTransformer(hWnd, stopMsg ,hr); if (pMFT == NULL) { hr = E_OUTOFMEMORY; if (FAILED(hr)) { pMFT->Release(); return hr; } } hr = pMFT->QueryInterface(iid, ppMFT); if (FAILED(hr)) { pMFT->Release(); return hr; } } //------------------------------------------------------------------- // Constructor //------------------------------------------------------------------- WmfTransformer::WmfTransformer(HWND hWnd, UINT stopMsg,HRESULT& hr): m_refCount(1), m_inputType(nullptr), m_outputType(nullptr), m_sample(nullptr), m_hMessageReceiver(hWnd), m_StopTimeReachedMsg(stopMsg) { } //------------------------------------------------------------------- // Destructor //------------------------------------------------------------------- WmfTransformer::~WmfTransformer() { assert(m_refCount == 0); if (m_inputType) m_inputType->Release(); if (m_outputType) m_outputType->Release(); if (m_sample) m_sample->Release(); } // IUnknown methods ULONG WmfTransformer::AddRef() { return InterlockedIncrement(&m_refCount); } ULONG WmfTransformer::Release() { ULONG count = InterlockedDecrement(&m_refCount); if (count == 0) delete this; return count; } HRESULT WmfTransformer::QueryInterface(REFIID iid, void** ppv) { if (!ppv) return E_POINTER; if (iid == IID_IUnknown) *ppv = static_cast<IUnknown*>(static_cast<IMFTransform*>(this)); else if (iid == IID_IMFTransform) *ppv = static_cast<IMFTransform*>(this); else { *ppv = nullptr; return E_NOINTERFACE; } AddRef(); return S_OK; } //------------------------------------------------------------------- // Name: GetStreamLimits // Returns the minimum and maximum number of streams. //------------------------------------------------------------------- HRESULT WmfTransformer::GetStreamLimits( DWORD* inMin, DWORD* inMax, DWORD* outMin, DWORD* outMax) { if ((inMin == NULL) || (inMax == NULL) || (outMin == NULL) || (outMax == NULL)) { return E_POINTER; } *inMin = *inMax = 1; *outMin = *outMax = 1; return S_OK; } //------------------------------------------------------------------- // Name: GetStreamCount // Returns the actual number of streams. //------------------------------------------------------------------- HRESULT WmfTransformer::GetStreamCount( DWORD* inStreams, DWORD* outStreams) { if ((inStreams == NULL) || (outStreams == NULL)) { return E_POINTER; } // This MFT has a fixed number of streams. *inStreams = 1; *outStreams = 1; return S_OK; } //------------------------------------------------------------------- // Name: GetStreamIDs // Returns stream IDs for the input and output streams. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetStreamIDs( DWORD, DWORD*, DWORD, DWORD*) { // Do not need to implement, because this MFT has a fixed number of // streams and the stream IDs match the stream indexes. return E_NOTIMPL; // Use default stream IDs (0) } //------------------------------------------------------------------- // Name: GetInputStreamInfo // Returns information about an input stream. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetInputStreamInfo( DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO* pStreamInfo) { //TRACE((L"GetInputStreamInfo\n")); //AutoLock lock(m_critSec); if (pStreamInfo == NULL) { return E_POINTER; } if (!IsValidInputStream(dwInputStreamID)) { return MF_E_INVALIDSTREAMNUMBER; } // NOTE: This method should succeed even when there is no media type on the // stream. If there is no media type, we only need to fill in the dwFlags // member of MFT_INPUT_STREAM_INFO. The other members depend on having a // a valid media type. pStreamInfo->hnsMaxLatency = 0; pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER; if (m_inputType == nullptr) { pStreamInfo->cbSize = 0; } else { pStreamInfo->cbSize = 1; } pStreamInfo->cbMaxLookahead = 0; pStreamInfo->cbAlignment = 0; return S_OK; } STDMETHODIMP WmfTransformer::GetOutputStreamInfo( DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO* pStreamInfo) { //AutoLock lock(m_critSec); if (pStreamInfo == NULL) { return E_POINTER; } if (!IsValidOutputStream(dwOutputStreamID)) { return MF_E_INVALIDSTREAMNUMBER; } // NOTE: This method should succeed even when there is no media type on the // stream. If there is no media type, we only need to fill in the dwFlags // member of MFT_OUTPUT_STREAM_INFO. The other members depend on having a // a valid media type. pStreamInfo->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER; if (m_outputType == NULL) { pStreamInfo->cbSize = 0; } else { pStreamInfo->cbSize = 1; } pStreamInfo->cbAlignment = 0; return S_OK; } //------------------------------------------------------------------- // Name: GetAttributes // Returns the attributes for the MFT. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetAttributes(IMFAttributes** pAttributes) { return E_NOTIMPL; } //------------------------------------------------------------------- // Name: GetInputStreamAttributes // Returns stream-level attributes for an input stream. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetInputStreamAttributes( DWORD, IMFAttributes** ppAttributes) { return E_NOTIMPL; } //------------------------------------------------------------------- // Name: GetOutputStreamAttributes // Returns stream-level attributes for an output stream. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetOutputStreamAttributes( DWORD, IMFAttributes** ppAttributes) { return E_NOTIMPL; } //optional STDMETHODIMP WmfTransformer::DeleteInputStream(DWORD) { return E_NOTIMPL; } //optional STDMETHODIMP WmfTransformer::AddInputStreams(DWORD, DWORD*) { return E_NOTIMPL; } //------------------------------------------------------------------- // Name: GetInputAvailableType // Description: Return a preferred input type. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetInputAvailableType( DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType** ppType) { //TRACE((L"GetInputAvailableType (stream = %d, type index = %d",dwInputStreamID, dwTypeIndex)); //AutoLock lock(m_critSec); if (ppType==NULL) return E_INVALIDARG; if (!IsValidInputStream(dwInputStreamID)) { return MF_E_INVALIDSTREAMNUMBER; } HRESULT hr = S_OK; // If the output type is set, return that type as our preferred input type. if (this->m_outputType) { if (dwTypeIndex > 0) { return MF_E_NO_MORE_TYPES; } *ppType = m_outputType; (*ppType)->AddRef(); } else { // The output type is not set. Create a partial media type. hr = OnGetPartialType(dwTypeIndex, ppType); } return hr; } //In some cases, an MFT cannot return a list of output types until // one or more input types are set. //If so, the method returns MF_E_TRANSFORM_TYPE_NOT_SET STDMETHODIMP WmfTransformer::GetOutputAvailableType( DWORD dwOutputStreamID, DWORD dwTypeIndex, IMFMediaType** ppType) { //TRACE((L"GetOutputAvailableType (stream = %d, type index = %d)\n", dwOutputStreamID, dwTypeIndex)); // AutoLock lock(m_critSec); if (ppType == NULL) { return E_INVALIDARG; } if (!IsValidOutputStream(dwOutputStreamID)) { return MF_E_INVALIDSTREAMNUMBER; } HRESULT hr = S_OK; // If the input type is set, return that type as our preferred output type. if (this->m_inputType) { if (dwTypeIndex > 0) { return MF_E_NO_MORE_TYPES; } *ppType = m_inputType; (*ppType)->AddRef(); } else { // The input type is not set. Create a partial media type. hr = OnGetPartialType(dwTypeIndex, ppType); } return hr; } //------------------------------------------------------------------- // Name: SetInputType //------------------------------------------------------------------- HRESULT WmfTransformer::SetInputType( DWORD dwInputStreamID, IMFMediaType* pType, DWORD dwFlags) { //TRACE((L"CGrayscale::SetInputType\n")); // AutoLock lock(m_critSec); if (!IsValidInputStream(dwInputStreamID)) { return MF_E_INVALIDSTREAMNUMBER; } // Validate flags. if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY) { return E_INVALIDARG; } HRESULT hr = S_OK; // Does the caller want us to set the type, or just test it? BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0); // If we have an input sample, the client cannot change the type now. if (HasPendingOutput()) { hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; if (FAILED(hr)) return hr; } // Validate the type, if non-NULL. if (pType) { hr = OnCheckInputType(pType); if (FAILED(hr)) return hr; } // The type is OK. // Set the type, unless the caller was just testing. if (bReallySet) { hr = OnSetInputType(pType); if (FAILED(hr)) return hr; } done: return hr; } HRESULT WmfTransformer::SetOutputType( DWORD dwOutputStreamID, IMFMediaType* pType, DWORD dwFlags) { //TRACE((L"CGrayscale::SetOutputType\n")); //AutoLock lock(m_critSec); if (!IsValidOutputStream(dwOutputStreamID)) { return MF_E_INVALIDSTREAMNUMBER; } // Validate flags. if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY) { return E_INVALIDARG; } HRESULT hr = S_OK; // Does the caller want us to set the type, or just test it? BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0); // If we have an input sample, the client cannot change the type now. if (HasPendingOutput()) { hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; if (FAILED(hr)) return hr; } // Validate the type, if non-NULL. if (pType) { hr = OnCheckOutputType(pType); if (FAILED(hr)) return hr; } if (bReallySet) { // The type is OK. // Set the type, unless the caller was just testing. hr = OnSetOutputType(pType); if (FAILED(hr)) return hr; } done: return hr; } //------------------------------------------------------------------- // Name: GetInputCurrentType // Description: Returns the current input type. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetInputCurrentType( DWORD dwInputStreamID, IMFMediaType** ppType) { //AutoLock lock(m_critSec); if (ppType == NULL) { return E_POINTER; } if (!IsValidInputStream(dwInputStreamID)) { return MF_E_INVALIDSTREAMNUMBER; } if (!m_inputType) { return MF_E_TRANSFORM_TYPE_NOT_SET; } *ppType = m_inputType; (*ppType)->AddRef(); return S_OK; } //------------------------------------------------------------------- // Name: GetOutputCurrentType // Description: Returns the current output type. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetOutputCurrentType( DWORD dwOutputStreamID, IMFMediaType** ppType) { // AutoLock lock(m_critSec); if (ppType == NULL) { return E_POINTER; } if (!IsValidOutputStream(dwOutputStreamID)) { return MF_E_INVALIDSTREAMNUMBER; } if (!m_outputType) { return MF_E_TRANSFORM_TYPE_NOT_SET; } *ppType = m_outputType; (*ppType)->AddRef(); return S_OK; } //------------------------------------------------------------------- // Name: GetInputStatus // Description: Query if the MFT is accepting more input. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetInputStatus( DWORD dwInputStreamID, DWORD* pdwFlags) { //TRACE((L"GetInputStatus\n")); //AutoLock lock(m_critSec); if (pdwFlags == NULL) { return E_POINTER; } if (!IsValidInputStream(dwInputStreamID)) { return MF_E_INVALIDSTREAMNUMBER; } // If we already have an input sample, we don't accept // another one until the client calls ProcessOutput or Flush. if (m_sample == NULL) { *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA; } else { *pdwFlags = 0; } return S_OK; } //------------------------------------------------------------------- // Name: GetOutputStatus // Description: Query if the MFT can produce output. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::GetOutputStatus(DWORD* pdwFlags) { // TRACE((L"GetOutputStatus\n")); //AutoLock lock(m_critSec); if (pdwFlags == NULL) { return E_POINTER; } // We can produce an output sample if (and only if) // we have an input sample. if (m_sample != NULL) { *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY; } else { *pdwFlags = 0; } return S_OK; } STDMETHODIMP WmfTransformer::SetOutputBounds( LONGLONG, LONGLONG) { return E_NOTIMPL; } //------------------------------------------------------------------- // Name: ProcessEvent // Sends an event to an input stream. //------------------------------------------------------------------- STDMETHODIMP WmfTransformer::ProcessEvent( DWORD dwInputStreamID, IMFMediaEvent* pEvent) { // This MFT does not handle any stream events, so the method can // return E_NOTIMPL. This tells the pipeline that it can stop // sending any more events to this MFT. return E_NOTIMPL; } //------------------------------------------------------------------- // Name: ProcessMessage //------------------------------------------------------------------- HRESULT WmfTransformer::ProcessMessage( MFT_MESSAGE_TYPE eMessage, ULONG_PTR) { // AutoLock lock(m_critSec); HRESULT hr = S_OK; switch (eMessage) { case MFT_MESSAGE_COMMAND_FLUSH: // Flush the MFT. hr = OnFlush(); break; case MFT_MESSAGE_COMMAND_DRAIN: // Drain: Tells the MFT not to accept any more input until // all of the pending output has been processed. That is our // default behevior already, so there is nothing to do. break; case MFT_MESSAGE_SET_D3D_MANAGER: // The pipeline should never send this message unless the MFT // has the MF_SA_D3D_AWARE attribute set to TRUE. However, if we // do get this message, it's invalid and we don't implement it. hr = E_NOTIMPL; break; // The remaining messages do not require any action from this MFT. case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: case MFT_MESSAGE_NOTIFY_END_STREAMING: case MFT_MESSAGE_NOTIFY_END_OF_STREAM: case MFT_MESSAGE_NOTIFY_START_OF_STREAM: break; } return hr; } ///////////////////////////////////////////// create Topology Method HRESULT WmfPlayer::CreateTopology( const DtkComPtr<IMFMediaSource>& pSource, HWND hVideoWnd, DtkComPtr<IMFTopology>& outTopology ) { UNREFERENCED_PARAMETER(hVideoWnd); if (!pSource) return E_POINTER; // Output Smart Pointer leeren outTopology.Reset(); DtkComPtr<IMFTopology> pTopology; DtkComPtr<IMFPresentationDescriptor> pPD; HRESULT hr = MFCreateTopology(&pTopology); if (FAILED(hr)) return hr; hr = pSource->CreatePresentationDescriptor(&pPD); if (FAILED(hr)) return hr; DWORD streamCount = 0; hr = pPD->GetStreamDescriptorCount(&streamCount); if (FAILED(hr)) return hr; for (DWORD i = 0; i < streamCount; ++i) { BOOL fSelected = FALSE; DtkComPtr<IMFStreamDescriptor> pSD; hr = pPD->GetStreamDescriptorByIndex(i, &fSelected, &pSD); if (FAILED(hr)) return hr; if (!fSelected) continue; DtkComPtr<IMFTopologyNode> pSourceNode; DtkComPtr<IMFTopologyNode> pOutputNode; DtkComPtr<IMFTopologyNode> pTransformNode; DtkComPtr<IMFMediaTypeHandler> pHandler; DtkComPtr<IMFMediaType> pType; GUID majorType = GUID_NULL; hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pSourceNode); if (FAILED(hr)) return hr; hr = pSourceNode->SetUnknown(MF_TOPONODE_SOURCE, pSource.Get()); if (FAILED(hr)) return hr; hr = pSourceNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD.Get()); if (FAILED(hr)) return hr; hr = pSourceNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD.Get()); if (FAILED(hr)) return hr; hr = pSD->GetMediaTypeHandler(&pHandler); if (FAILED(hr)) return hr; hr = pHandler->GetCurrentMediaType(&pType); if (FAILED(hr)) return hr; hr = pType->GetMajorType(&majorType); if (FAILED(hr)) return hr; if (majorType == MFMediaType_Video) { DtkComPtr<IMFActivate> pRendererActivate = m_renderer ? m_renderer->GetActivate() : nullptr; if (!pRendererActivate) return E_POINTER; hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pOutputNode); if (FAILED(hr)) return hr; hr = pOutputNode->SetObject(pRendererActivate.Get()); if (FAILED(hr)) return hr; //create Transformer hr = WmfTransformer::CreateInstance(m_hwnd, m_callbackMessage,NULL,IID_PPV_ARGS(&m_transform)); if (FAILED(hr)) return hr; auto* pReal = static_cast<WmfTransformer*>(pMFT.Get()); //pReal->SetStopTime(FramesToTime(m_stopFrame)); DtkComPtr<IMFTopologyNode> pTransformNode; hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &pTransformNode); if (FAILED(hr)) return hr; hr = pTransformNode->SetObject(pMFT.Get()); if (FAILED(hr)) return hr; hr = pTopology->AddNode(pSourceNode.Get()); if (FAILED(hr)) return hr; hr = pTopology->AddNode(pTransformNode.Get()); if (FAILED(hr)) return hr; hr = pTopology->AddNode(pOutputNode.Get()); if (FAILED(hr)) return hr; hr = pSourceNode->ConnectOutput(0, pTransformNode.Get(), 0); if (FAILED(hr)) return hr; hr = pTransformNode->ConnectOutput(0, pOutputNode.Get(), 0); if (FAILED(hr)) return hr; } else if (majorType == MFMediaType_Audio) { IMFActivate* pRendererActivateRaw = nullptr; hr = MFCreateAudioRendererActivate(&pRendererActivateRaw); if (FAILED(hr)) return hr; DtkComPtr<IMFActivate> pRendererActivate(pRendererActivateRaw); hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pOutputNode); if (FAILED(hr)) return hr; hr = pOutputNode->SetObject(pRendererActivate.Get()); if (FAILED(hr)) return hr; } } // Übergabe an Output Parameter outTopology = pTopology; return hr; }
Read Entire Article