首页 > 代码库 > a new Webcam Api Tutorial in C++ for Windows(Windows Media Foundation)--WMF

a new Webcam Api Tutorial in C++ for Windows(Windows Media Foundation)--WMF

Sample source code: http://pan.baidu.com/s/1o60VAEA

Foword from: http://www.dreamincode.net/forums/topic/347938-a-new-webcam-api-tutorial-in-c-for-windows/page__st__0%26


Well,


A long time ago I introduced a Webcam Tutorial.

It was for Video For Windows (VFW) an old XP api.

To be fair to myself I was running XP at the time on a Desktop PC with a usb webcam.

The program worked fine under these circumstances. It did what it said on the tin.

Then Came Vista, Windows 7 and finally Windows 8.

This muddied the waters somewhat as VFW was deprecated and performance was patchy
depending on your machine setup especially on Laptops.

Microsoft did their best to keep up with changing Technology they introduced DirectShow,
with GraphBuilders and FilterGraphs etc. It was possible to make a similar webcam program
with this technology that I had made with VFW but it was much harder to understand and get to grips with.

Then this technology was deprecated by Microsoft in favour of a new API called Windows Media Foundation (WMF).
This technology was much harder to understand than DirectShow it dealt with streams in a very different way than DirectShow and talked about topologies and sinkwriters. There was a book produced, I must say a very hard to understand book called "Developing Microsoft Media Foundation Applications" by Anton Pollinger which eased the pain of understanding the new API a little.

There was an easy to understand and implement side of WMF which was called MFPlay in which again I could write a similar program which did the same things I had implemented under VFW.

Then Microsoft deprecated MFPlay.... yes even though it was a brand new api.

So I have had a problem watching all this and considering all the options and what best course of action to take in developing a new Webcam Tutorial I have decided the best course of action is to take the newest still not deprecated route which is DirectX 3D rendered from WMF technologies, this currently seems to be Microsoft‘s preferred option and it does have a lot to give it merit as you will see you can do a lot more with WMF and DirectX 3D than you ever could with VFW.

However, this means this tutorial will be hard to understand, this once simple subject is now made very complicated.

I will try my best to guide you safely through it.

I am going to start light with the resource.rc file

resource.rc

#include "resource.h"
#include "windows.h"


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_MENU1 MENU 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "Choose &Device",              ID_FILE_CHOOSEDEVICE
		 MENUITEM "Capture Image",              ID_FILE_CAPTURE
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_CHOOSE_DEVICE DIALOGEX 0, 0, 186, 90
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Select Device"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,129,7,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,129,24,50,14
    LISTBOX         IDC_DEVICE_LIST,7,7,110,76,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
END


///////////////////////////////////////////////
//
//Images
//

IMAGE  RCDATA  "text.png"



This code sets up a Menu it has two functions
  • 1 to choose a webcam device
  • 2 a capture function which will grab a still image from the webcam.

It also includes a dialog box for choosing which webcam device to collect data from.

and finally a mystery IMAGE called text.png which I will supply later and talk about much later.

moving swiftly on...

resource.h
#define IDR_MENU1                       101
#define IDD_CHOOSE_DEVICE               102
#define IDC_LIST1                       1001
#define IDC_DEVICE_LIST                 1002
#define ID_FILE_CHOOSEDEVICE            40001
#define ID_FILE_CAPTURE                 50001
#define IDC_STATIC                      -1

#define IMAGE                          301

This sets up some defines for the resource.rc file the menu, the dialog box and finally the mystery image.

Before I go any further it would be wise to explain a little about WMF.

WMF uses COM extensively but it is not a pure COM API instead MF is a mix of COM and normal objects but because it does use COM you must at the beginning of your program initialize COM by callingCoInitializeEx() you must also initialize WMF by callingMFStartup() this means on exiting your application you must shutdown these objects.

Since the headers and libs I will be using are not available outwith the MS compilers this build will not as usual from my tutorials run on any MinGW compilers I am truly sorry for this but there was no way to make it so.

I will be however aiming for this to run on any Express versions of the MS compiler with the Windows 7 SDK and .net framework 4.0. installed. Since we are using MS compilers only I have decided to make this build UNICODE.

The DirectX 3D version that I will use will be 9. This is for backwards compatibility. Those who wish to reprogram it for DirectX 3D version 11 can do so and it is perfectly possible to translate this code to do so.

Most of the common headers I have included in a file called imaginatively ‘Common.h‘. And here it is.

Common.h


#include <d3d9.h>
#include <Windows.h>
#include <WindowsX.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <mferror.h>
#include <assert.h>
#include <d3dx9.h>
#include <ks.h>
#include <ksmedia.h>
#include <Dbt.h>
#include <tchar.h>
#include <strsafe.h>
#include <AclAPI.h>


template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

#define BREAK_ON_FAIL(value)            if(FAILED(value)) break;
#define BREAK_ON_NULL(value, newHr)     if(value =http://www.mamicode.com/= NULL) { hr = newHr; break; }>

Basically all the headers we need in one file kept this way for simplicity‘s sake..

A SafeRelease Template.

some defines for BREAK_ON_FAIL and two headers Device.h and Preview.h which are classes.

Next up is main.cpp

main.cpp

#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif

// Include the v6 common controls in the manifest
#pragma comment(linker,                          "\"/manifestdependency:type='Win32' "         "name='Microsoft.Windows.Common-Controls' "    "version='6.0.0.0' "                            "processorArchitecture='*' "                     "publicKeyToken='6595b64144ccf1df' "              "language='*'\"")


#include "Common.h"
#include "resource.h"

#pragma comment(lib,"mfplat.lib")
#pragma message("linking with Microsoft's Media Foundation mfplat library ...")
#pragma comment(lib,"mf.lib")
#pragma message("linking with Microsoft's Media Foundation mf library ...")
#pragma comment(lib,"mfreadwrite.lib")
#pragma message("linking with Microsoft's Media Foundation mfreadwrite library ...")
#pragma comment(lib,"mfuuid.lib")
#pragma message("linking with Microsoft's Media Foundation mfuuid library ...")
#pragma comment(lib,"d3d9.lib")
#pragma message("linking with Microsoft's DirectX 3D 9 library ...")
#pragma comment(lib,"shlwapi.lib")
#pragma message("linking with Microsoft's shlwapi library ...")
#pragma comment(lib,"D3dx9.lib")
#pragma message("linking with Microsoft's DirectX 3DX 9 library ...")
#pragma comment(lib,"Advapi32.lib")
#pragma message("linking with Microsoft's Advapi32 library ...")


