如何使用 Microsoft Media Foundation 将原始 48khz/32 位 PCM 编码为 FLAC?

Posted

技术标签:

【中文标题】如何使用 Microsoft Media Foundation 将原始 48khz/32 位 PCM 编码为 FLAC?【英文标题】:How do I encode raw 48khz/32bits PCM to FLAC using Microsoft Media Foundation? 【发布时间】:2018-02-22 14:57:00 【问题描述】:

我创建了一个能够使用 Microsoft 的媒体基础平台对视频和音频进行编码的 SinkWriter。

到目前为止,视频运行良好,但我在音频方面遇到了一些问题。

我的 PCM 源的采样率为 48828hz,每个采样 32 位并且是单声道的。

到目前为止,除了 FLAC,一切都运行良好。

例如,MP3 输出或多或少可以正常工作,但格式错误。关于MSDN (MP3 Audio Encoder),MP3 编码器仅支持每个样本 16 位作为输入。如上所述,我的 PCM 源每个样本有 32 位。

但是 MP3 的导出工作正常,因为 MF 平台似乎有某种回退,并且正在使用具有 2 个通道、32khz 和 320kb/s 比特率的 MPEG 音频层 1/2 (mpga)。

当我将 MF_MT_SUBTYPE 设置为 MFAudioFormat_FLAC 时,事情开始变得奇怪。导出也正常,但音频质量很棒。有很多噪音,但我能够识别音频。关于 VLC,FLAC 文件的采样率为 44,1khz,每个采样 8 位,并且是单声道的。

这是否意味着 FLAC 编解码器无法使用我提供的 PCM?

有没有人遇到过同样的问题并且能够解决它?

更新

在对这个问题进行了更多研究之后,我的 32 位分辨率的 PCM 音频似乎太高了。所以目前我正在尝试将 32 位 PCM 转换为 24 位的 FLAC 和 16 位的 MP3,但到目前为止没有运气。如果我取得了一些进展,我会及时通知您。

--------

更新 2

我创建了一个显示我面临的问题的最小示例应用程序。 它读取 48khz32bit 波形文件并尝试将其编码为 flac。

执行hr = pSinkWriter->BeginWriting(); 命令时,我收到错误0xc00d36b4,这意味着The data specified for the media type is invalid, inconsistent, or not supported by this object

我在这里做错了什么?

#include "stdafx.h"

#include <windows.h>
#include <windowsx.h>

#include <comdef.h>

#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <Mferror.h>

#pragma comment(lib, "ole32")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfuuid")

using namespace System;


int main(array<System::String ^> ^args)

    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);

    hr = MFStartup(MF_VERSION);

    IMFMediaType *pMediaType;
    IMFMediaType *pMediaTypeOut;
    IMFSourceReader *pSourceReader;
    IMFAttributes *pAttributes;
    IMFSinkWriter *pSinkWriter;

    hr = MFCreateSourceReaderFromURL(
        L"C:\\Temp\\48khz32bit.wav",
        NULL,
        &pSourceReader
    );

    hr = MFCreateAttributes(&pAttributes, 1);

    hr = pAttributes->SetGUID(
        MF_TRANSCODE_CONTAINERTYPE,
        MFTranscodeContainerType_WAVE
    );

    hr = MFCreateSinkWriterFromURL(
        L"C:\\Temp\\foo.flac",
        NULL,
        pAttributes,
        &pSinkWriter
    );

    hr = pSourceReader->GetCurrentMediaType(
        MF_SOURCE_READER_FIRST_AUDIO_STREAM,
        &pMediaType);

    hr = MFCreateMediaType(&pMediaTypeOut);

    hr = pMediaTypeOut->SetGUID(
        MF_MT_MAJOR_TYPE,
        MFMediaType_Audio
    );

    hr = pMediaTypeOut->SetGUID(
        MF_MT_SUBTYPE,
        MFAudioFormat_FLAC
    );

    hr = pMediaTypeOut->SetUINT32(
        MF_MT_AUDIO_SAMPLES_PER_SECOND,
        48000
    );

    hr = pMediaTypeOut->SetUINT32(
        MF_MT_AUDIO_NUM_CHANNELS,
        1
    );

    hr = pMediaTypeOut->SetUINT32(
        MF_MT_AUDIO_BITS_PER_SAMPLE,
        32
    );

    hr = pMediaTypeOut->SetUINT32(
        MF_MT_AUDIO_AVG_BYTES_PER_SECOND,
        (((32 + 7) / 8) * 1) * 48000
    );

    hr = pMediaTypeOut->SetUINT32(
        MF_MT_AUDIO_BLOCK_ALIGNMENT,
        ((32 + 7) / 8) * 1
    );

    DWORD nWriterStreamIndex = -1;

    hr = pSinkWriter->AddStream(pMediaTypeOut, &nWriterStreamIndex);

    hr = pSinkWriter->BeginWriting();

    _com_error err(hr);
    LPCTSTR errMsg = err.ErrorMessage();

    for (;;)
    
        DWORD nStreamIndex, nStreamFlags;
        LONGLONG nTime;
        IMFSample *pSample;

        hr = pSourceReader->ReadSample(
            MF_SOURCE_READER_FIRST_AUDIO_STREAM,
            0,
            &nStreamIndex,
            &nStreamFlags,
            &nTime,
            &pSample);

        if (pSample)
        
            OutputDebugString(L"Write sample...\n");

            hr = pSinkWriter->WriteSample(
                nWriterStreamIndex,
                pSample
            );
        

        if (nStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM)
        
            break;
        
    

    hr = pSinkWriter->Finalize();

    return 0;

