/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mp4.h" #define AUDIODEV "/dev/sound" extern const char *__progname; static int argc; static char **argv; static MP4 *curfile; 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_prec; static int prev_chan; static int prev_code; static Window topwin; static Window dispwin; static Colormap wincmap; static int prev_w; static int prev_h; static XrmDatabase db; 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; int class; const char *classname; 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 != class) { fprintf(stderr,"%s: visual %s isn't %s\n",__progname,visualstr,classname); 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); prev_w = -1; prev_h = -1; } 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); } prev_rate = -1; prev_chan = -1; prev_prec = -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 = (width - imgw) / 2; y = (width - 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.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,topwin,w,h); chg.width = w; chg.height = h; XReconfigureWMWindow(disp,dispwin,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 void play_file(const char *fn) { int i; int ax; int vx; MP4TRK *a; MP4TRK *v; XImage *img; printf("playing %s\n",fn); curfile = mp4_open(fn,0); mp4_scan(curfile); ax = -1; vx = -1; for (i=mp4_ntracks(curfile)-1;i>=0;i--) { switch (mp4_track_type(curfile,i)) { default: abort(); break; case MP4_TT_AUDIO: ax = i; break; case MP4_TT_VIDEO: vx = i; break; } } if (vx < 0) printf("*** no video found\n"); if (ax < 0) printf("*** no video found\n"); if ((vx < 0) || (ax < 0)) exit(1); a = mp4_open_audio(curfile,ax); v = mp4_open_video(curfile,vx); img = 0; while (1) { const MP4VDESC *d; d = mp4_get_video(v); if (! d->data) break; if ((d->w != prev_w) || (d->h != prev_h)) { if (prev_w < 0) { create_X_windows(d->w,d->h); } resize_X_window(d->w,d->h); prev_w = d->w; prev_h = d->h; if (img) XDestroyImage(img); img = XCreateImage(disp,visinfo.visual,24,ZPixmap,0,0,prev_w,prev_h,8,0); img->bytes_per_line = prev_w * 3; img->bits_per_pixel = 24; img->byte_order = MSBFirst; img->red_mask = 0x000000ff; img->green_mask = 0x0000ff00; img->blue_mask = 0x00ff0000; img->data = malloc(prev_w*prev_h*3); } bcopy(d->data,img->data,prev_w*prev_h*3); XPutImage(disp,dispwin,gc,img,0,0,0,0,prev_w,prev_h); XFlush(disp); } #if 0 AUDIO_INITINFO(&ai); ai.play.sample_rate = 48000; // XXX ai.play.channels = 1; // XXX ai.play.precision = 16; // XXX ai.play.encoding = AUDIO_ENCODING_SLINEAR; ai.play.pause = 1; 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_WSEEK int #endif prev_code = prev_code; } static void play_ifiles(void) { int i; for (i=0;i