00001
00002
00003
00004
00005
00006
00007
00008 #include <qapplication.h>
00009
00010 #include <stdlib.h>
00011 #include <stdio.h>
00012 #include <iostream>
00013 #include <sys/types.h>
00014 #include <sys/stat.h>
00015 using namespace std;
00016
00017 #ifndef WIN32
00018 #include <unistd.h>
00019 #include <sys/ioctl.h>
00020 #include <fcntl.h>
00021 #include <linux/videodev.h>
00022 #include <mythtv/mythcontext.h>
00023
00024 #include "config.h"
00025 #else
00026
00027 #include <windows.h>
00028 #include "vfw.h"
00029 #endif
00030
00031 #include "h263.h"
00032 #include "webcam.h"
00033
00034 #ifdef WIN32
00035 QWidget *wcMainWidget;
00036 HWND wcMainHwnd;
00037 #endif
00038
00039 Webcam::Webcam(QWidget *parent, QWidget *localVideoWidget)
00040 {
00041 hDev = 0;
00042 DevName = "";
00043 picbuff1 = 0;
00044 imageLen = 0;
00045 frameSize = 0;
00046 fps = 5;
00047 killWebcamThread = true;
00048 wcFormat = 0;
00049 wcFlip = false;
00050
00051 #ifndef WIN32
00052 (void)parent;
00053 (void)localVideoWidget;
00054 vCaps.name[0] = 0;
00055 vCaps.maxwidth = 0;
00056 vCaps.maxheight = 0;
00057 vCaps.minwidth = 0;
00058 vCaps.minheight = 0;
00059 memset(&vWin, 0, sizeof(struct video_window));
00060 vWin.width = 0;
00061 vWin.height = 0;
00062 vPic.brightness = 0;
00063 vPic.depth = 0;
00064 vPic.palette = 0;
00065 vPic.colour = 0;
00066 vPic.contrast = 0;
00067 vPic.hue = 0;
00068
00069 #else
00070 wcMainWidget = parent;
00071 hwndWebcam = localVideoWidget->winId();
00072 wcMainHwnd = hwndCap = capCreateCaptureWindow(NULL, WS_CHILD | WS_VISIBLE, 2, 2,
00073 localVideoWidget->width()-4,
00074 localVideoWidget->height()-4,
00075 hwndWebcam, 1);
00076
00077 capSetCallbackOnVideoStream(hwndCap, frameReadyCallbackProc);
00078 capSetCallbackOnError(hwndCap, ErrorCallbackProc) ;
00079 capSetCallbackOnStatus(hwndCap, StatusCallbackProc) ;
00080 capDriverConnect(hwndCap, 0);
00081 capSetUserData(hwndCap, (long)this);
00082 #endif
00083 }
00084
00085
00086 QString Webcam::devName(QString WebcamName)
00087 {
00088 #ifndef WIN32
00089 int handle = open(WebcamName, O_RDWR);
00090 if (handle <= 0)
00091 return "";
00092
00093 struct video_capability tempCaps;
00094 ioctl(handle, VIDIOCGCAP, &tempCaps);
00095 ::close(handle);
00096 return tempCaps.name;
00097 #else
00098 return "WIN32 Webcam (TODO)";
00099 #endif
00100 }
00101
00102
00103 bool Webcam::camOpen(QString WebcamName, int width, int height)
00104 {
00105 bool opened=true;
00106 #ifdef WIN32
00107 capPreviewRate(hwndCap, fps);
00108 capPreviewScale(hwndCap, true);
00109 capPreview(hwndCap, true);
00110
00111 capCaptureGetSetup(hwndCap, &capParams, sizeof(capParams));
00112 capParams.fYield = true;
00113 capParams.fAbortLeftMouse = false;
00114 capParams.fAbortRightMouse = false;
00115 capParams.fLimitEnabled = false;
00116 capCaptureSetSetup(hwndCap, &capParams, sizeof(capParams));
00117
00118 capCaptureSequenceNoFile(hwndCap);
00119 #else
00120
00121 DevName = WebcamName;
00122
00123 if ((hDev <= 0) && (WebcamName.length() > 0))
00124 hDev = open(DevName, O_RDWR);
00125 if ((hDev <= 0) || (WebcamName.length() <= 0))
00126 {
00127 cerr << "Couldn't open camera " << DevName << endl;
00128 opened = false;
00129 }
00130 #endif
00131 if (opened)
00132 {
00133 readCaps();
00134
00135 if (!SetPalette(VIDEO_PALETTE_YUV420P) &&
00136 !SetPalette(VIDEO_PALETTE_YUV422P) &&
00137 !SetPalette(VIDEO_PALETTE_RGB24))
00138 {
00139 cout << "Webcam does not support YUV420P, YUV422P, or RGB24 modes; these are the only ones currently supported. Closing webcam.\n";
00140 camClose();
00141 return false;
00142 }
00143
00144
00145 frameCount = 0;
00146 totalCaptureMs = 0;
00147
00148 SetSize(width, height);
00149 int actWidth, actHeight;
00150 GetCurSize(&actWidth, &actHeight);
00151 if ((width != actWidth) || (height != actHeight))
00152 {
00153 cout << "Could not set webcam to " << width << "x" << height << "; got " << actWidth << "x" << actHeight << " instead.\n";
00154 }
00155
00156
00157 if (isGreyscale())
00158 {
00159 cerr << "Greyscale not yet supported" << endl;
00160
00161 camClose();
00162 return false;
00163 }
00164 else
00165 {
00166 switch (GetPalette())
00167 {
00168 case VIDEO_PALETTE_RGB24: frameSize = RGB24_LEN(WCWIDTH, WCHEIGHT); break;
00169 case VIDEO_PALETTE_RGB32: frameSize = RGB32_LEN(WCWIDTH, WCHEIGHT); break;
00170 case VIDEO_PALETTE_YUV420P: frameSize = YUV420P_LEN(WCWIDTH, WCHEIGHT); break;
00171 case VIDEO_PALETTE_YUV422P: frameSize = YUV422P_LEN(WCWIDTH, WCHEIGHT); break;
00172 default:
00173 cerr << "Palette mode " << GetPalette() << " not yet supported" << endl;
00174 camClose();
00175 return false;
00176 break;
00177 }
00178
00179 picbuff1 = new unsigned char [frameSize];
00180 }
00181
00182 switch(GetPalette())
00183 {
00184 case VIDEO_PALETTE_YUV420P: wcFormat = PIX_FMT_YUV420P; break;
00185 case VIDEO_PALETTE_YUV422P: wcFormat = PIX_FMT_YUV422P; break;
00186 case VIDEO_PALETTE_RGB24: wcFormat = PIX_FMT_BGR24; break;
00187 case VIDEO_PALETTE_RGB32: wcFormat = PIX_FMT_RGBA32; break;
00188 default:
00189 cerr << "Webcam: Unsupported palette mode " << GetPalette() << endl;
00190 camClose();
00191 return false;
00192 break;
00193 }
00194
00195 StartThread();
00196 }
00197 return opened;
00198 }
00199
00200
00201 void Webcam::camClose()
00202 {
00203 KillThread();
00204
00205 #ifndef WIN32
00206 if (hDev <= 0)
00207 cerr << "Can't close a camera that isn't open" << endl;
00208 else
00209 {
00210
00211
00212 ::close(hDev);
00213 hDev = 0;
00214 }
00215 #else
00216 capCaptureStop(hwndCap);
00217 capPreview(hwndCap, false);
00218 #endif
00219
00220 if (picbuff1)
00221 delete picbuff1;
00222
00223 picbuff1 = 0;
00224 }
00225
00226
00227 #ifdef WIN32
00228
00229 LRESULT CALLBACK Webcam::frameReadyCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
00230 {
00231 Webcam *wc = (Webcam *)(capGetUserData(wcMainHwnd));
00232 if (wc)
00233 {
00234 wc->ProcessFrame(lpVHdr->lpData, lpVHdr->dwBytesUsed);
00235 return (LRESULT) true;
00236 }
00237 else
00238 return (LRESULT) false;
00239 }
00240
00241 LRESULT CALLBACK Webcam::ErrorCallbackProc(HWND hWnd, int nErrID, LPSTR lpErrorText)
00242 {
00243 QString dbg = QString("Webcam Error: ") + QString::number(nErrID) + " " + lpErrorText;
00244 QApplication::postEvent(wcMainWidget, new WebcamEvent(WebcamEvent::WebcamErrorEv, dbg));
00245 return (LRESULT) true;
00246 }
00247
00248 LRESULT CALLBACK Webcam::StatusCallbackProc(HWND hWnd, int nID, LPSTR lpStatusText)
00249 {
00250 QString dbg = QString("Webcam Status: ") + QString::number(nID) + " " + lpStatusText;
00251 QApplication::postEvent(wcMainWidget, new WebcamEvent(WebcamEvent::WebcamDebugEv, dbg));
00252 return (LRESULT) true;
00253 }
00254
00255 #endif
00256
00257
00258
00259 void Webcam::readCaps()
00260 {
00261 #ifndef WIN32
00262 if (hDev > 0)
00263 {
00264 ioctl(hDev, VIDIOCGCAP, &vCaps);
00265 ioctl(hDev, VIDIOCGWIN, &vWin);
00266 ioctl(hDev, VIDIOCGPICT, &vPic);
00267 }
00268 #else
00269 capCaptureGetSetup(hwndCap, &capParams, sizeof(capParams));
00270 capGetVideoFormat(hwndCap, &bitmapInfo, sizeof(bitmapInfo));
00271 #endif
00272 }
00273
00274
00275 void Webcam::SetSize(int width, int height)
00276 {
00277
00278 #ifndef WIN32
00279 memset(&vWin, 0, sizeof(struct video_window));
00280 vWin.width = width;
00281 vWin.height = height;
00282
00283 if (ioctl(hDev, VIDIOCSWIN, &vWin) == -1)
00284 cerr << "Webcam: Error setting capture size " << width << "x" << height << endl;
00285 #else
00286 bitmapInfo.bmiHeader.biHeight = height;
00287 bitmapInfo.bmiHeader.biWidth = width;
00288 capSetVideoFormat(hwndCap, &bitmapInfo, sizeof(bitmapInfo));
00289 #endif
00290
00291 readCaps();
00292 }
00293
00294
00295 bool Webcam::SetPalette(unsigned int palette)
00296 {
00297 int depth;
00298
00299 switch(palette)
00300 {
00301 case VIDEO_PALETTE_YUV420P: depth = 12; break;
00302 case VIDEO_PALETTE_YUV422: depth = 16; break;
00303 case VIDEO_PALETTE_YUV422P: depth = 16; break;
00304 case VIDEO_PALETTE_RGB32: depth = 32; break;
00305 case VIDEO_PALETTE_RGB24: depth = 24; break;
00306 default: depth = 0; break;
00307 }
00308
00309 #ifndef WIN32
00310 vPic.palette = palette;
00311 vPic.depth = depth;
00312 ioctl(hDev, VIDIOCSPICT, &vPic);
00313 #else
00314 int winPalette = 0;
00315 switch(palette)
00316 {
00317 default:
00318 case VIDEO_PALETTE_YUV420P: winPalette = (MAKEFOURCC('I', 'Y', 'U', 'V')); break;
00319 case VIDEO_PALETTE_YUV422: winPalette = (MAKEFOURCC('U', 'Y', 'V', 'Y')); break;
00320 case VIDEO_PALETTE_YUV422P: winPalette = (MAKEFOURCC('Y', 'V', '1', '6')); break;
00321 case VIDEO_PALETTE_RGB32: winPalette = 0; break;
00322 case VIDEO_PALETTE_RGB24: winPalette = 0; break;
00323 }
00324 bitmapInfo.bmiHeader.biCompression = winPalette;
00325 bitmapInfo.bmiHeader.biBitCount = depth;
00326 capSetVideoFormat(hwndCap, &bitmapInfo, sizeof(bitmapInfo));
00327 #endif
00328
00329 readCaps();
00330
00331 #ifndef WIN32
00332 return (vPic.palette == palette ? true : false);
00333 #else
00334 return ((bitmapInfo.bmiHeader.biCompression == winPalette) && (bitmapInfo.bmiHeader.biBitCount == depth) ? true : false);
00335 #endif
00336 }
00337
00338
00339 unsigned int Webcam::GetPalette(void)
00340 {
00341 #ifndef WIN32
00342 return (vPic.palette);
00343 #else
00344 int winPalette = 0;
00345 switch(bitmapInfo.bmiHeader.biCompression)
00346 {
00347 case MAKEFOURCC('I', 'Y', 'U', 'V'): return VIDEO_PALETTE_YUV420P;
00348 case MAKEFOURCC('U', 'Y', 'V', 'Y'): return VIDEO_PALETTE_YUV422;
00349 case MAKEFOURCC('Y', 'V', '1', '6'): return VIDEO_PALETTE_YUV422P;
00350 default:
00351 case 0:
00352 if (bitmapInfo.bmiHeader.biBitCount == 24)
00353 return VIDEO_PALETTE_RGB24;
00354 else if (bitmapInfo.bmiHeader.biBitCount == 32)
00355 return VIDEO_PALETTE_RGB32;
00356 }
00357 return 0;
00358 #endif
00359 }
00360
00361
00362 #ifndef WIN32
00363
00364 int Webcam::SetBrightness(int v)
00365 {
00366 if ((v >= 0) && (v <= 65535))
00367 {
00368 if (hDev > 0)
00369 {
00370 vPic.brightness = v;
00371
00372 if (ioctl(hDev, VIDIOCSPICT, &vPic) == -1)
00373 cerr << "Error setting brightness" << endl;
00374
00375 readCaps();
00376 }
00377 }
00378 else
00379 cerr << "Invalid Brightness parameter" << endl;
00380 return vPic.brightness;
00381 }
00382
00383 int Webcam::SetContrast(int v)
00384 {
00385 if ((v >= 0) && (v <= 65535))
00386 {
00387 if (hDev > 0)
00388 {
00389 vPic.contrast = v ;
00390
00391 if (ioctl(hDev, VIDIOCSPICT, &vPic) == -1)
00392 cerr << "Error setting contrast" << endl;
00393
00394 readCaps();
00395 }
00396 }
00397 else
00398 cerr << "Invalid contrast parameter" << endl;
00399 return vPic.contrast;
00400 }
00401
00402
00403 int Webcam::SetColour(int v)
00404 {
00405 if ((v >= 0) && (v <= 65535))
00406 {
00407 if (hDev > 0)
00408 {
00409 vPic.colour = v;
00410
00411 if (ioctl(hDev, VIDIOCSPICT, &vPic) == -1)
00412 cerr << "Error setting colour" << endl;
00413
00414 readCaps();
00415 }
00416 }
00417 else
00418 cerr << "Invalid colour parameter" << endl;
00419 return vPic.colour;
00420 }
00421
00422
00423 int Webcam::SetHue(int v)
00424 {
00425 if ((v >= 0) && (v <= 65535))
00426 {
00427 if (hDev > 0)
00428 {
00429 vPic.hue = v;
00430
00431 if (ioctl(hDev, VIDIOCSPICT, &vPic) == -1)
00432 cerr << "Error setting hue" << endl;
00433
00434 readCaps();
00435 }
00436 }
00437 else
00438 cerr << "Invalid hue parameter" << endl;
00439 return vPic.hue;
00440 }
00441
00442 #endif
00443
00444
00445 int Webcam::SetTargetFps(wcClient *client, int f)
00446 {
00447 if ((f >= 1) && (f <= 30) && (client != 0))
00448 {
00449 WebcamLock.lock();
00450 client->fps = f;
00451 client->interframeTime = 1000/f;
00452 WebcamLock.unlock();
00453 }
00454 else
00455 cerr << "Invalid FPS parameter" << endl;
00456
00457 return fps;
00458 }
00459
00460
00461 int Webcam::GetActualFps()
00462 {
00463 return actualFps;
00464 }
00465
00466 void Webcam::GetMaxSize(int *x, int *y)
00467 {
00468 #ifdef WIN32
00469 *y=bitmapInfo.bmiHeader.biHeight; *x=bitmapInfo.bmiHeader.biWidth;
00470 #else
00471 *y=vCaps.maxheight; *x=vCaps.maxwidth;
00472 #endif
00473 };
00474
00475 void Webcam::GetMinSize(int *x, int *y)
00476 {
00477 #ifdef WIN32
00478 *y=bitmapInfo.bmiHeader.biHeight; *x=bitmapInfo.bmiHeader.biWidth;
00479 #else
00480 *y=vCaps.minheight; *x=vCaps.minwidth;
00481 #endif
00482 };
00483
00484 void Webcam::GetCurSize(int *x, int *y)
00485 {
00486 *y = WCHEIGHT;
00487 *x = WCWIDTH;
00488 };
00489
00490 int Webcam::isGreyscale()
00491 {
00492 #ifdef WIN32
00493 return false;
00494 #else
00495 return ((vCaps.type & VID_TYPE_MONOCHROME) ? true : false);
00496 #endif
00497 };
00498
00499
00500
00501 wcClient *Webcam::RegisterClient(int format, int fps, QObject *eventWin)
00502 {
00503 wcClient *client = new wcClient;
00504
00505 if (fps == 0)
00506 {
00507 fps = 10;
00508 cerr << "Webcam requested fps of zero\n";
00509 }
00510
00511 client->eventWindow = eventWin;
00512 client->fps = fps;
00513 client->actualFps = fps;
00514 client->interframeTime = 1000/fps;
00515 client->timeLastCapture = QTime::currentTime();
00516 client->framesDelivered = 0;
00517
00518 switch (format)
00519 {
00520 case VIDEO_PALETTE_RGB24: client->frameSize = RGB24_LEN(WCWIDTH, WCHEIGHT); client->format = PIX_FMT_BGR24; break;
00521 case VIDEO_PALETTE_RGB32: client->frameSize = RGB32_LEN(WCWIDTH, WCHEIGHT); client->format = PIX_FMT_RGBA32; break;
00522 case VIDEO_PALETTE_YUV420P: client->frameSize = YUV420P_LEN(WCWIDTH, WCHEIGHT); client->format = PIX_FMT_YUV420P; break;
00523 case VIDEO_PALETTE_YUV422P: client->frameSize = YUV422P_LEN(WCWIDTH, WCHEIGHT); client->format = PIX_FMT_YUV422P; break;
00524 default:
00525 cerr << "SIP: Attempt to register unsupported Webcam format\n";
00526 delete client;
00527 return 0;
00528 }
00529
00530
00531 for (int i=0; i<WC_CLIENT_BUFFERS; i++)
00532 client->BufferList.append(new unsigned char[client->frameSize]);
00533
00534 WebcamLock.lock();
00535 wcClientList.append(client);
00536 WebcamLock.unlock();
00537
00538 return client;
00539 }
00540
00541 void Webcam::ChangeClientFps(wcClient *client, int fps)
00542 {
00543 if (client == 0)
00544 return;
00545
00546 if (fps == 0)
00547 {
00548 fps = 10;
00549 cerr << "Webcam requested fps of zero\n";
00550 }
00551
00552 WebcamLock.lock();
00553 client->fps = fps;
00554 client->actualFps = fps;
00555 client->interframeTime = 1000/fps;
00556 WebcamLock.unlock();
00557 }
00558
00559 void Webcam::UnregisterClient(wcClient *client)
00560 {
00561 WebcamLock.lock();
00562 wcClientList.remove(client);
00563 WebcamLock.unlock();
00564
00565
00566 unsigned char *it;
00567 while ((it=client->BufferList.first()) != 0)
00568 {
00569 client->BufferList.remove(it);
00570 delete it;
00571 }
00572
00573
00574 while ((it=client->FullBufferList.first()) != 0)
00575 {
00576 client->FullBufferList.remove(it);
00577 delete it;
00578 }
00579
00580 if (actualFps < client->fps)
00581 cerr << "Client wanted a FPS of " << client->fps << " but the camera delivered " << actualFps << endl;
00582
00583 delete client;
00584 }
00585
00586 unsigned char *Webcam::GetVideoFrame(wcClient *client)
00587 {
00588 WebcamLock.lock();
00589 unsigned char *buffer = client->FullBufferList.first();
00590 if (buffer)
00591 client->FullBufferList.remove(buffer);
00592 WebcamLock.unlock();
00593 return buffer;
00594 }
00595
00596 void Webcam::FreeVideoBuffer(wcClient *client, unsigned char *buffer)
00597 {
00598 WebcamLock.lock();
00599 if (buffer)
00600 client->BufferList.append(buffer);
00601 WebcamLock.unlock();
00602 }
00603
00604 void Webcam::StartThread()
00605 {
00606 #ifndef WIN32
00607 killWebcamThread = false;
00608 start();
00609 #endif
00610 }
00611
00612 void Webcam::KillThread()
00613 {
00614 #ifndef WIN32
00615 if (!killWebcamThread)
00616 {
00617 killWebcamThread = true;
00618 if (!wait(2000))
00619 {
00620 terminate();
00621 wait();
00622 cout << "SIP Webcam thread failed to terminate gracefully and was killed\n";
00623 }
00624 }
00625 #endif
00626 }
00627
00628 void Webcam::run(void)
00629 {
00630 WebcamThreadWorker();
00631 }
00632
00633 void Webcam::WebcamThreadWorker()
00634 {
00635 #ifndef WIN32
00636 int len=0;
00637
00638 while((!killWebcamThread) && (hDev > 0))
00639 {
00640 if ((len = read(hDev, picbuff1, frameSize)) == frameSize)
00641 {
00642 if (killWebcamThread)
00643 break;
00644
00645 ProcessFrame(picbuff1, frameSize);
00646 }
00647 else
00648 cerr << "Error reading from webcam; got " << len << " bytes; expected " << frameSize << endl;
00649 }
00650 #endif
00651 }
00652
00653 void Webcam::ProcessFrame(unsigned char *frame, int fSize)
00654 {
00655 static unsigned char tempBuffer[MAX_RGB_704_576];
00656
00657 WebcamLock.lock();
00658
00659
00660 if (frameCount++ > 0)
00661 totalCaptureMs += cameraTime.msecsTo(QTime::currentTime());
00662 cameraTime = QTime::currentTime();
00663 if (totalCaptureMs != 0)
00664 actualFps = (frameCount*1000)/totalCaptureMs;
00665
00666
00667 if (wcFlip)
00668 {
00669 switch(wcFormat)
00670 {
00671 case PIX_FMT_YUV420P:
00672 flipYuv420pImage(frame, WCWIDTH, WCHEIGHT, tempBuffer);
00673 frame = tempBuffer;
00674 break;
00675 case PIX_FMT_YUV422P:
00676 flipYuv422pImage(frame, WCWIDTH, WCHEIGHT, tempBuffer);
00677 frame = tempBuffer;
00678 break;
00679 case PIX_FMT_RGBA32:
00680 flipRgb32Image(frame, WCWIDTH, WCHEIGHT, tempBuffer);
00681 frame = tempBuffer;
00682 break;
00683 case PIX_FMT_BGR24:
00684 case PIX_FMT_RGB24:
00685 flipRgb24Image(frame, WCWIDTH, WCHEIGHT, tempBuffer);
00686 frame = tempBuffer;
00687 break;
00688 default:
00689 cout << "No routine to flip this type\n";
00690 break;
00691 }
00692 }
00693
00694
00695
00696 wcClient *it;
00697 for (it=wcClientList.first(); it; it=wcClientList.next())
00698 {
00699
00700 if ((it->timeLastCapture).msecsTo(QTime::currentTime()) > it->interframeTime)
00701 {
00702
00703 unsigned char *buffer = it->BufferList.first();
00704 if (buffer != 0)
00705 {
00706 it->BufferList.remove(buffer);
00707 it->FullBufferList.append(buffer);
00708 }
00709 else
00710 buffer = it->FullBufferList.first();
00711
00712 if (buffer != 0)
00713 {
00714 it->framesDelivered++;
00715
00716
00717 if (wcFormat != it->format)
00718 {
00719 AVPicture image_in, image_out;
00720 avpicture_fill(&image_in, (uint8_t *)frame, wcFormat, WCWIDTH, WCHEIGHT);
00721 avpicture_fill(&image_out, (uint8_t *)buffer, it->format, WCWIDTH, WCHEIGHT);
00722 img_convert(&image_out, it->format, &image_in, wcFormat, WCWIDTH, WCHEIGHT);
00723
00724 QApplication::postEvent(it->eventWindow, new WebcamEvent(WebcamEvent::FrameReady, it));
00725 }
00726 else
00727 {
00728 memcpy(buffer, frame, fSize);
00729 QApplication::postEvent(it->eventWindow, new WebcamEvent(WebcamEvent::FrameReady, it));
00730 }
00731 }
00732 else
00733 cerr << "No webcam buffers\n";
00734
00735 it->timeLastCapture = QTime::currentTime();
00736 }
00737 }
00738
00739 WebcamLock.unlock();
00740 }
00741
00742 Webcam::~Webcam()
00743 {
00744 if (hDev > 0)
00745 camClose();
00746 }
00747