--------

更新 3

我添加了解决方案作为答案。

--------

初始化 SinkWriter

HRESULT SinkWriter::InitializeSinkWriter(IMFSinkWriter **ppWriter, DWORD *pStreamIndex, DWORD *pAudiostreamIndex, LPCWSTR filename)

    *ppWriter = NULL;
    *pStreamIndex = NULL;
    *pAudioStreamIndex = NULL;

    IMFSinkWriter   *pSinkWriter = NULL;

    // Attributes
    IMFAttributes   *pAttributes;

    HRESULT hr = S_OK;

    DX::ThrowIfFailed(
        MFCreateAttributes(
            &pAttributes,
            3
        )
    );

#if defined(ENABLE_HW_ACCELERATION)
    CComPtr<ID3D11Device> device;
    D3D_FEATURE_LEVEL levels[] =  D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 ;

#if defined(ENABLE_HW_DRIVER)
    DX::ThrowIfFailed(
        D3D11CreateDevice(
            nullptr,
            D3D_DRIVER_TYPE_HARDWARE,
            nullptr,
            (0 * D3D11_CREATE_DEVICE_SINGLETHREADED) | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
            levels,
            ARRAYSIZE(levels),
            D3D11_SDK_VERSION,
            &device,
            nullptr,
            nullptr
        )
    );

    const CComQIPtr<ID3D10Multithread> pMultithread = device;
    pMultithread->SetMultithreadProtected(TRUE);
#else
    DX::ThrowIfFailed(
        D3D11CreateDevice(
            nullptr,
            D3D_DRIVER_TYPE_NULL,
            nullptr,
            D3D11_CREATE_DEVICE_SINGLETHREADED,
            levels,
            ARRAYSIZE(levels),
            D3D11_SDK_VERSION,
            &device,
            nullptr,
            nullptr)
    );
#endif

    UINT token;
    CComPtr<IMFDXGIDeviceManager> pManager;

    DX::ThrowIfFailed(
        MFCreateDXGIDeviceManager(
            &token,
            &pManager
        )
    );

    DX::ThrowIfFailed(
        pManager->ResetDevice(
            device,
            token
        )
    );

    DX::ThrowIfFailed(
        pAttributes->SetUnknown(
            MF_SOURCE_READER_D3D_MANAGER,
            pManager
        )
    );

    DX::ThrowIfFailed(
        pAttributes->SetUINT32(
            MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS,
            TRUE
        )
    );

#if (WINVER >= 0x0602)
    DX::ThrowIfFailed(
        pAttributes->SetUINT32(
            MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING,
            TRUE
        )
    );
#endif
#else
    DX::ThrowIfFailed(
        pAttributes->SetUINT32(
            MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS,
            TRUE
        )
    );

    DX::ThrowIfFailed(
        pAttributes->SetUINT32(
            MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING,
            TRUE
        )
    );
