Nekomprimované vyrovnávací paměti videa
Tento článek popisuje, jak pracovat s vyrovnávacími paměťmi médií, které obsahují nekomprimované snímky videa. V pořadí podle předvoleb jsou k dispozici následující možnosti. Ne každá vyrovnávací paměť médií podporuje každou možnost.
- Použijte podkladovou plochu Direct3D. (Platí jenom pro videorámečky uložené v plochách Direct3D.)
- Použijte rozhraní MMF2DBuffer.
- Použijte rozhraní MMFMediaBuffer.
Použití podkladového zařízení Direct3D Surface
Rámeček videa může být uložený uvnitř povrchu Direct3D. Pokud ano, můžete získat ukazatel na povrch voláním MMFGetService::GetService nebo MFGetService na objekt vyrovnávací paměti médií. Použijte identifikátor služby MR_BUFFER_SERVICE. Tento přístup se doporučuje, když je komponenta přistupující k rámečku videa navržená tak, aby používala Direct3D. Tento přístup by měl použít například dekodér videa podporující akceleraci videa DirectX.
Následující kód ukazuje, jak získat IDirect3DSurface9 ukazatel z vyrovnávací paměti média.
IDirect3DSurface9 *pSurface = NULL;
hr = MFGetService(
pBuffer,
MR_BUFFER_SERVICE,
__uuidof(IDirect3DSurface9),
(void**)&pSurface
);
if (SUCCEEDED(hr))
{
// Call IDirect3DSurface9 methods.
}
Následující objekty podporují službu MR_BUFFER_SERVICE:
- vyrovnávací paměti zařízení DirectX
- ukázky videa
Použití rozhraní MMF2DBuffer
Pokud není rámeček videa uložený uvnitř povrchu Direct3D nebo komponenta není navržená pro použití Direct3D, dalším doporučeným způsobem přístupu k rámečku videa je dotazování vyrovnávací paměti pro MMF2DBuffer rozhraní. Toto rozhraní je navržené speciálně pro data obrázků. Pokud chcete získat ukazatel na toto rozhraní, zavolejte QueryInterface v vyrovnávací paměti média. Toto rozhraní nezpřístupňují všechny objekty vyrovnávací paměti médií. Pokud však vyrovnávací paměť médií zveřejňuje MMF2DBuffer rozhraní, měli byste toto rozhraní použít pro přístup k datům, pokud je to možné, místo použití MMFMediaBuffer. Stále můžete použít MMFMediaBuffer rozhraní, ale může být méně efektivní.
- Volání QueryInterface na vyrovnávací paměti médií získat MMF2DBuffer rozhraní.
- Volejte MMF2DBuffer::Lock2D pro přístup k paměti pro vyrovnávací paměť. Tato metoda vrátí ukazatel na první bajt horního řádku pixelů. Vrátí také krok obrázku, což je počet bajtů od začátku řádku pixelů až po začátek dalšího řádku. Vyrovnávací paměť může za každým řádkem pixelů obsahovat odsazení bajtů, takže krok může být širší než šířka obrázku v bajtech. Stride může být také negativní, pokud je obrázek orientovaný směrem dolů v paměti. Další informace naleznete v tématu Image Stride.
- Zamkněte vyrovnávací paměť jenom v případě, že potřebujete přístup k paměti. Odemkněte vyrovnávací paměť voláním MMF2DBuffer::Unlock2D.
Ne každá vyrovnávací paměť médií implementuje MMF2DBuffer rozhraní. Pokud vyrovnávací paměť médií neimplementuje toto rozhraní (tj. objekt vyrovnávací paměti vrátí E_NOINTERFACE v kroku 1), musíte použít rozhraní rozhraní MMFMediaBuffer rozhraní popsané dále.
Použití rozhraní MMFMediaBuffer
Pokud vyrovnávací paměť médií nezpřístupňuje MMF2DBuffer rozhraní, použijte MMFMediaBuffer rozhraní. Obecná sémantika tohoto rozhraní jsou popsána v tématu Práce s vyrovnávacími paměťmi médií.
- Voláním QueryInterface na vyrovnávací paměti médií získáte rozhraní MMFMediaBuffer.
- Volání MMFMediaBuffer::Lock pro přístup k paměti vyrovnávací paměti. Tato metoda vrátí ukazatel na paměť vyrovnávací paměti. Při použití metody Lock je krok vždy minimální krok pro příslušný formát videa bez nadbytečných odsazení bajtů.
- Zamkněte vyrovnávací paměť jenom v případě, že potřebujete přístup k paměti. Odemkněte vyrovnávací paměť voláním MMFMediaBuffer::Unlock.
Vždy byste se měli vyhnout volání MMFMediaBuffer::Lock pokud vyrovnávací paměť zveřejňuje MMF2DBuffer, protože Lock metoda může vynutit objekt vyrovnávací paměti do souvislého bloku paměti a pak znovu. Na druhou stranu, pokud vyrovnávací paměť nepodporuje MMF2DBuffer, pak MMFMediaBuffer::Lock pravděpodobně nebude mít za následek kopii.
Minimální krok od typu média vypočítejte následujícím způsobem:
- Minimální krok může být uložen v atributu MF_MT_DEFAULT_STRIDE.
- Pokud není atribut MF_MT_DEFAULT_STRIDE nastaven, zavolejte funkci MFGetStrideForBitmapInfoHeader k výpočtu kroku pro nejběžnější formáty videa.
- Pokud funkce MFGetStrideForBitmapInfoHeader nerozpozná formát, je nutné vypočítat krok na základě definice formátu. V takovém případě neexistuje žádné obecné pravidlo; potřebujete znát podrobnosti definice formátu.
Následující kód ukazuje, jak získat výchozí krok pro nejběžnější formáty videa.
HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride)
{
LONG lStride = 0;
// Try to get the default stride from the media type.
HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride);
if (FAILED(hr))
{
// Attribute not set. Try to calculate the default stride.
GUID subtype = GUID_NULL;
UINT32 width = 0;
UINT32 height = 0;
// Get the subtype and the image size.
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
if (FAILED(hr))
{
goto done;
}
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr))
{
goto done;
}
hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride);
if (FAILED(hr))
{
goto done;
}
// Set the attribute for later reference.
(void)pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride));
}
if (SUCCEEDED(hr))
{
*plStride = lStride;
}
done:
return hr;
}
V závislosti na vaší aplikaci můžete předem vědět, jestli daná vyrovnávací paměť médií podporuje MMF2DBuffer (například pokud jste vytvořili vyrovnávací paměť). Jinak musíte být připraveni použít některé ze dvou rozhraní vyrovnávací paměti.
Následující příklad ukazuje pomocnou třídu, která skryje některé z těchto podrobností. Tato třída má LockBuffer metoda, která volá Lock2D nebo Lock a vrací ukazatel na začátek prvního řádku pixelů. (Toto chování odpovídá metodě Lock2D.) LockBuffer metoda přebírá výchozí krok jako vstupní parametr a vrátí skutečný krok jako výstupní parametr.
class CBufferLock
{
public:
CBufferLock(IMFMediaBuffer *pBuffer) : m_p2DBuffer(NULL), m_bLocked(FALSE)
{
m_pBuffer = pBuffer;
m_pBuffer->AddRef();
m_pBuffer->QueryInterface(IID_IMF2DBuffer, (void**)&m_p2DBuffer);
}
~CBufferLock()
{
UnlockBuffer();
SafeRelease(&m_pBuffer);
SafeRelease(&m_p2DBuffer);
}
HRESULT LockBuffer(
LONG lDefaultStride, // Minimum stride (with no padding).
DWORD dwHeightInPixels, // Height of the image, in pixels.
BYTE **ppbScanLine0, // Receives a pointer to the start of scan line 0.
LONG *plStride // Receives the actual stride.
)
{
HRESULT hr = S_OK;
// Use the 2-D version if available.
if (m_p2DBuffer)
{
hr = m_p2DBuffer->Lock2D(ppbScanLine0, plStride);
}
else
{
// Use non-2D version.
BYTE *pData = NULL;
hr = m_pBuffer->Lock(&pData, NULL, NULL);
if (SUCCEEDED(hr))
{
*plStride = lDefaultStride;
if (lDefaultStride < 0)
{
// Bottom-up orientation. Return a pointer to the start of the
// last row *in memory* which is the top row of the image.
*ppbScanLine0 = pData + abs(lDefaultStride) * (dwHeightInPixels - 1);
}
else
{
// Top-down orientation. Return a pointer to the start of the
// buffer.
*ppbScanLine0 = pData;
}
}
}
m_bLocked = (SUCCEEDED(hr));
return hr;
}
void UnlockBuffer()
{
if (m_bLocked)
{
if (m_p2DBuffer)
{
(void)m_p2DBuffer->Unlock2D();
}
else
{
(void)m_pBuffer->Unlock();
}
m_bLocked = FALSE;
}
}
private:
IMFMediaBuffer *m_pBuffer;
IMF2DBuffer *m_p2DBuffer;
BOOL m_bLocked;
};
Související témata