#include "ofVideoGrabber.h" #include "ofUtils.h" #ifdef OF_VIDEO_CAPTURE_V4L #include "ofV4LUtils.h" #endif //-------------------------------------------------------------------- ofVideoGrabber::ofVideoGrabber(){ //--------------------------------- #ifdef OF_VIDEO_CAPTURE_QUICKTIME //--------------------------------- initializeQuicktime(); bSgInited = false; pixels = NULL; gSeqGrabber = NULL; offscreenGWorldPixels = NULL; //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_DIRECTSHOW //--------------------------------- bVerbose = false; bDoWeNeedToResize = false; //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_V4L // kept around if people have unicap issues... //-------------------------------- bV4LGrabberInited = false; //--------------------------------- #endif //--------------------------------- // common bIsFrameNew = false; bVerbose = false; bGrabberInited = false; bUseTexture = true; bChooseDevice = false; deviceID = 0; width = 320; // default setting height = 240; // default setting pixels = NULL; } //-------------------------------------------------------------------- ofVideoGrabber::~ofVideoGrabber(){ close(); //--------------------------------- #ifdef OF_VIDEO_CAPTURE_QUICKTIME //--------------------------------- if (offscreenGWorldPixels != NULL){ delete[] offscreenGWorldPixels; } //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_UNICAP //-------------------------------- if(bGrabberInited) ucGrabber.close_unicap(); //--------------------------------- #endif //--------------------------------- } //-------------------------------------------------------------------- void ofVideoGrabber::listDevices(){ //--------------------------------- #ifdef OF_VIDEO_CAPTURE_QUICKTIME //--------------------------------- bool bNeedToInitGrabberFirst = false; if (!bSgInited) bNeedToInitGrabberFirst = true; //if we need to initialize the grabbing component then do it if( bNeedToInitGrabberFirst ){ if( !qtInitSeqGrabber() ){ return; } } ofLog(OF_LOG_NOTICE, "-------------------------------------"); /* //input selection stuff (ie multiple webcams) //from http://developer.apple.com/samplecode/SGDevices/listing13.html //and originally http://lists.apple.com/archives/QuickTime-API/2008/Jan/msg00178.html */ SGDeviceList deviceList; SGGetChannelDeviceList (gVideoChannel, sgDeviceListIncludeInputs, &deviceList); unsigned char pascalName[256]; unsigned char pascalNameInput[256]; //this is our new way of enumerating devices //quicktime can have multiple capture 'inputs' on the same capture 'device' //ie the USB Video Class Video 'device' - can have multiple usb webcams attached on what QT calls 'inputs' //The isight for example will show up as: //USB Video Class Video - Built-in iSight ('input' 1 of the USB Video Class Video 'device') //Where as another webcam also plugged whill show up as //USB Video Class Video - Philips SPC 1000NC Webcam ('input' 2 of the USB Video Class Video 'device') //this means our the device ID we use for selection has to count both capture 'devices' and their 'inputs' //this needs to be the same in our init grabber method so that we select the device we ask for int deviceCount = 0; ofLog(OF_LOG_NOTICE, "listing available capture devices"); for(int i = 0 ; i < (*deviceList)->count ; ++i) { SGDeviceName nameRec; nameRec = (*deviceList)->entry[i]; SGDeviceInputList deviceInputList = nameRec.inputs; int numInputs = 0; if( deviceInputList ) numInputs = ((*deviceInputList)->count); memcpy(pascalName, (*deviceList)->entry[i].name, sizeof(char) * 256); //this means we can use the capture method if(nameRec.flags != sgDeviceNameFlagDeviceUnavailable){ //if we have a capture 'device' (qt's word not mine - I would prefer 'system' ) that is ready to be used //we go through its inputs to list all physical devices - as there could be more than one! for(int j = 0; j < numInputs; j++){ //if our 'device' has inputs we get their names here if( deviceInputList ){ SGDeviceInputName inputNameRec = (*deviceInputList)->entry[j]; memcpy(pascalNameInput, inputNameRec.name, sizeof(char) * 256); } ofLog(OF_LOG_NOTICE, "device[%i] %s - %s", deviceCount, p2cstr(pascalName), p2cstr(pascalNameInput) ); //we count this way as we need to be able to distinguish multiple inputs as devices deviceCount++; } }else{ ofLog(OF_LOG_NOTICE, "(unavailable) device[%i] %s", deviceCount, p2cstr(pascalName) ); deviceCount++; } } ofLog(OF_LOG_NOTICE, "-------------------------------------"); //if we initialized the grabbing component then close it if( bNeedToInitGrabberFirst ){ qtCloseSeqGrabber(); } //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_DIRECTSHOW //--------------------------------- ofLog(OF_LOG_NOTICE, "---"); VI.listDevices(); ofLog(OF_LOG_NOTICE, "---"); //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_UNICAP //-------------------------------- ucGrabber.listUCDevices(); //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_V4L //-------------------------------- struct stat st; ofLog(OF_LOG_NOTICE, "listing available capture devices"); ofLog(OF_LOG_NOTICE, "---"); for (int i = 0; i < 8; i++) { sprintf(dev_name, "/dev/video%i", i); if (stat (dev_name, &st) == 0) { ofLog(OF_LOG_NOTICE, "Video device %i = /dev/video%i",i,i); } else { } } ofLog(OF_LOG_NOTICE, "---"); //--------------------------------- #endif //--------------------------------- } //-------------------------------------------------------------------- void ofVideoGrabber::setVerbose(bool bTalkToMe){ bVerbose = bTalkToMe; //--------------------------------- #ifdef OF_VIDEO_CAPTURE_UNICAP //-------------------------------- ucGrabber.verbose=bVerbose; //--------------------------------- #endif //--------------------------------- } //-------------------------------------------------------------------- void ofVideoGrabber::setDeviceID(int _deviceID){ deviceID = _deviceID; bChooseDevice = true; } //--------------------------------------------------------------------------- unsigned char * ofVideoGrabber::getPixels(){ return pixels; } //------------------------------------ //for getting a reference to the texture ofTexture & ofVideoGrabber::getTextureReference(){ if(!tex.bAllocated() ){ ofLog(OF_LOG_WARNING, "ofVideoGrabber - getTextureReference - texture is not allocated"); } return tex; } //--------------------------------------------------------------------------- bool ofVideoGrabber::isFrameNew(){ return bIsFrameNew; } //-------------------------------------------------------------------- void ofVideoGrabber::update(){ grabFrame(); } //-------------------------------------------------------------------- void ofVideoGrabber::grabFrame(){ //--------------------------------- #ifdef OF_VIDEO_CAPTURE_QUICKTIME //--------------------------------- if (bGrabberInited == true){ SGIdle(gSeqGrabber); // set the top pixel alpha = 0, so we can know if it // was a new frame or not.. // or else we will process way more than necessary // (ie opengl is running at 60fps +, capture at 30fps) if (offscreenGWorldPixels[0] != 0x00){ offscreenGWorldPixels[0] = 0x00; bHavePixelsChanged = true; convertPixels(offscreenGWorldPixels, pixels, width, height); if (bUseTexture){ tex.loadData(pixels, width, height, GL_RGB); } } } // newness test for quicktime: if (bGrabberInited == true){ bIsFrameNew = false; if (bHavePixelsChanged == true){ bIsFrameNew = true; bHavePixelsChanged = false; } } //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_DIRECTSHOW //--------------------------------- if (bGrabberInited == true){ bIsFrameNew = false; if (VI.isFrameNew(device)){ bIsFrameNew = true; /* rescale -- currently this is nearest neighbor scaling not the greatest, but fast this can be optimized too with pointers, etc better -- make sure that you ask for a "good" size.... */ unsigned char * viPixels = VI.getPixels(device, true, true); if (bDoWeNeedToResize == true){ int inputW = VI.getWidth(device); int inputH = VI.getHeight(device); float scaleW = (float)inputW / (float)width; float scaleH = (float)inputH / (float)height; for(int i=0;icount; if(numDevices == 0){ ofLog(OF_LOG_ERROR, "error: No catpure devices found"); return false; } int deviceCount = 0; for(int i = 0 ; i < numDevices; ++i) { SGDeviceName nameRec; nameRec = (*deviceList)->entry[i]; SGDeviceInputList deviceInputList = nameRec.inputs; int numInputs = 0; if( deviceInputList ) numInputs = ((*deviceInputList)->count); memcpy(pascalName, (*deviceList)->entry[i].name, sizeof(char) * 256); memset(pascalNameInput, 0, sizeof(char)*256); //this means we can use the capture method if(nameRec.flags != sgDeviceNameFlagDeviceUnavailable){ //if we have a capture 'device' (qt's word not mine - I would prefer 'system' ) that is ready to be used //we go through its inputs to list all physical devices - as there could be more than one! for(int j = 0; j < numInputs; j++){ //if our 'device' has inputs we get their names here if( deviceInputList ){ SGDeviceInputName inputNameRec = (*deviceInputList)->entry[j]; memcpy(pascalNameInput, inputNameRec.name, sizeof(char) * 256); } //if the device number matches we try and setup the device //if we didn't specifiy a device then we will try all devices till one works! if( deviceCount == deviceNumber || !didWeChooseADevice ){ ofLog(OF_LOG_NOTICE, "attempting to open device[%i] %s - %s", deviceCount, p2cstr(pascalName), p2cstr(pascalNameInput) ); OSErr err1 = SGSetChannelDevice(gVideoChannel, pascalName); OSErr err2 = SGSetChannelDeviceInput(gVideoChannel, j); int successLevel = 0; //if there were no errors then we have opened the device without issue if ( err1 == noErr && err2 == noErr){ successLevel = 2; } //parameter errors are not fatal so we will try and open but will caution the user else if ( (err1 == paramErr || err1 == noErr) && (err2 == noErr || err2 == paramErr) ){ successLevel = 1; } //the device is opened! if ( successLevel > 0 ){ deviceName = (char *)p2cstr(pascalName); deviceName += "-"; deviceName += (char *)p2cstr(pascalNameInput); if(successLevel == 2)ofLog(OF_LOG_NOTICE, "device opened successfully"); else ofLog(OF_LOG_WARNING, "device opened with some paramater errors - should be fine though!"); //no need to keep searching - return that we have opened a device! return true; }else{ //if we selected a device in particular but failed we want to go through the whole list again - starting from 0 and try any device. //so we return false - and try one more time without a preference if( didWeChooseADevice ){ ofLog(OF_LOG_WARNING, "problems setting device[%i] %s - %s *****", deviceNumber, p2cstr(pascalName), p2cstr(pascalNameInput)); return false; }else{ ofLog(OF_LOG_WARNING, "unable to open device, trying next device"); } } } //we count this way as we need to be able to distinguish multiple inputs as devices deviceCount++; } }else{ //ofLog(OF_LOG_ERROR, "(unavailable) device[%i] %s", deviceCount, p2cstr(pascalName) ); deviceCount++; } } return false; } //--------------------------------- #endif //--------------------------------- //-------------------------------------------------------------------- bool ofVideoGrabber::initGrabber(int w, int h, bool setUseTexture){ bUseTexture = setUseTexture; //--------------------------------- #ifdef OF_VIDEO_CAPTURE_QUICKTIME //--------------------------------- //---------------------------------- 1 - open the sequence grabber if( !qtInitSeqGrabber() ){ ofLog(OF_LOG_ERROR, "error: unable to initialize the seq grabber"); return false; } //---------------------------------- 2 - set the dimensions width = w; height = h; MacSetRect(&videoRect, 0, 0, width, height); //---------------------------------- 3 - buffer allocation // Create a buffer big enough to hold the video data, // make sure the pointer is 32-byte aligned. // also the rgb image that people will grab offscreenGWorldPixels = (unsigned char*)malloc(4 * width * height + 32); pixels = new unsigned char[width*height*3]; QTNewGWorldFromPtr (&videogworld, k32ARGBPixelFormat, &videoRect, NULL, NULL, 0, offscreenGWorldPixels, 4 * width); LockPixels(GetGWorldPixMap(videogworld)); SetGWorld (videogworld, NULL); SGSetGWorld(gSeqGrabber, videogworld, nil); //---------------------------------- 4 - device selection bool didWeChooseADevice = bChooseDevice; bool deviceIsSelected = false; //if we have a device selected then try first to setup //that device if(didWeChooseADevice){ deviceIsSelected = qtSelectDevice(deviceID, true); if(!deviceIsSelected && bVerbose) ofLog(OF_LOG_WARNING, "unable to open device[%i] - will attempt other devices", deviceID); } //if we couldn't select our required device //or we aren't specifiying a device to setup //then lets try to setup ANY device! if(deviceIsSelected == false){ //lets list available devices listDevices(); setDeviceID(0); deviceIsSelected = qtSelectDevice(deviceID, false); } //if we still haven't been able to setup a device //we should error and stop! if( deviceIsSelected == false){ goto bail; } //---------------------------------- 5 - final initialization steps OSStatus err; err = SGSetChannelUsage(gVideoChannel,seqGrabPreview); if ( err != noErr ) goto bail; err = SGSetChannelBounds(gVideoChannel, &videoRect); if ( err != noErr ) goto bail; err = SGPrepare(gSeqGrabber, true, false); //theo swapped so preview is true and capture is false if ( err != noErr ) goto bail; err = SGStartPreview(gSeqGrabber); if ( err != noErr ) goto bail; bGrabberInited = true; loadSettings(); ofLog(OF_LOG_NOTICE,"end setup ofVideoGrabber"); ofLog(OF_LOG_NOTICE,"-------------------------------------\n"); //---------------------------------- 6 - setup texture if needed if (bUseTexture){ // create the texture, set the pixels to black and // upload them to the texture (so at least we see nothing black the callback) tex.allocate(width,height,GL_RGB); memset(pixels, 0, width*height*3); tex.loadData(pixels, width, height, GL_RGB); } // we are done return true; //--------------------- (bail) something's wrong ----- bail: ofLog(OF_LOG_ERROR, "***** ofVideoGrabber error *****"); ofLog(OF_LOG_ERROR, "-------------------------------------\n"); //if we don't close this - it messes up the next device! if(bSgInited) qtCloseSeqGrabber(); bGrabberInited = false; return false; //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_DIRECTSHOW //--------------------------------- if (bChooseDevice){ device = deviceID; ofLog(OF_LOG_NOTICE, "choosing %i", deviceID); } else { device = 0; } width = w; height = h; bGrabberInited = false; bool bOk = VI.setupDevice(device, width, height); int ourRequestedWidth = width; int ourRequestedHeight = height; if (bOk == true){ bGrabberInited = true; width = VI.getWidth(device); height = VI.getHeight(device); if (width == ourRequestedWidth && height == ourRequestedHeight){ bDoWeNeedToResize = false; } else { bDoWeNeedToResize = true; width = ourRequestedWidth; height = ourRequestedHeight; } pixels = new unsigned char[width * height * 3]; if (bUseTexture){ // create the texture, set the pixels to black and // upload them to the texture (so at least we see nothing black the callback) tex.allocate(width,height,GL_RGB); memset(pixels, 0, width*height*3); tex.loadData(pixels, width, height, GL_RGB); } return true; } else { ofLog(OF_LOG_ERROR, "error allocating a video device"); ofLog(OF_LOG_ERROR, "please check your camera with AMCAP or other software"); bGrabberInited = false; return false; } //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_UNICAP //-------------------------------- if( !bGrabberInited ){ if ( !bChooseDevice ){ deviceID = 0; } width = w; height = h; pixels = new unsigned char[width * height * 3]; if (bUseTexture){ // create the texture, set the pixels to black and // upload them to the texture (so at least we see nothing black the callback) tex.allocate(width,height,GL_RGB); memset(pixels, 0, width*height*3); tex.loadData(pixels, width, height, GL_RGB); } bGrabberInited = ucGrabber.open_device (deviceID); if( bGrabberInited ){ ofLog(OF_LOG_NOTICE, "choosing device %i: %s", deviceID,ucGrabber.device_identifier()); ucGrabber.set_format(w,h); ucGrabber.start_capture(); } } return bGrabberInited; //--------------------------------- #endif //--------------------------------- //--------------------------------- #ifdef OF_VIDEO_CAPTURE_V4L //-------------------------------- if (bChooseDevice){ device = deviceID; } else { device = 0; } sprintf(dev_name, "/dev/video%i", device); ofLog(OF_LOG_NOTICE, "choosing device "+dev_name+""); bool bOk = initV4L(w, h, dev_name); if (bOk == true){ bV4LGrabberInited = true; width = getV4L_Width(); height = getV4L_Height(); pixels = new unsigned char[width * height * 3]; if (bUseTexture){ // create the texture, set the pixels to black and // upload them to the texture (so at least we see nothing black the callback) tex.allocate(width,height,GL_RGB); //memset(pixels, 0, width*height*3); //tex.loadData(pixels, width, height, GL_RGB); } ofLog(OF_LOG_NOTICE, "success allocating a video device "); return true; } else { ofLog(OF_LOG_ERROR, "error allocating a video device"); ofLog(OF_LOG_ERROR, "please check your camera and verify that your driver is correctly installed."); return false; } //--------------------------------- //--------------------------------- #endif //--------------------------------- } //------------------------------------ void ofVideoGrabber::setUseTexture(bool bUse){ bUseTexture = bUse; } //we could cap these values - but it might be more useful //to be able to set anchor points outside the image //---------------------------------------------------------- void ofVideoGrabber::setAnchorPercent(float xPct, float yPct){ if (bUseTexture)tex.setAnchorPercent(xPct, yPct); } //---------------------------------------------------------- void ofVideoGrabber::setAnchorPoint(int x, int y){ if (bUseTexture)tex.setAnchorPoint(x, y); } //---------------------------------------------------------- void ofVideoGrabber::resetAnchor(){ if (bUseTexture)tex.resetAnchor(); } //------------------------------------ void ofVideoGrabber::draw(float _x, float _y, float _w, float _h){ if (bUseTexture){ tex.draw(_x, _y, _w, _h); } } //------------------------------------ void ofVideoGrabber::draw(float _x, float _y){ draw(_x, _y, (float)width, (float)height); } //---------------------------------------------------------- float ofVideoGrabber::getHeight(){ return (float)height; } //---------------------------------------------------------- float ofVideoGrabber::getWidth(){ return (float)width; }