MPEG 解码器预处理转换

[与此页面关联的功能 DirectShow 是旧版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音频/视频捕获所取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能在 Media Foundation 中使用 MediaPlayerIMFMediaEngine音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]

Letterbox 和 PanScan

4x3 图像可以通过填充图像的顶部和底部 (称为 Letterbox 图像) 或通过提取图像的 4x3 部分 (称为 PanScan 图像) 来形成。 菜单和子图片流叠加在最终视频图像的顶部。 16x9 比率图像以 4x3 变形格式存储。 将变形 4x3 纵横比 720x480 源视频拉伸为 16x9 纵横比,形成原始的 16x9 纵横比。

下面介绍了如何正确显示每种模式及其亮点:

  • 宽屏: 源视频延伸至输出窗口的最大 16x9 区域。 亮点相对于 16x9 区域的内部。 应将黑色条添加到顶部和底部或两侧,以保持 16x9 区域。
  • 平移扫描: 在 16x9 视频中,使用 MPEG2 流中提供的水平偏移量提取 4x3 子窗口。 将 4x3 子窗口放入输出客户端窗口的最大 4x3 区域。 突出显示的坐标相对于 4x3 输出窗口,与源 16x9 视频没有关系。 应将黑色条添加到顶部和底部或两侧,以保持 4x3 区域。
  • 信箱: 计算输出窗口的最大 4x3 区域。 应将黑色条添加到顶部和底部或两侧,以保持 4x3 区域。 源变形 4x3 视频 (表示 16x9 图像) 放置在 4x3 区域内最大的 16x9 子窗口中。 应将黑条添加到子窗口的顶部和底部,以保持 16x9 区域。 突出显示的坐标相对于 4x3 区域,与源 16x9 视频没有关系。 磁盘可以指定位于 16x9 区域外部的突出显示 (但仍在 4x3 窗口) 。 对于 4x3 视频,视频放置在输出客户端窗口的最大 4x3 输出区域。 应将黑色条添加到顶部和底部或两侧,以保持 4x3 区域。

使用 DVD 导航器和 VMR 进行 MPEG 预处理

目前,解码器传递FORMAT_MPEG2_VIDEO媒体类型 (其格式块指向 MPEG2VIDEOINFO 结构) 。 在输出引脚上,解码器生成FORMAT_VideoInfo2媒体类型,其格式块指向 VIDEOINFOHEADER2 结构。 结构的 dwReserved 字段已重命名为 dwControls 标志。

dwControlFlags 成员现在将包含新位。

Label
AMCONTROL_USED 0x00000001
AMCONTROL_PAD_TO_4x3 0x00000002
AMCONTROL_PAD_TO_16x9 0x00000004

 

AMCONTROL_USED用于测试这些新标志是否受支持。 源筛选器应设置AMCONTROL_USED标志,并查看 QueryAccept (MediaType) 是否在下游引脚上成功。 如果它被拒绝,则无法使用 AMCONTROL 标志,并且 dwReserved1 必须设置为 0。

AMCONTROL_PAD_TO_4x3指示图像应填充并在 4x3 区域中显示。

AMCONTROL_PAD_TO_16x9指示图像应填充并在 16x9 区域中显示。

解码器应盲目复制或处理位。 如果解码器自行执行信箱,则必须更改像素纵横比、填充图像并删除相应的AMCONTROL_* 位。

