Condividi tramite


Passaggio 3: Implementare la funzione Frame-Grabbing

[La funzionalità associata a questa pagina, DirectShow, è una funzionalità legacy. È stata sostituita da MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation. Queste funzionalità sono state ottimizzate per Windows 10 e Windows 11. Microsoft consiglia vivamente che il nuovo codice usi MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation invece di DirectShow, quando possibile. Microsoft suggerisce che il codice esistente che usa le API legacy venga riscritto per usare le nuove API, se possibile.

[Questa API non è supportata e potrebbe essere modificata o non disponibile in futuro.]

Questo argomento è il passaggio 3 di afferrare un frame poster.

Il passaggio successivo consiste nell'implementare la funzione GetBitmap, che usa Media Detector per afferrare un frame poster. All'interno di questa funzione, seguire questa procedura:

  1. Creare il rilevatore di contenuti multimediali.
  2. Specificare un file multimediale.
  3. Esaminare ogni flusso nel file. Se si tratta di un flusso video, ottenere le dimensioni native del video.
  4. Ottenere un frame poster, specificando il tempo di ricerca e la dimensione di destinazione.

Creare l'oggetto Media Detector chiamando CoCreateInstance:

CComPtr<IMediaDet> pDet;
hr = pDet.CoCreateInstance(__uuidof(MediaDet));

Specificare un nome file usando il metodo IMediaDet::p ut_Filename . Questo metodo accetta un parametro BSTR .

hr = pDet->put_Filename(bstrFilename);

Ottenere il numero di flussi e scorrere ogni flusso controllando il tipo di supporto. Il metodo IMediaDet::get_OutputStreams recupera il numero di flussi e il metodo IMediaDet::p ut_CurrentStream specifica il flusso da esaminare. Chiudere il ciclo nel primo flusso video.

long lStreams;
bool bFound = false;
hr = pDet->get_OutputStreams(&lStreams);
for (long i = 0; i < lStreams; i++)
{
    GUID major_type;
    hr = pDet->put_CurrentStream(i);
    hr = pDet->get_StreamType(&major_type);
    if (major_type == MEDIATYPE_Video)  // Found a video stream.
    {
        bFound = true;
        break;
    }
}
if (!bFound) return VFW_E_INVALIDMEDIATYPE;

Se non è stato trovato alcun flusso video, la funzione viene chiusa.

Nel codice precedente il metodo IMediaDet::get_StreamType restituisce solo il GUID di tipo principale. Ciò è utile se non è necessario esaminare il tipo di supporto completo. Per ottenere le dimensioni del video, tuttavia, è necessario esaminare il blocco di formato, quindi è necessario il tipo di supporto completo. È possibile recuperarlo chiamando il metodo IMediaDet::get_StreamMediaType , che compila una struttura AM_MEDIA_TYPE . Media Detector converte tutti i flussi video in formato non compresso, con un blocco di formato VIDEOINFOHEADER .

long width = 0, height = 0; 
AM_MEDIA_TYPE mt;
hr = pDet->get_StreamMediaType(&mt);
if (mt.formattype == FORMAT_VideoInfo) 
{
    VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
    width = pVih->bmiHeader.biWidth;
    height = pVih->bmiHeader.biHeight;
    
    // We want the absolute height, and don't care about up-down orientation.
    if (height < 0) height *= -1;
}
else {
    return VFW_E_INVALIDMEDIATYPE; // This should not happen, in theory.
}
FreeMediaType(mt);

Il metodo get_StreamMediaType alloca il blocco di formato, che il chiamante deve liberare. In questo esempio viene utilizzata la funzione FreeMediaType dalla libreria di classi di base.

Ora si è pronti per ottenere la cornice del poster. Chiamare innanzitutto il metodo IMediaDet::GetBitmapBits con un puntatore NULL per il buffer:

long lSize;
hr = pDet->GetBitmapBits(0, &lSize, NULL, width, height);

Questa chiamata restituisce le dimensioni del buffer necessarie nel parametro lSize . Il primo parametro specifica il tempo di flusso a cui eseguire la ricerca; in questo esempio viene usato l'ora zero. Per la larghezza e l'altezza, in questo esempio vengono utilizzate le dimensioni video native ottenute in precedenza. Se si specificano altri valori, Rilevamento supporti estenderà la bitmap in modo che corrisponda.

Se il metodo ha esito positivo, allocare nuovamente il buffer e chiamare GetBitmapBits :

if (SUCCEEDED(hr)) 
{
    char *pBuffer = new char[lSize];
    if (!pBuffer) return E_OUTOFMEMORY;
    hr = pDet->GetBitmapBits(0, NULL, pBuffer, width, height);
    if (FAILED(hr))
        delete [] pBuffer;
}

Ecco la funzione completa, con una migliore gestione degli errori.

HRESULT GetBitmap(LPTSTR pszFileName, BITMAPINFOHEADER** ppbmih)
{
    HRESULT hr;
    CComPtr<IMediaDet> pDet;
    hr = pDet.CoCreateInstance(__uuidof(MediaDet));
    if (FAILED(hr)) return hr;

    // Convert the file name to a BSTR.
    CComBSTR bstrFilename(pszFileName);
    hr = pDet->put_Filename(bstrFilename);
    if (FAILED(hr)) return hr;

    long lStreams;
    bool bFound = false;
    hr = pDet->get_OutputStreams(&lStreams);
    if (FAILED(hr)) return hr;

    for (long i = 0; i < lStreams; i++)
    {
        GUID major_type;
        hr = pDet->put_CurrentStream(i);
        if (SUCCEEDED(hr))
        {
            hr = pDet->get_StreamType(&major_type);
        }
        if (FAILED(hr)) break;

        if (major_type == MEDIATYPE_Video)
        {
            bFound = true;
            break;
        }
    }
    if (!bFound) return VFW_E_INVALIDMEDIATYPE;

    long width = 0, height = 0; 
    AM_MEDIA_TYPE mt;
    hr = pDet->get_StreamMediaType(&mt);
    if (SUCCEEDED(hr)) 
    {
        if ((mt.formattype == FORMAT_VideoInfo) && 
            (mt.cbFormat >= sizeof(VIDEOINFOHEADER)))
        {
            VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
            width = pVih->bmiHeader.biWidth;
            height = pVih->bmiHeader.biHeight;
        
            // We want the absolute height, don't care about orientation.
            if (height < 0) height *= -1;
        }
        else
        {
            hr = VFW_E_INVALIDMEDIATYPE; // Should not happen, in theory.
        }
        FreeMediaType(mt);
    }
    if (FAILED(hr)) return hr;
    
    // Find the required buffer size.
    long size;
    hr = pDet->GetBitmapBits(0, &size, NULL, width, height);
    if (SUCCEEDED(hr)) 
    {
        char *pBuffer = new char[size];
        if (!pBuffer) return E_OUTOFMEMORY;
        try {
            hr = pDet->GetBitmapBits(0, NULL, pBuffer, width, height);
            if (SUCCEEDED(hr))
            {
                // Delete the old image, if any.
                if (*ppbmih) delete[] (*ppbmih);
                (*ppbmih) = (BITMAPINFOHEADER*)pBuffer;
            }
            else
            {
                delete [] pBuffer;
            }
        }
        catch (...) {
            delete [] pBuffer;
            return E_OUTOFMEMORY;
        }
    }
    return hr;
}

Passaggio 4: Disegnare la bitmap nell'area client

Afferrare un frame poster