管理音訊會話
[與此頁面 MFPlay 相關聯的功能是舊版功能。 它已被 MediaPlayer 和 IMFMediaEngine 取代。 這些功能已針對 Windows 10 和 Windows 11 進行優化。 Microsoft強烈建議新程式代碼盡可能使用 MediaPlayer 和 IMFMediaEngine,而不是 DirectShow。 Microsoft建議使用舊版 API 的現有程式代碼,盡可能改寫成使用新的 API。]
本主題描述如何使用 MFPlay 進行音訊/視訊播放時控制音訊音量。
MFPlay 提供下列方法來控制播放期間的音訊音量。
方法 | 描述 |
---|---|
IMFPMediaPlayer::SetBalance | 設定左右通道之間的平衡。 |
IMFPMediaPlayer::SetMute | 將音訊靜音或取消靜音。 |
IMFPMediaPlayer::SetVolume | 設定磁碟區層級。 |
若要了解這些方法的行為,您必須從 Windows 音訊會話 API (WASAPI) 瞭解一些術語,其會實作 MFPlay 所使用的低階音訊功能。
在 WASAPI 中,每個音訊串流都只屬於一個 音訊會話,這是一組相關的音訊串流。 一般而言,應用程式會維護單一音訊會話,雖然應用程式可以建立多個會話。 系統音量控制程式 (Sndvol) 會顯示每個音訊會話的音量控制程式。 透過 Sndvol,使用者可以從應用程式外部調整音訊會話的音量。 下圖說明此程式。
在 MFPlay 中,媒體專案可以有一或多個作用中的音訊串流(通常只有一個)。 在內部,MFPlay 會使用 串流音訊轉譯器 (SAR) 來轉譯音訊串流。 除非您另有設定,否則 SAR 會聯結應用程式的預設音訊會話。
MFPlay 音訊方法只會控制屬於目前媒體項目的數據流。 它們不會影響屬於相同音訊會話的任何其他數據流的音量。 就 WASAPI 而言,MFPlay 方法可控制 每個通道 的磁碟區層級,而不是主要磁碟區層級。 下圖說明此程式。
請務必瞭解 MFPlay 此功能的一些含意。 首先,應用程式可以調整播放音量,而不會影響其他音訊數據流。 如果 MFPlay 實作音訊交叉淡化,則可以使用此功能,方法是建立 MFPlay 物件的兩個實例,並個別調整音量。
如果您使用 MFPlay 方法來變更磁碟區或靜音狀態,則變更不會出現在 Sndvol 中。 例如,您可以呼叫 SetMute 將音訊設為靜音,但 Sndvol 不會將會話顯示為靜音。 相反地,如果使用 SndVol 來調整會話量,則變更不會反映在 IMFPMediaPlayer::GetVolume 或 IMFPMediaPlayer::GetMute 傳回的值中。
對於 MFPlay 播放程式物件的每個實例,有效音量層級等於 fPlayerVolume × fSessionVolume,其中 fPlayerVolume 是 GetVolume 傳回的值,而 fSessionVolume 是會話的主要磁碟區。
針對簡單的播放案例,最好使用WASAPI來控制整個會話的音訊音量,而不是使用MFPlay方法。
範例程式碼
以下是處理 WASAPI 中基本工作的C++類別:
- 控制會話的磁碟區和靜音狀態。
- 每當磁碟區或靜音狀態變更時,就會取得通知。
類別宣告
類別 CAudioSessionVolume
宣告會 實作 IAudioSessionEvents 介面,這是音訊會話事件的回呼介面。
class CAudioSessionVolume : public IAudioSessionEvents
{
public:
// Static method to create an instance of the object.
static HRESULT CreateInstance(
UINT uNotificationMessage,
HWND hwndNotification,
CAudioSessionVolume **ppAudioSessionVolume
);
// IUnknown methods.
STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IAudioSessionEvents methods.
STDMETHODIMP OnSimpleVolumeChanged(
float NewVolume,
BOOL NewMute,
LPCGUID EventContext
);
// The remaining audio session events do not require any action.
STDMETHODIMP OnDisplayNameChanged(LPCWSTR,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnIconPathChanged(LPCWSTR,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnChannelVolumeChanged(DWORD,float[],DWORD,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnGroupingParamChanged(LPCGUID,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnStateChanged(AudioSessionState)
{
return S_OK;
}
STDMETHODIMP OnSessionDisconnected(AudioSessionDisconnectReason)
{
return S_OK;
}
// Other methods
HRESULT EnableNotifications(BOOL bEnable);
HRESULT GetVolume(float *pflVolume);
HRESULT SetVolume(float flVolume);
HRESULT GetMute(BOOL *pbMute);
HRESULT SetMute(BOOL bMute);
HRESULT SetDisplayName(const WCHAR *wszName);
protected:
CAudioSessionVolume(UINT uNotificationMessage, HWND hwndNotification);
~CAudioSessionVolume();
HRESULT Initialize();
protected:
LONG m_cRef; // Reference count.
UINT m_uNotificationMessage; // Window message to send when an audio event occurs.
HWND m_hwndNotification; // Window to receives messages.
BOOL m_bNotificationsEnabled; // Are audio notifications enabled?
IAudioSessionControl *m_pAudioSession;
ISimpleAudioVolume *m_pSimpleAudioVolume;
};
CAudioSessionVolume
當物件收到音訊會話事件時,它會將私人視窗訊息張貼至應用程式。 視窗句柄和視窗訊息會以靜態 CAudioSessionVolume::CreateInstance
方法的參數的形式提供。
取得 WASAPI 介面指標
CAudioSessionVolume
使用兩個主要 WASAPI 介面:
- IAudioSessionControl 會管理音訊會話。
- ISimpleAudioVolume 會控制會話的磁碟區層級和靜音狀態。
若要取得這些介面,您必須列舉 SAR 所使用的音訊端點。 音訊端點是可擷取或取用音訊數據的硬體裝置。 針對音訊播放,端點只是喇叭或其他音訊輸出。 根據預設,SAR 會使用 eConsole 裝置角色的預設端點。 裝置角色是端點的指派角色。 裝置角色是由 ERole 列舉所指定,其記載於核心音訊 API 中。
下列程式代碼示範如何列舉端點並取得 WASAPI 介面。
HRESULT CAudioSessionVolume::Initialize()
{
HRESULT hr = S_OK;
IMMDeviceEnumerator *pDeviceEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioSessionManager *pAudioSessionManager = NULL;
// Get the enumerator for the audio endpoint devices.
hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator),
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pDeviceEnumerator)
);
if (FAILED(hr))
{
goto done;
}
// Get the default audio endpoint that the SAR will use.
hr = pDeviceEnumerator->GetDefaultAudioEndpoint(
eRender,
eConsole, // The SAR uses 'eConsole' by default.
&pDevice
);
if (FAILED(hr))
{
goto done;
}
// Get the session manager for this device.
hr = pDevice->Activate(
__uuidof(IAudioSessionManager),
CLSCTX_INPROC_SERVER,
NULL,
(void**) &pAudioSessionManager
);
if (FAILED(hr))
{
goto done;
}
// Get the audio session.
hr = pAudioSessionManager->GetAudioSessionControl(
&GUID_NULL, // Get the default audio session.
FALSE, // The session is not cross-process.
&m_pAudioSession
);
if (FAILED(hr))
{
goto done;
}
hr = pAudioSessionManager->GetSimpleAudioVolume(
&GUID_NULL, 0, &m_pSimpleAudioVolume
);
done:
SafeRelease(&pDeviceEnumerator);
SafeRelease(&pDevice);
SafeRelease(&pAudioSessionManager);
return hr;
}
控制磁碟區
CAudioSessionVolume
控制音訊音量的方法會呼叫對等的 ISimpleAudioVolume 方法。 例如, CAudioSessionVolume::SetVolume
呼叫 ISimpleAudioVolume::SetMasterVolume,如下列程式代碼所示。
HRESULT CAudioSessionVolume::SetVolume(float flVolume)
{
if (m_pSimpleAudioVolume == NULL)
{
return E_FAIL;
}
else
{
return m_pSimpleAudioVolume->SetMasterVolume(
flVolume,
&AudioSessionVolumeCtx // Event context.
);
}
}
完成 CAudioSessionVolume 程式代碼
以下是類別方法 CAudioSessionVolume
的完整清單。
static const GUID AudioSessionVolumeCtx =
{ 0x2715279f, 0x4139, 0x4ba0, { 0x9c, 0xb1, 0xb3, 0x51, 0xf1, 0xb5, 0x8a, 0x4a } };
CAudioSessionVolume::CAudioSessionVolume(
UINT uNotificationMessage,
HWND hwndNotification
)
: m_cRef(1),
m_uNotificationMessage(uNotificationMessage),
m_hwndNotification(hwndNotification),
m_bNotificationsEnabled(FALSE),
m_pAudioSession(NULL),
m_pSimpleAudioVolume(NULL)
{
}
CAudioSessionVolume::~CAudioSessionVolume()
{
EnableNotifications(FALSE);
SafeRelease(&m_pAudioSession);
SafeRelease(&m_pSimpleAudioVolume);
};
// Creates an instance of the CAudioSessionVolume object.
/* static */
HRESULT CAudioSessionVolume::CreateInstance(
UINT uNotificationMessage,
HWND hwndNotification,
CAudioSessionVolume **ppAudioSessionVolume
)
{
CAudioSessionVolume *pAudioSessionVolume = new (std::nothrow)
CAudioSessionVolume(uNotificationMessage, hwndNotification);
if (pAudioSessionVolume == NULL)
{
return E_OUTOFMEMORY;
}
HRESULT hr = pAudioSessionVolume->Initialize();
if (SUCCEEDED(hr))
{
*ppAudioSessionVolume = pAudioSessionVolume;
}
else
{
pAudioSessionVolume->Release();
}
return hr;
}
// Initializes the CAudioSessionVolume object.
HRESULT CAudioSessionVolume::Initialize()
{
HRESULT hr = S_OK;
IMMDeviceEnumerator *pDeviceEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioSessionManager *pAudioSessionManager = NULL;
// Get the enumerator for the audio endpoint devices.
hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator),
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pDeviceEnumerator)
);
if (FAILED(hr))
{
goto done;
}
// Get the default audio endpoint that the SAR will use.
hr = pDeviceEnumerator->GetDefaultAudioEndpoint(
eRender,
eConsole, // The SAR uses 'eConsole' by default.
&pDevice
);
if (FAILED(hr))
{
goto done;
}
// Get the session manager for this device.
hr = pDevice->Activate(
__uuidof(IAudioSessionManager),
CLSCTX_INPROC_SERVER,
NULL,
(void**) &pAudioSessionManager
);
if (FAILED(hr))
{
goto done;
}
// Get the audio session.
hr = pAudioSessionManager->GetAudioSessionControl(
&GUID_NULL, // Get the default audio session.
FALSE, // The session is not cross-process.
&m_pAudioSession
);
if (FAILED(hr))
{
goto done;
}
hr = pAudioSessionManager->GetSimpleAudioVolume(
&GUID_NULL, 0, &m_pSimpleAudioVolume
);
done:
SafeRelease(&pDeviceEnumerator);
SafeRelease(&pDevice);
SafeRelease(&pAudioSessionManager);
return hr;
}
STDMETHODIMP CAudioSessionVolume::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CAudioSessionVolume, IAudioSessionEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CAudioSessionVolume::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) CAudioSessionVolume::Release()
{
LONG cRef = InterlockedDecrement( &m_cRef );
if (cRef == 0)
{
delete this;
}
return cRef;
}
// Enables or disables notifications from the audio session. For example, the
// application is notified if the user mutes the audio through the system
// volume-control program (Sndvol).
HRESULT CAudioSessionVolume::EnableNotifications(BOOL bEnable)
{
HRESULT hr = S_OK;
if (m_hwndNotification == NULL || m_pAudioSession == NULL)
{
return E_FAIL;
}
if (m_bNotificationsEnabled == bEnable)
{
// No change.
return S_OK;
}
if (bEnable)
{
hr = m_pAudioSession->RegisterAudioSessionNotification(this);
}
else
{
hr = m_pAudioSession->UnregisterAudioSessionNotification(this);
}
if (SUCCEEDED(hr))
{
m_bNotificationsEnabled = bEnable;
}
return hr;
}
// Gets the session volume level.
HRESULT CAudioSessionVolume::GetVolume(float *pflVolume)
{
if ( m_pSimpleAudioVolume == NULL)
{
return E_FAIL;
}
else
{
return m_pSimpleAudioVolume->GetMasterVolume(pflVolume);
}
}
// Sets the session volume level.
//
// flVolume: Ranges from 0 (silent) to 1 (full volume)
HRESULT CAudioSessionVolume::SetVolume(float flVolume)
{
if (m_pSimpleAudioVolume == NULL)
{
return E_FAIL;
}
else
{
return m_pSimpleAudioVolume->SetMasterVolume(
flVolume,
&AudioSessionVolumeCtx // Event context.
);
}
}
// Gets the muting state of the session.
HRESULT CAudioSessionVolume::GetMute(BOOL *pbMute)
{
if (m_pSimpleAudioVolume == NULL)
{
return E_FAIL;
}
else
{
return m_pSimpleAudioVolume->GetMute(pbMute);
}
}
// Mutes or unmutes the session audio.
HRESULT CAudioSessionVolume::SetMute(BOOL bMute)
{
if (m_pSimpleAudioVolume == NULL)
{
return E_FAIL;
}
else
{
return m_pSimpleAudioVolume->SetMute(
bMute,
&AudioSessionVolumeCtx // Event context.
);
}
}
// Sets the display name for the session audio.
HRESULT CAudioSessionVolume::SetDisplayName(const WCHAR *wszName)
{
if (m_pAudioSession == NULL)
{
return E_FAIL;
}
else
{
return m_pAudioSession->SetDisplayName(wszName, NULL);
}
}
// Called when the session volume level or muting state changes.
// (Implements IAudioSessionEvents::OnSimpleVolumeChanged.)
HRESULT CAudioSessionVolume::OnSimpleVolumeChanged(
float NewVolume,
BOOL NewMute,
LPCGUID EventContext
)
{
// Check if we should post a message to the application.
if ( m_bNotificationsEnabled &&
(*EventContext != AudioSessionVolumeCtx) &&
(m_hwndNotification != NULL)
)
{
// Notifications are enabled, AND
// We did not trigger the event ourselves, AND
// We have a valid window handle.
// Post the message.
::PostMessage(
m_hwndNotification,
m_uNotificationMessage,
*((WPARAM*)(&NewVolume)), // Coerce the float.
(LPARAM)NewMute
);
}
return S_OK;
}
需求
MFPlay 需要 Windows 7。
相關主題