Несжатые буферы видео
В этой статье описывается, как работать с буферами мультимедиа, содержащими несжатые видеокадры. В порядке предпочтения доступны следующие параметры. Не каждый буфер мультимедиа поддерживает каждый параметр.
- Используйте базовую поверхность Direct3D. (Применяется только к видеокадрам, хранящимся на поверхностях Direct3D.)
- Используйте интерфейсМВФ2DBuffer.
- Используйте интерфейс IMFMediaBuffer.
Использование базовой поверхности Direct3D
Видеокадр может храниться в поверхности Direct3D. В этом случае можно получить указатель на поверхность, вызвав IMFGetService::GetService или MFGetService в объекте буфера мультимедиа. Используйте идентификатор службы MR_BUFFER_SERVICE. Этот подход рекомендуется, если компонент, обращаюющийся к видеокадру, предназначен для использования Direct3D. Например, декодировщик видео, поддерживающий ускорение видео DirectX, должен использовать этот подход.
В следующем коде показано, как получить указатель IDirect3DSurface9 из буфера мультимедиа.
IDirect3DSurface9 *pSurface = NULL;
hr = MFGetService(
pBuffer,
MR_BUFFER_SERVICE,
__uuidof(IDirect3DSurface9),
(void**)&pSurface
);
if (SUCCEEDED(hr))
{
// Call IDirect3DSurface9 methods.
}
Следующие объекты поддерживают службу MR_BUFFER_SERVICE:
Использование интерфейса МВФ2DBuffer
Если видеокадр не хранится внутри поверхности Direct3D или компонент не предназначен для использования Direct3D, следующий рекомендуемый способ доступа к кадру видео — запрашивать буфер для интерфейса МВФ2DBuffer. Этот интерфейс предназначен специально для данных изображения. Чтобы получить указатель на этот интерфейс, вызовите QueryInterface в буфере мультимедиа. Не все объекты буфера мультимедиа предоставляют этот интерфейс. Но если буфер мультимедиа предоставляет интерфейс МВФ2DBuffer, этот интерфейс следует использовать для доступа к данным, если это возможно, а не с помощью МВФMediaBuffer. Вы по-прежнему можете использовать интерфейс IMFMediaBuffer, но он может быть менее эффективным.
- Вызовите QueryInterface в буфере мультимедиа, чтобы получить интерфейсМВФ2DBuffer.
- Вызовите МВФ2DBuffer::Lock2D для доступа к памяти буфера. Этот метод возвращает указатель на первый байт верхней строки пикселей. Он также возвращает шаг изображения, который является числом байтов от начала строки пикселей до начала следующей строки. Буфер может содержать байты с заполнением после каждой строки пикселей, поэтому шаг может быть шире ширины изображения в байтах. Шаг также может быть отрицательным, если изображение ориентировано внизу в памяти. Дополнительные сведения см. в шаге изображения.
- Сохраняйте блокировку буфера только в то время как необходимо получить доступ к памяти. Разблокируйте буфер, вызвав МВФ2DBuffer::Unlock2D.
Не каждый буфер мультимедиа реализует интерфейс МВФ2DBuffer. Если буфер мультимедиа не реализует этот интерфейс (т. е. объект буфера возвращает E_NOINTERFACE на шаге 1), необходимо использовать интерфейс интерфейса IMFMediaBuffer, описанный далее.
Использование интерфейса МВФMediaBuffer
Если буфер мультимедиа не предоставляет интерфейс МВФ2DBuffer, используйте интерфейс IMFMediaBuffer. Общие семантики этого интерфейса описаны в разделе Работа с буферами мультимедиа.
- Вызовите QueryInterface в буфере мультимедиа, чтобы получить интерфейсМВФMediaBuffer.
- Вызовите IMFMediaBuffer::Lock для доступа к буферной памяти. Этот метод возвращает указатель на буферную память. При использовании метода блокировки шаг всегда является минимальным шагом для видеоформата без дополнительных байтов.
- Сохраняйте блокировку буфера только в то время как необходимо получить доступ к памяти. Разблокируйте буфер, вызвав IMFMediaBuffer::Разблокировка.
Всегда следует избегать вызова IMFMediaBuffer::Lock, если буфер предоставляет МВФ2DBuffer, так как метод Lock может принудительно заставить буферный объект к видеокадру в смежный блок памяти, а затем снова вернуться. С другой стороны, если буфер не поддерживает МВФ2DBuffer, то IMFMediaBuffer::Lock, вероятно, не приведет к копированию.
Вычислите минимальный шаг из типа носителя следующим образом:
- Минимальный шаг может храниться в атрибуте MF_MT_DEFAULT_STRIDE.
- Если атрибут MF_MT_DEFAULT_STRIDE не задан, вызов ите функцию MFGetStrideForBitmapInfoHeader, чтобы вычислить шаг для наиболее распространенных форматов видео.
- Если функцияMFGetStrideForBitmapInfoHeader не распознает формат, необходимо вычислить шаг на основе определения формата. В этом случае общего правила нет; Необходимо знать сведения о определении формата.
В следующем коде показано, как получить шаг по умолчанию для наиболее распространенных форматов видео.
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;
}
В зависимости от приложения вы можете заранее знать, поддерживает ли данный буфер мультимедиа МВФ2DBuffer (например, если вы создали буфер). В противном случае необходимо подготовиться к использованию одного из двух интерфейсов буфера.
В следующем примере показан вспомогательный класс, который скрывает некоторые из этих сведений. Этот класс имеет метод LockBuffer, который вызывает Lock2D или Lock и возвращает указатель на начало первой строки пикселей. (Это поведение соответствует методу Lock2D.) Метод LockBuffer принимает шаг по умолчанию в качестве входного параметра и возвращает фактический шаг в качестве выходного параметра.
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;
};
Связанные разделы