#endif

    DX::ThrowIfFailed(
        MFCreateSinkWriterFromURL(
            filename,
            NULL,
            pAttributes,
            &pSinkWriter
        )
    );

    if (m_vFormat != VideoFormat::SWFV_NONE)
    
        DX::ThrowIfFailed(
            InitializeVideoCodec(
                pSinkWriter,
                pStreamIndex
            )
        );
    

    if (m_audFormat != AudioFormat::SWAF_NONE)
    
        DX::ThrowIfFailed(
            InitializeAudioCodec(
                pSinkWriter,
                pAudioStreamIndex
            )
        );
    

    // Tell the sink writer to start accepting data.
    DX::ThrowIfFailed(
        pSinkWriter->BeginWriting()
    );

    // Return the pointer to the caller.
    *ppWriter = pSinkWriter;
    (*ppWriter)->AddRef();

    SAFE_RELEASE(pSinkWriter);
    return hr;

初始化音频编解码器

HRESULT SinkWriter::InitializeAudioCodec(IMFSinkWriter *pSinkWriter, DWORD *pStreamIndex)

    // Audio media types
    IMFMediaType    *pAudioTypeOut = NULL;
    IMFMediaType    *pAudioTypeIn = NULL;

    DWORD           audioStreamIndex;

    HRESULT hr = S_OK;

    // Set the output audio type.
    DX::ThrowIfFailed(
        MFCreateMediaType(
            &pAudioTypeOut
        )
    );

    DX::ThrowIfFailed(
        pAudioTypeOut->SetGUID(
            MF_MT_MAJOR_TYPE, 
            MFMediaType_Audio
        )
    );

    DX::ThrowIfFailed(
        pAudioTypeOut->SetGUID(
            MF_MT_SUBTYPE,
            AUDIO_SUBTYPE
        )
    );

    DX::ThrowIfFailed(
        pSinkWriter->AddStream(
            pAudioTypeOut,
            &audioStreamIndex
        )
    );

    // Set the input audio type
    DX::ThrowIfFailed(
        MFCreateMediaType(
            &pAudioTypeIn
        )
    );

    DX::ThrowIfFailed(
        pAudioTypeIn->SetGUID(
            MF_MT_MAJOR_TYPE,
            AUDIO_MAJOR_TYPE
        )
    );

    DX::ThrowIfFailed(
        pAudioTypeIn->SetGUID(
            MF_MT_SUBTYPE,
            MFAudioFormat_PCM
        )
    );

    DX::ThrowIfFailed(
        pAudioTypeIn->SetUINT32(
            MF_MT_AUDIO_NUM_CHANNELS,
            AUDIO_NUM_CHANNELS
        )
    );

    DX::ThrowIfFailed(
        pAudioTypeIn->SetUINT32(
            MF_MT_AUDIO_BITS_PER_SAMPLE,
            AUDIO_BITS_PER_SAMPLE
        )
    );

    DX::ThrowIfFailed(
        pAudioTypeIn->SetUINT32(
            MF_MT_AUDIO_BLOCK_ALIGNMENT,
            AUDIO_BLOCK_ALIGNMENT
        )
    );

    DX::ThrowIfFailed(
        pAudioTypeIn->SetUINT32(
            MF_MT_AUDIO_SAMPLES_PER_SECOND,
            AUDIO_SAMPLES_PER_SECOND
        )
    );

    DX::ThrowIfFailed(
        pAudioTypeIn->SetUINT32(
            MF_MT_AUDIO_AVG_BYTES_PER_SECOND,
            AUDIO_AVG_BYTES_PER_SECOND
        )
    );

    DX::ThrowIfFailed(
        pSinkWriter->SetInputMediaType(
            audioStreamIndex,
            pAudioTypeIn,
            NULL
        )
    );

    *pStreamIndex = audioStreamIndex;

    SAFE_RELEASE(pAudioTypeOut);
    SAFE_RELEASE(pAudioTypeIn);

    return hr;

推送音频数据

