MFGetService 在第一次尝试时没有获得 IMFSimpleAudioVolume

MFGetService doesnt get IMFSimpleAudioVolume on first try

我正在使用 windows 媒体基础播放一个 mp3 文件并尝试设置音量,但是当我尝试获取 IMFSimpleAudioVolume 接口时,它需要进行看似随机的尝试。我已经尝试了 1 到 200 次。 我尝试使用 IMFAudioStreamVolume 但结果是一样的

这是main.cpp

#include <iostream>
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <winrt/base.h>

#pragma comment (lib, "Mfplat.lib")
#pragma comment (lib, "Mfuuid.lib")
#pragma comment (lib, "Mf.lib")
#pragma comment (lib, "Winmm.lib")


template <class T>
using comptr = winrt::com_ptr<T>;

void AddBranchToTopology(comptr<IMFTopology> topology, comptr<IMFMediaSource> mediaSource, comptr<IMFPresentationDescriptor> presentationDesc);

int main()
{
    MFStartup(MF_VERSION);
    HRESULT hr = S_OK;

    comptr<IMFMediaSession> mediaSession;
    hr = MFCreateMediaSession(NULL, mediaSession.put());

    comptr<IMFSourceResolver> sourceResolver;

    MFCreateSourceResolver(sourceResolver.put());

    comptr<IMFMediaSource> mediaSource;

    MF_OBJECT_TYPE objType = MF_OBJECT_INVALID;

    {
        comptr<IUnknown> unknown;
        hr = sourceResolver->CreateObjectFromURL(L"<file path>",
            MF_RESOLUTION_MEDIASOURCE, NULL, &objType, unknown.put());
        mediaSource = unknown.as<IMFMediaSource>();
        unknown.detach();
    }

    comptr<IMFPresentationDescriptor> presentationDesc;
    hr = mediaSource->CreatePresentationDescriptor(presentationDesc.put());

    comptr<IMFTopology> topology;
    hr = MFCreateTopology(topology.put());

    AddBranchToTopology(topology, mediaSource, presentationDesc);

    mediaSession->SetTopology(NULL, topology.get());


    comptr<IMFSimpleAudioVolume> simpleVolume;

    DWORD tries = 0;
    while (!simpleVolume)
    {
        hr = MFGetService(mediaSession.get(), MR_POLICY_VOLUME_SERVICE, IID_PPV_ARGS(simpleVolume.put()));
        tries++;
    }

    std::cout << tries << "\n";

    PROPVARIANT var;
    PropVariantInit(&var);

    var.vt = VT_EMPTY;

    mediaSession->Start(&GUID_NULL, &var);
    simpleVolume->SetMasterVolume(0.5f);

    while (true)
    {
        if (GetAsyncKeyState(VK_ESCAPE) & 1)
        {
            break;
        }
    }

    mediaSession->Shutdown();
    mediaSource->Shutdown();

    MFShutdown();

    return 0;
}

void AddBranchToTopology(comptr<IMFTopology> topology, comptr<IMFMediaSource> mediaSource, comptr<IMFPresentationDescriptor> presentationDesc)
{
    comptr<IMFStreamDescriptor> streamDesc;
    comptr<IMFTopologyNode> sourceNode;
    comptr <IMFTopologyNode> outputNode;
    comptr<IMFActivate> activate;
    BOOL selected = FALSE;

    HRESULT hr = S_OK;

    hr = presentationDesc->GetStreamDescriptorByIndex(0, &selected, streamDesc.put());

    hr = MFCreateAudioRendererActivate(activate.put());

    hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, sourceNode.put());

    hr = sourceNode->SetUnknown(MF_TOPONODE_SOURCE, mediaSource.get());

    hr = sourceNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc.get());

    hr = sourceNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc.get());

    hr = topology->AddNode(sourceNode.get());

    hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, outputNode.put());

    hr = outputNode->SetObject(activate.get());

    hr = outputNode->SetUINT32(MF_TOPONODE_STREAMID, 0);

    hr = outputNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);

    hr = topology->AddNode(outputNode.get());

    hr = sourceNode->ConnectOutput(0, outputNode.get(), 0);

    activate->ShutdownObject();
}

IMFMediaSession::SetTopology 是异步的,因此您需要在请求服务之前进行同步。

This method is asynchronous. If the method returns S_OK, the Media Session sends an MESessionTopologySet event when the operation completes.

    mediaSession->SetTopology(NULL, topology.get());

    // INSERT BEGIN //

    // https://docs.microsoft.com/en-us/windows/win32/medfound/mesessiontopologyset
    auto const MediaEventGenerator = mediaSession.as<IMFMediaEventGenerator>();
    for(; ; )
    {
        winrt::com_ptr<IMFMediaEvent> MediaEvent;
        winrt::check_hresult(MediaEventGenerator->GetEvent(0, MediaEvent.put()));
        MediaEventType Type;
        winrt::check_hresult(MediaEvent->GetType(&Type));
        //std::cout << Type << std::endl;
        if(Type == MESessionTopologySet)
            break;
    }

    // INSERT END //

    comptr<IMFSimpleAudioVolume> simpleVolume;

这只是为了说明,在实际代码中,您可能希望订阅事件并异步处理(参见 Media Foundation samples)。