다음을 통해 공유


DirectShow에서 DXVA 2.0 지원

이 항목에서는 DirectShow 디코더 필터에서 DXVA(DirectX Video Acceleration) 2.0을 지원하는 방법을 설명합니다. 특히 디코더와 비디오 렌더러 간의 통신을 설명합니다. 이 항목에서는 DXVA 디코딩을 구현하는 방법을 설명하지 않습니다.

사전 요구 사항

이 항목에서는 DirectShow 필터 작성에 익숙하다고 가정합니다. 자세한 내용은 DirectShow SDK 설명서에서 DirectShow 필터 작성 항목을 참조하세요. 이 항목의 코드 예제에서는 다음 클래스 정의를 사용하여 디코더 필터가 CTransformFilter 클래스에서 파생되었다고 가정합니다.

class CDecoder : public CTransformFilter
{
public:
    static CUnknown* WINAPI CreateInstance(IUnknown *pUnk, HRESULT *pHr);

    HRESULT CompleteConnect(PIN_DIRECTION direction, IPin *pPin);

    HRESULT InitAllocator(IMemAllocator **ppAlloc);
    HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp);

    // TODO: The implementations of these methods depend on the specific decoder.
    HRESULT CheckInputType(const CMediaType *mtIn);
    HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut);
    HRESULT CTransformFilter::GetMediaType(int,CMediaType *);

private:
    CDecoder(HRESULT *pHr);
    ~CDecoder();

    CBasePin * GetPin(int n);

    HRESULT ConfigureDXVA2(IPin *pPin);
    HRESULT SetEVRForDXVA2(IPin *pPin);

    HRESULT FindDecoderConfiguration(
        /* [in] */  IDirectXVideoDecoderService *pDecoderService,
        /* [in] */  const GUID& guidDecoder, 
        /* [out] */ DXVA2_ConfigPictureDecode *pSelectedConfig,
        /* [out] */ BOOL *pbFoundDXVA2Configuration
        );

private:
    IDirectXVideoDecoderService *m_pDecoderService;

    DXVA2_ConfigPictureDecode m_DecoderConfig;
    GUID                      m_DecoderGuid;
    HANDLE                    m_hDevice;

    FOURCC                    m_fccOutputFormat;
};

이 항목의 나머지 부분에서 디코더 라는 용어는 압축된 비디오를 수신하고 압축되지 않은 비디오를 출력하는 디코더 필터를 나타냅니다. 디코더 디바이스라는 용어는 그래픽 드라이버에서 구현한 하드웨어 비디오 가속기를 나타냅니다.

디코더 필터가 DXVA 2.0을 지원하기 위해 수행해야 하는 기본 단계는 다음과 같습니다.

  1. 미디어 형식을 협상합니다.
  2. DXVA 디코더 구성을 찾습니다.
  3. 디코더가 DXVA 디코딩을 사용하고 있음을 비디오 렌더러에 알립니다.
  4. Direct3D 표면을 할당하는 사용자 지정 할당자를 제공합니다.

이러한 단계는 이 항목의 나머지 부분에서 자세히 설명합니다.

마이그레이션 정보

