MediaFoundation + VP8 + 颜色格式

MediaFoundation + VP8 + Color Formats

我的最终目标是为我的虚拟 Class 应用程序添加视频支持。我的方法是:

第一个问题出现在我的网络摄像头支持的颜色编码上。网络摄像头媒体类型仅包含 MFVideoFormat_NV12。我的第一次调试尝试将接收图像保存为位图,以便我可以测试它是否被正确捕获(删除了错误处理):

HRESULT CAP::StartRecord(HWND hh, CComPtr<IMFMediaSource> src)
{
    MFCreateSourceReaderFromMediaSource(src, 0, &sr);
    CComPtr<IMFMediaType> fmt;
    sr->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,&fmt);
    LogMediaType(fmt);      // Shows: MFVideoFormat_NV12

    auto [wi, he] = WidthHeight(fmt);
    for (;;)
    {
        DWORD streamIndex = 0, flags = 0;
        LONGLONG llTimeStamp = 0;
        CComPtr<IMFSample> pSample;
        hr = sr->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,0,&streamIndex,&flags,&llTimeStamp,&pSample);
        if (FAILED(hr))
            break;
        if (!pSample)
            continue;
        CComPtr<IMFMediaBuffer> bu;         
        pSample->ConvertToContiguousBuffer(&bu);            
        SaveSampleNV12(bu, wi, he);
    }
...
}

SaveSampleNV12 使用 here 中的代码将 NV12 转换为 RGB,然后:

void SaveSampleNV12(CComPtr<IMFMediaBuffer> mm, int width32, int height32)
{
    DWORD le = 0;
    mm->GetCurrentLength(&le);
    BYTE *pDatad = NULL;
    auto hr = mm->Lock(&pDatad, NULL, NULL);

    vector<char> rgb(1000000);
    NV12ToRGB((BYTE*)rgb.data(), pDatad, width32, height32);
    mm->Unlock();


    HANDLE file;
    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER fileInfo;
    DWORD write = 0;

    auto df = L"r:\f.bmp";
    file = CreateFile(df.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  //Sets up the new bmp to be written to

    int bits = 24;
    fileHeader.bfType = 19778;                                                                    //Sets our type to BM or bmp
    fileHeader.bfSize = sizeof(fileHeader.bfOffBits) + sizeof(RGBTRIPLE);                                                //Sets the size equal to the size of the header struct
    fileHeader.bfReserved1 = 0;                                                                    //sets the reserves to 0
    fileHeader.bfReserved2 = 0;
    fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);                    //Sets offbits equal to the size of file and info header

    fileInfo.biSize = sizeof(BITMAPINFOHEADER);
    fileInfo.biWidth = width32;
    fileInfo.biHeight = height32;
    fileInfo.biPlanes = 1;
    fileInfo.biBitCount = bits;
    fileInfo.biCompression = BI_RGB;
    fileInfo.biSizeImage = width32 * height32 * (bits / 8);
    fileInfo.biXPelsPerMeter = 0;// 2400;
    fileInfo.biYPelsPerMeter = 0;// 2400;
    fileInfo.biClrImportant = 0;
    fileInfo.biClrUsed = 0;

    WriteFile(file, &fileHeader, sizeof(fileHeader), &write, NULL);
    WriteFile(file, &fileInfo, sizeof(fileInfo), &write, NULL);

    unsigned char* ptrIn = (unsigned char*)rgb.data();
    int rgbs = width32 * height32 * (bits / 8);
    vector<char> d2(rgbs);
    unsigned char* ptrOut = (unsigned char*)d2.data();
    for (int i = 0; i < (width32*height32) / 2; ++i)
    {
        int y0 = ptrIn[0];
        int u0 = ptrIn[1];
        int y1 = ptrIn[2];
        int v0 = ptrIn[3];
        ptrIn += 4;
        int c = y0 - 16;
        int d = u0 - 128;
        int e = v0 - 128;
        int bb = clip((298 * c + 516 * d + 128) >> 8); // blue
        int gg = clip((298 * c - 100 * d - 208 * e + 128) >> 8); // green
        int rr = clip((298 * c + 409 * e + 128) >> 8); // red

        ptrOut[0] = bb;
        ptrOut[1] = gg;
        ptrOut[2] = rr;

        c = y1 - 16;
        ptrOut[3] = clip((298 * c + 516 * d + 128) >> 8); // blue
        ptrOut[4] = clip((298 * c - 100 * d - 208 * e + 128) >> 8); // green
        ptrOut[5] = clip((298 * c + 409 * e + 128) >> 8); // red
        ptrOut += 6;
    }

    unsigned char* cc = (unsigned char*)d2.data();
    WriteFile(file, cc, rgbs, &write, NULL);
    CloseHandle(file);
}

这张returns一张奇怪的粉红色图片。我做错了什么,但是什么?

非常感谢。

解决方案是使用IMFTransform在各种颜色空间之间进行转换。