Как воспроизводить звук с помощью Sink Writer от Microsoft Media Foundation - PullRequest
0 голосов
/ 28 февраля 2019

Я пытаюсь создать аудио-визуализатор, используя Microsoft Media Foundation.Для этого мне нужно перехватить сэмплы и одновременно их воспроизвести.Использование мультимедийного сеанса с топологией и приемником захвата образцов кажется непрактичным и чрезмерно сложным, поэтому я пытаюсь использовать для этого комбинацию Sink Reader и Sink Writer (см. Правую половину изображения на ОбзорМедиа Фонд Архитектура ).К сожалению, Воспроизведение аудио / видео не совсем объясняет, как это сделать.Книга Разработка приложений Microsoft Media Foundation содержит цикл от источника до приемника на стр. 92, но это все равно мне не очень помогает.

Создание Source Reader работает хорошо, и ячтение ненулевых образцов.Запись их в Sink Writer (который использует Streaming Audio Renderer) не дает мне никаких ошибок, но я ничего не слышу.Я пробовал несколько вещей, таких как выбор других типов мультимедиа и явный выбор устройства рендеринга (хотя у меня есть только один, как указано), но безрезультатно.Обратите внимание, что воспроизведение аудио с использованием Media Session работает нормально!

Я основал свой код на этом вопросе: Воспроизведение аудио из файла на динамик с помощью 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: Очевидно, это имеет значение, когда я создаю писателя, так что теперь я делаю это только в последний момент, но теперь я получаю "Mediaтип не поддерживается "ошибка.Может быть, мне нужно вручную выбрать какой-либо тип носителя, но я рассмотрю это позже - если кто-то не знает ответа.

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

Я изменил ваш код, чтобы он по-другому работал следующим образом: 1. Перечисляет типы выходных аудиосигналов 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 голосов
/ 01 марта 2019

Этот код прекрасно работает для меня с файлами 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 (правда) "без пауз нет смысла (ваш процессор работает слишком много).Конечно, 40 должно быть лучше решено.40 - минимальное время обработки выгрузки процессора в ОС Windows.Это мой выбор для этого примера кода.Мы можем поговорить о лучшем способе выбрать это значение.Я жду вашего предложения.

...