Link to home
Start Free TrialLog in
Avatar of jhshukla
jhshuklaFlag for United States of America

asked on

USB Camera

Hi

I have a USB camera from Logitech (QuickCam 8 for Notebooks Pro) and need to write a C/C++ program to capture frames from it. I have no clue how to start.

Anyone heard of ARToolkit? That is what my boss wants me to use but I cannot find any kind of documentation for it.
Should I open system > device manager and look for the addresses used by the cam? isn't that a potential crash?
I have MS Spy++ that can list the PID, name and memory used by any process. I don't think that  to read from some other prgm's mem would be a good idea either.
I have the device driver. if anybody could reverse engineer or hack it, that could be a great help.

thank you.
jaydutt
Avatar of Valadas2
Valadas2

I guess there must be some king of interface so different sorfwate can access the camera. I would try to post to the Programming Forum as it is more a programming issues than a hardware issue...
SOLUTION
Avatar of AlexFM
AlexFM

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
There would be no reason to reverse engineer/debug/hack the device driver unless you wanted to write another one yourself.

The capture functions from the Multimedia SDK seems to be a good idea, but unfortunately it's one I did not have when I needed to write a frame-capture application, so I went the DirectShow way.

Anyway, I have a minimal application (~200 lines) which uses DirectShow (DirectX SDK) to constantly capture frames from a selected capture device. I could also post fragments or the whole code.
Actually, I will post the code anyway. Note that it was designed to be a test/trial code, not production code, and I have added the comments for you just two minutes ago. Most of the COM/DirectShow error-checking should be good, but I have some "quick-n-dirty" code for the "dumping the frame" and the loop parts. (Hell, I have an infinite goto-loop!)

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <objbase.h>
#include <dshow.h>
#include <qedit.h>

