Using the VCD Simple Property Class

Up to the version 1.41 of IC Imaging Control, the properties of a device could be accessed using the properties %%Auto, %%AutoAvailable, %%Available, %%Default and %%Range. Since version 2.0, these properties are depreciated. The new VCD properties allow more detailed and generic access to the device's properties, but they require a little more coding. If you are happy with the old properties, a new helper class is introduced in version 2.0 that provides access methods to the old style VCD properties. This chapter shows you how to use this helper class.

Setting up the Project

The source code for this sample program can be found in the %TOPLEVEL%\samples\VC10\VCD Simple Property sample source directories. It also uses the files "SimplePropertyAccess.h" and "SimplePropertyAccess.cpp" that implement the helper class. They are located in the %TOPLEVEL%\samples\VC10\common directory.

Create a project as described in the FirstSteps chapter, but choose "Dialog based" application type instead of "Single document".

In VC71, right click on the project in the solution explorer and select "Add" from the context menu and then "New Folder". Name the folder SimplePropertyAccess. Right click on the folder that has just been created in the solution explorer and select "Add" and then "Add Existing Item...". Browse to the %TOPLEVEL%\Samples\VC\common directory, select the files "SimplePropertyAccess.h" and "SimplePropertyAccess.cpp" and click the "Open" button. The files are now part of the project and the "Simple Property Class" is ready for use.

Accessing a Property

First of all, we are going to add some controls to the dialog. They will allow us to manipulate a device property. Add a slider, a label and a check box. Give the slider the ID IDC_WB_SLIDER, the label the ID IDC_WB_VALUE and the check box IDC_WB_CHECK. Now add a static text field to the dialog. Give it the ID IDC_DISPLAY. It will be used to display the live video. Use the class wizard to add member variables for all these controls. Name them m_staticDisplay, m_sldWhiteBalance, m_lblValue and m_chkAuto. These controls will be used to manipulate the "WhiteBalance" property of a device.

image

We need an instance of the helper class in order to use it. Therefore, include the helper class and add an instance to the CCodeContainerDlg class:

#include "../Common/SimplePropertyAccess.h"

protected:
    HICON m_hIcon;
    DShowLib::Grabber m_Grabber;
    smart_com<DShowLib::IFrameFilter>    m_pRotFlipFilter;
    smart_com<DShowLib::IFrameFilter>    m_pROIFilter;
    // An instance of the helper class
    CSimplePropertyAccess m_VCDProp;

As you can see above, the grabber object is also declared here.

The helper class and the controls are initialized in the OnInitDialog method. This is accomplished by the following code:

BOOL CCodeContainerDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    // Set the icon for this dialog. The framework does this automatically
    // when the application's main window is not a dialog.
    SetIcon(m_hIcon, TRUE);            // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    // Initialize the VCDProp object to access the properties of the ICImagingControl
    // object.
    m_VCDProp.init( m_Grabber.getAvailableVCDProperties() );
    // Initialize the auto check boxes and deactivate automation.
    if( !m_VCDProp.isAutoAvailable( VCDID_WhiteBalance ) )
    {
        m_chkAuto.EnableWindow( FALSE );
    }
    else
    {
        m_VCDProp.setAuto( VCDID_WhiteBalance, false );
    }
    // Initialize the white balance slider.
    if( !m_VCDProp.isAvailable( VCDID_WhiteBalance ) )
    {
        m_sldWhiteBalance.EnableWindow(FALSE);
    }
    else
    {
        m_sldWhiteBalance.EnableWindow();
        m_sldWhiteBalance.SetRangeMin( m_VCDProp.getRangeMin( VCDID_WhiteBalance ) );
        m_sldWhiteBalance.SetRangeMax( m_VCDProp.getRangeMax( VCDID_WhiteBalance ) );
        int val = m_VCDProp.getValue( VCDID_WhiteBalance );
        m_sldWhiteBalance.SetPos( val );
        CString s;
        s.Format( TEXT("%d"), val );
        m_lblValue.SetWindowText( s );
    }
    // Set output window for live mode.
    m_Grabber.setHWND( m_staticDisplay.m_hWnd );

    // Start live mode.
    m_Grabber.startLive();

    return TRUE;  // return TRUE, unless you set the focus to a control.
}

First, the helper class is initialized, then the controls. After that the live video is started.

