Qt5 +如何为QMediaPlayer设置默认音频设备

时间:2015-02-09 11:23:35

标签: c++ qt

我有一个使用Qt的程序,我需要使用用户在首选项中选择的输出设备来播放声音。我可以通过调用以下代码列出Windows中所有可用的设备:

QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
foreach (QAudioDeviceInfo i, devices)
    this->ui->comboBox->addItem(i.deviceName());

但是我不知道如何更改将作为我的应用程序的默认设备的设备,以便QMediaPlayer将使用该设备播放所有声音,而不是默认设备。我怎样才能做到这一点?我很好用Windows只有Qt5 +特定的解决方案,虽然跨平台解决方案可能是最好的。

基本上我想实现类似于Microsoft Lync中的首选项对话框:

enter image description here

根据微软的说法:https://social.technet.microsoft.com/Forums/windows/en-US/b1d1acac-1f21-4d23-8d68-98964d67c2c7/assigning-an-application-to-different-sound-outputs Windows 7引入了可以做到这一点的API。但是我不知道记录的方式和位置。

2 个答案:

答案 0 :(得分:1)

我尝试过上述解决方案但成功率较低。所以我把我放在这里。它在win10上也能正常工作(同样的方法 - 未记录的API):

  1. 首先添加到.pro文件中:

    QT += axcontainer
    
  2. 然后添加以下修改后的QT标题(命名为:PolicyConfig.h)

    // ----------------------------------------------------------------------------
    // PolicyConfig.h
    // Undocumented COM-interface IPolicyConfig.
    // Use for set default audio render endpoint
    // @author EreTIk
    // ----------------------------------------------------------------------------
    
    
    #pragma once
    
    #include "winnt.h"
    //#include <QAxAggregated>
    
    class DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8") IPolicyConfig;
    class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient;
    // ----------------------------------------------------------------------------
    // class CPolicyConfigClient
    // {870af99c-171d-4f9e-af0d-e63df40c2bc9}
    //  
    // interface IPolicyConfig
    // {f8679f50-850a-41cf-9c72-430f290290c8}
    //
    // Query interface:
    // CComPtr<IPolicyConfig> PolicyConfig;
    // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient));
    // 
    // @compatible: Windows 7 and Later
    // ----------------------------------------------------------------------------
    class IPolicyConfig : public IUnknown
    {
    public:
    
        virtual HRESULT GetMixFormat(
            PCWSTR,
            WAVEFORMATEX **
        );
    
        virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
            PCWSTR,
            INT,
            WAVEFORMATEX **
        );
    
        virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat(
            PCWSTR
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
            PCWSTR,
            WAVEFORMATEX *,
            WAVEFORMATEX *
        );
    
        virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
            PCWSTR,
            INT,
            PINT64,
            PINT64
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
            PCWSTR,
            PINT64
        );
    
        virtual HRESULT STDMETHODCALLTYPE GetShareMode(
            PCWSTR,
            struct DeviceShareMode *
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetShareMode(
            PCWSTR,
            struct DeviceShareMode *
        );
    
        virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
            PCWSTR,
            const PROPERTYKEY &,
            PROPVARIANT *
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
            PCWSTR,
            const PROPERTYKEY &,
            PROPVARIANT *
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
            __in PCWSTR wszDeviceId,
            __in ERole eRole 
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
            PCWSTR,
            INT
        );
    };
    
    class DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") IPolicyConfigVista;
    class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") CPolicyConfigVistaClient;
    // ----------------------------------------------------------------------------
    // class CPolicyConfigVistaClient
    // {294935CE-F637-4E7C-A41B-AB255460B862}
    //  
    // interface IPolicyConfigVista
    // {568b9108-44bf-40b4-9006-86afe5b5a620}
    //
    // Query interface:
    // CComPtr<IPolicyConfigVista> PolicyConfig;
    // PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient));
    // 
    // @compatible: Windows Vista and Later
    // ----------------------------------------------------------------------------
    class IPolicyConfigVista : public IUnknown
    {
    public:
    
        virtual HRESULT GetMixFormat(
            PCWSTR,
            WAVEFORMATEX **
        );  // not available on Windows 7, use method from IPolicyConfig
    
        virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
            PCWSTR,
            INT,
            WAVEFORMATEX **
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
            PCWSTR,
            WAVEFORMATEX *,
            WAVEFORMATEX *
        );
    
        virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
            PCWSTR,
            INT,
            PINT64,
            PINT64
        );  // not available on Windows 7, use method from IPolicyConfig
    
        virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
            PCWSTR,
            PINT64
        );  // not available on Windows 7, use method from IPolicyConfig
    
        virtual HRESULT STDMETHODCALLTYPE GetShareMode(
            PCWSTR,
            struct DeviceShareMode *
        );  // not available on Windows 7, use method from IPolicyConfig
    
        virtual HRESULT STDMETHODCALLTYPE SetShareMode(
            PCWSTR,
            struct DeviceShareMode *
        );  // not available on Windows 7, use method from IPolicyConfig
    
        virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
            PCWSTR,
            const PROPERTYKEY &,
            PROPVARIANT *
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
            PCWSTR,
            const PROPERTYKEY &,
            PROPVARIANT *
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
            __in PCWSTR wszDeviceId,
            __in ERole eRole 
        );
    
        virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
            PCWSTR,
            INT
        );  // not available on Windows 7, use method from IPolicyConfig
    };
    
  3. 添加Qt界面DefaultOutput

    #ifndef DEFAULTOUTPUT_H
    #define DEFAULTOUTPUT_H
    
    #include <QString>
    #include <QHash>
    
    class DefaultOutput
    {
    public:
        static QHash<QString, QString>  enumOutputDevices();
        static bool                     changeOutputDevice( QString id );
    };
    
    #endif
    
  4. 实现文件(将其命名为DefaultOutput.cpp):

    #include <stdio.h>
    #include <wchar.h>
    #include <tchar.h>
    #include "windows.h"
    #include "Mmdeviceapi.h"
    #include "PolicyConfig.h"
    #include "Propidl.h"
    #include "Functiondiscoverykeys_devpkey.h"
    #include "defaultoutput.h"
    
    QHash<QString, QString> DefaultOutput::enumOutputDevices()
    {
        QHash<QString, QString> list;
    
        try
        {
            HRESULT hr = CoInitialize( NULL );
            if( FAILED( hr ) )
                return list;
            IMMDeviceEnumerator *pEnum = NULL;
            // Create a multimedia device enumerator.
            hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ), (void**)&pEnum );
            if( FAILED( hr ) )
                return list;
            IMMDeviceCollection *pDevices;
            // Enumerate the output devices.
            hr = pEnum->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &pDevices );
            if( FAILED( hr ) )
                return list;
            UINT count;
            pDevices->GetCount(&count);
            if( FAILED( hr ) )
                return list;
            for( UINT i = 0; i < count; i++ )
            {
                IMMDevice *pDevice;
                hr = pDevices->Item( i, &pDevice );
                if( SUCCEEDED( hr ) )
                {
                    LPWSTR wstrID = NULL;
                    hr = pDevice->GetId( &wstrID );
                    if( SUCCEEDED( hr ) )
                    {
                        IPropertyStore* pStore;
                        hr = pDevice->OpenPropertyStore( STGM_READ, &pStore );
                        if( SUCCEEDED( hr ) )
                        {
                            PROPVARIANT friendlyName;
                            PropVariantInit( &friendlyName );
                            hr = pStore->GetValue( PKEY_Device_FriendlyName, &friendlyName );
                            if( SUCCEEDED( hr ) )
                            {
                                QString qid = QString::fromStdU16String( (char16_t*)wstrID );
                                QString qname = QString::fromStdU16String( (char16_t*)friendlyName.pwszVal );
                                list[qid] = qname;
                                PropVariantClear( &friendlyName );
                            }
    
                            pStore->Release();
                        }
                    }
    
                    pDevice->Release();
                }
            }
    
            pDevices->Release();
            pEnum->Release();
            return list;
        }
        catch( ... )
        {
            return list;
        }
    }
    
    bool DefaultOutput::changeOutputDevice( QString id )
    {
        try
        {
            IPolicyConfigVista* pPolicyConfig;
            ERole reserved = eConsole;
            HRESULT hr = CoCreateInstance( __uuidof( CPolicyConfigVistaClient ), NULL, CLSCTX_ALL, __uuidof( IPolicyConfigVista ), (LPVOID*)&pPolicyConfig );
            if( SUCCEEDED( hr ) )
            {
                hr = pPolicyConfig->SetDefaultEndpoint( (PCWSTR)id.toStdU16String().data(), reserved );
                pPolicyConfig->Release();
                return SUCCEEDED( hr );
            }
            else
                return false;
        }
        catch( ... )
        {
            return false;
        }
    }
    
  5. 枚举用法示例:

    QHash<QString, QString> devices = DefaultOutput::enumOutputDevices();
    for( QHash<QString, QString>::iterator iter = devices.begin(); iter != devices.end(); iter++ )
    {
        ui->devicesComboBox->addItem( iter.value(), iter.key() );
    }
    
  6. 设置当前默认设备示例:

    int index = ui->devicesComboBox->currentIndex();
    QString deviceId = ui->devicesComboBox->itemData( index ).toString();
    DefaultOutput::changeOutputDevice( deviceId );
    

