4.25 How to enumerate Video Input Devices to use in OpenCV VideoCapture?

Imagine that you have mulitple video input devices and you would like to select and use one of those devices.
In Windows, you can get all video input devices informatiion by using COM interfaces of CLSID_SystemDeviceEnum, and CLSID_VideoInputDeviceCategory classes.
If you are familiar with COM interfaces, you can easily enumerate all video devices by using those interfaces as shown below.

1 Create an interface of ICreateDevEnum by using CoCreateInstance.
   ComPtr<ICreateDevEnum> pDevEnum;
   CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
                  CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
2 Create an interface of IEnumMoniker by using CreateClassEnumerator from the ICreateDevEnum object.
   ComPtr<IEnumMoniker> pEnum;
   pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
3 Get an IMoniker interface from the enumerator, and bind the moniker to an IPropertyBag interface, and read "FriendlyName" from the bag.
  IMoniker* pMoniker = NULL;
  while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
      ComPtr<IPropertyBag> pPropBag = NULL;
      HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
      if (FAILED(hr)) {
         pMoniker->Release();
         continue;  
      }
      VARIANT var;
      VariantInit(&var);
      //Read FriendlyName
      hr = pPropBag->Read(L"FriendlyName", &var, 0;)
      pMoniker->Release();
   }
