Buffer Video Tidak Dikompresi

Artikel ini menjelaskan cara bekerja dengan buffer media yang berisi bingkai video yang tidak dikompresi. Dalam urutan preferensi, opsi berikut tersedia. Tidak setiap buffer media mendukung setiap opsi.

  1. Gunakan permukaan Direct3D yang mendasar. (Hanya berlaku untuk bingkai video yang disimpan di permukaan Direct3D.)
  2. Gunakan antarmuka IMF2DBuffer .
  3. Gunakan antarmuka IMFMediaBuffer .

Menggunakan Permukaan Direct3D yang Mendasar

Bingkai video mungkin disimpan di dalam permukaan Direct3D. Jika demikian, Anda bisa mendapatkan pointer ke permukaan dengan memanggil IMFGetService::GetService atau MFGetService pada objek buffer media. Gunakan MR_BUFFER_SERVICE pengidentifikasi layanan. Pendekatan ini direkomendasikan ketika komponen yang mengakses bingkai video dirancang untuk menggunakan Direct3D. Misalnya, dekoder video yang mendukung DirectX Video Acceleration harus menggunakan pendekatan ini.

Kode berikut menunjukkan cara mendapatkan penunjuk IDirect3DSurface9 dari buffer media.

IDirect3DSurface9 *pSurface = NULL;

hr = MFGetService(
    pBuffer, 
    MR_BUFFER_SERVICE,
    __uuidof(IDirect3DSurface9), 
    (void**)&pSurface
    );

if (SUCCEEDED(hr))
{
    // Call IDirect3DSurface9 methods.
}

Objek berikut mendukung layanan MR_BUFFER_SERVICE :

Menggunakan Antarmuka IMF2DBuffer

Jika bingkai video tidak disimpan di dalam permukaan Direct3D, atau komponen tidak dirancang untuk menggunakan Direct3D, cara berikutnya yang disarankan untuk mengakses bingkai video adalah dengan mengkueri buffer untuk antarmuka IMF2DBuffer . Antarmuka ini dirancang khusus untuk data gambar. Untuk mendapatkan penunjuk ke antarmuka ini, panggil QueryInterface pada buffer media. Tidak semua objek buffer media mengekspos antarmuka ini. Tetapi jika buffer media mengekspos antarmuka IMF2DBuffer , Anda harus menggunakan antarmuka tersebut untuk mengakses data, jika memungkinkan, daripada menggunakan IMFMediaBuffer. Anda masih dapat menggunakan antarmuka IMFMediaBuffer , tetapi mungkin kurang efisien.

  1. Panggil QueryInterface pada buffer media untuk mendapatkan antarmuka IMF2DBuffer .
  2. Panggil IMF2DBuffer::Lock2D untuk mengakses memori untuk buffer. Metode ini mengembalikan penunjuk ke byte pertama dari baris atas piksel. Ini juga mengembalikan langkah gambar, yang merupakan jumlah byte dari awal baris piksel ke awal baris berikutnya. Buffer mungkin berisi byte padding setelah setiap baris piksel, sehingga langkahnya mungkin lebih lebar dari lebar gambar dalam byte. Langkah juga bisa negatif jika gambar berorientasi bawah ke atas dalam memori. Untuk informasi selengkapnya, lihat Image Stride.
  3. Jaga agar buffer tetap terkunci hanya saat Anda perlu mengakses memori. Buka kunci buffer dengan memanggil IMF2DBuffer::Unlock2D.

Tidak setiap buffer media mengimplementasikan antarmuka IMF2DBuffer . Jika buffer media tidak mengimplementasikan antarmuka ini (yaitu, objek buffer mengembalikan E_NOINTERFACE di langkah 1), Anda harus menggunakan antarmuka antarmuka IMFMediaBuffer , yang dijelaskan berikutnya.

Menggunakan Antarmuka IMFMediaBuffer

Jika buffer media tidak mengekspos antarmuka IMF2DBuffer , gunakan antarmuka IMFMediaBuffer . Semantik umum antarmuka ini dijelaskan dalam topik Bekerja dengan Buffer Media.

  1. Panggil QueryInterface pada buffer media untuk mendapatkan antarmuka IMFMediaBuffer .
  2. Panggil IMFMediaBuffer::Lock untuk mengakses memori buffer. Metode ini mengembalikan pointer ke memori buffer. Ketika metode Kunci digunakan, langkah selalu merupakan langkah minimum untuk format video yang dimaksud, tanpa byte padding tambahan.
  3. Jaga agar buffer tetap terkunci hanya saat Anda perlu mengakses memori. Buka kunci buffer dengan memanggil IMFMediaBuffer::Unlock.

Anda harus selalu menghindari panggilan IMFMediaBuffer::Lock jika buffer mengekspos IMF2DBuffer, karena metode Lock dapat memaksa objek buffer ke bingkai video ke blok memori yang berdekatan dan kemudian kembali lagi. Di sisi lain, jika buffer tidak mendukung IMF2DBuffer, maka IMFMediaBuffer::Lock mungkin tidak akan menghasilkan salinan.

Hitung langkah minimum dari jenis media sebagai berikut:

  • Langkah minimum mungkin disimpan dalam atribut MF_MT_DEFAULT_STRIDE .
  • Jika atribut MF_MT_DEFAULT_STRIDE tidak diatur, panggil fungsi MFGetStrideForBitmapInfoHeader untuk menghitung langkah untuk format video yang paling umum.
  • Jika fungsi MFGetStrideForBitmapInfoHeader tidak mengenali format, Anda harus menghitung langkah berdasarkan definisi format. Dalam hal ini, tidak ada aturan umum; Anda perlu mengetahui detail definisi format.

Kode berikut menunjukkan cara mendapatkan langkah default untuk format video yang paling umum.

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

Bergantung pada aplikasi Anda, Anda mungkin tahu terlebih dahulu apakah buffer media tertentu mendukung IMF2DBuffer (misalnya, jika Anda membuat buffer). Jika tidak, Anda harus siap menggunakan salah satu dari dua antarmuka buffer.

Contoh berikut menunjukkan kelas pembantu yang menyembunyikan beberapa detail ini. Kelas ini memiliki metode LockBuffer yang memanggil Lock2D atau Lock dan mengembalikan pointer ke awal baris pertama piksel. (Perilaku ini cocok dengan metode Lock2D .) Metode LockBuffer mengambil langkah default sebagai parameter input dan mengembalikan langkah aktual sebagai parameter output.

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

Langkah Gambar

Buffer Media