HRESULT SinkWriter::PushAudio(UINT32* data)

    HRESULT hr = S_FALSE;

    if (m_isInitializing)
    
        return hr;
    

    IMFSample *pSample = NULL;
    IMFMediaBuffer *pBuffer = NULL;
    BYTE *pMem = NULL;

    size_t cbBuffer = m_bufferLength * sizeof(short);   

    // Create a new memory buffer.
    hr = MFCreateMemoryBuffer(cbBuffer, &pBuffer);

    // Lock the buffer and copy the audio frame to the buffer.
    if (SUCCEEDED(hr))
    
        hr = pBuffer->Lock(&pMem, NULL, NULL);
    

    if (SUCCEEDED(hr))
    
        CopyMemory(pMem, data, cbBuffer);
    

    if (pBuffer)
    
        pBuffer->Unlock();
    

    if (m_vFormat == VideoFormat::SWFV_NONE && m_audFormat == AudioFormat::SWAF_WAV)
    
        DWORD cbWritten = 0;

        if (SUCCEEDED(hr))
        
            hr = m_pByteStream->Write(pMem, cbBuffer, &cbWritten);
        

        if (SUCCEEDED(hr))
        
            m_cbWrittenByteStream += cbWritten;
        
    
    else
    
        // Set the data length of the buffer.
        if (SUCCEEDED(hr))
        
            hr = pBuffer->SetCurrentLength(cbBuffer);
        

        // Create media sample and add the buffer to the sample.
        if (SUCCEEDED(hr))
        
            hr = MFCreateSample(&pSample);
        

        if (SUCCEEDED(hr))
        
            hr = pSample->AddBuffer(pBuffer);
        

        // Set the timestamp and the duration.
        if (SUCCEEDED(hr))
        
            hr = pSample->SetSampleTime(m_cbRtStartVideo);
        

        if (SUCCEEDED(hr))
        
            hr = pSample->SetSampleDuration(m_cbRtDurationVideo);
        

        // Send the sample to the Sink Writer
        if (SUCCEEDED(hr))
        
            hr = m_pSinkWriter->WriteSample(m_audioStreamIndex, pSample);
        

        /*if (SUCCEEDED(hr))
        
            m_cbRtStartAudio += m_cbRtDurationAudio;
        */

        SAFE_RELEASE(pSample);
        SAFE_RELEASE(pBuffer);
    

    return hr;

【问题讨论】:

这是一个很好的问题,但缺乏细节。你在创作 MP4 吗?您没有显示 Sink Writer 设置,也没有显示相关代码。 FLAC 支持肯定缺少 MSDN 文档,因此令人困惑。 在这个例子中,我只创建音频文件。所以我认为音频部分的配置应该足够了。如果您需要更多信息,我可以更新我的问题。经过一番研究,似乎我必须将我的 32 位 PCM 转换为 24 或 16 位 PCM 才能使其工作。 32 位是奇数,请确保它没有以浮点编码。 FLAC 的最大值为 24 位。 我知道这很奇怪。不幸的是,设备以 INT32 而不是浮点数发送这个特大号 PCM 数据。 您能向我展示您的实现吗? 【参考方案1】:

因此,Microsoft 在 Windows 10 中引入了 FLAC 媒体基础转换 (MFT) 编码器CLSID_CMSFLACEncMFT,但该编解码器目前仍未记录。

Supported Media Formats in Media Foundation 同样已过时,并不反映最近添加的内容。

我不知道对此有任何评论,我的观点是编解码器是为内部使用而添加的,但实现只是一个没有许可限制的标准媒体基础组件,因此编解码器也不受限制,例如, field of use 限制。

此股票编解码器似乎仅限于 8、16 和 24 位 PCM 输入选项(也就是说,不是 32 位/样本 - 您需要分别resample)。编解码器能够接受多达 8 个通道和灵活的每秒采样率(48828 Hz 可以)。

即使编解码器(转换)似乎正在工作,如果您想生成文件,您还需要与MFAudioFormat_FLAC 兼容的合适容器格式(多路复用器)(标识符在 Google 搜索中有 7 个结果,位于发布的那一刻,这基本上意味着没有人知道编解码器)。过时的文档并未反映股票媒体接收器中对 FLAC 的实际支持。

