将 Demux 与基本流配合使用

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

当 MPEG-2 demux 提供 PES 有效负载时,它会以批量的媒体样本形式发送 ES 字节流。 默认样本大小为 8K。 demux 在每个 PES 边界上启动一个新的媒体样本,但可能会将单个 PES 有效负载分解为多个样本。 例如,如果 PES 有效负载为 20K,则会在两个 8K 样本中提供,后跟一个 4K 样本。 demux 不检查字节流的内容。 由解码器来分析序列标头并查找格式更改。

当 demux 筛选器的输出引脚连接到解码器时,它会提供创建引脚时指定的媒体类型。 由于 demux 不检查 ES 字节流,因此它不会验证媒体类型。 理论上,MPEG-2 解码器应能够仅连接填充的主类型和子类型,以指示数据类型。 然后,解码器应检查到达媒体样本中的序列标头。 但是,实际上,除非媒体类型包含完整的格式块,否则许多解码器将无法连接。

例如,假设 PID 0x31包含 MPEG-2 main配置文件视频。 至少需要执行以下步骤。

首先,为 MPEG-2 视频创建媒体类型。 暂时不考虑格式块:

AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video ;
mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO;
// Possibly create a format block (not shown here).

接下来,在 demux 上创建输出引脚:

// Query the demux filter for IMpeg2Demultiplexer.
IMpeg2Demultiplexer *pDemux;
hr = pFilter->QueryInterface(IID_IMpeg2Demultiplexer, (void**)&pDemux);
if (SUCCEEDED(hr))
{
    // Create a new output pin.
    IPin *pPin0;
    hr = pDemux->CreateOutputPin(&mt, L"Video Pin", &pPin0);
    if (SUCCEEDED(hr))
    {
        ....
    }
}

然后,查询 IMPEG2PIDMap 接口的新引脚并调用 MapPID。 指定 PID 编号0x30,并为 PES 有效负载指定MEDIA_ELEMENTARY_STREAM。

// Query the pin for IMPEG2PIDMap. This implicitly configures the
// demux to carry a transport stream. 
IMPEG2PIDMap *pPidMap;
hr = pPin0->QueryInterface(IID_IMPEG2PIDMap, (void**)&pPidMap);
if (SUCCEEDED(hr))
{
    // Assign PID 0x31 to pin 0. Set the type to "PES payload."
    ULONG Pid = 0x031;
    hr = pPidMap->MapPID(1, &Pid, MEDIA_ELEMENTARY_STREAM);
    pPidMap->Release();
}

最后,完成后释放所有接口:

pPin0->Release();
pDemux->Release();

下面是设置媒体类型(包括格式块)的更完整示例。 此示例仍假定 MPEG-2 main配置文件视频。 详细信息将因流内容而异:

// Set up a byte array to hold the first sequence header. 
// This may or may not be required, depending on the decoder.
BYTE SeqHdr[] = { ... };  // Contains the sequence header (not shown).

AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video ;
mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO;
mt.formattype = FORMAT_MPEG2Video;

// Allocate the format block, including space for the sequence header. 
mt.cbFormat = sizeof(MPEG2VIDEOINFO) + sizeof(SeqHdr);
mt.pbFormat = (BYTE*)CoTaskMemAlloc(mt.cbFormat);
if (mt.pbFormat == NULL)
{
    // Out of memory; return an error code.
}
ZeroMemory(mt.pbFormat, mt.cbFormat);

// Cast the buffer pointer to an MPEG2VIDEOINFO struct.
MPEG2VIDEOINFO *pMVIH = (MPEG2VIDEOINFO*)mt.pbFormat;

RECT rcSrc = {0, 480, 0, 720};        // Source rectangle.
pMVIH->hdr.rcSource = rcSrc;
pMVIH->hdr.dwBitRate = 4000000;       // Bit rate.
pMVIH->hdr.AvgTimePerFrame = 333667;  // 29.97 fps.
pMVIH->hdr.dwPictAspectRatioX = 4;    // 4:3 aspect ratio.
pMVIH->hdr.dwPictAspectRatioY = 3;

// BITMAPINFOHEADER information.
pMVIH->hdr.bmiHeader.biSize = 40;
pMVIH->hdr.bmiHeader.biWidth = 720;
pMVIH->hdr.bmiHeader.biHeight = 480;

pMVIH->dwLevel = AM_MPEG2Profile_Main;  // MPEG-2 profile. 
pMVIH->dwProfile = AM_MPEG2Level_Main;  // MPEG-2 level.

// Copy the sequence header into the format block.
pMVIH->cbSequenceHeader = sizeof(SeqHdr); // Size of sequence header.
memcpy(pMVIH->dwSequenceHeader, SeqHdr, sizeof(SeqHdr));

使用 MPEG-2 Demultiplexer