[DirectShow] Webcam ISampleGrabber



  • Hallo,

    ich brauche ein Programm, was jede Sekunde (oder noch schneller) ein Bild von einer USB-Webcam ausliest und mit jpeg-Kompression auf der Festplatte abspeichert.
    Bis jetzt habe ich das insoweit erreicht, als dass es generell schon klappt, allerdings ist es noch zu langsam (~3 Sekunden). Die Schuldigen sind Render() und WaitForCompletion().

    Folgenden (verkürzten) Codeabschnitt verwende ich in einer Schleife (+JPG kompression etc):

    CComPtr< ISampleGrabber > pGrabber;
        CComPtr< IBaseFilter >    pSource;
        CComPtr< IGraphBuilder >  pGraph;
        CComPtr< IVideoWindow >   pVideoWindow;
    
        pGrabber.CoCreateInstance( CLSID_SampleGrabber );
        CComQIPtr< IBaseFilter, &IID_IBaseFilter > pGrabberBase( pGrabber );
    	GetDefaultCapDevice(&pSource);
        pGraph.CoCreateInstance( CLSID_FilterGraph );
        pGraph->AddFilter( pSource, L"Source" );
        pGraph->AddFilter( pGrabberBase, L"Grabber" );
    
    	AM_MEDIA_TYPE am_media_type;
    
    	ZeroMemory(&am_media_type, sizeof(am_media_type));
    	am_media_type.bFixedSizeSamples = TRUE;
    	am_media_type.lSampleSize = 1;
    
    	am_media_type.majortype = MEDIATYPE_Video;
    	am_media_type.subtype = MEDIASUBTYPE_RGB24;
    	am_media_type.formattype = FORMAT_VideoInfo;
    
        pGrabber->SetMediaType( &am_media_type );
    
       CComPtr< IPin > pSourcePin;
        CComPtr< IPin > pGrabPin;
    
        pSourcePin = GetOutPin( pSource, 0 );
        pGrabPin   = GetInPin( pGrabberBase, 0 );
    	pGraph->Connect( pSourcePin, pGrabPin );
    
       AM_MEDIA_TYPE mt;
        pGrabber->GetConnectedMediaType( &mt );
    
        VIDEOINFOHEADER * vih = (VIDEOINFOHEADER*) mt.pbFormat;
        CB.Width  = vih->bmiHeader.biWidth;
        CB.Height = vih->bmiHeader.biHeight;
        _FreeMediaType( mt );
    
        memset( &(cbInfo.bih), 0, sizeof( cbInfo.bih ) );
        cbInfo.bih.biSize = sizeof( cbInfo.bih );
        cbInfo.bih.biWidth = CB.Width;
        cbInfo.bih.biHeight = CB.Height;
        cbInfo.bih.biPlanes = 1;
        cbInfo.bih.biBitCount = 24;
    
        CComPtr <IPin> pGrabOutPin = GetOutPin( pGrabberBase, 0 );
        pGraph->Render( pGrabOutPin ); /*************** ~ 2 sec *******************/
        pGrabber->SetBufferSamples( FALSE );
        pGrabber->SetOneShot( TRUE );
    
        pGrabber->SetCallback( &CB, 1 );
    
        CComQIPtr< IMediaControl, &IID_IMediaControl > pControl( pGraph );
        pControl->Run( );
    
    	CComQIPtr< IMediaEvent, &IID_IMediaEvent > pEvent( pGraph );
        long EvCode = 0;
    
        pEvent->WaitForCompletion( INFINITE, &EvCode ); /******************** ~ 1 s ***********************/
    
    	CHAR *BitmapData = NULL;
    	cbInfo.biSize = CalcBitmapInfoSize(cbInfo.bih);
    	ULONG Size = cbInfo.biSize + cbInfo.lBufferSize;
    	*BitmapSize = Size;
    
    	if(Bitmap)
    	{
    		*Bitmap = (BITMAPINFO *) new BYTE[Size];
    		if(*Bitmap)
    		{
    			(**Bitmap).bmiHeader = cbInfo.bih;
    			BitmapData = (CHAR *)(*Bitmap) + cbInfo.biSize;
    			memcpy(BitmapData, cbInfo.pBuffer, cbInfo.lBufferSize);
    		}
    	}
    

    (Ich weiß leider nicht mehr, woher ich den Sourcecode habe (er ist leider mehr oder weniger zusammenkopiert))

    Wie muss ich vorgehen, um schnell mehrere Bilder mit DirectShow (mit ISampleGrabber, falls möglich) von der Webcam auszulesen?

    SetOneShot() habe ich bereits auf false gesetzt (weil ich ja mehrere Bilder haben möchte), aber dann funktioniert leider nichts mehr. Außerdem erschließt sich mir der Sinn von SetBufferSamples nicht...

    Vielen Dank schon mal für eure Hilfe!



  • Den ganzen Initialisierungs- und Definitionskram würde ich direkt mal rausnehmen. Und dann initialisierst du das Device jedesmal neu, reicht ja wenn du das selbe behältst.



  • Hey!

    Ja, danke für deine Antwort, darüber habe ich auch schon nachgedacht, allerdings sind diese Funktionen sowieso innerhalb von wenigen Millisekunden fertig.

    Das Problem sind halt Render() und WaitForCompletion() und die muss ich ja jedes mal aufrufen.

    Mein Problem ist, dass ich leider kein gutes Verständnis von DirectShow habe. Ich bin zwar momentan alle möglichen Dokumentationen am durchlesen, aber ich verstehe folgendes nicht:

    Was bewirkt SetBufferSamples(true) (bzw würde es überhaupt helfen, es auf true zu setzen)?

    Wenn ich mehrere Bilder von der Webcam holen will, dann müsste ich SetOneShot() ja eigentlich auf false setzen... bloß was muss ich dann noch ändern, damit das Programm auch funktioniert? (ich glaube WaitForCompletion war dann eine Endlosschleife)


Anmelden zum Antworten