【问题标题】:Qt5+ How to set default audio device for QMediaPlayerQt5+ 如何为 QMediaPlayer 设置默认音频设备
【发布时间】:2017-03-20 16:38:18
【问题描述】:

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

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

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

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

据微软称:https://social.technet.microsoft.com/Forums/windows/en-US/b1d1acac-1f21-4d23-8d68-98964d67c2c7/assigning-an-application-to-different-sound-outputs windows 7 引入了可以做到这一点的 API。但是我不知道它是如何以及在哪里记录的。

【问题讨论】:

  • @MikeMinaev 我已经读过,它没有帮助,因为它没有说明程序员如何更改默认设备的信息。我只能用声子在 Qt4 中找到如何做到这一点,但没有关于 Qt5 的信息
  • 根据stackoverflow.com/questions/2175318/…我认为你不能改变它,我认为你不能改变它是系统设置,但是有一些魔法可以做到。

标签: c++ qt


【解决方案1】:

看来你应该做这样的事情(其中播放器是 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 代码):

如何获取设备列表及其“友好名称”(键 - 名称;值 - 设备 ID):

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);
    }
}

【讨论】:

  • 是的,我在最后一个稳定的 Qt (5.4) 中也遇到了这些错误:/
  • @Petr 更新了答案。现在包括一个修复程序。
【解决方案2】:

我尝试了上述解决方案,但不太成功。所以我把我的放在这里。它在 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 );
    

【讨论】:

  • 你用的是哪个编译器?
猜你喜欢
  • 2011-10-18
  • 2019-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多