To add functionality to the controls, use the class wizard to add a click event for the check box and a handler for the WM_HSCROLL message. The click event of the check box toggles the automation on/off. It is implemented as follows:

void CCodeContainerDlg::OnWbCheck()
{
    if( m_chkAuto.GetCheck() )
    {
        m_VCDProp.setAuto( VCDID_WhiteBalance, true );
        m_sldWhiteBalance.EnableWindow( FALSE );
    }
    else
    {
        m_VCDProp.setAuto( VCDID_WhiteBalance, false );
        m_sldWhiteBalance.EnableWindow( TRUE );
    }
}

The event handler for the WM_HSCROLL message updates the "WhiteBalance" property. It is implemented as follows:

void CCodeContainerDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    int pos = ((CSliderCtrl*) pScrollBar)->GetPos();
    CString posText;
    posText.Format( TEXT("%d"), pos );
    m_VCDProp.setValue( VCDID_WhiteBalance, pos );
    m_lblValue.SetWindowText( posText );

    CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}

Before any properties can be manipulated, a valid video capture device must be selected. This is done via the built-in device selection dialog, which is displayed before the main dialog appears. To select a valid video capture device, change the code in the InitInstance of the CodeContainerApp class to the following:

CCodeContainerDlg dlg;
if( !dlg.selectDevice() )
{
    AfxMessageBox( TEXT("No device was selected.") );
    return FALSE;
}
m_pMainWnd = &dlg;
dlg.DoModal();

The selectDevice method of the CodeContainerDlg class calls the built-in dialog and is implemented as follows:

bool CCodeContainerDlg::selectDevice()
{
    m_Grabber.showDevicePage( m_hWnd );
    return m_Grabber.isDevValid();
}

Now run the application and use the slider to manipulate the "WhiteBalance" property or use the check box to toggle the automation feature of the property.

Accessing advanced Properties

There are some properties that allow more detailed access than the one described above. In case of "WhiteBalance", it is possible to specify a value for red and blue and to perform an automatic adjustment for a limited time - this is called "one push". These advanced properties can be accessed using the following IDs: VCDElement_WhiteBalanceBlue and VCDElement_WhiteBalanceRed. The "One Push" operation is triggered by calling the push method of the helper class with VCDID_WhiteBalance as a parameter.

The following sample shows you how to access "WhiteBalanceBlue" and "WhiteBalanceRed" property. It will also access the "Brightness" property to show the difference between the standard property and the advanced way.

Set up a project as described in the FirstSteps chapter, but choose "Dialog based" application type instead of "Single document". Now, add 3 sliders to the dialog. Give them the IDs IDC_BRIGHTNESS_SLIDER, IDC_WBBLUE_SLIDER and IDC_WBRED_SLIDER. Corresponding to the sliders add 3 static text fields and give them the IDs IDC_BRIGHTNESS_STATIC, IDC_WBBLUE_STATIC and IDC_WBRED_STATIC. They will display the current value of the according property. Then, add 2 check boxes with the IDs IDC_BRIGHTNESS_AUTO and IDC_WB_AUTO and a button with the ID IDC_WB_ONEPUSH. To display a live video, add a static text control with the ID IDC_DISPLAY.

image

Before the main dialog is called, the device selection dialog should be displayed. Therefore, add a call to the function selectDevice to the InitInstance method of the CVCDSimplePropertyApp class:

CVCDSimplePropertyDlg dlg;
if( !dlg.selectDevice() )
{
    AfxMessageBox( TEXT("No device was selected.") );
    return FALSE;
}
m_pMainWnd = &dlg;
dlg.DoModal();

Declare a public function selectDevice and some protected variables:

public:
    bool selectDevice();

