Buffers de vídeo não compactados
Este artigo descreve como trabalhar com buffers de mídia que contêm quadros de vídeo não compactados. Por ordem de preferência, estão disponíveis as seguintes opções. Nem todo buffer de mídia suporta cada opção.
- Use a superfície Direct3D subjacente. (Aplica-se apenas a quadros de vídeo armazenados em superfícies Direct3D.)
- Use o IMF2DBuffer interface.
- Use o interface IMFMediaBuffer.
Usar a superfície Direct3D subjacente
O quadro de vídeo pode ser armazenado dentro de uma superfície Direct3D. Em caso afirmativo, você pode obter um ponteiro para a superfície chamando IMFGetService::GetService ou MFGetService no objeto de buffer de mídia. Use o identificador de serviço MR_BUFFER_SERVICE. Essa abordagem é recomendada quando o componente que acessa o quadro de vídeo é projetado para usar Direct3D. Por exemplo, um decodificador de vídeo que suporte DirectX Video Acceleration deve usar essa abordagem.
O código a seguir mostra como obter o ponteiro IDirect3DSurface9 de um buffer de mídia.
IDirect3DSurface9 *pSurface = NULL;
hr = MFGetService(
pBuffer,
MR_BUFFER_SERVICE,
__uuidof(IDirect3DSurface9),
(void**)&pSurface
);
if (SUCCEEDED(hr))
{
// Call IDirect3DSurface9 methods.
}
Os seguintes objetos suportam o serviço MR_BUFFER_SERVICE:
- de buffer de superfície do DirectX
- Amostras de vídeo
Usar a interface IMF2DBuffer
Se o quadro de vídeo não estiver armazenado dentro de uma superfície Direct3D ou se o componente não for projetado para usar Direct3D, a próxima maneira recomendada de acessar o quadro de vídeo é consultar o buffer para a interfaceIMF2DBuffer. Esta interface é projetada especificamente para dados de imagem. Para obter um ponteiro para essa interface, chame QueryInterface no buffer de mídia. Nem todos os objetos de buffer de mídia expõem essa interface. Mas se um buffer de mídia expor a interface IMF2DBuffer, você deverá usar essa interface para acessar os dados, se possível, em vez de usar IMFMediaBuffer. Você ainda pode usar o IMFMediaBuffer interface, mas pode ser menos eficiente.
- Chame QueryInterface no buffer de mídia para obter o interface IMF2DBuffer.
- Chame IMF2DBuffer::Lock2D para acessar a memória do buffer. Esse método retorna um ponteiro para o primeiro byte da linha superior de pixels. Ele também retorna a passada da imagem, que é o número de bytes do início de uma linha de pixels até o início da próxima linha. O buffer pode conter bytes de preenchimento após cada linha de pixels, portanto, a passada pode ser maior do que a largura da imagem em bytes. Stride também pode ser negativo se a imagem estiver orientada de baixo para cima na memória. Para obter mais informações, consulte Image Stride.
- Mantenha o buffer bloqueado apenas enquanto você precisa acessar a memória. Desbloqueie o buffer chamando IMF2DBuffer::Unlock2D.
Nem todo buffer de mídia implementa o interface IMF2DBuffer. Se o buffer de mídia não implementar essa interface (ou seja, o objeto buffer retornará E_NOINTERFACE na etapa 1), você deverá usar o IMFMediaBuffer interface de interface, descrita a seguir.
Usar a interface IMFMediaBuffer
Se um buffer de mídia não expor a interfaceIMF2DBuffer, use a interface IMFMediaBuffer. A semântica geral dessa interface é descrita no tópico Trabalhando com buffers de mídia.
- Chame QueryInterface no buffer de mídia para obter a interfaceIMFMediaBuffer.
- Chame IMFMediaBuffer::Lock para acessar a memória do buffer. Esse método retorna um ponteiro para a memória buffer. Quando o método Lock é usado, a passada é sempre a passada mínima para o formato de vídeo em questão, sem bytes de preenchimento extras.
- Mantenha o buffer bloqueado apenas enquanto você precisa acessar a memória. Desbloqueie o buffer chamando IMFMediaBuffer::Unlock.
Você deve sempre evitar chamar IMFMediaBuffer::Lock se o buffer expõe IMF2DBuffer, porque o método Lock pode forçar o objeto buffer para o quadro de vídeo em um bloco de memória contíguo e depois de volta. Por outro lado, se o buffer não suportar IMF2DBuffer, então IMFMediaBuffer::Lock provavelmente não resultará em uma cópia.
Calcule a passada mínima a partir do tipo de mídia da seguinte maneira:
- A passada mínima pode ser armazenada no atributo MF_MT_DEFAULT_STRIDE.
- Se o atributo MF_MT_DEFAULT_STRIDE não estiver definido, chame a função MFGetStrideForBitmapInfoHeader para calcular a passada para os formatos de vídeo mais comuns.
- Se a função deMFGetStrideForBitmapInfoHeader donão reconhecer o formato, você deverá calcular a passada com base na definição do formato. Nesse caso, não existe uma regra geral; Você precisa saber os detalhes da definição de formato.
O código a seguir mostra como obter a passada padrão para os formatos de vídeo mais comuns.
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;
}
Dependendo do seu aplicativo, você pode saber com antecedência se um determinado buffer de mídia suporta IMF2DBuffer (por exemplo, se você criou o buffer). Caso contrário, você deve estar preparado para usar qualquer uma das duas interfaces de buffer.
O exemplo a seguir mostra uma classe auxiliar que oculta alguns desses detalhes. Essa classe tem um método LockBuffer que chama Lock2D ou Lock e retorna um ponteiro para o início da primeira linha de pixels. (Esse comportamento corresponde ao método Lock2D.) O método LockBuffer usa a passada padrão como um parâmetro de entrada e retorna a passada real como um parâmetro de saída.
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;
};
Tópicos relacionados