Partager via


Capture d’une image à partir d’une épingle d’image fixe

[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.]

Certaines caméras peuvent produire une image fixe distincte du flux de capture, et souvent l’image fixe est de meilleure qualité que les images produites par le flux de capture. L’appareil photo peut avoir un bouton qui joue le rôle de déclencheur matériel, ou il peut prendre en charge le déclenchement logiciel. Une caméra qui prend en charge les images fixes expose une broche d’image fixe, qui est la catégorie d’épingle PIN_CATEGORY_STILL.

La méthode recommandée pour obtenir des images fixes à partir de l’appareil consiste à utiliser les API d’acquisition d’images Windows (WIA). Pour plus d’informations, consultez « Acquisition d’images Windows » dans la documentation du Kit de développement logiciel (SDK) de plateforme. Toutefois, vous pouvez également utiliser DirectShow pour capturer une image.

Pour déclencher l’épingle fixe, utilisez la méthode IAMVideoControl::SetMode lorsque le graphique est en cours d’exécution, comme suit :

IAMVideoControl *pAMVidControl = NULL;

hr = pControl->Run(); // Run the graph.
if (FAILED(hr))
{
    // Handle error.
}

hr = pCap->QueryInterface(IID_IAMVideoControl, (void**)&pAMVidControl);

if (SUCCEEDED(hr))
{
    // Find the still pin.
    IPin *pPin = NULL;

    // pBuild is an ICaptureGraphBuilder2 pointer.

    hr = pBuild->FindPin(
        pCap,                  // Filter.
        PINDIR_OUTPUT,         // Look for an output pin.
        &PIN_CATEGORY_STILL,   // Pin category.
        NULL,                  // Media type (don't care).
        FALSE,                 // Pin must be unconnected.
        0,                     // Get the 0'th pin.
        &pPin                  // Receives a pointer to thepin.
        );

    if (SUCCEEDED(hr))
    {
        hr = pAMVidControl->SetMode(pPin, VideoControlFlag_Trigger);
        pPin->Release();
    }
    pAMVidControl->Release();
}

Interrogez le filtre de capture pour IAMVideoControl. Si l’interface est prise en charge, obtenez un pointeur vers l’interface IPin de l’épingle en appelant la méthode ICaptureGraphBuilder2::FindPin , comme indiqué dans l’exemple précédent. Appelez ensuite IAMVideoControl::SetMode avec le pointeur IPin et l’indicateur VideoControlFlag_Trigger.

Notes

Selon l’appareil photo, vous devrez peut-être restituer la broche de capture (PIN_CATEGORY_CAPTURE) avant que la broche fixe ne se connecte.

 

Exemple : utilisation du filtre Sample Grabber

L’une des façons de capturer l’image consiste à utiliser le filtre Sample Grabber . L’exemple de capture utilise une fonction de rappel définie par l’application pour traiter l’image. Pour plus d’informations sur le filtre Sample Grabber, consultez Using the Sample Grabber.

L’exemple suivant suppose que la broche fixe fournit une image RVB non compressée. Tout d’abord, définissez une classe qui implémente l’interface de rappel de l’sample Grabber, ISampleGrabberCB :

// Class to hold the callback function for the Sample Grabber filter.
class SampleGrabberCallback : public ISampleGrabberCB
{
    // Implementation is described later.
}

// Global instance of the class.
SampleGrabberCallback g_StillCapCB;

L’implémentation de la classe est décrite sous peu.

Ensuite, connectez l’épingle fixe à l’exemple de capture et connectez-le au filtre Renderer Null . Le renderer Null ignore simplement les exemples de supports qu’il reçoit ; le travail réel sera effectué dans le rappel. (La seule raison pour laquelle le renderer Null est de connecter la broche de sortie de l’exemple grabber à quelque chose.) Appelez CoCreateInstance pour créer les filtres Sample Grabber et Null Renderer, puis appelez IFilterGraph::AddFilter pour ajouter les deux filtres au graphique :

// Add the Sample Grabber filter to the graph.
IBaseFilter *pSG_Filter;
hr = CoCreateInstance(
    CLSID_SampleGrabber, 
    NULL, 
    CLSCTX_INPROC_SERVER,
    IID_IBaseFilter, 
    (void**)&pSG_Filter
    );

hr = pGraph->AddFilter(pSG_Filter, L"SampleGrab");

// Add the Null Renderer filter to the graph.
IBaseFilter *pNull;

hr = CoCreateInstance(
    CLSID_NullRenderer, 
    NULL, 
    CLSCTX_INPROC_SERVER,
    IID_IBaseFilter, 
    (void**)&pNull
    );