protected:
    HICON m_hIcon;
    DShowLib::Grabber m_Grabber;
    int m_wbOnePushUpdateCount;
    // VCDProperty interface pointers
    DShowLib::tIVCDAbsoluteValuePropertyPtr m_pGainAbsolute;
    DShowLib::tIVCDSwitchPropertyPtr m_pGainAuto;
    DShowLib::tIVCDAbsoluteValuePropertyPtr m_pExposureAbsolute;
    DShowLib::tIVCDSwitchPropertyPtr m_pExposureAuto;
    DShowLib::tIVCDRangePropertyPtr m_pWBRedRange;
    DShowLib::tIVCDRangePropertyPtr m_pWBGreenRange;
    DShowLib::tIVCDRangePropertyPtr m_pWBBlueRange;
    DShowLib::tIVCDSwitchPropertyPtr m_pWBAuto;
    DShowLib::tIVCDButtonPropertyPtr m_pWBOnePush;
    // The UI controls
    CSliderCtrl* m_pExposureSlider;
    CStatic* m_pExposureStatic;
    CButton* m_pExposureAutoCheck;
    CSliderCtrl* m_pGainSlider;
    CStatic* m_pGainStatic;
    CButton* m_pGainAutoCheck;
    CSliderCtrl* m_pWbRedSlider;
    CStatic* m_pWbRedStatic;
    CSliderCtrl* m_pWbGreenSlider;
    CStatic* m_pWbGreenStatic;
    CSliderCtrl* m_pWbBlueSlider;
    CStatic* m_pWbBlueStatic;
    CButton* m_pWbAutoCheck;
    CButton* m_pWbOnePushButton;

The helper class and the controls are initialized in the OnInitDialog method. Edit the file "VCD Simple PropertyDlg.cpp" and insert the following code:

