Unterstützung von DXVA 2.0 in DirectShow

In diesem Thema wird beschrieben, wie DirectX Video Acceleration (DXVA) 2.0 in einem DirectShow-Decoderfilter unterstützt wird. Insbesondere wird die Kommunikation zwischen dem Decoder und dem Videorenderer beschrieben. In diesem Thema wird nicht beschrieben, wie die DXVA-Decodierung implementiert wird.

Voraussetzungen

In diesem Thema wird davon ausgegangen, dass Sie mit dem Schreiben von DirectShow-Filtern vertraut sind. Weitere Informationen finden Sie im Thema Schreiben von DirectShow-Filtern in der Dokumentation zum DirectShow SDK. In den Codebeispielen in diesem Thema wird davon ausgegangen, dass der Decoderfilter von der CTransformFilter-Klasse mit der folgenden Klassendefinition abgeleitet ist:

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;
};

Im weiteren Verlauf dieses Themas bezieht sich der Begriff Decoder auf den Decoderfilter, der komprimierte Videos empfängt und unkomprimierte Videos ausgibt. Der Begriff Decodergerät bezieht sich auf einen hardwarebasierten Videobeschleuniger, der vom Grafiktreiber implementiert wird.

Hier sind die grundlegenden Schritte, die ein Decoderfilter ausführen muss, um DXVA 2.0 zu unterstützen:

  1. Aushandeln eines Medientyps.
  2. Suchen Sie nach einer DXVA-Decoderkonfiguration.
  3. Benachrichtigen Sie den Videorenderer, dass der Decoder die DXVA-Decodierung verwendet.
  4. Stellen Sie einen benutzerdefinierten Zuweisungsator bereit, der Direct3D-Oberflächen zuordnet.

Diese Schritte werden im weiteren Verlauf dieses Themas ausführlicher beschrieben.

Migrationshinweise

Wenn Sie von DXVA 1.0 migrieren, sollten Sie einige erhebliche Unterschiede zwischen den beiden Versionen beachten:

  • DXVA 2.0 verwendet die Schnittstellen IAMVideoAccelerator und IAMVideoAcceleratorNotify nicht, da der Decoder direkt über die IDirectXVideoDecoder-Schnittstelle auf die DXVA 2.0-APIs zugreifen kann.
  • Während der Medientypaushandlung verwendet der Decoder keine Videobeschleunigungs-GUID als Untertyp. Stattdessen ist der Untertyp nur das unkomprimierte Videoformat (z. B. NV12), wie bei der Softwaredecodierung.
  • Das Verfahren zum Konfigurieren des Accelerators wurde geändert. In DXVA 1.0 ruft der Decoder Ausführen mit einer DXVA_ConfigPictureDecode-Struktur auf, um den Accerlator zu konfigurieren. In DXVA 2.0 verwendet der Decoder die IDirectXVideoDecoderService-Schnittstelle , wie im nächsten Abschnitt beschrieben.
  • Der Decoder weist die nicht komprimierten Puffer zu. Der Videorenderer weist sie nicht mehr zu.
  • Anstatt IAMVideoAccelerator::D isplayFrame aufzurufen, um den decodierten Frame anzuzeigen, übermittelt der Decoder den Frame an den Renderer, indem er IMemInputPin::Receive aufruft, wie bei der Softwaredecodierung.
  • Der Decoder ist nicht mehr für die Überprüfung verantwortlich, wann Datenpuffer für Updates sicher sind. Daher verfügt DXVA 2.0 über keine Methode, die IAMVideoAccelerator::QueryRenderStatus entspricht.
  • Die Subpicture-Mischung wird vom Videorenderer mithilfe der DXVA2.0-Videoprozessor-APIs durchgeführt. Decoder, die Unterbilder bereitstellen (z. B. DVD-Decoder), sollten Unterpicturedaten an einen separaten Ausgabenadel senden.

Für Decodierungsvorgänge verwendet DXVA 2.0 dieselben Datenstrukturen wie DXVA 1.0.

Der EVR-Filter (Enhanced Video Renderer) unterstützt DXVA 2.0. Die Video Mixing Renderer-Filter (VMR-7 und VMR-9) unterstützen nur DXVA 1.0.

