/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mp4.h" #define AUDIODEV "/dev/sound" /* * AUDIO_DELAY is in seconds. It needs to be low enough that it * doesn't exceed the kernel's willingness to buffer audio data; it * needs to be high enough that no video frame decode takes long * enough for the data queued in the kernel to completley drain. */ #define AUDIO_DELAY .5 extern const char *__progname; typedef struct aq AQ; typedef struct vq VQ; typedef struct aqe AQE; typedef struct vqe VQE; typedef struct playstate PLAYSTATE; struct aq { AQE *head; AQE *tail; } ; struct vq { VQE *head; VQE *tail; } ; struct aqe { AQE *flink; AQE *blink; int len; unsigned char *data; double when; } ; struct vqe { VQE *flink; VQE *blink; int dw; int dh; int ow; int oh; unsigned char (*data)[4]; double when; } ; struct playstate { int ax; int vx; const char *curname; MP4 *curfile; MP4TRK *a; MP4TRK *v; AQ aq; VQ vq; int started; int paused; int zf; // zoom multiplier, -1 for full screen struct timeval start; struct timeval pausetime; double timebase; double vidrate; double maxt; double dB; double vol; unsigned char *cmdbuf; int cmdba; int cmdbl; } ; static int argc; static char **argv; static int nifiles = 0; static char **ifiles = 0; static char *displayname = 0; static char *bordercstr = 0; static char *borderwstr = 0; static char *visualstr = 0; static char *xname = 0; static char *iconname = 0; static char *resname = 0; static char *resclass = 0; static char *xrmstr = 0; static int synch = 0; static char *gammastr = 0; static Display *disp; static Screen *scr; static int scrno; static int width; static int height; static Window rootwin; static Colormap defcmap; static GC gc; static XVisualInfo visinfo; static int (*preverr)(Display *, XErrorEvent *); static int (*prevIOerr)(Display *); static int audiofd; static int prev_rate; static int prev_chan; static MP4_AFMT prev_fmt; static int sample_bytes; static int audio_Bps; static int audio_wthresh; static Window topwin; static Window dispwin; static Colormap wincmap; static int prev_dw; static int prev_dh; static int prev_ow; static int prev_oh; static XImage *img; static XrmDatabase db; #define Cisspace(x) isspace((unsigned char)(x)) static void *deconst_(int dummy, ...) { va_list ap; void *rv; va_start(ap,dummy); rv = va_arg(ap,void *); va_end(ap); return(rv); } static void *deconst(const volatile void *s) { return(deconst_(0,s)); } static char *strdup0(const char *s) { return(s?strdup(s):0); } static void setstr(char **vp, const char *s) { free(*vp); *vp = strdup0(s); } static void strappend(char **strp, const char *str) { char *new; int oldl; int sl; if (! *strp) { *strp = malloc(1); **strp = '\0'; } oldl = strlen(*strp); sl = strlen(str); new = malloc(oldl+sl+1); bcopy(*strp,new,oldl); bcopy(str,new+oldl,sl); new[oldl+sl] = '\0'; free(*strp); *strp = new; } static void saveargv(int ac, char **av) { int i; int nc; char *abuf; argc = ac; argv = malloc((ac+1)*sizeof(char *)); nc = 1; for (i=0;i 0) { skip --; continue; } if (**av != '-') { ifiles = realloc(ifiles,(nifiles+1)*sizeof(*ifiles)); ifiles[nifiles] = *av; nifiles ++; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-display")) { WANTARG(); displayname = av[skip]; continue; } if (!strcmp(*av,"-bordercolor") || !strcmp(*av,"-bd")) { WANTARG(); setstr(&bordercstr,av[skip]); continue; } if (!strcmp(*av,"-borderwidth") || !strcmp(*av,"-bw")) { WANTARG(); setstr(&borderwstr,av[skip]); continue; } if (!strcmp(*av,"-visual")) { WANTARG(); visualstr = av[skip]; continue; } if (!strcmp(*av,"-name")) { WANTARG(); setstr(&xname,av[skip]); continue; } if (!strcmp(*av,"-iconname")) { WANTARG(); setstr(&iconname,av[skip]); continue; } if (!strcmp(*av,"-resname")) { WANTARG(); resname = av[skip]; continue; } if (!strcmp(*av,"-resclass")) { WANTARG(); resclass = av[skip]; continue; } if (!strcmp(*av,"-xrm")) { WANTARG(); strappend(&xrmstr,"\n"); strappend(&xrmstr,av[skip]); continue; } if (!strcmp(*av,"-sync")) { synch = 1; continue; } if (!strcmp(*av,"-gamma")) { WANTARG(); setstr(&gammastr,av[skip]); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); } static Display *open_display(const char *disp) { Display *rv; rv = XOpenDisplay(disp); if (rv == 0) { fprintf(stderr,"%s: can't open display %s\n",__progname,XDisplayName(disp)); exit(1); } return(rv); } static int err(Display *d, XErrorEvent *ee) { return((*preverr)(d,ee)); } static int ioerr(Display *d) { return((*prevIOerr)(d)); } static void setup_visual(void) { XVisualInfo *xvi; int nvi; XVisualInfo template; if (visualstr) { unsigned long int id; char *cp; id = strtol(visualstr,&cp,0); if (*cp) { fprintf(stderr,"%s: %s: invalid visual option\n",__progname,visualstr); exit(1); } template.visualid = (VisualID) id; xvi = XGetVisualInfo(disp,VisualIDMask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: no such visual found\n",__progname); exit(1); } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo returned non-nil but zero count?\n",__progname); exit(1); } visinfo = xvi[0]; if (visinfo.class != TrueColor) { fprintf(stderr,"%s: visual %s isn't TrueColor\n",__progname,visualstr); exit(1); } if (visinfo.screen != XDefaultScreen(disp)) { fprintf(stderr,"%s: warning: visual %s is on screen %d\n",__progname,visualstr,(int)visinfo.screen); } } else { int best_onscr; int best; int i; template.class = TrueColor; xvi = XGetVisualInfo(disp,VisualClassMask,&template,&nvi); best = -1; best_onscr = -1; for (i=0;i xvi[best_onscr].depth) || ( (xvi[i].depth == xvi[best_onscr].depth) && (xvi[i].visual == XDefaultVisual(disp,xvi[i].screen)) ) ) { best_onscr = i; } } if ( (best < 0) || (xvi[i].depth > xvi[best].depth) || ( (xvi[i].depth == xvi[best].depth) && (xvi[i].visual == XDefaultVisual(disp,xvi[i].screen)) ) ) { best = i; } } if (best < 0) { fprintf(stderr,"%s: no TrueColor visual available\n",__progname); exit(1); } if (best_onscr < 0) { fprintf(stderr,"%s: no TrueColor visual on screen %d, using screen %d\n",__progname,XDefaultScreen(disp),xvi[best].screen); } else { best = best_onscr; } visinfo = xvi[best]; } } static void setup_db(void) { char *str; XrmDatabase db2; db = XrmGetStringDatabase(""); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 5) str = XScreenResourceString(scr); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #endif if (xrmstr) { db2 = XrmGetStringDatabase(xrmstr); XrmMergeDatabases(db2,&db); } } static void setup_X(void) { XrmInitialize(); disp = open_display(displayname); if (synch) XSynchronize(disp,True); preverr = XSetErrorHandler(err); prevIOerr = XSetIOErrorHandler(ioerr); setup_visual(); scr = XScreenOfDisplay(disp,visinfo.screen); scrno = visinfo.screen; width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); rootwin = XRootWindowOfScreen(scr); defcmap = XDefaultColormapOfScreen(scr); setup_db(); wincmap = XCreateColormap(disp,rootwin,visinfo.visual,AllocNone); } static void setup_sound(void) { audiofd = open(AUDIODEV,O_WRONLY,0); if (audiofd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,AUDIODEV,strerror(errno)); exit(1); } } static void create_X_windows(int imgw, int imgh) { int x; int y; int w; int h; int bits; unsigned long int attrmask; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XWMHints *wm_hints; XSizeHints *normal_hints; XClassHint *class_hints; wm_hints = XAllocWMHints(); normal_hints = XAllocSizeHints(); class_hints = XAllocClassHint(); w = imgw; h = imgh; x = 0; // (width - imgw) / 2; y = 0; // (height - imgh) / 2; wm_hints->flags = InputHint; wm_hints->input = False; normal_hints->min_width = 1; normal_hints->min_height = 1; normal_hints->width_inc = 1; normal_hints->height_inc = 1; normal_hints->base_width = imgw; normal_hints->base_height = imgh; normal_hints->flags = PMinSize | PBaseSize | PResizeInc; class_hints->res_name = deconst("play"); class_hints->res_class = deconst("Play"); normal_hints->x = x; normal_hints->y = y; normal_hints->width = w; normal_hints->height = h; if (bits & (XValue|YValue)) normal_hints->flags |= USPosition; normal_hints->flags |= (bits & (WidthValue|HeightValue)) ? USSize : PSize; attrmask = 0; attr.background_pixmap = None; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = StructureNotifyMask | ButtonPressMask; attrmask |= CWEventMask; attr.colormap = wincmap; attrmask |= CWColormap; topwin = XCreateWindow(disp,rootwin,x,y,w,h,0,visinfo.depth,InputOutput,visinfo.visual,attrmask,&attr); wn_prop.value = deconst("xplaymp4"); wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen((char *)wn_prop.value); in_prop.value = deconst("xplaymp4"); in_prop.encoding = XA_STRING; in_prop.format = 8; in_prop.nitems = strlen((char *)in_prop.value); XSetWMProperties(disp,topwin,&wn_prop,&in_prop,argv,argc,normal_hints,wm_hints,class_hints); gc = XCreateGC(disp,topwin,0,0); attrmask = 0; attr.background_pixmap = None; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask; attrmask |= CWEventMask; attr.colormap = wincmap; attrmask |= CWColormap; dispwin = XCreateWindow(disp,topwin,0,0,w,h,0,visinfo.depth,InputOutput,CopyFromParent,attrmask,&attr); XMapRaised(disp,dispwin); XRaiseWindow(disp,topwin); XMapWindow(disp,topwin); XFree((char *)wm_hints); XFree((char *)normal_hints); XFree((char *)class_hints); } static void resize_X_window(int w, int h) { XWindowChanges chg; XResizeWindow(disp,dispwin,w,h); chg.width = w; chg.height = h; XReconfigureWMWindow(disp,topwin,scrno,CWWidth|CWHeight,&chg); } #if 0 static int compute_shift(unsigned long int m) { int n; if (! m) abort(); m &= ~ (m >> 1); if (m & (m-1)) abort(); n = 0; if (m & 0xffff0000) { m >>= 16; n += 16; } if (m & 0x0000ff00) { m >>= 8; n += 8; } if (m & 0x000000f0) { m >>= 4; n += 4; } if (m & 0x0000000c) { m >>= 2; n += 2; } if (m & 0x00000002) { m >>= 1; n ++; } return(n); } #endif static struct timeval tvsub(struct timeval a, struct timeval b) { if (a.tv_usec >= b.tv_usec) { return((struct timeval){ .tv_sec = a.tv_sec - b.tv_sec, .tv_usec = a.tv_usec - b.tv_usec }); } else { return((struct timeval){ .tv_sec = a.tv_sec - b.tv_sec - 1, .tv_usec = a.tv_usec + 1000000 - b.tv_usec }); } } static void prompt(void) { printf("play> "); } static void init_play(void) { prev_dw = -1; prev_dh = -1; prev_ow = -1; prev_oh = -1; prev_rate = -1; prev_chan = -1; img = 0; } static void copy_zoom_integer(const unsigned char (*from)[4], unsigned char (*to)[4], int fw, int fh, int zf) { if (zf == 1) { bcopy(from,to,fw*fh*4); } else { int x; int y; int i; for (y=fh;y>0;y--) { for (x=fw;x>0;x--) { for (i=zf-1;i>=0;i--) { to[i][0] = from[0][0]; to[i][1] = from[0][1]; to[i][2] = from[0][2]; to[i][3] = from[0][3]; } from ++; to += zf; } for (i=zf-2;i>=0;i--) { bcopy(to-(zf*fw),to,fw*zf*sizeof(*to)); to += zf * fw; } } } } static void copy_zoom_fullscreen(const unsigned char (*from)[4], unsigned char (*to)[4], int fw, int fh, int tw, int th) { from=from; to=to; fw=fw; fh=fh; tw=tw; th=th; } static void playstate_init(PLAYSTATE *ps) { ps->aq.head = 0; ps->aq.tail = 0; ps->vq.head = 0; ps->vq.tail = 0; ps->started = 0; ps->paused = 0; ps->zf = 1; ps->timebase = 0; ps->vidrate = 1; ps->maxt = 0; ps->dB = 0; ps->vol = 1; ps->cmdbuf = 0; ps->cmdba = 0; ps->cmdbl = 0; } static void print_help(void) { printf("? print this help\n"); printf("p pause play\n"); printf("go unpause play\n"); printf(". print current state\n"); printf("zoom toggle x2 zoom\n"); printf("vol N set volume to N dB\n"); printf("seek T set to time T in the current file\n"); } static void print_state(PLAYSTATE *ps) { double maxt; maxt = mp4_index_time(ps->v,mp4_max_index(ps->v)) * ps->vidrate; printf("%s\n%dx%d (%dx%d), time %g of %g (%.3f%%)\n", ps->curname, prev_ow, prev_oh, prev_dw, prev_dh, ps->vq.head->when, maxt, (ps->vq.head->when*100)/maxt ); } static void pause_play(PLAYSTATE *ps) { if (ps->paused) { printf("Already paused.\n"); } else { audio_info_t ai; AUDIO_INITINFO(&ai); ai.play.pause = 1; ioctl(audiofd,AUDIO_SETINFO,&ai); ps->paused = 1; gettimeofday(&ps->pausetime,0); printf("Paused.\n"); } } static void unpause_play(PLAYSTATE *ps) { if (ps->paused) { audio_info_t ai; struct timeval tv; AUDIO_INITINFO(&ai); ai.play.pause = 0; ioctl(audiofd,AUDIO_SETINFO,&ai); ps->paused = 0; gettimeofday(&tv,0); if (tv.tv_usec >= ps->pausetime.tv_usec) { tv.tv_usec -= ps->pausetime.tv_usec; tv.tv_sec -= ps->pausetime.tv_sec; } else { tv.tv_usec += 1000000 - ps->pausetime.tv_usec; tv.tv_sec -= ps->pausetime.tv_sec + 1; } ps->start.tv_usec += tv.tv_usec; ps->start.tv_sec += tv.tv_sec; if (ps->start.tv_usec > 1000000) { ps->start.tv_usec -= 1000000; ps->start.tv_sec ++; } printf("Resumed.\n"); } else { printf("Not paused.\n"); } } static void set_volume(PLAYSTATE *ps, double dB) { ps->dB = dB; ps->vol = pow(2,dB/10); } static double seek_track(MP4TRK *trk, double t) { int l; int h; int m; l = -1; h = mp4_max_seekx(trk) + 1; while (h-l > 1) { m = (h + l) / 2; if (mp4_seekx_time(trk,m) < t) l = m; else h = m; } if (l < 0) l = 0; mp4_seek(trk,l); return(mp4_seekx_time(trk,l)); } static void free_aqe(AQE *e) { free(e); } static void free_vqe(VQE *e) { free(e->data); free(e); } static void seek_play(PLAYSTATE *ps, double t) { double oldt; double newt; int us; struct timeval tv; oldt = ps->vq.head->when; newt = seek_track(ps->v,t); seek_track(ps->a,newt); if (newt > oldt) { newt -= oldt; tv.tv_sec = newt; us = 1e6 * (newt - tv.tv_sec); if (us < 0) { us += 1000000; tv.tv_sec --; } else if (us > 1000000) { us -= 1000000; tv.tv_sec ++; } if (us > ps->start.tv_usec) { tv.tv_usec = ps->start.tv_usec + 1000000 - us; tv.tv_sec = ps->start.tv_sec - tv.tv_sec - 1; } else { tv.tv_usec = ps->start.tv_usec - us; tv.tv_sec = ps->start.tv_sec - tv.tv_sec; } } else { oldt -= newt; tv.tv_sec = oldt; us = ps->start.tv_usec + (int) (1e6 * (oldt - tv.tv_sec)); tv.tv_sec += ps->start.tv_sec; if (us < 0) { us += 1000000; tv.tv_sec --; } while (us >= 1000000) { us -= 1000000; tv.tv_sec ++; } tv.tv_usec = us; } printf("start changing from %lu.%06lu to %lu.%06lu\n", (unsigned long int) ps->start.tv_sec, (unsigned long int) ps->start.tv_usec, (unsigned long int) tv.tv_sec, (unsigned long int) tv.tv_usec ); ps->start = tv; while (ps->aq.head) { AQE *e; e = ps->aq.head; ps->aq.head = e->flink; free_aqe(e); } ps->aq.tail = 0; while (ps->vq.head) { VQE *e; e = ps->vq.head; ps->vq.head = e->flink; free_vqe(e); } ps->vq.tail = 0; } static void set_zoom(PLAYSTATE *ps, int z) { ps->zf = z; } static int parsenumber(const char *s, int l, int *vp) { int v; v = 0; for (;l>0;l--) { switch (*s++) { case '0': v = v * 10 ; break; case '1': v = (v * 10) + 1; break; case '2': v = (v * 10) + 2; break; case '3': v = (v * 10) + 3; break; case '4': v = (v * 10) + 4; break; case '5': v = (v * 10) + 5; break; case '6': v = (v * 10) + 6; break; case '7': v = (v * 10) + 7; break; case '8': v = (v * 10) + 8; break; case '9': v = (v * 10) + 9; break; default: return(0); break; } } *vp = v; return(1); } static void command_line(PLAYSTATE *ps, const char *txt, int len) { int i; int i0; int n; for (i=0;(izf==1)?-1:1); } else if ((i-i0 == 2) && !bcmp(&txt[i0],"zf",2)) { set_zoom(ps,-1); } else if ((i-i0 > 1) && (txt[i0] == 'z') && parsenumber(txt+i0+1,i-i0-1,&n)) { set_zoom(ps,n); } else { printf("unrecognized command `%.*s'\n",i-i0,&txt[i0]); } } static void cmd_save(PLAYSTATE *ps, const void *data, int len) { if (ps->cmdbl+len > ps->cmdba) { ps->cmdbuf = realloc(ps->cmdbuf,ps->cmdba=ps->cmdbl+len); } bcopy(data,ps->cmdbuf+ps->cmdbl,len); ps->cmdbl += len; } static void cmd_read(PLAYSTATE *ps) { unsigned char ibuf[256]; int n; unsigned char *nl; int bx; n = read(0,&ibuf[0],sizeof(ibuf)); if (n < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: stdin read: %s\n",__progname,strerror(errno)); exit(1); } bx = 0; while (bx < n) { nl = memchr(&ibuf[bx],'\n',n-bx); if (nl == 0) { cmd_save(ps,&ibuf[bx],n-bx); return; } else { if (ps->cmdbl == 0) { command_line(ps,&ibuf[bx],nl-&ibuf[bx]); } else { cmd_save(ps,&ibuf[bx],nl-&ibuf[bx]); command_line(ps,ps->cmdbuf,ps->cmdbl); } ps->cmdbl = 0; bx = (nl+1) - &ibuf[0]; prompt(); } } } static void handle_X_expose(void) { XEvent ev; XNextEvent(disp,&ev); if ((ev.type == Expose) && (ev.xexpose.window == dispwin)) { XPutImage(disp,dispwin,gc,img,ev.xexpose.x,ev.xexpose.y,ev.xexpose.x,ev.xexpose.y,ev.xexpose.width,ev.xexpose.height); } } static void apply_volume(MP4_AFMT fmt, double vol, void *data, int bytes) { if (vol == 1) return; switch (fmt) { case MP4_AFMT_SLIN16N: { signed short int *dp; int ns; dp = data; for (ns=bytes/2;ns>0;ns--) *dp++ *= vol; } break; default: abort(); break; } } static void compute_scale(PLAYSTATE *ps) { ps->vidrate = mp4_index_time(ps->a,mp4_max_index(ps->a)) / mp4_index_time(ps->v,mp4_max_index(ps->v)); } static void play_files(char **fnv, int nfn) { PLAYSTATE ps; int fno; int i; struct timeval ntv; struct timeval nextin; struct pollfd pfd[2]; playstate_init(&ps); prompt(); gettimeofday(&nextin,0); for (fno=0;fno { ps.curname = fnv[fno]; ps.curfile = mp4_open(fnv[fno],0); mp4_scan(ps.curfile); ps.ax = -1; ps.vx = -1; for (i=mp4_ntracks(ps.curfile)-1;i>=0;i--) { switch (mp4_track_type(ps.curfile,i)) { default: abort(); break; case MP4_TT_AUDIO: ps.ax = i; break; case MP4_TT_VIDEO: ps.vx = i; break; } } if (ps.vx < 0) printf("*** no video found\n"); if (ps.ax < 0) printf("*** no audio found\n"); if ((ps.vx < 0) || (ps.ax < 0)) break <"nextfile">; ps.a = mp4_open_audio(ps.curfile,ps.ax); ps.v = mp4_open_video(ps.curfile,ps.vx); compute_scale(&ps); printf("duration %g, vidrate %g\n",mp4_index_time(ps.v,mp4_max_index(ps.v)),ps.vidrate); while (1) { int deca; int decv; int usea; int usev; double t; int n; fflush(0); if (!ps.a && !ps.v && !ps.aq.head && !ps.vq.head) break; deca = (ps.a && (!ps.aq.head || (ps.aq.tail->when-ps.aq.head->when < .25))); decv = (ps.v && (!ps.vq.head || (ps.vq.tail->when-ps.vq.head->when < .25))); pfd[0].fd = 0; pfd[0].events = POLLIN | POLLRDNORM; if (ps.paused) { while (XEventsQueued(disp,QueuedAfterFlush) > 0) handle_X_expose(); pfd[1].fd = XConnectionNumber(disp); pfd[1].events = POLLIN | POLLRDNORM; n = poll(&pfd[0],2,INFTIM); if ((n > 0) && pfd[0].revents) cmd_read(&ps); continue; } n = poll(&pfd[0],1,0); if (n > 0) { cmd_read(&ps); continue; } gettimeofday(&ntv,0); if (ps.started) { struct timeval delta; delta = tvsub(ntv,ps.start); t = delta.tv_sec + (delta.tv_usec * 1e-6); ps.maxt = t; if (ps.aq.head) { /* Doc lies. AUDIO_WSEEK works in bytes, not samples. */ ioctl(audiofd,AUDIO_WSEEK,&n); usea = (n < audio_wthresh) || (prev_rate < 0); } else { usea = 0; } usev = (ps.vq.head && (!ps.aq.head || (ps.vq.head->when+AUDIO_DELAY < ps.aq.head->when))); } else { if (!deca && !decv) { ps.started = 1; ps.start = ntv; continue; } usea = 0; usev = 0; } #ifdef VERBOSE if (started) { printf("Loop, t %g",t); } else { printf("Preload,"); } if (aq.head) { printf(" a %g..%g",aq.head->when,aq.tail->when); } else { printf(" no a"); } if (vq.head) { printf(" v %g..%g",vq.head->when,vq.tail->when); } else { printf(" no v"); } printf(":"); if (deca) printf(" deca"); if (decv) printf(" decv"); if (usea) printf(" usea"); if (usev) printf(" usev"); printf("\n"); #endif if (deca) { const MP4ADESC *ad; AQE *e; int nb; ad = mp4_get_audio(ps.a); if (ad->samples) { if (prev_rate < 0) { audio_info_t ai; AUDIO_INITINFO(&ai); ai.play.sample_rate = ad->rate; prev_rate = ad->rate; ai.play.channels = ad->channels; prev_chan = ad->channels; switch (ad->format) { case MP4_AFMT_SLIN16N: ai.play.precision = 16; ai.play.encoding = AUDIO_ENCODING_SLINEAR; sample_bytes = 2; break; default: abort(); break; } prev_fmt = ad->format; ai.play.pause = 0; ai.blocksize = 0; ai.mode = AUMODE_PLAY_ALL; if (ioctl(audiofd,AUDIO_SETINFO,&ai) < 0) { fprintf(stderr,"%s: AUDIO_SETINFO on %s: %s\n",__progname,AUDIODEV,strerror(errno)); exit(1); } audio_Bps = sample_bytes * prev_chan * prev_rate; audio_wthresh = audio_Bps * AUDIO_DELAY; } if ( (prev_rate != ad->rate) || (prev_chan != ad->channels) || (prev_fmt != ad->format) ) { printf("*** audio format changed\n"); exit(1); } nb = ad->samples * ad->channels * sample_bytes; e = malloc(sizeof(AQE)+nb); e->len = nb; e->data = (void *)(e+1); bcopy(ad->data,e->data,nb); e->when = ad->when + ps.timebase; e->flink = 0; e->blink = ps.aq.tail; if (ps.aq.tail) ps.aq.tail->flink = e; else ps.aq.head = e; ps.aq.tail = e; } else { mp4_close_track(ps.a); ps.a = 0; } } if (!deca && !decv && !usea && !usev) { poll(0,0,10); continue; } if (decv) { const MP4VDESC *vd; VQE *e; int nb; vd = mp4_get_video(ps.v); if (vd->data) { e = malloc(sizeof(VQE)); e->ow = vd->w; e->oh = vd->h; if (ps.zf < 0) { e->dw = width; e->dh = height; nb = width * height * 4; e->data = malloc(nb); copy_zoom_fullscreen(vd->data,e->data,vd->w,vd->h,width,height); } else { e->dw = vd->w * ps.zf; e->dh = vd->h * ps.zf; nb = e->dw * e->dh * 4; e->data = malloc(nb); copy_zoom_integer(vd->data,e->data,vd->w,vd->h,ps.zf); } e->when = (vd->when * ps.vidrate) + ps.timebase; e->flink = 0; e->blink = ps.vq.tail; if (ps.vq.tail) ps.vq.tail->flink = e; else ps.vq.head = e; ps.vq.tail = e; } else { mp4_close_track(ps.v); ps.v = 0; } } if (usea) { AQE *e; e = ps.aq.head; ps.aq.head = e->flink; if (ps.aq.head) ps.aq.head->blink = 0; else ps.aq.tail = 0; apply_volume(prev_fmt,ps.vol,e->data,e->len); write(audiofd,e->data,e->len); free_aqe(e); } if (usev) { VQE *e; e = ps.vq.head; ps.vq.head = e->flink; if (ps.vq.head) ps.vq.head->blink = 0; else ps.vq.tail = 0; prev_ow = e->ow; prev_oh = e->oh; if ((e->dw != prev_dw) || (e->dh != prev_dh)) { if (prev_dw < 0) { create_X_windows(e->dw,e->dh); } resize_X_window(e->dw,e->dh); prev_dw = e->dw; prev_dh = e->dh; if (img) XDestroyImage(img); img = XCreateImage(disp,visinfo.visual,24,ZPixmap,0,0,prev_dw,prev_dh,32,0); #ifdef VERBOSE printf("bytes_per_line %d\n",img->bytes_per_line); printf("bits_per_pixel %d\n",img->bits_per_pixel); switch (img->byte_order) { case LSBFirst: printf("byte_order LSBFirst\n"); break; case MSBFirst: printf("byte_order MSBFirst\n"); break; default: printf("byte_order %d\n",(int)img->byte_order); break; } printf("red_mask %08lx\n",img->red_mask); printf("green_mask %08lx\n",img->green_mask); printf("blue_mask %08lx\n",img->blue_mask); #endif img->bytes_per_line = prev_dw * 4; img->bits_per_pixel = 32; /* why do these work?? they don't look right. */ img->byte_order = LSBFirst; img->red_mask = 0x00ff0000; img->green_mask = 0x0000ff00; img->blue_mask = 0x000000ff; img->data = malloc(prev_dw*prev_dh*4); } bcopy(e->data,img->data,prev_dw*prev_dh*4); XPutImage(disp,dispwin,gc,img,0,0,0,0,prev_dw,prev_dh); while (XEventsQueued(disp,QueuedAfterReading) > 0) { XEvent ev; XNextEvent(disp,&ev); } XFlush(disp); free_vqe(e); } } } while (0); mp4_close(ps.curfile); ps.timebase = ps.maxt; } ioctl(audiofd,AUDIO_DRAIN,0); } int main(int, char **); int main(int ac, char **av) { saveargv(ac,av); handleargs(ac-1,av+1); setup_X(); setup_sound(); init_play(); play_files(ifiles,nifiles); return(0); }