Created
August 28, 2019 20:25
-
-
Save mmozeiko/a5adab1ad11ea6d0643ceb67bb8e3e19 to your computer and use it in GitHub Desktop.
Capture webcam device with Media Foundation API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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, ×tamp, &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(); | |
} |
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
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
Check the functions where attributes are used. In this code example it is used for two functions:
- MFEnumDeviceSources - https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-mfenumdevicesources
- MFCreateDeviceSource - https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-mfcreatedevicesource
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
How to compile this?