hr = pGraph->AddFilter(pNull, L"NullRender");

Vous pouvez utiliser la méthode ICaptureGraphBuilder2::RenderStream pour connecter les trois filtres dans un appel de méthode, en passant de l’épingle fixe à l’sample grabber et de l’exemple grabber au renderer Null :

hr = pBuild->RenderStream(
    &PIN_CATEGORY_STILL, // Connect this pin ...
    &MEDIATYPE_Video,    // with this media type ...
    pCap,                // on this filter ...
    pSG_Filter,          // to the Sample Grabber ...
    pNull);              // ... and finally to the Null Renderer.

À présent, utilisez l’interface ISampleGrabber pour configurer l’exemple grabber afin qu’il met en mémoire tampon les exemples :

// Configure the Sample Grabber.
ISampleGrabber *pSG = NULL;

hr = pSG_Filter->QueryInterface(IID_ISampleGrabber, (void**)&pSG);
if (SUCCEEDED(hr))
{
    hr = pSG->SetOneShot(FALSE);
    hr = pSG->SetBufferSamples(TRUE);

    ...

Définissez l’interface de rappel avec un pointeur vers votre objet de rappel :

hr = pSG->SetCallback(&g_StillCapCB, 0); // 0 = Use the SampleCB callback method.

Obtenez le type de média que le repère a utilisé pour se connecter à l’exemple de capture :

// Store the media type for later use.
AM_MEDIA_TYPE g_StillMediaType;

hr = pSG->GetConnectedMediaType(&g_StillMediaType);
pSG->Release();

Ce type de média contient la structure BITMAPINFOHEADER qui définit le format de l’image fixe. Libérez le type de média avant la fermeture de l’application :

// On exit, remember to release the media type.
FreeMediaType(g_StillMediaType);

Voici un exemple de la classe de rappel. Notez que la classe implémente IUnknown, qu’elle hérite via l’interface ISampleGrabber , mais qu’elle ne conserve pas de nombre de références. Cela est sécurisé, car l’application crée l’objet sur la pile et l’objet reste dans l’étendue tout au long de la durée de vie du graphique de filtre.

Tout le travail se produit dans la méthode BufferCB , qui est appelée par l’sample grabber chaque fois qu’il obtient un nouvel exemple. Dans l’exemple suivant, la méthode écrit la bitmap dans un fichier :

class SampleGrabberCallback : public ISampleGrabberCB
{
public:
    // Fake referance counting.
    STDMETHODIMP_(ULONG) AddRef() { return 1; }
    STDMETHODIMP_(ULONG) Release() { return 2; }

    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
    {
        if (NULL == ppvObject) return E_POINTER;
        if (riid == __uuidof(IUnknown))
        {
            *ppvObject = static_cast<IUnknown*>(this);
             return S_OK;
        }
        if (riid == __uuidof(ISampleGrabberCB))
        {
            *ppvObject = static_cast<ISampleGrabberCB*>(this);
             return S_OK;
        }
        return E_NOTIMPL;
    }

    STDMETHODIMP SampleCB(double Time, IMediaSample *pSample)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen)
    {
        if ((g_StillMediaType.majortype != MEDIATYPE_Video) ||
            (g_StillMediaType.formattype != FORMAT_VideoInfo) ||
            (g_StillMediaType.cbFormat < sizeof(VIDEOINFOHEADER)) ||
            (g_StillMediaType.pbFormat == NULL))
        {
            return VFW_E_INVALIDMEDIATYPE;
        }
        HANDLE hf = CreateFile("C:\\Example.bmp", GENERIC_WRITE, 
        FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
        if (hf == INVALID_HANDLE_VALUE)
        {
            return E_FAIL;
        }
        long cbBitmapInfoSize = g_StillMediaType.cbFormat - SIZE_PREHEADER;
        VIDEOINFOHEADER *pVideoHeader =
           (VIDEOINFOHEADER*)g_StillMediaType.pbFormat;

        BITMAPFILEHEADER bfh;
        ZeroMemory(&bfh, sizeof(bfh));
        bfh.bfType = 'MB';  // Little-endian for "BM".
        bfh.bfSize = sizeof( bfh ) + BufferLen + cbBitmapInfoSize;
        bfh.bfOffBits = sizeof( BITMAPFILEHEADER ) + cbBitmapInfoSize;
        
        // Write the file header.
        DWORD dwWritten = 0;
        WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
        WriteFile(hf, HEADER(pVideoHeader), cbBitmapInfoSize, &dwWritten, NULL);        
        WriteFile( hf, pBuffer, BufferLen, &dwWritten, NULL );
        CloseHandle( hf );
        return S_OK;

    }
};

Tâches de capture vidéo