As you can easily see, we really would like to enumerate all FriendlyName properties of the CLSID_VideoInputDeviceCategory COM interface through IMoniker and IPropertyBag interfaces. To simplify a story, however, we have implemented following two classes:

  • VideoInputDeviceEnumerator

  • LabeledVideoDeviceComboBox

  • In OpenCV VideoCapture applications of SOL9, you can use the LabeledVideoDeviceComboBox class to select a video device by name from the ComboBox, and get a video device index which can be used as an argument of open method of cv::VideoCapture class.

    The following VideoDeviceEnumerator program is a simple example to select a video device from LabeledVideoDeviceComboBox, and start to read a video image from the device.




    
    /*
     * VideoDeviceEnumerator.cpp 
     * Copyright (c) 2017 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
     */
    //2017/07/25
    //This is a simple example to capture videoDevice, and to display the capturued frames 
    //to original imageview and blurred image view based on cv::boxFiter.
    
    #define _CONSOLE_
    
    //#define STRSAFE_LIB
    #include <sol/com/ApartmentThreadedModel.h>
    #include <sol/ModuleFileName.h>
    #include <sol/LabeledTrackBar.h>
    #include <sol/PushButton.h>
    #include <sol/FileDialog.h>
    #include <sol/directshow/LabeledVideoDeviceComboBox.h>
    
    #include <sol/opencv/OpenCVVideoApplication.h>
    #include <sol/opencv/OpenCVVideoCaptureView.h>
    #include <sol/opencv/OpenCVNamedWindow.h>
    
    
    namespace SOL {
    
    class MainView :public OpenCVVideoCaptureView {
    
    private:
      ////////////////////////////////////////////////////////////////////////////////////////
      //Inner classes start.
      class OriginalImageView :public OpenCVNamedWindow {
      private:
        cv::Mat originalImage;
      public:  
        void display()
        {
          show(originalImage);
        }
        
      public:
        OriginalImageView(View* parent, const char* name, Args& args)
        :OpenCVNamedWindow(parent, name, args)
        {
        }
    
        void setImage(cv::Mat& image)
        {
          originalImage = image;
        }
      };
      
      //Inner classes end.
      ////////////////////////////////////////////////////////////////////////////////////////
      
        
      SmartPtr<OriginalImageView>           originalImage;
      SmartPtr<LabeledVideoDeviceComboBox>  videoDeviceComboBox;
      SmartPtr<PushButton>                  startButton;
      SmartPtr<PushButton>                  endButton;
      
      static const int                      CONTROLPANE_WIDTH = 200;
      
      void resize(int w, int h)
      {
        if (originalImage ) {
          
          originalImage   -> reshape(0, 0,  w-CONTROLPANE_WIDTH-5,  h);
          videoDeviceComboBox -> reshape(w-CONTROLPANE_WIDTH+5, 0,   180, 40);
          startButton         -> reshape(w-CONTROLPANE_WIDTH+30, 60,  120, 30);
          endButton           -> reshape(w-CONTROLPANE_WIDTH+30, 100, 120, 30);
        }
      }
    
      void confirm(Action& action)
      {
        int rc = MessageBox(NULL, "Are you sure to close this window?", "Confirmation", 
                    MB_OKCANCEL|MB_ICONEXCLAMATION);
        if (rc == IDOK) {
          exit(action);
        }
      }
      
    public:
      MainView(OpenCVVideoApplication& applet, const char* name, Args& args)
      :OpenCVVideoCaptureView(applet, name, args)
      {
        try {
          Args ar;
          originalImage = new OriginalImageView(this, "cvwindow1", ar); 
          
          ar.reset();
          ar.reset();
          ar.set(XmNstyle, CBS_DROPDOWNLIST);
          videoDeviceComboBox = new LabeledVideoDeviceComboBox(this, "VideoDevice", ar);
          videoDeviceComboBox->addCallback(XmNselChangeCallback, this,
            (Callback)&MainView::selChangeCallback, NULL);
          
          ar.reset();
          startButton = new PushButton(this, "Start Caputure", ar);
          startButton->addCallback(XmNactivateCallback, this,
              (Callback)&MainView::startCapture, NULL);
          startButton->disable();
          
          ar.reset();
          endButton = new PushButton(this, "End Capture", ar);
          endButton -> addCallback(XmNactivateCallback, this,
              (Callback)&MainView::endCapture, NULL);   
          endButton->disable();
          
          addCallback(XmNmenuCallback, IDM_EXIT, this,
              (Callback)&MainView::confirm, NULL);
          
        } catch (Exception& ex) {
          caught(ex);
        }
      }
    
      ~MainView()
      {
      }
    
      void selChangeCallback(Action& action)
      {
        int deviceIndex = videoDeviceComboBox->getCurSel();
        if (deviceIndex >=0) {
          startButton->enable();
        }
      }
      
      void startCapture(Action& action)
      {
        try {
          
          int deviceIndex = videoDeviceComboBox->getCurSel();
          if (deviceIndex >=0) {
            String  name;
            videoDeviceComboBox->getString(deviceIndex, name);
            char message[1024];
            sprintf_s(message, CountOf(message), 
                "Are you sure to open device (%d):%s", deviceIndex,
                (const char*)name);
            if (MessageBoxA(NULL, message, "Device", MB_OKCANCEL) == IDOK) {
              openDevice(deviceIndex);
              endButton->enable();
            }
          }
        } catch (SOL::Exception& ex) {
          caught(ex);
        }
      }
    
      void endCapture(Action& action)
      {
        stopCapture(action);
        endButton->disable();
      }
      
      //This method will be called from the event loop OpenCVVideoCaptureApplition::run.
      virtual void render()
      {
        cv::Mat frame;
        if (readVideoFrame(frame)) {
          if (!frame.empty() && originalImage) {
            originalImage -> setImage(frame);
            originalImage -> display();
          }
        }
      }
    };
    }
    
    
    void main(int argc, char** argv) 
    {
      try {
        ModuleFileName module(argv[0]);
        //For VideoInputDeviceEnumerator class
        ApartmentThreadedModel model;
        
        const char*  name = module.getAppName();
        int delay = 40; //40 milliseconds
        OpenCVVideoApplication applet(name, argc, argv, delay);
    
        Args args;
        args.set(XmNwidth,     700);
        args.set(XmNheight,    420);
        args.set(XmNvideoDeviceIndex, 0);
        args.set(XmNcaptureAutoStart, false);
        MainView view(applet, name, args);
        view.realize();
    
        applet.run(view);
        
      } catch (SOL::Exception& ex) {
        caught(ex);
      }
    }
    
    

    Last modified: 18 Sep. 2017

    Copyright (c) 2017 Antillia.com ALL RIGHTS RESERVED.