MPEG2VIDEOINFO.dwFlags 现在包含三个标志,用于控制内容的显示方式:

  • AMMPEG2_DoPanScan (0x00000001):如果设置了此标志,MPEG-2 视频解码器应根据picture_display_extension中的泛扫描矢量裁剪输出图像,并将图片纵横比更改为 4x3。 VMR 不应收到具有此标志的 16x9 示例。 简单的实现可能会更改源矩形,以指示 540 宽的源区域,其左边缘等于picture_display_extension中的显示偏移量。

  • AMMPEG2_LetterboxAnalogOut (0x00000020):当硬件解码器将此流显示到视频输出 (通常是卡) 上的 SVIDEO 连接器时,它应应用规则在 4x3 显示器上显示 16x9 示例。

    在处理图像时,生成发送到 VMR) 输出的软件解码器 (或硬件解码器有两个选项:

    1. 忽略此标志并将 VideoInfoHeader2 内容传递给 VMR, (DVD 导航器 已在示例) 上设置AMCONTROL_PAD_TO_4x3标志。 VMR 将遇到具有AMCONTROL_PAD_TO_4x3标志集和 4x3 子图片流的 16x9 视频示例。 应用程序必须将两个流的输出规范化目标矩形设置为相同的宽度。
    2. 通过填充图像的顶部和底部并将图像纵横比设置为 4x3,将变形流转换为 4x3 图像 (请参阅上面的 letterbox) 并从 VIDEOINFOHEADER2 中删除AMCONTROL_PAD_TO_4x3位。

    混合视频和子图片流的 DirectXVA 解码器必须处理此标志。 如果硬件无法缩放混合子图片,则解码器应生成单独的子图片流,以便 VMR 与视频混合。

  • AMMPEG2_WidescreenAnalogOut (0x00000200):当硬件解码器将此流显示到视频输出 (通常是卡) 上的 SVIDEO 连接器时,它应假定显示 16x9 (变形) 。

    (的软件解码器或生成发送到 VMR) 输出的硬件解码器在处理变形图像时有两个选项:

    1. 忽略此标志,并将 VideoInfoHeader2 内容复制到 VMR。 如果 VMR 设置了AMCONTROL_PAD_TO_16x9,则 VMR 会将 4x3 映像填充到 16x9。
    2. 将输出图像填充到 16x9 图像并删除AMCONTROL_PAD_TO_16x9位。

大多数解码器应使用 GetMediaType 来检测输入引脚上的媒体更改,并将 MPEG2INFOHEADER) 中包含的 VIDEOINFOHEADER2 内容 (复制到输出引脚。 它们可能只处理 PanScan 位。

以下示例代码演示如何将 VIDEOINFOHEADER2 内容从输入引脚复制到输出引脚。

#include <dvdmedia.h>
HRESULT CopyMPeg2ToVideoInfoHeader2(CMediaSample* pInSample, CMediaSample* pOutSample)
{
    HRESULT hr = S_OK;
    // Check for a media type on the input sample.
    AM_MEDIA_TYPE* pInType;
    if (pInSample->GetMediaType(&pInType) == S_OK) 
    {
        // Make sure it's an MPEG2 Video format.
        if ((pInType->formattype == FORMAT_MPEG2_VIDEO) &&
            (pInType->cbFormat >= sizeof(MPEG2VIDEOINFO)))
        {
            hr = S_OK; // Initialize hr for the CMediaType constructor.
            CMediaType outType(*pInType, &hr);
            if (FAILED(hr))
            {
                DeleteMediaType( pInType );
                return hr;
            }

            // Set the format type GUID.
            outType.SetFormatType(&FORMAT_VideoInfo2);
                
            // Truncate the format block to include just the VIDEOINFOHEADER part.
            MPEG2VIDEOINFO *pMPeg2Header = (MPEG2VIDEOINFO*)pInType->pbFormat;
            BYTE *pVIH = (BYTE*)&pMPeg2Header->hdr;
            hr = (outType.SetFormat(pVIH, sizeof(VIDEOINFOHEADER2)) ? S_OK : E_OUTOFMEMORY);
            if (SUCCEEDED(hr))
            {
                hr = pOutSample->SetMediaType(&outType);
            }
        } 
        else 
        {
            ASSERT(FALSE); // Not a MPEG2 header.
            hr = VFW_E_INVALIDMEDIATYPE;
        }
        DeleteMediaType( pInType );
    } 

    return hr;
}