DXVA 1.0에서 마이그레이션하는 경우 두 버전 간의 몇 가지 중요한 차이점을 알고 있어야 합니다.

  • 디코더가 IDirectXVideoDecoder 인터페이스를 통해 직접 DXVA 2.0 API에 액세스할 수 있으므로 DXVA 2.0은 IAMVideoAccelerator 및 IAMVideoAcceleratorNotify 인터페이스를 사용하지 않습니다.
  • 미디어 형식 협상 중에 디코더는 비디오 가속 GUID를 하위 형식으로 사용하지 않습니다. 대신 하위 형식은 소프트웨어 디코딩과 마찬가지로 압축되지 않은 비디오 형식(예: NV12)입니다.
  • 가속기를 구성하는 절차가 변경되었습니다. DXVA 1.0에서 디코더는 DXVA_ConfigPictureDecode 구조로 Execute를 호출하여 accerlator를 구성합니다. DXVA 2.0에서 디코더는 다음 섹션에 설명된 대로 IDirectXVideoDecoderService 인터페이스를 사용합니다.
  • 디코더는 압축되지 않은 버퍼를 할당합니다. 비디오 렌더러가 더 이상 할당하지 않습니다.
  • 디코딩된 프레임을 표시하기 위해 IAMVideoAccelerator::D isplayFrame 을 호출하는 대신 디코더는 소프트웨어 디코딩과 마찬가지로 IMemInputPin::Receive를 호출하여 프레임을 렌더러에 전달합니다.
  • 디코더는 데이터 버퍼가 업데이트에 안전한 시기를 확인하는 데 더 이상 책임을 지지 않습니다. 따라서 DXVA 2.0에는 IAMVideoAccelerator::QueryRenderStatus와 동일한 메서드가 없습니다.
  • 하위 사진 혼합은 DXVA2.0 비디오 프로세서 API를 사용하여 비디오 렌더러에서 수행됩니다. 하위 이미지(예: DVD 디코더)를 제공하는 디코더는 별도의 출력 핀에 하위 이미지 데이터를 보내야 합니다.

디코딩 작업의 경우 DXVA 2.0은 DXVA 1.0과 동일한 데이터 구조를 사용합니다.

향상된 EVR(비디오 렌더러) 필터는 DXVA 2.0을 지원합니다. 비디오 혼합 렌더러 필터(VMR-7 및 VMR-9)는 DXVA 1.0만 지원합니다.

디코더 구성 찾기

디코더가 출력 미디어 형식을 협상한 후 DXVA 디코더 디바이스에 대한 호환되는 구성을 찾아야 합니다. 출력 핀의 CBaseOutputPin::CompleteConnect 메서드 내에서 이 단계를 수행할 수 있습니다. 이 단계에서는 디코더가 DXVA를 사용하여 커밋하기 전에 그래픽 드라이버가 디코더에 필요한 기능을 지원하도록 합니다.

디코더 디바이스에 대한 구성을 찾으려면 다음을 수행합니다.

  1. IMFGetService 인터페이스에 대한 렌더러의 입력 핀을 쿼리합니다.

  2. IMFGetService::GetService를 호출하여 IDirect3DDeviceManager9 인터페이스에 대한 포인터를 가져옵니다. 서비스 GUID가 MR_VIDEO_ACCELERATION_SERVICE.

  3. IDirect3DDeviceManager9::OpenDeviceHandle을 호출하여 렌더러의 Direct3D 디바이스에 대한 핸들을 가져옵니다.

  4. IDirect3DDeviceManager9::GetVideoService를 호출하고 디바이스 핸들을 전달합니다. 이 메서드는 IDirectXVideoDecoderService 인터페이스에 대한 포인터를 반환합니다.

  5. IDirectXVideoDecoderService::GetDecoderDeviceGuids를 호출합니다. 이 메서드는 디코더 디바이스 GUID의 배열을 반환합니다.

  6. 디코더 GUID 배열을 반복하여 디코더 필터가 지원하는 GUID를 찾습니다. 예를 들어 MPEG-2 디코더의 경우 DXVA2_ModeMPEG2_MOCOMP, DXVA2_ModeMPEG2_IDCT 또는 DXVA2_ModeMPEG2_VLD 찾습니다.

  7. 후보 디코더 디바이스 GUID를 찾으면 IDirectXVideoDecoderService::GetDecoderRenderTargets 메서드에 GUID를 전달합니다. 이 메서드는 D3DFORMAT 값으로 지정된 렌더링 대상 형식의 배열을 반환합니다.

  8. 렌더링 대상 형식을 반복하고 출력 형식과 일치하는 형식을 찾습니다. 일반적으로 디코더 디바이스는 단일 렌더링 대상 형식을 지원합니다. 디코더 필터는 이 하위 형식을 사용하여 렌더러에 연결해야 합니다. CompleteConnect에 대한 첫 번째 호출에서 디코더는 렌더링 대상 형식을 결정한 다음 이 형식을 기본 출력 형식으로 반환할 수 있습니다.

  9. IDirectXVideoDecoderService::GetDecoderConfigurations를 호출합니다. 제안된 형식을 설명하는 DXVA2_VideoDesc 구조와 함께 동일한 디코더 디바이스 GUID를 전달합니다. 메서드는 DXVA2_ConfigPictureDecode 구조체의 배열을 반환합니다. 각 구조체는 디코더 디바이스에 대한 하나의 가능한 구성을 설명합니다.

  10. 이전 단계가 성공한다고 가정하면 Direct3D 디바이스 핸들, 디코더 디바이스 GUID 및 구성 구조를 저장합니다. 필터는 이 정보를 사용하여 디코더 디바이스를 만듭니다.

