如何使用Microsoft Media Foundation中的Sink Writer播放音频

问题描述 投票:1回答:2

我正在尝试使用Microsoft Media Foundation创建一个音频可视化工具。为此,我需要拦截样本并同时播放它们。使用具有拓扑的媒体会话和样本抓取器接收器似乎不切实际且过于复杂,因此我尝试使用Sink Reader和Sink Writer的组合(参见Overview of the Media Foundation Architecture上图像的右半部分)。不幸的是,Audio/Video Playback并没有真正解释如何做到这一点。 “开发Microsoft Media Foundation应用程序”一书包含第92页的源到接收器循环,但这仍然无法帮助我。

创建源阅读器工作正常,我正在阅读非零样本。将它们写入Sink Writer(使用Streaming Audio Renderer)并没有给我任何错误,但我没有听到任何声音。我尝试了多种选择,比如选择其他媒体类型并明确选择渲染设备(虽然我只有一个,如图所示),但无济于事。请注意,使用媒体会话播放音频工作正常!

我的代码基于这个问题:Play audio from file to speaker with Media Foundation

这是我此时的代码:

#include <iostream>

#include <cassert>

#include <mfidl.h>
#include <mfapi.h>
#include <mfreadwrite.h>
#include <Mferror.h>
#pragma comment(lib, "mf")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")

#include <winrt/base.h>
#pragma comment(lib, "windowsapp")

void winHr(const HRESULT result) { winrt::check_hresult(result); }

template<class T>
struct ComPtr : winrt::com_ptr<T>
{
    auto operator&() noexcept { return this->put(); }

    operator T*() noexcept
    {
        assert(this->get());
        return this->get();
    }
};

int main() noexcept
{
    winHr(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
    winHr(MFStartup(MF_VERSION));

    {
        ComPtr<IMFSourceReader> reader;
        winHr(MFCreateSourceReaderFromURL(
            LR"(test.wav)",
            nullptr, &reader));

        constexpr auto inStreamIndex = MF_SOURCE_READER_FIRST_AUDIO_STREAM;

        // Select only the audio stream
        winHr(reader->SetStreamSelection(MF_SOURCE_READER_ALL_STREAMS, false));
        winHr(reader->SetStreamSelection(inStreamIndex, true));

        ComPtr<IMFMediaSink> mediaSink;
        winHr(MFCreateAudioRenderer(nullptr, &mediaSink));

        ComPtr<IMFSinkWriter> writer;

        {
            ComPtr<IMFStreamSink> streamSink;
            winHr(mediaSink->GetStreamSinkByIndex(0, &streamSink));

            ComPtr<IMFMediaTypeHandler> typeHandler;
            winHr(streamSink->GetMediaTypeHandler(&typeHandler));

            ComPtr<IMFMediaType> inputType;
            winHr(reader->GetCurrentMediaType(inStreamIndex, &inputType));

            ComPtr<IMFMediaType> closestSupportedType;
            const auto result = typeHandler->IsMediaTypeSupported(inputType, &closestSupportedType);
            if (result == MF_E_INVALIDMEDIATYPE)
            {
                if (!closestSupportedType)
                {
                    std::cerr << "Media type not supported" << std::endl;
                    winHr(mediaSink->Shutdown());
                    goto end; //:o
                }
                winHr(reader->SetCurrentMediaType(inStreamIndex, nullptr, closestSupportedType));
                winHr(typeHandler->SetCurrentMediaType(closestSupportedType));
                winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &writer));
                winHr(writer->SetInputMediaType(0, closestSupportedType, nullptr));
            }
            else {
                winHr(result);
                winHr(reader->SetCurrentMediaType(inStreamIndex, nullptr, inputType));
                winHr(typeHandler->SetCurrentMediaType(inputType));
                winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &writer));
                winHr(writer->SetInputMediaType(0, inputType, nullptr));
            }
        }

        winHr(writer->BeginWriting());
        while (true)
        {
            ComPtr<IMFSample> sample;
            DWORD streamFlags;
            MFTIME timestamp;
            winHr(reader->ReadSample(inStreamIndex, 0, nullptr, &streamFlags, &timestamp, &sample));

            if (streamFlags & MF_SOURCE_READERF_ENDOFSTREAM)
            {
                winHr(writer->NotifyEndOfSegment(0));
                break;
            }
            if (streamFlags & MF_SOURCE_READERF_STREAMTICK)
                winHr(writer->SendStreamTick(0, timestamp));

            if (!sample) continue;

            winHr(sample->SetSampleTime(timestamp));
            winHr(writer->WriteSample(0, sample));
        }
        winHr(writer->Flush(0));

        std::cout << "(Press enter to stop)" << std::endl;
        std::cin.get();

        winHr(writer->Finalize());
        writer.attach(nullptr);
        winHr(mediaSink->Shutdown());
    }