//
// ChooseDeviceParam structure
//
// Holds an array of IMFActivate pointers that represent video
// capture devices.
//



struct ChooseDeviceParam
{
    IMFActivate **ppDevices;    // Array of IMFActivate pointers.
    UINT32      count;          // Number of elements in the array.
    UINT32      selection;      // Selected device, by array index.
};



BOOL    InitializeApplication();
BOOL    InitializeWindow(HWND *pHwnd);
void    CleanUp();
INT     MessageLoop(HWND hwnd);


INT_PTR CALLBACK DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void    ShowErrorMessage(PCWSTR format, HRESULT hr);

// Window message handlers
BOOL    OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
void    OnClose(HWND hwnd);
void    OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
void    OnSize(HWND hwnd, UINT state, int cx, int cy);
void    OnDeviceChange(HWND hwnd, DEV_BROADCAST_HDR *pHdr);

// Command handlers
void    OnChooseDevice(HWND hwnd, bool bPrompt);

// Global variables
CPreview    *mf_Preview = NULL;
HDEVNOTIFY  g_hdevnotify = NULL;


/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = L"WebCam Capture";
/*  Make the window name into a global variable  */
TCHAR szWindowName[ ] = L"Snoopy's WMF Webcam Capture.";

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd= NULL;               /* This is the handle for our window */
    
    if (InitializeApplication() && InitializeWindow(&hwnd))
    {
        MessageLoop(hwnd);
    }

    CleanUp();

    
    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return 0;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
        HANDLE_MSG(hwnd, WM_CLOSE,  OnClose);
        HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
        HANDLE_MSG(hwnd, WM_SIZE,    OnSize);

    case WM_APP_PREVIEW_ERROR:
        ShowErrorMessage(L"Error", (HRESULT)wParam);
        break;

    case WM_DEVICECHANGE:
        OnDeviceChange(hwnd, (PDEV_BROADCAST_HDR)lParam);
        break;

    case WM_ERASEBKGND:
        return 1;
    
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

BOOL InitializeApplication()
{
    HRESULT hr = S_OK;

    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    if (SUCCEEDED(hr))
    {
        hr = MFStartup(MF_VERSION);
    }

    return (SUCCEEDED(hr));
}

//-------------------------------------------------------------------
// CleanUp
//
// Releases resources.
//-------------------------------------------------------------------

void CleanUp()
{
    if (g_hdevnotify)
    {
        UnregisterDeviceNotification(g_hdevnotify);
    }

    if (mf_Preview)
    {
        mf_Preview->CloseDevice();
    }

    SafeRelease(&mf_Preview);

    MFShutdown();
    CoUninitialize();
}


//-------------------------------------------------------------------
// InitializeWindow
//
// Creates the application window.
//-------------------------------------------------------------------

BOOL InitializeWindow(HWND *pHwnd)
{
    WNDCLASS wc = {0};

    wc.lpfnWndProc   = WindowProcedure;
    wc.hInstance     = GetModuleHandle(NULL);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = szClassName;
    wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);

    if (!RegisterClass(&wc))
    {
        return FALSE;
    }

    HWND hwnd = CreateWindow(
        szClassName,
        szWindowName,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        GetModuleHandle(NULL),
        NULL
        );

    if (!hwnd)
    {
        return FALSE;
    }

    ShowWindow(hwnd, SW_SHOWDEFAULT);
    UpdateWindow(hwnd);

    *pHwnd = hwnd;

    return TRUE;
}


//-------------------------------------------------------------------
// MessageLoop 
//
// Implements the window message loop.
//-------------------------------------------------------------------

INT MessageLoop(HWND hwnd)
{
    MSG msg = {0};

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    DestroyWindow(hwnd);

    return INT(msg.wParam);
}


//-------------------------------------------------------------------
// OnCreate
//
// Handles the WM_CREATE message.
//-------------------------------------------------------------------

BOOL OnCreate(HWND hwnd, LPCREATESTRUCT)
{
    HRESULT hr = S_OK;
	SetSecurityInfo(GetModuleHandle(NULL),SE_FILE_OBJECT,OWNER_SECURITY_INFORMATION,NULL,NULL,NULL,NULL);
    // Register this window to get device notification messages.

    DEV_BROADCAST_DEVICEINTERFACE di = { 0 };
    di.dbcc_size = sizeof(di);
    di.dbcc_devicetype  = DBT_DEVTYP_DEVICEINTERFACE;
    di.dbcc_classguid  = KSCATEGORY_CAPTURE; 

    g_hdevnotify = RegisterDeviceNotification(
        hwnd,
        &di,
        DEVICE_NOTIFY_WINDOW_HANDLE
        );

    if (g_hdevnotify == NULL)
    {
        ShowErrorMessage(L"RegisterDeviceNotification failed.", HRESULT_FROM_WIN32(GetLastError()));
        return FALSE;
    }

    // Create the object that manages video preview. 
    hr = CPreview::CreateInstance(hwnd, hwnd, &mf_Preview);

    if (FAILED(hr))
    {
        ShowErrorMessage(L"CPreview::CreateInstance failed.", hr);
		CleanUp();
        return FALSE;
    }

    // Select the first available device (if any).
    OnChooseDevice(hwnd, true);
	SetWindowPos(hwnd,HWND_TOP,0,0,mf_Preview->m_draw.width,mf_Preview->m_draw.height,NULL);
    return TRUE;
}



//-------------------------------------------------------------------
// OnClose
//
// Handles WM_CLOSE messages.
//-------------------------------------------------------------------

void OnClose(HWND /*hwnd*/)
{
    CleanUp();
	PostQuitMessage(0);
}



//-------------------------------------------------------------------
// OnSize
//
// Handles WM_SIZE messages.
//-------------------------------------------------------------------

void OnSize(HWND hwnd, UINT /*state */, int cx, int cy)
{
    if (mf_Preview)
    {
        mf_Preview->ResizeVideo((WORD)cx, (WORD)cy);

        InvalidateRect(hwnd, NULL, FALSE);
    }
}


//-------------------------------------------------------------------
// OnCommand 
//
// Handles WM_COMMAND messages
//-------------------------------------------------------------------

void OnCommand(HWND hwnd, int id, HWND /*hwndCtl*/, UINT /*codeNotify*/)
{
    switch (id)
    {
            case ID_FILE_CHOOSEDEVICE:
            OnChooseDevice(hwnd, TRUE);
            break;

			case ID_FILE_CAPTURE:
            mf_Preview->m_draw.saveframe= true;
            break;
    }
}

