You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修改说明

  1. 移除自定义GUID:直接使用mfapi.h中定义的标准GUID,避免格式识别错误。
  2. 创建新的目标媒体类型:不再修改设备原生的媒体类型,而是创建全新的RGB32格式类型,确保格式转换逻辑正确。
  3. 获取实际图像参数:设置完媒体类型后,读取实际生效的宽高和格式,确保BMP头信息与像素数据匹配。
  4. 增加格式验证:确认实际获取的媒体类型为RGB32,避免因格式转换失败导致的颜色解析错误。

内容的提问来源于stack exchange,提问作者alireza soltani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 09:27:33