Sample code to control USB webcam and adjust its settings

Discussion Sample code to control USB webcam and adjust its settings

  • Monday, February 27, 2012 11:26 PM
     
      Has Code

    It took me many days to find enough examples that would allow me to do what's described in the subject and I thought it might be useful for others.  I got the code to select and turn on a camera here and spent a lot of time figuring out how to:

    • set the resolution and color mode with IAMStreamControl
    • control the camera (focus and exposure) with IAMCameraControl
    • adjust its settings (brightness, saturation, etc) IAMVideoProcAmp

    Please note:  I am not a programmer by trade and some of this code is ugly.  Especially the part where I selected the resolution and color mode by trial error (the start of a logical enumerate and selection is there, but I didn't finish it).  But it should be enough to get people started.  Feel free to point out flaws and suggest improvements.  If someone has better example code to submit or link to, by all means, post it.  I just could not find many useful examples, and I am normally quite good at searching the internet.

    #include <stdio.h>
    #include <streams.h>
    #include <dshow.h>
    
    int
    main()
    {
     // for playing
     IGraphBuilder *pGraphBuilder;
     ICaptureGraphBuilder2 *pCaptureGraphBuilder2;
     IMediaControl *pMediaControl;
     IBaseFilter *pDeviceFilter = NULL;
    
     // to select a video input device
     ICreateDevEnum *pCreateDevEnum = NULL;
     IEnumMoniker *pEnumMoniker = NULL;
     IMoniker *pMoniker = NULL;
     ULONG nFetched = 0;
    
     // initialize COM
     CoInitialize(NULL);
    
     //
     // selecting a device
     //
    
     // Create CreateDevEnum to list device
     CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, 
       IID_ICreateDevEnum, (PVOID *)&pCreateDevEnum);
        
     // Create EnumMoniker to list VideoInputDevice 
     pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
       &pEnumMoniker, 0);
     if (pEnumMoniker == NULL) {
       // this will be shown if there is no capture device
       printf("no device\n");
       return 0;
     }
    
     // reset EnumMoniker
     pEnumMoniker->Reset();
    
     // get each Moniker
     while (pEnumMoniker->Next(1, &pMoniker, &nFetched) == S_OK) {
       IPropertyBag *pPropertyBag;
       TCHAR devname[256];
    
       // bind to IPropertyBag
       pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
         (void **)&pPropertyBag);
    
       VARIANT var;
    
       // get FriendlyName
       var.vt = VT_BSTR;
       pPropertyBag->Read(L"FriendlyName", &var, 0);
       WideCharToMultiByte(CP_ACP, 0,
         var.bstrVal, -1, devname, sizeof(devname), 0, 0);
       VariantClear(&var);
    
       printf("%s\r\n", devname);
       printf("  select this device ? [y] or [n]\r\n");
       int ch = getchar();
    
       // you can start playing by 'y' + return key
       // if you press the other key, it will not be played.
       if (ch == 'y') {
         // Bind Monkier to Filter
         pMoniker->BindToObject(0, 0, IID_IBaseFilter,
             (void**)&pDeviceFilter );
       }
    
       // release
       pMoniker->Release();
       pPropertyBag->Release();
    
       if (pDeviceFilter != NULL) {
         // go out of loop if getchar() returns 'y'
         break;
       }
     }
    
     if (pDeviceFilter != NULL) {
       //
       // PLAY
       //
    
       // create FilterGraph
       CoCreateInstance(CLSID_FilterGraph,
         NULL,
         CLSCTX_INPROC,
         IID_IGraphBuilder,
         (LPVOID *)&pGraphBuilder);
    
       // create CaptureGraphBuilder2
       CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, 
         IID_ICaptureGraphBuilder2, 
         (LPVOID *)&pCaptureGraphBuilder2);
    
       //============================================================
       //===========  MY CODE  ======================================
       //=============================================================
    	HRESULT hr = CoInitialize(0);
    	IAMStreamConfig *pConfig = NULL;
    	hr = pCaptureGraphBuilder2->FindInterface(&PIN_CATEGORY_CAPTURE, 0, pDeviceFilter, IID_IAMStreamConfig, (void**)&pConfig);
    
    	int iCount = 0, iSize = 0;
    	hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
    
    	// Check the size to make sure we pass in the correct structure.
    	if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS))
    	{
    		// Use the video capabilities structure.
    
    		for (int iFormat = 0; iFormat < iCount; iFormat++)
    		{
    			VIDEO_STREAM_CONFIG_CAPS scc;
    			AM_MEDIA_TYPE *pmtConfig;
    			hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc);
    			if (SUCCEEDED(hr))
    			{
    				/* Examine the format, and possibly use it. */
    				if ((pmtConfig->majortype == MEDIATYPE_Video) &&
    					(pmtConfig->subtype == MEDIASUBTYPE_RGB24) &&
    					(pmtConfig->formattype == FORMAT_VideoInfo) &&
    					(pmtConfig->cbFormat >= sizeof (VIDEOINFOHEADER)) &&
    					(pmtConfig->pbFormat != NULL))
    				{
    					VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)pmtConfig->pbFormat;
    					// pVih contains the detailed format information.
    					LONG lWidth = pVih->bmiHeader.biWidth;
    					LONG lHeight = pVih->bmiHeader.biHeight;
    				}
    				if (iFormat == 26) { //2 = '1280x720YUV' YUV, 22 = '1280x800YUV', 26 = '1280x720RGB'
    					hr = pConfig->SetFormat(pmtConfig);
    				}
    				// Delete the media type when you are done.
    				DeleteMediaType(pmtConfig);
    			}
    		}
    	}
    
    
    	// Query the capture filter for the IAMCameraControl interface.
    	IAMCameraControl *pCameraControl = 0;
    	hr = pDeviceFilter->QueryInterface(IID_IAMCameraControl, (void**)&pCameraControl);
    	if (FAILED(hr))
    	{
    		// The device does not support IAMCameraControl
    	}
    	else
    	{
    		long Min, Max, Step, Default, Flags, Val;
    		
    		// Get the range and default values 
    		hr = pCameraControl->GetRange(CameraControl_Exposure, &Min, &Max, &Step, &Default, &Flags);
    		hr = pCameraControl->GetRange(CameraControl_Focus, &Min, &Max, &Step, &Default, &Flags);
    		if (SUCCEEDED(hr))
    		{
    			hr = pCameraControl->Set(CameraControl_Exposure, -11, CameraControl_Flags_Manual ); // Min = -11, Max = 1, Step = 1
    			hr = pCameraControl->Set(CameraControl_Focus, 12, CameraControl_Flags_Manual );
    		}
    	}
    
    
    	// Query the capture filter for the IAMVideoProcAmp interface.
    	IAMVideoProcAmp *pProcAmp = 0;
    	hr = pDeviceFilter->QueryInterface(IID_IAMVideoProcAmp, (void**)&pProcAmp);
    	if (FAILED(hr))
    	{
    		// The device does not support IAMVideoProcAmp
    	}
    	else
    	{
    		long Min, Max, Step, Default, Flags, Val;
    		
    		// Get the range and default values 
    		hr = pProcAmp->GetRange(VideoProcAmp_Brightness, &Min, &Max, &Step, &Default, &Flags);
    		hr = pProcAmp->GetRange(VideoProcAmp_BacklightCompensation, &Min, &Max, &Step, &Default, &Flags);
    		hr = pProcAmp->GetRange(VideoProcAmp_Contrast, &Min, &Max, &Step, &Default, &Flags);
    		hr = pProcAmp->GetRange(VideoProcAmp_Saturation, &Min, &Max, &Step, &Default, &Flags);
    		hr = pProcAmp->GetRange(VideoProcAmp_Sharpness, &Min, &Max, &Step, &Default, &Flags);
    		hr = pProcAmp->GetRange(VideoProcAmp_WhiteBalance, &Min, &Max, &Step, &Default, &Flags);
    		if (SUCCEEDED(hr))
    		{
    			hr = pProcAmp->Set(VideoProcAmp_Brightness, 142, VideoProcAmp_Flags_Manual);
    			hr = pProcAmp->Set(VideoProcAmp_BacklightCompensation, 0, VideoProcAmp_Flags_Manual);
    			hr = pProcAmp->Set(VideoProcAmp_Contrast, 4, VideoProcAmp_Flags_Manual);
    			hr = pProcAmp->Set(VideoProcAmp_Saturation, 100, VideoProcAmp_Flags_Manual);
    			hr = pProcAmp->Set(VideoProcAmp_Sharpness, 0, VideoProcAmp_Flags_Manual);
    			hr = pProcAmp->Set(VideoProcAmp_WhiteBalance, 2800, VideoProcAmp_Flags_Manual);
    		}
    	}
    
    
       //============================================================
       //=========== END MY CODE  ======================================
       //=============================================================
    
       // set FilterGraph
       pCaptureGraphBuilder2->SetFiltergraph(pGraphBuilder);
    
       // get MediaControl interface
       pGraphBuilder->QueryInterface(IID_IMediaControl,
         (LPVOID *)&pMediaControl);
    
       // add device filter to FilterGraph
       pGraphBuilder->AddFilter(pDeviceFilter, L"Device Filter");
    
       // create Graph
       pCaptureGraphBuilder2->RenderStream(&PIN_CATEGORY_CAPTURE,
         NULL, pDeviceFilter, NULL, NULL);
       
       // start playing
       pMediaControl->Run();
    
    
    
    
       // to block execution
       // without this messagebox, the graph will be stopped immediately
       MessageBox(NULL,
         "Block Execution",
         "Block",
         MB_OK);
    
       // release
       pMediaControl->Release();
       pCaptureGraphBuilder2->Release();
       pGraphBuilder->Release();
     }
    
     // release
     pEnumMoniker->Release();
     pCreateDevEnum->Release();
    
     // finalize COM
     CoUninitialize();
    
     return 0;
    }



    • Edited by Kevin H M Monday, February 27, 2012 11:26 PM
    • Changed Type Kevin H M Monday, February 27, 2012 11:28 PM
    •  

All Replies

  • Tuesday, February 28, 2012 5:31 AM
     
     

    Kevin,

    Look in some of the OLD DirectX SDK's for a program called "AMCAP". (December 2002, Summer 2004, etc.)

    It is a good starting point for learning how to setup DirectShow Graphs then make calls that allow you to adjust video configuration data etc.

    Billnew


    DirectShow Filter Graph Spy: http://alax.info/blog/777 Extremely helpful for finding deep details for DirectShow Graphs.

  • Tuesday, February 28, 2012 3:11 PM
     
     

    Bill,

    That's actually where I started (it's actually included in the current Windows 7 SDK), but there was just so much code and so many different files that I wasn't able to get much out of it.  Maybe those with stronger programming experience wouldn't have as much trouble, but I have seen others complain about not being able to understand the DirectShow samples very well.

    But I agree, people should definitely look at those examples.  They demonstrate a lot of functionality and are presumably much more well-programmed.  I think this example might be useful as a starting point for those that feel lost in the provided examples in the SDK.