Media Foundation摄像头捕获图像异常:黑白双画面问题求助
解决Media Foundation摄像头捕获图像黑白且双画面的问题
问题原因分析
- 硬编码图像尺寸:代码中固定BITMAPINFOHEADER的宽高为640x480,但实际摄像头输出或格式转换后的图像尺寸可能不同,导致像素数据解析错位,出现左右双画面。
- 自定义GUID错误:手动定义的
MFVideoFormat_RGB32等GUID与系统标准定义不一致,导致Media Foundation无法正确识别格式,颜色解码失败,出现黑白画面。 - 媒体类型修改逻辑错误:直接修改从设备获取的原生媒体类型并重新设置,可能引发属性冲突,导致格式转换未实际生效,代码仍按RGB32处理摄像头原生的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, "mfreadwrite.lib") #pragma comment(lib, "mf.lib") // 移除自定义GUID,使用系统内置定义 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* pTargetType = NULL; IMFMediaType* pActualType = 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 = MFCreateMediaType(&pTargetType); if (FAILED(hr)) { printf("MFCreateMediaType failed: 0x%lx\n", hr); pReader->Release(); pSource->Release(); return hr; } hr = pTargetType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); if (FAILED(hr)) { printf("SetGUID MF_MT_MAJOR_TYPE failed: 0x%lx\n", hr); pTargetType->Release(); pReader->Release(); pSource->Release(); return hr; } hr = pTargetType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32); if (FAILED(hr)) { printf("SetGUID MF_MT_SUBTYPE failed: 0x%lx\n", hr); pTargetType->Release(); pReader->Release(); pSource->Release(); return hr; } // 设置目标媒体类型 hr = pReader->SetCurrentMediaType(streamIndex, NULL, pTargetType); if (FAILED(hr)) { printf("SetCurrentMediaType failed: 0x%lx\n", hr); pTargetType->Release(); pReader->Release(); pSource->Release(); return hr; } pTargetType->Release(); // 获取实际生效的媒体类型,确认格式和尺寸 hr = pReader->GetCurrentMediaType(streamIndex, &pActualType); if (FAILED(hr)) { printf("GetCurrentMediaType (actual) failed: 0x%lx\n", hr); pReader->Release(); pSource->Release(); return hr; } UINT32 width = 0, height = 0; hr = MFGetAttributeSize(pActualType, MF_MT_FRAME_SIZE, &width, &height); if (FAILED(hr)) { printf("MFGetAttributeSize failed: 0x%lx\n", hr); pActualType->Release(); pReader->Release(); pSource->Release(); return hr; } GUID subtype = {0}; hr = pActualType->GetGUID(MF_MT_SUBTYPE, &subtype); if (FAILED(hr)) { printf("GetGUID MF_MT_SUBTYPE failed: 0x%lx\n", hr); pActualType->Release(); pReader->Release(); pSource->Release(); return hr; } if (subtype != MFVideoFormat_RGB32) { printf("Warning: Failed to convert to RGB32, actual subtype: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", subtype.Data1, subtype.Data2, subtype.Data3, subtype.Data4[0], subtype.Data4[1], subtype.Data4[2], subtype.Data4[3], subtype.Data4[4], subtype.Data4[5], subtype.Data4[6], subtype.Data4[7]); pActualType->Release(); pReader->Release(); pSource->Release(); return E_FAIL; } pActualType->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; } 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 = width; // 使用实际获取的宽度 biHeader.biHeight = -height; // 使用实际获取的高度,负号表示顶行在前 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; }
关键修改说明
- 移除自定义GUID:直接使用
mfapi.h中定义的标准GUID,避免格式识别错误。 - 创建新的目标媒体类型:不再修改设备原生的媒体类型,而是创建全新的RGB32格式类型,确保格式转换逻辑正确。
- 获取实际图像参数:设置完媒体类型后,读取实际生效的宽高和格式,确保BMP头信息与像素数据匹配。
- 增加格式验证:确认实际获取的媒体类型为RGB32,避免因格式转换失败导致的颜色解析错误。
内容的提问来源于stack exchange,提问作者alireza soltani
相关产品推荐
相关产品推荐

