Click here to Skip to main content
15,888,803 members
Articles / Programming Languages / C++

Capturing Video from Web-camera on Windows 7 and 8 by using Media Foundation

Rate me:
Please Sign up or sign in to vote.
4.96/5 (25 votes)
10 Apr 2013CPOL5 min read 280.6K   33.1K   71  
Simple lib for capturing video from web-camera by using Media Foundation
#include <new>
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfobjects.h>
#include <Shlwapi.h>
#include <stdio.h>


#include "ImageGrabber.h"
#include "Common.h"
#include "DebugPrintOut.h"
#include "RawImage.h"
#include "FormatReader.h"


#pragma comment(lib, "mfplat")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "Shlwapi")


#define CHECK_HR(x) if (FAILED(x)) { goto done; }

ImageGrabber::ImageGrabber(unsigned int deviceID): m_cRef(1), ig_DeviceID(deviceID), ig_pSource(NULL), ig_pSession(NULL), ig_pTopology(NULL), ig_RIE(true), ig_Close(false)
{
}


ImageGrabber::~ImageGrabber(void)
{
	if (ig_pSession)
    {
		ig_pSession->Shutdown();
    }
					
	SafeReleaseAllCount(&ig_pSession);

	SafeReleaseAllCount(&ig_pTopology);

	DebugPrintOut *DPO = &DebugPrintOut::getInstance();

	DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Destroing instance of the ImageGrabber class \n", ig_DeviceID);

}