//-------------------------------------------------------------------
//  OnChooseDevice
//
//  Select a video capture device.
//
//  hwnd:    A handle to the application window.
/// bPrompt: If TRUE, prompt to user to select the device. Otherwise,
//           select the first device in the list.
//-------------------------------------------------------------------

void OnChooseDevice(HWND hwnd, bool bPrompt)
{
    HRESULT hr = S_OK;
    ChooseDeviceParam param = { 0 };

    UINT iDevice = 0;   // Index into the array of devices
    BOOL bCancel = FALSE;

    IMFAttributes *pAttributes = NULL;

    // Initialize an attribute store to specify enumeration parameters.

    hr = MFCreateAttributes(&pAttributes, 1);
    
    if (FAILED(hr)) { CleanUp(); }

    // Ask for source type = video capture devices.
    
    hr = pAttributes->SetGUID(
        MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, 
        MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
        );

    if (FAILED(hr)) { CleanUp(); }
    
    // Enumerate devices.
    hr = MFEnumDeviceSources(pAttributes, & param.ppDevices, & param.count);

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

    // NOTE: param.count might be zero.

    if (bPrompt)
    {
        // Ask the user to select a device.

        INT_PTR result = DialogBoxParam(
            GetModuleHandle(NULL),
            MAKEINTRESOURCE(IDD_CHOOSE_DEVICE),
            hwnd,
            DlgProc,
            (LPARAM)& param
            );

        if (result == IDOK)
        {
            iDevice = param.selection;
        }
        else
        {
            bCancel = true; // User cancelled
			PostQuitMessage(0);
        }
    }

    if (!bCancel && (param.count > 0))
    {
        // Give this source to the CPlayer object for preview.
        hr = mf_Preview->SetDevice( param.ppDevices[iDevice] );
		if (FAILED(hr))
        {
        ShowErrorMessage(L"Cannot create a video capture device", hr);
		SafeRelease(&pAttributes);

       for (DWORD i = 0; i < param.count; i++)
       {
        SafeRelease(& param.ppDevices[i]);
       }
       CoTaskMemFree(param.ppDevices);
        }
		

    }
	else
	{
		
		CleanUp();
		hr = -1;
	}
	SetWindowPos(hwnd,HWND_TOP,0,0,mf_Preview->m_draw.m_width-60,mf_Preview->m_draw.m_height,SWP_SHOWWINDOW);



    SafeRelease(&pAttributes);

    for (DWORD i = 0; i < param.count; i++)
    {
        SafeRelease(& param.ppDevices[i]);
    }
    CoTaskMemFree(param.ppDevices);

    if (FAILED(hr))
    {
        ShowErrorMessage(L"Cannot create a video capture device", hr);
    }
}


//-------------------------------------------------------------------
//  OnDeviceChange
//
//  Handles WM_DEVICECHANGE messages.
//-------------------------------------------------------------------

void OnDeviceChange(HWND hwnd, DEV_BROADCAST_HDR *pHdr)
{
    if (mf_Preview == NULL || pHdr == NULL)
    {
        return;
    }

    HRESULT hr = S_OK;
    BOOL bDeviceLost = FALSE;

    // Check if the current device was lost.

    hr = mf_Preview->CheckDeviceLost(pHdr, &bDeviceLost);

    if (FAILED(hr) || bDeviceLost)
    {
        mf_Preview->CloseDevice();

        MessageBox(hwnd, L"Lost the capture device.", szWindowName, MB_OK);
    }
}


/////////////////////////////////////////////////////////////////////

// Dialog functions

void    OnInitDialog(HWND hwnd, ChooseDeviceParam *pParam);
HRESULT OnOK(HWND hwnd, ChooseDeviceParam *pParam);

//-------------------------------------------------------------------
//  DlgProc
//
//  Dialog procedure for the "Select Device" dialog.
//-------------------------------------------------------------------

INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static ChooseDeviceParam *pParam = NULL;

    switch (msg)
    {
    case WM_INITDIALOG:
        pParam = (ChooseDeviceParam*)lParam;
        OnInitDialog(hwnd, pParam);
        return TRUE;

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
        case IDOK:
            OnOK(hwnd, pParam);
            EndDialog(hwnd, LOWORD(wParam));
            return TRUE;

        case IDCANCEL:
            EndDialog(hwnd, LOWORD(wParam));
            return TRUE;
        }
        break;
    }

    return FALSE;
}


//-------------------------------------------------------------------
//  OnInitDialog
//
//  Handles the WM_INITDIALOG message.
//-------------------------------------------------------------------

void OnInitDialog(HWND hwnd, ChooseDeviceParam *pParam)
{
    HRESULT hr = S_OK;

    // Populate the list with the friendly names of the devices.

    HWND hList = GetDlgItem(hwnd, IDC_DEVICE_LIST);

    for (DWORD i = 0; i < pParam->count; i++)
    {
        WCHAR *szFriendlyName = NULL;

        hr = pParam->ppDevices[i]->GetAllocatedString(
            MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, 
            &szFriendlyName, 
            NULL
            );

        if (FAILED(hr)) 
        { 
            break; 
        }


        int index = ListBox_AddString(hList, szFriendlyName);

        ListBox_SetItemData(hList, index, i);

        CoTaskMemFree(szFriendlyName);
    }

    // Assume no selection for now.
    pParam->selection = (UINT32)-1;

    if (pParam->count == 0)
    {
        // If there are no devices, disable the "OK" button.
        EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
    }
}


HRESULT OnOK(HWND hwnd, ChooseDeviceParam *pParam)
{
    HWND hList = GetDlgItem(hwnd, IDC_DEVICE_LIST);

    int sel = ListBox_GetCurSel(hList);

    if (sel != LB_ERR)
    {
        pParam->selection = (UINT32)ListBox_GetItemData(hList, sel);
    }

    return S_OK;
}


void ShowErrorMessage(PCWSTR format, HRESULT hrErr)
{
    HRESULT hr = S_OK;
    WCHAR msg[MAX_PATH];

    hr = StringCbPrintf(msg, sizeof(msg), L"%s ",hrErr);

    if (SUCCEEDED(hr))
    {
        MessageBox(NULL, msg, L"Error", MB_OK);
    }
    else
    {
        DebugBreak();
    }
}


I define UNICODE then activate the v6 common controls, this enables visual styles available inComCtl32.dll version 6 or later.

I introduce pragma comments to load all our libs.

I set up a structure which holds an array of IMFActivate objects this is for choosing a webcam.

I set up some functions, windows message handlers and global variables.

I introduce a WinMain function this is a departure from my usual window structure.

I set up a Windows callback procedure WindowProcedure.

I initialize the application.
I introduce a cleanup function.

