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
00055
00056
00057
00058
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
00069 sg = OpenDefaultComponent(SeqGrabComponentType, 0);
00070 if(sg==NULL) throw std::make_pair(err,"OpenDefaultComponent(SeqGrabComponentType,0)");
00071
00072
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
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094 err = SGNewChannel(sg, VideoMediaType, &sgChan);
00095 if(err!=noErr) throw std::make_pair(err,"SGNewChannel");
00096
00097 unsigned char pstr[256];
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
00106
00107
00108
00109
00110
00111
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
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
00125 }
00126 err = SGSetChannelBounds(sgChan, &srcBounds);
00127 if(err!=noErr) std::cerr << "Warning: SGSetChannelBounds returned error " << err << std::endl;
00128 }
00129
00130
00131 err = SGSetChannelUsage(sgChan, seqGrabRecord | seqGrabLowLatencyCapture );
00132 if(err!=noErr) throw std::make_pair(err,"SGSetChannelUsage");
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142 err = SGSetGWorld(sg, NULL, NULL);
00143 if(err!=noErr) throw std::make_pair(err,"SGSetGWorld");
00144
00145
00146
00147
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;
00154 vb.grabCompressCompleteProc = NewSGGrabCompressCompleteBottleUPP(compressCompleteBottleProc);
00155
00156 err = SGSetVideoBottlenecks(sgChan, &vb);
00157 if(err!=noErr) throw std::make_pair(err,"SGSetVideoBottlenecks");
00158
00159
00160 err = SGSetDataProc(sg, NewSGDataUPP(grabDataProc), (long)this);
00161 if(err!=noErr) throw std::make_pair(err,"SGSetDataProc");
00162 err = SGSetChannelRefCon(sgChan, (long)this);
00163 if(err!=noErr) throw std::make_pair(err,"SGSetChannelRefCon");
00164
00165
00166 err = SGSetVideoCompressorType(sgChan,k422YpCbCr8CodecType);
00167 if(err!=noErr) {
00168 if(err!=noCodecErr) std::cerr << " Could not switch to yuv codec (k422YpCbCr8CodecType), err " << err << std::endl;
00169
00170
00171
00172
00173
00174
00175
00176 }
00177
00178
00179
00180
00181
00182
00183 }
00184
00185
00186 void CameraSourceQTSG::registerSource() {
00187 if(!checkQTThreadInit())
00188 return;
00189 if(!grabbing) {
00190
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
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212 err = SGStartRecord(sg);
00213 if(err!=noErr) {
00214 cerr << "CameraSource: SGStartRecord returned error " << err << endl;
00215 err = SGRelease(sg);
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);
00230 if(err!=noErr)
00231 cerr << "CameraSource: SGStop returned error " << err << endl;
00232 err = SGRelease(sg);
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
00249
00250
00251
00252
00253
00254
00255
00256
00257 bool CameraSourceQTSG::advance() {
00258
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
00270 for(unsigned int i=0; i<100 && err==noErr && prev==frame; ++i) {
00271 usleep(10*1000);
00272 err = SGIdle(sg);
00273 }
00274 }
00275 if (err!=noErr && err!=callbackerr) {
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288 cerr << "CameraSource: SGIdle error " << err << " occurred, resetting camera!" << endl;
00289
00290
00291
00292
00293
00294 err = SGStop(sg);
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);
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
00322
00323
00324
00325
00326
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
00335 err = SGGrabCompressComplete(c, queuedFrameCount, ci, t);
00336
00337
00338 cam->queuedFrames = *queuedFrameCount;
00339
00340
00341
00342 return err;
00343 }
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
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
00384
00385 CameraSourceQTSG* cam = (CameraSourceQTSG*)refCon;
00386 if (NULL == cam) {
00387 cerr << "CameraSource::grabDataProc called without a context" << endl;
00388 return -1;
00389 }
00390
00391
00392 if (c != cam->sgChan) {
00393 cerr << "CameraSource::grabDataProc called for something other than our channel" << endl;
00394 return noErr;
00395 }
00396
00397 if(cam->burnIn || (cam->frozen && cam->queuedFrames>1))
00398 return noErr;
00399
00400 ++cam->skipped;
00401
00402 ComponentResult err=noErr;
00403 TimeValue frameTimeDelta=0;
00404 ImageDescriptionHandle imageDesc = NULL;
00405 RCRegion * region = NULL;
00406 try {
00407
00408 if(cam->chanTimeScale==0) {
00409 Fixed framesPerSecond;
00410 long milliSecPerFrameIgnore, bytesPerSecondIgnore;
00411
00412
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
00426
00427 }
00428
00429
00430
00431 imageDesc = (ImageDescriptionHandle)NewHandle(0);
00432 err = SGGetChannelSampleDescription(c, (Handle)imageDesc);
00433 if(err!=noErr) throw "SGGetChannelSampleDescription";
00434 string formatName = p2c((**imageDesc).name);
00435
00436
00437 if((**imageDesc).cType==k422YpCbCr8CodecType) {
00438
00439 region = cam->imgFrom2vuy((unsigned char*)p, (**imageDesc).width, (**imageDesc).height, (**imageDesc).depth, (**imageDesc).dataSize);
00440
00441 } else if((**imageDesc).cType==kComponentVideoCodecType) {
00442
00443 region = cam->imgFromyuv2((unsigned char*)p, (**imageDesc).width, (**imageDesc).height, (**imageDesc).depth, (**imageDesc).dataSize);
00444
00445 } else {
00446
00447 if (cam->drawSeq == 0) {
00448
00449
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);
00461
00462
00463 err = QTNewGWorldFromPtr(&cam->gworld, k2vuyPixelFormat, &srcBounds, NULL, NULL, 0, cam->gworldBuf, width*2);
00464 if(err!=noErr) throw "QTNewGWorldFromPtr";
00465 }
00466
00467
00468 sourceRect.right = (**imageDesc).width;
00469 sourceRect.bottom = (**imageDesc).height;
00470 RectMatrix(&scaleMatrix, &sourceRect, &(*GetPortPixMap(cam->gworld))->bounds);
00471
00472
00473
00474
00475
00476
00477
00478 err = DecompressSequenceBeginS(&cam->drawSeq,
00479 imageDesc,
00480 p,
00481 len,
00482 cam->gworld,
00483 NULL,
00484 NULL,
00485 &scaleMatrix,
00486 srcCopy,
00487 (RgnHandle)NULL,
00488 0,
00489 cFlags,
00490 bestSpeedCodec);
00491
00492 if(err!=noErr) throw "DecompressSequenceBeginS";
00493 }
00494
00495
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;
00502
00503
00504 if ((cam->queuedFrames > 1) && ((cam->chanTimeScale / frameTimeDelta) < 10) && (cam->skipped < 15)) {
00505
00506 } else {
00507 CodecFlags ignore;
00508
00509
00510 err = DecompressSequenceFrameS(cam->drawSeq,
00511 p,
00512 len,
00513 0,
00514 &ignore,
00515 NULL);
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
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553 ASSERTRETVAL(region!=NULL,"region still NULL at end of CameraSource::grabDataProc",-1)
00554
00555
00556 ++cam->frame;
00557 --cam->skipped;
00558 cam->setImage(region);
00559
00560 return err;
00561 }
00562
00563
00564
00565
00566
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
00611
00612
00613
00614
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
00664
00665
00666