Поделиться через


Управление звуковым сеансом

[MFPlay доступен для использования в операционных системах, указанных в разделе Требования. В последующих версиях он может быть изменен или недоступен. ]

В этом разделе описывается, как управлять громкость звука при использовании MFPlay для воспроизведения звука и видео.

MFPlay предоставляет следующие методы для управления громкость звука во время воспроизведения.

Метод Описание
IMFPMediaPlayer::SetBalance Задает баланс между левым и правым каналами.
IMFPMediaPlayer::SetMute Отключает или отключает звук.
IMFPMediaPlayer::SetVolume Задает уровень громкости.

 

Чтобы понять поведение этих методов, необходимо знать определенную терминологию из API сеанса звука Windows (WASAPI), который реализует низкоуровневые звуковые функции, используемые MFPlay.

В WASAPI каждый звуковой поток принадлежит ровно к одному звуковому сеансу, который представляет собой группу связанных аудиопотоков. Как правило, приложение поддерживает один звуковой сеанс, хотя приложения могут создавать несколько сеансов. Системная программа управления громкостем (Sndvol) отображает регулятор громкости для каждого звукового сеанса. С помощью Sndvol пользователь может настраивать громкость звукового сеанса за пределами приложения. Этот процесс показан на следующем рисунке.

схема, показывающая звуковые потоки, проходящие через регулятор громкости на пути к динамикам; application и sndvol указывают на регулятор громкости

В MFPlay элемент мультимедиа может иметь один или несколько активных аудиопотоков (обычно только один). Внутри MFPlay использует отрисовщик потокового аудио (SAR) для отрисовки аудиопотоков. Если вы не настроите иное, SAR присоединяется к звуковому сеансу приложения по умолчанию.

Звуковые методы MFPlay управляют только потоками, принадлежащими текущему элементу мультимедиа. Они не влияют на громкость для других потоков, относящихся к тому же звуковому сеансу. С точки зрения WASAPI методы MFPlay управляют уровнями тома по каналу, а не уровнем master. Этот процесс показан на следующем рисунке.

схема похожа на предыдущую, но второй поток начинается с элемента мультимедиа, а приложение указывает на второй поток и управление громкости

Важно понимать некоторые последствия этой функции MFPlay. Во-первых, приложение может настраивать громкость воспроизведения, не затрагивая другие звуковые потоки. Эту функцию можно использовать, если MFPlay реализует перекрестное перекрестное воспроизведение звука, создав два экземпляра объекта MFPlay и изменив громкость отдельно.

Если для изменения громкости или отключения звука используются методы MFPlay, изменения не отображаются в Sndvol. Например, вы можете вызвать SetMute , чтобы отключить звук, но Sndvol не будет отображать сеанс как отключенный. И наоборот, если SndVol используется для корректировки объема сеанса, изменения не отражаются в значениях, возвращаемых IMFPMediaPlayer::GetVolume или IMFPMediaPlayer::GetMute.

Для каждого экземпляра объекта проигрывателя MFPlay действующий уровень тома равен fPlayerVolume × fSessionVolume, где fPlayerVolume — это значение, возвращаемое GetVolume, а fSessionVolume — master том для сеанса.

Для простых сценариев воспроизведения может быть предпочтительнее использовать WASAPI для управления громкость звука для всего сеанса, а не использовать методы MFPlay.

Пример кода

Ниже приведен класс C++, который обрабатывает основные задачи в WASAPI:

  • Управление громкостью и состоянием отключения звука для сеанса.
  • Получение уведомлений при изменении громкости или выключения звука.

Объявление класса

Объявление 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использует два main интерфейса 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.

Использование MFPlay для воспроизведения звука и видео