Figure 1 Filter Graph
Figure 2 Building a File Playback Graph
#include <dshow.h>
void main(void)
{
IGraphBuilder *pGraph = 0;
IMediaControl *pMediaControl = 0;
IMediaEvent *pEvent = 0;
CoInitialize(NULL);
// Create the FGM and query for interfaces.
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
// Obtain the interface used to run, stop, and pause the graph
pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
// Obtain the interface to receive events from the graph
pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
// Build the graph. IMPORTANT: Change string to a file on your
// system.
pGraph->RenderFile(L"C:\\Example.avi", NULL);
// Run the graph.
pMediaControl->Run();
// Wait for completion.
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
// Clean up.
pMediaControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize();
}
Figure 4 Building a Video Capture Graph
#include <dshow.h>
int main (void)
{
IGraphBuilder *pGraph = 0;
ICreateDevEnum *pDevEnum = 0;
ICaptureGraphBuilder2 *pBuild = 0;
HRESULT hr;
CoInitialize(NULL);
// Create the FGM.
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph );
// Create the capture graph builder helper object
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2,
(void **)&pBuild );
// Tell the capture graph builder about the FGM.
hr = pBuild->SetFiltergraph(pGraph);
// Create a helper object to find the capture device.
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (LPVOID*)&pDevEnum);
// Use the first capture filter that we find.
IEnumMoniker* pEnum = 0;
IMoniker* pMoniker = 0;
IBaseFilter *pCapture = 0;
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&pEnum, 0);
if (S_OK == pEnum->Next(1, &pMoniker, NULL))
{
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void
**)&pCapture);
}
pMoniker->Release();
pEnum->Release();
pDevEnum->Release();
// Add the capture filter to the filter graph
hr = pGraph->AddFilter(pCapture, L"CaptureFilter");
// Build the preview part of the graph.
hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
pCapture, NULL, NULL);
// Build the file writing part of the graph.
IBaseFilter *pMux = 0;
pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, L"C:\\test.avi",
&pMux, 0);
hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
pCapture, NULL, pMux);
// Run the graph. (Not shown.)
pGraph->Release();
pBuild->Release();
pCapture->Release();
pMux->Release();
CoUninitialize();
return 0;
}
Figure 9 CheckInputType Method
HRESULT CYuvGray::CheckInputType(const CMediaType *pmt)
{
if ((pmt->majortype == MEDIATYPE_Video) &&
(pmt->subtype == MEDIASUBTYPE_UYVY) &&
(pmt->formattype == FORMAT_VideoInfo) &&
(pmt->pbFormat != NULL) &&
(pmt->cbFormat >= sizeof(VIDEOINFOHEADER)))
{
VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER*>
(pmt->pbFormat);
BITMAPINFOHEADER *pBmi = &(pVih->bmiHeader);
if ((pBmi->biBitCount == 16) &&
(pBmi->biCompression == FCC('UYVY')) &&
(pBmi->biSizeImage >= DIBSIZE(*pBmi)))
{
return S_OK;
}
}
return VFW_E_TYPE_NOT_ACCEPTED;
}
Figure 10 CheckTransform Method
HRESULT CYuvGray::CheckTransform(const CMediaType *mtIn, const
CMediaType *mtOut)
{
if (!IsValidUYVY(mtOut))
{
return VFW_E_TYPE_NOT_ACCEPTED;
}
BITMAPINFOHEADER *pBmi = HEADER(mtIn);
BITMAPINFOHEADER *pBmi2 = HEADER(mtOut);
if ((pBmi->biWidth <= pBmi2->biWidth) &&
(pBmi->biHeight == abs(pBmi2->biHeight)))
{
return S_OK;
}
return VFW_E_TYPE_NOT_ACCEPTED;
}
Figure 11 DecideBufferSize Method
HRESULT CYuvGray::DecideBufferSize(
IMemAllocator *pAlloc, // Pointer the downstream allocator
ALLOCATOR_PROPERTIES *pProp) // Downstream filter's buffer
// requirements (may be all zeroes)
{
// Make sure our input pin connected.
if (!m_pInput->IsConnected())
{
return E_UNEXPECTED;
}
// Find out the existing properties on the upstream allocator.
// First, get a pointer to the upstream allocator...
ALLOCATOR_PROPERTIES InputProps;
IMemAllocator *pAllocInput = 0;
HRESULT hr = m_pInput->GetAllocator(&pAllocInput);
if (FAILED(hr))
{
return hr;
}
// ... then get the properties.
hr = pAllocInput->GetProperties(&InputProps);
pAllocInput->Release();
if (FAILED(hr))
{
return hr;
}
// Now find a suitable set of properties for the downstream
// allocator.
if (pProp->cbAlign == 0)
{
pProp->cbAlign = 1;
}
if (pProp->cbBuffer == 0)
{
pProp->cBuffers = 1;
}
pProp->cbBuffer = max(InputProps.cbBuffer, pProp->cbBuffer);
// Set the allocator properties
ALLOCATOR_PROPERTIES ActualProp;
hr = pAlloc->SetProperties(pProp, &ActualProp);
if (FAILED(hr))
{
return hr;
}
// Check what we got.... Even if SetProperties() succeeds, the
// allocator might not cooperate. The only property we care about
// is buffer size.
if (InputProps.cbBuffer > ActualProp.cbBuffer)
{
return E_FAIL;
}
return S_OK;
}
Figure 13 Transform Method
HRESULT CYuvGray::Transform(IMediaSample *pSource, IMediaSample *pDest)
{
// Look for format changes from the video renderer.
CMediaType *pmt = 0;
if (S_OK == pDest->GetMediaType((AM_MEDIA_TYPE**)&pmt) && pmt)
{
// Notify our own output pin about the new type.
m_pOutput->SetMediaType(pmt);
DeleteMediaType(pmt);
}
BYTE *pBufferIn, *pBufferOut;
pSource->GetPointer(&pBufferIn);
pDest->GetPointer(&pBufferOut);
// Process the buffers.
long cbByte = m_VihOut.bmiHeader.biSizeImage;
HRESULT hr = ProcessFrame(pBufferIn, pBufferOut);
pDest->SetActualDataLength(cbByte);
return hr;
}
Figure 14 ProcessFrame Method
HRESULT CYuvGray::ProcessFrame(BYTE *pbInput, BYTE *pbOutput)
{
DWORD dwWidth, dwHeight;
LONG lStrideIn, lStrideOut; // Stride in bytes
BYTE *pbSource, *pbTarget;
GetVideoInfoParameters(&m_VihIn, pbOutput, &dwWidth, &dwHeight,
&lStride, &pbTarget, true);
GetVideoInfoParameters(&m_VihOut, pbInput, &dwWidth, &dwHeight,
&lStride, &pbSource, true);
for (DWORD y = 0; y < dwHeight; y++)
{
WORD *pwTarget = (WORD*)pbTarget;
WORD *pwSource = (WORD*)pbSource;
for (DWORD x = 0; x < dwWidth; x++)
{
// Each WORD is a 'UY' or 'VY' block.
// Set the low byte (chroma) to 0x80 and leave the high byte
// (luma)
WORD pixel = pwSource[x] & 0xFF00;
pixel |= 0x0080;
pwTarget[x] = pixel;
}
// Advance the stride on both buffers.
pbTarget += lStrideIn;
pbSource += lStrideOut;
}
return S_OK;
}
Figure 16 Class Factory Template
static const WCHAR g_wszName[] = L"YUV Filter"; // Friendly name.
AMOVIESETUP_FILTER FilterInfo =
{
&CLSID_YuvGray, // CLSID
g_wszName, // Name
MERIT_DO_NOT_USE, // Merit
0, // Number of AMOVIESETUP_PIN structs
NULL // Pin registration information.
};
CFactoryTemplate g_Templates[1] =
{
{
g_wszName, // Name
&CLSID_YuvGray, // CLSID
CYuvGray::CreateInstance, // Method to create an instance of
// MyComponent
NULL, // Initialization function
&FilterInfo // Set-up information (for filters)
}
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
|