I then introduce a window initializing function and several windows message handling functions.

In our OnCommand function which handles selections from the Menu.
This has two options a device chooser and a routine which sets a ‘saveframe‘ variable which activates a routine which grabs a still image from the video stream and saves it to disk to a file called‘Capture.jpg‘.

The rest is made up of mainly Dialog functions associated with choosing the webcam.

Next up is a file called

BufferLock.h
#pragma once


//-------------------------------------------------------------------
//  VideoBufferLock class
//
//  Locks a video buffer that might or might not support IMF2DBuffer.
//
//-------------------------------------------------------------------

class VideoBufferLock
{   
public:
    VideoBufferLock(IMFMediaBuffer *pBuffer) : m_p2DBuffer(NULL), m_bLocked(FALSE)
    {
        m_pBuffer = pBuffer;
        m_pBuffer->AddRef();

        // Query for the 2-D buffer interface. OK if this fails.
        (void)m_pBuffer->QueryInterface(IID_PPV_ARGS(&m_p2DBuffer));
    }

    ~VideoBufferLock()
    {
        UnlockBuffer();
        SafeRelease(&m_pBuffer);
        SafeRelease(&m_p2DBuffer);
    }

    //-------------------------------------------------------------------
    // LockBuffer
    //
    // Locks the buffer. Returns a pointer to scan line 0 and returns the stride.
    //
    // The caller must provide the default stride as an input parameter, in case
    // the buffer does not expose IMF2DBuffer. You can calculate the default stride
    // from the media type.
    //-------------------------------------------------------------------

    HRESULT LockBuffer(
        LONG  lDefaultStride,    // Minimum stride (with no padding).
        DWORD dwHeightInPixels,  // Height of the image, in pixels.
        BYTE  **ppbScanLine0,    // Receives a pointer to the start of scan line 0.
        LONG  *plStride          // Receives the actual stride.
        )
    {
        HRESULT hr = S_OK;

        // Use the 2-D version if available.
        if (m_p2DBuffer)
        {
            hr = m_p2DBuffer->Lock2D(ppbScanLine0, plStride);
        }
        else
        {
            // Use non-2D version.
            BYTE *pData = http://www.mamicode.com/NULL;>
  

This class locks a video buffer so an image can be extracted from the video stream.


First up is a constructor and destructor.

The main function is LockBuffer this locks in a video buffer it tries to use the 2d version if the hardware supports it first then if it doesn‘t defaults to the non 2d version which isIMFMediaBuffer.

The other function is UnlockBuffer() which of course Unlocks the video stream buffer after an image has been extracted from the buffer.

Next up is Preview.h this deals with class prototypes for display purposes.

Preview.h
#pragma once

const UINT WM_APP_PREVIEW_ERROR = WM_APP + 1;    // wparam = HRESULT

class CPreview : public IMFSourceReaderCallback
{
public:
	 DrawDevice              m_draw;             // Manages the Direct3D device.

    static HRESULT CreateInstance(
        HWND hVideo, 
        HWND hEvent, 
        CPreview **ppPlayer
    );

    // IUnknown methods
    STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // IMFSourceReaderCallback methods
    STDMETHODIMP OnReadSample(
        HRESULT hrStatus,
        DWORD dwStreamIndex,
        DWORD dwStreamFlags,
        LONGLONG llTimestamp,
        IMFSample *pSample
    );

    STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *)
    {
        return S_OK;
    }

    STDMETHODIMP OnFlush(DWORD)
    {
        return S_OK;
    }

    HRESULT       SetDevice(IMFActivate *pActivate);
    HRESULT       CloseDevice();
    HRESULT       ResizeVideo(WORD width, WORD height);
    HRESULT       CheckDeviceLost(DEV_BROADCAST_HDR *pHdr, BOOL *pbDeviceLost);

protected:
    
    // Constructor is private. Use static CreateInstance method to create.
    CPreview(HWND hVideo, HWND hEvent);

    // Destructor is private. Caller should call Release.
    virtual ~CPreview();

    HRESULT Initialize();
    void    NotifyError(HRESULT hr) { PostMessage(m_hwndEvent, WM_APP_PREVIEW_ERROR, (WPARAM)hr, 0L); }
    HRESULT TryMediaType(IMFMediaType *pType);

    long                    m_nRefCount;        // Reference count.
    CRITICAL_SECTION        m_critsec;

    HWND                    m_hwndVideo;        // Video window.
    HWND                    m_hwndEvent;        // Application window to receive events. 

    IMFSourceReader         *m_pReader;

   

    WCHAR                   *m_pwszSymbolicLink;
    UINT32                  m_cchSymbolicLink;
};

The class CPreview inherits from IMFSourceReaderCallback

which has these methods :-

OnEvent
OnFlush
and
OnReadSample

see http://msdn.microsof...v=vs.85%29.aspx

for more info.

It also uses the DrawDevice class which I will go over later.

The constructor and destructor for this class are private.

Instead to create a new instance of the preview player use CreateInstance
to destroy the IUnknown Release method should be called via the SafeRelease template.

Preview.cpp
#include "Common.h"
#include <shlwapi.h>

//-------------------------------------------------------------------
//  CreateInstance
//
//  Static class method to create the CPreview object.
//-------------------------------------------------------------------

HRESULT CPreview::CreateInstance(
    HWND hVideo,        // Handle to the video window.
    HWND hEvent,        // Handle to the window to receive notifications.
    CPreview **ppPlayer // Receives a pointer to the CPreview object.
    )
{
    assert(hVideo != NULL);
    assert(hEvent != NULL);

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

    CPreview *pPlayer = new  CPreview(hVideo, hEvent);

    // The CPlayer constructor sets the ref count to 1.

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

    HRESULT hr = pPlayer->Initialize();

    if (SUCCEEDED(hr))
    {
        *ppPlayer = pPlayer;
        (*ppPlayer)->AddRef();
    }

    SafeRelease(&pPlayer);
    return hr;
}


//-------------------------------------------------------------------
//  constructor
//-------------------------------------------------------------------

CPreview::CPreview(HWND hVideo, HWND hEvent) : 
    m_pReader(NULL),
    m_hwndVideo(hVideo),
    m_hwndEvent(hEvent),
    m_nRefCount(1),
    m_pwszSymbolicLink(NULL),
    m_cchSymbolicLink(0)
{
    InitializeCriticalSection(&m_critsec);
}

//-------------------------------------------------------------------
//  destructor
//-------------------------------------------------------------------

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

    m_draw.DestroyDevice();

    DeleteCriticalSection(&m_critsec);
}


//-------------------------------------------------------------------
//  Initialize
//
//  Initializes the object.
//-------------------------------------------------------------------

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

