/* * record * * vl -> ic -> mv * * jrecvid ht nfields */ #define PRINT #include /* select() */ #include /* exit() */ #include #include /* select() timeout */ #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); } /* 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); } 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_ENCODE) { #ifdef PRINT 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 */ } main(int argc, char **argv) { int wid, ht; int npix; DMparams *p; DMimageconverter ic; DMbufferpool inpool, outpool; int cpix = 0, icfd, jmiss = 0; double quality = .90; VLServer svr; VLPath path; VLNode drn, src; VLControlValue val; int xferbytes; int vpix = 0, vidfd, vdrop = 0, vmiss = 0; char *mvfile = argv[1]; int maxfd; fd_set fdset; MVid mid, tid; MVtimescale tscale; MVtime dur; double rate; int prrate; if (argc != 3) { fprintf(stderr, "usage: %s file.mv nfields\n", argv[0]); exit(1); } npix = atoi(argv[2]); /* * Open video input. */ EV(svr, vlOpenVideo(""), "open video"); EV(drn, vlGetNode(svr, VL_DRN, VL_MEM, VL_ANY), "get drain"); EV(src, vlGetNode(svr, VL_SRC, VL_VIDEO, VL_ANY), "get src"); EV(path, vlCreatePath(svr, VL_ANY, src, drn), "create path"); EVN(vlSetupPaths(svr, (VLPathList)&path, 1, VL_SHARE,VL_SHARE),"Setup"); val.intVal = VL_PACKING_YVYU_422_8; EVN(vlSetControl(svr, path, drn, VL_PACKING, &val), "set ycrcb422"); val.intVal = VL_CAPTURE_NONINTERLEAVED; EVN(vlSetControl(svr, path, drn, VL_CAP_TYPE, &val), "set fields"); EVN(vlGetControl(svr, path, src, VL_TIMING, &val), "get timing"); switch (val.intVal) { case VL_TIMING_525_SQ_PIX: case VL_TIMING_625_SQ_PIX: #ifdef PRINT printf("square pixel timing\n"); #endif break; case VL_TIMING_525_CCIR601: case VL_TIMING_625_CCIR601: #ifdef PRINT printf("601 (non-square) pixel timing\n"); #endif break; default: printf("unknown timing %d\n", val.intVal); exit(1); } EVN(vlGetControl(svr, path, drn, VL_SIZE, &val), "get size"); wid = val.xyVal.x; /* Expected to be 16x */ ht = val.xyVal.y; ht = ht & ~7; /* Round down to 8x */ xferbytes = vlGetTransferSize(svr, path); EVN(vlGetControl(svr, path, drn, VL_RATE, &val), "get rate"); switch (val.fractVal.numerator/val.fractVal.denominator) { case 60: rate = 29.97; prrate = 60; break; /* "NTSC" */ case 50: rate = 25.; prrate = 50; break; /* "PAL" */ default: printf("unknown rate %d/%d\n", val.fractVal.numerator, val.fractVal.denominator); break; } #ifdef PRINT printf("wid %d, ht %d, xferbytes %d, rate %f\n", wid, ht, xferbytes, rate); #endif /* * Create JPEG compression image converter 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_UNCOMPRESSED); ED(dmICSetSrcParams(ic, p), "dmICSetSrcParams"); dmParamsSetString(p, DM_IMAGE_COMPRESSION, DM_IMAGE_JPEG); ED(dmICSetDstParams(ic, p), "dmICSetDstParams"); dmParamsDestroy(p); /* * Converstion params (q-factor) */ dmParamsCreate(&p); dmParamsSetFloat(p, DM_IMAGE_QUALITY_SPATIAL, quality); ED(dmICSetConvParams(ic, p), "dmICSetConvParams"); dmParamsDestroy(p); /* * Create the movie file. */ ED(dmParamsCreate(&p), "movie params"); EMV(mvSetMovieDefaults(p, MV_FORMAT_QT), "movie defaults"); EMV(mvCreateFile(mvfile, p, NULL, &mid), "mv create"); dmParamsDestroy(p); /* * Create an image track in the movie. */ ED(dmParamsCreate(&p), "image params"); ED(dmSetImageDefaults(p, wid, ht*2, DM_PACKING_RGBX), "img def"); ED(dmParamsSetString(p, DM_IMAGE_COMPRESSION, DM_IMAGE_JPEG), "jpeg"); ED(dmParamsSetFloat(p, DM_IMAGE_QUALITY_SPATIAL, quality), "quality"); ED(dmParamsSetFloat(p, DM_IMAGE_RATE, rate), "rate"); ED(dmParamsSetEnum(p, DM_IMAGE_ORIENTATION, DM_TOP_TO_BOTTOM), "ttob"); ED(dmParamsSetEnum(p, DM_IMAGE_INTERLACING, rate == 25. ? DM_IMAGE_INTERLACED_EVEN : DM_IMAGE_INTERLACED_ODD), "ilace"); EMV(mvAddTrack(mid, DM_IMAGE, p, NULL, &tid), "add track"); dmParamsDestroy(p); tscale = mvGetTrackTimeScale(tid); dur = (long long)((double)tscale/rate); #ifdef PRINT printf("tscale %d, dur %lld\n", tscale, dur); #endif #define NOTMAPPED DM_FALSE #define NOTCACHED DM_FALSE #define MAPPED DM_TRUE #define CACHED DM_TRUE /* * Create pool for video -> compression. */ #define INBUFS 10 ED(dmParamsCreate(&p), "input params"); ED(dmBufferSetPoolDefaults(p, INBUFS, xferbytes, NOTCACHED, NOTMAPPED), "i defaults"); ED(dmICGetSrcPoolParams(ic, p), "dmICGetSrcPoolParams"); EVN(vlDMPoolGetParams(svr, path, drn, p), "vl requirements"); ED(dmBufferCreatePool(p, &inpool), "create inpool"); dmParamsDestroy(p); /* Connect this pool to video input. */ EVN(vlDMPoolRegister(svr, path, drn, inpool), "vlDMPoolRegister"); /* * Create pool for compression -> disk */ #define OUTBUFS 10 ED(dmParamsCreate(&p), "output params"); ED(dmBufferSetPoolDefaults(p, OUTBUFS, xferbytes, NOTCACHED, MAPPED), "o defaults"); ED(dmICGetDstPoolParams(ic, p), "dmICGetDstPoolParams"); ED(dmBufferCreatePool(p, &outpool), "create outpool"); dmParamsDestroy(p); /* Connect this pool to compression output. */ ED(dmICSetDstPool(ic, outpool), "dmICSetDstPool"); /* * Start video */ #define MASK (VLTransferCompleteMask|VLTransferFailedMask|VLSequenceLostMask) EVN(vlSelectEvents(svr, path, MASK),"vlSelectEvents"); EVN(vlBeginTransfer(svr, path, 0, 0), "begin transfer"); /* * Get file descriptors */ EVN(vlPathGetFD(svr, path, &vidfd), "get fd"); icfd = dmICGetDstQueueFD(ic); maxfd = (icfd > vidfd ? icfd : vidfd) + 1; /* * Show time! */ while (cpix < npix) { struct timeval to; int n; to.tv_sec = 1; to.tv_usec = 0; FD_ZERO(&fdset); FD_SET(icfd, &fdset); if (vpix < npix) { FD_SET(vidfd, &fdset); } n = select(maxfd, &fdset, 0, 0, &to); if (n == 0) { printf("select timed out after %d secs\n", to.tv_sec); mvClose(mid); exit(1); } if (n == -1) { perror("select"); exit(1); } if (FD_ISSET(icfd, &fdset)) { static DMbuffer buf[2]; static size_t onesec; static USTMSCpair prev, base; USTMSCpair this; int f = cpix % 2; ED(dmICReceive(ic, &buf[f]), "dmICReceive"); this = dmBufferGetUSTMSCpair(buf[f]); if (prev.msc == 0) { base = this; } else if (prev.msc + 1 != this.msc) { #ifdef PRINT printf("jpeg missed %lld\n", prev.msc+1-base.msc); #endif jmiss++; } prev = this; if (f) { /* Now have both fields */ MVtime mvtime = (long long)(cpix/2) * dur; size_t s1, s2; EMV(mvInsertTrackDataFields(tid, mvtime, dur, tscale, s1 = dmBufferGetSize(buf[0]), dmBufferMapData(buf[0]), s2 = dmBufferGetSize(buf[1]), dmBufferMapData(buf[1]), MV_FRAMETYPE_KEY, 0), "insert"); dmBufferFree(buf[0]); dmBufferFree(buf[1]); onesec += s1 + s2; } if (cpix % prrate == 0) { #ifdef PRINT printf("c %d bitrate %d\n", cpix, (onesec*8)/1000000); #endif onesec = 0; } cpix++; } if (FD_ISSET(vidfd, &fdset)) { VLEvent vlevent; DMbuffer buf; static USTMSCpair prev, base; if (vlEventRecv(svr, path, &vlevent) == DM_SUCCESS) { USTMSCpair this; if (vlevent.reason == VLSequenceLost) { vdrop++; #ifdef PRINT printf("vdrop %d\n", vdrop); #endif continue; } if (vlevent.reason != VLTransferComplete) { fprintf(stderr, "vlevent %d\n", vlevent.reason); exit(1); } EVN(vlEventToDMBuffer(&vlevent, &buf), "vldmbuffer"); ED(dmICSend(ic, buf, 0, 0), "dmicsend"); this = dmBufferGetUSTMSCpair(buf); if (prev.msc == 0) { base = this; } else if (prev.msc + 1 != this.msc) { #ifdef PRINT printf("vid missed %lld\n", prev.msc+1-base.msc); #endif vmiss++; } prev = this; dmBufferFree(buf); #ifdef PRINT if (vpix % prrate == 0) printf("v %d\n", vpix); #endif vpix++; } } } vlEndTransfer(svr, path); mvClose(mid); printf("missed vid %d, jpeg %d\n", vmiss, jmiss); }