다음을 통해 공유


오디오 세션 관리

[이 페이지와 연결된 기능인 MFPlay는 레거시 기능입니다. 그것은 MediaPlayerIMFMediaEngine에 의해 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11에 최적화되었습니다. 가능한 경우 새 코드에서 DirectShow 대신 MediaPlayerIMFMediaEngine을 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용한 기존 코드를 다시 작성하여 새 API를 사용하는 것을 추천합니다.]

이 항목에서는 오디오/비디오 재생에 MFPlay를 사용할 때 오디오 볼륨을 제어하는 방법을 설명합니다.

MFPlay는 재생 중에 오디오 볼륨을 제어하는 다음 메서드를 제공합니다.

메서드 설명
IMFPMediaPlayer::SetBalance 왼쪽 채널과 오른쪽 채널 간의 균형을 설정합니다.
IMFPMediaPlayer::SetMute 오디오를 음소거하거나 음소거 해제합니다.
IMFPMediaPlayer::SetVolume 볼륨 수준을 설정합니다.

 

이러한 메서드의 동작을 이해하려면 MFPlay에서 사용하는 하위 수준 오디오 기능을 구현하는 WASAPI(Windows 오디오 세션 API)의 몇 가지 용어를 알고 있어야 합니다.

WASAPI에서 모든 오디오 스트림은 관련된 오디오 스트림 그룹인 정확히 하나의 오디오 세션에 속합니다. 애플리케이션은 둘 이상의 세션을 만들 수 있지만 일반적으로 애플리케이션은 단일 오디오 세션을 유지 관리합니다. 시스템 볼륨 제어 프로그램(Sndvol)은 각 오디오 세션에 대한 볼륨 컨트롤을 표시합니다. Sndvol을 통해 사용자는 애플리케이션 외부에서 오디오 세션의 볼륨을 조정할 수 있습니다. 다음 이미지는 이 프로세스를 보여 줍니다.

스피커로 가는 길에 볼륨 컨트롤을 통과하는 오디오 스트림을 보여 주는 다이어그램 애플리케이션 및 sndvol point to volume control

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은 세션의 마스터 볼륨입니다.

간단한 재생 시나리오의 경우 MFPlay 메서드를 사용하는 대신 WASAPI를 사용하여 전체 세션의 오디오 볼륨을 제어하는 것이 좋습니다.

코드 예

다음은 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 인터페이스를 사용합니다.

이러한 인터페이스를 얻으려면 SAR에서 사용하는 오디오 엔드포인트를 열거해야 합니다. 오디오 엔드포인트는 오디오 데이터를 캡처하거나 사용하는 하드웨어 디바이스입니다. 오디오 재생의 경우 엔드포인트는 단순히 스피커 또는 기타 오디오 출력입니다. 기본적으로 SAR은 eConsole 디바이스 역할에 대한 기본 엔드포인트를 사용합니다. 디바이스 역할은 엔드포인트에 할당된 역할입니다. 디바이스 역할은 핵심 오디오 API에 설명된 ERole 열거형에 의해 지정됩니다.

다음 코드에서는 엔드포인트를 열거하고 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 사용