我借用了一个自定义媒体接收器,它将原始 MFT 输出负载写入文件,并且这种 FLAC 输出是可播放的,因为 FLAC 帧包含解析比特流以进行播放的必要信息。

供参考,文件本身为:20180224-175524.flac。

股票媒体接收器WAVE Media Sink 中的一个明显候选者无法接受 FLAC 输入。尽管如此,它有可能实现,但可能仅限于更简单的音频格式。

AVI 媒体接收器可能采用 FLAC 音频,但似乎不可能创建仅音频的 AVI。

在其他媒体接收器中,还有一个可以处理 FLAC 的媒体接收器:MPEG-4 File Sink。同样,尽管文档已过时,媒体接收器仍接受 FLAC 输入,因此您应该能够创建带有 FLAC 音轨的 .MP4 文件。

示例文件:20180224-184012.mp4。 “FLAC(加框)”

总结一下:

FLAC 编码器 MFT 存在于 Windows 10 中,可供使用;虽然缺乏适当的文档 需要将输入转换为兼容格式(不直接支持 32 位 PCM) 可以直接管理MFT,消费MFT输出,得到FLAC比特流 或者,可以使用库存 MP4 媒体接收器生成带有 FLAC 音轨的输出 或者,可以开发自定义媒体接收器并使用来自上游编码器连接的 FLAC 比特流

编解码器可能与Transcode API 兼容,但上述限制适用。容器类型尤其需要为MFTranscodeContainerType_MPEG4

该编解码器显然与Media Session API 兼容,想必它也适用于Sink Writer API。

在您的代码中,当您尝试使用 Sink Writer API 时,您同样应该将 MP4 输出和输入可能转换为代码中的兼容格式(兼容 PCM 或兼容 FLAC,编码器 MFT 由您管理)。知道 MP4 媒体接收器整体能够创建 FLAC 音轨后,您应该能够调试代码中的精细细节并让组件协同工作。

【讨论】:

感谢您抽出宝贵时间撰写此说明!首先,我将尝试读取 48kHz/24Bit 波形并尝试将其转换为 flac。当这工作时,我希望我也能够从我的 PCM 流中创建 flac 文件。正如你首先所说,我必须将它从 32 位重新采样到 24 位。 让您了解最新情况。我试图用 NAudio for c# 来实现同样的效果,因为它也使用了 Media Foundation。在查看了代码和你给我的提示后,我能够让它工作。现在我尝试在我的 c++ 应用程序中实现相同的功能。完成后,我将使用此问题的最终解决方案更新我的帖子。 我添加了我的最终解决方案作为答案。感谢您的帮助@Roman R。【参考方案2】:

最后我能够解决问题。说实话并不难。但是,如果您知道如何实现某事,情况总是如此;)。

我在下面创建了一个复制和粘贴示例,以说明如何使用 Microsoft Media Foundation 实现 FLAC 编码。

拼图中缺少的部分是MFTranscodeGetAudioOutputAvailableTypes。此函数列出音频编码器的所有可用输出格式。

如果您不确定操作系统支持哪些 MFT,您可以先致电MFTEnumEx function。这为您提供了所有可用 MFT 的列表。在我使用 Windows 10 的情况下,FLAC MFT 是这样定义的。

Name: Microsoft FLAC Audio Encoder MFT
Input Types: 1 items:
    Audio-PCM
Class identifier: 128509e9-c44e-45dc-95e9-c255b8f466a6
Output Types: 1 items:
    Audio-0000f1ac-0000-0010-8000-00aa00389b71
Transform Flags: 1
Transform Category: Audio Encoder

所以我接下来要做的是创建源阅读器并获取当前的媒体类型。对我来说重要的值是采样率、比特率和通道。

然后我创建了一个GetOutputMediaTypes 函数,它需要请求的音频格式、采样率、比特率、通道和对 IMFMediaType 的引用。

MFTranscodeGetAudioOutputAvailableTypes 函数返回 MFAudioFormat_flac GUID 的所有可用类型。

使用hr = pAvailableTypes-&gt;GetElementCount(&amp;dwMTCount); 获得可用媒体类型的计数后,我可以遍历它们并检查类型是否支持我的请求。如果是这种情况,我会返回媒体类型。

