【问题标题】:How do I encode raw 48khz/32bits PCM to FLAC using Microsoft Media Foundation?如何使用 Microsoft Media Foundation 将原始 48khz/32 位 PCM 编码为 FLAC?
【发布时间】: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 数据。
  • 您能向我展示您的实现吗?

标签: c++ audio ms-media-foundation flac


【解决方案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;
}

【讨论】:

    猜你喜欢
    • 2013-06-12
    • 1970-01-01
    • 2012-04-18
    • 2020-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-29
    • 2012-07-04
    相关资源
    最近更新 更多