Skip to content

Instantly share code, notes, and snippets.

@mmozeiko
Created August 28, 2019 20:25
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save mmozeiko/a5adab1ad11ea6d0643ceb67bb8e3e19 to your computer and use it in GitHub Desktop.
Save mmozeiko/a5adab1ad11ea6d0643ceb67bb8e3e19 to your computer and use it in GitHub Desktop.
Capture webcam device with Media Foundation API
#define COBJMACROS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <stdio.h>
#include <intrin.h>
#pragma comment (lib, "ole32.lib")
#pragma comment (lib, "mf.lib")
#pragma comment (lib, "mfplat.lib")
#pragma comment (lib, "mfuuid.lib")
#pragma comment (lib, "mfreadwrite.lib")
#define ASSERT(x) do { if (!(x)) __debugbreak(); } while (0)
#define CHECK(hr) ASSERT(SUCCEEDED(hr))
int main(int argc, char* argv[])
{
HRESULT hr;
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
CHECK(hr);
hr = MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET);
CHECK(hr);
if (argc == 1) // enumerate available devices
{
UINT32 count;
IMFActivate** devices;
{
IMFAttributes* attr;
hr = MFCreateAttributes(&attr, 1);
CHECK(hr);
hr = IMFAttributes_SetGUID(attr, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
CHECK(hr);
hr = MFEnumDeviceSources(attr, &devices, &count);
CHECK(hr);
IMFAttributes_Release(attr);
}
printf("Detected %u devices:\n", count);
for (UINT32 i = 0; i < count; i++)
{
UINT32 length;
LPWSTR name;
LPWSTR symlink;
hr = IMFActivate_GetAllocatedString(devices[i], &MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &length);
CHECK(hr);
hr = IMFActivate_GetAllocatedString(devices[i], &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &symlink, &length);
CHECK(hr);
printf("%S = %S\n", name, symlink);
CoTaskMemFree(name);
CoTaskMemFree(symlink);
IMFActivate_Release(devices[i]);
}
CoTaskMemFree(devices);
}
else // create device from name
{
IMFMediaSource* device;
{
IMFAttributes* attr;
hr = MFCreateAttributes(&attr, 2);
CHECK(hr);
hr = IMFAttributes_SetGUID(attr, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
CHECK(hr);
WCHAR name[1024];
MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, name, 1024);
hr = IMFAttributes_SetString(attr, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, name);
CHECK(hr);
hr = MFCreateDeviceSource(attr, &device);
CHECK(hr);
IMFAttributes_Release(attr);
}
IMFSourceReader* reader;
hr = MFCreateSourceReaderFromMediaSource(device, NULL, &reader);
CHECK(hr);
IMFMediaSource_Release(device);
// this assumes camera can provide mjpeg output
// typically webcams provide YUV2 format, you'll need to convert it to
// RGB yourself or with help of IMFTransform
// you can enumerate all supported types with IMFSourceReader_GetNativeMediaType
{
IMFMediaType* type;
hr = MFCreateMediaType(&type);
CHECK(hr);
hr = IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
CHECK(hr);
hr = IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, &MFVideoFormat_MJPG);
CHECK(hr);
// you can also set desired width/height here
hr = IMFSourceReader_SetCurrentMediaType(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, type);
CHECK(hr);
IMFMediaType_Release(type);
}
UINT32 width;
UINT32 height;
// get width/height
{
IMFMediaType* type;
hr = IMFSourceReader_GetCurrentMediaType(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, &type);
CHECK(hr);
UINT64 tmp;
hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &tmp);
CHECK(hr);
width = (UINT32)(tmp >> 32);
height = (UINT32)(tmp);
IMFMediaType_Release(type);
}
printf("Size = %ux%u\n", width, height);
// read one frame and save it to file
{
IMFSample* sample;
DWORD stream;
DWORD flags;
LONGLONG timestamp;
for (;;)
{
// this is reading in syncronous blocking mode, MF supports also async calls
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &stream, &flags, &timestamp, &sample);
CHECK(hr);
if (flags & MF_SOURCE_READERF_STREAMTICK)
{
continue;
}
break;
}
{
IMFMediaBuffer* buffer;
hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer);
CHECK(hr);
BYTE* data;
DWORD size;
hr = IMFMediaBuffer_Lock(buffer, &data, NULL, &size);
CHECK(hr);
{
HANDLE h = CreateFileA("image.jpg", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
ASSERT(h != INVALID_HANDLE_VALUE);
DWORD written;
BOOL ok = WriteFile(h, data, size, &written, NULL);
ASSERT(ok && written == size);
CloseHandle(h);
}
IMFMediaBuffer_Unlock(buffer);
IMFMediaBuffer_Release(buffer);
}
IMFSample_Release(sample);
}
IMFSourceReader_Release(reader);
}
MFShutdown();
CoUninitialize();
}
@ryan-weil
Copy link

IMFSample_ConvertToContiguousBuffer
IMFSourceReader_Release
IMFAttributes_SetString
...

Where are these defined?

@mmozeiko
Copy link
Author

Those are macros available in C from mf* headers when you define COBJMACROS before including windows headers.

@subhash-coder
Copy link

How to compile this?

@mmozeiko
Copy link
Author

mmozeiko commented Aug 3, 2022

You save the code to webcam_capture.c file and then use MSVC or Clang compiler to compile it - cl.exe webcam_capture.c or clang-cl.exe webcam_capture.c

@JerryMVC
Copy link

JerryMVC commented Oct 3, 2022

Hi, from where I can obtain more detailed manual for MF ?
For example what is "MFCreateAttributes" ?. How many attributes I have to create ?
Or ... on lines 160-172 why there is the cycle for ?
Can someone help where is more detailed manual ?
I found this:
https://learn.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mfcreateattributes
but this MS manual is really very brief.
Jerry

@mmozeiko
Copy link
Author

mmozeiko commented Oct 3, 2022

Check the functions where attributes are used. In this code example it is used for two functions:

Documentation of these functions explains exactly what attributes to set and why.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment