/* * jplaygfx - play compressed movie to graphics screen * * jplaygfx file.mv */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* libdm errors */ DMstatus e; const char *e_short; int e_num; char e_long[DM_MAX_ERROR_DETAIL]; #define GE() e_short = dmGetError(&e_num, e_long); #define PR(str) fprintf(stderr, "libdm failed %s %s %d %s\n", str, e_short, e_num, e_long); #define EX(v) exit(v) #define ED(dmfunc, str) if ((e = dmfunc) != DM_SUCCESS) { GE(); PR(str); EX(1); } /* moviefile errors */ #define MVPR(s) fprintf(stderr, "%s: %s\n", s, mvGetErrorStr(mvGetErrno())) #define EMV(func, s) if (func == DM_FAILURE) { MVPR(s); exit(1); } /* xlib errors */ #define PRSTR(str) fprintf(stderr, "%s\n", str); #define X0(func, str) if (func != 0) { PRSTR(str); exit(1); } #define X(ret, func, str) if (!(ret = func)) { PRSTR(str); exit(1); } static int visual_attrs[] = { GLX_RGBA, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, /*GLX_DOUBLEBUFFER,*/ /*GLX_DEPTH_SIZE, 1,*/ None, }; static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg) { d = d; if ((e->type == MapNotify) && (e->xmap.window == (Window)arg)) { return GL_TRUE; } return GL_FALSE; } int findrealtimejpeg(void); char *a_prog; int findrealtimejpeg(void) { int n; DMparams *p; if ((n = dmICGetNum()) <= 0 ) { fprintf(stderr, "no converters? dmICGetNum %d\n", n); exit(1); } while (n--) { dmParamsCreate(&p); if (dmICGetDescription(n, p) == DM_SUCCESS && dmParamsGetInt(p, DM_IC_ID) == 'jpeg' && dmParamsGetEnum(p, DM_IC_SPEED) == DM_IC_SPEED_REALTIME && dmParamsGetEnum(p, DM_IC_CODE_DIRECTION) == DM_IC_CODE_DIRECTION_DECODE) { printf("%s: found %s, vers %d, rev %d\n", a_prog, dmParamsGetString(p, DM_IC_ENGINE), dmParamsGetInt(p, DM_IC_VERSION), dmParamsGetInt(p, DM_IC_REVISION)); dmParamsDestroy(p); return n; } dmParamsDestroy(p); } fprintf(stderr, "%s: found no real time JPEG decoder\n", a_prog); exit(1); /* NOTREACHED */ } void (*mv_read)(int p); DMbufferpool inpool; MVtime dur; MVtimescale tscale; MVid track; int outpix = 0; DMimageconverter ic; void mv_read_qt_fields(int picno) { static DMbuffer buf[2]; int f = picno % 2; ED(dmBufferAllocate(inpool, &buf[f]), "bufalloc"); if (f) { /* Now have a buffer for each field */ int index; MVframe fo; MVtime time = (long long)picno/2 * dur; size_t n1, n2; off64_t g; EMV(mvGetTrackDataIndexAtTime(track, time, tscale, &index, &fo), "get index"); EMV(mvGetTrackDataFieldInfo(track, index, &n1, &g, &n2), "field info"); EMV(mvReadTrackDataFields(track, index, n1, dmBufferMapData(buf[0]), n2, dmBufferMapData(buf[1])), "read"); ED(dmBufferSetSize(buf[0], n1), "set f1"); if (dmICSend(ic, buf[0], 0, 0) == DM_FAILURE) { GE(); printf("dmICSend %s %d %s i %d o %d\n", e_short, e_num, e_long, picno, outpix); /* That's one we're not gettin' */ ++outpix; } dmBufferFree(buf[0]); ED(dmBufferSetSize(buf[1], n2), "set f2"); if (dmICSend(ic, buf[1], 0, 0) == DM_FAILURE) { GE(); printf("dmICSend %s %d %s i %d o %d\n", e_short, e_num, e_long, picno, outpix); /* That's one we're not gettin' */ ++outpix; } dmBufferFree(buf[1]); } } static uchar_t * j_copy(unsigned char *cp, unsigned char *to, unsigned char marker) { for (;;cp++) { *to++ = *cp; if (*cp == 0xff) { if (*(cp + 1) == marker) { *to = marker; return cp + 2; } } } } void mv_read_cosmo_fields(int picno) { static unsigned char xbuf[1000000]; /* XXX */ static DMbuffer buf[2]; int f = picno % 2; ED(dmBufferAllocate(inpool, &buf[f]), "bufalloc"); if (f) { /* Now have a buffer for each field */ MVtime mvtime = (long long)picno/2 * dur; int index; MVframe fr; size_t nbytes, nb1; MVdatatype dt; unsigned char *cp; int paramsid; EMV(mvGetTrackDataIndexAtTime(track, mvtime, tscale, &index, &fr), "index"); EMV(mvGetTrackDataInfo(track, index, &nbytes, ¶msid, &dt, &fr), "info"); EMV(mvReadTrackData(track, index, nbytes, xbuf), "read"); cp = j_copy(xbuf, dmBufferMapData(buf[0]), 0xd9); nb1 = cp - xbuf; dmBufferSetSize(buf[0], nb1); bcopy(cp, dmBufferMapData(buf[1]), nbytes - nb1); dmBufferSetSize(buf[1], nbytes - nb1); if (dmICSend(ic, buf[0], 0, 0) == DM_FAILURE) { GE(); printf("dmICSend 1 %s %d %s i %d o %d\n", e_short, e_num, e_long, picno, outpix); /* That's one we're not gettin' */ ++outpix; } dmBufferFree(buf[0]); if (dmICSend(ic, buf[1], 0, 0) == DM_FAILURE) { GE(); printf("dmICSend 2 %s %d %s i %d o %d\n", e_short, e_num, e_long, picno, outpix); /* That's one we're not gettin' */ ++outpix; } dmBufferFree(buf[1]); } } main(int argc, char **argv) { DMparams *p; DMbufferpool outpool; DMbuffer buf; size_t imagebytes; int poolfd, dmicfd; fd_set fdr, fdw; int inpix = 0; int wid, ht, npix; char *mvfile; MVid movie; const char *comp; double rate; Display *dpy; XVisualInfo *vi; Window win; GLXContext ctx; XSetWindowAttributes swa; int can; XEvent event; time_t secs; a_prog = argv[0]; if (argc != 2) { fprintf(stderr, "usage: %s file.mv\n", a_prog); exit(1); } mvfile = argv[1]; /* * Make sure the movie is: * MV_FILE_FORMAT == MV_FORMAT_QT * DM_IMAGE_COMPRESSION == DM_IMAGE_JPEG * DM_IMAGE_INTERLACING == DM_IMAGE_INTERLACED_EVEN,ODD */ EMV(mvOpenFile(mvfile, O_RDONLY, &movie), mvfile); p = mvGetParams(movie); switch (dmParamsGetEnum(p, MV_FILE_FORMAT)) { case MV_FORMAT_QT: mv_read = mv_read_qt_fields; break; case MV_FORMAT_SGI_3: mv_read = mv_read_cosmo_fields; break; default: fprintf(stderr, "%s: %s format unknown\n", a_prog, mvfile); exit(1); } EMV(mvFindTrackByMedium(movie, DM_IMAGE, &track), "mvfind"); comp = mvGetImageCompression(track); if (strcmp(comp, DM_IMAGE_JPEG)) { fprintf(stderr,"%s: %s is %s, this program only knows %s.\n", a_prog, mvfile, comp, DM_IMAGE_JPEG); exit(1); } p = mvGetParams(track); switch (dmParamsGetEnum(p, DM_IMAGE_INTERLACING)) { case DM_IMAGE_INTERLACED_EVEN: case DM_IMAGE_INTERLACED_ODD: break; default: fprintf(stderr, "%s: %s is not interlaced\n", a_prog, mvfile); exit(1); } if (dmParamsGetEnum(p, DM_IMAGE_ORIENTATION) != DM_TOP_TO_BOTTOM) { fprintf(stderr, "%s: %s is not ttob\n", a_prog, mvfile); exit(1); } npix = mvGetTrackLength(track); wid = mvGetImageWidth(track); ht = mvGetImageHeight(track); /* XXX be cool to get max Compressed Image Size right about here */ tscale = mvGetTrackTimeScale(track); rate = mvGetImageRate(track); dur = (long long)((double)tscale/rate); printf("rate %g, tscale %d, dur %lld\n", rate, tscale, dur); /* mvlib works in frames, this code in fields */ npix *= 2; ht /= 2; printf("%s: wid %d, ht %d, npix %d\n", a_prog, wid, ht, npix); ED(dmICCreate(findrealtimejpeg(), &ic), "dmICCreate"); /* * Image params. "src" is jpeg, "dst" is 422 YCrCb */ dmParamsCreate(&p); dmSetImageDefaults(p, wid, ht, DM_IMAGE_PACKING_CbYCrY); dmParamsSetString(p, DM_IMAGE_COMPRESSION, DM_IMAGE_JPEG); dmParamsSetEnum(p,DM_IMAGE_ORIENTATION,DM_IMAGE_TOP_TO_BOTTOM); ED(dmICSetSrcParams(ic, p), "dmICSetSrcParams"); imagebytes = dmImageFrameSize(p); dmParamsSetString(p, DM_IMAGE_COMPRESSION, DM_IMAGE_UNCOMPRESSED); dmICSetDstParams(ic, p); dmParamsDestroy(p); /* * A pool for input */ dmParamsCreate(&p); #define NOTCACHED DM_FALSE #define NOTMAPPED DM_FALSE #define IBUFS 3 dmBufferSetPoolDefaults(p, IBUFS, imagebytes, NOTCACHED, NOTMAPPED); ED(dmICGetSrcPoolParams(ic, p), "dmICGetSrcPoolParams"); ED(dmBufferCreatePool(p, &inpool), "dmBufferCreatePool inpool"); dmParamsDestroy(p); /* * A pool for output */ dmParamsCreate(&p); #define OBUFS 3 dmBufferSetPoolDefaults(p, OBUFS, imagebytes, NOTCACHED, DM_TRUE); ED(dmICGetDstPoolParams(ic, p), "dmICGetDstPoolParams"); ED(dmBufferCreatePool(p, &outpool), "dmBufferCreatePool outpool"); dmParamsDestroy(p); ED(dmICSetDstPool(ic, outpool), "dmICSetDstPool"); /* * X, Opengl init */ X(dpy, XOpenDisplay(0), "XOpenDisplay"); X(vi, glXChooseVisual(dpy, DefaultScreen(dpy), visual_attrs),"chooz"); X0(glXGetConfig(dpy, vi, GLX_USE_GL, &can), "cannot ogl"); X0(glXGetConfig(dpy, vi, GLX_RGBA, &can), "cannot rgba"); X0(glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &can), "cannot z"); swa.border_pixel = 0; swa.colormap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone); swa.event_mask = ExposureMask | StructureNotifyMask; win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0, wid, ht*2, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); XMapWindow(dpy, win); XIfEvent(dpy, &event, WaitForMapNotify, (char *)win); ctx = glXCreateContext(dpy, vi, 0, GL_TRUE); glXMakeCurrent(dpy, win, ctx); glViewport(0, 0, wid, ht*2); glOrtho(0., (GLdouble)wid, 0., (GLdouble)(ht*2), -1., 1.); glPixelZoom(1.0, -2.0); /* video is ttob, gfx is btot */ /* * Configure select(2) */ ED(dmBufferSetPoolSelectSize(inpool, imagebytes), "select size"); poolfd = dmBufferGetPoolFD(inpool); dmicfd = dmICGetDstQueueFD(ic); secs = time(0); while (outpix < npix) { int n, max; FD_ZERO(&fdw); if (inpix < npix) { FD_SET(poolfd, &fdw); } FD_ZERO(&fdr); if (outpix < npix) { FD_SET(dmicfd, &fdr); } max = (poolfd > dmicfd ? poolfd : dmicfd) + 1; n = select(max, &fdr, &fdw, 0, 0); if (n < 0) { perror("select"); exit(1); } /* * If both input (poolfd) and output (dmicfd) are available * it's better to pop off the output first... keeps things * flowing better as new input also requires an output * allocation. So, this first frees up what might * very well be the last output. */ if (FD_ISSET(dmicfd, &fdr)) { char *cp; ED(dmICReceive(ic, &buf), "dmICReceive"); cp = dmBufferMapData(buf); glRasterPos2i(0, ht*2 + outpix % 2); /* use interlace extension */ /* XXX check for this extension */ glDrawPixels(wid, ht*2, GL_YCRCB_422_SGIX, GL_UNSIGNED_BYTE, cp); dmBufferFree(buf); outpix++; } if (FD_ISSET(poolfd, &fdw)) { (*mv_read)(inpix); inpix++; } } secs = time(0) - secs; printf("%d seconds, %d pix, %d pix/sec\n", secs, npix, npix/(int)secs); dmBufferDestroyPool(inpool); dmBufferDestroyPool(outpool); dmICDestroy(ic); exit(0); }