我在使用 Windows API 和 Media Foundation 库时遇到问题。我下面的代码从网络摄像头捕获并保存图像,但生成的图像是黑白的,并且有两个并排的图像。谁能帮我解决这个问题吗?
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <mfobjects.h>
#include <mferror.h>
#include <stdio.h>
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mf.lib")
const GUID MFVideoFormat_RGB32 = { 0x00000016, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} };
const GUID MF_MT_MAJOR_TYPE = { 0x48eba18e, 0xf8c9, 0x4687, {0xbf, 0x11, 0x0a, 0x74, 0xc9, 0xf9, 0x6a, 0x8f} };
const GUID MF_MT_SUBTYPE = { 0xf7e34c9a, 0x42e8, 0x4714, {0xb7, 0x83, 0x5e, 0xbd, 0xa4, 0x3a, 0xe5, 0x04} };
const GUID MFMediaType_Video = { 0x73646976, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} };
HRESULT InitializeMediaFoundation() {
HRESULT hr = MFStartup(MF_VERSION);
if (FAILED(hr)) {
printf("MFStartup failed: 0x%lx\n", hr);
}
return hr;
}
HRESULT CreateVideoDeviceSource(IMFMediaSource** ppSource) {
IMFAttributes* pAttributes = NULL;
IMFActivate** ppDevices = NULL;
UINT32 count = 0;
HRESULT hr = MFCreateAttributes(&pAttributes, 1);
if (FAILED(hr)) {
printf("MFCreateAttributes failed: 0x%lx\n", hr);
return hr;
}
hr = pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if (FAILED(hr)) {
printf("SetGUID MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE failed: 0x%lx\n", hr);
pAttributes->Release();
return hr;
}
hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count);
if (FAILED(hr)) {
printf("MFEnumDeviceSources failed: 0x%lx\n", hr);
pAttributes->Release();
return hr;
}
if (count > 0) {
hr = ppDevices[0]->ActivateObject(IID_PPV_ARGS(ppSource));
if (FAILED(hr)) {
printf("ActivateObject failed: 0x%lx\n", hr);
}
}
else {
printf("No video capture devices found.\n");
hr = E_FAIL;
}
for (UINT32 i = 0; i < count; i++) {
ppDevices[i]->Release();
}
CoTaskMemFree(ppDevices);
pAttributes->Release();
return hr;
}
HRESULT CaptureImage() {
IMFMediaSource* pSource = NULL;
IMFSourceReader* pReader = NULL;
IMFMediaType* pType = NULL;
DWORD streamIndex = MF_SOURCE_READER_FIRST_VIDEO_STREAM;
HRESULT hr = CreateVideoDeviceSource(&pSource);
if (FAILED(hr)) {
return hr;
}
hr = MFCreateSourceReaderFromMediaSource(pSource, NULL, &pReader);
if (FAILED(hr)) {
printf("MFCreateSourceReaderFromMediaSource failed: 0x%lx\n", hr);
pSource->Release();
return hr;
}
hr = pReader->GetCurrentMediaType(streamIndex, &pType);
if (FAILED(hr)) {
printf("GetCurrentMediaType failed: 0x%lx\n", hr);
pReader->Release();
pSource->Release();
return hr;
}
hr = pType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
if (FAILED(hr)) {
printf("SetGUID MF_MT_SUBTYPE failed: 0x%lx\n", hr);
pType->Release();
pReader->Release();
pSource->Release();
return hr;
}
hr = pReader->SetCurrentMediaType(streamIndex, NULL, pType);
if (FAILED(hr)) {
printf("SetCurrentMediaType failed: 0x%lx\n", hr);
pType->Release();
pReader->Release();
pSource->Release();
return hr;
}
pType->Release();
IMFSample* pSample = NULL;
DWORD dwFlags = 0;
for (int i = 0; i < 10; ++i) {
hr = pReader->ReadSample(streamIndex, 0, NULL, &dwFlags, NULL, &pSample);
if (SUCCEEDED(hr) && pSample) {
break;
}
Sleep(100);
}
if (FAILED(hr) || !pSample) {
printf("ReadSample failed: 0x%lx\n", hr);
printf("Flags: 0x%lx\n", dwFlags); flags
pReader->Release();
pSource->Release();
return hr;
}
printf("Sample read successfully.\n");
IMFMediaBuffer* pBuffer = NULL;
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (FAILED(hr)) {
printf("ConvertToContiguousBuffer failed: 0x%lx\n", hr);
pSample->Release();
pReader->Release();
pSource->Release();
return hr;
}
BYTE* pData = NULL;
DWORD cbBuffer = 0;
hr = pBuffer->Lock(&pData, NULL, &cbBuffer);
if (FAILED(hr)) {
printf("Lock failed: 0x%lx\n", hr);
pBuffer->Release();
pSample->Release();
pReader->Release();
pSource->Release();
return hr;
}
const char* filePath = "C:\\capture.bmp";
FILE* pFile;
fopen_s(&pFile, filePath, "wb");
if (pFile) {
BITMAPFILEHEADER bfHeader;
BITMAPINFOHEADER biHeader;
bfHeader.bfType = 'MB';
bfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + cbBuffer;
bfHeader.bfReserved1 = 0;
bfHeader.bfReserved2 = 0;
bfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
biHeader.biSize = sizeof(BITMAPINFOHEADER);
biHeader.biWidth = 640;
biHeader.biHeight = -480;
biHeader.biPlanes = 1;
biHeader.biBitCount = 32;
biHeader.biCompression = BI_RGB;
biHeader.biSizeImage = cbBuffer;
biHeader.biXPelsPerMeter = 0;
biHeader.biYPelsPerMeter = 0;
biHeader.biClrUsed = 0;
biHeader.biClrImportant = 0;
fwrite(&bfHeader, sizeof(BITMAPFILEHEADER), 1, pFile);
fwrite(&biHeader, sizeof(BITMAPINFOHEADER), 1, pFile);
fwrite(pData, cbBuffer, 1, pFile);
fclose(pFile);
printf("Image saved to %s\n", filePath);
}
else {
printf("Failed to open file for writing\n");
}
hr = pBuffer->Unlock();
pBuffer->Release();
if (pSample) {
pSample->Release();
}
if (pReader) {
pReader->Release();
}
if (pSource) {
pSource->Release();
}
return hr;
}
int main() {
HRESULT hr = InitializeMediaFoundation();
if (SUCCEEDED(hr)) {
hr = CaptureImage();
if (SUCCEEDED(hr)) {
printf("Image captured successfully.\n");
}
else {
printf("Failed to capture image: 0x%lx\n", hr);
}
}
MFShutdown();
return 0;
}
这是一张示例图片
我使用的是 Visual Studio 2022。这是一个从网络摄像头捕获图像并将其保存到 C 盘的程序。
MFVideoFormat_RGB32无法工作。我只能在 Windows 上使用 MFVideoFormat_YUY2。您可以尝试我的示例代码。
我还修改了你的代码:
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <mfobjects.h>
#include <mferror.h>
#include <stdio.h>
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")
// const GUID MFVideoFormat_RGB32 = {0x00000016, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
// const GUID MF_MT_MAJOR_TYPE = {0x48eba18e, 0xf8c9, 0x4687, {0xbf, 0x11, 0x0a, 0x74, 0xc9, 0xf9, 0x6a, 0x8f}};
// const GUID MF_MT_SUBTYPE = {0xf7e34c9a, 0x42e8, 0x4714, {0xb7, 0x83, 0x5e, 0xbd, 0xa4, 0x3a, 0xe5, 0x04}};
// const GUID MFMediaType_Video = {0x73646976, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
unsigned char clamp(double value, double min, double max)
{
if (value < min)
return static_cast<unsigned char>(min);
if (value > max)
return static_cast<unsigned char>(max);
return static_cast<unsigned char>(value);
}
void ConvertYUY2ToRGB(const unsigned char *yuy2Data, unsigned char *rgbData, int width, int height)
{
int rgbIndex = 0;
for (int i = 0; i < width * height * 2; i += 4)
{
unsigned char y1 = yuy2Data[i];
unsigned char u = yuy2Data[i + 1];
unsigned char y2 = yuy2Data[i + 2];
unsigned char v = yuy2Data[i + 3];
rgbData[rgbIndex++] = clamp(y1 + 1.772 * (u - 128), 0.0, 255.0);
rgbData[rgbIndex++] = clamp(y1 - 0.344136 * (u - 128) - 0.714136 * (v - 128), 0.0, 255.0);
rgbData[rgbIndex++] = clamp(y1 + 1.402 * (v - 128), 0.0, 255.0);
rgbData[rgbIndex++] = clamp(y2 + 1.772 * (u - 128), 0.0, 255.0);
rgbData[rgbIndex++] = clamp(y2 - 0.344136 * (u - 128) - 0.714136 * (v - 128), 0.0, 255.0);
rgbData[rgbIndex++] = clamp(y2 + 1.402 * (v - 128), 0.0, 255.0);
}
}
HRESULT InitializeMediaFoundation()
{
HRESULT hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
printf("MFStartup failed: 0x%lx\n", hr);
}
return hr;
}
HRESULT CreateVideoDeviceSource(IMFMediaSource **ppSource)
{
IMFAttributes *pAttributes = NULL;
IMFActivate **ppDevices = NULL;
UINT32 count = 0;
HRESULT hr = MFCreateAttributes(&pAttributes, 1);
if (FAILED(hr))
{
printf("MFCreateAttributes failed: 0x%lx\n", hr);
return hr;
}
hr = pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if (FAILED(hr))
{
printf("SetGUID MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE failed: 0x%lx\n", hr);
pAttributes->Release();
return hr;
}
hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count);
if (FAILED(hr))
{
printf("MFEnumDeviceSources failed: 0x%lx\n", hr);
pAttributes->Release();
return hr;
}
if (count > 0)
{
hr = ppDevices[0]->ActivateObject(IID_PPV_ARGS(ppSource));
if (FAILED(hr))
{
printf("ActivateObject failed: 0x%lx\n", hr);
}
}
else
{
printf("No video capture devices found.\n");
hr = E_FAIL;
}
for (UINT32 i = 0; i < count; i++)
{
ppDevices[i]->Release();
}
CoTaskMemFree(ppDevices);
pAttributes->Release();
return hr;
}
HRESULT CaptureImage()
{
int width = 640, height = 480;
IMFMediaSource *pSource = NULL;
IMFSourceReader *pReader = NULL;
IMFMediaType *pType = NULL;
DWORD streamIndex = MF_SOURCE_READER_FIRST_VIDEO_STREAM;
HRESULT hr = CreateVideoDeviceSource(&pSource);
if (FAILED(hr))
{
return hr;
}
hr = MFCreateSourceReaderFromMediaSource(pSource, NULL, &pReader);
if (FAILED(hr))
{
printf("MFCreateSourceReaderFromMediaSource failed: 0x%lx\n", hr);
pSource->Release();
return hr;
}
hr = pReader->GetCurrentMediaType(streamIndex, &pType);
if (FAILED(hr))
{
printf("GetCurrentMediaType failed: 0x%lx\n", hr);
pReader->Release();
pSource->Release();
return hr;
}
hr = pType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2);
if (FAILED(hr))
{
printf("SetGUID MF_MT_SUBTYPE failed: 0x%lx\n", hr);
pType->Release();
pReader->Release();
pSource->Release();
return hr;
}
hr = MFSetAttributeSize(pType, MF_MT_FRAME_SIZE, width, height);
if (FAILED(hr))
{
printf("MFSetAttributeSize failed: 0x%lx\n", hr);
pType->Release();
pReader->Release();
pSource->Release();
return hr;
}
hr = pReader->SetCurrentMediaType(streamIndex, NULL, pType);
if (FAILED(hr))
{
printf("SetCurrentMediaType failed: 0x%lx\n", hr);
pType->Release();
pReader->Release();
pSource->Release();
return hr;
}
pType->Release();
IMFSample *pSample = NULL;
DWORD dwFlags = 0;
for (int i = 0; i < 10; ++i)
{
hr = pReader->ReadSample(streamIndex, 0, NULL, &dwFlags, NULL, &pSample);
if (SUCCEEDED(hr) && pSample)
{
break;
}
Sleep(100);
}
if (FAILED(hr) || !pSample)
{
printf("ReadSample failed: 0x%lx\n", hr);
printf("Flags: 0x%lx\n", dwFlags);
pReader->Release();
pSource->Release();
return hr;
}
printf("Sample read successfully.\n");
IMFMediaBuffer *pBuffer = NULL;
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (FAILED(hr))
{
printf("ConvertToContiguousBuffer failed: 0x%lx\n", hr);
pSample->Release();
pReader->Release();
pSource->Release();
return hr;
}
BYTE *pData = NULL;
DWORD cbBuffer = 0;
hr = pBuffer->Lock(&pData, NULL, &cbBuffer);
if (FAILED(hr))
{
printf("Lock failed: 0x%lx\n", hr);
pBuffer->Release();
pSample->Release();
pReader->Release();
pSource->Release();
return hr;
}
printf("cbBuffer: %lu\n", cbBuffer);
unsigned char *rgbData = new unsigned char[width * height * 3];
ConvertYUY2ToRGB(pData, rgbData, width, height);
const char *filePath = "capture.bmp";
FILE *pFile;
fopen_s(&pFile, filePath, "wb");
if (pFile)
{
BITMAPFILEHEADER bfHeader;
BITMAPINFOHEADER biHeader;
bfHeader.bfType = 'MB';
bfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + width * height * 3;
bfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfHeader.bfReserved1 = 0;
bfHeader.bfReserved2 = 0;
biHeader.biSize = sizeof(BITMAPINFOHEADER);
biHeader.biBitCount = 24;
biHeader.biClrImportant = 0;
biHeader.biClrUsed = 0;
biHeader.biCompression = BI_RGB;
biHeader.biHeight = height;
biHeader.biWidth = width;
biHeader.biPlanes = 1;
biHeader.biSizeImage = width * height * 3;
biHeader.biXPelsPerMeter = 0;
biHeader.biYPelsPerMeter = 0;
fwrite(&bfHeader, sizeof(BITMAPFILEHEADER), 1, pFile);
fwrite(&biHeader, sizeof(BITMAPINFOHEADER), 1, pFile);
fwrite(rgbData, width * height * 3, 1, pFile);
fclose(pFile);
printf("Image saved to %s\n", filePath);
}
else
{
printf("Failed to open file for writing\n");
}
hr = pBuffer->Unlock();
pBuffer->Release();
if (pSample)
{
pSample->Release();
}
if (pReader)
{
pReader->Release();
}
if (pSource)
{
pSource->Release();
}
delete[] rgbData;
return hr;
}
int main()
{
HRESULT hr = InitializeMediaFoundation();
if (SUCCEEDED(hr))
{
hr = CaptureImage();
if (SUCCEEDED(hr))
{
printf("Image captured successfully.\n");
}
else
{
printf("Failed to capture image: 0x%lx\n", hr);
}
}
MFShutdown();
return 0;
}