如何使用 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->GetElementCount(&dwMTCount);
获得可用媒体类型的计数后,我可以遍历它们并检查类型是否支持我的请求。如果是这种情况,我会返回媒体类型。
最后一部分是最简单的。
首先将输出的媒体类型添加到 sinkwriter 获取流索引。
DWORD dwWriterStreamIndex = -1;
// Add the stream
hr = pSinkWriter->AddStream(
pOuputMediaType,
&dwWriterStreamIndex
);
然后设置输入类型并调用pSinkWriter->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 无损模式