Freigeben über


Schritt 3: Implementieren der Frame-Grabbing-Funktion

[Das dieser Seite zugeordnete Feature DirectShow ist ein Legacyfeature. Es wurde durch MediaPlayer, IMFMediaEngine und Audio/Video Capture in Media Foundation ersetzt. Diese Features wurden für Windows 10 und Windows 11 optimiert. Microsoft empfiehlt dringend, dass neuer Code nach Möglichkeit MediaPlayer, IMFMediaEngine und Audio/Video Capture in Media Foundation anstelle von DirectShow verwendet. Microsoft schlägt vor, vorhandenen Code, der die Legacy-APIs verwendet, um nach Möglichkeit die neuen APIs zu verwenden.]

[Diese API wird nicht unterstützt und kann in Zukunft geändert oder nicht mehr verfügbar sein.]

Dieses Thema ist Schritt 3 des Greifens eines Posterrahmens.

Der nächste Schritt besteht darin, die GetBitmap-Funktion zu implementieren, die den Mediendetektor zum Abrufen eines Posterrahmens verwendet. Führen Sie in dieser Funktion die folgenden Schritte aus:

  1. Erstellen Sie den Mediendetektor.
  2. Geben Sie eine Mediendatei an.
  3. Untersuchen Sie jeden Stream in der Datei. Wenn es sich um einen Videostream handelt, rufen Sie die nativen Dimensionen des Videos ab.
  4. Rufen Sie einen Posterrahmen ab, der die Suchzeit und die Zieldimension angibt.

Erstellen Sie das Media Detector-Objekt, indem Sie CoCreateInstance aufrufen:

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

Geben Sie einen Dateinamen mit der IMediaDet::p ut_Filename-Methode an. Diese Methode verwendet einen BSTR-Parameter .

hr = pDet->put_Filename(bstrFilename);

Rufen Sie die Anzahl der Streams ab, und durchlaufen Sie jeden Stream, um den Medientyp zu überprüfen. Die IMediaDet::get_OutputStreams-Methode ruft die Anzahl der Streams ab, und die IMediaDet::p ut_CurrentStream-Methode gibt den zu untersuchenden Stream an. Beenden Sie die Schleife für den ersten Videostream.

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;

Wenn kein Videostream gefunden wurde, wird die Funktion beendet.

Im vorherigen Code gibt die IMediaDet::get_StreamType-Methode nur die Haupttyp-GUID zurück. Dies ist praktisch, wenn Sie den vollständigen Medientyp nicht untersuchen müssen. Um die Videodimensionen abzurufen, ist es jedoch erforderlich, den Formatblock zu untersuchen, sodass der vollständige Medientyp benötigt wird. Sie können diese abrufen, indem Sie die IMediaDet::get_StreamMediaType-Methode aufrufen, die eine AM_MEDIA_TYPE-Struktur ausfüllt. Der Mediendetektor konvertiert alle Videostreams mit einem VIDEOINFOHEADER-Formatblock in ein unkomprimiertes Format.

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

Die get_StreamMediaType-Methode weist den Formatblock zu, den der Aufrufer freigeben muss. In diesem Beispiel wird die FreeMediaType-Funktion aus der Basisklassenbibliothek verwendet.

Jetzt können Sie den Posterrahmen erhalten. Rufen Sie zunächst die IMediaDet::GetBitmapBits-Methode mit einem NULL-Zeiger für den Puffer auf:

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

Dieser Aufruf gibt die erforderliche Puffergröße im lSize-Parameter zurück. Der erste Parameter gibt die zu suchende Streamzeit an. In diesem Beispiel wird zeit null verwendet. Für die Breite und Höhe verwendet dieses Beispiel die zuvor abgerufenen nativen Videodimensionen. Wenn Sie andere Werte angeben, wird die Bitmap vom Mediendetektor entsprechend gestreckt.

Wenn die Methode erfolgreich ist, ordnen Sie den Puffer zu, und rufen Sie GetBitmapBits erneut auf:

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

Hier ist die vollständige Funktion mit etwas besserer Fehlerbehandlung.

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

Weiter: Schritt 4: Zeichnen der Bitmap im Clientbereich

Greifen eines Posterrahmens