【问题标题】:IMFTransform SetInputType()/SetOutputType() failsIMFTransform SetInputType()/SetOutputType() 失败
【发布时间】:2020-10-29 14:53:29
【问题描述】:

我正在尝试在 Windows 7 上使用 WASAPI 共享模式和媒体基础 IMFSourceReader 播放 MP3(和类似的音频文件)。据我了解,我必须在 IMFSourceReader 解码和 WASAPI 播放之间使用 IMFTransform。除了我在 IMFTransform 上调用 SetInputType()/SetOutputType() 之外,一切似乎都很好?

相关sn-ps的代码有:

MFCreateSourceReaderFromURL(...);   //  Various test mp3 files
...

sourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &reader.audioType);
//sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &reader.audioType);
...

audioClient->GetMixFormat(&player.mixFormat);
...

MFCreateMediaType(&player.audioType);
MFInitMediaTypeFromWaveFormatEx(player.audioType, player.mixFormat, sizeof(WAVEFORMATEX) + player.mixFormat->cbSize);
...




hr = CoCreateInstance(CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&unknown);
ASSERT(SUCCEEDED(hr));

hr = unknown->QueryInterface(IID_PPV_ARGS(&resampler.transform));
ASSERT(SUCCEEDED(hr));
unknown->Release();

hr = resampler.transform->SetInputType(0, inType, 0);
ASSERT(hr != DMO_E_INVALIDSTREAMINDEX);
ASSERT(hr != DMO_E_TYPE_NOT_ACCEPTED);
ASSERT(SUCCEEDED(hr));          //  Fails here with hr = 0xc00d36b4

hr = resampler.transform->SetOutputType(0, outType, 0);
ASSERT(hr != DMO_E_INVALIDSTREAMINDEX);
ASSERT(hr != DMO_E_TYPE_NOT_ACCEPTED);
ASSERT(SUCCEEDED(hr));          //  Fails here with hr = 0xc00d6d60

我怀疑我误解了如何在事物之间协商输入/输出 IMFMediaType,以及如何考虑 IMFTransform 需要对未压缩数据进行操作?

输出类型失败对我来说似乎很奇怪,但这可能是输入类型首先失败的连锁反应 - 如果我尝试先设置输出类型,它也会失败。

【问题讨论】:

    标签: audio ms-media-foundation wasapi


    【解决方案1】:

    在最新版本的 Windows 中,您可能更愿意利用已有的功能。

    当您配置 Source Reader 对象时,IMFSourceReader::SetCurrentMediaType 允许您指定您希望数据包含的媒体类型。如果您设置的媒体类型与 WASAPI 要求兼容,Source Reader 会自动添加转换来为您转换数据。

    不过……

    音频重采样支持已添加到 Windows 8 的源阅读器。在 Windows 8 之前的 Windows 版本中,源阅读器不支持音频重采样。如果您需要在 Windows 8 之前的 Windows 版本中重新采样音频,您可以使用 Audio Resampler DSP。

    ... 这意味着您确实可能需要自己管理 MFT。 MFT 的输入媒体类型应该来自IMFSourceReader::GetCurrentMediaType。要指示源阅读器使用未压缩的音频,您需要为这种类型的流构建一个媒体类型解码器,将音频解码为。例如,如果您的文件是 MP3,那么您将读取通道数、采样率并构建兼容的 PCM 媒体类型(或使用系统解码器并单独询问输出媒体类型,这甚至是一种更简洁的方式)。您可以使用IMFSourceReader::SetCurrentMediaType 设置这种未压缩的音频媒体类型。此媒体类型也将是音频重采样器 MFT 的输入媒体类型。这将指示源阅读器添加必要的解码器,IMFSourceReader::ReadSample 将为您提供转换后的数据。

    reasmpler MFT 的输出媒体类型将派生自您从 WASAPI 获得的音频格式,并使用您在代码 sn-p 顶部提到的 API 调用进行转换。

    要查找错误代码,您可以使用:

    此外,您通常应该能够使用Media Foundation Media Session API 以更轻松的方式播放音频文件。 Media Session 使用相同的原语来构建播放管道并负责格式拟合。

    啊,你是说我需要创建一个附加对象作为解码器,以适应 IMFSourceReader 和 IMFTransform/Resampler 之间的关系?

    没有。通过使用正确的媒体类型执行SetCurrentMediaType,您可以让 Source Reader 在内部添加解码器,以便它可以为您提供已经解压缩的数据。从 Windows 8 开始,它还能够在 PCM 风格之间进行转换,但在 Windows 7 中,您需要使用 Audio Resampler DSP 自行处理。

    您可以自己管理解码器,但您不需要这样做,因为 Source Reader 的解码器会更可靠地执行相同操作。

    您可能需要一个单独的解码器来帮助您猜测 PCM 媒体类型解码器会产生什么,以便您从 Source Reader 请求它。 MFTEnumEx 是查找解码器的正确 API。

    我不确定如何决定或创建合适的解码器对象?我是否需要以某种方式列举合适的列表而不是假设特定的列表?

    提到的MFTEnumMFTEnumEx API 调用可以枚举所有可用或按给定条件过滤的解码器。

    另一种方法是使用部分媒体类型(参见相关解释和代码 sn-p 此处:Tutorial: Decoding Audio)。部分媒体类型是有关所需格式的信号,要求媒体基础 API 提供与此部分类型匹配的原语。相关讨论链接见下方的 cmets。

    【讨论】:

    • 啊,你是说我需要创建一个附加对象,它是解码器,以适应 IMFSourceReader 和 IMFTransform/Resampler 之间的关系?我想在最低级别构建它,这样我还可以访问未压缩的音频数据,因为它可以有效地流过 - 这就是为什么我没有查看 Session API(另外,它在哪里可能有点令人困惑! )。
    • 看完还是一头雾水:docs.microsoft.com/en-us/windows/win32/medfound/…。因为我不确定如何决定或创建合适的解码器对象?我是否需要以某种方式列举合适的列表而不是假设特定的列表?我可以在文档中看到一个带有特定 CLSID_CWMV9EncMediaObject 的示例(但显然我想解码)。我不确定如何“获取系统解码器并单独询问输出媒体类型”?
    • 我认为这是我错过的步骤:docs.microsoft.com/en-us/windows/win32/api/mfapi/…,也许还有一些关于拓扑连接的信息:docs.microsoft.com/en-us/windows/win32/medfound/…
    • 事实证明这很有帮助:codeproject.com/Articles/501521/…
    • 使用 MFTEnumEx() 我可以在我的 Win7 PC 上找到 MFAudioFormat_AAC 到 PCM/float 的 1 个解码器,但 MP3 没有。 Win7没有默认的MP3解码器吗?同时,我将尝试围绕 AAC 测试将其拼凑起来,并进行报告以帮助形成更完整的答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-07
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 2012-06-06
    • 2014-08-28
    相关资源
    最近更新 更多