ref: 54665a8d26c814f399e8dfd7bc85cb7e00484712
dir: /main.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <memdraw.h> #include <mouse.h> #include <keyboard.h> #include <thread.h> #include "frame.h" #include "misc.h" #include "stream.h" #include "decoder.h" int mainstacksize = 128*1024; static Image *curim; static uvlong lastframe; static int audiofd = -1; static Channel *audiosync; static Channel *audiofinished; static char info[256]; static int showinfo; static uvlong dispdelay; static int framedrop; static void audioproc(void *x) { Streamframe f; Stream *s; int audiofd; if((audiofd = open("/dev/audio", OWRITE|OCEXEC)) < 0){ fprint(2, "runaudio: %r\n"); recvp(audiosync); chanclose(audiosync); }else{ for(s = x; Sread(s, &f) == 0 && f.sz > 0;){ recvp(audiosync); chanclose(audiosync); write(audiofd, f.buf, f.sz); } close(audiofd); } sendp(audiofinished, nil); threadexits(nil); } static void drawframe(Frame *f) { uvlong x, end, left; Rectangle r, clip; if(lastframe == 0) lastframe = nanosec(); end = lastframe + f->dt - dispdelay; if(framedrop && dispdelay > 0 && nanosec() >= end+dispdelay) goto drop; lockdisplay(display); if(curim != nil && (Dx(curim->r) != f->w || Dy(curim->r) != f->h)){ freeimage(curim); curim = nil; } r = Rect(0,0,f->w,f->h); if(curim == nil) curim = allocimage(display, r, RGB24, 0, DNofill); loadimage(curim, r, f->rgb, f->w*f->h*3); while(1){ x = nanosec(); if(x >= end) break; left = end - x; if(left > 750000000ULL) sleep(70); else if(left > 250000000ULL) sleep(20); else if(left > 100000000ULL) sleep(1); } clip = Rect(f->crop.left, f->crop.top, f->crop.right, f->crop.bottom); if(badrect(clip)) clip = curim->r; replclipr(curim, 0, clip); x = nanosec(); if(Dx(clip) < Dx(screen->r) && Dy(clip) < Dy(screen->r)) r = rectsubpt(screen->r, divpt(Pt(Dx(clip)-Dx(screen->r), Dy(clip)-Dy(screen->r)), 2)); else r = screen->r; draw(screen, r, curim, nil, ZP); if(showinfo) stringbg(screen, screen->r.min, display->white, ZP, font, info, display->black, ZP); flushimage(display, 1); unlockdisplay(display); dispdelay = nanosec() - x; drop: if(audiosync != nil) sendp(audiosync, nil); lastframe += f->dt; free(f); } static void usage(void) { fprint(2, "usage: %s [-a AUDIO] file.mp4\n", argv0); threadexitsall("usage"); } void threadmain(int argc, char **argv) { enum { Cframe, Cplayerdone, Cmouse, Ckeyboard, Cresize, Cnum, }; Streamframe f; Frame *frame; Mousectl *mctl; Keyboardctl *kctl; Rune key; Mouse m; char *s, *audio, *status; Stream *stream, *svideo, *saudio; Decoder *d; int i, j, end, done, forced, res, nstreams; Alt a[Cnum+1] = { [Cframe] = { nil, &frame, CHANRCV }, [Cplayerdone] = { nil, &status, CHANRCV }, [Cmouse] = { nil, &m, CHANRCV }, [Ckeyboard] = { nil, &key, CHANRCV }, [Cresize] = { nil, nil, CHANRCV }, [Cnum] = { nil, nil, CHANEND }, }; debug = 0; audio = nil; ARGBEGIN{ case 'a': if(audio != nil){ fprint(2, "only one audio file can be specified\n"); usage(); } audio = EARGF(usage()); break; case 'D': framedrop++; break; case 'd': debug++; break; }ARGEND if(argc < 1) usage(); nproc = atoi((s = getenv("NPROC")) != nil ? s : "1"); if(nproc < 1) nproc = 1; else if(nproc > 4) nproc = 4; srand(nanosec()); if(initdraw(nil, nil, "treason") < 0) sysfatal("initdraw: %r"); flushimage(display, 1); display->locking = 1; unlockdisplay(display); if((mctl = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); if((kctl = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); a[Cmouse].c = mctl->c; a[Cresize].c = mctl->resizec; a[Ckeyboard].c = kctl->c; saudio = nil; if(audio != nil) saudio = Sopen(audio, &nstreams); for(end = i = 0; !end && i < argc; i++){ draw(screen, screen->r, display->black, nil, ZP); if((stream = Sopen(argv[i], &nstreams)) == nil) sysfatal("%r"); svideo = nil; for(j = 0; j < nstreams; j++){ if(stream[j].type == Svideo && svideo == nil) svideo = stream+j; else if(stream[j].type == Saudio && saudio == nil) saudio = stream+j; else Sclose(stream+j); } if(svideo == nil) sysfatal("%s: no video", argv[i]); if((d = Dopen(svideo)) == nil) sysfatal("%r"); a[Cframe].c = d->frames; a[Cplayerdone].c = d->finished; lastframe = 0; if(saudio != nil){ audiosync = chancreate(sizeof(void*), 1); audiofinished = chancreate(sizeof(void*), 0); proccreate(audioproc, saudio, 4096); }else{ audiosync = nil; audiofinished = nil; } for(done = forced = 0; !done && !end;){ res = alt(a); switch(res){ case Cframe: snprint( info, sizeof(info), "decode=%zdms yuv→rgb=%zdms display=%zdms total=%zdms %dx%d %s %s", d->decodetime/1000000ULL, dispdelay/1000000ULL, yuv→rgb/1000000ULL, (d->decodetime + dispdelay + yuv→rgb)/1000000ULL, frame->w, frame->h, d->info, d->s->info ); drawframe(frame); break; case Cplayerdone: done = 1; if(status != nil) forced = 1; break; case Cmouse: break; case Ckeyboard: end = key == 'q' || key == Kdel; done = key == '\n'; showinfo = key == 'i' ? !showinfo : showinfo; forced = 1; break; case Cresize: lockdisplay(display); if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); freeimage(curim); curim = nil; draw(screen, screen->r, display->black, nil, ZP); flushimage(display, 1); unlockdisplay(display); break; } for(; !end && audiofd >= 0 && saudio != nil;){ if(Sread(saudio, &f) != 0 || f.sz == 0) break; write(audiofd, f.buf, f.sz); } } if(forced && saudio != nil){ Sclose(saudio); chanclose(audiosync); } if(audiofinished != nil){ recvp(audiofinished); chanclose(audiofinished); audiofinished = nil; } if(!forced) Sclose(saudio); saudio = nil; Dclose(d); for(i = 0; i < nstreams; i++) Sclose(stream+i); free(stream); } threadexitsall(nil); }