我有一个使用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。但是我不知道记录的方式和位置。
答案 0 :(得分:1)
我尝试过上述解决方案但成功率较低。所以我把我放在这里。它在win10上也能正常工作(同样的方法 - 未记录的API):
首先添加到.pro文件中:
QT += axcontainer
然后添加以下修改后的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
};
添加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
实现文件(将其命名为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;
}
}
枚举用法示例:
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() );
}
设置当前默认设备示例:
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);
}
}
但由于this和this,我无法在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);
}
}