/* GridFlow Copyright (c) 2001-2011 by Mathieu Bouchard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See file ../COPYING for further informations on licensing terms. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "gridflow.hxx.fcs" #include "colorspace.hxx" #include #include #include #include #include extern map oserr_table; #define DEBUG false typedef ComponentInstance VideoDigitizerComponent, VDC; typedef ComponentResult VideoDigitizerError, VDE; #if 0 //enum {VDCType='vdig', vdigInterfaceRev=2 }; //enum {ntscIn=0, currentIn=0, palIn, secamIn, ntscReallyIn }; //enum {compositeIn, sVideoIn, rgbComponentIn, rgbComponentSyncIn, yuvComponentIn, yuvComponentSyncIn, tvTunerIn, sdiIn}; //enum {vdPlayThruOff, vdPlayThruOn}; //enum {vdDigitizerBW, vdDigitizerRGB}; //enum {vdBroadcastMode, vdVTRMode}; //enum {vdUseAnyField, vdUseOddField, vdUseEvenField}; //enum {vdTypeBasic, vdTypeAlpha, vdTypeMask, vdTypeKey}; /*enum {digiInDoesNTSC, digiInDoesPAL, digiInDoesSECAM, skip 4, digiInDoesGenLock, digiInDoesComposite, digiInDoesSVideo, digiInDoesComponent, digiInVTR_Broadcast, digiInDoesColor, digiInDoesBW, skip 17, digiInSignalLock};*/ /*bitset {digiOutDoes1, digiOutDoes2, digiOutDoes4, digiOutDoes8, digiOutDoes16, digiOutDoes32, digiOutDoesDither, digiOutDoesStretch, digiOutDoesShrink, digiOutDoesMask, skip 1, digiOutDoesDouble, digiOutDoesQuad, digiOutDoesQuarter, digiOutDoesSixteenth, digiOutDoesRotate, digiOutDoesHorizFlip, digiOutDoesVertFlip, digiOutDoesSkew, digiOutDoesBlend, digiOutDoesWarp, digiOutDoesHW_DMA, digiOutDoesHWPlayThru, digiOutDoesILUT, digiOutDoesKeyColor, digiOutDoesAsyncGrabs, digiOutDoesUnreadableScreenBits, digiOutDoesCompress, digiOutDoesCompressOnly, digiOutDoesPlayThruDuringCompress, digiOutDoesCompressPartiallyVisible, digiOutDoesNotNeedCopyOfCompressData};*/ /*struct DigitizerInfo { short vdigType; long inputCapabilityFlags, outputCapabilityFlags; long inputCurrentFlags, outputCurrentFlags; short slot; GDHandle gdh, maskgdh; short minDestHeight, minDestWidth; short maxDestHeight, maxDestWidth; short blendLevels; long reserved;};*/ /*struct VdigType { long digType, reserved;};*/ /*struct VdigTypeList { short count; VdigType list[1];};*/ /*struct VdigBufferRec { PixMapHandle dest; Point location; long reserved;};*/ /*struct VdigBufferRecList { short count; MatrixRecordPtr matrix; RgnHandle mask; VdigBufferRec list[1];};*/ //typedef VdigBufferRecList *VdigBufferRecListPtr; //typedef VdigBufferRecListPtr *VdigBufferRecListHandle; //typedef CALLBACK_API(void,VdigIntProcPtr)(long flags, long refcon); //typedef STACK_UPP_TYPE(VdigIntProcPtr); /*struct VDCompressionList { CodecComponent codec; CodecType cType; Str63 typeName, name; long formatFlags, compressFlags, reserved;};*/ //typedef VDCompressionList * VDCompressionListPtr; //typedef VDCompressionListPtr *VDCompressionListHandle; /*bitset { dmaDepth1, dmaDepth2, dmaDepth4, dmaDepth8, dmaDepth16, dmaDepth32, dmaDepth2Gray, dmaDepth4Gray, dmaDepth8Gray};*/ //enum {kVDIGControlledFrameRate=-1}; //bitset {vdDeviceFlagShowInputsAsDevices, vdDeviceFlagHideDevice}; /*bitset { vdFlagCaptureStarting, vdFlagCaptureStopping, vdFlagCaptureIsForPreview, vdFlagCaptureIsForRecord, vdFlagCaptureLowLatency, vdFlagCaptureAlwaysUseTimeBase, vdFlagCaptureSetSettingsBegin, vdFlagCaptureSetSettingsEnd};*/ /*\class VDC VDE VDGetMaxSrcRect (short inputStd, Rect *maxSrcRect) VDE VDGetActiveSrcRect(short inputStd, Rect *activeSrcRect) VDE VD[GS]etDigitizerRect(Rect *digitizerRect) VDE VDGetVBlankRect(short inputStd, Rect *vBlankRect) VDE VDGetMaskPixMap(PixMapHandlemaskPixMap) VDE VDGetPlayThruDestination(PixMapHandle * dest, Rect *destRect, MatrixRecord * m, RgnHandle *mask) VDE VDUseThisCLUT(CTabHandle colorTableHandle) VDE VD[SG*]etInputGammaValue(Fixed channel1, Fixed channel2, Fixed channel3) VDE VD[GS]etSharpness(uint16 *) VDE VDGrabOneFrame(VDC ci) VDE VDGetMaxAuxBuffer(PixMapHandle *pm, Rect *r) VDE VDGetDigitizerInfo(DigitizerInfo *info) VDE VDGetCurrentFlags(long *inputCurrentFlag, long *outputCurrentFlag) VDE VD[SG*]etKeyColor(long index) VDE VDAddKeyColor(long *index) VDE VDGetNextKeyColor(long index) VDE VD[GS]etKeyColorRange(RGBColor minRGB, RGBColor maxRGB) VDE VDSetDigitizerUserInterrupt(long flags, VdigIntUPP userInterruptProc, long refcon) VDE VD[SG*]etInputColorSpaceMode(short colorSpaceMode) VDE VD[SG*]etClipState(short clipEnable) VDE VDSetClipRgn(RgnHandle clipRegion) VDE VDClearClipRgn(RgnHandle clipRegion) VDE VDGetCLUTInUse(CTabHandle *colorTableHandle) VDE VD[SG*]etPLLFilterType(short pllType) VDE VDGetMaskandValue(uint16 blendLevel, long *mask, long *value) VDE VDSetMasterBlendLevel(uint16 *blendLevel) VDE VDSetPlayThruDestination(PixMapHandledest, RectPtr destRect, MatrixRecordPtr m, RgnHandle mask) VDE VDSetPlayThruOnOff(short state) VDE VD[SG*]etFieldPreference(short fieldFlag) VDE VDPreflightDestination(Rect *digitizerRect, PixMap **dest, RectPtr destRect, MatrixRecordPtr m) VDE VDPreflightGlobalRect(GrafPtr theWindow, Rect *globalRect) VDE VDSetPlayThruGlobalRect(GrafPtr theWindow, Rect *globalRect) VDE VDSetInputGammaRecord(VDGamRecPtrinputGammaPtr) VDE VDGetInputGammaRecord(VDGamRecPtr *inputGammaPtr) VDE VD[SG]etBlackLevelValue(uint16 *) VDE VD[SG]etWhiteLevelValue(uint16 *) VDE VDGetVideoDefaults(uint16 *blackLevel, uint16 *whiteLevel, uint16 *brightness, uint16 *hue, uint16 *saturation, uint16 *contrast, uint16 *sharpness) VDE VDGetNumberOfInputs(short *inputs) VDE VDGetInputFormat(short input, short *format) VDE VD[SG*]etInput(short input) VDE VDSetInputStandard(short inputStandard) VDE VDSetupBuffers(VdigBufferRecListHandle bufferList) VDE VDGrabOneFrameAsync(short buffer) VDE VDDone(short buffer) VDE VDSetCompression(OSTypecompressType, short depth, Rect *bounds, CodecQspatialQuality, CodecQtemporalQuality, long keyFrameRate) VDE VDCompressOneFrameAsync(VDC ci) VDE VDCompressDone(UInt8 *queuedFrameCount, Ptr *theData, long *dataSize, UInt8 *similarity, TimeRecord *t) VDE VDReleaseCompressBuffer(Ptr bufferAddr) VDE VDGetImageDescription(ImageDescriptionHandle desc) VDE VDResetCompressSequence(VDC ci) VDE VDSetCompressionOnOff(Boolean) VDE VDGetCompressionTypes(VDCompressionListHandle h) VDE VDSetTimeBase(TimeBase t) VDE VDSetFrameRate(Fixed framesPerSecond) VDE VDGetDataRate(long *milliSecPerFrame, Fixed *framesPerSecond, long *bytesPerSecond) VDE VDGetSoundInputDriver(Str255 soundDriverName) VDE VDGetDMADepths(long *depthArray, long *preferredDepth) VDE VDGetPreferredTimeScale(TimeScale *preferred) VDE VDReleaseAsyncBuffers(VDC ci) VDE VDSetDataRate(long bytesPerSecond) VDE VDGetTimeCode(TimeRecord *atTime, void *timeCodeFormat, void *timeCodeTime) VDE VDUseSafeBuffers(Boolean useSafeBuffers) VDE VDGetSoundInputSource(long videoInput, long *soundInput) VDE VDGetCompressionTime(OSTypecompressionType, short depth, Rect *srcRect, CodecQ *spatialQuality, CodecQ *temporalQuality, ulong *compressTime) VDE VDSetPreferredPacketSize(long preferredPacketSizeInBytes) VDE VD[SG*]etPreferredImageDimensions(long width, long height) VDE VDGetInputName(long videoInput, Str255 name) VDE VDSetDestinationPort(CGrafPtr destPort) VDE VDGetDeviceNameAndFlags(Str255 outName, UInt32 *outNameFlags) VDE VDCaptureStateChanging(UInt32inStateFlags) VDE VDGetUniqueIDs(UInt64 *outDeviceID, UInt64 *outInputID) VDE VDSelectUniqueIDs(const UInt64 *inDeviceID, const UInt64 *inInputID) */ #endif static OSErr callback(ComponentInstanceRecord*, char*, long int, long int*, long int, TimeValue, short int, long int) { post("FormatQuickTimeCamera callback"); return noErr; } \class FormatQuickTimeCamera : Format { Dim dim; uint8 *buf; uint8 *buf2; VDC vdc; int m_newFrame; SeqGrabComponent m_sg; SGChannel m_vc; SGDeviceList deviceList; int nDevices; short m_pixelDepth; Rect rect; GWorldPtr m_srcGWorld; PixMapHandle m_pixMap; Ptr m_baseAddr; long m_rowBytes; int m_quality; P bit_packing3; \constructor (t_symbol *mode, int device) { dim = Dim(240,320,3); _0_colorspace(gensym("rgb")); OSErr e; rect.top=rect.left=0; rect.bottom=dim[0]; rect.right=dim[1]; int n=0, i, j; Component c = 0; ComponentDescription cd; cd.componentType = SeqGrabComponentType; cd.componentSubType = 0; cd.componentManufacturer = 0; cd.componentFlags = 0; cd.componentFlagsMask = 0; for(;;) { c = FindNextComponent(c, &cd); if (!c) break; ComponentDescription cd2; Ptr name=0,info=0,icon=0; GetComponentInfo(c,&cd2,&name,&info,&icon); //post("Component #%d",n); char *t = (char *)&cd.componentType; //post(" type='%c%c%c%c'",t[3],t[2],t[1],t[0]); t = (char *)&cd.componentSubType; //post(" subtype='%c%c%c%c'",t[3],t[2],t[1],t[0]); //post(" name=%08x, *name='%*s'",name, *name, name+1); //post(" info=%08x, *info='%*s'",info, *name, info+1); n++; } m_sg = OpenDefaultComponent(SeqGrabComponentType, 0); if(!m_sg) RAISE("could not open default component"); e=SGInitialize(m_sg); if(e!=noErr) RAISE("could not initialize SG"); e=SGSetDataRef(m_sg, 0, 0, seqGrabDontMakeMovie); if (e!=noErr) RAISE("dataref failed"); e=SGNewChannel(m_sg, VideoMediaType, &m_vc); if(e!=noErr) RAISE("could not make new SG channel"); e=SGSetChannelBounds(m_vc, &rect); if(e!=noErr) RAISE("could not set SG ChannelBounds"); e=SGGetChannelDeviceList(m_vc, sgDeviceListIncludeInputs, &deviceList); if (e!=noErr) RAISE("could not get device list"); else { nDevices = (*deviceList)->count; //fprintf(stderr," number of available devices: %d\n", nDevices); //fprintf(stderr," current device: %d\n", (*deviceList)->selectedIndex); //for (int i=0; ientry[i].name,1+(*deviceList)->entry[i].name); } // treat the device list in reverse order device = nDevices-1-device; e=SGSetChannelDevice(m_vc, (*deviceList)->entry[device].name); if(e!=noErr) RAISE("could not set channel device"); else { char *s1, s2[MAXPDSTRING]; s1 = (char *)(*deviceList)->entry[device].name; for (i=1, j=0; i<=*s1; i++) { if (isalnum(s1[i])) s2[j++] = s1[i]; else if (s1[i] == ' ') s2[j++] = '_'; } s2[j] = '\0'; name = gensym(s2); } e=SGSetChannelUsage(m_vc, seqGrabPreview); if(e!=noErr) post("could not set SG ChannelUsage"); e=SGSetDataProc(m_sg,NewSGDataUPP(callback),0); if (e!=noErr) post("could not set SG DataProc"); switch (3) { case 0: e=SGSetChannelPlayFlags(m_vc, channelPlayNormal); break; case 1: e=SGSetChannelPlayFlags(m_vc, channelPlayHighQuality); break; case 2: e=SGSetChannelPlayFlags(m_vc, channelPlayFast); break; case 3: e=SGSetChannelPlayFlags(m_vc, channelPlayAllData); break; } vdc = SGGetVideoDigitizerComponent(m_vc); int sy = dim[0], sx = dim[1], sc = dim[2]; int dataSize = dim.prod(); buf = new uint8[sy*sx*4]; buf2 = new uint8[sy*sx*4]; m_rowBytes = sx*4; e=QTNewGWorldFromPtr (&m_srcGWorld,k32ARGBPixelFormat,&rect,NULL,NULL,0,buf,m_rowBytes); if (0/*yuv*/) { int dataSize = dim.prod()*2/4; buf = new uint8[dataSize]; m_rowBytes = dim.prod(1)*2/4; e=QTNewGWorldFromPtr (&m_srcGWorld,k422YpCbCr8CodecType,&rect,NULL,NULL,0,buf,m_rowBytes); } if (e!=noErr) RAISE("error #%d at QTNewGWorldFromPtr",e); if (!m_srcGWorld) RAISE("Could not allocate off screen"); SGSetGWorld(m_sg,(CGrafPtr)m_srcGWorld, NULL); e=SGStartRecord(m_sg); if (e!=noErr) RAISE("error #%d at SGStartRecord",e); } ~FormatQuickTimeCamera() { if (m_vc) if (::SGDisposeChannel(m_sg, m_vc)) RAISE("SGDisposeChannel"); if (m_sg) { if (::CloseComponent(m_sg)) RAISE("CloseComponent"); if (m_srcGWorld) ::DisposeGWorld(m_srcGWorld); } } \decl 0 bang (); \grin 0 int \attr t_symbol *name; \attr uint16 brightness(); \attr uint16 contrast(); \attr uint16 hue(); \attr uint16 colour(); \attr t_symbol *colorspace; \decl 0 get (t_symbol *s=0); \decl 0 size (int height, int width); }; \def 0 size (int height, int width) { OSErr e = VDGetDigitizerRect(vdc,&rect); if (e!=noErr) RAISE("VDGetDigitizerRect error"); if (DEBUG) post("rect1: top=%d left=%d bottom=%d right=%d",rect.top,rect.left,rect.bottom,rect.right); dim = Dim(height,width,dim[2]); rect.bottom = height; rect.right = width; if (DEBUG) post("rect2: top=%d left=%d bottom=%d right=%d",rect.top,rect.left,rect.bottom,rect.right); e = VDSetDigitizerRect(vdc,&rect); if (e!=noErr) post("VDSetDigitizerRect error"); e = SGSetChannelBounds(m_vc,&rect); if (e!=noErr) post("could not set SG ChannelBounds"); } \def 0 get (t_symbol *s=0) { FObject::_0_get(s); if (!s) { DigitizerInfo di; OSErr e = VDGetDigitizerInfo(vdc,&di); if (e!=noErr) RAISE("VDGetDigitizerInfo error"); if (DEBUG) { post("vdigType=%d inputCapabilityFlags=0x%08lx outputCapabilityFlags=0x%08lx inputCurrentFlags=0x%08lx outputCurrentFlags=0x%08lx", di.vdigType, di.inputCapabilityFlags, di.outputCapabilityFlags, di.inputCurrentFlags, di.outputCurrentFlags); post("slot=%d gdh=%p maskgdh=%p",di.slot,di.gdh,di.maskgdh); post("minDestHeight=%ld minDestWidth=%ld maxDestHeight=%ld maxDestWidth=%ld", di.minDestHeight, di.minDestWidth, di.maxDestHeight, di.maxDestWidth); post("blendLevels=%d reserved=%d",di.blendLevels,di.reserved); } //Rect r; //OSErr e = SGGetChannelBounds (m_vc,&r); //if (e!=noErr) RAISE("SGGetChannelBounds error"); //{t_atom2 a[2] = { 1, 1}; out[0](gensym("minsize"),2,a);} //{t_atom2 a[2] = { r.bottom, r.right}; out[0](gensym("maxsize"),2,a);} {t_atom2 a[2] = {di.minDestHeight,di.minDestWidth}; out[0](gensym("minsize"),2,a);} {t_atom2 a[2] = {di.maxDestHeight,di.maxDestWidth}; out[0](gensym("maxsize"),2,a);} {t_atom2 a[2] = { rect.bottom, rect.right}; out[0](gensym( "size"),2,a);} } } \def 0 colorspace (t_symbol *colorspace) { /* y yuv rgb rgba magic */ string c = colorspace->s_name; if (c=="y" ) {} else if (c=="yuv" ) {} else if (c=="rgb" ) {} else if (c=="rgba" ) {} else //if (c=="magic") {} else RAISE("got '%s' but supported colorspaces are: y yuv rgb rgba",c.data()); uint32 masks[4]={0x0000ff00,0x00ff0000,0xff000000,0x00000000}; if (!is_le()) swap32(4,masks); bit_packing3 = new BitPacking(is_le(),4,3,masks); //bit_packing4 = new BitPacking(is_le(),bytes,4,masks); this->colorspace=gensym(c.data()); dim = Dim(dim[0],dim[1],c=="y"?1:c=="rgba"?4:3); } static int nn(int c) {return c?c:' ';} \def 0 bang () { GridOut out(this,0,dim,cast); string cs = colorspace->s_name; int sy = dim[0]; int sx = dim[1]; int sc = dim[2]; uint8 rgb[sx*4+4]; // with extra padding in case of odd size... uint8 b2[ sx*3+3]; int bs = sx*sc; if (cs=="y") { for(int y=0; yunpack(sx,buf+y*sx*bit_packing3->bytes,rgb); for (int x=0,xx=0; x>8; b2[x+1] = (76*rgb[xx+3]+150*rgb[xx+4]+29*rgb[xx+5])>>8; } out.send(bs,b2); } } else if (cs=="yuv") { for(int y=0; yunpack(sx,buf+y*sx*bit_packing3->bytes,rgb); for (int x=0,xx=0; xunpack(sx,buf+y*sx*bit_packing3->bytes,rgb); bit_packing3->unpack(n,buf,buf2); out.send(dim.prod(),buf2); } else if (cs=="rgba") { int n = dim.prod()/4; if (is_le()) { for (int i=0; i> 8) | 0xff000000; } else { for (int i=0; i