Partager via


Étape 3 : Implémenter la fonction Frame-Grabbing

[La fonctionnalité associée à cette page, DirectShow, est une fonctionnalité héritée. Il a été remplacé par MediaPlayer, IMFMediaEngine et Audio/Video Capture in Media Foundation. Ces fonctionnalités ont été optimisées pour Windows 10 et Windows 11. Microsoft recommande vivement que le nouveau code utilise MediaPlayer, IMFMediaEngine et Audio/Video Capture dans Media Foundation au lieu de DirectShow, si possible. Microsoft suggère que le code existant qui utilise les API héritées soit réécrit pour utiliser les nouvelles API si possible.]

[Cette API n’est pas prise en charge et peut être modifiée ou indisponible à l’avenir.]

Cette rubrique est l’étape 3 de l’obtention d’un cadre d’affiche.

L’étape suivante consiste à implémenter la fonction GetBitmap, qui utilise le détecteur de médias pour récupérer un cadre d’affiche. Dans cette fonction, effectuez les étapes suivantes :

  1. Créez le détecteur de médias.
  2. Spécifiez un fichier multimédia.
  3. Examinez chaque flux dans le fichier. S’il s’agit d’un flux vidéo, obtenez les dimensions natives de la vidéo.
  4. Obtenez une image d’affiche, en spécifiant l’heure de recherche et la dimension cible.

Créez l’objet Media Detector en appelant CoCreateInstance :

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

Spécifiez un nom de fichier à l’aide de la méthode IMediaDet::p ut_Filename . Cette méthode prend un paramètre BSTR .

hr = pDet->put_Filename(bstrFilename);

Obtenez le nombre de flux et parcourez en boucle chaque flux en vérifiant le type de média. La méthode IMediaDet::get_OutputStreams récupère le nombre de flux, et la méthode IMediaDet::p ut_CurrentStream spécifie le flux à examiner. Quittez la boucle sur le premier flux vidéo.

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;

Si aucun flux vidéo n’a été trouvé, la fonction se ferme.

Dans le code précédent, la méthode IMediaDet::get_StreamType retourne uniquement le GUID de type principal. Cela est pratique si vous n’avez pas besoin d’examiner le type de média complet. Toutefois, pour obtenir les dimensions de la vidéo, il est nécessaire d’examiner le bloc de format, de sorte que le type de média complet est nécessaire. Vous pouvez récupérer cela en appelant la méthode IMediaDet::get_StreamMediaType , qui remplit une structure AM_MEDIA_TYPE . Media Detector convertit tous les flux vidéo dans un format non compressé, avec un bloc de format 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);

La méthode get_StreamMediaType alloue le bloc de format, que l’appelant doit libérer. Cet exemple utilise la fonction FreeMediaType de la bibliothèque de classes de base.

Maintenant, vous êtes prêt à obtenir le cadre de l’affiche. Appelez d’abord la méthode IMediaDet::GetBitmapBits avec un pointeur NULL pour la mémoire tampon :

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

Cet appel retourne la taille de mémoire tampon requise dans le paramètre lSize . Le premier paramètre spécifie l’heure de flux à rechercher ; cet exemple utilise le temps zéro. Pour la largeur et la hauteur, cet exemple utilise les dimensions vidéo natives obtenues précédemment. Si vous spécifiez d’autres valeurs, le détecteur de médias étend la bitmap pour qu’elle corresponde.

Si la méthode réussit, allouez la mémoire tampon et appelez à nouveau 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;
}

Voici la fonction complète, avec une meilleure gestion des erreurs.

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

Suivant : Étape 4 : Dessiner la bitmap sur la zone cliente

Saisie d’un cadre d’affiche