Suchen einer Decoderkonfiguration

Nachdem der Decoder den Ausgabemedientyp ausgehandelt hat, muss eine kompatible Konfiguration für das DXVA-Decodergerät gefunden werden. Sie können diesen Schritt in der CBaseOutputPin::CompleteConnect-Methode des Ausgabepins ausführen. Mit diesem Schritt wird sichergestellt, dass der Grafiktreiber die vom Decoder benötigten Funktionen unterstützt, bevor der Decoder mit DXVA committet.

Gehen Sie wie folgt vor, um eine Konfiguration für das Decodergerät zu finden:

  1. Fragen Sie den Eingabenadel des Renderers für die IMFGetService-Schnittstelle ab.

  2. Rufen Sie IMFGetService::GetService auf, um einen Zeiger auf die IDirect3DDeviceManager9-Schnittstelle zu erhalten. Die Dienst-GUID ist MR_VIDEO_ACCELERATION_SERVICE.

  3. Rufen Sie IDirect3DDeviceManager9::OpenDeviceHandle auf, um ein Handle für das Direct3D-Gerät des Renderers abzurufen.

  4. Rufen Sie IDirect3DDeviceManager9::GetVideoService auf, und übergeben Sie das Gerätehandle. Diese Methode gibt einen Zeiger auf die IDirectXVideoDecoderService-Schnittstelle zurück.

  5. Rufen Sie IDirectXVideoDecoderService::GetDecoderDeviceGuids auf. Diese Methode gibt ein Array von Decodergeräte-GUIDs zurück.

  6. Durchlaufen Sie das Array der Decoder-GUIDs, um die vom Decoderfilter unterstützten zu finden. Für einen MPEG-2-Decoder würden Sie beispielsweise nach DXVA2_ModeMPEG2_MOCOMP, DXVA2_ModeMPEG2_IDCT oder DXVA2_ModeMPEG2_VLD suchen.

  7. Wenn Sie eine Kandidaten-Decodergeräte-GUID finden, übergeben Sie die GUID an die IDirectXVideoDecoderService::GetDecoderRenderTargets-Methode . Diese Methode gibt ein Array von Renderzielformaten zurück, die als D3DFORMAT-Werte angegeben sind.

  8. Durchlaufen Sie die Renderzielformate, und suchen Sie nach einem Format, das Ihrem Ausgabeformat entspricht. In der Regel unterstützt ein Decodergerät ein einzelnes Renderzielformat. Der Decoderfilter sollte mithilfe dieses Untertyps eine Verbindung mit dem Renderer herstellen. Beim ersten Aufruf von CompleteConnect kann der Decoder das Renderzielformat abschrecken und dieses Format dann als bevorzugten Ausgabetyp zurückgeben.

  9. Rufen Sie IDirectXVideoDecoderService::GetDecoderConfigurations auf. Übergeben Sie die gleiche Decodergeräte-GUID zusammen mit einer DXVA2_VideoDesc-Struktur , die das vorgeschlagene Format beschreibt. Die Methode gibt ein Array von DXVA2_ConfigPictureDecode Strukturen zurück. Jede Struktur beschreibt eine mögliche Konfiguration für das Decodergerät.

  10. Wenn die vorherigen Schritte erfolgreich sind, speichern Sie das Direct3D-Gerätehandle, die Decodergeräte-GUID und die Konfigurationsstruktur. Der Filter verwendet diese Informationen, um das Decodergerät zu erstellen.

Der folgende Code zeigt, wie Sie eine Decoderkonfiguration finden.

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;
}

Da dieses Beispiel generisch ist, wurde ein Teil der Logik in Hilfsfunktionen platziert, die vom Decoder implementiert werden müssten. Der folgende Code zeigt die Deklarationen für diese Funktionen:

// 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);

Benachrichtigen des Videorenderers