//int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
int main(void)
{
      IGraphBuilder *filterGraph = NULL;

      ICreateDevEnum *devenum = NULL;
      IEnumMoniker *capenum = NULL;
      IPropertyBag *capdevinfo = NULL;

      IMoniker *capdev = NULL;

      IMediaControl *MAC = NULL;
      IMediaEvent *MAE = NULL;
      ISampleGrabber *samplegrabber = NULL;

      IBaseFilter *capfilter = NULL;
      IBaseFilter *capgrabber = NULL;
      IBaseFilter *capnull = NULL;

      VARIANT v;
      AM_MEDIA_TYPE mt;

      /* Initialize COM */
      if (FAILED(CoInitialize(NULL))) {
            printf("Failed to initialize COM.\n");
            return -1;
      }

      /* Finding and selecting a device */
      
      /* Create enumerator */
      if (FAILED(CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **) &devenum))) {
            printf("Failed to create SystemDeviceEnum instance.\n");
            return -1;
      }

      /* Make it a video enumerator */
      if (FAILED(devenum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &capenum, 0))) {
            printf("Failed to enumerate video capture devices.\n");
            return -1;
      }

      if (capenum == NULL) {
            printf("There are no video input devices available.\n");
            return 1;
      }

      printf("The devices:\n");

      while(capenum->Next(1, &capdev, NULL) == S_OK) {
            if (FAILED(capdev->BindToStorage(NULL, NULL, IID_IPropertyBag, (void **) &capdevinfo))) {
                  printf("Failed to query capture device.\n");
                  return -1;
            }

            VariantInit(&v);
            capdevinfo->Read(L"FriendlyName", &v, NULL);
            printf("%S\n", v.bstrVal);
            /* find the first device with the name "cam" in it :) */
            if ((wcsstr(v.bstrVal, L"cam")) || (wcsstr(v.bstrVal, L"Cam")) || (wcsstr(v.bstrVal, L"CAM"))) {
                  if (FAILED(capdev->BindToObject(NULL, NULL, IID_IBaseFilter, (void **) &capfilter))) {
                        printf("Failed to bind to capture device.\n");
                        return -1;
                  } else {
                        break;
                  }
            }
            VariantClear(&v);
      }

      capdevinfo->Release();
      capdev->Release();
      capenum->Release();
      devenum->Release();
      /* Finding and selecting a device ends */

      /* Create the Filter Graph - the main thing */
      if (FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **) &filterGraph))) {
            printf("Unable to create filter graph.\n");
            return -1;
      }

      /* Media Control and Media Event interfaces for running/stopping the graph */
      filterGraph->QueryInterface(IID_IMediaControl, (void **) &MAC);
      if (MAC == NULL) {
            printf("Unable to query media control interface.\n");
            return -1;
      }

      filterGraph->QueryInterface(IID_IMediaEvent, (void **) &MAE);
      if (MAE == NULL) {
            printf("Unable to query media event interface.\n");
            return -1;
      }

      /* Add the video source to graph */
      /* Graph looks like:
            Video Source
      */
      if (filterGraph->AddFilter(capfilter, L"Video Source") != S_OK) {
            printf("Unable to add source filter.\n");
            return -1;
      }

      /* Create the BMP grabber */
      if (FAILED(CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **) &capgrabber))) {
            printf("Unable to create video grabber.\n");
            return -1;
      }

      capgrabber->QueryInterface(IID_ISampleGrabber, (void **) &samplegrabber);
      if (samplegrabber == NULL) {
            printf("Unable to query video grabber interface.\n");
            return -1;
      }

      /* Add grabber to graph */
      /* Graph looks like:
            Video Source   Grabber
      */
      if (FAILED(filterGraph->AddFilter(capgrabber, L"Grabber"))) {
            printf("Unable to add grabber filter.\n");
            return -1;
      }

      /* Create the null renderer */
      if (FAILED(CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **) &capnull))) {
            printf("Unable to create null renderer.\n");
            return -1;
      }

      /* Add null renderer to graph */
      /* Graph looks like:
            Video Source   Grabber   Null Renderer
      */
      if (FAILED(filterGraph->AddFilter(capnull, L"Null Renderer"))) {
            printf("Unable to add null renderer filter.\n");
            return -1;
      }

      /* Create and set up the media format for the capture */
      ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
      mt.majortype = MEDIATYPE_Video;
      mt.subtype = MEDIASUBTYPE_RGB32;

      /* Webcam uses RGB32 although it has no alpha channel and thus returns '0'
      every 4th byte in the frame */

      mt.formattype = FORMAT_VideoInfo;

      if (FAILED(samplegrabber->SetMediaType(&mt))) {
            printf("Unable to set media type.\n");
            return -1;
      }

      /* Get all the pins */
      IEnumPins *pinenum;
      IPin *cameraOut;
      IPin *grabberIn;
      IPin *grabberOut;
      IPin *nullIn;
      
      /* Find camera output pin */
      capfilter->EnumPins(&pinenum);
      pinenum->Reset();
      pinenum->Next(1, &cameraOut, NULL);
      pinenum->Release();

      /* Find grabber input/output pins */
      capgrabber->EnumPins(&pinenum);
      pinenum->Reset();
      pinenum->Next(1, &grabberIn, NULL);
      pinenum->Next(1, &grabberOut, NULL);
      pinenum->Release();

      /* Find null renderer input pin */
      capnull->EnumPins(&pinenum);
      pinenum->Reset();
      pinenum->Next(1, &nullIn, NULL);
      pinenum->Release();

      /* Connect all the pins */

      if (FAILED(filterGraph->Connect(cameraOut, grabberIn))) {
            printf("Unable to connect camera output pin.\n");
            return -1;
      } /* Graph looks like:
            Video Source -> Grabber   Null Renderer
      */

      if (FAILED(filterGraph->Connect(grabberOut, nullIn))) {
            printf("Unable to connect grabber output pin.\n");
            return -1;
      } /* Graph looks like:
            Video Source -> Grabber -> Null Renderer
      */

      /* Enable internal buffering (internal frame storage) for the grabber */
      if (FAILED(samplegrabber->SetBufferSamples(TRUE))) {
            printf("Unable to set internal buffering.\n");
            return -1;
      }

      /* We're capturing lots of frames, not 1, comment it out */
/*      if (FAILED(samplegrabber->SetOneShot(TRUE))) {
            printf("Unable to set one-shot grabbing.\n");
            return -1;
      }*/

