Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

CameraSourceQTSG.cc

Go to the documentation of this file.
00001 #if defined(__APPLE__) && !defined(__x86_64__)
00002 
00003 #include <AvailabilityMacros.h>
00004 #ifndef MAC_OS_X_VERSION_10_6
00005 
00006 #include "CameraSourceQTSG.h"
00007 #include "Shared/LoadSave.h"
00008 #include "Shared/get_time.h"
00009 #include "Shared/RobotInfo.h"
00010 #include <pthread.h>
00011 #include <set>
00012 #include <sstream>
00013 #include <iostream>
00014 
00015 using namespace std;
00016 
00017 static pthread_key_t qtInit;
00018 struct QTThreadInfo {};
00019 
00020 static bool doGlobalQTInit();
00021 static bool autoRegisterQTInit = doGlobalQTInit();
00022 
00023 bool checkQTThreadInit() {
00024   if(!autoRegisterQTInit)
00025     return false;
00026   if(pthread_getspecific(qtInit)!=NULL)
00027     return true;
00028   OSErr err = EnterMoviesOnThread(kQTEnterMoviesFlagDontSetComponentsThreadMode);
00029   if(err!=noErr) {
00030     cerr << "CameraSource: EnterMoviesOnThread returned error " << err << endl;
00031     return false;
00032   }
00033   pthread_setspecific(qtInit,new QTThreadInfo);
00034   return true;
00035 }
00036 static void qtThreadDestructor(void* threadInfo) {
00037   ExitMoviesOnThread();
00038   delete static_cast<QTThreadInfo*>(threadInfo);
00039 }
00040 static bool doGlobalQTInit() {
00041   int err = pthread_key_create(&qtInit,qtThreadDestructor);
00042   if(err!=0)
00043     cerr << "CameraSource: error during doGlobalQTInit, pthread_key_create:" << strerror(err) << endl;
00044   return (err==0);
00045 }
00046 
00047 CameraSourceQTSG::~CameraSourceQTSG() {
00048   if(sgChan!=NULL)
00049     SGDisposeChannel(sg,sgChan);
00050   sgChan=NULL;
00051   if(gworld!=NULL)
00052     DisposeGWorld(gworld);
00053   gworld=NULL;
00054   // docs say that QTNewGWorldFromPtr is supposed to mark the underlying buffer
00055   // so that DisposeGWorld doesn't release it, but that doesn't seem to be the case...
00056   // If we delete here, we get double-free warnings.
00057   /*if(gworldBuf!=NULL)
00058     free(gworldBuf);*/
00059   gworldBuf=NULL;
00060   if(sg!=NULL)
00061     CloseComponent(sg);
00062   sg=NULL;
00063 }
00064 
00065 void CameraSourceQTSG::initCamera() {
00066   OSErr err=noErr;
00067   
00068   // open the sequence grabber, assuming there's only ever one component of this type listed
00069   sg = OpenDefaultComponent(SeqGrabComponentType, 0);
00070   if(sg==NULL) throw std::make_pair(err,"OpenDefaultComponent(SeqGrabComponentType,0)");
00071   
00072   // initialize the default sequence grabber component
00073   err = SGInitialize(sg);
00074   if(err!=noErr) throw std::make_pair(err,"SGInitialize");
00075   
00076   err = SGSetDataRef(sg, 0, 0, seqGrabToMemory | seqGrabDontMakeMovie | seqGrabDataProcIsInterruptSafe);
00077   if(err!=noErr) throw "SGSetDataRef";
00078     
00079   // this section would get the "default" capture device
00080   /*
00081   ComponentDescription searchCompDesc;
00082   memset(&searchCompDesc,0,sizeof(searchCompDesc));
00083   searchCompDesc.componentType=SeqGrabChannelType;
00084   searchCompDesc.componentSubType=VideoMediaType;
00085   Component chanComponent = FindNextComponent(chanComponent,&searchCompDesc);
00086   if(chanComponent==0) throw std::make_pair(err,"FindNextComponent");
00087     
00088   SGNewChannelFromComponent(sg,&sgChan,chanComponent);
00089   if(err!=noErr) throw std::make_pair(err,"SGNewChannelFromComponent");
00090    */
00091 
00092   // instead, open the *specified* capture device
00093   // thanks Harald ( hxr AT users sourceforge net ) for 'wacaw' source to demonstrate this
00094   err = SGNewChannel(sg, VideoMediaType, &sgChan);
00095   if(err!=noErr) throw std::make_pair(err,"SGNewChannel");
00096   
00097   unsigned char pstr[256]; // sigh, convert devName to pascal-style string
00098   pstr[0]=deviceName.size();
00099   memcpy(pstr+1,deviceName.c_str(),pstr[0]);
00100   err = SGSetChannelDevice(sgChan, pstr);
00101   if(err!=noErr) throw std::make_pair(err,"SGSetChannelDevice");
00102   err = SGSetChannelDeviceInput(sgChan,devInputIdx);
00103   if(err!=noErr) throw std::make_pair(err,"SGSetChannelDeviceInput");
00104   
00105   // does this need to be done? not found in any sample code...
00106   // doesn't seem to work here in any case... 
00107   // (error -9400: noDeviceForChannel; tried before or after the SGSetChannelDevice()...)
00108   /*err = SGInitChannel(sgChan,sg);
00109   if(err!=noErr) throw std::make_pair(err,"SGInitChannel");*/
00110       
00111   // get the active rectangle 
00112   Rect  srcBounds;
00113   err = SGGetSrcVideoBounds(sgChan, &srcBounds);
00114   if(err!=noErr)
00115     std::cerr << "Warning: CameraSource SGGetSrcVideoBounds returned error " << err << std::endl;
00116   else {
00117     srcBounds.right -= srcBounds.left;
00118     srcBounds.bottom -= srcBounds.top;
00119     srcBounds.left = srcBounds.top = 0;
00120     //cout << "original bounds " << srcBounds.right << "x" << srcBounds.bottom << endl;
00121     if((unsigned int)srcBounds.right>CameraResolutionX*2 || (unsigned int)srcBounds.bottom>CameraResolutionY*2) {
00122       srcBounds.right=CameraResolutionX*2;
00123       srcBounds.bottom=CameraResolutionY*2;
00124       //cout << "restricting to " << srcBounds.right << "x" << srcBounds.bottom << endl;
00125     }
00126     err = SGSetChannelBounds(sgChan, &srcBounds);
00127     if(err!=noErr) std::cerr << "Warning: SGSetChannelBounds returned error " << err << std::endl;
00128   }
00129   
00130   // set options for our expected usage
00131   err = SGSetChannelUsage(sgChan, seqGrabRecord | seqGrabLowLatencyCapture /* | seqGrabPreview | seqGrabAlwaysUseTimeBase */);
00132   if(err!=noErr) throw std::make_pair(err,"SGSetChannelUsage");
00133   
00134   // don't need a gworld (yet) -- we want to grab the raw YUV bytes from the camera
00135   /*unsigned int width = srcBounds.right-srcBounds.left;
00136   unsigned int height = srcBounds.bottom-srcBounds.top;
00137   gworldBuf = new char[width*height*4];
00138   err = QTNewGWorldFromPtr(&gworld, k32ARGBPixelFormat, &srcBounds, NULL, NULL, 0, gworldBuf, width*4);
00139   if(err!=noErr) throw std::make_pair(err,"QTNewGWorldFromPtr"); */
00140   
00141   // still have to call SGSetGWorld() though or else SGPrepare() will complain later
00142   err = SGSetGWorld(sg, NULL, NULL);
00143   if(err!=noErr) throw std::make_pair(err,"SGSetGWorld");
00144   
00145   //SGSettingsDialog(sg, sgChan, 0, nil, 0, nil, 0);
00146   
00147   // set up the video bottlenecks so we can get our queued frame count
00148   VideoBottles vb;
00149   memset(&vb,0,sizeof(VideoBottles));
00150   err = SGGetVideoBottlenecks(sgChan, &vb);
00151   if(err!=noErr) throw std::make_pair(err,"SGGetVideoBottlenecks");
00152   
00153   vb.procCount = 9; // there are 9 bottleneck procs; this must be filled in
00154   vb.grabCompressCompleteProc = NewSGGrabCompressCompleteBottleUPP(compressCompleteBottleProc);
00155   
00156   err = SGSetVideoBottlenecks(sgChan, &vb);
00157   if(err!=noErr) throw std::make_pair(err,"SGSetVideoBottlenecks");
00158   
00159   // specify a sequence grabber data function
00160   err = SGSetDataProc(sg, NewSGDataUPP(grabDataProc), (long)this);
00161   if(err!=noErr) throw std::make_pair(err,"SGSetDataProc");
00162   err = SGSetChannelRefCon(sgChan, (long)this); // callback reference context
00163   if(err!=noErr) throw std::make_pair(err,"SGSetChannelRefCon");
00164 
00165   // try to switch to YUV mode
00166   err = SGSetVideoCompressorType(sgChan,k422YpCbCr8CodecType); // produces 2vuy images
00167   if(err!=noErr) {
00168     if(err!=noCodecErr) std::cerr << "    Could not switch to yuv codec (k422YpCbCr8CodecType), err " << err << std::endl;
00169     
00170     // try component video...
00171     // Actually, this is a little slower than converting each frame individually, so we'll do that instead.
00172     // See color space specification in NewGWorld call below...
00173     /*err = SGSetVideoCompressorType(sgChan,kComponentVideoCodecType); // produces yuv2/yuvu images
00174     if(err==noCodecErr) std::cerr << "    Could not switch to yuv codec (k422YpCbCr8CodecType or kComponentVideoCodecType), not supported by camera (?)" << std::endl;
00175     else if(err!=noErr) std::cerr << "    Could not switch to yuv codec (kComponentVideoCodecType), err " << err << std::endl;*/
00176   }
00177   
00178   // just for debugging...
00179   /*OSType ct;
00180   err = SGGetVideoCompressorType(sgChan,&ct);
00181   if(err!=noErr) std::cerr << "    Could not get current codec" << std::endl;
00182   else { std::cout << "    Current codec type is "; dumpLiteral(ct); std::cout << std::endl; }*/
00183 }
00184 
00185 
00186 void CameraSourceQTSG::registerSource() {
00187   if(!checkQTThreadInit())
00188     return;
00189   if(!grabbing) { // we're about to be used!
00190     // lights...camera...
00191     OSErr err = SGPrepare(sg, false, true);
00192     if(err!=noErr) {
00193       cerr << "CameraSource: SGPrepare returned error " << err << endl;
00194       return;
00195     }
00196     
00197     /*
00198      // make sure the timebase used by the video channel is being driven by
00199      // the sound clock if there is a sound channel, this has to be done
00200      // after calling SGPrepare - see Q&A 1314
00201      if (NULL != sgchanSound) {
00202      TimeBase soundTimeBase = NULL, sgTimeBase = NULL;
00203      err = SGGetTimeBase(this->seqGrab, &sgTimeBase);
00204      if(noErr == err)
00205      err = SGGetChannelTimeBase(sgchanSound, &soundTimeBase);
00206      if (noErr == err && NULL != soundTimeBase)
00207      SetTimeBaseMasterClock(sgTimeBase, (Component)GetTimeBaseMasterClock(soundTimeBase), NULL);
00208      }
00209      */
00210     
00211     // ...action
00212     err = SGStartRecord(sg);
00213     if(err!=noErr) {
00214       cerr << "CameraSource: SGStartRecord returned error " << err << endl;
00215       err = SGRelease(sg); // undo SGPrepare()
00216       if(err!=noErr)
00217         cerr << "CameraSource: SGRelease returned error during recovery " << err << endl;
00218       return;
00219     }
00220     
00221     grabbing=true;
00222   }
00223 }
00224 
00225 void CameraSourceQTSG::deregisterSource() {
00226   if(!checkQTThreadInit())
00227     return;
00228   if(grabbing) {
00229     OSErr err = SGStop(sg); // undo SGStartPreview or SGStartRecord
00230     if(err!=noErr)
00231       cerr << "CameraSource: SGStop returned error " << err << endl;
00232     err = SGRelease(sg); // undo SGPrepare()
00233     if(err!=noErr)
00234       cerr << "CameraSource: SGRelease returned error " << err << endl;
00235     grabbing=false;
00236   }
00237 }
00238 
00239 void CameraSourceQTSG::doUnfreeze() {
00240   poller.start();
00241 }
00242 
00243 void CameraSourceQTSG::doFreeze() { 
00244   if(poller.isStarted())
00245     poller.stop().join();
00246 }
00247 
00248 /*void CameraSourceQTSG::setDataSourceFramerate(float fps) {
00249   DataSource::setDataSourceFramerate(fps);
00250   float s = std::max(getTimeScale(),.1f);
00251   ComponentResult err = SGSetFrameRate(sgChan, FloatToFixed(framerate*s));
00252   if(err!=noErr)
00253     std::cerr << "CameraSource::setDataSourceFramerate("<<fps<<") had error calling SGSetFrameRate " << err << endl;
00254 }*/
00255 
00256 
00257 bool CameraSourceQTSG::advance() {
00258   //cout << "getData at " << get_time() << " grabbing " << grabbing << endl;
00259   
00260   if(!checkQTThreadInit() || !grabbing)
00261     return false;
00262   
00263   unsigned int prev=frame;
00264   burnIn = frozen;
00265   
00266   OSErr err = SGIdle(sg);
00267   if(frozen) {
00268     burnIn = false;
00269     // we just cleared the backlog, but didn't get a current frame
00270     for(unsigned int i=0; i<100 && err==noErr && prev==frame; ++i) {
00271       usleep(10*1000); // wait for a new frame
00272       err = SGIdle(sg);
00273     }
00274   }
00275   if (err!=noErr && err!=callbackerr) {
00276     // some error specific to SGIdle occurred - any errors returned from the
00277     // data proc will also show up here and we don't want to write over them
00278     
00279     // in QT 4 you would always encounter a cDepthErr error after a user drags
00280     // the window, this failure condition has been greatly relaxed in QT 5
00281     // it may still occur but should only apply to vDigs that really control
00282     // the screen
00283     
00284     // you don't always know where these errors originate from, some may come
00285     // from the VDig...
00286     
00287     //DisplayError(pMungData->pWindow, "SGIdle", err);
00288     cerr << "CameraSource: SGIdle error " << err << " occurred, resetting camera!" << endl;
00289     
00290     // ...to fix this we simply call SGStop and SGStartRecord again
00291     // calling stop allows the SG to release and re-prepare for grabbing
00292     // hopefully fixing any problems, this is obviously a very relaxed
00293     // approach
00294     err = SGStop(sg); // undo SGStartPreview or SGStartRecord
00295     if(err!=noErr)
00296       cerr << "CameraSource: SGStop returned error during recovery " << err << endl;
00297     err = SGStartRecord(sg);
00298     if(err!=noErr) {
00299       cerr << "CameraSource: SGStartRecord returned error during recovery " << err << endl;
00300       grabbing=false;
00301       err = SGRelease(sg); // undo SGPrepare()
00302       if(err!=noErr)
00303         cerr << "CameraSource: SGRelease returned error during recovery " << err << endl;
00304     }
00305   }
00306   
00307   return (prev!=frame);
00308 }
00309   
00310 void CameraSourceQTSG::dumpLiteral(OSType t) {
00311   union {
00312     OSType v;
00313     char s[4];
00314   } x;
00315   x.v=t;
00316   cout << x.s[3] << x.s[2] << x.s[1] << x.s[0];
00317 }
00318 
00319 
00320 /* 
00321 * Purpose:   used to allow us to figure out how many frames are queued by the vDig
00322 *
00323 * Notes:     the UInt8 *queuedFrameCount replaces Boolean *done.  0 (==false) still means no frames, and 1 (==true) one,
00324 *            but if more than one are available, the number should be returned here - The value 2 previously meant more than
00325 *            one frame, so some VDIGs may return 2 even if more than 2 are available, and some will still return 1 as they are
00326 *            using the original definition.
00327 */
00328 pascal ComponentResult CameraSourceQTSG::compressCompleteBottleProc(SGChannel c, UInt8 *queuedFrameCount, SGCompressInfo *ci, TimeRecord *t, long refCon)
00329 {
00330   OSErr err;
00331   CameraSourceQTSG* cam = (CameraSourceQTSG*)refCon;
00332   if (NULL == cam) return -1;
00333   
00334   // call the original proc; you must do this
00335   err = SGGrabCompressComplete(c, queuedFrameCount, ci, t);
00336   
00337   // save the queued frame count so we have it
00338   cam->queuedFrames = *queuedFrameCount;
00339   /*if(cam->queuedFrames>0)
00340     cout << "compressCompleteBottleProc " << cam->queuedFrames << endl;*/
00341   
00342   return err;
00343 }
00344 
00345 
00346 /*****************************************************
00347 * Purpose:   sequence grabber data procedure - this is where the work is done
00348 *
00349 * Notes:
00350 
00351 the sequence grabber calls the data function whenever
00352 any of the grabber's channels write digitized data to the destination movie file.
00353 
00354 NOTE: We really mean any, if you have an audio and video channel then the DataProc will
00355 be called for either channel whenever data has been captured. Be sure to check which
00356 channel is being passed in. In this example we never create an audio channel so we know
00357 we're always dealing with video.
00358 
00359 This data function does two things, it first decompresses captured video
00360 data into an offscreen GWorld, draws some status information onto the frame then
00361 transfers the frame to an onscreen window.
00362 
00363 For more information refer to Inside Macintosh: QuickTime Components, page 5-120
00364 c - the channel component that is writing the digitized data.
00365 p - a pointer to the digitized data.
00366 len - the number of bytes of digitized data.
00367 offset - a pointer to a field that may specify where you are to write the digitized data,
00368 and that is to receive a value indicating where you wrote the data.
00369 chRefCon - per channel reference constant specified using SGSetChannelRefCon.
00370 time  - the starting time of the data, in the channel's time scale.
00371 writeType - the type of write operation being performed.
00372 seqGrabWriteAppend - Append new data.
00373 seqGrabWriteReserve - Do not write data. Instead, reserve space for the amount of data
00374 specified in the len parameter.
00375 seqGrabWriteFill - Write data into the location specified by offset. Used to fill the space
00376 previously reserved with seqGrabWriteReserve. The Sequence Grabber may
00377 call the DataProc several times to fill a single reserved location.
00378 refCon - the reference constant you specified when you assigned your data function to the sequence grabber.
00379 */
00380 pascal OSErr CameraSourceQTSG::grabDataProc(SGChannel c, Ptr p, long len, long *offset, long chRefCon, TimeValue time, short writeType, long refCon)
00381 {
00382 #pragma unused(offset,chRefCon,writeType)
00383   //cout << "MungGrabDataProc" << endl;
00384   
00385   CameraSourceQTSG* cam = (CameraSourceQTSG*)refCon; // might want to use chRefCon instead?
00386   if (NULL == cam) {
00387     cerr << "CameraSource::grabDataProc called without a context" << endl;
00388     return -1;
00389   }
00390   
00391   // we only care about the video 
00392   if (c != cam->sgChan) {
00393     cerr << "CameraSource::grabDataProc called for something other than our channel" << endl;
00394     return noErr; //not an error as far as OS is concerned...
00395   }
00396   
00397   if(cam->burnIn || (cam->frozen && cam->queuedFrames>1))
00398     return noErr; // we want a current frame when being manually advanced, skip backlog
00399   
00400   ++cam->skipped; // unless we make it through all the way, in which case we'll subtract this off and add to frame instead
00401   
00402   ComponentResult err=noErr;
00403   TimeValue frameTimeDelta=0;
00404   ImageDescriptionHandle imageDesc = NULL;
00405   RCRegion * region = NULL;
00406   try {
00407     // apparently can't do this before we get a frame (I tried, get seqGrabInfoNotAvailable (-9407) on SGGetChannelTimeScale
00408     if(cam->chanTimeScale==0) {
00409       Fixed framesPerSecond;
00410       long  milliSecPerFrameIgnore, bytesPerSecondIgnore;
00411       
00412       // first time here so get the time scale & timebase
00413       err = SGGetChannelTimeScale(cam->sgChan, &cam->chanTimeScale);
00414       if(err!=noErr) throw make_pair(err,"SGGetChannelTimeScale");
00415       
00416       err = SGGetTimeBase(cam->sg, &cam->chanTimeBase);
00417       if(err!=noErr) throw make_pair(err,"SGGetTimeBase");
00418       
00419       err = VDGetDataRate(SGGetVideoDigitizerComponent(cam->sgChan), &milliSecPerFrameIgnore, &framesPerSecond, &bytesPerSecondIgnore);
00420       if(err!=noErr) throw make_pair(err,"VDGetDataRate");
00421       
00422       cam->duration = 1.f / FixedToFloat(framesPerSecond);
00423       cam->poller.resetPeriod(cam->duration,false);
00424 
00425       //SGGetFrameRate(cam->sgChan, &framesPerSecond); // returns 0 fps
00426       //std::cout << "SGGetFrameRate says " << (1.f / FixedToFloat(framesPerSecond)) << std::endl;
00427     }
00428     
00429     // retrieve a channel's current sample description, the channel returns a
00430     // sample description that is appropriate to the type of data being captured
00431     imageDesc = (ImageDescriptionHandle)NewHandle(0);
00432     err = SGGetChannelSampleDescription(c, (Handle)imageDesc);
00433     if(err!=noErr) throw "SGGetChannelSampleDescription";
00434     string formatName = p2c((**imageDesc).name);
00435     //cout << formatName << " TYPE IS "; dumpLiteral((**imageDesc).cType); cout << " " << (**imageDesc).width << "x" << (**imageDesc).height << " => " << (**imageDesc).dataSize << " @ " << (**imageDesc).depth << endl;
00436 
00437     if((**imageDesc).cType==k422YpCbCr8CodecType) { // aka 2vuy
00438     
00439       region = cam->imgFrom2vuy((unsigned char*)p, (**imageDesc).width, (**imageDesc).height, (**imageDesc).depth, (**imageDesc).dataSize);
00440       
00441     } else if((**imageDesc).cType==kComponentVideoCodecType) { // aka yuv2 aka yuvu
00442       
00443       region = cam->imgFromyuv2((unsigned char*)p, (**imageDesc).width, (**imageDesc).height, (**imageDesc).depth, (**imageDesc).dataSize);
00444       
00445     } else {
00446       // use a OS provided decompression sequence to put it in the gworld
00447       if (cam->drawSeq == 0) {
00448         
00449         // set up decompression sequence  
00450         Rect           sourceRect = { 0, 0, 0, 0 };
00451         MatrixRecord       scaleMatrix; 
00452         CodecFlags         cFlags = codecNormalQuality;
00453         
00454         if(cam->gworld==NULL) {
00455           Rect  srcBounds;
00456           err = SGGetChannelBounds(cam->sgChan, &srcBounds);
00457           if(err!=noErr) throw "SGGetSrcVideoBounds";
00458           unsigned int width = srcBounds.right-srcBounds.left;
00459           unsigned int height = srcBounds.bottom-srcBounds.top;
00460           cam->gworldBuf = (char*)malloc(width*height*2); // NewPtr(width*height*4);
00461           // once upon a time I could've sworn I had to use k32ARGBPixelFormat and do
00462           // the color space conversion manually, but apparently this is working... (yay! very fast...)
00463           err = QTNewGWorldFromPtr(&cam->gworld, k2vuyPixelFormat, &srcBounds, NULL, NULL, 0, cam->gworldBuf, width*2);
00464           if(err!=noErr) throw "QTNewGWorldFromPtr";
00465         }
00466           
00467         // make a scaling matrix for the sequence
00468         sourceRect.right = (**imageDesc).width;
00469         sourceRect.bottom = (**imageDesc).height;
00470         RectMatrix(&scaleMatrix, &sourceRect, &(*GetPortPixMap(cam->gworld))->bounds);
00471         
00472         // begin the process of decompressing a sequence of frames
00473         // this is a set-up call and is only called once for the sequence - the ICM will interrogate different codecs
00474         // and construct a suitable decompression chain, as this is a time consuming process we don't want to do this
00475         // once per frame (eg. by using DecompressImage)
00476         // for more information see Ice Floe #8 http://developer.apple.com/quicktime/icefloe/dispatch008.html
00477         // the destination is specified as the GWorld
00478         err = DecompressSequenceBeginS(&cam->drawSeq,         // pointer to field to receive unique ID for sequence
00479           imageDesc,              // handle to image description structure
00480           p,                  // points to the compressed image data
00481           len,                                // size of the data buffer
00482           cam->gworld,  // port for the DESTINATION image
00483           NULL,                 // graphics device handle, if port is set, set to NULL
00484           NULL,                 // decompress the entire source image - no source extraction
00485           &scaleMatrix,             // transformation matrix
00486           srcCopy,                // transfer mode specifier
00487           (RgnHandle)NULL,            // clipping region in dest. coordinate system to use as a mask
00488           0,                  // flags
00489           cFlags,                 // accuracy in decompression
00490           bestSpeedCodec);            // compressor identifier or special identifiers ie. bestSpeedCodec
00491         
00492         if(err!=noErr) throw "DecompressSequenceBeginS";
00493       }
00494       
00495       // get the TimeBase time and figure out the delta between that time and this frame time
00496       TimeValue timeBaseTime, timeBaseDelta;
00497       timeBaseTime = GetTimeBaseTime(cam->chanTimeBase, cam->chanTimeScale, NULL);
00498       timeBaseDelta = timeBaseTime - time;
00499       frameTimeDelta = time - cam->lastTime;
00500       
00501       if (timeBaseDelta < 0) return err; // probably don't need this
00502       
00503       // if we have more than one queued frame and our capture rate drops below 10 frames, skip the frame to try and catch up
00504       if ((cam->queuedFrames > 1) &&  ((cam->chanTimeScale / frameTimeDelta) < 10) && (cam->skipped < 15)) {
00505         // do nothing, skipping frame
00506       } else {
00507         CodecFlags ignore;
00508         
00509         // decompress a frame into the window - can queue a frame for async decompression when passed in a completion proc
00510         err = DecompressSequenceFrameS(cam->drawSeq,  // sequence ID returned by DecompressSequenceBegin
00511            p,         // pointer to compressed image data
00512            len,         // size of the buffer
00513            0,         // in flags
00514            &ignore,       // out flags
00515            NULL);       // async completion proc
00516         
00517         if(err!=noErr) throw "DecompressSequenceFrameS";
00518         
00519         cam->skipped = 1;
00520         cam->lastTime = time;
00521         
00522         {
00523           PixMapPtr pm=*GetPortPixMap(cam->gworld);
00524           unsigned int width = pm->bounds.right - pm->bounds.left;
00525           unsigned int height = pm->bounds.bottom - pm->bounds.top;
00526           unsigned char * s = (unsigned char *)GetPixBaseAddr(&pm);
00527           region = cam->imgFrom2vuy(s,width,height,16,width*height*2);
00528         }
00529       } 
00530     }
00531   } catch(const char* call) {
00532     cerr << "CameraSource: " << call << " returned error " << err << endl;
00533     if(imageDesc!=NULL)
00534       DisposeHandle((Handle)imageDesc);
00535     return cam->callbackerr=err;
00536   }
00537   if(imageDesc!=NULL)
00538     DisposeHandle((Handle)imageDesc);
00539     
00540   /*
00541   // status information
00542   float fps, averagefps;
00543   UInt8   minutes, seconds, frames;
00544   
00545   fps = (float)cam->chanTimeScale / (float)frameTimeDelta;
00546   averagefps = ((float)cam->frame * (float)cam->chanTimeScale) / (float)time;
00547   minutes = (time / cam->chanTimeScale) / 60;
00548   seconds = (time / cam->chanTimeScale) % 60;
00549   frames = (time % cam->chanTimeScale) / frameTimeDelta; //cam->duration;
00550   printf("t: %ld, %02d:%02d.%02d, fps:%5.1f av:%5.1f\n", time, minutes, seconds, frames, fps, averagefps);
00551   */
00552   
00553   ASSERTRETVAL(region!=NULL,"region still NULL at end of CameraSource::grabDataProc",-1)
00554   
00555   // made it!  increment frame and decrement our pessimistic skip
00556   ++cam->frame;
00557   --cam->skipped;
00558   cam->setImage(region);
00559   
00560   return err;
00561 }
00562 
00563 
00564 /*! 2vuy is an interleaved format, where the u and v channels are downsampled by half.
00565 *  The first column of pixels has u values, the second column is v's.
00566 *  For example, the first 6 pixels of a row are stored as: uy, vy, uy, vy, uy, vy, ...  */
00567 RCRegion* CameraSourceQTSG::imgFrom2vuy(const unsigned char * s, short srcWidth, short srcHeight, short depth, long dataSize) {
00568 #pragma unused(depth,dataSize)
00569   const unsigned int width=srcWidth/2;
00570   const unsigned int height=srcHeight/2;
00571   const unsigned int components=3;
00572   ssize_t reqSize = sizeof(ImageHeader) + width * height * components;
00573   RCRegion * region = getUnusedRegion(reqSize, 0);
00574   unsigned char * buf = reinterpret_cast<unsigned char*>(region->Base());
00575   new (region->Base()) ImageHeader(0, layer, width, height, components, frame, get_time(), nextName());
00576   
00577   const unsigned int srcStride=srcWidth*2;
00578   unsigned char * dst = buf + sizeof(ImageHeader);
00579   unsigned char * const dstEnd=dst+width*height*components;
00580   while(dst!=dstEnd) {
00581     unsigned char * const rowEnd=dst+width*components;
00582     while(dst!=rowEnd) {
00583       unsigned int y,u,v;
00584       u=*s;
00585       u+=*(s+srcStride);
00586       ++s;
00587       
00588       y=*s;
00589       y+=*(s+srcStride);
00590       ++s;
00591       
00592       v=*s;
00593       v+=*(s+srcStride);
00594       ++s;
00595       
00596       y+=*s;
00597       y+=*(s+srcStride);
00598       ++s;
00599       
00600       *dst++ = y/4;
00601       *dst++ = u/2;
00602       *dst++ = v/2;
00603     }
00604     s+=srcStride;
00605   }
00606   ASSERTRETVAL(dst-buf==reqSize,"CameraSource bad imgFrom2vuy " << reqSize << " vs " << (dst-buf),NULL);
00607   return region;
00608 }
00609 
00610 /*! Similar to 2vuy, except:
00611  *  - byte swapped (yu, yv instead of uy, vy)
00612  *  - u and v channels are signed instead of unsigned
00613  *  - channels use the full byte range, whereas apparently 2vuy only uses 16-235 for the y and 16-240 for the u and v
00614  *    (aka 601/YCbCr standard)
00615  */
00616 RCRegion* CameraSourceQTSG::imgFromyuv2(const unsigned char * s, short srcWidth, short srcHeight, short depth, long dataSize) {
00617 #pragma unused(depth,dataSize)
00618   const unsigned int width=srcWidth/2;
00619   const unsigned int height=srcHeight/2;
00620   const unsigned int components=3;
00621   ssize_t reqSize = sizeof(ImageHeader) + width * height * components;
00622   RCRegion * region = getUnusedRegion(reqSize, 0);
00623   unsigned char * buf = reinterpret_cast<unsigned char*>(region->Base());
00624   new (region->Base()) ImageHeader(0, layer, width, height, components, frame, get_time(), nextName());
00625   
00626   const unsigned int srcStride=srcWidth*2;
00627   unsigned char * dst = buf + sizeof(ImageHeader);
00628   unsigned char * const dstEnd=dst+width*height*components;
00629   while(dst!=dstEnd) {
00630     unsigned char * const rowEnd=dst+width*components;
00631     while(dst!=rowEnd) {
00632       unsigned int y;
00633       int u,v;
00634       y=*s;
00635       y+=*(s+srcStride);
00636       ++s;
00637       
00638       u=(char)*s;
00639       u+=(char)*(s+srcStride);
00640       ++s;
00641       
00642       y+=*s;
00643       y+=*(s+srcStride);
00644       ++s;
00645       
00646       v=(char)*s;
00647       v+=(char)*(s+srcStride);
00648       ++s;
00649             
00650       *dst++ = (y*219/255)/4 + 16;
00651       *dst++ = (u*224/255)/2 + 128;
00652       *dst++ = (v*224/255)/2 + 128;
00653     }
00654     s+=srcStride;
00655   }
00656   ASSERTRETVAL(dst-buf==reqSize,"CameraSourceOSX bad imgFromyuv2 " << reqSize << " vs " << (dst-buf),NULL);
00657   return region;
00658 }
00659 
00660 #endif // pre-10.6
00661 #endif // Apple 32 bit
00662 
00663 /*! @file
00664  * @brief Describes CameraSourceQTSG, which interfaces with a specific camera through QuickTime and the Sequence Grabber, which is deprecated.  See the alternative CameraSourceQTKit implementation.
00665  * @author Ethan Tira-Thompson (ejt) (Creator)
00666  */

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:38 2016 by Doxygen 1.6.3