Поддержка DXVA 2.0 в DirectShow
В этом разделе описывается поддержка DirectX Video Acceleration (DXVA) 2.0 в фильтре декодера DirectShow. В частности, он описывает обмен данными между декодером и отрисовщиком видео. В этом разделе не описывается, как реализовать декодирование 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.
- Согласование типа мультимедиа.
- Найдите конфигурацию декодера DXVA.
- Сообщите отрисовщику видео о том, что декодер использует декодирование DXVA.
- Предоставление пользовательского распределителя, который выделяет поверхности Direct3D.
Эти шаги более подробно описаны в оставшейся части этого раздела.
Заметки о миграции
При миграции с DXVA 1.0 следует учитывать некоторые существенные различия между двумя версиями:
- DXVA 2.0 не использует интерфейсы IAMVideoAccelerator и IAMVideoAcceleratorNotify , так как декодер может обращаться к API DXVA 2.0 непосредственно через интерфейс IDirectXVideoDecoder .
- Во время согласования типов мультимедиа декодер не использует GUID ускорения видео в качестве подтипа. Вместо этого подтипом является только несжатый формат видео (например, NV12), как и при декодировании программного обеспечения.
- Процедура настройки ускорителя изменилась. В DXVA 1.0 декодер вызывает метод Execute со структурой DXVA_ConfigPictureDecode для настройки аксерлятора. В DXVA 2.0 декодер использует интерфейс IDirectXVideoDecoderService , как описано в следующем разделе.
- Декодер выделяет несжатые буферы. Отрисовщик видео больше не выделяет их.
- Вместо вызова IAMVideoAccelerator::D isplayFrame для отображения декодированного кадра декодер доставляет кадр в отрисовщик, вызывая метод IMemInputPin::Receive, как и при декодировании программного обеспечения.
- Декодер больше не отвечает за проверку безопасности буферов данных для обновлений. Поэтому DXVA 2.0 не имеет метода, эквивалентного методу IAMVideoAccelerator::QueryRenderStatus.
- Вложенное наложение выполняется отрисовщиком видео с помощью API-интерфейсов видеопроцессоров DXVA2.0. Декодеры, предоставляющие вложенные фрагменты (например, декодеры DVD), должны отправлять данные вложенныхpicture на отдельный выходной контакт.
Для операций декодирования DXVA 2.0 использует те же структуры данных, что и DXVA 1.0.
Фильтр расширенного отрисовщика видео (EVR) поддерживает DXVA 2.0. Фильтры отрисовщика смешанного видео (VMR-7 и VMR-9) поддерживают только DXVA 1.0.
Поиск конфигурации декодера
После согласования типа выходного носителя декодер должен найти совместимую конфигурацию для устройства декодера DXVA. Этот шаг можно выполнить в методе CBaseOutputPin::CompleteConnect выходного закрепления. Этот шаг гарантирует, что графический драйвер поддерживает возможности, необходимые декодетору, прежде чем декодер зафиксирует использование DXVA.
Чтобы найти конфигурацию устройства декодера, выполните следующие действия.
Запросите входной контакт отрисовщика для интерфейса IMFGetService .
Вызовите IMFGetService::GetService , чтобы получить указатель на интерфейс IDirect3DeviceManager9 . Guid службы MR_VIDEO_ACCELERATION_SERVICE.
Вызовите IDirect3DDeviceManager9::OpenDeviceHandle , чтобы получить дескриптор устройства Direct3D отрисовщика.
Вызовите IDirect3DDeviceManager9::GetVideoService и передайте дескриптор устройства. Этот метод возвращает указатель на интерфейс IDirectXVideoDecoderService .
Вызовите IDirectXVideoDecoderService::GetDecoderDeviceGuids. Этот метод возвращает массив идентификаторов GUID устройства декодера.
Циклически просматривайте массив идентификаторов GUID декодера, чтобы найти те, которые поддерживает фильтр декодера. Например, для декодера MPEG-2 следует искать DXVA2_ModeMPEG2_MOCOMP, DXVA2_ModeMPEG2_IDCT или DXVA2_ModeMPEG2_VLD.
При поиске GUID устройства-кандидата передайте GUID в метод IDirectXVideoDecoderService::GetDecoderRenderTargets . Этот метод возвращает массив целевых форматов отрисовки, указанных как значения D3DFORMAT .
Выполните цикл по целевым форматам отрисовки и найдите тот, который соответствует формату вывода. Как правило, декодер поддерживает один формат целевого объекта отрисовки. Фильтр декодера должен подключаться к отрисовщику с помощью этого подтипа. При первом вызове CompleteConnect декодер может определить целевой формат отрисовки, а затем вернуть этот формат в качестве предпочтительного типа вывода.
Вызовите IDirectXVideoDecoderService::GetDecoderConfigurations. Передайте один и тот же GUID устройства декодера вместе с DXVA2_VideoDesc структурой, описывающей предлагаемый формат. Метод возвращает массив DXVA2_ConfigPictureDecode структур. Каждая структура описывает одну возможную конфигурацию для устройства декодера.
Если предыдущие шаги выполнены успешно, сохраните дескриптор устройства 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);
Уведомление отрисовщика видео
Если декодер находит конфигурацию декодера, следующим шагом является уведомление видео отрисовщика о том, что декодер будет использовать аппаратное ускорение. Этот шаг можно выполнить в методе CompleteConnect . Этот шаг должен выполняться до выбора распределителя, так как он влияет на способ выбора распределителя.
- Запросите входной контакт отрисовщика для интерфейса IMFGetService .
- Вызовите IMFGetService::GetService , чтобы получить указатель на интерфейс IDirectXVideoMemoryConfiguration . Идентификатор GUID службы MR_VIDEO_ACCELERATION_SERVICE.
- Вызовите IDirectXVideoMemoryConfiguration::GetAvailableSurfaceTypeByIndex в цикле, при этом переменная dwTypeIndex увеличивается с нуля. Остановите работу, когда метод возвращает значение , DXVA2_SurfaceType_DecoderRenderTarget в параметре pdwType . Этот шаг гарантирует, что отрисовщик видео поддерживает аппаратное ускорение декодирования. Этот шаг всегда будет успешным для фильтра EVR.
- Если предыдущий шаг выполнен успешно, вызовите метод IDirectXVideoMemoryConfiguration::SetSurfaceType со значением DXVA2_SurfaceType_DecoderRenderTarget. Вызов Метода 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.
Чтобы предоставить пользовательский распределител, выполните следующие действия.
- Определите класс для примеров мультимедиа. Этот класс может быть производным от класса CMediaSample . Внутри этого класса сделайте следующее:
- Храните указатель на поверхность Direct3D.
- Реализуйте интерфейс IMFGetService . Если в методе GetService guid службы MR_BUFFER_SERVICE, запросите запрошенный интерфейс в области Direct3D. В противном случае GetService может вернуть MF_E_UNSUPPORTED_SERVICE.
- Переопределите метод CMediaSample::GetPointer , чтобы вернуть E_NOTIMPL.
- Определите класс для распределителя. Распределитель может быть производным от класса CBaseAllocator . В этом классе выполните следующие действия.
- Переопределите метод CBaseAllocator::Alloc . Внутри этого метода вызовите IDirectXVideoAccelerationService::CreateSurface , чтобы создать поверхности. (Интерфейс IDirectXVideoDecoderService наследует этот метод от IDirectXVideoAccelerationService.)
- Переопределите метод CBaseAllocator::Free , чтобы освободить поверхности.
- В выходном контакте фильтра переопределите метод CBaseOutputPin::InitAllocator . В этом методе создайте экземпляр пользовательского распределителя.
- В фильтре реализуйте метод 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;
}
Дополнительные сведения о реализации пользовательских распределителей см. в разделе Предоставление пользовательского распределителя документации по пакету SDK DirectShow.
Декодирование
Чтобы создать устройство декодера, вызовите IDirectXVideoDecoderService::CreateVideoDecoder. Метод возвращает указатель на интерфейс IDirectXVideoDecoder устройства декодера.
В каждом кадре вызовите IDirect3DeviceManager9::TestDevice , чтобы протестировать дескриптор устройства. Если устройство изменилось, метод возвращает DXVA2_E_NEW_VIDEO_DEVICE. В этом случае выполните указанные ниже действия.
- Закройте дескриптор устройства, вызвав IDirect3DeviceManager9::CloseDeviceHandle.
- Отпустите указатели IDirectXVideoDecoderService и IDirectXVideoDecoder .
- Откройте новый дескриптор устройства.
- Согласование новой конфигурации декодера, как описано в разделе Поиск конфигурации декодера.
- Создайте устройство декодера.
При условии, что дескриптор устройства является допустимым, процесс декодирования выполняется следующим образом:
- Вызовите метод IDirectXVideoDecoder::BeginFrame.
- Выполните следующие действия один или несколько раз:
- Вызовите метод IDirectXVideoDecoder::GetBuffer , чтобы получить буфер декодера DXVA.
- Заполните буфер.
- Вызовите метод IDirectXVideoDecoder::ReleaseBuffer.
- Вызовите метод 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.
Связанные темы