DirectShow 通用媒体解码器

DirectShow universal media decoder

我是 DirectShow 新手 API。

我想解码媒体文件并使用 DirectShow 获得未压缩的 RGB 视频帧。

我注意到所有这些操作都应该通过一个GraphBuilder来完成。此外,每个处理块都称为过滤器,并且针对不同的媒体文件有许多不同的过滤器。例如,对于解码 H264 我们应该使用 "Microsoft MPEG-2 Video Decoder",对于 AVI 文件 "AVI Splitter Filter" 等

我想知道是否有可以处理所有这些文件类型的通用方法(解码器)?

如果有人能指出从导入本地文件到将其解码为未压缩的 RGB 帧的示例,我将不胜感激。我发现的所有示例都在处理 window 句柄,它们只是对其进行配置并调用 pGraph->运行()。我还浏览了 Windows 个 SDK 示例,但找不到有用的示例。

非常感谢。

通用DirectShow解码器一般来说是反对DirectShow的概念API。整个想法是单独的过滤器负责单独的任务(特别是解码特定编码或多路分解特定容器格式)。过滤器的注册表和 Intelligent Connect 允许将过滤器内置在链中以执行某些请求的处理,特别是从压缩格式解码为视频的 24 位 RGB。

从这个角度来看,您不需要通用解码器,并且预计不会存在这样的解码器。但是,这样的解码器(或关闭)确实存在并且它是 ffdshow 或其衍生物之一。例如,目前,您可能想查看 LAVFilters。他们包装 FFmpeg,它本身可以处理多种格式,并将其连接到 DirectShow API,这样,作为过滤器,ffdshow 可以处理许多 formats/encodings.

没有使用或不使用此类编解码器包的一般规则,在大多数情况下,您会考虑各种因素并决定做什么。如果您的应用程序处理各种场景,那么构建图形的一个很好的起点是 Overview of Graph Building.

My goal is to accomplish the task using DirectShow in order to have no external dependencies. Do you know a particular example that does uncompressing frames for some file type?

您的要求过于宽泛,同时又很典型,在某种程度上又很简单。如果您花一些时间玩弄 GraphEdit SDK tool, or rather GraphStudioNext,它是前者的更强大版本,您将能够以交互方式构建滤镜图,还可以渲染不同类型的媒体文件并查看哪些滤镜参与渲染。您也可以通过编程方式完成相同的操作,因为交互操作基本上都有单独匹配的 API 调用。

您将能够看到特定格式由不同的过滤器处理,上面提到的 Intelligent Connect 正在组合构建过滤器链以满足请求并将管道整合在一起。

默认用例是播放,如果你想将视频渲染为 24/32 位 RGB,你的操作过程非常相似:你要构建一个图形,它只需要用一些东西终止别的。更灵活、复杂和典型的高级开发方法是提供自定义视频渲染器过滤器并在其上接受解压缩的 RGB 帧。

一个简单且非常流行的解决方案版本是使用 Sample Grabber 过滤器,将其初始化为接受 RGB,在其上设置一个回调,以便每次 RGB 帧时调用您的 SampleCB 回调方法解压缩,并在图中使用 Sample Grabber。 (如果您在开放源代码 and/or 网络上搜索关键字 ISampleGrabberISampleGrabberCBSampleCBBufferCB,您会发现确实有很多尝试可以实现这一目标, MEDIASUBTYPE_RGB24).

另一种或多或少流行的方法是设置播放管道、播放文件并从视频演示器中读回帧。这是在问题的另一个答案中建议的,相对容易做到,如果您没有性能要求和提取每一帧的要求,它就可以完成这项工作。也就是说,这是从提要中获取随机 RGB 帧而不是 every/all 帧的好方法。参见相关内容:

  • Different approaches on getting captured video frames in DirectShow

您正在寻找 DirectShow 库中的 vmr9 示例。

在您的 Windows SDK 安装中,查找此示例:

微软SDKs\Windows\v7.0\Samples\multimedia\directshow\vmr9\windowless\windowless.sln

然后搜索这个函数:CaptureImage,在这个方法中,看到IVMRWindowlessControl9::GetCurrentImage,正是你想要的

此方法以位图格式 (RGB) 捕获视频帧。 接下来,这是 CaptureImage 代码的副本:

BOOL CaptureImage(LPCTSTR szFile)
{
HRESULT hr;

if(pWC && !g_bAudioOnly)
{
    BYTE* lpCurrImage = NULL;

    // Read the current video frame into a byte buffer.  The information
    // will be returned in a packed Windows DIB and will be allocated
    // by the VMR.
    if(SUCCEEDED(hr = pWC->GetCurrentImage(&lpCurrImage)))
    {
        BITMAPFILEHEADER    hdr;
        DWORD               dwSize, dwWritten;
        LPBITMAPINFOHEADER  pdib = (LPBITMAPINFOHEADER) lpCurrImage;

        // Create a new file to store the bitmap data
        HANDLE hFile = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_READ, NULL,
                                  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

        if (hFile == INVALID_HANDLE_VALUE)
            return FALSE;

        // Initialize the bitmap header
        dwSize = DibSize(pdib);
        hdr.bfType          = BFT_BITMAP;
        hdr.bfSize          = dwSize + sizeof(BITMAPFILEHEADER);
        hdr.bfReserved1     = 0;
        hdr.bfReserved2     = 0;
        hdr.bfOffBits       = (DWORD)sizeof(BITMAPFILEHEADER) + pdib->biSize +
            DibPaletteSize(pdib);

        // Write the bitmap header and bitmap bits to the file
        WriteFile(hFile, (LPCVOID) &hdr, sizeof(BITMAPFILEHEADER), &dwWritten, 0);
        WriteFile(hFile, (LPCVOID) pdib, dwSize, &dwWritten, 0);

        // Close the file
        CloseHandle(hFile);

        // The app must free the image data returned from GetCurrentImage()
        CoTaskMemFree(lpCurrImage);

        // Give user feedback that the write has completed
        TCHAR szDir[MAX_PATH];

        GetCurrentDirectory(MAX_PATH, szDir);

        // Strip off the trailing slash, if it exists
        int nLength = (int) _tcslen(szDir);
        if (szDir[nLength-1] == TEXT('\'))
            szDir[nLength-1] = TEXT('[=10=]');

        Msg(TEXT("Captured current image to %s\%s."), szDir, szFile);
        return TRUE;
    }
    else
    {
        Msg(TEXT("Failed to capture image!  hr=0x%x"), hr);
        return FALSE;
    }
}

return FALSE;
}