/* * jplayvid file.mv * * Play a movie to video output. * * mv -> dmic (jpeg decode) -> vl */ #include #include #include #include #include #include #include #include #include #include #include /* vl errors: sometimes error is 0, sometimes -1... */ #define EV(var, func, str) if (!(var = func)) { vlPerror(str); exit(1); } #define EVN(func, str) if (func == -1) { vlPerror(str); exit(1); } #define EV1(func, str) if (func) { vlPerror(str); exit(1); } /* libdm errors */ #define PR(e, str) fprintf(stderr, "libdm failed %d %s\n", e, str) #define ED(dmfunc, str) if ((e = dmfunc) != DM_SUCCESS) { PR(e,str); exit(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); } int findrealtimejpeg(void); 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) { #if 0 printf("found %s, vers %d, rev %d\n", dmParamsGetString(p, DM_IC_ENGINE), dmParamsGetInt(p, DM_IC_VERSION), dmParamsGetInt(p, DM_IC_REVISION)); #endif dmParamsDestroy(p); return n; } dmParamsDestroy(p); } fprintf(stderr, "found no real time JPEG decoder\n"); exit(1); /* NOTREACHED */ } int cpix, vpix, lostcount; main(int argc, char **argv) { int wid, ht; DMstatus e; DMparams *p; int icfd; DMbufferpool cpool, vpool; DMimageconverter ic; VLServer svr; VLPath path; VLNode drn, src; VLControlValue val; int vidfd; size_t imagebytes; int poolfd; int npix; int maxfd; char *mvfile = argv[1]; MVid mvid, track; double rate; MVtimescale tscale; MVtime dur; if (argc != 2) { fprintf(stderr, "usage: %s file.mv\n", argv[0]); exit(1); } /* * Open the Movie File and make sure it's okay for this program. */ EMV(mvOpenFile(mvfile, O_RDONLY, &mvid), mvfile); p = mvGetParams(mvid); if (dmParamsGetEnum(p, MV_FILE_FORMAT) != MV_FORMAT_QT) { fprintf(stderr, "%s not Quicktime\n", mvfile); exit(1); } EMV(mvFindTrackByMedium(mvid, DM_IMAGE, &track), "find img trk"); if (strcmp(mvGetImageCompression(track), DM_IMAGE_JPEG)) { fprintf(stderr, "%s not %s\n", mvfile, 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 not interlaced\n", mvfile); exit(1); } npix = mvGetTrackNumDataIndex(track); wid = mvGetImageWidth(track); ht = mvGetImageHeight(track); npix *= 2; /* Movie File thinks in frames */ ht /= 2; /* Movie File thinks in frames */ /* * Create and configure JPEG decode context. */ ED(dmICCreate(findrealtimejpeg(), &ic), "dmICCreate"); dmParamsCreate(&p); dmSetImageDefaults(p, wid, ht, DM_IMAGE_PACKING_CbYCrY); dmParamsSetEnum(p, DM_IMAGE_ORIENTATION, DM_IMAGE_TOP_TO_BOTTOM); dmParamsSetString(p, DM_IMAGE_COMPRESSION, DM_IMAGE_JPEG); ED(dmICSetSrcParams(ic, p), "dmICSetSrcParams"); imagebytes = dmImageFrameSize(p); dmParamsSetString(p, DM_IMAGE_COMPRESSION, DM_IMAGE_UNCOMPRESSED); dmICSetDstParams(ic, p); dmParamsDestroy(p); /* * Open and configure video output. */ EV(svr, vlOpenVideo(""), "open video"); EV(drn, vlGetNode(svr, VL_DRN, VL_VIDEO, VL_ANY), "drn vid"); EV(src, vlGetNode(svr, VL_SRC, VL_MEM, VL_ANY), "src mem"); EV(path, vlCreatePath(svr, VL_ANY, src, drn), "create path"); EVN(vlSetupPaths(svr, (VLPathList)&path, 1, VL_SHARE,VL_SHARE),"Setup"); tscale = mvGetTrackTimeScale(track); rate = mvGetImageRate(track); dur = (long long)((double)tscale/rate); if (rate == 29.97 || rate == 30.) { /* "NTSC" */ switch (wid) { case 640: val.intVal = VL_TIMING_525_SQ_PIX; break; case 720: val.intVal = VL_TIMING_525_CCIR601; break; default: fprintf(stderr, "bad width %d (not 640 or 720)\n", wid); exit(1); break; } } else if (rate == 25.) { /* "PAL" */ switch (wid) { case 768: val.intVal = VL_TIMING_625_SQ_PIX; break; case 720: val.intVal = VL_TIMING_625_CCIR601; break; default: fprintf(stderr, "bad width %d (not 768 or 720)\n", wid); exit(1); break; } } else { fprintf(stderr, "%s: bad rate %f\n", mvfile, rate); exit(1); } EVN(vlSetControl(svr, path, drn, VL_TIMING, &val), "timing src"); val.intVal = VL_PACKING_YVYU_422_8; EVN(vlSetControl(svr, path, src, VL_PACKING, &val), "packing"); val.intVal = VL_CAPTURE_NONINTERLEAVED; EVN(vlSetControl(svr, path, src, VL_CAP_TYPE, &val), "fields"); val.xyVal.x = wid; val.xyVal.y = ht; EVN(vlSetControl(svr, path, src, VL_SIZE, &val), "set size"); /* * Create pool for (disk)->mem->dmic */ dmParamsCreate(&p); #define IBUFS 8 dmBufferSetPoolDefaults(p, IBUFS, imagebytes, DM_FALSE, DM_TRUE); ED(dmICGetSrcPoolParams(ic, p), "dmICGetSrcPoolParams"); ED(dmBufferCreatePool(p, &cpool), "dmBufferCreatePool"); dmParamsDestroy(p); /* * Create pool for dmic->mem->vid */ dmParamsCreate(&p); #define OBUFS 10 dmBufferSetPoolDefaults(p, OBUFS, imagebytes, DM_FALSE, DM_FALSE); ED(dmICGetDstPoolParams(ic, p), "dmICGetDstPoolParams"); EVN(vlDMPoolGetParams(svr, path, src, p), "vl requirements"); ED(dmBufferCreatePool(p, &vpool), "dmBufferCreatePool"); dmParamsDestroy(p); /* * Associate dmic->mem pool with video output */ EVN(vlDMPoolRegister(svr, path, src, vpool), "vlDMPoolRegister"); /* * Associate mem->dmic pool with dmic context */ ED(dmICSetDstPool(ic, vpool), "dmICSetDstPool"); /* * Configure select(2) */ ED(dmBufferSetPoolSelectSize(cpool, imagebytes), "select size"); poolfd = dmBufferGetPoolFD(vpool); icfd = dmICGetDstQueueFD(ic); EVN(vlSelectEvents(svr, path, VLSequenceLostMask), "vlevents"); vidfd = vlGetFD(svr); maxfd = icfd > poolfd ? icfd : poolfd; maxfd = (maxfd > vidfd ? maxfd : vidfd) + 1; /* * Start video */ EVN(vlBeginTransfer(svr, path, 0, 0), "begin transfer"); while (vpix < npix) { fd_set fdswrite, fdsread; FD_ZERO(&fdswrite); if (cpix < npix) { FD_SET(poolfd, &fdswrite); } FD_ZERO(&fdsread); if (vpix < npix) { FD_SET(icfd, &fdsread); } FD_SET(vidfd, &fdsread); if (select(maxfd, &fdsread, &fdswrite, 0, 0) < 0) { perror("select"); exit(1); } /* A decompressed field is ready for video output */ if (FD_ISSET(icfd, &fdsread)) { DMbuffer buf; ED(dmICReceive(ic, &buf), "dmICReceive"); EV1(vlDMBufferSend(svr, path, buf), "vl send"); dmBufferFree(buf); vpix++; } /* Something bad happened on video output. */ if (FD_ISSET(vidfd, &fdsread)) { VLEvent vlevent; EVN(vlEventRecv(svr, path, &vlevent), "vl recv"); switch (vlevent.reason) { case VLSequenceLost: lostcount++; break; default:printf("vlevent %d\n", vlevent.reason); } } /* Since there's space for output we can start a decode */ if (FD_ISSET(poolfd, &fdswrite)) { static DMbuffer buf[2]; int f = cpix % 2; if (dmBufferAllocate(cpool, &buf[f]) != DM_SUCCESS) { continue; } if (f) { /* Now have 2 free buffers. */ size_t n1, n2; off64_t gap; MVtime time = (long long)cpix/2 * dur; int index; MVframe fo; /* time -> index */ EMV(mvGetTrackDataIndexAtTime(track, time, tscale, &index, &fo), "tr index"); /* index -> frame info */ EMV(mvGetTrackDataFieldInfo(track, index, &n1, &gap, &n2), "field info"); /* index -> frame data */ EMV(mvReadTrackDataFields(track, index, n1, dmBufferMapData(buf[0]), n2, dmBufferMapData(buf[1])), "read fields"); dmBufferSetSize(buf[0], n1); ED(dmICSend(ic, buf[0], 0, 0), "send 1"); dmBufferFree(buf[0]); dmBufferSetSize(buf[1], n2); ED(dmICSend(ic, buf[1], 0, 0), "send 2"); dmBufferFree(buf[1]); } ++cpix; } } }