#include "ofQuickTimeGrabber.h"
#include "ofUtils.h"
#if !defined(TARGET_LINUX) && !defined(MAC_OS_X_VERSION_10_7) && (!defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12)
#ifdef OF_VIDEO_CAPTURE_QUICKTIME
static ComponentResult frameIsGrabbedProc(SGChannel sgChan, short nBufferNum, Boolean *pbDone, long lRefCon);
static ComponentResult frameIsGrabbedProc(SGChannel sgChan, short nBufferNum, Boolean *pbDone, long lRefCon){
ComponentResult err = SGGrabFrameComplete( sgChan, nBufferNum, pbDone );
bool * havePixChanged = (bool *)lRefCon;
*havePixChanged = true;
return err;
}
#endif
ofQuickTimeGrabber::ofQuickTimeGrabber(){
#ifdef OF_VIDEO_CAPTURE_QUICKTIME
initializeQuicktime();
bSgInited = false;
gSeqGrabber = nullptr;
offscreenGWorldPixels = nullptr;
#endif
bIsFrameNew = false;
bVerbose = false;
bGrabberInited = false;
bChooseDevice = false;
deviceID = 0;
attemptFramerate = -1;
}
ofQuickTimeGrabber::~ofQuickTimeGrabber(){
close();
#ifdef OF_VIDEO_CAPTURE_QUICKTIME
if (offscreenGWorldPixels != nullptr){
delete[] offscreenGWorldPixels;
offscreenGWorldPixels = nullptr;
}
#endif
}
void ofQuickTimeGrabber::setVerbose(bool bTalkToMe){
bVerbose = bTalkToMe;
}
void ofQuickTimeGrabber::setDeviceID(int _deviceID){
deviceID = _deviceID;
bChooseDevice = true;
}
void ofQuickTimeGrabber::setDesiredFrameRate(int framerate){
attemptFramerate = framerate;
}
bool ofQuickTimeGrabber::setPixelFormat(ofPixelFormat pixelFormat){
if( pixelFormat == OF_PIXELS_RGB ){
return true;
}
ofLogWarning("ofQuickTimeGrabber") << "setPixelFormat(): requested pixel format " << pixelFormat << " not supported";
return false;
}
ofPixelFormat ofQuickTimeGrabber::getPixelFormat() const {
return OF_PIXELS_RGB;
}
bool ofQuickTimeGrabber::setup(int w, int h){
#ifdef OF_VIDEO_CAPTURE_QUICKTIME
if( !qtInitSeqGrabber() ){
ofLogError("ofQuickTimeGrabber") << "initGrabber(): unable to initialize the seq grabber";
return false;
}
MacSetRect(&videoRect, 0, 0, w, h);
offscreenGWorldPixels = (unsigned char*)malloc(4 * w * h + 32);
pixels.allocate(w, h, OF_IMAGE_COLOR);
#if defined(TARGET_OSX) && defined(__BIG_ENDIAN__)
QTNewGWorldFromPtr (&(videogworld), k32ARGBPixelFormat, &(videoRect), nullptr, nullptr, 0, (offscreenGWorldPixels), 4 * w);
#else
QTNewGWorldFromPtr (&(videogworld), k24RGBPixelFormat, &(videoRect), nullptr, nullptr, 0, (pixels.getPixels()), 3 * w);
#endif
LockPixels(GetGWorldPixMap(videogworld));
SetGWorld (videogworld, nullptr);
SGSetGWorld(gSeqGrabber, videogworld, nil);
bool didWeChooseADevice = bChooseDevice;
bool deviceIsSelected = false;
if(didWeChooseADevice){
deviceIsSelected = qtSelectDevice(deviceID, true);
if(!deviceIsSelected && bVerbose)
ofLogError("ofQuickTimeGrabber") << "initGrabber(): unable to open device[" << deviceID << "], will attempt other devices";
}
if(deviceIsSelected == false){
listDevices();
setDeviceID(0);
deviceIsSelected = qtSelectDevice(deviceID, false);
}
if( deviceIsSelected == false){
goto bail;
}
OSStatus err;
err = SGSetChannelUsage(gVideoChannel,seqGrabPreview);
if ( err != noErr ) goto bail;
err = SGSetChannelRefCon(gVideoChannel, (long)&bHavePixelsChanged );
if(!err) {
VideoBottles vb;
vb.procCount = 9;
err = SGGetVideoBottlenecks(gVideoChannel, &vb);
if (!err) {
myGrabCompleteProc = NewSGGrabCompleteBottleUPP(frameIsGrabbedProc);
vb.grabCompleteProc = myGrabCompleteProc;
err = SGSetVideoBottlenecks(gVideoChannel, &vb);
}
}
err = SGSetChannelBounds(gVideoChannel, &videoRect);
if ( err != noErr ) goto bail;
err = SGPrepare(gSeqGrabber, true, false);
if ( err != noErr ) goto bail;
err = SGStartPreview(gSeqGrabber);
if ( err != noErr ) goto bail;
bGrabberInited = true;
loadSettings();
if( attemptFramerate >= 0 ){
err = SGSetFrameRate(gVideoChannel, IntToFixed(attemptFramerate) );
if ( err != noErr ){
ofLogError("ofQuickTimeGrabber") << "initGrabber: couldn't setting framerate to " << attemptFramerate << ": OSStatus " << err;
}
}
ofLogNotice("ofQuickTimeGrabber") << " inited grabbed ";
ofLogNotice("ofQuickTimeGrabber") << "-------------------------------------";
return true;
bail:
ofLogError("ofQuickTimeGrabber") << "***** ofQuickTimeGrabber error *****";
ofLogError("ofQuickTimeGrabber") << "------------------------------------";
if(bSgInited) qtCloseSeqGrabber();
bGrabberInited = false;
return false;
#else
return false;
#endif
}
bool ofQuickTimeGrabber::isInitialized() const{
return bGrabberInited;
}
vector<ofVideoDevice> ofQuickTimeGrabber::listDevices() const{
vector <ofVideoDevice> devices;
#ifdef OF_VIDEO_CAPTURE_QUICKTIME
bool bNeedToInitGrabberFirst = false;
if (!bSgInited) bNeedToInitGrabberFirst = true;
if( bNeedToInitGrabberFirst ){
if( !qtInitSeqGrabber() ){
return devices;
}
}
ofLogNotice("ofQuickTimeGrabber") << "-------------------------------------";
SGDeviceList deviceList;
SGGetChannelDeviceList (gVideoChannel, sgDeviceListIncludeInputs, &deviceList);
unsigned char pascalName[64];
unsigned char pascalNameInput[64];
int deviceCount = 0;
ofLogNotice("ofQuickTimeGrabber") << "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) * 64);
if(nameRec.flags != sgDeviceNameFlagDeviceUnavailable){
for(int j = 0; j < numInputs; j++){
if( deviceInputList ){
SGDeviceInputName inputNameRec = (*deviceInputList)->entry[j];
memcpy(pascalNameInput, inputNameRec.name, sizeof(char) * 64);
}
ofLogNotice() << "device [" << deviceCount << "] " << p2cstr(pascalName) << " - " << p2cstr(pascalNameInput);
ofVideoDevice vd;
vd.id = deviceCount;
vd.deviceName = p2cstr(pascalName);
vd.bAvailable = true;
devices.push_back(vd);
deviceCount++;
}
}else{
ofLogNotice("ofQuickTimeGrabber") << "(unavailable) device [" << deviceCount << "] " << p2cstr(pascalName);
ofVideoDevice vd;
vd.id = deviceCount;
vd.deviceName = p2cstr(pascalName);
vd.bAvailable = false;
devices.push_back(vd);
deviceCount++;
}
}
ofLogNotice("ofQuickTimeGrabber") << "-------------------------------------";
if( bNeedToInitGrabberFirst ){
qtCloseSeqGrabber();
}
#endif
return devices;
}
void ofQuickTimeGrabber::update(){
#ifdef OF_VIDEO_CAPTURE_QUICKTIME
if (bGrabberInited == true){
SGIdle(gSeqGrabber);
if (bHavePixelsChanged){
#if defined(TARGET_OSX) && defined(__BIG_ENDIAN__)
convertPixels(offscreenGWorldPixels, pixels.getPixels(), width, height);
#endif
}
}
if (bGrabberInited == true){
bIsFrameNew = false;
if (bHavePixelsChanged == true){
bIsFrameNew = true;
bHavePixelsChanged = false;
}
}
#endif
}
ofPixels& ofQuickTimeGrabber::getPixels(){
return pixels;
}
const ofPixels& ofQuickTimeGrabber::getPixels() const {
return pixels;
}
bool ofQuickTimeGrabber::isFrameNew() const {
return bIsFrameNew;
}
float ofQuickTimeGrabber::getWidth() const {
return pixels.getWidth();
}
float ofQuickTimeGrabber::getHeight() const {
return pixels.getHeight();
}
void ofQuickTimeGrabber::clearMemory(){
pixels.clear();
}
void ofQuickTimeGrabber::close(){
#ifdef OF_VIDEO_CAPTURE_QUICKTIME
qtCloseSeqGrabber();
DisposeSGGrabCompleteBottleUPP(myGrabCompleteProc);
#endif
clearMemory();
}
void ofQuickTimeGrabber::videoSettings(void){
#ifdef OF_VIDEO_CAPTURE_QUICKTIME
Rect curBounds, curVideoRect;
ComponentResult err;
err = SGGetChannelBounds (gVideoChannel, &curBounds);
if (err != noErr){
ofLogError("ofQuickTimeGrabber") << "videoSettings(): couldn't get get channel bounds: ComponentResult " << err;
return;
}
err = SGGetVideoRect (gVideoChannel, &curVideoRect);
if (err != noErr){
ofLogError("ofQuickTimeGrabber") << "videoSettings(): couldn't get video rect: ComponentResult " << err;
return;
}
err = SGPause (gSeqGrabber, true);
if (err != noErr){
ofLogError("ofQuickTimeGrabber") << "videoSettings(): couldn't set pause: ComponentResult " << err;
return;
}
#ifdef TARGET_OSX
loadSettings();
static SGModalFilterUPP gSeqGrabberModalFilterUPP = NewSGModalFilterUPP(SeqGrabberModalFilterUPP);
ComponentResult result = SGSettingsDialog(gSeqGrabber, gVideoChannel, 0, nil, 0, gSeqGrabberModalFilterUPP, nil);
if (result != noErr){
ofLogError("ofQuickTimeGrabber") << "videoSettings(): settings dialog error: ComponentResult " << err;
return;
}
saveSettings();
#else
SGSettingsDialog(gSeqGrabber, gVideoChannel, 0, nil, seqGrabSettingsPreviewOnly, nullptr, 0);
#endif
SGSetChannelBounds(gVideoChannel, &videoRect);
SGPause (gSeqGrabber, false);
#endif
}
#ifdef TARGET_OSX
bool ofQuickTimeGrabber::saveSettings(){
if (bGrabberInited != true) return false;
ComponentResult err;
UserData mySGVideoSettings = nullptr;
err = SGGetChannelSettings(gSeqGrabber, gVideoChannel, &mySGVideoSettings, 0);
if ( err != noErr ){
ofLogError("ofQuickTimeGrabber") << "saveSettings(): couldn't get camera settings: ComponentResult " << err;
return false;
}
string pref = "ofVideoSettings-"+deviceName;
CFStringRef cameraString = CFStringCreateWithCString(kCFAllocatorDefault,pref.c_str(),kCFStringEncodingMacRoman);
SaveSettingsPreference( cameraString, mySGVideoSettings);
DisposeUserData(mySGVideoSettings);
return true;
}
bool ofQuickTimeGrabber::loadSettings(){
if (bGrabberInited != true || deviceName.length() == 0) return false;
ComponentResult err;
UserData mySGVideoSettings = nullptr;
string pref = "ofVideoSettings-"+deviceName;
CFStringRef cameraString = CFStringCreateWithCString(kCFAllocatorDefault,pref.c_str(),kCFStringEncodingMacRoman);
GetSettingsPreference(cameraString, &mySGVideoSettings);
if (mySGVideoSettings){
Rect curBounds, curVideoRect;
err = SGGetChannelBounds (gVideoChannel, &curBounds);
if (err != noErr){
ofLogError("ofQuickTimeGrabber") << "loadSettings(): couldn't set channel bounds: ComponentResult " << err;
}
err = SGGetVideoRect (gVideoChannel, &curVideoRect);
if (err != noErr){
ofLogError("ofQuickTimeGrabber") << "loadSettings(): couldn't set video rect: ComponentResult " << err;
}
err = SGSetChannelSettings(gSeqGrabber, gVideoChannel, mySGVideoSettings, 0);
if ( err != noErr ) {
ofLogError("ofQuickTimeGrabber") << "loadSettings(): couldn't set channel settings: ComponentResult " << err;
return false;
}
DisposeUserData(mySGVideoSettings);
err = SGPause (gSeqGrabber, true);
if (err != noErr){
ofLogError("ofQuickTimeGrabber") << "loadSettings(): couldn't set pause: ComponentResult " << err;
}
SGSetChannelBounds(gVideoChannel, &videoRect);
SGPause (gSeqGrabber, false);
}else{
ofLogWarning("ofQuickTimeGrabber") << "loadSettings(): no camera settings to load";
return false;
}
return true;
}
bool ofQuickTimeGrabber::qtInitSeqGrabber(){
if (bSgInited != true){
OSErr err = noErr;
ComponentDescription theDesc;
Component sgCompID;
theDesc.componentType = SeqGrabComponentType;
theDesc.componentSubType = nullptr;
theDesc.componentManufacturer = 'appl';
theDesc.componentFlags = nullptr;
theDesc.componentFlagsMask = nullptr;
sgCompID = FindNextComponent (nullptr, &theDesc);
if (sgCompID == nullptr){
ofLogError("ofQuickTimeGrabber") << "qtInitSeqGrabber(): findNextComponent did not return a valid component";
return false;
}
gSeqGrabber = OpenComponent(sgCompID);
err = GetMoviesError();
if (gSeqGrabber == nullptr || err) {
ofLogError("ofQuickTimeGrabber") << "qtInitSeqGrabber(): couldn't get default sequence grabber component: OSErr " << err;
return false;
}
err = SGInitialize(gSeqGrabber);
if (err != noErr) {
ofLogError("ofQuickTimeGrabber") << "qtInitSeqGrabber(): can't initialize sequence grabber component: OSErr " << err;
return false;
}
err = SGSetDataRef(gSeqGrabber, 0, 0, seqGrabDontMakeMovie);
if (err != noErr) {
ofLogError("ofQuickTimeGrabber") << "qtInitSeqGrabber(): can't set the destination data reference: OSErr " << err;
return false;
}
err = SGSetGWorld(gSeqGrabber, 0, 0);
if (err != noErr) {
ofLogError("ofQuickTimeGrabber") << "qtInitSeqGrabber(): setting up the gworld: OSErr " << err;
return false;
}
err = SGNewChannel(gSeqGrabber, VideoMediaType, &(gVideoChannel));
if (err != noErr) {
ofLogError("ofQuickTimeGrabber") << "qtInitSeqGrabber(): couldn't create a new channel: OSErr " << err;
ofLogError("ofQuickTimeGrabber") << "qtInitSeqGrabber(): check if you have any qt capable cameras attached";
return false;
}
bSgInited = true;
return true;
}
return false;
}
bool ofQuickTimeGrabber::qtCloseSeqGrabber(){
if (gSeqGrabber != nullptr){
SGStop (gSeqGrabber);
CloseComponent (gSeqGrabber);
gSeqGrabber = nullptr;
bSgInited = false;
return true;
}
return false;
}
bool ofQuickTimeGrabber::qtSelectDevice(int deviceNumber, bool didWeChooseADevice){
SGDeviceList deviceList;
SGGetChannelDeviceList(gVideoChannel, sgDeviceListIncludeInputs, &deviceList);
unsigned char pascalName[64];
unsigned char pascalNameInput[64];
int numDevices = (*deviceList)->count;
if(numDevices == 0){
ofLogError("ofQuickTimeGrabber") << "no capture 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) * 64);
memset(pascalNameInput, 0, sizeof(char)*64);
if(nameRec.flags != sgDeviceNameFlagDeviceUnavailable){
for(int j = 0; j < numInputs; j++){
if( deviceInputList ){
SGDeviceInputName inputNameRec = (*deviceInputList)->entry[j];
memcpy(pascalNameInput, inputNameRec.name, sizeof(char) * 64);
}
if( deviceCount == deviceNumber || !didWeChooseADevice ){
ofLogNotice("ofQuickTimeGrabber") << "attempting to open device [" << deviceCount << "] "
<< p2cstr(pascalName) << " - " << p2cstr(pascalNameInput);
OSErr err1 = SGSetChannelDevice(gVideoChannel, pascalName);
OSErr err2 = SGSetChannelDeviceInput(gVideoChannel, j);
int successLevel = 0;
if ( err1 == noErr && err2 == noErr){
successLevel = 2;
}
else if ( (err1 == paramErr || err1 == noErr) && (err2 == noErr || err2 == paramErr) ){
successLevel = 1;
}
if ( successLevel > 0 ){
deviceName = (char *)p2cstr(pascalName);
deviceName += "-";
deviceName += (char *)p2cstr(pascalNameInput);
if(successLevel == 2){
ofLogNotice("ofQuickTimeGrabber") << "device " << deviceName << " opened successfully";
}
else{
ofLogWarning("ofQuickTimeGrabber") << "device " << deviceName << " opened with some paramater errors, should be fine though!";
}
return true;
}else{
if( didWeChooseADevice ){
ofLogWarning("ofQuickTimeGrabber") << "problems setting device [" << deviceNumber << "] "
<< p2cstr(pascalName) << " - " << p2cstr(pascalNameInput) << " *****";
return false;
}else{
ofLogWarning("ofQuickTimeGrabber") << "unable to open device, trying next device";
}
}
}
deviceCount++;
}
}else{
deviceCount++;
}
}
return false;
}
#endif
#endif
Comments