现在的位置: 首页 > 综合 > 正文

视频播放器增加color space converter + In-Place Transform Filter

2013年10月09日 ⁄ 综合 ⁄ 共 10668字 ⁄ 字号 评论关闭

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);
};


IPFilter.cpp

//
// 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

抱歉!评论已关闭.