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

本文详细介绍了在MFC中使用DirectshowTransformfilter处理视频时,将YUV彩色空间转换为RGB24彩色空间的过程。通过在videorender之前插入colorspaceconverter和transformfilter,解决视频编辑中RGB空间需求的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.youkuaiyun.com/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

### In-Place Algorithm Implementation and Usage In-place algorithms operate directly on input data structures without requiring additional memory allocation proportional to the size of the input. These algorithms modify elements within existing storage locations rather than creating new ones, which can lead to significant performance improvements by reducing space complexity. #### Characteristics of In-Place Algorithms An in-place operation modifies an object without producing a separate result object; instead, changes occur inside the original one. For instance, sorting operations like QuickSort or Merge Sort variants may implement this approach when working with arrays where swaps happen between indices belonging to the same array[^1]. When discussing refactorings as described elsewhere, transforming non-in-place methods into their counterparts could involve altering how temporary variables store intermediate results during computation phases while preserving overall functionality[^2]. #### Example: Implementing an In-Place Operation Using Python Consider reversing a list in place: ```python def reverse_in_place(lst): start_index = 0 end_index = len(lst)-1 while start_index < end_index: lst[start_index], lst[end_index] = lst[end_index], lst[start_index] start_index += 1 end_index -= 1 sample_list = ['a', 'b', 'c'] reverse_in_place(sample_list) print(sample_list) # Output will be ['c', 'b', 'a'] ``` This function reverses `lst` without allocating extra lists for storing reversed items temporarily. The swap happens internally through index manipulation alone. #### Applications Beyond Sorting and Reversal Beyond simple transformations such as reversal or ordering sequences numerically/alphabetically, other areas benefitting from in-place processing include matrix transposition, string manipulations, graph traversals optimized via adjacency matrices, etc.[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值