Buffer video non compressi

Questo articolo descrive come usare i buffer multimediali che contengono fotogrammi video non compressi. In ordine di preferenza, sono disponibili le opzioni seguenti. Non ogni buffer multimediale supporta ogni opzione.

  1. Usare la superficie Direct3D sottostante. Si applica solo ai fotogrammi video archiviati nelle superfici Direct3D.
  2. Usare l'interfaccia IMF2DBuffer .
  3. Usare l'interfaccia IMFMediaBuffer .

Usare la superficie Direct3D sottostante

Il frame video potrebbe essere archiviato all'interno di una superficie Direct3D. In tal caso, è possibile ottenere un puntatore alla superficie chiamando FMGetService::GetService o MFGetService nell'oggetto buffer multimediale. Usare l'identificatore del servizio MR_BUFFER_SERVICE. Questo approccio è consigliato quando il componente che accede al fotogramma video è progettato per l'uso di Direct3D. Ad esempio, un decodificatore video che supporta l'accelerazione video DirectX deve usare questo approccio.

Il codice seguente illustra come ottenere il puntatore IDirect3DSurface9 da un buffer multimediale.

IDirect3DSurface9 *pSurface = NULL;

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

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

Gli oggetti seguenti supportano il servizio di MR_BUFFER_SERVICE :

Usare l'interfaccia IMF2DBuffer

Se il frame video non viene archiviato all'interno di una superficie Direct3D o il componente non è progettato per l'uso di Direct3D, il modo consigliato successivo per accedere al frame video consiste nell'eseguire query sul buffer per l'interfaccia FMI2DBuffer . Questa interfaccia è progettata in modo specifico per i dati dell'immagine. Per ottenere un puntatore a questa interfaccia, chiamare QueryInterface nel buffer multimediale. Non tutti gli oggetti buffer multimediali espongono questa interfaccia. Tuttavia, se un buffer multimediale espone l'interfaccia FM2DBuffer , è consigliabile usare tale interfaccia per accedere ai dati, se possibile, anziché usare IMFMediaBuffer. È comunque possibile usare l'interfaccia IMFMediaBuffer , ma potrebbe essere meno efficiente.

  1. Chiamare QueryInterface nel buffer multimediale per ottenere l'interfaccia FMI2DBuffer .
  2. Chiamare FMI2DBuffer::Lock2D per accedere alla memoria per il buffer. Questo metodo restituisce un puntatore al primo byte della riga superiore di pixel. Restituisce anche lo stride dell'immagine, ovvero il numero di byte dall'inizio di una riga di pixel all'inizio della riga successiva. Il buffer potrebbe contenere byte di spaziatura interna dopo ogni riga di pixel, pertanto lo stride potrebbe essere più ampio rispetto alla larghezza dell'immagine in byte. Stride può anche essere negativo se l'immagine è orientata in basso in memoria. Per altre informazioni, vedere Image Stride.
  3. Mantenere bloccato il buffer solo quando è necessario accedere alla memoria. Sbloccare il buffer chiamando FMI2DBuffer::Unlock2D.

Non tutti i buffer multimediali implementano l'interfaccia IMF2DBuffer . Se il buffer multimediale non implementa questa interfaccia, ovvero l'oggetto buffer restituisce E_NOINTERFACE nel passaggio 1, è necessario usare l'interfaccia dell'interfaccia FMMediaBuffer , descritta di seguito.

Usare l'interfaccia IMFMediaBuffer

Se un buffer multimediale non espone l'interfaccia IMF2DBuffer , usare l'interfaccia FMIMediaBuffer . La semantica generale di questa interfaccia è descritta nell'argomento Uso dei buffer multimediali.

  1. Chiamare QueryInterface nel buffer multimediale per ottenere l'interfaccia IMFMediaBuffer .
  2. Chiamare FMIMediaBuffer::Lock per accedere alla memoria del buffer. Questo metodo restituisce un puntatore alla memoria del buffer. Quando viene usato il metodo Lock , lo stride è sempre lo stride minimo per il formato video in questione, senza byte di riempimento aggiuntivo.
  3. Mantenere bloccato il buffer solo quando è necessario accedere alla memoria. Sbloccare il buffer chiamando FMMediaBuffer::Unlock.

È sempre consigliabile evitare di chiamare FMMediaBuffer::Lock se il buffer espone FM2DBuffer, perché il metodo Lock può forzare l'oggetto buffer al fotogramma video in un blocco di memoria contiguo e tornare nuovamente. D'altra parte, se il buffer non supporta FMI2DBuffer, FMIMediaBuffer::Lock probabilmente non genera una copia.

Calcolare lo stride minimo dal tipo di supporto come indicato di seguito:

  • Lo stride minimo può essere archiviato nell'attributo MF_MT_DEFAULT_STRIDE .
  • Se l'attributo MF_MT_DEFAULT_STRIDE non è impostato, chiamare la funzione MFGetStrideForBitmapInfoHeader per calcolare lo stride per i formati video più comuni.
  • Se la funzione MFGetStrideForBitmapInfoHeader non riconosce il formato, è necessario calcolare lo stride in base alla definizione del formato. In questo caso, non esiste alcuna regola generale; è necessario conoscere i dettagli della definizione del formato.

Il codice seguente illustra come ottenere lo stride predefinito per i formati video più comuni.

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

A seconda dell'applicazione, è possibile sapere in anticipo se un determinato buffer multimediale supporta FM2DBuffer , ad esempio se è stato creato il buffer. In caso contrario, è necessario essere pronti a usare una delle due interfacce del buffer.

Nell'esempio seguente viene illustrata una classe helper che nasconde alcuni di questi dettagli. Questa classe ha un metodo LockBuffer che chiama Lock2D o Lock e restituisce un puntatore all'inizio della prima riga di pixel. Questo comportamento corrisponde al metodo Lock2D . Il metodo LockBuffer accetta lo stride predefinito come parametro di input e restituisce lo stride effettivo come parametro di 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;
};

Immagine Stride

Buffer multimediali