hede:
      if (FAILED(MAC->Run())) {
            printf("Error running graph.\n");
            return -1;
      }

      Sleep(3000);

      /* This is needed for 1-frame capture too, comment it out */
/*      long eventCode = 0;
      if (FAILED(MAE->WaitForCompletion(INFINITE, &eventCode))) {
            printf("Error waiting for completion.\n");
            return -1;
      }*/

      long bufsize = 0;
      char *buffer;
      /* Get current frame from the internal buffer */
      samplegrabber->GetCurrentBuffer(&bufsize, NULL);

      if (bufsize == 0) {
            printf("Sample grabber does not have a buffer.\n");
            return -1;
      } else {
            buffer = (char *) malloc(bufsize); /* VS.NET does not like uncasted mallocs, WTF?!? */
            if (buffer == NULL) {
                  printf("Unable to allocate %u bytes of memory.\n", (unsigned) bufsize);
                  return -1;
            }
      }

      samplegrabber->GetCurrentBuffer(&bufsize, (long *) buffer);

      /* Dump the frame into a temporary file */

      FILE *of;

      if ((of = fopen("C:\\TEMP\\test.bin", "wb")) == NULL) return -2;
      fwrite(buffer, bufsize, 1, of);
      fclose(of);
      free(buffer);

goto hede;

      capgrabber->Release();
      capfilter->Release();
      capnull->Release();
      filterGraph->Release();

      CoUninitialize();

      return 0;
}
no need for all these things
if ur camera supports TWAIN (and probably it is)
you can use one of many activex controls available on the internet

here u are 3 controls availble to try:

http://www.download.com/3120-20-0.html?qt=webcam&tg=dl-2026

if you're looking for a free/fast solution
install yahoo messenger
and u'll find a control used for video capture installed with it :)

Anyway, no need for reverse engineering, direct communication with the port, or even API calls...

never re-invent the wheels
Avatar of jhshukla

ASKER

I have posted a link to this question is C/C++ forums as suggested by valadas2.
and i will try out the things suggested by all of you. i have to go to work now so i will post the further development tomorrow.

thx for help so far and if possible please try to locate ARToolkit documentation for me. (Google: ARToolkit Download)

thx again.
jaydutt
Note that most of the TWAIN-compatible cameras pop up a camera driver-supplied "capture" dialog which ruins the "capture frames without intervention from the user" aspect of such projects.
Ok I tried out everything that was suggested here and would prefer to follow Alex's suggestions.

@Alex (but everyone is welcome!)
I was going through SDK Documentation and found this variable called DeviceDriverIndex/DriverIndex/wIndex etc that can range from 0-9.
How can I find the index of the driver I want to use?

thx
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I tried using capCreateCaptureWindow but it gives me "unresolved external" error. Apparently, it is replaced by _capCreateCaptureWindowA in compiler-generated symbol table. I included the header file that contains the prototype but that does not help. Am I missing something?

Jaydutt
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
thx
I can now create a CaptureWindow (i haven't tried connecting to the driver yet) but the window thus created is 100% transparent except for the titleBar and border. It does no close on Alt+F4 nor does it have Minimize/Maximixe/Close buttons. I know that I have to modify the window class for doing so but how can I do so?
The prototype for capCreateCaptureWindow is:
capCreateCaptureWindow(LPCTSTR, DWORD dwStyle, int, int, int, int, HWND Parent, int ID)

also how do I specify the WindowProcedure. Through window class?

thx
jaydutt
CaptureWindow should be child window. Create it as dialog or frame window child.
Check out CapTest and VidCap samples which are available in MSDN Library.
can't we specify wndProc for a child window?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
thanks for all the help. I finally figured out how to use ARtoolkit. I found a tutorial for it. It's very easy to use provided you read the source code carefully. It is open source and is actually a partial wrapper for Windows Multimedia and IO. thx anyways.

now i have this question about Visual Studio. As you told me earlier,
> Add Vfw32.lib to the list of libraries for linker: Project - Settings - Link - Library modules.
I added libarvideo.lib to my proj. It compiles and links perfectly but when I try to run the exe it complains about missing "libarvideo.dll". how do i go about solving this problem?

thanks
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
thanks. I learned a lot.