end:
    winHr(MFShutdown());
    CoUninitialize();
}

需要明确的是:当我运行它时打印(Press enter to stop),我可以听到来自耳机的噪音(读取:来自电子信号的扭曲),我可以推断出短时间内音频端口被打开然后关闭,但没有播放实际音频。我怎样才能让它发挥作用?

编辑1:我刚刚修正了如果result != MF_E_INVALIDMEDIATYPE我没有设置媒体类型,但现在我经常(但不总是,出于某种原因)在MF_E_TOPO_CODEC_NOT_FOUND行获得winHr(writer->SetInputMediaType(0, inputType, nullptr));。为什么会这样? (在任何情况下都不播放音频。)

编辑2:显然,当我创建编写器时它很重要,所以现在我只在最后一刻才这样做,但现在我得到“媒体类型不支持”错误。也许我需要手动选择一些媒体类型,但我会稍后再看一下 - 除非有人知道答案。

c++ audio com playback ms-media-foundation
2个回答
0
投票

我修改了您的代码,使其工作方式与以下方式不同:1。枚举Audio Sink输出媒体类型,直到找到支持的媒体类型。 2.将此媒体类型设置为Reader以强制它使用Audio Resampler DSP(这是IMFMediaTopology所做的)。

这是代码,它正确播放输入的wav文件。请让我知道这对你有没有用。

#include <iostream>

#include <cassert>

#include <mfidl.h>
#include <mfapi.h>
#include <mfreadwrite.h>
#include <Mferror.h>
#pragma comment(lib, "mf")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")

#include <winrt/base.h>
#pragma comment(lib, "windowsapp")

void winHr(const HRESULT result) { winrt::check_hresult(result); }

template<class T>
struct ComPtr : winrt::com_ptr<T>
{
    auto operator&() noexcept { return this->put(); }

    operator T*() noexcept
    {
        assert(this->get());
        return this->get();
    }
};