다음 코드는 디코더 구성을 찾는 방법을 보여줍니다.

HRESULT CDecoder::ConfigureDXVA2(IPin *pPin)
{
    UINT    cDecoderGuids = 0;
    BOOL    bFoundDXVA2Configuration = FALSE;
    GUID    guidDecoder = GUID_NULL;

    DXVA2_ConfigPictureDecode config;
    ZeroMemory(&config, sizeof(config));

    // Variables that follow must be cleaned up at the end.

    IMFGetService               *pGetService = NULL;
    IDirect3DDeviceManager9     *pDeviceManager = NULL;
    IDirectXVideoDecoderService *pDecoderService = NULL;

    GUID   *pDecoderGuids = NULL; // size = cDecoderGuids
    HANDLE hDevice = INVALID_HANDLE_VALUE;

    // Query the pin for IMFGetService.
    HRESULT hr = pPin->QueryInterface(IID_PPV_ARGS(&pGetService));

    // Get the Direct3D device manager.
    if (SUCCEEDED(hr))
    {
        hr = pGetService->GetService(

            MR_VIDEO_ACCELERATION_SERVICE,
            IID_PPV_ARGS(&pDeviceManager)
            );
    }

    // Open a new device handle.
    if (SUCCEEDED(hr))
    {
        hr = pDeviceManager->OpenDeviceHandle(&hDevice);
    } 

    // Get the video decoder service.
    if (SUCCEEDED(hr))
    {
        hr = pDeviceManager->GetVideoService(
            hDevice, IID_PPV_ARGS(&pDecoderService));
    }

    // Get the decoder GUIDs.
    if (SUCCEEDED(hr))
    {
        hr = pDecoderService->GetDecoderDeviceGuids(
            &cDecoderGuids, &pDecoderGuids);
    }

    if (SUCCEEDED(hr))
    {
        // Look for the decoder GUIDs we want.
        for (UINT iGuid = 0; iGuid < cDecoderGuids; iGuid++)
        {
            // Do we support this mode?
            if (!IsSupportedDecoderMode(pDecoderGuids[iGuid]))
            {
                continue;
            }

            // Find a configuration that we support. 
            hr = FindDecoderConfiguration(pDecoderService, pDecoderGuids[iGuid],
                &config, &bFoundDXVA2Configuration);
            if (FAILED(hr))
            {
                break;
            }

            if (bFoundDXVA2Configuration)
            {
                // Found a good configuration. Save the GUID and exit the loop.
                guidDecoder = pDecoderGuids[iGuid];
                break;
            }
        }
    }

    if (!bFoundDXVA2Configuration)
    {
        hr = E_FAIL; // Unable to find a configuration.
    }

    if (SUCCEEDED(hr))
    {
        // Store the things we will need later.

        SafeRelease(&m_pDecoderService);
        m_pDecoderService = pDecoderService;
        m_pDecoderService->AddRef();

        m_DecoderConfig = config;
        m_DecoderGuid = guidDecoder;
        m_hDevice = hDevice;
    }

    if (FAILED(hr))
    {
        if (hDevice != INVALID_HANDLE_VALUE)
        {
            pDeviceManager->CloseDeviceHandle(hDevice);
        }
    }

    SafeRelease(&pGetService);
    SafeRelease(&pDeviceManager);
    SafeRelease(&pDecoderService);
    return hr;
}
HRESULT CDecoder::FindDecoderConfiguration(
    /* [in] */  IDirectXVideoDecoderService *pDecoderService,
    /* [in] */  const GUID& guidDecoder, 
    /* [out] */ DXVA2_ConfigPictureDecode *pSelectedConfig,
    /* [out] */ BOOL *pbFoundDXVA2Configuration
    )
{
    HRESULT hr = S_OK;
    UINT cFormats = 0;
    UINT cConfigurations = 0;

    D3DFORMAT                   *pFormats = NULL;     // size = cFormats
    DXVA2_ConfigPictureDecode   *pConfig = NULL;      // size = cConfigurations

    // Find the valid render target formats for this decoder GUID.
    hr = pDecoderService->GetDecoderRenderTargets(
        guidDecoder,
        &cFormats,
        &pFormats
        );

    if (SUCCEEDED(hr))
    {
        // Look for a format that matches our output format.
        for (UINT iFormat = 0; iFormat < cFormats;  iFormat++)
        {
            if (pFormats[iFormat] != (D3DFORMAT)m_fccOutputFormat)
            {
                continue;
            }

            // Fill in the video description. Set the width, height, format, 
            // and frame rate.
            DXVA2_VideoDesc videoDesc = {0};

            FillInVideoDescription(&videoDesc); // Private helper function.
            videoDesc.Format = pFormats[iFormat];

            // Get the available configurations.
            hr = pDecoderService->GetDecoderConfigurations(
                guidDecoder,
                &videoDesc,
                NULL, // Reserved.
                &cConfigurations,
                &pConfig
                );

            if (FAILED(hr))
            {
                break;
            }

            // Find a supported configuration.
            for (UINT iConfig = 0; iConfig < cConfigurations; iConfig++)
            {
                if (IsSupportedDecoderConfig(pConfig[iConfig]))
                {
                    // This configuration is good.
                    *pbFoundDXVA2Configuration = TRUE;
                    *pSelectedConfig = pConfig[iConfig];
                    break;
                }
            }

            CoTaskMemFree(pConfig);
            break;

        } // End of formats loop.
    }

    CoTaskMemFree(pFormats);

    // Note: It is possible to return S_OK without finding a configuration.
    return hr;
}

