Click here to Skip to main content
15,891,136 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.9K   33.1K   71  
Simple lib for capturing video from web-camera by using Media Foundation
//////////////////////////////////////////////////////////////////////////
//
// preview.cpp : Preview helper class.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////

#include <new>
#include <windows.h>
#include <mfplay.h>
#include <Dbt.h>
#include <shlwapi.h>

#include "Preview.h"

#include "videoInput.h"




void ShowErrorMessage(HWND hwnd, PCWSTR format, HRESULT hr);

//-------------------------------------------------------------------
//  CreateInstance
//  Static class method to create the CPreview object.
//
//  hVideo:   Handle to the video window.
//  ppPlayer: Receives an AddRef's pointer to the CPreview object.
//            The caller must release the pointer.
//-------------------------------------------------------------------

HRESULT CPreview::CreateInstance(HWND hVideo, CPreview **ppPlayer)
{
    if (ppPlayer == NULL)
    {
        return E_POINTER;
    }

    CPreview *pPlayer = new (std::nothrow) CPreview(hVideo);

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

    // The CPreview constructor sets the ref count to 1.
    *ppPlayer = pPlayer;

    return S_OK;
}


//-------------------------------------------------------------------
//  CPreview constructor
//-------------------------------------------------------------------

CPreview::CPreview(HWND hVideo) :
    m_pPlayer(NULL),
    m_pSource(NULL),
    m_nRefCount(1),
    m_hwnd(hVideo),
    m_bHasVideo(FALSE),
    m_pwszSymbolicLink(NULL),
    m_cchSymbolicLink(0)
{

}

//-------------------------------------------------------------------
//  CPreview destructor
//-------------------------------------------------------------------

CPreview::~CPreview()
{
    CloseDevice();
}


//// IUnknown methods


//-------------------------------------------------------------------
//  AddRef
//-------------------------------------------------------------------

ULONG CPreview::AddRef()
{
    return InterlockedIncrement(&m_nRefCount);
}


//-------------------------------------------------------------------
//  Release
//-------------------------------------------------------------------

ULONG CPreview::Release()
{
    ULONG uCount = InterlockedDecrement(&m_nRefCount);
    if (uCount == 0)
    {
        delete this;
    }
    // For thread safety, return a temporary variable.
    return uCount;
}



//-------------------------------------------------------------------
//  QueryInterface
//-------------------------------------------------------------------

HRESULT CPreview::QueryInterface(REFIID riid, void** ppv)
{
    static const QITAB qit[] =
    {
        QITABENT(CPreview, IMFPMediaPlayerCallback),
        { 0 },
    };
    return QISearch(this, qit, riid, ppv);
}

//// IMFPMediaPlayerCallback methods


//-------------------------------------------------------------------
//  OnMediaPlayerEvent
//
//  This method is called by the MFPlay object to send events to
//  the application. For live preview, there are not many events to
//  worry about. (For example, no end-of-stream, paused, or stopped
//  events.)
//-------------------------------------------------------------------

void STDMETHODCALLTYPE CPreview::OnMediaPlayerEvent(MFP_EVENT_HEADER * pEventHeader)
{

    if (FAILED(pEventHeader->hrEvent))
    {
        ShowErrorMessage(NULL, L"Preview error.", pEventHeader->hrEvent);
        return;
    }

    switch (pEventHeader->eEventType)
    {
    case MFP_EVENT_TYPE_MEDIAITEM_CREATED:
        OnMediaItemCreated(MFP_GET_MEDIAITEM_CREATED_EVENT(pEventHeader));
        break;

    case MFP_EVENT_TYPE_MEDIAITEM_SET:
        OnMediaItemSet(MFP_GET_MEDIAITEM_SET_EVENT(pEventHeader));
        break;
    }
}


//// Class methods


//-------------------------------------------------------------------
//  SetDevice
//
//  Sets the capture device source on the player.
//
//  pActivate: Pointer to the activation object for the device
//             source.
//-------------------------------------------------------------------

HRESULT CPreview::SetDevice(ChooseDeviceParam pParam)
{
    HRESULT hr = S_OK;

    IMFMediaSource *pSource = NULL;

    // Release the current instance of the player (if any).
    CloseDevice();

    // Create a new instance of the player.
    hr = MFPCreateMediaPlayer(
        NULL,   // URL
        FALSE,
        0,      // Options
        this,   // Callback
        m_hwnd,
        &m_pPlayer
        );

    // Create the media source for the device.
    if (SUCCEEDED(hr))
    {
        //hr = pActivate->ActivateObject(
        //    __uuidof(IMFMediaSource),
        //    (void**)&pSource
        //    );

		videoInput *VI = &videoInput::getInstance();

		if(VI->setupDevice(pParam.selection, pParam.selectedtype))
		{
			pSource = VI->getMediaSource(pParam.selection);
		}
		else
		{
			hr = -1;
		}
    }

    // Get the symbolic link. This is needed to handle device-
    // loss notifications. (See CheckDeviceLost.)

    //if (SUCCEEDED(hr))
    //{
    //    hr = pActivate->GetAllocatedString(
    //        MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
    //        &m_pwszSymbolicLink,
    //        &m_cchSymbolicLink
    //        );

    //}

    // Create a new media item for this media source.
    if (SUCCEEDED(hr))
    {
        hr = m_pPlayer->CreateMediaItemFromObject(
            pSource,
            FALSE,  // FALSE = asynchronous call
            0,
            NULL
            );
    }

    // When the method completes, MFPlay will call OnMediaPlayerEvent
    // with the MFP_EVENT_TYPE_MEDIAITEM_CREATED event.

    if (SUCCEEDED(hr))
    {
        m_pSource = pSource;
        m_pSource->AddRef();
    }

    if (FAILED(hr))
    {
        CloseDevice();
    }

    //if (pSource)
    //{
    //    pSource->Release();
    //}
    return hr;
}