    hr = m_draw.CreateDevice(m_hwndVideo);

    return hr;
}


//-------------------------------------------------------------------
//  CloseDevice
//
//  Releases all resources held by this object.
//-------------------------------------------------------------------

HRESULT CPreview::CloseDevice()
{
    EnterCriticalSection(&m_critsec);

    SafeRelease(&m_pReader);

    CoTaskMemFree(m_pwszSymbolicLink);
    m_pwszSymbolicLink = NULL;
    m_cchSymbolicLink = 0;

    LeaveCriticalSection(&m_critsec);
    return S_OK;
}


/////////////// 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, IMFSourceReaderCallback),
        { 0 },
    };
    return QISearch(this, qit, riid, ppv);
}


/////////////// IMFSourceReaderCallback methods ///////////////

//-------------------------------------------------------------------
// OnReadSample
//
// Called when the IMFMediaSource::ReadSample method completes.
//-------------------------------------------------------------------

HRESULT CPreview::OnReadSample(
    HRESULT hrStatus,
    DWORD /* dwStreamIndex */,
    DWORD /* dwStreamFlags */,
    LONGLONG /* llTimestamp */,
    IMFSample *pSample      // Can be NULL
    )
{
    HRESULT hr = S_OK;
    IMFMediaBuffer *pBuffer = NULL;

    EnterCriticalSection(&m_critsec);

    if (FAILED(hrStatus))
    {
        hr = hrStatus;
    }

    if (SUCCEEDED(hr))
    {
        if (pSample)
        {
            // Get the video frame buffer from the sample.

            hr = pSample->GetBufferByIndex(0, &pBuffer);

            // Draw the frame.

            if (SUCCEEDED(hr))
            {
                hr = m_draw.DrawFrame(pBuffer);
            }
        }
    }

    // Request the next frame.
    if (SUCCEEDED(hr))
    {
        hr = m_pReader->ReadSample(
            (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
            0,
            NULL,   // actual
            NULL,   // flags
            NULL,   // timestamp
            NULL    // sample
            );
    }

    if (FAILED(hr))
    {
        NotifyError(hr);
    }
    SafeRelease(&pBuffer);

    LeaveCriticalSection(&m_critsec);
    return hr;
}


//-------------------------------------------------------------------
// TryMediaType
//
// Test a proposed video format.
//-------------------------------------------------------------------

HRESULT CPreview::TryMediaType(IMFMediaType *pType)
{
    HRESULT hr = S_OK;

    BOOL bFound = FALSE;
    GUID subtype = { 0 };

    hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);

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

    // Do we support this type directly?
    if (m_draw.IsFormatSupported(subtype))
    {
        bFound = TRUE;
    }
    else
    {
        // Can we decode this media type to one of our supported
        // output formats?

        for (DWORD i = 0;  ; i++)
        {
            // Get the i'th format.
            m_draw.GetFormat(i, &subtype);

            hr = pType->SetGUID(MF_MT_SUBTYPE, subtype);
            
            if (FAILED(hr)) { break; }

            // Try to set this type on the source reader.
            hr = m_pReader->SetCurrentMediaType(
                (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
                NULL,
                pType
                );

            if (SUCCEEDED(hr))
            {
                bFound = TRUE;
                break;
            }
        }
    }

    if (bFound)
    {
        hr = m_draw.SetVideoType(pType);
    }

    return hr;
}



//-------------------------------------------------------------------
// SetDevice
//
// Set up preview for a specified video capture device. 
//-------------------------------------------------------------------

HRESULT CPreview::SetDevice(IMFActivate *pActivate)
{
    HRESULT hr = S_OK;

    IMFMediaSource  *pSource = NULL;
    IMFAttributes   *pAttributes = NULL;
    IMFMediaType    *pType = NULL;

    EnterCriticalSection(&m_critsec);

    // Release the current device, if any.

    hr = CloseDevice();

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

    // Get the symbolic link.
    if (SUCCEEDED(hr))
    {
        hr = pActivate->GetAllocatedString(
            MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
            &m_pwszSymbolicLink,
            &m_cchSymbolicLink
            );
    }


    //
    // Create the source reader.
    //

    // Create an attribute store to hold initialization settings.

    if (SUCCEEDED(hr))
    {
        hr = MFCreateAttributes(&pAttributes, 2);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, TRUE);
    }

    // Set the callback pointer.
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUnknown(
            MF_SOURCE_READER_ASYNC_CALLBACK,
            this
            );
    }

    if (SUCCEEDED(hr))
    {
        hr = MFCreateSourceReaderFromMediaSource(
            pSource,
            pAttributes,
            &m_pReader
            );
    }

    // Try to find a suitable output type.
    if (SUCCEEDED(hr))
    {
        for (DWORD i = 0; ; i++)
        {
            hr = m_pReader->GetNativeMediaType(
                (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
                i,
                &pType
                );

            if (FAILED(hr)) { break; }

            hr = TryMediaType(pType);

            SafeRelease(&pType);

            if (SUCCEEDED(hr))
            {
                // Found an output type.
                break;
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        // Ask for the first sample.
        hr = m_pReader->ReadSample(
            (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
            0,
            NULL,
            NULL,
            NULL,
            NULL
            );
    }

    if (FAILED(hr))
    {
        if (pSource)
        {
            pSource->Shutdown();

            // NOTE: The source reader shuts down the media source
            // by default, but we might not have gotten that far.
        }
        CloseDevice();
    }

    SafeRelease(&pSource);
    SafeRelease(&pAttributes);
    SafeRelease(&pType);

    LeaveCriticalSection(&m_critsec);
    return hr;
}



//-------------------------------------------------------------------
//  ResizeVideo
//  Resizes the video rectangle.
//
//  The application should call this method if the size of the video
//  window changes; e.g., when the application receives WM_SIZE.
//-------------------------------------------------------------------

HRESULT CPreview::ResizeVideo(WORD /*width*/, WORD /*height*/)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_critsec);

    hr = m_draw.ResetDevice();

    if (FAILED(hr))
    {
        MessageBox(NULL, L"ResetDevice failed!", NULL, MB_OK);
    }

    LeaveCriticalSection(&m_critsec);

    return hr;
}


//-------------------------------------------------------------------
//  CheckDeviceLost
//  Checks whether the current device has been lost.
//
//  The application should call this method in response to a
//  WM_DEVICECHANGE message. (The application must register for 
//  device notification to receive this message.)
//-------------------------------------------------------------------

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

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

    *pbDeviceLost = FALSE;
    
    if (pHdr == NULL)
    {
        return S_OK;
    }

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

    pDi = (DEV_BROADCAST_DEVICEINTERFACE*)pHdr;


    EnterCriticalSection(&m_critsec);

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

    LeaveCriticalSection(&m_critsec);

    return S_OK;
}