이 예제는 제네릭이므로 일부 논리는 디코더에서 구현해야 하는 도우미 함수에 배치되었습니다. 다음 코드는 이러한 함수에 대한 선언을 보여 줍니다.

// Returns TRUE if the decoder supports a given decoding mode.
BOOL IsSupportedDecoderMode(const GUID& mode);

// Returns TRUE if the decoder supports a given decoding configuration.
BOOL IsSupportedDecoderConfig(const DXVA2_ConfigPictureDecode& config);

// Fills in a DXVA2_VideoDesc structure based on the input format.
void FillInVideoDescription(DXVA2_VideoDesc *pDesc);

Video Renderer에 알림

디코더가 디코더 구성을 찾은 경우 다음 단계는 디코더가 하드웨어 가속을 사용하도록 비디오 렌더러에 알리는 것입니다. CompleteConnect 메서드 내에서 이 단계를 수행할 수 있습니다. 할당자를 선택하는 방법에 영향을 주므로 할당자를 선택하기 전에 이 단계가 발생해야 합니다.

  1. IMFGetService 인터페이스에 대한 렌더러의 입력 핀을 쿼리합니다.
  2. IMFGetService::GetService를 호출하여 IDirectXVideoMemoryConfiguration 인터페이스에 대한 포인터를 가져옵니다. 서비스 GUID가 MR_VIDEO_ACCELERATION_SERVICE.
  3. 루프에서 IDirectXVideoMemoryConfiguration::GetAvailableSurfaceTypeByIndex 를 호출하여 dwTypeIndex 변수를 0에서 증가합니다. 메서드가 pdwType 매개 변수에 DXVA2_SurfaceType_DecoderRenderTarget 값을 반환할 때 중지합니다. 이 단계에서는 비디오 렌더러가 하드웨어 가속 디코딩을 지원하도록 합니다. 이 단계는 항상 EVR 필터에 성공합니다.
  4. 이전 단계가 성공하면 값이 DXVA2_SurfaceType_DecoderRenderTarget IDirectXVideoMemoryConfiguration::SetSurfaceType 을 호출합니다. 이 값을 사용하여 SetSurfaceType 을 호출하면 비디오 렌더러가 DXVA 모드로 전환됩니다. 비디오 렌더러가 이 모드에 있는 경우 디코더는 자체 할당자를 제공해야 합니다.