//-------------------------------------------------------------------
//  OnMediaItemCreated
//
//  Called when the IMFPMediaPlayer::CreateMediaItemFromObject method
//  completes.
//-------------------------------------------------------------------

void CPreview::OnMediaItemCreated(MFP_MEDIAITEM_CREATED_EVENT *pEvent)
{
    HRESULT hr = S_OK;

    if (m_pPlayer)
    {
        // Check if there is video.

        BOOL bHasVideo = FALSE, bIsSelected = FALSE;

        hr = pEvent->pMediaItem->HasVideo(&bHasVideo, &bIsSelected);

        if (SUCCEEDED(hr))
        {
            m_bHasVideo = bHasVideo && bIsSelected;

            // Set this media item on the player.
            hr = m_pPlayer->SetMediaItem(pEvent->pMediaItem);
        }
    }

    if (FAILED(hr))
    {
        ShowErrorMessage(NULL, L"Preview error.", hr);
    }
}

//-------------------------------------------------------------------
//  OnMediaItemCreated
//
//  Called when the IMFPMediaPlayer::SetMediaItem method completes.
//-------------------------------------------------------------------

void CPreview::OnMediaItemSet(MFP_MEDIAITEM_SET_EVENT * /*pEvent*/)
{
    HRESULT hr = S_OK;

    SIZE szVideo = { 0 };
    RECT rc = { 0 };

    // Adjust the preview window to match the native size
    // of the captured video.

    hr = m_pPlayer->GetNativeVideoSize(&szVideo, NULL);

    if (SUCCEEDED(hr))
    {
        SetRect(&rc, 0, 0, szVideo.cx, szVideo.cy);

        AdjustWindowRect(
            &rc,
            GetWindowLong(m_hwnd, GWL_STYLE),
            TRUE
            );

        SetWindowPos(m_hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
            SWP_NOZORDER | SWP_NOMOVE | SWP_NOOWNERZORDER);

        hr = m_pPlayer->Play();
    }

    if (FAILED(hr))
    {
        ShowErrorMessage(NULL, L"Preview error.", hr);
    }
}



//-------------------------------------------------------------------
//  UpdateVideo
//
//  Repaints and resizes the video image. The application calls this
//  method when it receives a WM_PAINT or WM_SIZE message.
//-------------------------------------------------------------------

HRESULT CPreview::UpdateVideo()
{
    HRESULT hr = S_OK;

    if (m_pPlayer)
    {
        hr = m_pPlayer->UpdateVideo();
    }
    return hr;
}


//-------------------------------------------------------------------
//  CloseDevice
//  Releases the video capture device.
//-------------------------------------------------------------------

HRESULT CPreview::CloseDevice()
{
    HRESULT hr = S_OK;

    if (m_pPlayer)
    {
        m_pPlayer->Shutdown();
        m_pPlayer->Release();
        m_pPlayer = NULL;
    }


	videoInput *VI = &videoInput::getInstance();

	VI->closeAllDevices();

    m_bHasVideo = FALSE;

    CoTaskMemFree(m_pwszSymbolicLink);
    m_pwszSymbolicLink = NULL;

    m_cchSymbolicLink = 0;

    return hr;
}


//-------------------------------------------------------------------
//  CheckDeviceLost
//  Checks whether the video capture device was removed.
//
//  The application calls this method when is receives a
//  WM_DEVICECHANGE message.
//-------------------------------------------------------------------

HRESULT CPreview::CheckDeviceLost(DEV_BROADCAST_HDR *pHdr, BOOL *pbDeviceLost)
{
    DEV_BROADCAST_DEVICEINTERFACE *pDi = NULL;

    if (pbDeviceLost == NULL)
    {
        return E_POINTER;
    }

    *pbDeviceLost = FALSE;

    if (m_pSource == NULL)
    {
        return S_OK;
    }
    if (pHdr == NULL)
    {
        return S_OK;
    }
    if (pHdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
    {
        return S_OK;
    }

    // Compare the device name with the symbolic link.

    pDi = (DEV_BROADCAST_DEVICEINTERFACE*)pHdr;

    if (m_pwszSymbolicLink)
    {
        if (_wcsicmp(m_pwszSymbolicLink, pDi->dbcc_name) == 0)
        {
            *pbDeviceLost = TRUE;
        }
    }

    return S_OK;
}

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