不想写前言的,就是想记录一下平常在做和操作系统相关的测试时用的demo,整理一下发出来,具体使用场景根据需求具体对待。
该博客只是记录了在windows下如何监听系统扬声器和麦克风静音、音量的事件,过会再整理一下mac os的。因为需求不同,只用作借鉴。
直接上代码
|版本声明:山河君,未经博主允许,禁止转载
#ifndef AUDIO_DEVICE_MOITOR_WIN_
#define AUDIO_DEVICE_MOITOR_WIN_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#pragma comment (lib, "setupapi.lib")
#pragma comment(lib, "avrt.lib")
#define MIC_SYS_MUTE_CHANGE_EVENTS 0x1001
#define MIC_SYS_VOLUME_CHANGE_EVENTS 0x1002
#define SPEAKER_SYS_MUTE_CHANGE_EVENTS 0x2001
#define SPEAKER_SYS_VOLUME_CHANGE_EVENTS 0x2002
namespace webrtc {
class AudioDeviceMonitorBase
{
public:
typedef std::function<void(int, int)> DevEventFun;
virtual ~AudioDeviceMonitorBase() {};
public:
virtual void SetDeviceEventFuntion(DevEventFun pDevEventFun) = 0;
virtual bool CreateMicrophoneMonitor(int nIndex) = 0;
virtual bool CreateSpeakerMonitor(int Index) = 0;
virtual bool RemoveMoitorMicrophone() = 0;
virtual bool RemoveMoitorSpeaker() = 0;
};
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) \
if ((p)) { \
(p)->Release(); \
(p) = NULL; \
}
#endif
class AudioDeviceMoitor : public AudioDeviceMonitorBase, public IAudioEndpointVolumeCallback
{
public:
static AudioDeviceMoitor* GetInstance();
virtual ~AudioDeviceMoitor();
public:
virtual void SetDeviceEventFuntion(DevEventFun pDevEventFun) override;
virtual bool CreateMicrophoneMonitor(int nIndex) override;
virtual bool CreateSpeakerMonitor(int nIndex) override;
virtual bool RemoveMoitorMicrophone() override;
virtual bool RemoveMoitorSpeaker() override;
public:
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface) override;
virtual ULONG STDMETHODCALLTYPE AddRef(void) override;
virtual ULONG STDMETHODCALLTYPE Release(void) override;
virtual HRESULT STDMETHODCALLTYPE OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify) override;
private:
std::string UTF8Encode(const std::wstring &strInput);
void SendEvent(int event, int value);
LPWSTR GetGuid(int index, EDataFlow eDataFlow);
private:
AudioDeviceMoitor();
static AudioDeviceMoitor* m_pInstance;
private:
bool m_bMicMuteStatus = false;
bool m_bSpeakerMuteStatus = false;
uint16_t m_uMicrophoneIndex = 0;
uint16_t m_uSpeakerIndex = 0;
uint32_t m_uMicVolume = 0;
uint32_t m_uSpeakerVolume = 0;
ULONG m_uRef;
IMMDeviceEnumerator* m_pDeviceEnum = nullptr;
IMMDevice* m_pMicrophoneMMDev = nullptr;
IMMDevice* m_pSpeakerMMDev = nullptr;
IAudioEndpointVolume* m_pMicEndpointVolume = nullptr;
IAudioEndpointVolume* m_pSpeakerEndpointVolume = nullptr;
DevEventFun m_pDevEventFun = nullptr;
std::mutex m_DevEventMutex;
};
} //namespace webrtc
#endif
#include "audio_device_moitor_win.h"
#include
namespace webrtc
{
AudioDeviceMoitor* AudioDeviceMoitor::m_pInstance = nullptr;
AudioDeviceMoitor* AudioDeviceMoitor::GetInstance()
{
if (m_pInstance == nullptr)
{
m_pInstance = new AudioDeviceMoitor();
}
return m_pInstance;
}
AudioDeviceMoitor::AudioDeviceMoitor()
{
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator), (LPVOID *)&m_pDeviceEnum);
if (FAILED(hr) || nullptr == m_pDeviceEnum)
{
assert(NULL != m_pDeviceEnum);
}
m_uRef = 1;
}
AudioDeviceMoitor::~AudioDeviceMoitor()
{
RemoveMoitorMicrophone();
CoUninitialize();
m_pInstance = nullptr;
m_pDeviceEnum = nullptr;
}
void AudioDeviceMoitor::SetDeviceEventFuntion(DevEventFun pDevEventFun)
{
std::lock_guard<std::mutex> lockGuard(m_DevEventMutex);
m_pDevEventFun = pDevEventFun;
}
bool AudioDeviceMoitor::CreateMicrophoneMonitor(int nIndex)
{
RemoveMoitorMicrophone();
LPWSTR wpGuid = GetGuid(nIndex, EDataFlow::eCapture);
if (wpGuid == nullptr)
{
SAFE_RELEASE(m_pMicrophoneMMDev);
return false;
}
int32_t nRet = 0;
SAFE_RELEASE(m_pMicEndpointVolume);
nRet = m_pMicrophoneMMDev->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
reinterpret_cast<void**>(&m_pMicEndpointVolume));
if (nRet != 0 || m_pMicEndpointVolume == NULL)
{
return false;
}
m_pMicEndpointVolume->RegisterControlChangeNotify((IAudioEndpointVolumeCallback*)this);
return true;
}
bool AudioDeviceMoitor::CreateSpeakerMonitor(int nIndex)
{
RemoveMoitorSpeaker();
LPWSTR wpGuid = GetGuid(nIndex, EDataFlow::eRender);
if (wpGuid == nullptr)
{
SAFE_RELEASE(m_pSpeakerMMDev);
return false;
}
HRESULT hr = m_pSpeakerMMDev->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&m_pSpeakerEndpointVolume);
if (FAILED(hr) || nullptr == m_pSpeakerEndpointVolume)
{
return false;
}
m_pSpeakerEndpointVolume->RegisterControlChangeNotify((IAudioEndpointVolumeCallback*)this);
return true;
}
bool AudioDeviceMoitor::RemoveMoitorMicrophone()
{
if (m_pMicEndpointVolume)
m_pMicEndpointVolume->UnregisterControlChangeNotify(this);
SAFE_RELEASE(m_pMicEndpointVolume);
SAFE_RELEASE(m_pMicrophoneMMDev);
return true;
}
bool AudioDeviceMoitor::RemoveMoitorSpeaker()
{
if (m_pSpeakerEndpointVolume)
m_pSpeakerEndpointVolume->UnregisterControlChangeNotify(this);
SAFE_RELEASE(m_pSpeakerEndpointVolume);
SAFE_RELEASE(m_pSpeakerMMDev);
return true;
}
void AudioDeviceMoitor::SendEvent(int event, int value)
{
std::lock_guard<std::mutex> lockGuard(m_DevEventMutex);
if (m_pDevEventFun != nullptr)
{
m_pDevEventFun(event, value);
}
}
LPWSTR AudioDeviceMoitor::GetGuid(int index, EDataFlow eDataFlow)
{
HRESULT hr = S_OK;
PROPVARIANT varValue;
LPWSTR pwGuid = nullptr;
IMMDevice* pDevice = nullptr;
IPropertyStore* pProperties = nullptr;
IMMDeviceCollection *pCollection = nullptr;
hr = m_pDeviceEnum->EnumAudioEndpoints(eDataFlow, DEVICE_STATE_ACTIVE, &pCollection);
if (S_OK != hr || nullptr == pCollection)
{
return nullptr;
}
hr = pCollection->Item(index, &pDevice);
if (S_OK != hr || nullptr == pDevice)
{
pCollection->Release();
return nullptr;
}
PropVariantInit(&varValue);
hr = pDevice->OpenPropertyStore(STGM_READ, &pProperties);
if (S_OK != hr || nullptr == pProperties)
{
pDevice->Release();
pCollection->Release();
return nullptr;
}
pProperties->GetValue(PKEY_Device_FriendlyName, &varValue);
PropVariantClear(&varValue);
pProperties->GetValue(PKEY_AudioEndpoint_GUID, &varValue);
pwGuid = varValue.pwszVal;
PropVariantClear(&varValue);
pProperties->Release();
pCollection->Release();
if (eDataFlow == EDataFlow::eCapture)
{
m_pMicrophoneMMDev = pDevice;
}
else if (eDataFlow == EDataFlow::eRender)
{
m_pSpeakerMMDev = pDevice;
}
return pwGuid;
}
HRESULT STDMETHODCALLTYPE AudioDeviceMoitor::QueryInterface(REFIID riid, VOID** ppvInterface) {
if (IID_IUnknown == riid)
{
AddRef();
*ppvInterface = this;
}
else if (__uuidof(IMMNotificationClient) == riid)
{
AddRef();
*ppvInterface = (IMMNotificationClient*)this;
}
else if (__uuidof(IAudioEndpointVolumeCallback) == riid)
{
AddRef();
*ppvInterface = (IAudioEndpointVolumeCallback*)this;
}
else if (__uuidof(IAudioSessionEvents) == riid)
{
AddRef();
*ppvInterface = (IAudioSessionEvents*)this;
}
else
{
*ppvInterface = nullptr;
return E_NOINTERFACE;
}
return S_OK;
}
ULONG STDMETHODCALLTYPE AudioDeviceMoitor::AddRef(void) {
return InterlockedIncrement(&m_uRef);
}
ULONG STDMETHODCALLTYPE AudioDeviceMoitor::Release(void) {
ULONG uRef = InterlockedDecrement(&m_uRef);
if (0 == m_uRef)
{
delete this;
}
return uRef;
}
HRESULT STDMETHODCALLTYPE AudioDeviceMoitor::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify) {
if (pNotify == NULL) {
return E_INVALIDARG;
}
//当有扬声器或者是麦克风发生了静音、音量改变,就会调用到这里
//我这里不写代码的原因是因为同时监听了扬声器和麦克风,方案1:可以通过guid进行检查,这里就不写了
//方案2:再编写主动调用扬声器或者麦克风静音的方法
return S_OK;
}
std::string AudioDeviceMoitor::UTF8Encode(const std::wstring &strInput)
{
std::string strOutput;
LPSTR pstrRes = nullptr;
int nLen = ::WideCharToMultiByte(CP_UTF8, 0, strInput.c_str(), strInput.size(), 0, 0, NULL, NULL);
if (nLen > 0)
{
pstrRes = new CHAR[nLen + 1];
memset(pstrRes, 0, nLen + 1);
}
else
{
return "";
}
::WideCharToMultiByte(CP_UTF8, 0, strInput.c_str(), strInput.size(), pstrRes, nLen, NULL, NULL);
pstrRes[nLen] = 0;
strOutput = pstrRes;
delete[]pstrRes;
return strOutput;
}
}