HRESULT ImageGrabber::initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat)
{
    IMFActivate *pSinkActivate = NULL;
    IMFMediaType *pType = NULL;

	IMFPresentationDescriptor *pPD = NULL;
    IMFStreamDescriptor *pSD = NULL;
    IMFMediaTypeHandler *pHandler = NULL;
    IMFMediaType *pCurrentType = NULL;

	HRESULT hr = S_OK;
	MediaType MT;

	 // Clean up.
    if (ig_pSession)
    {
        ig_pSession->Shutdown();
    }

    SafeReleaseAllCount(&ig_pSession);
    SafeReleaseAllCount(&ig_pTopology);

	ig_pSource = pSource;




    hr = pSource->CreatePresentationDescriptor(&pPD);
    if (FAILED(hr))
    {
        goto err;
    }

    BOOL fSelected;
    hr = pPD->GetStreamDescriptorByIndex(0, &fSelected, &pSD);
    if (FAILED(hr))
    {
        goto err;
    }

    hr = pSD->GetMediaTypeHandler(&pHandler);
    if (FAILED(hr))
    {
        goto err;
    }

    DWORD cTypes = 0;
    hr = pHandler->GetMediaTypeCount(&cTypes);
    if (FAILED(hr))
    {
        goto err;
    }

    if(cTypes > 0)
    {
		hr = pHandler->GetCurrentMediaType(&pCurrentType);

        if (FAILED(hr))
        {
            goto err;
        }

        MT = FormatReader::Read(pCurrentType);
    }

err:
    SafeReleaseAllCount(&pPD);
    SafeRelease(&pSD);
    SafeRelease(&pHandler);
    SafeRelease(&pCurrentType);

	unsigned int sizeRawImage = 0;
	
	if(VideoFormat == MFVideoFormat_RGB24)
	{
		sizeRawImage = MT.MF_MT_FRAME_SIZE * 3;
	}
	else
	if(VideoFormat == MFVideoFormat_RGB32)
	{
		sizeRawImage = MT.MF_MT_FRAME_SIZE * 4;
	}

	RawImage *ig_temp;
		
	CHECK_HR(hr = RawImage::CreateInstance(&ig_temp, sizeRawImage));
		
	ig_RIFirst.reset(ig_temp);
	
	CHECK_HR(hr = RawImage::CreateInstance(&ig_temp, sizeRawImage));
		
	ig_RISecond.reset(ig_temp);

	ig_RIOut = ig_RISecond.get();
	
		
    // Configure the media type that the Sample Grabber will receive.
    // Setting the major and subtype is usually enough for the topology loader
    // to resolve the topology.

    CHECK_HR(hr = MFCreateMediaType(&pType));
    CHECK_HR(hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
    CHECK_HR(hr = pType->SetGUID(MF_MT_SUBTYPE, VideoFormat));

    // Create the sample grabber sink.
    CHECK_HR(hr = MFCreateSampleGrabberSinkActivate(pType, this, &pSinkActivate));

    // To run as fast as possible, set this attribute (requires Windows 7):
    CHECK_HR(hr = pSinkActivate->SetUINT32(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, TRUE));

    // Create the Media Session.
    
    CHECK_HR(hr = MFCreateMediaSession(NULL, &ig_pSession));
	
    // Create the topology.
    CHECK_HR(hr = CreateTopology(pSource, pSinkActivate, &ig_pTopology));
	
done:

	// Clean up.
	if (FAILED(hr))
	{		
		if (ig_pSession)
		{
			ig_pSession->Shutdown();
		}

		SafeRelease(&ig_pSession);
		SafeRelease(&ig_pTopology);
	}

    SafeRelease(&pSinkActivate);
    SafeRelease(&pType);

	return hr;
}

void ImageGrabber::stopGrabbing()
{
	if(ig_pSession)
		ig_pSession->Stop();

	DebugPrintOut *DPO = &DebugPrintOut::getInstance();
	
	DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Stopping of of grabbing of images\n", ig_DeviceID);
}

HRESULT ImageGrabber::startGrabbing(void)
{
	DebugPrintOut *DPO = &DebugPrintOut::getInstance();

	IMFMediaEvent *pEvent = NULL;
	
    PROPVARIANT var;
    PropVariantInit(&var);

    HRESULT hr = S_OK;
    CHECK_HR(hr = ig_pSession->SetTopology(0, ig_pTopology));
    CHECK_HR(hr = ig_pSession->Start(&GUID_NULL, &var));

	DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Start Grabbing of the images\n", ig_DeviceID);

    while (1)
    {
		
        HRESULT hrStatus = S_OK;
        MediaEventType met;

		if(!ig_pSession) break;
        hr = ig_pSession->GetEvent(0, &pEvent);
		if(!SUCCEEDED(hr))
		{
			hr = S_OK;

			goto done;
		}

        hr = pEvent->GetStatus(&hrStatus);
		if(!SUCCEEDED(hr))
		{
			hr = S_OK;

			goto done;
		}

        hr = pEvent->GetType(&met);
		if(!SUCCEEDED(hr))
		{
			hr = S_OK;

			goto done;
		}

        if (met == MESessionEnded)
        {			
			DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: MESessionEnded \n", ig_DeviceID);

			ig_pSession->Stop();

            break;
        }

		if (met == MESessionStopped)
        {
			DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: MESessionStopped \n", ig_DeviceID);
			
            break;
        }


		if (met == MEVideoCaptureDeviceRemoved)
        {
			DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: MEVideoCaptureDeviceRemoved \n", ig_DeviceID);
			
            break;       
        }

        SafeRelease(&pEvent);
    }

	DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Finish startGrabbing \n", ig_DeviceID);
			
done:



    SafeRelease(&pEvent);

	SafeRelease(&ig_pSession);

	SafeRelease(&ig_pTopology);
    return hr;
}

HRESULT ImageGrabber::CreateTopology(IMFMediaSource *pSource, IMFActivate *pSinkActivate, IMFTopology **ppTopo)
{
    IMFTopology *pTopology = NULL;
    IMFPresentationDescriptor *pPD = NULL;
    IMFStreamDescriptor *pSD = NULL;
    IMFMediaTypeHandler *pHandler = NULL;
    IMFTopologyNode *pNode1 = NULL;
    IMFTopologyNode *pNode2 = NULL;

    HRESULT hr = S_OK;
    DWORD cStreams = 0;

    CHECK_HR(hr = MFCreateTopology(&pTopology));
    CHECK_HR(hr = pSource->CreatePresentationDescriptor(&pPD));
    CHECK_HR(hr = pPD->GetStreamDescriptorCount(&cStreams));
    
    for (DWORD i = 0; i < cStreams; i++)
    {
        // In this example, we look for audio streams and connect them to the sink.

        BOOL fSelected = FALSE;
        GUID majorType;

        CHECK_HR(hr = pPD->GetStreamDescriptorByIndex(i, &fSelected, &pSD));
        CHECK_HR(hr = pSD->GetMediaTypeHandler(&pHandler));
        CHECK_HR(hr = pHandler->GetMajorType(&majorType));

        if (majorType == MFMediaType_Video && fSelected)
        {
            CHECK_HR(hr = AddSourceNode(pTopology, pSource, pPD, pSD, &pNode1));
            CHECK_HR(hr = AddOutputNode(pTopology, pSinkActivate, 0, &pNode2));
            CHECK_HR(hr = pNode1->ConnectOutput(0, pNode2, 0));
            break;
        }
        else
        {
            CHECK_HR(hr = pPD->DeselectStream(i));
        }
        SafeRelease(&pSD);
        SafeRelease(&pHandler);
    }

    *ppTopo = pTopology;
    (*ppTopo)->AddRef();

done:
    SafeRelease(&pTopology);
    SafeRelease(&pNode1);
    SafeRelease(&pNode2);
    SafeRelease(&pPD);
    SafeRelease(&pSD);
    SafeRelease(&pHandler);
    return hr;
}

HRESULT ImageGrabber::AddSourceNode(
    IMFTopology *pTopology,           // Topology.
    IMFMediaSource *pSource,          // Media source.
    IMFPresentationDescriptor *pPD,   // Presentation descriptor.
    IMFStreamDescriptor *pSD,         // Stream descriptor.
    IMFTopologyNode **ppNode)         // Receives the node pointer.
{
    IMFTopologyNode *pNode = NULL;

    HRESULT hr = S_OK;
    CHECK_HR(hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode));
    CHECK_HR(hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource));
    CHECK_HR(hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD));
    CHECK_HR(hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD));
    CHECK_HR(hr = pTopology->AddNode(pNode));

    // Return the pointer to the caller.
    *ppNode = pNode;
    (*ppNode)->AddRef();

