Click here to Skip to main content
15,881,852 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
Hello!

I'm programing with 3 webcams to build preview videos and then take 3 pictures from each one. My developing environment is visual studio c++ 2010 express/ clr/ windows forms app.

I can already successfully get three preview videos on my pc. But I'm facing with a big problem: when I connect my webcams to another computer using the same code, the capturing has an error (program breaks or showing videos in a different order).

After I tested this on several computers,
I finally found the reason. It is when the webcams are connected to another pc, the new pc may get a new order of them when the new pc counts the devices (Which means they were on my pc as usb-device 1, 2, 3; they are on the new pc as usb-device 2, 1, 3). But I need to use the webcams in the order like before. Webcam1 on pictureBox1, webcam2 on pBox2, and webcam3 on pBox3. And I would like my code to be generally useful, whatever on which pc and with any webcams.

What also means, I want to choose the webcams in an "user-wanted" order on the window(of 3 pictureBoxes). Now I'm thinking of one way to solve this, namely to put a comboBox under each pictureBox, which are used to choose the device for each pictureBox. But I don't know how to realize that. Could you please help me? Or do you have a better idea to prevent the problem???

What may also be very important: Could it be wrong with my counting devices method in the CamShow.cpp? Should I make some correctors there?

Any answers would be appreciated!

My recently code is:

Form.h:
private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
	CamShow test1;
        CamShow test2;
        CamShow test3;

	button1->Enabled=false; 
        
        hr = test1.InitComLib();
	hr = test1.CaptureVideo(1);
	System::Drawing::Rectangle rc = pictureBox1->ClientRectangle;
	hr = test1.pVW->put_Owner(OAHWND(this->pictureBox1->Handle.ToInt64()));

hr = test1.pVW->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
hr = test1.pVW->SetWindowPosition( 0, 0, rc.Right, rc.Bottom );
hr = test1.pMC->Run();

        test1.ControlRelease();
	test1.EventRelease();
	test1.GraphBuilderRelease();
        test1.CoUnini();

        hr = test2.InitComLib();
        hr = test2.CaptureVideo(2);
	System::Drawing::Rectangle rc = pictureBox1->ClientRectangle;
	hr = test2.pVW->put_Owner(OAHWND(this->pictureBox1->Handle.ToInt64()));
        // IVideoWindow *pVW

hr = test2.pVW->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
hr = test2.pVW->SetWindowPosition( 0, 0, rc.Right, rc.Bottom );
hr = test2.pMC->Run();

        test2.ControlRelease();
	test2.EventRelease();
	test2.GraphBuilderRelease();
        test2.CoUnini();

        hr = test3.InitComLib();
        hr= test3.CaptureVideo(3);
	System::Drawing::Rectangle rc = pictureBox1->ClientRectangle;
	hr = test3.pVW->put_Owner(OAHWND(this->pictureBox1->Handle.ToInt64()));

hr = test3.pVW->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
hr = test3.pVW->SetWindowPosition( 0, 0, rc.Right, rc.Bottom );
hr = test3.pMC->Run();

        test3.ControlRelease();
	test3.EventRelease();
	test3.GraphBuilderRelease();
        test3.CoUnini();
}


CamShow.cpp:

I give the most relevant code-sections to my problem here. And the declarations of variables functions are in CamShow.h, which I don't need to copy here for you I think ;).

#include "stdafx.h"
#include <DShow.h>
#include <Windows.h>
#include <comdef.h>
#include "CamShow.h"

CamShow::CamShow() {...}
CamShow::~CamShow() {}

HRESULT CamShow::InitComLib()
	{
		HRESULT hr = NULL;
		hr = CoInitialize(NULL);
		return hr;
	}