最后一部分是最简单的。

首先将输出的媒体类型添加到 sinkwriter 获取流索引。

DWORD dwWriterStreamIndex = -1;

// Add the stream
hr = pSinkWriter->AddStream(
    pOuputMediaType,
    &dwWriterStreamIndex
);

然后设置输入类型并调用pSinkWriter-&gt;BeginWriting();,以便 sinkwriter 开始接受数据。

// Set input media type
hr = pSinkWriter->SetInputMediaType(
    dwWriterStreamIndex,
    pInputType,
    NULL
);

// Tell the sink writer to accept data
hr = pSinkWriter->BeginWriting();

如果输出和输入媒体类型设置正确,BeginWriting 应该返回 0 作为 HRESULT。

我们应该不会出错,因为我们使用的是函数MFTranscodeGetAudioOutputAvailableTypes 提供的媒体类型。

最后一步是从源读取器读取所有样本,并通过 sinkwriter 将其写入 flac 容器。

完成:)

我希望我能帮助回答这个问题。

还要感谢 Roman R.

更新

此示例仅适用于 4 位到 24 位的音频 PCM 格式。如果要对 32 位音频 PCM 进行编码,则必须先对其重新采样,然后再对其进行编码。

--------

这是最小的示例应用程序。

#include <windows.h>
#include <windowsx.h>

#include <atlstr.h>
#include <comdef.h>
#include <exception>

#include <mfapi.h>
#include <mfplay.h>
#include <mfreadwrite.h>
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <mferror.h>
#include <Wmcodecdsp.h>

#pragma comment(lib, "mf.lib")
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfplay.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "wmcodecdspuuid")


inline void ThrowIfFailed(HRESULT hr)

    if (FAILED(hr))
    
        // Get the error message
        _com_error err(hr);
        LPCTSTR errMsg = err.ErrorMessage();

        OutputDebugString(L"################################## ERROR ##################################\n");
        OutputDebugString(errMsg);
        OutputDebugString(L"\n################################## ----- ##################################\n");

        CStringA sb(errMsg);
        // Set a breakpoint on this line to catch DirectX API errors
        throw std::exception(sb);
    


template <class T> void SafeRelease(T **ppT)

    if (*ppT)
    
        (*ppT)->Release();
        *ppT = nullptr;
    


using namespace System;

HRESULT GetOutputMediaTypes(
    GUID cAudioFormat,
    UINT32 cSampleRate,
    UINT32 cBitPerSample,
    UINT32 cChannels,
    IMFMediaType **ppType
)

    // Enumerate all codecs except for codecs with field-of-use restrictions.
    // Sort the results.
    DWORD dwFlags =
        (MFT_ENUM_FLAG_ALL & (~MFT_ENUM_FLAG_FIELDOFUSE)) |
        MFT_ENUM_FLAG_SORTANDFILTER;

    IMFCollection   *pAvailableTypes = NULL;    // List of audio media types.
    IMFMediaType    *pAudioType = NULL;         // Corresponding codec.

    HRESULT hr = MFTranscodeGetAudioOutputAvailableTypes(
        cAudioFormat,
        dwFlags,
        NULL,
        &pAvailableTypes
    );

    // Get the element count.
    DWORD dwMTCount;
    hr = pAvailableTypes->GetElementCount(&dwMTCount);

    // Iterate through the results and check for the corresponding codec.
    for (DWORD i = 0; i < dwMTCount; i++)
    
        hr = pAvailableTypes->GetElement(i, (IUnknown**)&pAudioType);

        GUID majorType;
        hr = pAudioType->GetMajorType(&majorType);

        GUID subType;
        hr = pAudioType->GetGUID(MF_MT_SUBTYPE, &subType);

        if (majorType != MFMediaType_Audio || subType != MFAudioFormat_FLAC)
        
            continue;
        

        UINT32 sampleRate = NULL;
        hr = pAudioType->GetUINT32(
            MF_MT_AUDIO_SAMPLES_PER_SECOND,
            &sampleRate
        );

        UINT32 bitRate = NULL;
        hr = pAudioType->GetUINT32(
            MF_MT_AUDIO_BITS_PER_SAMPLE,
            &bitRate
        );

        UINT32 channels = NULL;
        hr = pAudioType->GetUINT32(
            MF_MT_AUDIO_NUM_CHANNELS,
            &channels
        );

        if (sampleRate == cSampleRate
            && bitRate == cBitPerSample
            && channels == cChannels)
        
            // Found the codec.
            // Jump out!
            break;
               
    

    // Add the media type to the caller
    *ppType = pAudioType;
    (*ppType)->AddRef();
    SafeRelease(&pAudioType);

    return hr;