done:
    SafeRelease(&pNode);
    return hr;
}

HRESULT ImageGrabber::AddOutputNode(
    IMFTopology *pTopology,     // Topology.
    IMFActivate *pActivate,     // Media sink activation object.
    DWORD dwId,                 // Identifier of the stream sink.
    IMFTopologyNode **ppNode)   // Receives the node pointer.
{
    IMFTopologyNode *pNode = NULL;

    HRESULT hr = S_OK;
    CHECK_HR(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode));
    CHECK_HR(hr = pNode->SetObject(pActivate));
    CHECK_HR(hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId));
    CHECK_HR(hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE));
    CHECK_HR(hr = pTopology->AddNode(pNode));

    // Return the pointer to the caller.
    *ppNode = pNode;
    (*ppNode)->AddRef();

done:
    SafeRelease(&pNode);
    return hr;
}



HRESULT ImageGrabber::CreateInstance(ImageGrabber **ppIG, unsigned int deviceID)
{
    *ppIG = new (std::nothrow) ImageGrabber(deviceID);

    if (ppIG == NULL)
    {
        return E_OUTOFMEMORY;
    }

	DebugPrintOut *DPO = &DebugPrintOut::getInstance();
	
	DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Creating instance of ImageGrabber\n", deviceID);

    return S_OK;
}

STDMETHODIMP ImageGrabber::QueryInterface(REFIID riid, void** ppv)
{
	// Creation tab of shifting interfaces from start of this class
    static const QITAB qit[] = 
    {

        QITABENT(ImageGrabber, IMFSampleGrabberSinkCallback),
        QITABENT(ImageGrabber, IMFClockStateSink),
        { 0 }
    };
    return QISearch(this, qit, riid, ppv);
}

STDMETHODIMP_(ULONG) ImageGrabber::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) ImageGrabber::Release()
{
    ULONG cRef = InterlockedDecrement(&m_cRef);
    if (cRef == 0)
    {
        delete this;
    }
    return cRef;
}

STDMETHODIMP ImageGrabber::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset)
{
    return S_OK;
}

STDMETHODIMP ImageGrabber::OnClockStop(MFTIME hnsSystemTime)
{
    return S_OK;
}

STDMETHODIMP ImageGrabber::OnClockPause(MFTIME hnsSystemTime)
{
    return S_OK;
}

STDMETHODIMP ImageGrabber::OnClockRestart(MFTIME hnsSystemTime)
{
    return S_OK;
}

STDMETHODIMP ImageGrabber::OnClockSetRate(MFTIME hnsSystemTime, float flRate)
{
    return S_OK;
}

STDMETHODIMP ImageGrabber::OnSetPresentationClock(IMFPresentationClock* pClock)
{
    return S_OK;
}

STDMETHODIMP ImageGrabber::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags,
    LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer,
    DWORD dwSampleSize)
{	
	if(ig_RIE)
	{
		ig_RIFirst->fastCopy(pSampleBuffer);

		ig_RIOut = ig_RIFirst.get();
	}
	else
	{
		ig_RISecond->fastCopy(pSampleBuffer);

		ig_RIOut = ig_RISecond.get();
	}

	ig_RIE = !ig_RIE;
		

    return S_OK;
}

STDMETHODIMP ImageGrabber::OnShutdown()
{
    return S_OK;
}

RawImage *ImageGrabber::getRawImage()
{
	return ig_RIOut;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions