ref: c3241a31142f7e367c78965a9fbbc01fb557750d
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 u8int *curpx; 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 uvlong late; static int bw; static int benchmark; static void audioproc(void *x) { Streamframe f; Stream *s; int audiofd, synced; synced = 0; if((audiofd = open("/dev/audio", OWRITE|OCEXEC)) < 0){ fprint(2, "runaudio: %r\n"); }else{ for(s = x; Sread(s, &f) == 0 && f.sz > 0;){ if(!synced){ recvp(audiosync); chanclose(audiosync); synced = 1; } write(audiofd, f.buf, f.sz); } close(audiofd); } if(!synced){ recvp(audiosync); chanclose(audiosync); } sendp(audiofinished, nil); threadexits(nil); } static void drawframe(Frame *f) { uvlong x, end, left; Rectangle r, clip; int y; x = nanosec(); if(lastframe == 0) lastframe = x; end = lastframe + f->dt; if(x+dispdelay >= end){ late = x+dispdelay - end; if(late > 0 && framedrop) goto drop; }else{ late = 0; } if(curim != nil && (Dx(curim->r) != f->w || Dy(curim->r) != f->h)){ freeimage(curim); curim = nil; free(curpx); curpx = nil; } r = Rect(0,0,f->w,f->h); if(bw){ if(curim == nil) curim = allocimage(display, r, GREY8, 0, DNofill); if(curpx == nil) curpx = malloc(f->w*f->h); for(y = 0; y < f->h; y++) memmove(curpx+f->w*y, f->yuv[0]+f->ystride*y, f->w); loadimage(curim, r, curpx, f->w*f->h); }else{ if(curim == nil) curim = allocimage(display, r, RGB24, 0, DNofill); if(curpx == nil) curpx = malloc(f->w*f->h*3); yuv420_rgb24(f->w, f->h, f->yuv[0], f->yuv[1], f->yuv[2], f->ystride, f->uvstride, curpx, f->w*3); loadimage(curim, r, curpx, f->w*f->h*3); } dispdelay = nanosec() - x; if(!benchmark) while(1){ x = nanosec(); if(x >= end - 1*1000ULL*1000ULL) break; left = end - x; if(left > 80ULL*1000ULL*1000ULL) sleep(70); else if(left > 30ULL*1000ULL*1000ULL) sleep(20); } clip = Rect(f->crop.left, f->crop.top, f->crop.right, f->crop.bottom); if(badrect(clip)) clip = curim->r; replclipr(curim, 0, clip); 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); 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, }; 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 'B': benchmark++; break; case 'b': bw++; 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"); 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 && !benchmark) 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: if(showinfo){ snprint( info, sizeof(info), "decode=%-5lld yuv→rgb=%-3lld display=%-3lld total=%-3lld late=%-3lld %dx%d %s %s %s", d->decodetime/1000000ULL, dispdelay/1000000ULL, yuv→rgb/1000000ULL, (d->decodetime + dispdelay)/1000000ULL, late/1000000ULL, frame->w, frame->h, d->info, d->s->info, saudio ? saudio->info : "no audio" ); } 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: if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); draw(screen, screen->r, display->black, nil, ZP); flushimage(display, 1); break; } } 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); }