Wenn der Decoder eine Decoderkonfiguration findet, besteht der nächste Schritt darin, den Videorenderer zu benachrichtigen, dass der Decoder die Hardwarebeschleunigung verwendet. Sie können diesen Schritt in der CompleteConnect-Methode ausführen. Dieser Schritt muss erfolgen, bevor der Zuweisungsator ausgewählt wird, da er sich auf die Auswahl des Zuteilungsgebers auswirkt.

  1. Fragen Sie den Eingabenadel des Renderers für die IMFGetService-Schnittstelle ab.
  2. Rufen Sie IMFGetService::GetService auf, um einen Zeiger auf die IDirectXVideoMemoryConfiguration-Schnittstelle zu erhalten. Die Dienst-GUID ist MR_VIDEO_ACCELERATION_SERVICE.
  3. Rufen Sie IDirectXVideoMemoryConfiguration::GetAvailableSurfaceTypeByIndex in einer Schleife auf, und erhöhen Sie die dwTypeIndex-Variable von 0. Beenden Sie, wenn die Methode den Wert DXVA2_SurfaceType_DecoderRenderTarget im pdwType-Parameter zurückgibt. Mit diesem Schritt wird sichergestellt, dass der Videorenderer die hardwarebeschleunigte Decodierung unterstützt. Dieser Schritt ist für den EVR-Filter immer erfolgreich.
  4. Wenn der vorherige Schritt erfolgreich war, rufen Sie IDirectXVideoMemoryConfiguration::SetSurfaceType mit dem Wert DXVA2_SurfaceType_DecoderRenderTarget auf. Durch Aufrufen von SetSurfaceType mit diesem Wert wird der Videorenderer in den DXVA-Modus versetzt. Wenn sich der Videorenderer in diesem Modus befindet, muss der Decoder einen eigenen Zuteilungscode bereitstellen.

Der folgende Code zeigt, wie sie den Videorenderer benachrichtigen.

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;
}

Wenn der Decoder eine gültige Konfiguration findet und den Videorenderer erfolgreich benachrichtigt, kann der Decoder DXVA für die Decodierung verwenden. Der Decoder muss einen benutzerdefinierten Zuweisungscode für seinen Ausgabenadel implementieren, wie im nächsten Abschnitt beschrieben.

Zuweisung nicht komprimierter Puffer

In DXVA 2.0 ist der Decoder für die Zuweisung von Direct3D-Oberflächen zur Verwendung als unkomprimierte Videopuffer verantwortlich. Daher muss der Decoder einen benutzerdefinierten Zuteilungscode implementieren, der die Oberflächen erstellt. Die von diesem Zuteilungsgeber bereitgestellten Medienbeispiele enthalten Zeiger auf die Direct3D-Oberflächen. Der EVR ruft einen Zeiger auf die Oberfläche ab, indem im Medienbeispiel IMFGetService::GetService aufgerufen wird. Der Dienstbezeichner ist MR_BUFFER_SERVICE.

Führen Sie zum Bereitstellen der benutzerdefinierten Zuweisung die folgenden Schritte aus:

  1. Definieren Sie eine Klasse für die Medienbeispiele. Diese Klasse kann von der CMediaSample-Klasse abgeleitet werden. Führen Sie innerhalb dieser Klasse die folgenden Schritte aus:
    • Speichern Sie einen Zeiger auf die Direct3D-Oberfläche.
    • Implementieren Sie die IMFGetService-Schnittstelle . Wenn die Dienst-GUID in der GetService-Methode MR_BUFFER_SERVICE ist, fragen Sie die Direct3D-Oberfläche für die angeforderte Schnittstelle ab. Andernfalls kann GetServiceMF_E_UNSUPPORTED_SERVICE zurückgeben.
    • Überschreiben Sie die CMediaSample::GetPointer-Methode , um E_NOTIMPL zurückzugeben.
  2. Definieren Sie eine Klasse für die Zuweisung. Die Zuweisung kann von der CBaseAllocator-Klasse abgeleitet werden . Führen Sie in dieser Klasse die folgenden Schritte aus.
  3. Überschreiben Sie im Ausgabepin Ihres Filters die CBaseOutputPin::InitAllocator-Methode . Erstellen Sie in dieser Methode eine instance Ihrer benutzerdefinierten Zuweisung.
  4. Implementieren Sie in Ihrem Filter die CTransformFilter::D ecideBufferSize-Methode . Der pProperties-Parameter gibt die Anzahl der Oberflächen an, die der EVR benötigt. Fügen Sie diesem Wert die Anzahl von Oberflächen hinzu, die Ihr Decoder benötigt, und rufen Sie IMemAllocator::SetProperties auf der Zuweisung auf.

