vfw.cxx
上传用户:hzhsqp
上传日期:2007-01-06
资源大小:1600k
文件大小:12k
源码类别:

IP电话/视频会议

开发平台:

Visual C++

  1. /*
  2.  * vfw.cxx
  3.  *
  4.  * Classes to support streaming video input (grabbing) and output.
  5.  *
  6.  * Portable Windows Library
  7.  *
  8.  * Copyright (c) 1993-2000 Equivalence Pty. Ltd.
  9.  *
  10.  * The contents of this file are subject to the Mozilla Public License
  11.  * Version 1.0 (the "License"); you may not use this file except in
  12.  * compliance with the License. You may obtain a copy of the License at
  13.  * http://www.mozilla.org/MPL/
  14.  *
  15.  * Software distributed under the License is distributed on an "AS IS"
  16.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  17.  * the License for the specific language governing rights and limitations
  18.  * under the License.
  19.  *
  20.  * The Original Code is Portable Windows Library.
  21.  *
  22.  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
  23.  *
  24.  * Contributor(s): ______________________________________.
  25.  *
  26.  * $Log: vfw.cxx,v $
  27.  * Revision 1.4  2000/07/26 03:50:50  robertj
  28.  * Added last error variable to video device.
  29.  *
  30.  * Revision 1.3  2000/07/25 13:38:26  robertj
  31.  * Added frame rate parameter to video frame grabber.
  32.  *
  33.  * Revision 1.2  2000/07/25 13:14:07  robertj
  34.  * Got the video capture stuff going!
  35.  *
  36.  * Revision 1.1  2000/07/15 09:47:35  robertj
  37.  * Added video I/O device classes.
  38.  *
  39.  */
  40. #include <ptlib.h>
  41. #include <ptlib/videoio.h>
  42. class PVideoInputThread : public PThread
  43. {
  44.   PCLASSINFO(PVideoInputThread, PThread);
  45.   public:
  46.     PVideoInputThread(PVideoInputDevice & dev)
  47.       : PThread(30000, NoAutoDeleteThread, HighPriority), device(dev) { Resume(); }
  48.     void Main() { device.HandleCapture(); }
  49.   protected:
  50.     PVideoInputDevice & device;
  51. };
  52. ///////////////////////////////////////////////////////////////////////////////
  53. class PCapStatus : public CAPSTATUS
  54. {
  55.   public:
  56.     PCapStatus(HWND hWnd);
  57.     BOOL IsOK() { return uiImageWidth != 0; }
  58. };
  59. ///////////////////////////////////////////////////////////////////////////////
  60. class PVideoDeviceBitmap : PBYTEArray
  61. {
  62.   public:
  63.     PVideoDeviceBitmap(unsigned width, unsigned height, PVideoDevice::ColourFormat fmt);
  64.     PVideoDeviceBitmap(HWND hWnd);
  65.     BOOL ApplyFormat(HWND hWnd) { return capSetVideoFormat(hWnd, theArray, GetSize()); }
  66.     BITMAPINFO * operator->() const { return (BITMAPINFO *)theArray; }
  67. };
  68. ///////////////////////////////////////////////////////////////////////////////
  69. PVideoDeviceBitmap::PVideoDeviceBitmap(unsigned width, unsigned height,
  70.                                        PVideoDevice::ColourFormat fmt)
  71.   : PBYTEArray(sizeof(BITMAPINFO))
  72. {
  73.   PINDEX i;
  74.   BITMAPINFO * bi = (BITMAPINFO *)theArray;
  75.   bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  76.   bi->bmiHeader.biWidth = width;
  77.   bi->bmiHeader.biHeight = height;
  78.   bi->bmiHeader.biPlanes = 1;
  79.   switch (fmt) {
  80.     case PVideoDevice::Grey :
  81.       bi->bmiHeader.biCompression = BI_RGB;
  82.       bi->bmiHeader.biBitCount = 8;
  83.       SetSize(sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256);
  84.       for (i = 0; i < 256; i++)
  85.         bi->bmiColors[i].rgbBlue = bi->bmiColors[i].rgbGreen = bi->bmiColors[i].rgbRed = (BYTE)i;
  86.       break;
  87.     case PVideoDevice::RGB24 :
  88.       bi->bmiHeader.biCompression = BI_RGB;
  89.       bi->bmiHeader.biBitCount = 24;
  90.       break;
  91.     case PVideoDevice::RGB32 :
  92.       bi->bmiHeader.biCompression = BI_RGB;
  93.       bi->bmiHeader.biBitCount = 32;
  94.       break;
  95.     case PVideoDevice::YUV422 :
  96.       bi->bmiHeader.biCompression = mmioFOURCC('Y', 'U', 'V', '8');
  97.       bi->bmiHeader.biBitCount = 8;
  98.       break;
  99.     case PVideoDevice::RGB565 :
  100.       bi->bmiHeader.biCompression = BI_BITFIELDS;
  101.       bi->bmiHeader.biBitCount = 16;
  102.       break;
  103.     case PVideoDevice::MJPEG :
  104.       bi->bmiHeader.biCompression = mmioFOURCC('M','J','P','G');
  105.       bi->bmiHeader.biBitCount = 0;
  106.       break;
  107.   }
  108.   bi->bmiHeader.biSizeImage = height*((bi->bmiHeader.biBitCount*width + 31)/32)*4;
  109. }
  110. PVideoDeviceBitmap::PVideoDeviceBitmap(HWND hCaptureWindow)
  111. {
  112.   PINDEX sz = capGetVideoFormatSize(hCaptureWindow);
  113.   SetSize(sz);
  114.   if (capGetVideoFormat(hCaptureWindow, theArray, sz))
  115.     return;
  116.   PTRACE(1, "capGetVideoFormat: failed - " << ::GetLastError());
  117.   SetSize(0);
  118. }
  119. ///////////////////////////////////////////////////////////////////////////////
  120. PCapStatus::PCapStatus(HWND hWnd)
  121. {
  122.   memset(this, 0, sizeof(*this));
  123.   if (capGetStatus(hWnd, this, sizeof(*this)))
  124.     return;
  125.   PTRACE(1, "capGetStatus: failed - " << ::GetLastError());
  126. }
  127. ///////////////////////////////////////////////////////////////////////////////
  128. // PVideoDevice
  129. PVideoInputDevice::PVideoInputDevice(VideoFormat videoFmt,
  130.                                      unsigned channel,
  131.                                      ColourFormat colourFmt)
  132.   : PVideoDevice(videoFmt, channel, colourFmt)
  133. {
  134.   captureThread = NULL;
  135.   hCaptureWindow = NULL;
  136. }
  137. BOOL PVideoInputDevice::Open(const PString & devName, BOOL startImmediate)
  138. {
  139.   Close();
  140.   deviceName = devName;
  141.   captureThread = new PVideoInputThread(*this);
  142.   threadStarted.Wait();
  143.   if (hCaptureWindow == NULL) {
  144.     delete captureThread;
  145.     captureThread = NULL;
  146.     return FALSE;
  147.   }
  148.   if (startImmediate)
  149.     return Start();
  150.   return TRUE;
  151. }
  152. BOOL PVideoInputDevice::IsOpen() const
  153. {
  154.   return hCaptureWindow != NULL;
  155. }
  156. BOOL PVideoInputDevice::Close()
  157. {
  158.   if (!IsOpen())
  159.     return FALSE;
  160.   Stop();
  161.   ::PostThreadMessage(captureThread->GetThreadId(), WM_QUIT, 0, 0L);
  162.   captureThread->WaitForTermination();
  163.   delete captureThread;
  164.   captureThread = NULL;
  165.   return TRUE;
  166. }
  167. BOOL PVideoInputDevice::Start()
  168. {
  169.   if (capCaptureSequenceNoFile(hCaptureWindow))
  170.     return TRUE;
  171.   lastError = ::GetLastError();
  172.   PTRACE(1, "capCaptureSequenceNoFile: failed - " << lastError);
  173.   return FALSE;
  174. }
  175. BOOL PVideoInputDevice::Stop()
  176. {
  177.   if (capCaptureStop(hCaptureWindow))
  178.     return TRUE;
  179.   lastError = ::GetLastError();
  180.   PTRACE(1, "capCaptureStop: failed - " << lastError);
  181.   return FALSE;
  182. }
  183. BOOL PVideoInputDevice::IsCapturing()
  184. {
  185.   PCapStatus status(hCaptureWindow);
  186.   return status.fCapturingNow;
  187. }
  188. BOOL PVideoInputDevice::SetFrameRate(unsigned rate)
  189. {
  190.   if (!PVideoDevice::SetFrameRate(rate))
  191.     return FALSE;
  192.   BOOL running = IsCapturing();
  193.   if (running)
  194.     Stop();
  195.   CAPTUREPARMS parms;
  196.   memset(&parms, 0, sizeof(parms));
  197.   if (!capCaptureGetSetup(hCaptureWindow, &parms, sizeof(parms))) {
  198.     lastError = ::GetLastError();
  199.     PTRACE(1, "capCaptureGetSetup: failed - " << lastError);
  200.     return FALSE;
  201.   }
  202.   parms.dwRequestMicroSecPerFrame = 100000000 / frameRate;
  203.   parms.fMakeUserHitOKToCapture = FALSE;
  204.   parms.wPercentDropForError = 100;
  205.   parms.fCaptureAudio = FALSE;
  206.   parms.fAbortLeftMouse = FALSE;
  207.   parms.fAbortRightMouse = FALSE;
  208.   parms.fLimitEnabled = FALSE;
  209.   if (!capCaptureSetSetup(hCaptureWindow, &parms, sizeof(parms))) {
  210.     lastError = ::GetLastError();
  211.     PTRACE(1, "capCaptureSetSetup: failed - " << lastError);
  212.     return FALSE;
  213.   }
  214.   if (running)
  215.     return Start();
  216.   return TRUE;
  217. }
  218. BOOL PVideoInputDevice::SetFrameSize(unsigned width, unsigned height)
  219. {
  220.   if (!PVideoDevice::SetFrameSize(width, height))
  221.     return FALSE;
  222.   BOOL running = IsCapturing();
  223.   if (running)
  224.     Stop();
  225.   ::SetWindowPos(hCaptureWindow, NULL, 0, 0,
  226.                  width + GetSystemMetrics(SM_CXFIXEDFRAME),
  227.                  height + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFIXEDFRAME),
  228.                  SWP_NOMOVE|SWP_NOACTIVATE);
  229.   PCapStatus status(hCaptureWindow);
  230.   if (status.IsOK()) {
  231.     frameWidth = status.uiImageWidth;
  232.     frameHeight = status.uiImageHeight;
  233.   }
  234.   if (running)
  235.     return Start();
  236.   return TRUE;
  237. }
  238. BOOL PVideoInputDevice::SetColourFormat(ColourFormat colourFmt)
  239. {
  240.   BOOL running = IsCapturing();
  241.   if (running)
  242.     Stop();
  243.   ColourFormat oldFormat = colourFormat;
  244.   if (!PVideoDevice::SetColourFormat(colourFmt))
  245.     return FALSE;
  246.   PVideoDeviceBitmap bitmapInfo(frameWidth, frameHeight, colourFmt);
  247.   if (!bitmapInfo.ApplyFormat(hCaptureWindow)) {
  248.     lastError = ::GetLastError();
  249.     PTRACE(1, "capSetVideoFormat: failed - " << lastError);
  250.     PVideoDevice::SetColourFormat(oldFormat);
  251.     return FALSE;
  252.   }
  253.   if (running)
  254.     return Start();
  255.   return TRUE;
  256. }
  257. PStringList PVideoInputDevice::GetDeviceNames() const
  258. {
  259.   PStringList list;
  260.   for (WORD devId = 0; devId < 10; devId++) {
  261.     char name[100];
  262.     char version[200];
  263.     if (capGetDriverDescription(devId, name, sizeof(name), version, sizeof(version)))
  264.       list.AppendString(name);
  265.   }
  266.   return list;
  267. }
  268. PINDEX PVideoInputDevice::GetMaxFrameBytes()
  269. {
  270.   return PVideoDeviceBitmap(hCaptureWindow)->bmiHeader.biSizeImage;
  271. }
  272. BOOL PVideoInputDevice::GetFrameData(BYTE * buffer, PINDEX * bytesReturned)
  273. {
  274.   if (!frameAvailable.Wait(1000))
  275.     return FALSE;
  276.   lastFrameMutex.Wait();
  277.   memcpy(buffer, lastFramePtr, lastFrameSize);
  278.   if (bytesReturned != NULL)
  279.     *bytesReturned = lastFrameSize;
  280.   lastFrameMutex.Signal();
  281.   return TRUE;
  282. }
  283.     
  284. LRESULT CALLBACK PVideoInputDevice::ErrorHandler(HWND hWnd, int id, LPCSTR err)
  285. {
  286.   if (hWnd == NULL)
  287.     return FALSE;
  288.   return ((PVideoInputDevice *)capGetUserData(hWnd))->HandleError(id, err);
  289. }
  290. LRESULT PVideoInputDevice::HandleError(int id, LPCSTR err)
  291. {
  292.   if (id != 0) {
  293.     PTRACE(1, "ErrorHandler: [id="<< id << "] " << err);
  294.   }
  295.   return TRUE;
  296. }
  297. LRESULT CALLBACK PVideoInputDevice::VideoHandler(HWND hWnd, LPVIDEOHDR vh)
  298. {
  299.   if (hWnd == NULL)
  300.     return FALSE;
  301.   return ((PVideoInputDevice *)capGetUserData(hWnd))->HandleVideo(vh);
  302. }
  303. LRESULT PVideoInputDevice::HandleVideo(LPVIDEOHDR vh)
  304. {
  305.   if (vh->dwFlags & VHDR_DONE) {
  306.     lastFrameMutex.Wait();
  307.     lastFramePtr = vh->lpData;
  308.     lastFrameSize = vh->dwBytesUsed;
  309.     lastFrameMutex.Signal();
  310.     frameAvailable.Signal();
  311.   }
  312.   return TRUE;
  313. }
  314. BOOL PVideoInputDevice::InitialiseCapture()
  315. {
  316.   if ((hCaptureWindow = capCreateCaptureWindow("Capture Window",
  317.                                                WS_POPUP | WS_CAPTION,
  318.                                                CW_USEDEFAULT, CW_USEDEFAULT,
  319.                                                frameWidth + GetSystemMetrics(SM_CXFIXEDFRAME),
  320.                                                frameHeight + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFIXEDFRAME),
  321.                                                (HWND)0,
  322.                                                0)) == NULL) {
  323.     lastError = ::GetLastError();
  324.     PTRACE(1, "capCreateCaptureWindow: failed");
  325.     return FALSE;
  326.   }
  327.   capSetCallbackOnError(hCaptureWindow, ErrorHandler);
  328.   if (!capSetCallbackOnVideoStream(hCaptureWindow, VideoHandler)) {
  329.     lastError = ::GetLastError();
  330.     PTRACE(1, "capSetCallbackOnVideoStream: failed - " << lastError);
  331.     return FALSE;
  332.   }
  333.   WORD devId;
  334.   if (deviceName.GetLength() == 1 && isdigit(deviceName[0]))
  335.     devId = (WORD)(deviceName[0] - '0');
  336.   else {
  337.     for (devId = 0; devId < 10; devId++) {
  338.       char name[100];
  339.       char version[200];
  340.       if (capGetDriverDescription(devId, name, sizeof(name), version, sizeof(version)) &&
  341.           (deviceName *= name))
  342.         break;
  343.     }
  344.   }
  345.   // Use first driver available.
  346.   if (!capDriverConnect(hCaptureWindow, devId)) {
  347.     lastError = ::GetLastError();
  348.     PTRACE(1, "capDriverConnect: failed - " << lastError);
  349.     return FALSE;
  350.   }
  351.   capSetUserData(hCaptureWindow, this);
  352.   CAPDRIVERCAPS driverCaps;
  353.   memset(&driverCaps, 0, sizeof(driverCaps));
  354.   if (!capDriverGetCaps(hCaptureWindow, &driverCaps, sizeof(driverCaps))) {
  355.     lastError = ::GetLastError();
  356.     PTRACE(1, "capGetDriverCaps: failed - " << lastError);
  357.     return FALSE;
  358.   }
  359.   if (driverCaps.fHasOverlay)
  360.     capOverlay(hCaptureWindow, TRUE);
  361.   else {
  362.     capPreviewRate(hCaptureWindow, 66);
  363.     capPreview(hCaptureWindow, TRUE);
  364.   }
  365.   if (!SetFrameRate(frameRate))
  366.     return FALSE;
  367.   if (!SetFrameSize(frameWidth, frameHeight))
  368.     return FALSE;
  369.   return SetColourFormat(colourFormat);
  370. }
  371. void PVideoInputDevice::HandleCapture()
  372. {
  373.   if (InitialiseCapture()) {
  374.     threadStarted.Signal();
  375.     MSG msg;
  376.     while (::GetMessage(&msg, NULL, 0, 0))
  377.       ::DispatchMessage(&msg);
  378.   }
  379.   capDriverDisconnect(hCaptureWindow);
  380.   capSetCallbackOnVideoStream(hCaptureWindow, NULL);
  381.   DestroyWindow(hCaptureWindow);
  382.   hCaptureWindow = NULL;
  383.   threadStarted.Signal();
  384. }
  385. // End Of File ///////////////////////////////////////////////////////////////