BOOL CVCDSimplePropertyDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);            // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon
    // Get references to the controls
    m_pExposureSlider = (CSliderCtrl*)GetDlgItem( IDC_EXPOSURE_SLIDER );
    m_pExposureStatic = (CStatic*)GetDlgItem( IDC_EXPOSURE_STATIC );
    m_pExposureAutoCheck = (CButton*)GetDlgItem( IDC_EXPOSURE_AUTO );
    m_pGainSlider = (CSliderCtrl*)GetDlgItem( IDC_GAIN_SLIDER );
    m_pGainStatic = (CStatic*)GetDlgItem( IDC_GAIN_STATIC );
    m_pGainAutoCheck = (CButton*)GetDlgItem( IDC_GAIN_AUTO );

    m_pWbRedSlider = (CSliderCtrl*)GetDlgItem( IDC_WBRED_SLIDER );
    m_pWbRedStatic = (CStatic*)GetDlgItem( IDC_WBRED_STATIC );
    m_pWbGreenSlider = (CSliderCtrl*)GetDlgItem( IDC_WBGREEN_SLIDER );
    m_pWbGreenStatic = (CStatic*)GetDlgItem( IDC_WBGREEN_STATIC );
    m_pWbBlueSlider = (CSliderCtrl*)GetDlgItem( IDC_WBBLUE_SLIDER );
    m_pWbBlueStatic = (CStatic*)GetDlgItem( IDC_WBBLUE_STATIC );
    m_pWbAutoCheck = (CButton*)GetDlgItem( IDC_WB_AUTO );
    m_pWbOnePushButton = (CButton*)GetDlgItem( IDC_WB_ONEPUSH );
    // Get exposure absolute value and auto interfaces
    m_pExposureAbsolute = m_Grabber.getVCDPropertyInterface<DShowLib::IVCDAbsoluteValueProperty>( VCDID_Exposure, VCDElement_Value );
    m_pExposureAuto = m_Grabber.getVCDPropertyInterface<DShowLib::IVCDSwitchProperty>( VCDID_Exposure, VCDElement_Auto );
    // Get gain absolute value and auto interfaces
    m_pGainAbsolute = m_Grabber.getVCDPropertyInterface<DShowLib::IVCDAbsoluteValueProperty>( VCDID_Gain, VCDElement_Value );
    m_pGainAuto = m_Grabber.getVCDPropertyInterface<DShowLib::IVCDSwitchProperty>( VCDID_Gain, VCDElement_Auto );
    // Get white balance red range, green range, blue range, auto and one push interfaces
    m_pWBRedRange = m_Grabber.getVCDPropertyInterface<DShowLib::IVCDRangeProperty>( VCDID_WhiteBalance, VCDElement_WhiteBalanceRed );
    m_pWBGreenRange = m_Grabber.getVCDPropertyInterface<DShowLib::IVCDRangeProperty>( VCDID_WhiteBalance, VCDElement_WhiteBalanceGreen );
    m_pWBBlueRange = m_Grabber.getVCDPropertyInterface<DShowLib::IVCDRangeProperty>( VCDID_WhiteBalance, VCDElement_WhiteBalanceBlue );
    m_pWBAuto = m_Grabber.getVCDPropertyInterface<DShowLib::IVCDSwitchProperty>( VCDID_WhiteBalance, VCDElement_Auto );
    m_pWBOnePush = m_Grabber.getVCDPropertyInterface<DShowLib::IVCDButtonProperty>( VCDID_WhiteBalance, VCDElement_OnePush );
    // Initialize exposure auto
    if( m_pExposureAuto == 0 )
    {
        m_pExposureAutoCheck->EnableWindow( FALSE );
    }
    else
    {
        if( m_pExposureAuto->getSwitch() )
        {
            m_pExposureAutoCheck->SetCheck( BST_CHECKED );
            m_pExposureSlider->EnableWindow( FALSE );
        }
        else
        {
            m_pExposureAutoCheck->SetCheck( BST_UNCHECKED );
        }
    }
    // Initialize exposure value
    if( m_pExposureAbsolute != 0 )
    {
        int rangeMin = 0;
        int rangeMax = 100;
        int rangePos = AbsValToSliderPosLogarithmic( m_pExposureAbsolute, m_pExposureAbsolute->getValue(), 100 );
        m_pExposureSlider->SetRange( rangeMin, rangeMax );
        m_pExposureSlider->SetPos( rangePos );
        CString s;
        s.Format( TEXT( "%g" ), m_pExposureAbsolute->getValue() );
        m_pExposureStatic->SetWindowText( s );
    }
    // Initialize gain auto
    if( m_pGainAuto == 0 )
    {
        m_pGainAutoCheck->EnableWindow( FALSE );
    }
    else
    {
        if( m_pGainAuto->getSwitch() )
        {
            m_pGainAutoCheck->SetCheck( BST_CHECKED );
            m_pGainSlider->EnableWindow( FALSE );
        }
        else
        {
            m_pGainAutoCheck->SetCheck( BST_UNCHECKED );
        }
    }
    // Initialize gain value
    if( m_pGainAbsolute != 0 )
    {
        int rangeMin = 0;
        int rangeMax = 100;
        int rangePos = AbsValToSliderPosLinear( m_pGainAbsolute, m_pGainAbsolute->getValue(), 100 );
        m_pGainSlider->SetRange( rangeMin, rangeMax );
        m_pGainSlider->SetPos( rangePos );
        CString s;
        s.Format( TEXT( "%g" ), m_pGainAbsolute->getValue() );
        m_pGainStatic->SetWindowText( s );
    }

    if( m_pWBAuto == 0 )
    {
        m_pWbAutoCheck->EnableWindow( FALSE );
    }
    else
    {
        m_pWbAutoCheck->SetCheck( m_pWBAuto->getSwitch() ? BST_CHECKED : BST_UNCHECKED );
        m_pWbRedSlider->EnableWindow( !m_pWBAuto->getSwitch() );
        m_pWbGreenSlider->EnableWindow( !m_pWBAuto->getSwitch() );
        m_pWbBlueSlider->EnableWindow( !m_pWBAuto->getSwitch() );
    }
    if( m_pWBRedRange != 0 )
    {
        m_pWbRedSlider->SetRange( m_pWBRedRange->getRangeMin(), m_pWBRedRange->getRangeMax() );
        m_pWbRedSlider->SetPos( m_pWBRedRange->getValue() );
        CString s;
        s.Format( TEXT( "%d" ), m_pWBRedRange->getValue() );
        m_pWbRedStatic->SetWindowText( s );
    }
    if( m_pWBGreenRange != 0 )
    {
        m_pWbGreenSlider->SetRange( m_pWBGreenRange->getRangeMin(), m_pWBGreenRange->getRangeMax() );
        m_pWbGreenSlider->SetPos( m_pWBGreenRange->getValue() );
        CString s;
        s.Format( TEXT( "%d" ), m_pWBGreenRange->getValue() );
        m_pWbGreenStatic->SetWindowText( s );
    }
    if( m_pWBBlueRange != 0 )
    {
        m_pWbBlueSlider->SetRange( m_pWBBlueRange->getRangeMin(), m_pWBBlueRange->getRangeMax() );
        m_pWbBlueSlider->SetPos( m_pWBBlueRange->getValue() );
        CString s;
        s.Format( TEXT( "%d" ), m_pWBBlueRange->getValue() );
        m_pWbBlueStatic->SetWindowText( s );
    }
    if( m_pWBOnePush == 0)
    {
        m_pWbOnePushButton->EnableWindow( FALSE );
    }
    // Set output window for live mode
    m_Grabber.setHWND( GetDlgItem( IDC_DISPLAY )->GetSafeHwnd() );
    // Start live mode
    m_Grabber.startLive();
    SetTimer( 0, 250, 0 );

    return TRUE;  // return TRUE  unless you set the focus to a control
}