HRESULT CamShow::CaptureVideo(int NO)
	{
		HRESULT hr;
		IBaseFilter *pSrcFilter=NULL;

		// Get DirectShow interfaces
		hr = GetInterfaces();
		if (FAILED(hr))
		{
			Msg(TEXT("error get interfaces: hr=0x%x"), hr);
			return hr;
		}

                // Attach the filter graph to the capture graph
		hr = pCGB2->SetFiltergraph(pGraphBuilder); //pCGB2: ICaptureGraphBuilder2 *pCGB2 = NULL;
		if (FAILED(hr))
		{
			Msg(TEXT("error Set Filtergraph: hr=0x%x"), hr);
			return hr;
		}

                // Use the system device enumerator and class enumerator to find
             // a video capture/preview device, such as a desktop USB video camera.
		hr = FindCaptureDevice(&pSrcFilter, NO); 
		if (FAILED(hr))
		{
               // Don't display a message because FindCaptureDevice will handle it
			return hr;
		}

                // Add Capture filter to our graph. 
		hr = pGraphBuilder->AddFilter(pSrcFilter, L"Capture Filter");
		if (FAILED(hr))
		{
			Msg(TEXT("Couldn't add the capture filter to the graph!  hr=0x%x\r\n\r\n") 
            TEXT("If you have a working video capture device, please make sure\r\n")
            TEXT("that it is connected and is not being used by another application.\r\n\r\n")
            TEXT("The sample will now close."), hr);
                        pSrcFilter->Release();
			return hr;
		}

		// Render the preview pin on the video capture filter
                hr = pCGB2->RenderStream (&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,				          pSrcFilter, NULL, NULL);
		if (FAILED(hr))
		{
			Msg(TEXT("Couldn't render the video capture stream.  hr=0x%x\r\n"), hr);
			pSrcFilter->Release();
			return hr;
		}

// Now that the filter has been added to the graph and we have
// rendered its stream, we can release this reference to the filter.	
pSrcFilter->Release();
		

		// Set video window style and position		
                hr = intSetupVideoWindow();
		if (FAILED(hr))
		{
			Msg(TEXT"Couldn't initialize video window!  hr=0x%x"),hr);
			return hr;
		}

		// Start previewing video data
                hr = pMC->Run();  // IMediaControl *pMC
                if (FAILED(hr))
                {
                  Msg(TEXT("Couldn't run the graph!  hr=0x%x"), hr);
                  return hr;
                }

                // Remember current state
                psCurrent = Running;
        
                return S_OK;	
}

HRESULT CamShow::FindCaptureDevice(IBaseFilter ** ppSrcFilter, int NO)
	{
		HRESULT hr = S_OK;
		IBaseFilter * pSrc = NULL;
		IMoniker* pMoniker = NULL;
		ULONG cFetched;
		int DevNo=0;

		IEnumMoniker *pClassEnum = NULL;
		ICreateDevEnum *pDevEnum =NULL;

		if (!ppSrcFilter)  return E_POINTER; 		
		
                // Create the system device enumerator
		hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
					IID_ICreateDevEnum, (void **) &pDevEnum);
		if (FAILED(hr))
		{
		Msg(TEXT("Couldn't create system enumerator!  hr=0x%x"), hr);		        }
	
		 // Create an enumerator for the video capture devices
		if (SUCCEEDED(hr))
		{
              hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
			if (FAILED(hr))
			{
		        Msg(TEXT("Couldn't create class enumerator!  hr=0x%x"), hr);			}
			pDevEnum->Release();
		}

		if (SUCCEEDED(hr))
		{
		// If there are no enumerators for the requested type, then 
		// CreateClassEnumerator will succeed, but pClassEnum will be NULL.			if (pClassEnum == NULL)
			{
				MessageBox(ghApp,TEXT("No video capture device was detected.\r\n\r\n")
				TEXT("This sample requires a video capture device, such as a USB WebCam,\r\n")
				TEXT("to be installed and working properly.  The sample will now close."),
				TEXT("No Video Capture Hardware"), MB_OK | MB_ICONINFORMATION);				
                         hr = E_FAIL;
			}
		}


// This section counts the webcam devices on the computer!! 
		while (pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK)
		{	
			IPropertyBag *pBag = NULL;
                        BSTR sName;
			VARIANT var;
			VariantInit(&var);
	hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);

			 hr = pBag->Read(L"FriendlyName", &var, 0); 
			 sName = var.bstrVal; // Get the name, but not to be used
			 // Count the devices and give them the number in the order:1, 2, 3, ...
                         DevNo++;
			 VariantClear(&var);

			 if (SUCCEEDED(hr))
			 {
				 if (DevNo==NO) // I think this condition is problematic and kritic. What I thought is to get webcam N on pictureBox N, because I thought the devices should be counted in a same order on different PCs, that's wrong acturally.
		           { 
              // Bind Moniker to a filter object for every webcam 
	      hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void **)&pSrc);
	      if (FAILED(hr))
              return hr;
              
              pBag->Release();                 
	      pMoniker->Release();                 
              break; 
			   }
			 }

		}

        // Copy the found filter pointer to the output parameter.     
        if (SUCCEEDED(hr))
        {
	  *ppSrcFilter = pSrc;
	  (*ppSrcFilter)->AddRef();
	}


	return hr;
       }

......(Other functions)



I hope I've explained my problem clearly. If anything is not clear for you, just tell me and I'm going to answer you and improve my question.