다음 코드는 비디오 렌더러에 알리는 방법을 보여줍니다.

HRESULT CDecoder::SetEVRForDXVA2(IPin *pPin)
{
    HRESULT hr = S_OK;

    IMFGetService                       *pGetService = NULL;
    IDirectXVideoMemoryConfiguration    *pVideoConfig = NULL;

    // Query the pin for IMFGetService.
    hr = pPin->QueryInterface(__uuidof(IMFGetService), (void**)&pGetService);

    // Get the IDirectXVideoMemoryConfiguration interface.
    if (SUCCEEDED(hr))
    {
        hr = pGetService->GetService(
            MR_VIDEO_ACCELERATION_SERVICE, IID_PPV_ARGS(&pVideoConfig));
    }

    // Notify the EVR. 
    if (SUCCEEDED(hr))
    {
        DXVA2_SurfaceType surfaceType;

        for (DWORD iTypeIndex = 0; ; iTypeIndex++)
        {
            hr = pVideoConfig->GetAvailableSurfaceTypeByIndex(iTypeIndex, &surfaceType);
            
            if (FAILED(hr))
            {
                break;
            }

            if (surfaceType == DXVA2_SurfaceType_DecoderRenderTarget)
            {
                hr = pVideoConfig->SetSurfaceType(DXVA2_SurfaceType_DecoderRenderTarget);
                break;
            }
        }
    }

    SafeRelease(&pGetService);
    SafeRelease(&pVideoConfig);

    return hr;
}

디코더가 유효한 구성을 찾고 비디오 렌더러에 성공적으로 알리면 디코더는 디코딩에 DXVA를 사용할 수 있습니다. 디코더는 다음 섹션에 설명된 대로 출력 핀에 대한 사용자 지정 할당자를 구현해야 합니다.

압축되지 않은 버퍼 할당

DXVA 2.0에서 디코더는 압축되지 않은 비디오 버퍼로 사용할 Direct3D 표면을 할당해야 합니다. 따라서 디코더는 표면을 만드는 사용자 지정 할당자를 구현해야 합니다. 이 할당자가 제공하는 미디어 샘플은 Direct3D 표면에 대한 포인터를 보유합니다. EVR은 미디어 샘플에서 IMFGetService::GetService 를 호출하여 표면에 대한 포인터를 검색합니다. 서비스 식별자가 MR_BUFFER_SERVICE.

사용자 지정 할당자를 제공하려면 다음 단계를 수행합니다.

  1. 미디어 샘플에 대한 클래스를 정의합니다. 이 클래스는 CMediaSample 클래스에서 파생할 수 있습니다. 이 클래스 내에서 다음을 수행합니다.
  2. 할당자에 대한 클래스를 정의합니다. 할당자는 CBaseAllocator 클래스에서 파생할 수 있습니다. 이 클래스 내에서 다음을 수행합니다.
  3. 필터의 출력 핀에서 CBaseOutputPin::InitAllocator 메서드를 재정의합니다. 이 메서드 내에서 사용자 지정 할당자의 instance 만듭니다.
  4. 필터에서 CTransformFilter::D ecideBufferSize 메서드를 구현합니다. pProperties 매개 변수는 EVR에 필요한 표면 수를 나타냅니다. 이 값에 디코더에 필요한 표면 수를 추가하고 할당자 에서 IMemAllocator::SetProperties 를 호출합니다.

다음 코드는 미디어 샘플 클래스를 구현하는 방법을 보여줍니다.

class CDecoderSample : public CMediaSample, public IMFGetService
{
    friend class CDecoderAllocator;

public:

    CDecoderSample(CDecoderAllocator *pAlloc, HRESULT *phr)
        : CMediaSample(NAME("DecoderSample"), (CBaseAllocator*)pAlloc, phr, NULL, 0),
          m_pSurface(NULL),
          m_dwSurfaceId(0)
    { 
    }