First is CreateInstance a function that creates a new preview player.

The constructor and destructor creates and destroys a critical section respectively what this means is
it waits until it is granted ownership of the critical section in this case the video stream from the webcam.

Initialize simply initializes the drawing surface through the DrawDevice class.

CloseDevice
releases all the resources used by the preview player.

The IUnknown methods you dont have to worry about apart from you should know they cannot be changed.

The IMFSourceReaderCallback methods is where it all happensOnReadSample gets a frame for the video buffer and passes it to the DrawDevice class for drawing and on certain conditions saving to a file.

TryMediaType
works out which media type the camera hardware supports this can be one of many formats YUV uncompressed RGB etc.

This function once it has negotiated a format sets the video type in the DrawDevice class.

SetDevice basically creates a source reader from a media source i.e. in this case a webcam it does this through the functionMFCreateSourceReaderFromMediaSource once it has this it tries to negotiate a suitable output type
in this case probably RGB-32.

ResizeVideo
simply calls ResetDevice from the DrawDevice class.

CheckDeviceLost
checks to see if the webcam has been unplugged if it has it sets pbDeviceLost toTRUE.

At this point it would be good to introduce my image file text.png this could literally be anything to overlay on the video device in fact it‘s a transparent image of course with the word Snoopy on it. But it could be a handle bar moustache a cowboy hat or anything that you can imagine.

Yes later on I will be using this image to overlay on the webcam video... I know exciting and something that you couldn‘t do with VFW.

MF is capable of much more however you can create audio/video streams that can be encoded to .wmv, .mp4 etc. It is capable of streaming audio and/or video files across the internet too.

However I will just be grabbing a single frame and saving it to disk as there is more information on how to create .mp4 files out there than grabbing a single frame... I know but its the way it is.

Moving on..


Device.h

#pragma once

// Function pointer for the function that transforms the image.

typedef void (*IMAGE_TRANSFORM_FN)(
    BYTE*       pDest,
    LONG        lDestStride,
    const BYTE* pSrc,
    LONG        lSrcStride,
    DWORD       dwWidthInPixels,
    DWORD       dwHeightInPixels
    );


// DrawDevice class

class DrawDevice
{
private:

    HWND                    m_hwnd;
	HDC                     *phdc;
    IDirect3D9              *m_pD3D;
    IDirect3DDevice9        *m_pDevice;
    IDirect3DSwapChain9     *m_pSwapChain;
	
    D3DPRESENT_PARAMETERS   m_d3dpp;
	ID3DXSprite             *textSprite;
    // Format information
    D3DFORMAT               m_format;
    LONG                    m_lDefaultStride;
    MFRatio                 m_PixelAR;
    MFVideoInterlaceMode    m_interlace;
    RECT                    m_rcDest;       // Destination rectangle

    // Drawing
    IMAGE_TRANSFORM_FN      m_convertFn;    // Function to convert the video to RGB32

private:
    
    HRESULT TestCooperativeLevel();
    HRESULT SetConversionFunction(REFGUID subtype);
    HRESULT CreateSwapChains();
    void    UpdateDestinationRect();
    
public:
	bool saveframe;
	UINT                    m_width;
        UINT                    m_height;
	UINT                    width;
	UINT                    height;
    DrawDevice();
    virtual ~DrawDevice();

    HRESULT CreateDevice(HWND hwnd);
    HRESULT ResetDevice();
    void    DestroyDevice();

    HRESULT SetVideoType(IMFMediaType *pType);
    HRESULT DrawFrame(IMFMediaBuffer *pBuffer);

    // What video formats we accept
    BOOL     IsFormatSupported(REFGUID subtype) const;
    HRESULT  GetFormat(DWORD index, GUID *pSubtype)  const;
};


 


This contains the DrawDevice class which is responsible for all drawing operations.

It contains the private member functions
HRESULT TestCooperativeLevel();
HRESULT SetConversionFunction(REFGUID subtype);
HRESULT CreateSwapChains();
void    UpdateDestinationRect();
 

and the public member functions
HRESULT CreateDevice(HWND hwnd);
HRESULT ResetDevice();
void    DestroyDevice();

HRESULT SetVideoType(IMFMediaType *pType);
HRESULT DrawFrame(IMFMediaBuffer *pBuffer);

BOOL     IsFormatSupported(REFGUID subtype) const;
HRESULT  GetFormat(DWORD index, GUID *pSubtype)  const;
 
Out of these DrawFrame is the most interesting as this is where all the drawing takes place inside theDrawDevice class.

Well with that in mind we better move on to the implementations of those functions in

Device.cpp
.
#include "Common.h"
#include "BufferLock.h"

const DWORD NUM_BACK_BUFFERS = 2;

void TransformImage_RGB24(
	BYTE*       pDest,
	LONG        lDestStride,
	const BYTE* pSrc,
	LONG        lSrcStride,
	DWORD       dwWidthInPixels,
	DWORD       dwHeightInPixels
	);

void TransformImage_RGB32(
	BYTE*       pDest,
	LONG        lDestStride,
	const BYTE* pSrc,
	LONG        lSrcStride,
	DWORD       dwWidthInPixels,
	DWORD       dwHeightInPixels
	);

void TransformImage_YUY2(
	BYTE*       pDest,
	LONG        lDestStride,
	const BYTE* pSrc,
	LONG        lSrcStride,
	DWORD       dwWidthInPixels,
	DWORD       dwHeightInPixels
	);

void TransformImage_NV12(
	BYTE* pDst, 
	LONG dstStride, 
	const BYTE* pSrc, 
	LONG srcStride,
	DWORD dwWidthInPixels,
	DWORD dwHeightInPixels
	);


RECT    LetterBoxRect(const RECT& rcSrc, const RECT& rcDst);
RECT    CorrectAspectRatio(const RECT& src, const MFRatio& srcPAR);
HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride);


inline LONG Width(const RECT& r)
{
	return r.right - r.left;
}

inline LONG Height(const RECT& r)
{
	return r.bottom - r.top;
}


// Static table of output formats and conversion functions.
struct ConversionFunction
{
	GUID               subtype;
	IMAGE_TRANSFORM_FN xform;
};


