압축되지 않은 비디오 버퍼

이 문서에서는 압축되지 않은 비디오 프레임이 포함된 미디어 버퍼를 사용하는 방법을 설명합니다. 기본 설정 순서대로 다음 옵션을 사용할 수 있습니다. 모든 미디어 버퍼가 각 옵션을 지원하는 것은 아닙니다.

  1. 기본 Direct3D 표면을 사용합니다. Direct3D 화면에 저장된 비디오 프레임에만 적용됩니다.
  2. IMF2DBuffer 인터페이스를 사용합니다.
  3. IMFMediaBuffer 인터페이스를 사용합니다.

기본 Direct3D Surface 사용

비디오 프레임은 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 서비스를 지원합니다.

IMF2DBuffer 인터페이스 사용

비디오 프레임이 Direct3D 화면 내에 저장되지 않거나 구성 요소가 Direct3D를 사용하도록 설계되지 않은 경우 비디오 프레임에 액세스하는 다음 권장 방법은 IMF2DBuffer 인터페이스에 대한 버퍼를 쿼리하는 것입니다. 이 인터페이스는 이미지 데이터를 위해 특별히 설계되었습니다. 이 인터페이스에 대한 포인터를 얻으려면 미디어 버퍼 에서 QueryInterface 를 호출합니다. 모든 미디어 버퍼 개체가 이 인터페이스를 노출하는 것은 아닙니다. 그러나 미디어 버퍼가 IMF2DBuffer 인터페이스를 노출하는 경우 가능하면 IMFMediaBuffer를 사용하는 대신 해당 인터페이스를 사용하여 데이터에 액세스해야 합니다. IMFMediaBuffer 인터페이스를 계속 사용할 수 있지만 효율성이 떨어질 수 있습니다.

  1. 미디어 버퍼에서 QueryInterface 를 호출하여 IMF2DBuffer 인터페이스를 가져옵니다.
  2. IMF2DBuffer::Lock2D를 호출하여 버퍼의 메모리에 액세스합니다. 이 메서드는 픽셀 맨 위 행의 첫 번째 바이트에 대한 포인터를 반환합니다. 또한 픽셀 행의 시작부터 다음 행의 시작까지의 바이트 수인 이미지 보폭을 반환합니다. 버퍼는 각 픽셀 행 다음에 패딩 바이트를 포함할 수 있으므로 보폭이 이미지 너비(바이트)보다 넓을 수 있습니다. 이미지가 메모리에서 상향식으로 표시되는 경우에도 Stride가 음수일 수 있습니다. 자세한 내용은 Image Stride를 참조하세요.
  3. 메모리에 액세스해야 하는 동안에만 버퍼를 잠근 상태로 유지합니다. IMF2DBuffer::Unlock2D를 호출하여 버퍼의 잠금을 해제합니다.

모든 미디어 버퍼가 IMF2DBuffer 인터페이스를 구현하는 것은 아닙니다. 미디어 버퍼가 이 인터페이스를 구현하지 않는 경우(즉, 버퍼 개체가 1단계에서 E_NOINTERFACE 반환함) 다음에 설명된 IMFMediaBuffer 인터페이스 인터페이스를 사용해야 합니다.

IMFMediaBuffer 인터페이스 사용

미디어 버퍼가 IMF2DBuffer 인터페이스를 노출하지 않는 경우 IMFMediaBuffer 인터페이스를 사용합니다. 이 인터페이스의 일반적인 의미 체계는 미디어 버퍼 작업 항목에 설명되어 있습니다.

  1. 미디어 버퍼에서 QueryInterface 를 호출하여 IMFMediaBuffer 인터페이스를 가져옵니다.
  2. IMFMediaBuffer::Lock을 호출하여 버퍼 메모리에 액세스합니다. 이 메서드는 버퍼 메모리에 대한 포인터를 반환합니다. Lock 메서드를 사용하는 경우 스트라이드는 항상 추가 패딩 바이트 없이 해당 비디오 형식의 최소 보폭입니다.
  3. 메모리에 액세스해야 하는 동안에만 버퍼를 잠근 상태로 유지합니다. IMFMediaBuffer::Unlock를 호출하여 버퍼의 잠금을 해제합니다.

버퍼가 IMF2DBuffer를 노출하는 경우 항상 IMFMediaBuffer::Lock을 호출하지 않아야 합니다. Lock 메서드는 버퍼 개체를 비디오 프레임에 강제로 적용한 다음 다시 연속 메모리 블록으로 되돌릴 수 있기 때문입니다. 반면 버퍼가 IMF2DBuffer를 지원하지 않는 경우 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;
}

애플리케이션에 따라 지정된 미디어 버퍼가 IMF2DBuffer 를 지원하는지 여부를 미리 알 수 있습니다(예: 버퍼를 만든 경우). 그렇지 않으면 두 버퍼 인터페이스 중 하나를 사용할 준비가 되어 있어야 합니다.

다음 예제에서는 이러한 세부 정보 중 일부를 숨기는 도우미 클래스를 보여 줍니다. 이 클래스에는 Lock2D 또는 Lock 을 호출하고 첫 번째 픽셀 행의 시작 부분에 대한 포인터를 반환하는 LockBuffer 메서드가 있습니다. 이 동작은 Lock2D 메서드와 일치합니다. LockBuffer 메서드는 기본 stride를 입력 매개 변수로 사용하고 실제 보폭을 출력 매개 변수로 반환합니다.

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;
};

Image Stride

미디어 버퍼