Der folgende Code zeigt, wie die Medienbeispielklasse implementiert wird:

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;
};

Der folgende Code zeigt, wie die Alloc-Methode für die Zuweisung implementiert wird.

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;
}

Hier ist der Code für die Free-Methode :

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;
}

Weitere Informationen zum Implementieren benutzerdefinierter Zuweisungen finden Sie im Thema Bereitstellen einer benutzerdefinierten Zuweisung in der DirectShow SDK-Dokumentation.

Decodierung

Rufen Sie zum Erstellen des Decodergeräts IDirectXVideoDecoderService::CreateVideoDecoder auf. Die -Methode gibt einen Zeiger auf die IDirectXVideoDecoder-Schnittstelle des Decodergeräts zurück.

Rufen Sie in jedem Frame IDirect3DDeviceManager9::TestDevice auf, um das Gerätehandle zu testen. Wenn sich das Gerät geändert hat, gibt die Methode DXVA2_E_NEW_VIDEO_DEVICE zurück. Gehen Sie in diesem Fall wie folgt vor:

  1. Schließen Sie das Gerätehandle, indem Sie IDirect3DDeviceManager9::CloseDeviceHandle aufrufen.
  2. Geben Sie die Zeiger IDirectXVideoDecoderService und IDirectXVideoDecoder frei.
  3. Öffnen Sie ein neues Gerätehandle.
  4. Verhandeln Sie eine neue Decoderkonfiguration, wie im Abschnitt Suchen einer Decoderkonfiguration beschrieben.
  5. Erstellen Sie ein neues Decodergerät.

Unter der Annahme, dass das Gerätehandle gültig ist, funktioniert der Decodierungsprozess wie folgt:

  1. Rufen Sie IDirectXVideoDecoder::BeginFrame auf.
  2. Führen Sie die folgenden Aktionen ein oder mehrere Male aus:
    1. Rufen Sie IDirectXVideoDecoder::GetBuffer auf, um einen DXVA-Decoderpuffer abzurufen.
    2. Füllen Sie den Puffer.
    3. Rufen Sie IDirectXVideoDecoder::ReleaseBuffer auf.
  3. Rufen Sie IDirectXVideoDecoder::Execute auf, um die Decodierungsvorgänge für den Frame auszuführen.

DXVA 2.0 verwendet die gleichen Datenstrukturen wie DXVA 1.0 für Decodierungsvorgänge. Für den ursprünglichen Satz von DXVA-Profilen (für H.261, H.263 und MPEG-2) werden diese Datenstrukturen in der DXVA 1.0-Spezifikation beschrieben.

Innerhalb jedes BeginFrame/Execute-Paars können Sie GetBuffer mehrmals aufrufen, aber nur einmal für jeden Typ von DXVA-Puffer. Wenn Sie es zweimal mit demselben Puffertyp aufrufen, überschreiben Sie die Daten.

Rufen Sie nach dem Aufrufen von ExecuteIMemInputPin::Receive auf, um den Frame wie bei der Softwaredecodierung an den Videorenderer zu übermitteln. Die Receive-Methode ist asynchron. nach der Rückgabe kann der Decoder die Decodierung des nächsten Frames fortsetzen. Der Anzeigetreiber verhindert, dass Decodierungsbefehle den Puffer überschreiben, während der Puffer verwendet wird. Der Decoder sollte eine Oberfläche nicht wiederverwenden, um einen anderen Frame zu decodieren, bis der Renderer das Beispiel freigegeben hat. Wenn der Renderer das Beispiel freigibt, fügt der Zuweisungsgeber das Beispiel wieder in den Pool der verfügbaren Beispiele ein. Um das nächste verfügbare Beispiel zu erhalten, rufen Sie CBaseOutputPin::GetDeliveryBuffer auf, der wiederum IMemAllocator::GetBuffer aufruft. Weitere Informationen finden Sie im Thema Übersicht über Datenfluss in DirectShow in der DirectShow-Dokumentation.

DirectX-Videobeschleunigung 2.0