/* * Usage: * * $0 -q < vidfile * Query: print info to stdout about the video stream in * vidfile. * * $0 -x fno1 file1 fno2 file2 ... < vidfile * Extract: write stills from vidfile. The fnoN are frame * numbers; the fileN are the file names to which the * corresponding frames are to be written. As each one is * written, its number and filename are printed to stdout; * if timestamps are present in vidfile, frame timestamps * are printed as well. * * Frame numbers are 1-origin. */ #include #include #include #include #include #include "vidfile.h" #include "ycbcr-rgb-cvt.h" extern const char *__progname; typedef enum { OP_NONE = 1, OP_QUERY, OP_EXTRACT } OP; typedef struct xframe XFRAME; struct xframe { XFRAME *link; int fno; const char *file; } ; static OP op = OP_NONE; static int verbose = 0; static const char **args = 0; static int aargs = 0; static int nargs = 0; static int have_timestamps; static VFR_HANDLE *vfh; static VF_VIDFMT framefmt; static int framesize; static int framex; static int framey; static unsigned char *framebuf; static unsigned long long int ts; static int newframe; static void (*newframe_fail)(void); static void savearg(const char *s) { if (nargs >= aargs) args = realloc(args,(aargs=nargs+8)*sizeof(*args)); args[nargs++] = s; } static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { savearg(*av); continue; } #if 0 if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) #undef WANTARG #endif if (!strcmp(*av,"-q")) { op = OP_QUERY; continue; } if (!strcmp(*av,"-x")) { op = OP_EXTRACT; continue; } if (!strcmp(*av,"-v")) { verbose = 1; continue; } fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } static void vidfile_err(void *cookie __attribute__((__unused__)), const char *msg) { if (newframe) (*newframe_fail)(); fprintf(stderr,"%s: error in input: %s\n",__progname,msg); exit(1); } static int vidfile_read(void *cookie __attribute__((__unused__)), void *buf, int len) { int n; n = fread(buf,1,len,stdin); if (n > 0) newframe = 0; return(n); } static void read_header(void) { int w; int h; VF_VIDFMT fmt; void feature(void *cookie __attribute__((__unused__)), unsigned int bit, ...) { switch (bit) { case VFF_TIMESTAMPS: have_timestamps = 1; break; default: fprintf(stderr,"%s: don't understand feature bit %#x\n",__progname,bit); exit(1); break; } } have_timestamps = 0; vfh = open_vidfile_read(&vidfile_err,0,&vidfile_read,0,&w,&h,&fmt,&feature,0); switch (fmt) { case VF_FMT_RGB888_RGB: framesize = w * h * 3; break; case VF_FMT_YCbCr422_YCbYCr_0_255: framesize = w * h * 2; break; default: fprintf(stderr,"%s: don't understand format %d\n",__progname,(int)fmt); exit(1); break; } framefmt = fmt; framex = w; framey = h; } static void setup_frame(void) { void *mmrv; mmrv = mmap(0,framesize,PROT_READ|PROT_WRITE,MAP_ANON|MAP_SHARED,-1,0); if (mmrv == MAP_FAILED) { fprintf(stderr,"%s: buffer mmap (%u): %s\n",__progname,(unsigned int)(framesize*2),strerror(errno)); exit(1); } framebuf = mmrv; bzero(framebuf,framesize); } static void read_packet(void) { newframe = 1; if (have_timestamps) { read_vidfile_frame(vfh,&framebuf[0],&ts); } else { read_vidfile_frame(vfh,&framebuf[0]); } } static void query_file(void) { __label__ failed; int nframes; __typeof__(ts) firstts; void fail(void) { goto failed; } if (nargs > 0) printf("Note: -q ignores frame numbers\n"); printf("%d x %d, ",framex,framey); switch (framefmt) { case VF_FMT_RGB888_RGB: printf("RGB888_RGB"); break; case VF_FMT_YCbCr422_YCbYCr_0_255: printf("YCbCr422-YCbYCr-0-255"); break; default: abort(); break; } if (have_timestamps) printf(", timestamps present"); printf("\n"); fflush(stdout); newframe_fail = &fail; nframes = 0; while (1) { read_packet(); if (have_timestamps && (nframes == 0)) firstts = ts; nframes ++; } failed:; printf("frame count: %d\n",nframes); if (have_timestamps) { printf("timestamp range: %llu.%06lu to %llu.%06lu\n", firstts / 1000000U, (unsigned long int) (firstts % 1000000U), ts / 1000000U, (unsigned long int) (ts % 1000000U) ); } } static XFRAME *sort_frames(XFRAME *list) { XFRAME *a; XFRAME *b; XFRAME *t; XFRAME **lp; if (!list || !list->link) return(list); a = 0; b = 0; while (list) { t = list; list = t->link; t->link = a; a = b; b = t; } a = sort_frames(a); b = sort_frames(b); lp = &list; while (a || b) { if (!b || (a && (a->fno < b->fno))) { t = a; a = a->link; } else { t = b; b = b->link; } *lp = t; lp = &t->link; } *lp = 0; return(list); } static void write_frame(const char *fn) { FILE *f; int i; unsigned char *bp; CVTPARAMS p; RGB rgb1; RGB rgb2; f = fopen(fn,"w"); if (! f) { fprintf(stderr,"%s: open %s: %s\n",__progname,fn,strerror(errno)); return; } fprintf(f,"P6\n%d %d\n255\n",framex,framey); switch (framefmt) { case VF_FMT_RGB888_RGB: fwrite(framebuf,1,framex*framey*3,f); break; case VF_FMT_YCbCr422_YCbYCr_0_255: p.Kr = .299; p.Kb = .114; p.minY = 0; p.maxY = 255; p.maxCC = 127; reset_cvt(&p); bp = framebuf; for (i=framex*framey;i>=0;i-=2) { rgb1 = cvt_YCbCr_to_RGB(bp[0],bp[1],bp[3]); rgb2 = cvt_YCbCr_to_RGB(bp[2],bp[1],bp[3]); if ( (putc(rgb1.r,f) == EOF) || (putc(rgb1.g,f) == EOF) || (putc(rgb1.b,f) == EOF) || (putc(rgb2.r,f) == EOF) || (putc(rgb2.g,f) == EOF) || (putc(rgb2.b,f) == EOF) ) { fprintf(stderr,"%s: writing %s: %s\n",__progname,fn,strerror(errno)); fclose(f); return; } bp += 4; } break; default: abort(); break; } if (fclose(f) == EOF) { fprintf(stderr,"%s: closing %s: %s\n",__progname,fn,strerror(errno)); } } static void extract_frames(void) { __label__ failed; XFRAME *frames; int nframe; int i; long int fnl; int fni; char *ep; int errs; XFRAME *xf; int nextframe; void fail(void) { goto failed; } if (nargs % 2) { fprintf(stderr,"%s: -x takes an even number of arguments\n",__progname); exit(1); } nframe = nargs / 2; frames = 0; errs = 0; for (i=nframe-1;i>=0;i--) { fnl = strtol(args[i+i],&ep,0); if (*ep || (ep == args[i+i])) { fprintf(stderr,"%s: %s: bad frame number\n",__progname,args[i+i]); errs = 1; } fni = fnl; if ((fni != fnl) || (fni <= 0)) { fprintf(stderr,"%s: %s: frame number out of range\n",__progname,args[i+i]); errs = 1; } xf = malloc(sizeof(XFRAME)); xf->fno = fni; xf->file = args[i+i+1]; xf->link = frames; frames = xf; } if (errs) exit(1); frames = sort_frames(frames); newframe_fail = &fail; nextframe = 1; xf = frames; while (xf) { read_packet(); while (xf && (nextframe == xf->fno)) { printf("%d",nextframe); if (have_timestamps) printf(" %llu.%06u",ts/1000000U,(unsigned int)(ts%1000000U)); printf(" %s\n",xf->file); write_frame(xf->file); xf = xf->link; } nextframe ++; } return; failed:; printf("Frames not found:"); for (;xf;xf=xf->link) printf(" %d",xf->fno); printf("\n"); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); newframe = 0; read_header(); setup_frame(); switch (op) { case OP_QUERY: query_file(); break; case OP_EXTRACT: extract_frames(); break; default: fprintf(stderr,"%s: need an operation (-q or -x)\n",__progname); exit(1); break; } return(0); }