I. Introduction
I don't want to write a preface, but I just want to record the demos that are usually used in operating system-related tests, sort them out and send them out. The specific usage scenarios are treated according to the needs.
This blog only records how to monitor the mute and volume events of the system speaker and microphone under windows, and I will sort out the mac os later. Because the needs are different, it is only used as a reference.
directly on the code
| Version statement: Mr. Shanhe, without the permission of the blogger, reprinting is prohibited
Two, realize
1. Header file
#ifndef AUDIO_DEVICE_MOITOR_WIN_
#define AUDIO_DEVICE_MOITOR_WIN_
#include <initguid.h>
#include <windows.h>
#include <commctrl.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <cfgmgr32.h>
#include <audiopolicy.h>
#include <mutex>
#include <string>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#include <functiondiscoverykeys.h>
#include <algorithm>
#include <setupapi.h>
#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
2. Source files
#include "audio_device_moitor_win.h"
#include <assert.h>
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;
}
}