使用 Media Foundation 将音频从文件播放到扬声器
Posted
技术标签:
【中文标题】使用 Media Foundation 将音频从文件播放到扬声器【英文标题】:Play audio from file to speaker with Media Foundation 【发布时间】:2015-01-30 11:59:15 【问题描述】:我正在尝试将 mp4 文件中的音轨播放到我的扬声器。我知道 Media Foundation 能够解码音频流,因为我可以使用 TopoEdit 工具播放它。
在下面的示例代码中,我没有使用媒体会话或拓扑。我正在尝试手动将媒体源连接到接收器写入器。我想这样做的原因是我最终打算从网络而不是从文件中获取源样本。
运行下面的示例时,我在 pSinkWriter->WriteSample 行上遇到的错误是 MF_E_INVALIDREQUEST (0xC00D36B2)。所以我怀疑有些东西我没有正确接线。
#include <stdio.h>
#include <tchar.h>
#include <mfapi.h>
#include <mfplay.h>
#include <mfreadwrite.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")
#define CHECK_HR(hr, msg) if (hr != S_OK) printf(msg); printf("Error: %.2X.\n", hr); goto done;
int _tmain(int argc, _TCHAR* argv[])
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
MFStartup(MF_VERSION);
IMFSourceResolver *pSourceResolver = NULL;
IUnknown* uSource = NULL;
IMFMediaSource *mediaFileSource = NULL;
IMFSourceReader *pSourceReader = NULL;
IMFMediaType *pAudioOutType = NULL;
IMFMediaType *pFileAudioMediaType = NULL;
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
IMFMediaSink *pAudiosink = NULL;
IMFStreamSink *pStreamSink = NULL;
IMFMediaTypeHandler *pMediaTypeHandler = NULL;
IMFMediaType *pMediaType = NULL;
IMFMediaType *pSinkMediaType = NULL;
IMFSinkWriter *pSinkWriter = NULL;
// Set up the reader for the file.
CHECK_HR(MFCreateSourceResolver(&pSourceResolver), "MFCreateSourceResolver failed.\n");
CHECK_HR(pSourceResolver->CreateObjectFromURL(
L"big_buck_bunny.mp4", // URL of the source.
MF_RESOLUTION_MEDIASOURCE, // Create a source object.
NULL, // Optional property store.
&ObjectType, // Receives the created object type.
&uSource // Receives a pointer to the media source.
), "Failed to create media source resolver for file.\n");
CHECK_HR(uSource->QueryInterface(IID_PPV_ARGS(&mediaFileSource)),
"Failed to create media file source.\n");
CHECK_HR(MFCreateSourceReaderFromMediaSource(mediaFileSource, NULL, &pSourceReader),
"Error creating media source reader.\n");
CHECK_HR(pSourceReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, &pFileAudioMediaType),
"Error retrieving current media type from first audio stream.\n");
// printf("File Media Type:\n");
// Dump pFileAudioMediaType.
// Set the audio output type on the source reader.
CHECK_HR(MFCreateMediaType(&pAudioOutType), "Failed to create audio output media type.\n");
CHECK_HR(pAudioOutType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), "Failed to set audio output media major type.\n");
CHECK_HR(pAudioOutType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float), "Failed to set audio output audio sub type (Float).\n");
CHECK_HR(pSourceReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, pAudioOutType),
"Error setting reader audio output type.\n");
// printf("Source Reader Output Type:");
// Dump pAudioOutType.
CHECK_HR(MFCreateAudioRenderer(NULL, &pAudioSink), "Failed to create audio sink.\n");
CHECK_HR(pAudioSink->GetStreamSinkByIndex(0, &pStreamSink), "Failed to get audio renderer stream by index.\n");
CHECK_HR(pStreamSink->GetMediaTypeHandler(&pMediaTypeHandler), "Failed to get media type handler.\n");
// My speaker has 3 audio types of which I got the furthesr with the third one.
CHECK_HR(pMediaTypeHandler->GetMediaTypeByIndex(2, &pSinkMediaType), "Failed to get sink media type.\n");
CHECK_HR(pMediaTypeHandler->SetCurrentMediaType(pSinkMediaType), "Failed to set current media type.\n");
// printf("Sink Media Type:\n");
// Dump pSinkMediaType.
CHECK_HR(MFCreateSinkWriterFromMediaSink(pAudioSink, NULL, &pSinkWriter), "Failed to create sink writer from audio sink.\n");
printf("Read audio samples from file and write to speaker.\n");
IMFSample *audioSample = NULL;
DWORD streamIndex, flags;
LONGLONG llAudioTimeStamp;
for (int index = 0; index < 10; index++)
//while (true)
// Initial read results in a null pSample??
CHECK_HR(pSourceReader->ReadSample(
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
0, // Flags.
&streamIndex, // Receives the actual stream index.
&flags, // Receives status flags.
&llAudioTimeStamp, // Receives the time stamp.
&audioSample // Receives the sample or NULL.
), "Error reading audio sample.");
if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
printf("End of stream.\n");
break;
if (flags & MF_SOURCE_READERF_STREAMTICK)
printf("Stream tick.\n");
pSinkWriter->SendStreamTick(0, llAudioTimeStamp);
if (!audioSample)
printf("Null audio sample.\n");
else
CHECK_HR(audioSample->SetSampleTime(llAudioTimeStamp), "Error setting the audio sample time.\n");
CHECK_HR(pSinkWriter->WriteSample(0, audioSample), "The stream sink writer was not happy with the sample.\n");
done:
printf("finished.\n");
getchar();
return 0;
为了简洁起见,我省略了转储媒体类型的代码,但它们的输出如下所示。很可能是我没有正确连接媒体类型。
File Media Type:
Audio: MAJOR_TYPE=Audio, PREFER_WAVEFORMATEX=1, BFBABE79-7434-4D1C-94F0-72A3B9E17188=0, 7632F0E6-9538-4D61-ACDA-EA29C8C14456=0, SUBTYPE=00001610-0000-0010-8000-00AA00389B71, NUM_CHANNELS=2, SAMPLES_PER_SECOND=22050, BLOCK_ALIGNMENT=1, AVG_BYTES_PER_SECOND=8000, BITS_PER_SAMPLE=16, USER_DATA=<BLOB>, 73D1072D-1870-4174-A063-29FF4FF6C11E=05589F81-C356-11CE-BF01-00AA0055595A, ALL_SAMPLES_INDEPENDENT=1, FIXED_SIZE_SAMPLES=1, SAMPLE_SIZE=1, MPEG4_SAMPLE_DESCRIPTION=<BLOB>, MPEG4_CURRENT_SAMPLE_ENTRY=0, AVG_BITRATE=64000,
Source Reader Output Type:
Audio: MAJOR_TYPE=Audio, SUBTYPE=Float,
Sink Media Type:
Audio: MAJOR_TYPE=Audio, SUBTYPE=Float, NUM_CHANNELS=2, SAMPLES_PER_SECOND=48000, BLOCK_ALIGNMENT=8, AVG_BYTES_PER_SECOND=384000, BITS_PER_SAMPLE=32, ALL_SAMPLES_INDEPENDENT=1, CHANNEL_MASK=3,
任何关于我下一步可以看哪里的提示将不胜感激。
【问题讨论】:
【参考方案1】:我错过了接收器编写器 BeginWriting 调用。
CHECK_HR(pSinkWriter->BeginWriting(), "Sink writer begin writing call failed.\n");
从扬声器中发出了一些音频,但它的效果非常糟糕,所以我还缺少其他一些东西。
【讨论】:
【参考方案2】:您为阅读器设置的媒体类型不完整。 MF_MT_AUDIO_AVG_BYTES_PER_SECOND MF_MT_AUDIO_BLOCK_ALIGNMENT 和 MF_MT_AUDIO_NUM_CHANNELS 不见了。
我会设置您从接收器媒体类型处理程序中检索到的媒体类型。
【讨论】:
感谢您的建议。尝试了这些东西,但无济于事。 mediatypehandler 的输出类型不适合在阅读器上设置。如果引起您的兴趣,请在此处获取完整示例github.com/sipsorcery/mediafoundationsamples/tree/master/…。以上是关于使用 Media Foundation 将音频从文件播放到扬声器的主要内容,如果未能解决你的问题,请参考以下文章
在 Windows Media Foundation 中使用 Sink Writer 添加到视频的音频示例
Windows Media Foundation 枚举音频设备
使用 IMFSourceReader 进行音频流式传输(Microsoft Media Foundation)
如何使用 Media Foundation 从 IMFSourceReader 播放音频缓冲区
如何使用 Microsoft Media Foundation 将原始 48khz/32 位 PCM 编码为 FLAC?