ConversionFunction   g_FormatConversions[] =
{
	{ MFVideoFormat_RGB32, TransformImage_RGB32 },
	{ MFVideoFormat_RGB24, TransformImage_RGB24 },
	{ MFVideoFormat_YUY2,  TransformImage_YUY2  },      
	{ MFVideoFormat_NV12,  TransformImage_NV12  }
};

const DWORD   g_cFormats = ARRAYSIZE(g_FormatConversions);


//-------------------------------------------------------------------
// Constructor
//-------------------------------------------------------------------

DrawDevice::DrawDevice() : 
m_hwnd(NULL),
	m_pD3D(NULL),
	m_pDevice(NULL),
	m_pSwapChain(NULL),
	m_format(D3DFMT_UNKNOWN),
	m_width(0),
	m_height(0),
	m_lDefaultStride(0),
	m_interlace(MFVideoInterlace_Unknown),
	m_convertFn(NULL)
{
	m_PixelAR.Denominator = m_PixelAR.Numerator = 1; 

	ZeroMemory(&m_d3dpp, sizeof(m_d3dpp));
}


//-------------------------------------------------------------------
// Destructor
//-------------------------------------------------------------------

DrawDevice::~DrawDevice()
{
	DestroyDevice();
}


//-------------------------------------------------------------------
// GetFormat
//
// Get a supported output format by index.
//-------------------------------------------------------------------

HRESULT DrawDevice::GetFormat(DWORD index, GUID *pSubtype) const
{
	if (index < g_cFormats)
	{
		*pSubtype = g_FormatConversions[index].subtype;
		return S_OK;
	}
	return MF_E_NO_MORE_TYPES;
}


//-------------------------------------------------------------------
//  IsFormatSupported
//
//  Query if a format is supported.
//-------------------------------------------------------------------

BOOL DrawDevice::IsFormatSupported(REFGUID subtype) const
{
	for (DWORD i = 0; i < g_cFormats; i++)
	{
		if (subtype == g_FormatConversions[i].subtype)
		{
			return TRUE;
		}
	}
	return FALSE;
}




//-------------------------------------------------------------------
// CreateDevice
//
// Create the Direct3D device.
//-------------------------------------------------------------------

HRESULT DrawDevice::CreateDevice(HWND hwnd)
{
	if (m_pDevice)
	{
		return S_OK;
	}

	// Create the Direct3D object.
	if (m_pD3D == NULL)
	{
		m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);

		if (m_pD3D == NULL)
		{
			return E_FAIL;
		}
	}


	HRESULT hr = S_OK;
	D3DPRESENT_PARAMETERS pp = { 0 };
	D3DDISPLAYMODE mode = { 0 };

	hr = m_pD3D->GetAdapterDisplayMode(
		D3DADAPTER_DEFAULT, 
		&mode
		);

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

	hr = m_pD3D->CheckDeviceType(
		D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL,
		mode.Format,
		D3DFMT_X8R8G8B8,
		TRUE    // windowed
		);

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

	pp.BackBufferFormat = D3DFMT_X8R8G8B8;
	pp.SwapEffect = D3DSWAPEFFECT_COPY;
	pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;  
	pp.Windowed = TRUE;
	pp.hDeviceWindow = hwnd;

	hr = m_pD3D->CreateDevice(
		D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL,
		hwnd,
		D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE,
		&pp,
		&m_pDevice
		);

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

	m_hwnd = hwnd;
	m_d3dpp = pp;


	return hr;
}

//-------------------------------------------------------------------
// SetConversionFunction
//
// Set the conversion function for the specified video format.
//-------------------------------------------------------------------

HRESULT DrawDevice::SetConversionFunction(REFGUID subtype)
{
	m_convertFn = NULL;

	for (DWORD i = 0; i < g_cFormats; i++)
	{
		if (g_FormatConversions[i].subtype == subtype)
		{
			m_convertFn = g_FormatConversions[i].xform;
			return S_OK;
		}
	}

	return MF_E_INVALIDMEDIATYPE;
}


//-------------------------------------------------------------------
// SetVideoType
//
// Set the video format.  
//-------------------------------------------------------------------

HRESULT DrawDevice::SetVideoType(IMFMediaType *pType)
{
	HRESULT hr = S_OK;
	GUID subtype = { 0 };
	MFRatio PAR = { 0 };

	// Find the video subtype.
	hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);

	if (FAILED(hr))
	{ 
		m_format = D3DFMT_UNKNOWN;
		m_convertFn = NULL;
		return hr;
	}

	// Choose a conversion function.
	// (This also validates the format type.)

	hr = SetConversionFunction(subtype); 

	if (FAILED(hr))
	{ 
		m_format = D3DFMT_UNKNOWN;
		m_convertFn = NULL;
		return hr;
	}

	//
	// Get some video attributes.
	//

	// Get the frame size.
	hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &m_width, &m_height);

	if (FAILED(hr))
	{ 
		m_format = D3DFMT_UNKNOWN;
		m_convertFn = NULL;
		return hr;
	}


	// Get the interlace mode. Default: assume progressive.
	m_interlace = (MFVideoInterlaceMode)MFGetAttributeUINT32(
		pType,
		MF_MT_INTERLACE_MODE, 
		MFVideoInterlace_Progressive
		);

	// Get the image stride.
	hr = GetDefaultStride(pType, &m_lDefaultStride);

	if (FAILED(hr))
	{ 
		m_format = D3DFMT_UNKNOWN;
		m_convertFn = NULL;
		return hr;
	}

	// Get the pixel aspect ratio. Default: Assume square pixels (1:1)
	hr = MFGetAttributeRatio(
		pType, 
		MF_MT_PIXEL_ASPECT_RATIO, 
		(UINT32*)&PAR.Numerator, 
		(UINT32*)&PAR.Denominator
		);

	if (SUCCEEDED(hr))
	{
		m_PixelAR = PAR;
	}
	else
	{
		m_PixelAR.Numerator = m_PixelAR.Denominator = 1;
	}

	m_format = (D3DFORMAT)subtype.Data1;

	// Create Direct3D swap chains.

	hr = CreateSwapChains();

	if (FAILED(hr))
	{ 
		m_format = D3DFMT_UNKNOWN;
		m_convertFn = NULL;
		return hr;
	}


	// Update the destination rectangle for the correct
	// aspect ratio.

	UpdateDestinationRect();



	return hr;
}

//-------------------------------------------------------------------
//  UpdateDestinationRect
//
//  Update the destination rectangle for the current window size.
//  The destination rectangle is letterboxed to preserve the 
//  aspect ratio of the video image.
//-------------------------------------------------------------------