int main() noexcept
{
    winHr(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
    winHr(MFStartup(MF_VERSION));

    {
        ComPtr<IMFSourceReader> reader;
        winHr(MFCreateSourceReaderFromURL(
            LR"(test.wav)",
            nullptr, &reader));

        constexpr auto inStreamIndex = MF_SOURCE_READER_FIRST_AUDIO_STREAM;

        // Select only the audio stream
        winHr(reader->SetStreamSelection(MF_SOURCE_READER_ALL_STREAMS, false));
        winHr(reader->SetStreamSelection(inStreamIndex, true));

        ComPtr<IMFMediaSink> mediaSink;
        winHr(MFCreateAudioRenderer(nullptr, &mediaSink));

        ComPtr<IMFSinkWriter> writer;

        {
            ComPtr<IMFStreamSink> streamSink;
            winHr(mediaSink->GetStreamSinkByIndex(0, &streamSink));

            ComPtr<IMFMediaTypeHandler> typeHandler;
            winHr(streamSink->GetMediaTypeHandler(&typeHandler));

            DWORD dwCount = 0;
            ComPtr<IMFMediaType> inputType;
            winHr(typeHandler->GetMediaTypeCount(&dwCount));

            for (INT i = 0; i < dwCount; i++)
            {
                inputType.attach(nullptr);
                winHr(typeHandler->GetMediaTypeByIndex(i, &inputType));
                if (SUCCEEDED(typeHandler->IsMediaTypeSupported(inputType, NULL)))
                    break;
            }

            //ComPtr<IMFMediaType> inputType;
            //winHr(reader->GetCurrentMediaType(inStreamIndex, &inputType));

            winHr(reader->SetCurrentMediaType(inStreamIndex, NULL, inputType));


            //ComPtr<IMFMediaType> closestSupportedType;
            //const auto result = typeHandler->IsMediaTypeSupported(inputType, &closestSupportedType);
            //if (result == MF_E_INVALIDMEDIATYPE)
            //{
            //  if (!closestSupportedType)
            //  {
            //      std::cerr << "Media type not supported" << std::endl;
            //      winHr(mediaSink->Shutdown());
            //      goto end; //:o
            //  }
            //  winHr(reader->SetCurrentMediaType(inStreamIndex, nullptr, closestSupportedType));
            //  winHr(typeHandler->SetCurrentMediaType(closestSupportedType));
            //  winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &writer));
            //  winHr(writer->SetInputMediaType(0, closestSupportedType, nullptr));
            //}
            //else 
            {
                //winHr(result);
                //winHr(reader->SetCurrentMediaType(inStreamIndex, nullptr, inputType));
                winHr(typeHandler->SetCurrentMediaType(inputType));
                winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &writer));
                winHr(writer->SetInputMediaType(0, inputType, nullptr));
            }
        }

        winHr(writer->BeginWriting());
        while (true)
        {
            ComPtr<IMFSample> sample;
            DWORD streamFlags;
            MFTIME timestamp;
            winHr(reader->ReadSample(inStreamIndex, 0, nullptr, &streamFlags, &timestamp, &sample));

            if (streamFlags & MF_SOURCE_READERF_ENDOFSTREAM)
            {
                winHr(writer->NotifyEndOfSegment(0));
                break;
            }
            if (streamFlags & MF_SOURCE_READERF_STREAMTICK)
                winHr(writer->SendStreamTick(0, timestamp));

            if (!sample) 
                continue;

            // SetSampleTime is redundant
            //winHr(sample->SetSampleTime(timestamp));
            winHr(writer->WriteSample(0, sample));
        }

        // Flush shouldn't be called! 
        // winHr(writer->Flush(0));

        std::cout << "(Press enter to stop)" << std::endl;
        std::cin.get();

        winHr(writer->Finalize());
        writer.attach(nullptr);
        winHr(mediaSink->Shutdown());
    }

end:
    winHr(MFShutdown());
    CoUninitialize();
}

0
投票

这段代码适用于wav文件(Win7,默认声卡):

//----------------------------------------------------------------------------------------------
// Main.cpp
//----------------------------------------------------------------------------------------------
#pragma once
#define WIN32_LEAN_AND_MEAN
#define STRICT

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

//----------------------------------------------------------------------------------------------
// Microsoft Windows SDK for Windows 7
#include <WinSDKVer.h>
#include <new>
#include <windows.h>
#include <strsafe.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mferror.h>
#include <mfreadwrite.h>

template <class T> inline void SAFE_RELEASE(T*& p){

    if(p){
        p->Release();
        p = NULL;
    }
}

#define AUDIO_FILE L"C:\\Project\\Media\\Audio\\test.wav"

HRESULT ProcessAudio(const WCHAR*);

void main(){

    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    if(SUCCEEDED(hr)){

        hr = MFStartup(MF_VERSION, MFSTARTUP_LITE);

        if(SUCCEEDED(hr)){

            hr = ProcessAudio(AUDIO_FILE);

            hr = MFShutdown();
        }

        CoUninitialize();
    }
}

