http://blog.csdn.net/luckyboy101/article/details/7832179
讲了直接在MFC中用Directshow Transform filter,但是在运行中发现,视频的彩色空间(color space,可以由格式工厂软件查看)通常是YUV,而我们对视频编辑通常是在RGB空间,所以需要增加一个color space converter把YUV彩色空间转换为RGB24彩色空间。
这样就需要在video render之前增加两个filter color space converter + transform filter, 发现一次增加1个filter无法连接,所以要同时一次性把两个filter加入链路,代码如下:
IPFilter.h
// // Sample DirectShow In-Place Transform Filter that accepts data for use in application // #include <streams.h> // This is an example in-place transform filter that is created within // the application, and not by CoCreateInstance class CAppTransform : public CTransInPlaceFilter { public: CAppTransform(LPUNKNOWN pUnkOuter, HRESULT *phr); HRESULT CheckInputType(const CMediaType* mtIn); HRESULT Transform(IMediaSample *pSample); }; // DirectShow graph management sample code: // This builds a playback graph using RenderFile // and then inserts a transform filter on the uncompressed video. class CAppGraphBuilder { private: CAppTransform* m_pFilter; IGraphBuilder* m_pGraph; DWORD m_dwObjectTable; public: CAppGraphBuilder(); ~CAppGraphBuilder(); void DestroyGraph(void); HRESULT BuildFromFile(LPCWSTR pszFile); HRESULT Run(void); HRESULT MakeChild(HWND hwnd); HRESULT ResizeVideoWindow(RECT* prc); private: void CreateAppFilter(void); HRESULT FindFilterByInterface(REFIID riid, IBaseFilter** ppFilter); HRESULT ConnectUpstreamOf(IBaseFilter* pFilter,IBaseFilter*pColor, IBaseFilter* pTransform); HRESULT NextUpstream(IBaseFilter* pFilter, IBaseFilter** ppNext); HRESULT CAppGraphBuilder::AddFilterByCLSID(IGraphBuilder *pGraph,const GUID& clsid,LPCWSTR wszName,IBaseFilter **ppF); IPin* GetPin(IBaseFilter* pFilter, PIN_DIRECTION dirRequest); // Helper methods IPin* InputPinOf(IBaseFilter* pFilter) { return GetPin(pFilter, PINDIR_INPUT); } IPin* OutputPinOf(IBaseFilter* pFilter) { return GetPin(pFilter, PINDIR_OUTPUT); } void AddToObjectTable(void) ; void RemoveFromObjectTable(void); };
// // Sample DirectShow In-Place Transform Filter that accepts data for use in application // #include "stdafx.h" #include "IPFilter.h" //////////////////////////////////////////////////////////////////////////////// CAppTransform::CAppTransform(LPUNKNOWN pUnkOuter, HRESULT *phr) : CTransInPlaceFilter(NAME("App Transform"), pUnkOuter, GUID_NULL, phr) { } HRESULT CAppTransform::Transform(IMediaSample *pSample) { // Override to do something inside the application // Such as grabbing a poster frame... // ... BYTE *pData; // Pointer to the actual image buffer long lDataLen; // Holds length of any given sample int iPixel; // Used to loop through the image pixels tagRGBTRIPLE *prgb; // Holds a pointer to the current pixel AM_MEDIA_TYPE* pType = &m_pInput->CurrentMediaType(); VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *) pType->pbFormat; ASSERT(pvi); CheckPointer(pSample,E_POINTER); pSample->GetPointer(&pData); lDataLen = pSample->GetSize(); // Get the image properties from the BITMAPINFOHEADER int cxImage = pvi->bmiHeader.biWidth; int cyImage = pvi->bmiHeader.biHeight; int numPixels = cxImage * cyImage; // int iPixelSize = pvi->bmiHeader.biBitCount / 8; // int cbImage = cyImage * cxImage * iPixelSize; prgb = (tagRGBTRIPLE*) pData; for (iPixel=0; iPixel < numPixels; iPixel++, prgb++) { prgb->rgbtRed=255; } return S_OK; } // Check if we can support this specific proposed type and format HRESULT CAppTransform::CheckInputType(const CMediaType *pmt) { // We accept a series of raw media types if (pmt->majortype == MEDIATYPE_Video && (pmt->subtype == MEDIASUBTYPE_RGB24)) { return NOERROR; } return E_FAIL; } // --- graph building (examples) --------- CAppGraphBuilder::CAppGraphBuilder() : m_pFilter(NULL), m_pGraph(NULL), m_dwObjectTable(0) { CoInitialize(NULL); } CAppGraphBuilder::~CAppGraphBuilder() { DestroyGraph(); CoUninitialize(); } void CAppGraphBuilder::DestroyGraph(void) { if (m_pGraph) { RemoveFromObjectTable(); // ensure graph window is not child of ours IVideoWindow* pVW = NULL; HRESULT hr = m_pGraph->QueryInterface(IID_IVideoWindow, (void**)&pVW); if (SUCCEEDED(hr)) { pVW->put_Visible(OAFALSE); pVW->put_Owner(NULL); pVW->put_MessageDrain(NULL); pVW->Release(); } m_pGraph->Release(); m_pGraph = NULL; } if (m_pFilter) { m_pFilter->Release(); m_pFilter = NULL; } } HRESULT CAppGraphBuilder::AddFilterByCLSID( IGraphBuilder *pGraph, // Pointer to the Filter Graph Manager. const GUID& clsid, // CLSID of the filter to create. LPCWSTR wszName, // A name for the filter. IBaseFilter **ppF) // Receives a pointer to the filter. { if (!pGraph || ! ppF) return E_POINTER; *ppF = 0; IBaseFilter *pF = 0; HRESULT hr = CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&pF)); if (SUCCEEDED(hr)) { hr = pGraph->AddFilter(pF, wszName); if (SUCCEEDED(hr)) *ppF = pF; else pF->Release(); } return hr; } HRESULT CAppGraphBuilder::BuildFromFile(LPCWSTR pszFile) { DestroyGraph(); // Build a filter graph HRESULT hr = CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void**)&m_pGraph); if (FAILED(hr)) { return hr; } AddToObjectTable(); // render the file to build the initial graph hr = m_pGraph->RenderFile(pszFile, NULL); if (FAILED(hr)) { return hr; } // Try to find the video renderer, by looking for IVideoWindow IBaseFilter* pVR; hr = FindFilterByInterface(IID_IVideoWindow, &pVR); if (FAILED(hr)) { return hr; } // Find the media type on the input pin of the Video Renderer // to check for overlay connection where no actual data is passed IPin* pPin = InputPinOf(pVR); AM_MEDIA_TYPE mt; pPin->ConnectionMediaType(&mt); pPin->Release(); CMediaType mtIn = mt; FreeMediaType(mt); if (mtIn.subtype == MEDIASUBTYPE_Overlay) { // This connection may be a overlay mixer // need to move upstream one place IBaseFilter* pOvMix = NULL; hr = NextUpstream(pVR, &pOvMix); pVR->Release(); if (FAILED(hr)) { return hr; } pVR = pOvMix; } // Create the transform and insert in graph CreateAppFilter(); // Add Color Space Convert IBaseFilter *pColor; hr=AddFilterByCLSID(m_pGraph, CLSID_Colour, L"Color Space Converter", &pColor); // Try to insert our transform filter hr = ConnectUpstreamOf(pVR, pColor,m_pFilter); //pVR->Release(); //pColor->Release(); return hr; } // Start the graph HRESULT CAppGraphBuilder::Run(void) { IMediaControl* pControl = NULL; HRESULT hr = m_pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl); if (SUCCEEDED(hr)) { hr = pControl->Run(); pControl->Release(); } return hr; } // Make the video window a child of this app HRESULT CAppGraphBuilder::MakeChild(HWND hwnd) { if (!m_pGraph) { return E_FAIL; } IVideoWindow* pVW = NULL; HRESULT hr = m_pGraph->QueryInterface(IID_IVideoWindow, (void**)&pVW); if (SUCCEEDED(hr)) { HWND hwndOld; pVW->get_Owner((LONG*)&hwndOld); if (hwndOld != hwnd) { pVW->put_AutoShow(OAFALSE); pVW->put_Visible(OAFALSE); long WindowStyle = 0; // Tweak the video's window style to get rid of the caption and frame: hr = pVW->get_WindowStyle(&WindowStyle); if (SUCCEEDED(hr)) { WindowStyle &= ~WS_OVERLAPPEDWINDOW; // No frame junk WindowStyle |= WS_CHILD; // Needs to be child hr = pVW->put_WindowStyle(WindowStyle); } pVW->put_Owner((LONG)hwnd); pVW->put_MessageDrain((LONG)hwnd); if (hwnd != NULL) { RECT rc; GetClientRect(hwnd, &rc); pVW->SetWindowPosition( rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top); pVW->put_Visible(OATRUE); } } pVW->Release(); } return hr; } // Resize the video window HRESULT CAppGraphBuilder::ResizeVideoWindow(RECT* prc) { if (!m_pGraph) { return E_FAIL; } IVideoWindow* pVW = NULL; HRESULT hr = m_pGraph->QueryInterface(IID_IVideoWindow, (void**)&pVW); if (SUCCEEDED(hr)) { hr = pVW->SetWindowPosition( prc->left, prc->top, prc->right - prc->left, prc->bottom - prc->top); pVW->Release(); } return hr; } // Create the app-based filter and insert into graph (unconnected) void CAppGraphBuilder::CreateAppFilter(void) { if (m_pFilter) { m_pFilter->Release(); m_pFilter = NULL; } HRESULT hr = S_OK; m_pFilter = new CAppTransform(NULL, &hr); // Make the initial refcount 1 to match COM creation!!! m_pFilter->AddRef(); // Add to graph -- nb need to Query properly for the // right interface before giving that to the graph object IBaseFilter* pFilter = NULL; hr = m_pFilter->QueryInterface(IID_IBaseFilter, (void**)&pFilter); if (SUCCEEDED(hr)) { hr = m_pGraph->AddFilter(pFilter, L"App Transform"); pFilter->Release(); } } // Locate a filter within the graph by searching (from renderers upstream) // looking for a specific interface on the filter HRESULT CAppGraphBuilder::FindFilterByInterface(REFIID riid, IBaseFilter** ppFilter) { *ppFilter = NULL; IEnumFilters* pEnum; HRESULT hr = m_pGraph->EnumFilters(&pEnum); if (FAILED(hr)) { return hr; } IBaseFilter* pFilter = NULL; while (pEnum->Next(1, &pFilter, NULL) == S_OK) { // Check for required interface IUnknown* pUnk; HRESULT hrQuery = pFilter->QueryInterface(riid, (void**)&pUnk); if (SUCCEEDED(hrQuery)) { pUnk->Release(); pEnum->Release(); *ppFilter = pFilter; return S_OK; } pFilter->Release(); } pEnum->Release(); return E_FAIL; } // Connect the filter pTransform upstream of pFilter by reconnecting pins. // Assumes that pTransform has only one input and one output, and // that pFilter has only one input. HRESULT CAppGraphBuilder::ConnectUpstreamOf(IBaseFilter* pFilter, IBaseFilter*pColor,IBaseFilter* pTransform) { IPin* pPinIn = InputPinOf(pFilter); if (!pPinIn) { return E_FAIL; } // Get the peer output pin IPin* pPinOut = NULL; HRESULT hr = pPinIn->ConnectedTo(&pPinOut); if (FAILED(hr)) { pPinIn->Release(); return hr; } // Disconnect the current connection hr = m_pGraph->Disconnect(pPinOut); if (SUCCEEDED(hr)) { hr = m_pGraph->Disconnect(pPinIn); } // Insert pTransform filter by connecting its input pin and output pin if (SUCCEEDED(hr)) { IPin* pPinInCor = InputPinOf(pColor); hr = m_pGraph->Connect(pPinOut, pPinInCor); pPinInCor->Release(); } if (SUCCEEDED(hr)) { IPin* pPinInXfm = InputPinOf(pTransform); IPin* pPinOutCor = OutputPinOf(pColor); hr = m_pGraph->Connect(pPinOutCor, pPinInXfm); pPinInXfm->Release(); pPinOutCor->Release(); } if (SUCCEEDED(hr)) { IPin* pPinOutXfm = OutputPinOf(pTransform); hr = m_pGraph->Connect(pPinOutXfm, pPinIn); pPinOutXfm->Release(); } pPinIn->Release(); pPinOut->Release(); return hr; } // Find the first pin of a specific direction on a given filter IPin* CAppGraphBuilder::GetPin(IBaseFilter* pFilter, PIN_DIRECTION dirRequest) { IPin * foundPin = NULL; IEnumPins* pEnum = NULL; HRESULT hr = pFilter->EnumPins(&pEnum); if (SUCCEEDED(hr)) { IPin* pPin = NULL; while (!foundPin && pEnum->Next(1, &pPin, 0) == S_OK) { PIN_DIRECTION dir; pPin->QueryDirection(&dir); if (dir == dirRequest) { foundPin = pPin; } else { pPin->Release(); } } pEnum->Release(); } return foundPin; } // Follow the pin connections to return the filter that is // connected to the first input pin of pFilter HRESULT CAppGraphBuilder::NextUpstream(IBaseFilter* pFilter, IBaseFilter** ppNext) { IPin* pPin = InputPinOf(pFilter); if (!pPin) { return E_FAIL; } // Get the peer output pin IPin* pPinOut = NULL; HRESULT hr = pPin->ConnectedTo(&pPinOut); pPin->Release(); if (FAILED(hr)) { return hr; } PIN_INFO info; pPinOut->QueryPinInfo(&info); pPinOut->Release(); *ppNext = info.pFilter; return S_OK; } //////////////////////// For GraphEdit Dubug purpose ///////////////////////////// void CAppGraphBuilder::AddToObjectTable(void) { IMoniker * pMoniker = 0; IRunningObjectTable * objectTable = 0; if (SUCCEEDED(GetRunningObjectTable(0, &objectTable))) { WCHAR wsz[256]; wsprintfW(wsz, L"FilterGraph %08p pid %08x", (DWORD_PTR)m_pGraph, GetCurrentProcessId()); HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker); if (SUCCEEDED(hr)) { hr = objectTable->Register(0, m_pGraph, pMoniker, &m_dwObjectTable); pMoniker->Release(); } objectTable->Release(); } } void CAppGraphBuilder::RemoveFromObjectTable(void) { IRunningObjectTable * objectTable = 0; if (SUCCEEDED(GetRunningObjectTable(0, &objectTable))) { objectTable->Revoke(m_dwObjectTable); objectTable->Release(); m_dwObjectTable = 0; } }
程序在graphedit里的连接情况看上图,在app Transform后面自动增加了一个color space converter