    // Note: CMediaSample does not derive from CUnknown, so we cannot use the
    //       DECLARE_IUNKNOWN macro that is used by most of the filter classes.

    STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        CheckPointer(ppv, E_POINTER);

        if (riid == IID_IMFGetService)
        {
            *ppv = static_cast<IMFGetService*>(this);
            AddRef();
            return S_OK;
        }
        else
        {
            return CMediaSample::QueryInterface(riid, ppv);
        }
    }
    STDMETHODIMP_(ULONG) AddRef()
    {
        return CMediaSample::AddRef();
    }

    STDMETHODIMP_(ULONG) Release()
    {
        // Return a temporary variable for thread safety.
        ULONG cRef = CMediaSample::Release();
        return cRef;
    }

    // IMFGetService::GetService
    STDMETHODIMP GetService(REFGUID guidService, REFIID riid, LPVOID *ppv)
    {
        if (guidService != MR_BUFFER_SERVICE)
        {
            return MF_E_UNSUPPORTED_SERVICE;
        }
        else if (m_pSurface == NULL)
        {
            return E_NOINTERFACE;
        }
        else
        {
            return m_pSurface->QueryInterface(riid, ppv);
        }
    }

    // Override GetPointer because this class does not manage a system memory buffer.
    // The EVR uses the MR_BUFFER_SERVICE service to get the Direct3D surface.
    STDMETHODIMP GetPointer(BYTE ** ppBuffer)
    {
        return E_NOTIMPL;
    }

private:

    // Sets the pointer to the Direct3D surface. 
    void SetSurface(DWORD surfaceId, IDirect3DSurface9 *pSurf)
    {
        SafeRelease(&m_pSurface);

        m_pSurface = pSurf;
        if (m_pSurface)
        {
            m_pSurface->AddRef();
        }

        m_dwSurfaceId = surfaceId;
    }

    IDirect3DSurface9   *m_pSurface;
    DWORD               m_dwSurfaceId;
};

다음 코드에서는 할당자에서 Alloc 메서드를 구현하는 방법을 보여 있습니다.

HRESULT CDecoderAllocator::Alloc()
{
    CAutoLock lock(this);

    HRESULT hr = S_OK;

    if (m_pDXVA2Service == NULL)
    {
        return E_UNEXPECTED;
    }

    hr = CBaseAllocator::Alloc();

    // If the requirements have not changed, do not reallocate.
    if (hr == S_FALSE)
    {
        return S_OK;
    }

    if (SUCCEEDED(hr))
    {
        // Free the old resources.
        Free();

        // Allocate a new array of pointers.
        m_ppRTSurfaceArray = new (std::nothrow) IDirect3DSurface9*[m_lCount];
        if (m_ppRTSurfaceArray == NULL)
        {
            hr = E_OUTOFMEMORY;
        }
        else
        {
            ZeroMemory(m_ppRTSurfaceArray, sizeof(IDirect3DSurface9*) * m_lCount);
        }
    }

    // Allocate the surfaces.
    if (SUCCEEDED(hr))
    {
        hr = m_pDXVA2Service->CreateSurface(
            m_dwWidth,
            m_dwHeight,
            m_lCount - 1,
            (D3DFORMAT)m_dwFormat,
            D3DPOOL_DEFAULT,
            0,
            DXVA2_VideoDecoderRenderTarget,
            m_ppRTSurfaceArray,
            NULL
            );
    }

    if (SUCCEEDED(hr))
    {
        for (m_lAllocated = 0; m_lAllocated < m_lCount; m_lAllocated++)
        {
            CDecoderSample *pSample = new (std::nothrow) CDecoderSample(this, &hr);

            if (pSample == NULL)
            {
                hr = E_OUTOFMEMORY;
                break;
            }
            if (FAILED(hr))
            {
                break;
            }
            // Assign the Direct3D surface pointer and the index.
            pSample->SetSurface(m_lAllocated, m_ppRTSurfaceArray[m_lAllocated]);

            // Add to the sample list.
            m_lFree.Add(pSample);
        }
    }

    if (SUCCEEDED(hr))
    {
        m_bChanged = FALSE;
    }
    return hr;
}