HRESULT ProcessAudio(const WCHAR*){

    IMFSourceReader* pSourceReader = NULL;
    IMFMediaType* pType = NULL;
    DWORD dwMediaTypeIndex = 0;
    IMFMediaSink* pAudioSink = NULL;
    IMFStreamSink* pStreamSink = NULL;
    IMFMediaTypeHandler* pMediaTypeHandler = NULL;
    IMFSinkWriter* pSinkWriter = NULL;

    HRESULT hr = MFCreateSourceReaderFromURL(AUDIO_FILE, NULL, &pSourceReader);

    // Check native media type
    if(SUCCEEDED(hr))
        hr = pSourceReader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, dwMediaTypeIndex, &pType);

    SAFE_RELEASE(pType);

    // Get current media type
    if(SUCCEEDED(hr))
        hr = pSourceReader->GetCurrentMediaType(dwMediaTypeIndex, &pType);

    if(SUCCEEDED(hr))
        hr = MFCreateAudioRenderer(NULL, &pAudioSink);

    if(SUCCEEDED(hr))
        hr = pAudioSink->GetStreamSinkByIndex(0, &pStreamSink);

    if(SUCCEEDED(hr))
        hr = pStreamSink->GetMediaTypeHandler(&pMediaTypeHandler);

    if(FAILED(hr = pMediaTypeHandler->IsMediaTypeSupported(pType, NULL))){

        SAFE_RELEASE(pType);

        // This is a compatible type with my soundcard
        // MF_MT_MAJOR_TYPE                     MFMediaType_Audio
        // MF_MT_SUBTYPE                        MFAudioFormat_PCM
        // MF_MT_AUDIO_NUM_CHANNELS             2
        // MF_MT_AUDIO_SAMPLES_PER_SECOND       48000
        // MF_MT_AUDIO_BLOCK_ALIGNMENT          4
        // MF_MT_AUDIO_AVG_BYTES_PER_SECOND     192000
        // MF_MT_AUDIO_BITS_PER_SAMPLE          16
        // MF_MT_ALL_SAMPLES_INDEPENDENT        1
        // MF_MT_AUDIO_PREFER_WAVEFORMATEX      1

        hr = MFCreateMediaType(&pType);

        if(SUCCEEDED(hr))
            hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);

        if(SUCCEEDED(hr))
            hr = pType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);

        if(SUCCEEDED(hr))
            hr = pType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, 2);

        if(SUCCEEDED(hr))
            hr = pType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000);

        if(SUCCEEDED(hr))
            hr = pType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4);

        if(SUCCEEDED(hr))
            hr = pType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 192000);

        if(SUCCEEDED(hr))
            hr = pType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16);

        if(SUCCEEDED(hr))
            hr = pType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);

        if(SUCCEEDED(hr))
            hr = pType->SetUINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, TRUE);
    }

    if(SUCCEEDED(hr))
        hr = pMediaTypeHandler->SetCurrentMediaType(pType);

    if(SUCCEEDED(hr))
        hr = MFCreateSinkWriterFromMediaSink(pAudioSink, NULL, &pSinkWriter);

    if(SUCCEEDED(hr))
        hr = pSinkWriter->BeginWriting();

    BOOL bProcess = (hr == S_OK ? TRUE : FALSE);
    DWORD streamIndex;
    DWORD flags;
    LONGLONG llTimeStamp;
    IMFSample* pSample = NULL;

    while(bProcess){

        hr = pSourceReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &streamIndex, &flags, &llTimeStamp, &pSample);

        if(SUCCEEDED(hr) && (flags == 0)){

            if(pSample){

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

                SAFE_RELEASE(pSample);
            }
        }
        else{

            bProcess = FALSE;
        }
    }

    if(pSinkWriter)
        pSinkWriter->Finalize();

    if(pAudioSink)
        pAudioSink->Shutdown();

    SAFE_RELEASE(pSample);
    SAFE_RELEASE(pSinkWriter);
    SAFE_RELEASE(pMediaTypeHandler);
    SAFE_RELEASE(pStreamSink);
    SAFE_RELEASE(pAudioSink);
    SAFE_RELEASE(pType);
    SAFE_RELEASE(pSourceReader);

    return hr;
}

如果您遇到MF_E_TOPO_CODEC_NOT_FOUND问题,请共享您的文件。在这种情况下可能需要额外的代码。

编辑

  • 你有多个声卡吗?
  • 分享您的音频文件,以便我们可以看到正在发生的事情。

我对Sleep(40)的小小修理,就是在这里告诉你,在没有停顿的情况下做“while(true)”没有任何感觉(你的处理器工作太多了)。当然,应该更好地决定40。 40是Windows OS上处理器抢占的最小处理时间。我选择这个示例代码。我们可以谈谈选择这个价值的最佳方式。我在等你的建议。

© www.soinside.com 2019 - 2024. All rights reserved.