答案 1 :(得分:0)

看起来你应该做这样的事情(玩家是QMediaPlayer *):

QMediaService *svc = player->service();
if (svc != nullptr)
{
    QAudioOutputSelectorControl *out = qobject_cast<QAudioOutputSelectorControl *>
                                       (svc->requestControl(QAudioOutputSelectorControl_iid));
    if (out != nullptr)
    {
        out->setActiveOutput(this->ui->comboBox->currentText());
        svc->releaseControl(out);
    }
}

但由于thisthis,我无法在Win7安装中测试此内容。

<强>更新

嗯,这是我做的修复(使用Desktop_Qt_5_4_0_MSVC2013_32 / 64-Debug / Release测试;您可能需要更改不同工具链的vtable代码):

如何获取具有“友好名称”的设备列表(键 - 名称;值 - deviceID):

auto outputs = MFAudioEndpointControl_Fixed::availableOutputsFriendly();
for (auto it = outputs.cbegin(), e = outputs.cend(); it != e; ++it)
{
    this->ui->comboBox->addItem(it.key(), it.value());
    this->ui->plainTextEdit->appendPlainText(it.key() + " (" + it.value() + ")");
}

您必须为您创建的每个QMediaPlayer应用以下修复:

QMediaPlayer *player = new QMediaPlayer(this);

QMediaService *svc = player->service();
if (svc != nullptr)
{
    QAudioOutputSelectorControl *out = reinterpret_cast<QAudioOutputSelectorControl*>
                                       (svc->requestControl(QAudioOutputSelectorControl_iid));
    if (out != nullptr)
    {
        new MFAudioEndpointControl_Fixed_Helper(out); // <- the fix; notice that it's a HELPER class

        out->setActiveOutput(this->ui->comboBox->itemData(this->ui->comboBox->currentIndex()).toString()); // we have to pass deviceID, not the name
        svc->releaseControl(out);
    }
}