As you can see above, the code to initialize the sliders for "WhiteBalanceBlue" and "WhiteBalanceRed" is the same as the code to initialize the slider for "Brightness". The only difference is that you must use the element ID "VCDElement_WhiteBalanceBlue" or "VCDElement_WhiteBalanceRed" instead of the item ID "VCDID_Brightness".

The selectDevice method that is called at program start calls the built-in device selection dialog. It is implemented as follows:

bool CVCDSimplePropertyDlg::selectDevice()
{
    m_Grabber.showDevicePage( m_hWnd );
    return m_Grabber.isDevValid();
}

Finally, all we need are the event handlers of the controls. We start with the event handlers of the check boxes. Use the class wizard to add a click event for every check box and insert the following code:

void CVCDSimplePropertyDlg::OnExposureAuto()
{
    if( m_pExposureAutoCheck->GetCheck() )
    {
        m_pExposureAuto->setSwitch( true );
        m_pExposureSlider->EnableWindow( FALSE );
    }
    else
    {
        m_pExposureAuto->setSwitch( false );
        m_pExposureSlider->EnableWindow();
    }
}

The click event for the "WhiteBalance" check box is implemented the same way except that both sliders "WhiteBalanceBlue", "WhiteBalanceRed" and the "One Push" button must be disabled/enabled.

The click event for the "One Push" button has a very simple implementation. It just calls the push method of the helper class:

void CVCDSimplePropertyDlg::OnWbOnepush()
{
    m_wbOnePushUpdateCount = 0;
    m_pWBOnePush->push();
}

Finally add an event that handles the ON_WM_HSCROLL message. It is used to update the slider controls. Since the ON_WM_HSCROLL is sent to the dialog window and not to the respective control itself, this event handles all 3 slider controls. The correct slider control is determined via the ID of the control. This is done by the switch statement. Then the property and the appropriate static text field is set to the current value. The implementation looks as follows:

void CVCDSimplePropertyDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    int pos = ((CSliderCtrl*) pScrollBar)->GetPos();
    CString posText;
    posText.Format( TEXT("%d"), pos );
    // Check which slider was scrolled
    switch( pScrollBar->GetDlgCtrlID() )
    {
    case IDC_WBRED_SLIDER:
        {
            m_pWBRedRange->setValue( pos );
            m_pWbRedStatic->SetWindowText( posText );
        }
        break;
    case IDC_WBGREEN_SLIDER:
        {
            m_pWBGreenRange->setValue( pos );
            m_pWbGreenStatic->SetWindowText( posText );
        }
        break;
    case IDC_WBBLUE_SLIDER:
        {
            m_pWBBlueRange->setValue( pos );
            m_pWbBlueStatic->SetWindowText( posText );
        }
        break;
    case IDC_EXPOSURE_SLIDER:
        {
            double val = SliderPosToAbsValLogarithmic( m_pExposureAbsolute, pos, 100 );
            m_pExposureAbsolute->setValue( val );
            posText.Format( TEXT( "%g %s" ), val, m_pExposureAbsolute->getDimTypeW().c_str() );
            m_pExposureStatic->SetWindowText( posText );
        }
        break;
    case IDC_GAIN_SLIDER:
        {
            double val = SliderPosToAbsValLinear( m_pGainAbsolute, pos, 100 );
            m_pGainAbsolute->setValue( val );
            posText.Format( TEXT( "%g %s" ), val, m_pGainAbsolute->getDimTypeW().c_str() );
            m_pGainStatic->SetWindowText( posText );
        }
        break;
    }

    CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}

Now run the application and use the sliders and the "One Push" button to manipulate the properties or use the check boxes to toggle the automation feature of the properties.