#include #include #include #include #include #include #include #include #include #include #include "QTML.h" #include "Movies.h" #include "debug.h" // Status: // Judy is working on getting rowbytes working from source // gworlds // Currently doesn't work on QT4 with Hugh MR, but Judy fixed // a problem in the output component so the latest // version should work, but is not yet tested. void p_img_desc(GWorldPtr gw, char *name); GWorldPtr makegworld(Rect *r, void *newbuf, int ctype, int rowbytes); static void video_send_buf(GWorldPtr buf); char *qtformat(int format); int swapl(int x); // Digitizer globals LONG movieCtype; // VideoOut globals CGrafPtr origPort; // Save old GWorld GDHandle origDevice; GWorldPtr videoOutputGWorld, sourceGWorld; // Instance of a video output component ComponentInstance videoOutput; long globimagesize = -1; ImageSequence globseq = -1; GWorldPtr globfirstgw = 0; ComponentResult startVideoOutput(int width, int height); ComponentResult stopVideoOutput(void); OSErr MakeImageSequenceForGWorld (GWorldPtr srcGW, GWorldPtr destGW, long *imageSize, ImageSequence *seq); void v_init(void) { // Initialize QuickTime Media Layer InitializeQTML(0); // Initialize QuickTime EnterMovies(); } void v_done(void) { // Deinitialize QuickTime Media Layer ExitMovies(); // Deinitialize QuickTime Media Layer TerminateQTML(); } // // Make a list of jacks available for output. // If there is more than one, the index in the array // uniquely indicates which jack to select. // // QT doesn't support multiple jacks, so make one up. // int v_make_jacklist(void *pjlist, int *pjcount) { char *jlist[] = { "QT Default Output", (char *)0 }; int njacks = 1; *(char ***)pjlist = jlist; *pjcount = njacks; return 0; } int v_unmake_jacklist(void *pjlist, int jcount) { return 0; } int v_make_header(void *inbuf, void **outheader, int w, int h, int rowbytes, int size) { Rect sr; GWorldPtr pgw; sr.top = sr.left = 0; sr.right = w; sr.bottom = h; if (size == 32) movieCtype = 'ABGR'; else movieCtype = '5551'; pgw = makegworld(&sr, inbuf, movieCtype, rowbytes); if (!pgw) { PRINT1 ("Could not create GWorld for source data\n", 0); return 1; } p_img_desc(pgw, "v_make_header"); *outheader = pgw; // save gworld pointer globfirstgw = pgw; return 0; } int v_unmake_header(void *outheader) { DisposeGWorld(outheader); return 0; } int v_start(int jack, int width, int height) { ImageSequence seq = 0; long imageSize; OSErr err; PixMapHandle sourcePixmap; startVideoOutput(width, height); sourcePixmap = GetGWorldPixMap(globfirstgw); LockPixels (sourcePixmap); err = MakeImageSequenceForGWorld (globfirstgw, videoOutputGWorld, &imageSize, &seq); if (err) { PRINT1("MakeImageSequenceForGWorld returned error message\n",0); } p_img_desc(globfirstgw, "FirstGW"); p_img_desc(videoOutputGWorld, "OutGW"); globseq = seq; globimagesize = imageSize; return 0; } int v_stop(void) { stopVideoOutput(); return 0; } int v_reset(void) { return 0; } // Wait for and dispatch video messages in this thread. // DWORD WINAPI v_thread (LPVOID args) { volatile threadargs_t *ta = args; void *buf; void *(*next)(void); next = ta->nextbuf; while (1) { if (ta->killthread == 1) { ta->killthread = 0; ExitThread(0); } buf = (*next)(); video_send_buf(buf); } return 0; } #ifdef completion void mycallback(long arg) { // printf ("Callback received: %d\n", arg); } #endif GWorldPtr makegworld(Rect *r, void *newbuf, int ctype, int rowbytes) { #ifdef GWALLOCMEM char *unalignedbuf; int psize, w; #endif GWorldPtr gw; char *buf; int rv; PRINT2("makegworld: %d by %d\n", r->bottom, r->right); PRINT2("makegworld: newbuf 0x%x, rowbytes %d\n", newbuf, rowbytes); PRINT1("makegworld: ctype %s\n", qtformat(ctype)); #ifdef GWALLOCMEM switch(ctype) { // These two have the best performance reading from the screen // on the SG320 // case 'ABGR': psize = 4 ; break; case '5551': psize = 2 ; break; // case 'raw ': psize = 4 ; break; // case 'BGRA': psize = 4 ; break; // case '2vuy': psize = 2 ; break; // case 'yuvs': psize = 2 ; break; default: PRINT1 ("Cannot understand ctype 0x%x\n", ctype); return 0; } #endif #ifdef GWALLOCMEM // Allocate plain old memory for GWorld, align on 16-byte // boundary. w = r->right - r->left; if (!newbuf) { unalignedbuf = malloc (w * (r->bottom - r->top) * psize + 1024); if (!unalignedbuf) return 0; buf = unalignedbuf + 16; buf = (char *)((unsigned)buf & ~0xf); } else { buf = newbuf; } #else if (!newbuf) return 0; buf = newbuf; #endif rv = NewGWorldFromPtr((GWorldPtr *) &gw, ctype, r, 0, 0, (GWorldFlags) 0, buf, rowbytes); if (rv) { #ifdef GWALLOCMEM if (!newbuf) free(unalignedbuf); #endif return 0; } // NewGWorldFromPtr always seems to create a gworld with // the same format as the screen. Override it. Any possible // side effects? // (**(gw->portPixMap)).pixelFormat = ctype; return gw; } static void video_send_buf(GWorldPtr buf) { PixMapHandle pixmap; Ptr addr; int err; CodecFlags outFlags; #ifdef completion struct ICMCompletionProcRecord r; #endif #ifdef completion r.completionProc = mycallback; r.completionRefCon = 0xe0e0e0e0; #endif pixmap = GetGWorldPixMap(buf); addr = StripAddress(GetPixBaseAddr(pixmap)); #ifdef completion err = DecompressSequenceFrameS(globseq, addr, globimagesize, 0, &outFlags, &r); #else err = DecompressSequenceFrameS(globseq, addr, globimagesize, 0, &outFlags, 0); #endif if (err) { PRINT1("DecompressSequenceFrameS returned error\n", 0); } return; } OSErr MakeImageSequenceForGWorld (GWorldPtr srcGW, GWorldPtr destGW, long *imageSize, ImageSequence *seq) { OSErr err = noErr; ImageDescriptionHandle desc = nil; PixMapHandle srcPixMap = GetGWorldPixMap(srcGW); Rect bounds = (**srcPixMap).bounds; *seq = 0; err = MakeImageDescriptionForPixMap (srcPixMap, &desc); if (err || !desc) goto bail; (**desc).vendor = 'SGI!'; *imageSize = ((**srcPixMap).rowBytes & 0x3fff) * (**desc).height; err = DecompressSequenceBeginS (seq, desc, StripAddress(GetPixBaseAddr(srcPixMap)), *imageSize, destGW, nil, &bounds, nil, ditherCopy, (RgnHandle)nil, 0, codecNormalQuality, anyCodec); if (err) goto bail; bail: if (desc) DisposeHandle ((Handle)desc); if (err) printf ("error in makeimageseq\n"); return err; } int matchmode (QTAtomContainer c, QTAtom fa, long ctype, int w, int h) { QTAtom dmnext, dmcurrent; int *ptr; int size; int rv; QTAtomType atype; QTAtomID aid; int aw = 0, ah = 0, at = 0; // atom width, height, type dmcurrent = 0; dmnext = 0; do { rv = QTNextChildAnyType(c, fa, dmcurrent, &dmnext); if (rv) { PRINT0 ("error getting mode child\n"); break; } if (!dmnext) break; dmcurrent = dmnext; QTGetAtomTypeAndID(c, dmcurrent, &atype, &aid); #if 0 PRINT3 ("matchmode: Atom 0x%03x, type %s, id 0x%x\n", dmcurrent, qtformat(atype), aid); #endif size = 0; ptr = 0; QTGetAtomDataPtr(c, dmcurrent, &size, (char **)&ptr); if (atype == kQTVODimensions) { aw = swapl(ptr[0]); ah = swapl(ptr[1]); } else if (atype == kQTVOResolution) { // don't care } else if (atype == kQTVORefreshRate) { // don't care } else if (atype == kQTVOPixelType) { // don't care - SGVC for out component } else if (atype == kQTVOName) { // don't care } else if (atype == kQTVODecompressors) { QTAtom dec; short idx; if (dec = QTFindChildByID(c, dmcurrent, kQTVODecompressorType, 1, &idx)) { size = 0; ptr = 0; QTGetAtomDataPtr(c, dec, &size, (char **)&ptr); at = swapl(ptr[0]); } } else { // don't care } } while (dmnext); // Now try to match the attributes of the current modelist // entry with the requested width, height, and format. #if 0 PRINT3("matchmode: search %s %d %d\n", qtformat(ctype), w, h); PRINT3("matchmode: found %s %d %d\n", qtformat(at), aw, ah); #endif // pixel format must match -- then try to get the right size if (ctype == at) { // match widths exactly if (w != aw) return 0; // match NTSC height to 480 or 486 if (h == 486 && ah == 486) return 1; if (h == 480 && ah == 486) return 1; if (h == 480 && ah == 480) return 1; // match PAL height exactly to 576 if (h = 576 && ah == 576) return 1; } return 0; } // Return the display mode best fitting the requested ctype // and size // long bestDisplayMode(QTAtomContainer modelist, long ctype, int w, int h) { QTAtom current, next; QTAtomType atype; QTAtomID aid; int rv; current = 0; next = 0; do { rv = QTNextChildAnyType(modelist, kParentAtomIsContainer, current, &next); if (rv) { PRINT0 ("error getting child\n"); break; } if (next) { current = next; QTGetAtomTypeAndID(modelist, current, &atype, &aid); if (atype != kQTVODisplayModeItem) { PRINT0 ("Not a display mode item atom?\n"); continue; } #if 0 PRINT3 ("best: Mode Atom type %s, id 0x%x, address 0x%x\n", qtformat(atype), aid, current); #endif // matchmode returns 1 if found, 0 if not found if (matchmode(modelist, current, ctype, w, h)) return aid; } } while (next); return 0; } ComponentResult startVideoOutput(int width, int height) { QTAtomContainer displayModeList; long displayModeId = 0; ComponentDescription cd; Component c = 0; int result; cd.componentType = QTVideoOutputComponentType; cd.componentSubType = 0; cd.componentManufacturer = 0; cd.componentFlags = 0; cd.componentFlagsMask = kQTVideoOutputDontDisplayToUser; c = FindNextComponent (c, &cd); videoOutput = OpenComponent(c); result = QTVideoOutputGetDisplayModeList(videoOutput, &displayModeList); if (result != noErr) debugPrintf("QTVideoOutputGetDisplayModeList returned error\n"); result = QTVideoOutputGetDisplayMode(videoOutput, &displayModeId); if (result != noErr) debugPrintf("QTVideoOutputGetDisplayMode returned error\n"); // find the display mode that matches the movie type displayModeId = bestDisplayMode(displayModeList, movieCtype, width, height); // XXX crashes when this line is executed // QTDisposeAtomContainer(displayModeList); PRINT1("Setting displayModeId to %d\n", displayModeId); result = QTVideoOutputSetDisplayMode(videoOutput, displayModeId); if (result != noErr) debugPrintf("QTVideoOutputSetDisplayMode returned error\n"); #ifdef VOUTCPL result = QTVideoOutputCustomConfigureDisplay(videoOutput, NULL); if (result != noErr) debugPrintf("QTVideoOutputCustomConfigureDisplay returned error\n"); #endif result = QTVideoOutputBegin(videoOutput); if (result != noErr) debugPrintf("QTVideoOutputBegin returned error %d\n", result); result = QTVideoOutputGetGWorld (videoOutput, &videoOutputGWorld); if (result != noErr) PRINT1("QTVideoOutputGetGWorld returned error %d\n", result); GetGWorld(&origPort, &origDevice); p_img_desc(videoOutputGWorld, "output"); // save window's graphics port SetGWorld(videoOutputGWorld, nil); return result; } ComponentResult stopVideoOutput(void) { int result=noErr; debugPrintf("In stopVideoOutput\n"); result = QTVideoOutputEnd(videoOutput); result = CloseComponent(videoOutput); DisposeGWorld(sourceGWorld); SetGWorld(origPort, origDevice); // restore original graphics port return result; } void p_img_desc(GWorldPtr gw, char *name) { ImageDescriptionHandle desc = nil; PixMapHandle pm = GetGWorldPixMap(gw); Rect bounds = (**pm).bounds; int err; err = MakeImageDescriptionForPixMap (pm, &desc); if (err || !desc) { PRINT0 ("cannot get imagedesc for output gworld\n"); } else { PRINT1 ("%s image descriptor:\n", name); PRINT1 ("\tvendor: 0x%x\n", (**desc).vendor); PRINT1 ("\tctype: '%s'\n", qtformat((**desc).cType)); PRINT2 ("\twidth %d height %d\n", (**desc).width, (**desc).height); PRINT2 ("\tsize %d depth %d\n", (**desc).dataSize, (**desc).depth); } } // // Begin: Stuff for QuickTime // int swapl(int x) { static int be = 0; // big or little endian system? if (be) return x; return (x & 0xff000000) >> 24 | (x & 0x00ff0000) >> 8 | (x & 0x0000ff00) << 8 | (x & 0x000000ff) << 24; } char * qtformat(int format) { static char fbuf0[32]; static char fbuf1[32]; static int count = 0; char *fbuf; if (count & 1) fbuf = fbuf1; else fbuf = fbuf0; count++; if ((unsigned)format < 64) sprintf(fbuf, "%d", format); else { fbuf[0] = (char)(format >> 24); fbuf[1] = (char)(format >> 16); fbuf[2] = (char)(format >> 8); fbuf[3] = (char)(format >> 0); } return fbuf; } // // End: Stuff for QuickTime //