/* $Id: quicktimeapple.c,v 1.2 2006-03-15 04:37:46 matju Exp $ GridFlow Copyright (c) 2001,2002,2003,2004 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. */ #define T_DATA T_COCOA_DATA #include #include #include #undef T_DATA #include "../base/grid.h.fcs" #include #include #include #include typedef ComponentInstance VideoDigitizerComponent, VDC; typedef ComponentResult VideoDigitizerError, VDE; //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]etBrightness(uint16 *) VDE VD[GS]etContrast(uint16 *) VDE VD[GS]etHue(uint16 *) VDE VD[GS]etSharpness(uint16 *) VDE VD[GS]etSaturation(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) \end class VDC */ \class FormatQuickTimeCamera < Format struct FormatQuickTimeCamera : Format { P dim; Pt buf; VDC vdc; int m_newFrame; SeqGrabComponent m_sg; SGChannel m_vc; short m_pixelDepth; Rect rect; GWorldPtr m_srcGWorld; PixMapHandle m_pixMap; Ptr m_baseAddr; long m_rowBytes; int m_quality; //int m_colorspace; FormatQuickTimeCamera() : vdc(0) {} \decl void initialize (Symbol mode, Symbol source, String filename); \decl void frame (); \decl void close (); \grin 0 int }; // /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Headers/Components.h static int nn(int c) {return c?c:' ';} \def void initialize (Symbol mode, Symbol source, String filename) { L //vdc = SGGetVideoDigitizerComponent(c); rb_call_super(argc,argv); dim = new Dim(240,320,4); OSErr e; rect.top=rect.left=0; rect.bottom=dim->v[0]; rect.right=dim->v[1]; int n=0; 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); gfpost("Component #%d",n); char *t = (char *)&cd.componentType; gfpost(" type='%c%c%c%c'",nn(t[3]),nn(t[2]),nn(t[1]),nn(t[0])); t = (char *)&cd.componentSubType; gfpost(" subtype='%c%c%c%c'",nn(t[3]),nn(t[2]),nn(t[1]),nn(t[0])); gfpost(" name=%08x, *name='%*s'",name, *name, name+1); gfpost(" info=%08x, *info='%*s'",info, *name, info+1); n++; } gfpost("number of components: %d",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) gfpost("could not make new SG channel"); e=SGSetChannelBounds(m_vc, &rect); if(e!=noErr) gfpost("could not set SG ChannelBounds"); e=SGSetChannelUsage(m_vc, seqGrabPreview); if(e!=noErr) gfpost("could not set SG ChannelUsage"); // m_rowBytes = m_vidXSize*4; 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; } int dataSize = dim->prod(); buf = ARRAY_NEW(uint8,dataSize); m_rowBytes = dim->prod(1); e=QTNewGWorldFromPtr (&m_srcGWorld,k32ARGBPixelFormat, &rect,NULL,NULL,0,buf,m_rowBytes); if (0/*yuv*/) { int dataSize = dim->prod()*2/4; buf = ARRAY_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); SGStartPreview(m_sg); } /*pascal Boolean pix_videoDarwin :: SeqGrabberModalFilterProc (DialogPtr theDialog, const EventRecord *theEvent, short *itemHit, long refCon){ Boolean handled = false; if ((theEvent->what == updateEvt) && ((WindowPtr) theEvent->message == (WindowPtr) refCon)) { BeginUpdate ((WindowPtr) refCon); EndUpdate ((WindowPtr) refCon); handled = true; } WindowRef awin = GetDialogWindow(theDialog); ShowWindow (awin); SetWindowClass(awin,kUtilityWindowClass); //ChangeWindowAttributes(awin,kWindowStandardHandlerAttribute,0); SGPanelEvent(m_sg,m_vc,theDialog,0,theEvent,itemHit,&handled); // AEProcessAppleEvent (theEvent); return handled; } void pix_videoDarwin :: DoVideoSettings(){ Rect newActiveVideoRect; Rect curBounds, curVideoRect, newVideoRect; ComponentResult err; SGModalFilterUPP seqGragModalFilterUPP; err = SGGetChannelBounds (m_vc, &curBounds); err = SGGetVideoRect (m_vc, &curVideoRect); err = SGPause (m_sg, true); seqGragModalFilterUPP = (SGModalFilterUPP)NewSGModalFilterUPP(SeqGrabberModalFilterProc); err = SGSettingsDialog(m_sg, m_vc, 0, NULL, seqGrabSettingsPreviewOnly, seqGragModalFilterUPP, (long)m_srcGWorld); DisposeSGModalFilterUPP(seqGragModalFilterUPP); err = SGGetVideoRect (m_vc, &newVideoRect); err = SGGetSrcVideoBounds (m_vc, &newActiveVideoRect); err = SGPause (m_sg, false); } */ \def void frame () { GridOutlet out(this,0,dim); out.send(dim->prod(),buf); L} \def void close () { L if (m_vc) { if (::SGDisposeChannel(m_sg, m_vc)) RAISE("SGDisposeChannel"); m_vc=0; } if (m_sg) { if (::CloseComponent(m_sg)) RAISE("CloseComponent"); m_sg = NULL; if (m_srcGWorld) { ::DisposeGWorld(m_srcGWorld); m_pixMap = NULL; m_srcGWorld = NULL; m_baseAddr = NULL; } } } GRID_INLET(FormatQuickTimeCamera,0) { RAISE("Unimplemented. Sorry."); //!@#$ if (in->dim->n != 3) RAISE("expecting 3 dimensions: rows,columns,channels"); if (in->dim->get(2) != 3) RAISE("expecting 3 channels (got %d)",in->dim->get(2)); in->set_factor(in->dim->prod()); } GRID_FLOW { } GRID_FINISH { } GRID_END \classinfo { IEVAL(rself, \ruby install '#io:quicktimecamera',1,1 @comment="Apple Quicktime (CAMERA MODULE)" @flags=4 \end ruby );} \end class FormatQuickTimeCamera \class FormatQuickTimeApple < Format struct FormatQuickTimeApple : Format { Movie movie; TimeValue time; short movie_file; GWorldPtr gw; /* just like an X11 Image or Pixmap, maybe. */ Pt buffer; P dim; int nframe, nframes; FormatQuickTimeApple() : movie(0), time(0), movie_file(0), gw(0), buffer(), dim(0), nframe(0), nframes(0) {} \decl void initialize (Symbol mode, Symbol source, String filename); \decl void close (); \decl void codec_m (String c); \decl void colorspace_m (Symbol c); \decl Ruby frame (); \decl void seek (int frame); \grin 0 }; \def void seek (int frame) { nframe=frame; } \def Ruby frame () { CGrafPtr savedPort; GDHandle savedDevice; SetMovieGWorld(movie,gw,GetGWorldDevice(gw)); Rect r; GetMovieBox(movie,&r); PixMapHandle pixmap = GetGWorldPixMap(gw); short flags = nextTimeStep; if (nframe>=nframes) return Qfalse; if (nframe==0) flags |= nextTimeEdgeOK; TimeValue duration; OSType mediaType = VisualMediaCharacteristic; GetMovieNextInterestingTime(movie, flags,1,&mediaType,time,0,&time,&duration); if (time<0) { time=0; return Qfalse; } // gfpost("quicktime frame #%d; time=%d duration=%d", nframe, (long)time, (long)duration); SetMovieTimeValue(movie,nframe*duration); MoviesTask(movie,0); GridOutlet out(this,0,dim); Pt bufu32 = Pt((uint32 *)buffer.p,dim->prod()/4); int n = dim->prod()/4; int i; for (i=0; i>24); bufu32[i+1]=(bufu32[i+1]<<8)+(bufu32[i+1]>>24); bufu32[i+2]=(bufu32[i+2]<<8)+(bufu32[i+2]>>24); bufu32[i+3]=(bufu32[i+3]<<8)+(bufu32[i+3]>>24); } for (; i>24); } out.send(dim->prod(),buffer); int nf=nframe; nframe++; return INT2NUM(nf); } GRID_INLET(FormatQuickTimeApple,0) { RAISE("Unimplemented. Sorry."); //!@#$ if (in->dim->n != 3) RAISE("expecting 3 dimensions: rows,columns,channels"); if (in->dim->get(2) != 3) RAISE("expecting 3 channels (got %d)",in->dim->get(2)); in->set_factor(in->dim->prod()); } GRID_FLOW { } GRID_FINISH { } GRID_END \def void codec_m (String c) { RAISE("Unimplemented. Sorry."); } \def void colorspace_m (Symbol c) { RAISE("Unimplemented. Sorry."); } \def void close () { //!@#$ if (movie) { DisposeMovie(movie); DisposeGWorld(gw); CloseMovieFile(movie_file); movie_file=0; } rb_call_super(argc,argv); } \def void initialize (Symbol mode, Symbol source, String filename) { int err; rb_call_super(argc,argv); if (source==SYM(file)) { filename = rb_funcall(mGridFlow,SI(find_file),1,filename); FSSpec fss; FSRef fsr; err = FSPathMakeRef((const UInt8 *)rb_str_ptr(filename), &fsr, NULL); if (err) goto err; err = FSGetCatalogInfo(&fsr, kFSCatInfoNone, NULL, NULL, &fss, NULL); if (err) goto err; err = OpenMovieFile(&fss,&movie_file,fsRdPerm); if (err) goto err; } else { RAISE("usage: quicktime [file | camera bleh]"); } NewMovieFromFile(&movie, movie_file, NULL, NULL, newMovieActive, NULL); Rect r; GetMovieBox(movie, &r); gfpost("handle=%d movie=%d tracks=%d", movie_file, movie, GetMovieTrackCount(movie)); gfpost("duration=%d; timescale=%d cHz", (long)GetMovieDuration(movie), (long)GetMovieTimeScale(movie)); nframes = GetMovieDuration(movie); /* i don't think so */ gfpost("rect=((%d..%d),(%d..%d))", r.top, r.bottom, r.left, r.right); OffsetRect(&r, -r.left, -r.top); SetMovieBox(movie, &r); dim = new Dim(r.bottom-r.top, r.right-r.left, 4); SetMoviePlayHints(movie, hintsHighQuality, hintsHighQuality); buffer = ARRAY_NEW(uint8,dim->prod()); err = QTNewGWorldFromPtr(&gw, k32ARGBPixelFormat, &r, NULL, NULL, 0, buffer, dim->prod(1)); if (err) goto err; return; err: RAISE("can't open file `%s': error #%d (%s)", rb_str_ptr(filename), err, rb_str_ptr(rb_funcall(mGridFlow,SI(macerr),1,INT2NUM(err)))); } \classinfo { EnterMovies(); IEVAL(rself, \ruby install '#io:quicktime',1,1 @comment="Apple Quicktime (using Apple's)" @flags=4 suffixes_are'mov' def self.new(mode,source,filename) if source==:camera then FormatQuickTimeCamera.new(mode,source,filename) else super end end \end ruby );} \end class FormatQuickTimeApple void startup_quicktimeapple () { \startall }