Thanks for helping!
Posted
Updated 15-Oct-12 0:25am
v3
Comments
aasikRaja 16-Oct-12 2:01am    
Try to use camera name instead of assigning numbers to them.
1.First get the list of camera devices on your system
2.Then assign camera names to the specific handle u wish to use.
for eg:
if (cameraname == "X")
{
select handle 1 to run(i.e., picturebox1);
}
christmars 16-Oct-12 4:20am    
thanks! that's also what Maxim suggested(Solution 1) i think. I'm coding that currently, namly use the webcams with their friendly names from system.

1 solution

Hello,

Devices are enumerated in your PC according they were initially appear in the system (Installed/USB Connected and so on). To identify the camera you can use moniker display name. Is contains full path to the device in a system, It can contains device PID and VID or other device key, plus you can get access to the actual driver - so you can try to idendify devices anyway you need and build the proper order. Along with it you can sort this with names of cameras.

1. Get's moniker display name:
C++
HRESULT hr;
CComPtr< IMoniker > pMoniker;
// initialized moniker
CComPtr<IBindCtx> pBindCtx = NULL;
hr = CreateBindCtx(NULL,&pBindCtx);
if (SUCCEEDED(hr))
{
    LPOLESTR  lpszDisplayName   = NULL;
    hr = pMoniker->GetDisplayName(pBindCtx,NULL,&lpszDisplayName);
    if (lpszDisplayName)
    {
        // Use display name
        // ....
        // Clear memory
        CComPtr<IMalloc> pMalloc;
        hr = CoGetMalloc(1,&pMalloc);
        if (SUCCEEDED(hr)) pMalloc->Free(lpszDisplayName);
    }
}

2. Create IBaseFilter from moniker display name:
C++
HRESULT hr;
LPCWSTR szMoniker = L""; // your moniker display name
CComPtr<IBaseFilter> pFilter;
CComPtr<IBindCtx> pBindCtx;
hr = CreateBindCtx(0, &pBindCtx);
ULONG chEaten = 0;
CComPtr<IMoniker> pMoniker;
hr = MkParseDisplayName(pBindCtx, szMoniker, &chEaten, &pMoniker); // Create Moniker from display name
if (SUCCEEDED(hr))
{
    // Bind moniker to a DirectShow filter.
    hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pFilter);
    if (SUCCEEDED(hr))
    {
        // Usage of pFilter
    }
}

3. How to get camera name:
C++
HRESULT hr;
CComPtr< IMoniker > pMoniker; // initialized before

CComPtr< IPropertyBag > pBag;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**) &pBag);
if(SUCCEEDED(hr))
{
    VARIANT var;
    VariantInit(&var);
    hr = pBag->Read(L"FriendlyName",&var, NULL);
    if(SUCCEEDED(hr))
    {
        // Usage of device name - var.bstrVal
        wprintf(L"%s",var.bstrVal); // - Just an example of usage
    }
    VariantClear(&var);
}

I think you not be require to access driver object directly but that possible via IKsObject interface and KsSynchronousDeviceControl API.
Additional notes - you should review your code (I just look at the FindCaptureDevice function) it consist of a lot of memory leaks - you are not handle releasing COM objects, in case of HRESULT failure you will get app crash.
Other CoInitialize necessary to call only one time for each thread; if you initialize and use your cameras in one thread you are not require to call it each time before initialization.
I suggest you to read information about COM, and how to use it.

Regards,
Maxim.
 
Share this answer
 
v2
Comments
christmars 15-Oct-12 5:30am    
Thanks a lot!! I'd review my code and learn more about COM basics, I am just a beginner in this area.
christmars 16-Oct-12 8:41am    
Hi Maxim,

I'm coding to get the camera names. I want to get the 3 names in 3 strings(not like you did, using wprintf).
But unfortunaly, I can only get one name but not three. My prog is:

while (pClassEnum->Next(1, &pReadMoniker, &cFetched) == S_OK)
{
hr = pReadMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
hr = pBag->Read(L"FriendlyName", &var, 0);

if (SUCCEEDED(hr))
{ String^ NAME = gcnew String(var.bstrVal);
}
VariantClear(&var);
}

I know a string can obviously get only one name. I think in the fact the names are made three times by prog and what be given is the last device name. So do I need a array string? Or can I get the three names each to a string? Could you help?
Maxim Kartavenkov 16-Oct-12 9:10am    
wprintf in my code just an example what field in variant you should use. Sure you need an array of strings or as you using .NET use any collection objects.
ananddhelawat 4-May-16 8:05am    
Hi Maxim,
When calling BindToObject on device moniker of webcam with Ibase filter then my system gives Access denied. So can you suggest how to fix this?
Veerendra-13142768 2-Aug-18 3:18am    
i want inter face my USB 2.0 which comes under universal serial Bus controller. how to interface our Device with Direct show application and also i want to enumerate device AS image Device for this what i have to do can you explain?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900