int main(array<System::String ^> ^args)

    HRESULT hr = S_OK;

    // Initialize com interface
    ThrowIfFailed(
        CoInitializeEx(0, COINIT_MULTITHREADED)
    );

    // Start media foundation
    ThrowIfFailed(
        MFStartup(MF_VERSION)
    );

    IMFMediaType        *pInputType = NULL;
    IMFSourceReader     *pSourceReader = NULL;
    IMFMediaType        *pOuputMediaType = NULL;
    IMFSinkWriter       *pSinkWriter = NULL;

    // Create source reader
    hr = MFCreateSourceReaderFromURL(
        L"C:\\Temp\\48khz24bit.wav",
        NULL,
        &pSourceReader
    );

    // Create sink writer
    hr = MFCreateSinkWriterFromURL(
        L"C:\\Temp\\foo.flac",
        NULL,
        NULL,
        &pSinkWriter
    );

    // Get media type from source reader
    hr = pSourceReader->GetCurrentMediaType(
        MF_SOURCE_READER_FIRST_AUDIO_STREAM,
        &pInputType
    );

    // Get sample rate, bit rate and channels
    UINT32 sampleRate = NULL;
    hr = pInputType->GetUINT32(
        MF_MT_AUDIO_SAMPLES_PER_SECOND,
        &sampleRate
    );

    UINT32 bitRate = NULL;
    hr = pInputType->GetUINT32(
        MF_MT_AUDIO_BITS_PER_SAMPLE,
        &bitRate
    );

    UINT32 channels = NULL;
    hr = pInputType->GetUINT32(
        MF_MT_AUDIO_NUM_CHANNELS,
        &channels
    );

    // Try to find a media type that is fitting.
    hr = GetOutputMediaTypes(
        MFAudioFormat_FLAC,
        sampleRate,
        bitRate,
        channels,
        &pOuputMediaType);

    DWORD dwWriterStreamIndex = -1;

    // Add the stream
    hr = pSinkWriter->AddStream(
        pOuputMediaType,
        &dwWriterStreamIndex
    );

    // Set input media type
    hr = pSinkWriter->SetInputMediaType(
        dwWriterStreamIndex,
        pInputType,
        NULL
    );

    // Tell the sink writer to accept data
    hr = pSinkWriter->BeginWriting();

    // Forever alone loop
    for (;;)
    
        DWORD nStreamIndex, nStreamFlags;
        LONGLONG nTime;
        IMFSample *pSample;

        // Read through the samples until...
        hr = pSourceReader->ReadSample(
            MF_SOURCE_READER_FIRST_AUDIO_STREAM,
            0,
            &nStreamIndex,
            &nStreamFlags,
            &nTime,
            &pSample);

        if (pSample)
        
            OutputDebugString(L"Write sample...\n");

            hr = pSinkWriter->WriteSample(
                dwWriterStreamIndex,
                pSample
            );
        

        // ... we are at the end of the stream...
        if (nStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM)
        
            // ... and jump out.
            break;
        
    

    // Call finalize to finish writing.
    hr = pSinkWriter->Finalize();

    // Done :D
    return 0;

【讨论】:

以上是关于如何使用 Microsoft Media Foundation 将原始 48khz/32 位 PCM 编码为 FLAC?的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有拓扑的情况下将 Windows Media Foundation 与 UWP 结合使用

Microsoft Windows Server 2008 R2 中的 Microsoft Media Foundation

使用 IMFSourceReader 进行音频流式传输(Microsoft Media Foundation)

Microsoft Media Foundation 中的 h264 无损模式

microsoft azure Media Services 媒体服务解决方案

隐藏MediaPlayer控件(Microsoft Media Platform Player框架)