Obsługa DXVA 2.0 w DirectShow
W tym temacie opisano sposób obsługi przyspieszania wideo DirectX (DXVA) 2.0 w filtrze dekodera DirectShow. W szczególności opisuje komunikację między dekoderem a modułem renderowania wideo. W tym temacie nie opisano sposobu implementowania dekodowania DXVA.
- wymagania wstępne
- uwagi dotyczące migracji
- znajdowanie konfiguracji dekodera
- Powiadamianie renderera wideo
- przydzielanie nieskompresowanych buforów
- dekodowanie
- Tematy pokrewne
Warunki wstępne
W tym temacie założono, że wiesz już, jak pisać filtry DirectShow. Aby uzyskać więcej informacji, zobacz temat Pisanie filtrów DirectShow w dokumentacji zestawu SDK DirectShow. W przykładach kodu w tym temacie przyjęto założenie, że filtr dekodera pochodzi z klasy CTransformFilter z następującą definicją klasy:
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;
};
W pozostałej części tego tematu termin dekoder odnosi się do filtru dekodera, który odbiera skompresowane wideo i generuje nieskompresowane wideo. Termin urządzenia dekodera odnosi się do sprzętowego akceleratora wideo zaimplementowanego przez sterownik graficzny.
Poniżej przedstawiono podstawowe kroki, które filtr dekodera musi wykonać w celu obsługi DXVA 2.0:
- Negocjowanie typu nośnika.
- Znajdź konfigurację dekodera DXVA.
- Powiadom program renderujący wideo, że dekoder używa dekodowania DXVA.
- Podaj niestandardowy alokator, który przydziela powierzchnie Direct3D.
Te kroki zostały szczegółowo opisane w pozostałej części tego tematu.
Uwagi dotyczące migracji
W przypadku migracji z wersji DXVA 1.0 należy pamiętać o pewnych istotnych różnicach między dwiema wersjami:
- DXVA 2.0 nie używa interfejsów IAMVideoAccelerator i IAMVideoAcceleratorNotify, ponieważ dekoder może uzyskać dostęp do API DXVA 2.0 bezpośrednio przez interfejs IDirectXVideoDecoder.
- Podczas negocjacji typu nośnika dekoder nie używa identyfikatora GUID dla przyspieszania wideo jako podtypu. Zamiast tego podtyp jest tylko nieskompresowanym formatem wideo (takim jak NV12), podobnie jak w przypadku dekodowania oprogramowania.
- Procedura konfigurowania akceleratora uległa zmianie. W DXVA 1.0 dekoder wywołuje Execute ze strukturą DXVA_ConfigPictureDecode w celu skonfigurowania akceleratora. W systemie DXVA 2.0 dekoder używa interfejsuIDirectXVideoDecoderService, zgodnie z opisem w następnej sekcji.
- Dekoder przydziela nieskompresowane bufory. Renderer wideo nie przydziela ich już.
- Zamiast wywoływać IAMVideoAccelerator::DisplayFrame w celu wyświetlenia zdekodowanej ramki, dekoder dostarcza ramkę do modułu renderowania, wywołując IMemInputPin::Receive, podobnie jak w przypadku dekodowania w oprogramowaniu.
- Dekoder nie jest już odpowiedzialny za sprawdzanie, kiedy bufory danych są bezpieczne do aktualizacji. W związku z tym DXVA 2.0 nie ma żadnej metody równoważnej IAMVideoAccelerator::QueryRenderStatus.
- Łączenie obrazu podglądowego odbywa się przez renderer wideo przy użyciu interfejsów API procesora wideo DXVA2.0. Dekodatory, które zapewniają subpictures (na przykład dekodatory DVD) powinny wysyłać dane subpicture na osobnym wyprowadzeniu wyjściowym.
W przypadku operacji dekodowania DXVA 2.0 używa tych samych struktur danych co DXVA 1.0.
Ulepszony filtr renderatora wideo (EVR) obsługuje DXVA 2.0. Filtry renderera mieszania wideo (VMR-7 i VMR-9) obsługują tylko DXVA 1.0.
Znajdowanie konfiguracji dekodera
Gdy dekoder negocjuje typ nośnika wyjściowego, musi znaleźć zgodną konfigurację urządzenia dekodera DXVA. Ten krok można wykonać wewnątrz metody pinu wyjściowego CBaseOutputPin::CompleteConnect. Ten krok zapewnia, że sterownik graficzny obsługuje funkcje wymagane przez dekoder, zanim dekoder zdecyduje się na użycie DXVA.
Aby znaleźć konfigurację urządzenia dekodera, wykonaj następujące czynności:
Wykonaj zapytanie dotyczące numeru PIN wejściowego modułu renderowania dla interfejsu IMFGetService.
Wywołaj IMFGetService::GetService, aby uzyskać wskaźnik do interfejsuIDirect3DDeviceManager9. Identyfikator GUID dla usługi to MR_VIDEO_ACCELERATION_SERVICE.
Wywołaj IDirect3DDeviceManager9::OpenDeviceHandle, aby uzyskać dojście do urządzenia renderowania Direct3D.
Wywołaj IDirect3DDeviceManager9::GetVideoService i przekaż uchwyt urządzenia. Ta metoda zwraca wskaźnik do interfejsu IDirectXVideoDecoderService.
Wywołaj IDirectXVideoDecoderService::GetDecoderDeviceGuids. Ta metoda zwraca tablicę identyfikatorów GUID urządzenia dekodera.
Pętluj przez tablicę identyfikatorów GUID dekoderów, aby znaleźć te, które są obsługiwane przez filtr dekodera. Na przykład w przypadku dekodera MPEG-2 należy wyszukać DXVA2_ModeMPEG2_MOCOMP, DXVA2_ModeMPEG2_IDCTlub DXVA2_ModeMPEG2_VLD.
Po znalezieniu identyfikatora GUID urządzenia kandydata dekodera, przekaż ten identyfikator do metody IDirectXVideoDecoderService::GetDecoderRenderTargets. Ta metoda zwraca tablicę formatów docelowych renderowania, wartości określone jako D3DFORMAT.
Iteruj przez formaty docelowe renderowania i znajdź taki, który odpowiada formatowi danych wyjściowych. Zazwyczaj urządzenie dekodera obsługuje jeden format docelowy renderowania. Filtr dekodera powinien łączyć się z modułem renderowania przy użyciu tego podtypu. W pierwszym wywołaniu CompleteConnectdekoder może określić format docelowy renderowania, a następnie zwrócić ten format jako preferowany typ wyjściowy.
Wywołaj IDirectXVideoDecoderService::GetDecoderConfigurations. Przekaż ten sam identyfikator GUID urządzenia dekodera wraz ze strukturą DXVA2_VideoDesc opisującą proponowany format. Metoda zwraca tablicę struktur DXVA2_ConfigPictureDecode. Każda struktura opisuje jedną możliwą konfigurację urządzenia dekodera.
Zakładając, że poprzednie kroki zakończyły się pomyślnie, zapisz uchwyt urządzenia Direct3D, identyfikator GUID urządzenia dekodera i strukturę konfiguracji. Filtr użyje tych informacji do utworzenia urządzenia dekodera.
Poniższy kod pokazuje, jak znaleźć konfigurację dekodera.
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;
}
Ponieważ ten przykład jest ogólny, część logiki została umieszczona w funkcjach pomocnika, które muszą zostać zaimplementowane przez dekoder. Poniższy kod przedstawia deklaracje dla tych funkcji:
// 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);
Powiadamianie programu renderowania wideo
Jeśli dekoder znajdzie konfigurację dekodera, następnym krokiem jest powiadomienie modułu renderowania wideo, że dekoder będzie używać przyspieszania sprzętowego. Ten krok można wykonać wewnątrz metody CompleteConnect. Ten krok musi wystąpić przed wybraniem alokatora, ponieważ ma wpływ na sposób wybierania alokatora.
- Wykonaj zapytanie dotyczące numeru PIN wejściowego modułu renderowania dla interfejsu IMFGetService.
- Wywołaj IMFGetService::GetService, aby uzyskać wskaźnik do interfejsu IDirectXVideoMemoryConfiguration. Identyfikator GUID usługi jest MR_VIDEO_ACCELERATION_SERVICE.
- Wywołaj IDirectXVideoMemoryConfiguration::GetAvailableSurfaceTypeByIndex w pętli, zwiększając zmienną dwTypeIndex, począwszy od zera. Zatrzymaj się, gdy metoda zwraca wartość DXVA2_SurfaceType_DecoderRenderTarget w parametrze pdwType. Ten krok gwarantuje, że program renderujący wideo obsługuje dekodowanie przyspieszane sprzętowo. Ten krok zawsze powiedzie się dla filtru EVR.
- Jeśli poprzedni krok zakończył się pomyślnie, wywołaj IDirectXVideoMemoryConfiguration::SetSurfaceType z wartością DXVA2_SurfaceType_DecoderRenderTarget. Wywołanie SetSurfaceType przy użyciu tej wartości powoduje przełączanie modułu renderowania wideo do trybu DXVA. Gdy moduł renderowania wideo jest w tym trybie, dekoder musi zapewnić własny mechanizm alokacji.
Poniższy kod pokazuje, jak powiadomić program renderowania wideo.
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;
}
Jeśli dekoder znajdzie prawidłową konfigurację i pomyślnie powiadomi program renderujący wideo, dekoder może używać DXVA do dekodowania. Dekoder musi zaimplementować niestandardowy alokator dla pinu wyjściowego, jak opisano w następnej sekcji.
Przydzielanie nieskompresowanych buforów
W DXVA 2.0 dekoder jest odpowiedzialny za przydzielanie powierzchni Direct3D do wykorzystania jako bufory wideo nieskompresowanego. W związku z tym dekoder musi zaimplementować niestandardowy alokator, który utworzy powierzchnie. Próbki multimediów dostarczone przez ten alokator będą przechowywać wskaźniki do powierzchni Direct3D. EVR pobiera wskaźnik na powierzchnię, wywołując IMFGetService::GetService na próbce multimedialnej. Identyfikator usługi jest MR_BUFFER_SERVICE.
Aby udostępnić alokator niestandardowy, wykonaj następujące kroki:
- Zdefiniuj klasę dla przykładów multimediów. Ta klasa może pochodzić z klasy CMediaSample. W tej klasie wykonaj następujące czynności:
- Zapisz wskaźnik na powierzchnię Direct3D.
- Zaimplementuj interfejs IMFGetService. W metodzie GetService, jeśli identyfikator GUID usługi jest MR_BUFFER_SERVICE, wykonaj zapytanie o powierzchnię Direct3D dla żądanego interfejsu. W przeciwnym razie GetService może zwrócić MF_E_UNSUPPORTED_SERVICE.
- Zastąpij metodę CMediaSample::GetPointer, aby zwrócić E_NOTIMPL.
- Zdefiniuj klasę dla alokatora. Alokator może pochodzić z klasy CBaseAllocator. W tej klasie wykonaj następujące czynności.
- Zastąpi metodę CBaseAllocator::Alloc. Wewnątrz tej metody wywołaj metodę IDirectXVideoAccelerationService::CreateSurface, aby utworzyć powierzchnie. (Interfejs IDirectXVideoDecoderService dziedziczy tę metodę z IDirectXVideoAccelerationService.)
- Zastąpij metodę CBaseAllocator::Free, aby zwolnić powierzchnie.
- Na pinie wyjściowym filtru zastąp metodę CBaseOutputPin::InitAllocator. Wewnątrz tej metody utwórz wystąpienie niestandardowego alokatora.
- W filtrze zaimplementuj metodę CTransformFilter::DecideBufferSize. Parametr pProperties wskazuje liczbę powierzchni, których wymaga EVR. Dodaj do tej wartości liczbę powierzchni, których wymaga dekoder, i wywołaj IMemAllocator::SetProperties na alokatorze.
Poniższy kod pokazuje, jak zaimplementować przykładową klasę multimediów:
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;
};
Poniższy kod pokazuje, jak zaimplementować metodę Alloc w alokatorze.
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;
}
Oto kod metody 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;
}
Aby uzyskać więcej informacji na temat implementowania alokatorów niestandardowych, zobacz temat Zapewnianie niestandardowego alokatora w dokumentacji zestawu SDK DirectShow.
Dekodowanie
Aby utworzyć urządzenie dekodera, wywołaj IDirectXVideoDecoderService::CreateVideoDecoder. Metoda zwraca wskaźnik do interfejsu IDirectXVideoDecoder urządzenia dekodera.
Na każdej ramce wywołaj IDirect3DDeviceManager9::TestDevice, aby przetestować uchwyt urządzenia. Jeśli urządzenie uległo zmianie, metoda zwraca DXVA2_E_NEW_VIDEO_DEVICE. W takim przypadku wykonaj następujące czynności:
- Zamknij dojście urządzenia, wywołując IDirect3DDeviceManager9::CloseDeviceHandle.
- Zwolnij wskaźniki IDirectXVideoDecoderService oraz IDirectXVideoDecoder.
- Otwórz nowy uchwyt urządzenia.
- Negocjuj nową konfigurację dekodera zgodnie z opisem w sekcji Znajdowanie konfiguracji dekodera.
- Utwórz nowe urządzenie dekodera.
Zakładając, że uchwyt urządzenia jest prawidłowy, proces dekodowania działa w następujący sposób:
- Wywołaj IDirectXVideoDecoder::BeginFrame.
- Wykonaj następujące czynności co najmniej raz:
- Wywołaj IDirectXVideoDecoder::GetBuffer, aby uzyskać bufor dekodera DXVA.
- Zapełnij bufor.
- Wywołaj IDirectXVideoDecoder::ReleaseBuffer.
- Wywołaj IDirectXVideoDecoder::Wykonaj, aby wykonać operacje dekodowania na ramce.
DXVA 2.0 używa tych samych struktur danych co DXVA 1.0 na potrzeby operacji dekodowania. W przypadku oryginalnego zestawu profilów DXVA (dla H.261, H.263 i MPEG-2) te struktury danych są opisane w specyfikacji DXVA 1.0.
W każdej parze wywołań BeginFrame/Execute można wywołać GetBuffer wiele razy, ale tylko raz dla każdego typu buforu DXVA. Jeśli wywołasz funkcję dwa razy z tym samym typem buforu, zastąpisz dane.
Po wywołaniu Execute, wywołaj metodę IMemInputPin::Receive, aby dostarczyć klatkę do renderera wideo, podobnie jak w przypadku dekodowania oprogramowania. Metoda Receive jest asynchroniczna; po powrocie dekoder może kontynuować dekodowanie następnej ramki. Sterownik wyświetlania uniemożliwia zastępowanie buforu przez jakiekolwiek polecenia dekodowania, gdy bufor jest używany. Dekoder nie powinien ponownie używać powierzchni do dekodowania innej ramki, dopóki renderer nie zwolni próbki. Gdy moduł renderowania zwalnia próbkę, alokator umieszcza ją z powrotem w puli dostępnych próbek. Aby uzyskać następną dostępną próbkę, wywołaj CBaseOutputPin::GetDeliveryBuffer, co z kolei wywołuje IMemAllocator::GetBuffer. Aby uzyskać więcej informacji, zobacz temat Omówienie przepływu danych w DirectShow w dokumentacji DirectShow.
Tematy pokrewne