Free 메서드의 코드는 다음과 같습니다.

void CDecoderAllocator::Free()
{
    CMediaSample *pSample = NULL;

    do
    {
        pSample = m_lFree.RemoveHead();
        if (pSample)
        {
            delete pSample;
        }
    } while (pSample);

    if (m_ppRTSurfaceArray)
    {
        for (long i = 0; i < m_lAllocated; i++)
        {
            SafeRelease(&m_ppRTSurfaceArray[i]);
        }

        delete [] m_ppRTSurfaceArray;
    }
    m_lAllocated = 0;
}

사용자 지정 할당자 구현에 대한 자세한 내용은 DirectShow SDK 설명서에서 사용자 지정 할당자 제공 항목을 참조하세요.

디코딩

디코더 디바이스를 만들려면 IDirectXVideoDecoderService::CreateVideoDecoder를 호출합니다. 메서드는 디코더 디바이스의 IDirectXVideoDecoder 인터페이스에 대한 포인터를 반환합니다.

각 프레임에서 IDirect3DDeviceManager9::TestDevice 를 호출하여 디바이스 핸들을 테스트합니다. 디바이스가 변경된 경우 메서드는 DXVA2_E_NEW_VIDEO_DEVICE 반환합니다. 이 경우 다음을 수행합니다.

  1. IDirect3DDeviceManager9::CloseDeviceHandle을 호출하여 디바이스 핸들을 닫습니다.
  2. IDirectXVideoDecoderServiceIDirectXVideoDecoder 포인터를 해제합니다.
  3. 새 디바이스 핸들을 엽니다.
  4. 디코더 구성 찾기 섹션에 설명된 대로 새 디코더 구성을 협상합니다.
  5. 새 디코더 디바이스를 만듭니다.

디바이스 핸들이 유효하다고 가정하면 디코딩 프로세스는 다음과 같이 작동합니다.

  1. IDirectXVideoDecoder::BeginFrame을 호출합니다.
  2. 다음을 한 번 이상 수행합니다.
    1. IDirectXVideoDecoder::GetBuffer를 호출하여 DXVA 디코더 버퍼를 가져옵니다.
    2. 버퍼를 채웁니다.
    3. IDirectXVideoDecoder::ReleaseBuffer를 호출합니다.
  3. IDirectXVideoDecoder::Execute를 호출하여 프레임에서 디코딩 작업을 수행합니다.

DXVA 2.0은 디코딩 작업에 DXVA 1.0과 동일한 데이터 구조를 사용합니다. 원래 DXVA 프로필 집합(H.261, H.263 및 MPEG-2의 경우)의 경우 이러한 데이터 구조는 DXVA 1.0 사양에 설명되어 있습니다.

BeginFrame/Execute 호출의 각 쌍 내에서 GetBuffer를 여러 번 호출할 수 있지만 DXVA 버퍼의 각 유형에 대해 한 번만 호출할 수 있습니다. 동일한 버퍼 형식으로 두 번 호출하는 경우 데이터를 덮어쓰게 됩니다.

Execute를 호출한 후 IMemInputPin::Receive를 호출하여 소프트웨어 디코딩과 마찬가지로 프레임을 비디오 렌더러에 전달합니다. Receive 메서드는 비동기적입니다. 반환된 후 디코더는 다음 프레임을 계속 디코딩할 수 있습니다. 디스플레이 드라이버는 버퍼를 사용하는 동안 디코딩 명령이 버퍼를 덮어쓰지 못하도록 합니다. 디코더는 렌더러가 샘플을 해제할 때까지 표면을 다시 사용하여 다른 프레임을 디코딩하면 안 됩니다. 렌더러가 샘플을 해제하면 할당자는 샘플을 사용 가능한 샘플 풀에 다시 배치합니다. 사용 가능한 다음 샘플을 얻으려면 CBaseOutputPin::GetDeliveryBuffer를 호출합니다. 그러면 IMemAllocator::GetBuffer가 호출됩니다. 자세한 내용은 DirectShow 설명서의 DirectShow에서 Data Flow 개요 항목을 참조하세요.

DirectX 비디오 가속 2.0