通过 DirectShow 播放 FLAC

问题描述

我想写一个简单的(只有一个 exe)音乐播放器。 如果我想播放 mp3,播放器可以工作,但如果我想使用 MadFLAC 过滤器,则播放器不起作用。 我无法连接 MadFLAC 引脚。始终删除 0x80040207 错误。 (我认为,MadFLAC 过滤器已正确加载,我可以毫无错误地添加到 GraphBuilder 并在 EnumFilters 中查看。)

我不坚持使用 MadFLAC。我对所有其他选项感兴趣,我可以通过 DirectShow 播放 FLAC 文件,而无需将编解码器包安装到客户端 PC。

这是我的简化代码:

#include <windows.h>
#include <strmif.h>
#include <control.h>
#include <uuids.h>
#pragma comment(lib,"strmiids.lib")

IPin* GetPin(IBaseFilter *bFilter,PIN_DIRECTION pindir)
{
    IEnumPins *EnumPin;
    bFilter->EnumPins(&EnumPin);
    unsigned long int num;
    IPin *TempPin = NULL;
    do {
        EnumPin->Next(1,&TempPin,&num);
        if (num != 0)
        {
            PIN_INFO PinInfo;
            TempPin->QueryPinInfo(&PinInfo);
            if (PinInfo.dir == pindir) break;
        }
    } while (num != 0);
    return TempPin;
}

typedef HRESULT __stdcall DLLGETCLASSOBJECT(REFCLSID rclsid,REFIID riid,void **ppv);
HRESULT CreateFilterFromFile(HINSTANCE hLibInst,GUID TGUID,void **Filter) {
    IClassFactory * ClassFactory;
    HRESULT Result = S_FALSE;
    FARPROC func;
    func = GetProcAddress(hLibInst,"DllGetClassObject");
    if (func != NULL)
    {
        IClassFactory *classFactory;
        DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)func;
        Result = dllGetClassObject(TGUID,IID_IClassFactory,(void**)&classFactory);
        if (SUCCEEDED(Result))
        {
            Result = classFactory->CreateInstance(NULL,IID_IBaseFilter,Filter);
            classFactory->Release();
        }
    }
    return Result;
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    switch (msg)
    {
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd,msg,wParam,lParam);
    }
    return 0;
}

void Player(bool isFlac) {
    IGraphBuilder * player;
    IMediaControl * mcontrol;
    CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,IID_IGraphBuilder,(void **)&player);
    player->QueryInterface(IID_IMediaControl,(void **)&mcontrol);
    IBaseFilter *ARS = NULL;
    HRESULT hr = CoCreateInstance(CLSID_AsyncReader,(void **)&ARS);
    IBaseFilter * AudioDecoderFilter = NULL;
    if (isFlac) {
        hr = player->AddSourceFilter(L"D:\\test.flac",L"Async Reader Source",&ARS);
        GUID CLSID_MadFlacAudioDecoder;
        CLSIDFromString(L"{6B257121-CBB6-46B3-ABFA-B14DFA98C4A6}",&CLSID_MadFlacAudioDecoder);
        HINSTANCE FHMPCAudioFilterInst = CoLoadLibrary(L"d:\\_MadFlac\\madFlac.ax",true);
        if (FHMPCAudioFilterInst != 0) {
            hr = CreateFilterFromFile(FHMPCAudioFilterInst,CLSID_MadFlacAudioDecoder,(void **)&AudioDecoderFilter);
            if (AudioDecoderFilter != NULL) {
                player->AddFilter(AudioDecoderFilter,L"MadFLAC Audio decoder (internal)");
            }
        }
    }
    else {
        hr = player->AddSourceFilter(L"D:\\test.mp3",&ARS);
        hr = CoCreateInstance(CLSID_MPEG1Splitter,(void **)&AudioDecoderFilter);
        hr = player->AddFilter(AudioDecoderFilter,L"MPEG1Splitter");
    }

    IBaseFilter *PCM = NULL;
    hr = CoCreateInstance(CLSID_ACMWrapper,(void **)&PCM);
    hr = player->AddFilter(PCM,L"PCM");

    IBaseFilter *DSE = NULL;
    hr = CoCreateInstance(CLSID_DSoundRender,(void **)&DSE);
    hr = player->AddFilter(DSE,L"Direct Renderer");

    //ARS -> AdioDecoder
    hr = GetPin(ARS,PINDIR_OUTPUT)->Connect(GetPin(AudioDecoderFilter,PINDIR_INPUT),NULL);
    //If I use MadFLAC filter this hresult is 0x80040207 
    if (FAILED(hr)) MessageBoxA(NULL,"There is no common media type between these pins.","Error",NULL);
    //AudioDecoderFilter -> PCM
    hr = GetPin(AudioDecoderFilter,PINDIR_OUTPUT)->Connect(GetPin(PCM,NULL);
    //PCM -> DSE
    hr = GetPin(PCM,PINDIR_OUTPUT)->Connect(GetPin(DSE,NULL);
    //ListGraph(player);
    mcontrol->Run();
}


int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "Teszt";
    wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION);

    if (!RegisterClassEx(&wc)) { MessageBox(NULL,"Window Registration Failed!","Error!",MB_ICONEXCLAMATION | MB_OK); return 0; }
    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"Teszt","AppName",WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,CW_USEDEFAULT,460,240,hInstance,NULL);
    if (hwnd == NULL) { MessageBox(NULL,"Window Creation Failed!",MB_ICONEXCLAMATION | MB_OK); return 0; }
    ShowWindow(hwnd,nCmdShow);
    UpdateWindow(hwnd);

    Player(false);

    while (GetMessage(&Msg,0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

我使用 VS2015,原生 C++。

我也会尝试使用 LibFLAC,但我已经被困在那里无法构建,因为它抛出了数百个错误,我最终减少到不到 20 个,但几个小时后我已经放弃了痛苦。

https://github.com/xiph/flac

https://ftp.osuosl.org/pub/xiph/releases/oggdsf/

解决方法

Windows 未附带用于 DirectShow 的 FLAC 解码器。因此,通过 DirectShow 播放 FLAC 音频需要第三方解码器。如果您不想回复编解码器包,则需要将某些 FLAC 解码器与 DirectShow 接口捆绑在一起,或者在代码中实现解码器。 https://xiph.org/dshow 对我来说似乎是最好的第三方解码器选项,在 FLAC 库上实现 DirectShow 过滤器需要一些开发工作,这对于相关任务来说可能是一种矫枉过正。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...