/* * Module to grab video data from VfW interface. The interface is * poorly documented for which some odd comments maybe found here. */ #include "config.h" #include #include #include #include #include #include #include "convert.h" #include "videodev-vfw.h" static char classNameVfW[] = "VFW-Class"; /* * Constructor */ VideoDev_VFW::VideoDev_VFW() { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); hwnd = cap = NULL; camid = -1; inframe_pixfmt = 0x0; inframe_w = -1; inframe_h = -1; inframe = NULL; inframe_size = 0; vfw_size = 0; hclass = NULL; hinst = NULL; }; /* * Destructor */ VideoDev_VFW::~VideoDev_VFW() { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); if (running > 0) CaptureStop(); } /* * Check for connected devices and returns the result in a list. */ int VideoDev_VFW::GetDeviceList(std::list *list) { char name[256]; char desc[256]; printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); if (list == NULL) return VDEV_STATUS_ERROR; // go through a list of 10 (maximum number defined by VfW) for(int i=0; i < 10; i++) if(capGetDriverDescription(i, name, sizeof(name), desc, sizeof(desc))) { // create a driver for the return list std::string device; device = "VFW " + to_string(i) + " " + (string)name + " [" + (string)desc + "]"; printf ("%s:%d %s Found device '%s'\n", __FILE__, __LINE__, __FUNCTION__, device.c_str()); list->push_back(device); } return VDEV_STATUS_OK; } /* * Destroy and unregister window class. */ int VideoDev_VFW::DestroyClass() { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); if (hclass) { if(!UnregisterClass(classNameVfW, hinst)) { printf ("%s:%d %s Could not unregister VFW class\n", __FILE__, __LINE__, __FUNCTION__); return 0; } free (hclass); hclass = NULL; } return 1; } /* * Create and register window class. */ int VideoDev_VFW::CreateClass() { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); if (hclass) return 1; hclass = (WNDCLASSEX *)malloc(sizeof(WNDCLASSEX)); hclass->hInstance = hinst; hclass->lpszClassName = classNameVfW; hclass->lpfnWndProc = DefWindowProc; hclass->style = CS_DBLCLKS; hclass->cbSize = sizeof(WNDCLASSEX); hclass->hIcon = LoadIcon(NULL,IDI_APPLICATION); hclass->hIconSm = LoadIcon(NULL,IDI_APPLICATION); hclass->hCursor = LoadCursor(NULL,IDC_ARROW); hclass->lpszMenuName = NULL; hclass->cbClsExtra = 0; hclass->cbWndExtra = 0; hclass->hbrBackground = (HBRUSH)COLOR_BACKGROUND; if(!RegisterClassEx (hclass)) { printf ("%s:%d %s Could not register VFW class\n", __FILE__, __LINE__, __FUNCTION__); return 0; } return 1; } /* * Print driver capabilities. */ void VideoDev_VFW::GetCapabilities() { CAPDRIVERCAPS caps; if(!capDriverGetCaps(cap, &caps, sizeof(caps))) { printf ("%s:%d %s Unable to get driver capabilities\n", __FILE__, __LINE__, __FUNCTION__); } else { printf ("%s:%d %s Id: %d\n", __FILE__, __LINE__, __FUNCTION__, caps.wDeviceIndex); printf ("%s:%d %s Overlay: %d\n", __FILE__, __LINE__, __FUNCTION__, caps.fHasOverlay); printf ("%s:%d %s VideoSource Dialog: %d\n", __FILE__, __LINE__, __FUNCTION__, caps.fHasDlgVideoSource); printf ("%s:%d %s VideoFormat Dialog: %d\n", __FILE__, __LINE__, __FUNCTION__, caps.fHasDlgVideoFormat); printf ("%s:%d %s VideoDisplay Dialog: %d\n", __FILE__, __LINE__, __FUNCTION__, caps.fHasDlgVideoDisplay); } } /* * The callback is run when an error in the capture driver occurred. */ LRESULT VFW_error_callback (HWND h, int nID, LPCSTR lpsz) { printf("%s:%d %s id=%d (%s)\n", __FILE__, __LINE__, __FUNCTION__, nID, lpsz); return TRUE; } /* * The callback is run when a status update in the capture driver occurred. */ LRESULT VFW_status_callback (HWND h, int nID, LPCSTR lpsz) { printf("%s:%d %s id=%d (%s)\n", __FILE__, __LINE__, __FUNCTION__, nID, lpsz); return TRUE; } #define USE_PREVIEW 0 /* * Opens the device. */ int VideoDev_VFW::Open() { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); // extract camera id from device name camid = atoi (conf_device.c_str()); printf ("%s:%d %s Opening VFW id %d\n", __FILE__, __LINE__, __FUNCTION__, camid); // find application instance handle for later use hinst = GetModuleHandle(NULL); // create and register window class if (!CreateClass()) return VDEV_STATUS_ERROR; // now create the parent window for capture devicey // some notes: // - the minimum possible size of 3x3 // - the WS_POPUPWINDOW removes window decorations // - it does not need a parent window // - the window MUST be visible, otherwise video grabbing does not work (no callbacks run) #if USE_PREVIEW hwnd = CreateWindowEx(0, classNameVfW, "CameraPreview", WS_POPUPWINDOW|WS_VISIBLE, 0, 0, 3, 3, NULL, NULL, hinst, NULL); #else hwnd = CreateWindowEx(0, classNameVfW, "CameraPreview", WS_POPUPWINDOW|WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hinst, NULL); #endif if (!hwnd) { printf ("%s:%d %s Could not create window (%ld)\n", __FILE__, __LINE__, __FUNCTION__, GetLastError()); return VDEV_STATUS_ERROR; } #if USE_PREVIEW if(!ShowWindow(hwnd, SW_SHOW)) { printf ("%s:%d %s Could not show window (%ld)\n", __FILE__, __LINE__, __FUNCTION__, GetLastError()); return VDEV_STATUS_ERROR; } #else if(!ShowWindow(hwnd, SW_HIDE)) { printf ("%s:%d %s Could not hide window (%ld)\n", __FILE__, __LINE__, __FUNCTION__, GetLastError()); return VDEV_STATUS_ERROR; } #endif // create capture driver window handle, also here minim size is 1x1 #if USE_PREVIEW cap = capCreateCaptureWindow("VFW", WS_CHILD|WS_VISIBLE, 0, 0, 1, 1, hwnd, camid); #else cap = capCreateCaptureWindow("VFW", WS_CHILD, 0, 0, 0, 0, hwnd, camid); #endif if(!cap) { printf ("%s:%d %s Could not open VFW id %d window\n", __FILE__, __LINE__, __FUNCTION__, camid); return VDEV_STATUS_ERROR; } if(!capSetCallbackOnStatus(cap, VFW_status_callback)) { printf ("%s:%d %s Could not set status callback to VFW id %d (%ld)\n", __FILE__, __LINE__, __FUNCTION__, camid, GetLastError()); return VDEV_STATUS_ERROR; } if(!capSetCallbackOnError(cap, VFW_error_callback)) { printf ("%s:%d %s Could not set error callback to VFW id %d (%ld)\n", __FILE__, __LINE__, __FUNCTION__, camid, GetLastError()); return VDEV_STATUS_ERROR; } // connect to driver if(!capDriverConnect(cap, camid)) { printf ("%s:%d %s Could not connect to VFW id %d\n", __FILE__, __LINE__, __FUNCTION__, camid); return VDEV_STATUS_ERROR; } // check capabilities GetCapabilities(); // let the callbacks know this class pointer capSetUserData(cap, this); // set video source, capture format and size capDlgVideoSource(cap); capDlgVideoFormat(cap); CAPTUREPARMS cp; if(!capCaptureGetSetup(cap, &cp, sizeof(cp))) { printf ("%s:%d %s Could not get VFW capture setup\n", __FILE__, __LINE__, __FUNCTION__); return VDEV_STATUS_ERROR; } cp.dwRequestMicroSecPerFrame = 10000; // rate in us cp.fMakeUserHitOKToCapture = 0; cp.wPercentDropForError = 10; cp.fYield = TRUE; cp.wNumVideoRequested = 1; cp.fCaptureAudio = 0; cp.vKeyAbort = 0; cp.fAbortLeftMouse = 0; cp.fAbortRightMouse = 0; cp.fLimitEnabled = 0; if(!capCaptureSetSetup(cap, &cp, sizeof(cp))) { printf ("%s:%d %s Could not set VFW capture setup\n", __FILE__, __LINE__, __FUNCTION__); return VDEV_STATUS_ERROR; } // get current valid width/height BITMAPINFO bi; int i = 0; capGetVideoFormat(cap, &bi, sizeof(BITMAPINFO)); inframe_w = bi.bmiHeader.biWidth; inframe_h = bi.bmiHeader.biHeight; printf ("%s:%d %s Current capture size is %dx%d\n", __FILE__, __LINE__, __FUNCTION__, inframe_w, inframe_h); conf_format = convert_from_pixelformat(bi.bmiHeader.biCompression); inframe_pixfmt = bi.bmiHeader.biCompression; printf ("%s:%d %s Current capture format is %s\n", __FILE__, __LINE__, __FUNCTION__, conf_format.c_str()); // try to set the configured width/height if(conf_width != -1) { inframe_w = bi.bmiHeader.biWidth = conf_width; inframe_h = bi.bmiHeader.biHeight = conf_height; bi.bmiHeader.biCompression = inframe_pixfmt; if(!capSetVideoFormat(cap, &bi, sizeof(BITMAPINFO))) { printf ("%s:%d %s Could not set capture size %dx%d (%s)\n", __FILE__, __LINE__, __FUNCTION__, conf_width, conf_height, conf_format.c_str()); return VDEV_STATUS_ERROR; } } // find out maximum resolution else { int sizes[][2] = {{320, 240}, {640, 480}, {800, 600}, {1024, 768}, {1280, 720}, {1280, 1024}, {1920, 1080}, {-1, -1}}; while(sizes[i][0] != -1) { inframe_w = bi.bmiHeader.biWidth = sizes[i][0]; inframe_h = bi.bmiHeader.biHeight = sizes[i][1]; bi.bmiHeader.biCompression = inframe_pixfmt; if(capSetVideoFormat(cap, &bi, sizeof(BITMAPINFO))) { printf ("%s:%d %s Resolution %dx%d (%s) works\n", __FILE__, __LINE__, __FUNCTION__, inframe_w, inframe_h, conf_format.c_str()); if (inframe_w >= conf_width && inframe_h >= conf_height) { conf_width = inframe_w; conf_height = inframe_h; } } i++; } } // translate this special format to what our convert functions can work with if (inframe_pixfmt == V4L2_PIX_FMT_YUY2) { inframe_pixfmt = V4L2_PIX_FMT_YUYV; } return VDEV_STATUS_OK; }; /* * Close the device. */ int VideoDev_VFW::Close() { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); if(cap) { capDriverDisconnect(cap); DestroyWindow(cap); DestroyWindow(hwnd); hwnd = cap = NULL; } DestroyClass(); return VDEV_STATUS_OK; }; /***************************************************************************************************** * VideoGrabbing */ /* * The callback is run when a new frame is available. It copies the * available data into the framebuffer of the class instance. */ LRESULT CALLBACK VFW_frame_callback (HWND h, LPVIDEOHDR v) { VideoDev_VFW * obj = (VideoDev_VFW *)capGetUserData(h); obj->SetFrameBufferSize(v->dwBytesUsed); if (obj->GetFrameBuffer()) { memcpy(obj->GetFrameBuffer(), v->lpData, obj->GetFrameBufferSize()); } return TRUE; } /* * Prepare inframe for raw picture data, will hold a video frame with * maximum size of inframe size = 4*W*H. Setup capture * callbacks. Then send the start capture signal to the camera. */ int VideoDev_VFW::CaptureStart() { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); // allocate memory for frame data if (inframe != NULL) free (inframe); inframe_size = 4 * inframe_w * inframe_h; inframe = (unsigned char *) malloc(inframe_size); pixelformat = inframe_pixfmt; #if USE_PREVIEW if(!capSetCallbackOnFrame(cap, VFW_frame_callback)) { printf ("%s:%d %s Could not set frame callback to VFW id %d (%ld)\n", __FILE__, __LINE__, __FUNCTION__, camid, GetLastError()); return VDEV_STATUS_ERROR; } #else if(!capSetCallbackOnVideoStream(cap, VFW_frame_callback)) { printf ("%s:%d %s Could not set videostream callback to VFW id %d (%ld)\n", __FILE__, __LINE__, __FUNCTION__, camid, GetLastError()); return VDEV_STATUS_ERROR; } #endif // disable overlay capOverlay(cap, FALSE); #if USE_PREVIEW // use preview window for grabbing capPreviewRate (cap, 10); // rate in ms capPreviewScale (cap, FALSE); capPreview (cap, TRUE); #else // otherwise use other capture capPreview (cap, FALSE); if(!capCaptureSequenceNoFile (cap)) { printf ("%s:%d %s Could not start capture (%ld)\n", __FILE__, __LINE__, __FUNCTION__, GetLastError()); } #endif return VDEV_STATUS_OK; }; /* * Stop capture and free framebuffer. */ int VideoDev_VFW::CaptureStop() { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); capCaptureAbort(cap); capSetCallbackOnFrame(cap, NULL); capSetCallbackOnVideoStream(cap, NULL); capSetCallbackOnStatus(cap, NULL); capSetCallbackOnError(cap, NULL); // free inframe memory if (inframe) { free (inframe); inframe_size = 0; inframe = NULL; } return VDEV_STATUS_OK; }; /* * This function needed to continue running the capture window. */ void VideoDev_VFW::HandleMessages() { MSG msg; while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } /* * Try to grab one frame and copy it into raw video buffer. If * something goes wrong return an error code. Return code * VDEV_STATUS_AGAIN is not an error. There was no video image ready * to read. */ int VideoDev_VFW::Grab(VideoFrameRaw *vf) { HandleMessages(); if (inframe == NULL) return VDEV_STATUS_ERROR; if (GetFrameBufferSize() > 0) { LockMutex(); vf->CopyFrom(inframe_pixfmt, inframe_w, inframe_h, GetFrameBufferSize(), inframe); SetFrameBufferSize(0); UnLockMutex(); } else { return VDEV_STATUS_AGAIN; } return VDEV_STATUS_OK; } /* * For VfW this seems difficult / impossible to obtain the supported * video formats, but they must be selected via capDlgVideoFormat(). */ int VideoDev_VFW::GetDeviceFormats(string device, std::list *formats) { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); return VDEV_STATUS_OK; } /***************************************************************************************************** * Controls */ /* * set video control identified by id */ int VideoDev_VFW::SetDevCtrl(unsigned int id, int value) { return VDEV_STATUS_OK; }; /* * get video control identified by id */ int VideoDev_VFW::GetDevCtrl(unsigned int id, int *value) { return VDEV_STATUS_OK; };