void DrawDevice::UpdateDestinationRect()
{
	RECT rcClient;
	RECT rcSrc = http://www.mamicode.com/{ 0, 0, m_width, m_height };>


First we set the number of back buffers a blue screen and a surface for the webcam video information which is2.

Then we have a set of image transforms for TransformImage_RGB to TransformImage_NV12.

This translates the color information from one format to another.

The first really interesting bit after that is GetFormat this sets the pSubType GUID.

IsFormatSupported
returns true or false depending on whether the format is supported.

CreateDevice creates the DirectX 3D 9 device which we will use to draw stuff.

SetConversionFunction
works out which Conversion function to use to translate the video.

SetVideoType
uses the conversion function to work out and set the video format.

UpdateDestinationRect
The destination rectangle is letterboxed to preserve the aspect ratio of the video image.
This ensures a good image even when the image is resized.

CreateSwapChains this creates the D3D swapchains a little explanation is required however on exactly what a swapchain is and why its so important. A swap chain is a collection of buffers that are used for displaying frames to the user. Each time an application presents a new frame for display, the first buffer in the swap chain takes the place of the displayed buffer. This process is called swapping or flipping.

A graphics adapter holds a pointer to a surface that represents the image being displayed on the monitor, called a front buffer. As the monitor is refreshed, the graphics card sends the contents of the front buffer to the monitor to be displayed. However, this leads to a problem when rendering real-time graphics. The heart of the problem is that monitor refresh rates are very slow in comparison to the rest of the computer. Common refresh rates range from 60 Hz (60 times per second) to 100 Hz. If your application is updating the front buffer while the monitor is in the middle of a refresh, the image that is displayed will be cut in half with the upper half of the display containing the old image and the lower half containing the new image. This problem is referred to as tearing.

Direct3D implements two options to avoid tearing:

1. An option to only allow updates of the monitor on the vertical retrace (or vertical sync) operation. A monitor typically refreshes its image by moving a light pin horizontally, zigzagging from the top left of the monitor and ending at the bottom right. When the light pin reaches the bottom, the monitor recalibrates the light pin by moving it back to the upper left so that the process can start again. This recalibration is called a vertical sync. During a vertical sync, the monitor is not drawing anything, so any update to the front buffer will not be seen until the monitor starts to draw again. The vertical sync is relatively slow; however, not slow enough to render a complex scene while waiting. What is needed to avoid tearing and be able to render complex scenes is a process called back buffering.

2. An option to use a technique called back buffering. Back buffering is the process of drawing a scene to an off-screen surface, called a back buffer. Note that any surface other than the front buffer is called an off-screen surface because it is never directly viewed by the monitor. By using a back buffer, an application has the freedom to render a scene whenever the system is idle (that is, no windows messages are waiting) without having to consider the monitor‘s refresh rate. Back buffering brings in an additional complication of how and when to move the back buffer to the front buffer.

It is option 2 back buffering that we will be using to display our webcam image.

DrawFrame is next and this is where most of the fun things are done.

We set up some IDirect3DSurface9‘s and lock the video buffer.
We get the swapchain surface and lock it.
We convert the frame. This also copies it to the Direct3D surface.
This is a frame from the webcam video stream.
We color the back buffer ‘Blue‘.

We set up another D3D9 surface this will hold our text.png.
We set up a D3D9 texture this is for drawing our textSprite.
We use or D3D9 device to begin a new scene.
We create a sprite, create a blank texture then load from a resource our text.png into our blank texture.
We call begin on textSprite, set the x,y,z position of our sprite.
We draw the textSprite onto our texture.
We then call end on our textSprite.
We set the render target to pSurf and set up a POINT object called p.
We call endScene on our device.

We then update the surface.

If saveframe is true we save the file using D3DXSaveSurfaceToFile.
We then Present the Frame.

The rest of the functions are pretty much self explanatory up until the Image Transforms
these functions should not be altered they cannot be guessed at and have to be learnt it is the type of function you will use over an over in your WMF apps.


You will need the Direct X SDK for June 2010

this can be found here

http://www.microsoft...ls.aspx?id=6812

If you are using VS2012 or 2013 you will need to do somethings outlined and explained in this article.

http://blogs.msdn.co...1023-error.aspx

as you can see you need to go into control panel-> Uninstall Programs

find the Visual C++ 2010 Redistributable Package version 10.0.30319 and delete both the (x86) and (x64) versions

Install the DirectX sdk for June 2010.

Then download the Microsoft Visual C++ 2010 SP1 Redistributable Package (x86)

found here

http://www.microsoft...ls.aspx?id=8328

and install it.

Well I think that is about it.

You should have the following files before you press compile.

BufferLock.h
Common.h
Device.h
Preview.h
resource.h
resource.rc
Device.cpp
Preview.cpp


main.cpp


The program should link and compile without warnings or errors.

The libs are linked in by pragma comments so I will not go over the libs req‘d to run this program as it is unnecessary.

What the program does allows you to connect to a webcam through a dialog displays that webcam image in a window overlays a graphic over it and allows you to save the composite image to a file called Capture.jpg.

The code base comes from Anton Pollinger‘s book

Developing Microsoft
Media Foundation
Applications


however that code and book does not show you how to save an image to disk or overlay a graphic it does however show you how to display a webcam image in a window.
Also the code on msdn and in Pollinger‘s book is liberally sprinkled with goto‘s which I have removed and redesigned the
class structures somewhat to accomodate for this.

The saving of the image and graphic overlay comes from my own understanding and I believe the Tutorial to be unique in the world of C++ Tutorials.

References

Developing Microsoft
Media Foundation
Applications


MSDN


Link to text.png file for downloading

https://app.box.com/...sd4e4910oia30g6


I hope you have found this Tutorial useful.

Regards

Snoopy.


from another code;

CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
MFStartup(MF_VERSION);

IMFMediaSource *src;
IMFAttributes *attr;
IMFActivate **devices;
UINT32 count = 0;

IMFAttributes *attributes = nullptr;
attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
MFEnumDeviceSources(attributes, &devices, &count);

IMFSourceReader *reader;
IMFMediaSource *source = nullptr;
devices[0]->ActivateObject( __uuidof(IMFMediaSource), (void**)&source);
MFCreateSourceReaderFromMediaSource(source, NULL, &reader);

IMFSample *sample;
reader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, NULL, NULL, &sample);
if(sample)
{
  IMFMediaBuffer* buffer;
  BYTE* data;
  DWORD max, current;
  sample->GetBufferByIndex(0, &buffer);
  buffer->Lock(&data, &max, &cur);
 
   buffer->UnLock();
  SafeRelease(&buffer);
}
SafeRelease(&sample);

SafeRelease(&source);
MFShutdown();
CoUninitialize();