diff options
Diffstat (limited to 'appl/wm/avi.b')
| -rw-r--r-- | appl/wm/avi.b | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/appl/wm/avi.b b/appl/wm/avi.b new file mode 100644 index 00000000..a1331a1e --- /dev/null +++ b/appl/wm/avi.b @@ -0,0 +1,384 @@ +implement WmAVI; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Rect, Display, Image: import draw; + +include "tk.m"; + tk: Tk; + Toplevel: import tk; + +include "tkclient.m"; + tkclient: Tkclient; + ctxt: ref Draw->Context; + +include "selectfile.m"; + selectfile: Selectfile; + +include "dialog.m"; + dialog: Dialog; + +include "riff.m"; + avi: Riff; + AVIhdr, AVIstream, RD: import avi; + video: ref AVIstream; + +WmAVI: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +Stopped, Playing, Paused: con iota; +state := Stopped; + + +cmap: array of byte; +codedbuf: array of byte; +pixelbuf: array of byte; +pixelrec: Draw->Rect; + +task_cfg := array[] of { + "canvas .c", + "frame .b", + "button .b.File -text File -command {send cmd file}", + "button .b.Stop -text Stop -command {send cmd stop}", + "button .b.Pause -text Pause -command {send cmd pause}", + "button .b.Play -text Play -command {send cmd play}", + "frame .f", + "label .f.file -text {File:}", + "label .f.name", + "pack .f.file .f.name -side left", + "pack .b.File .b.Stop .b.Pause .b.Play -side left", + "pack .f -fill x", + "pack .b -anchor w", + "pack .c -side bottom -fill both -expand 1", + "pack propagate . 0", +}; + +init(xctxt: ref Draw->Context, nil: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + tkclient= load Tkclient Tkclient->PATH; + dialog = load Dialog Dialog->PATH; + selectfile = load Selectfile Selectfile->PATH; + + ctxt = xctxt; + + sys->pctl(Sys->NEWPGRP, nil); + + tkclient->init(); + dialog->init(); + selectfile->init(); + + (t, wmctl) := tkclient->toplevel(ctxt, "", "AVI Player", 0); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + for (c:=0; c<len task_cfg; c++) + tk->cmd(t, task_cfg[c]); + + tk->cmd(t, "bind . <Configure> {send cmd resize}"); + tk->cmd(t, "update"); + tkclient->onscreen(t, nil); + tkclient->startinput(t, "kbd"::"ptr"::nil); + + avi = load Riff Riff->PATH; + if(avi == nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Loading Interfaces", + "Failed to load the RIFF/AVI\ninterface:"+sys->sprint("%r"), + 0, "Exit"::nil); + return; + } + avi->init(); + + fname := ""; + state = Stopped; + + for(;;) alt { + s := <-t.ctxt.kbd => + tk->keyboard(t, s); + s := <-t.ctxt.ptr => + tk->pointer(t, *s); + s := <-t.ctxt.ctl or + s = <-t.wreq or + s = <-wmctl => + if(s == "exit") { + state = Stopped; + return; + } + tkclient->wmctl(t, s); + press := <-cmd => + case press { + "file" => + state = Stopped; + patterns := list of { + "*.avi (Microsoft movie files)", + "* (All Files)" + }; + fname = selectfile->filename(ctxt, t.image, "Locate AVI files", + patterns, nil); + if(fname != nil) { + tk->cmd(t, ".f.name configure -text {"+fname+"}"); + tk->cmd(t, "update"); + } + "play" => + if (state != Stopped) { + state = Playing; + continue; + } + if(fname != nil) { + state = Playing; + spawn play(t, fname); + } + "pause" => + if(state == Playing) + state = Paused; + "stop" => + state = Stopped; + } + } +} + +play(t: ref Toplevel, file: string) +{ + sp := list of { "Stop Play" }; + + (r, err) := avi->open(file); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Open AVI file", err, 0, sp); + return; + } + + err = avi->r.check4("AVI "); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Read AVI format", err, 0, sp); + return; + } + + (code, l) := avi->r.gethdr(); + if(code != "LIST") { + dialog->prompt(ctxt, t.image, "error -fg red", "Parse AVI headers", + "no list under AVI section header", 0, sp); + return; + } + + err = avi->r.check4("hdrl"); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Read AVI header", err, 0, sp); + return; + } + + avihdr: ref AVIhdr; + (avihdr, err) = avi->r.avihdr(); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Read AVI header", err, 0, sp); + return; + } + + # + # read the stream info & format structures + # + stream := array[avihdr.streams] of ref AVIstream; + for(i := 0; i < avihdr.streams; i++) { + (stream[i], err) = avi->r.streaminfo(); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", "Parse AVI headers", + "Failed to parse stream headers\n"+err, 0, sp); + return; + } + if(stream[i].stype == "vids") { + video = stream[i]; + err = video.fmt2binfo(); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", + "Parse AVI Video format", + "Invalid stream headers\n"+err, 0, sp); + return; + } + } + } + + img: ref Draw->Image; + if(video != nil) { + case video.binfo.compression { + * => + dialog->prompt(ctxt, t.image, "error -fg red", + "Parse AVI Compression method", + "unknown compression/encoding method", 0, sp); + return; + avi->BI_RLE8 => + cmap = array[len video.binfo.cmap] of byte; + for(i = 0; i < len video.binfo.cmap; i++) { + e := video.binfo.cmap[i]; + cmap[i] = byte ctxt.display.rgb2cmap(e.r, e.g, e.b); + } + break; + } + chans: draw->Chans; + case video.binfo.bitcount { + * => + dialog->prompt(ctxt, t.image, "error -fg red", + "Check AVI Video format", + string video.binfo.bitcount+ + " bits per pixel not supported", 0, sp); + return; + 8 => + chans = Draw->CMAP8; + mem := video.binfo.width*video.binfo.height; + pixelbuf = array[mem] of byte; + }; + pixelrec.min = (0, 0); + pixelrec.max = (video.binfo.width, video.binfo.height); + img = ctxt.display.newimage(pixelrec, chans, 0, Draw->White); + if (img == nil) { + sys->fprint(sys->fildes(2), "coffee: failed to allocate image\n"); + exit; + } + } + + # + # Parse out the junk headers we don't understand + # + parse: for(;;) { + (code, l) = avi->r.gethdr(); + if(l < 0) + break; + + case code { + * => +# sys->print("%s %d\n", code, l); + avi->r.skip(l); + "LIST" => + err = avi->r.check4("movi"); + if(err != nil) { + dialog->prompt(ctxt, t.image, "error -fg red", + "Strip AVI headers", + "no movi chunk", 0, sp); + return; + } + break parse; + } + } + + canvr := canvsize(t); + p := (Draw->Point)(0, 0); + dx := canvr.dx(); + if(dx > video.binfo.width) + p.x = (dx - video.binfo.width)/2; + + dy := canvr.dy(); + if(dy > video.binfo.height) + p.y = (dy - video.binfo.height)/2; + + canvr = canvr.addpt(p); + + chunk: for(;;) { + while(state == Paused) + sys->sleep(0); + if(state == Stopped) + break chunk; + (code, l) = avi->r.gethdr(); + if(l <= 0) + break; + if(l & 1) + l++; + case code { + * => + avi->r.skip(l); + "00db" => # Stream 0 Video DIB + dib(r, img, l); + "00dc" => # Stream 0 Video DIB compressed + dibc(r, img, l); + t.image.draw(canvr, img, nil, img.r.min); + "idx1" => + break chunk; + } + } + state = Stopped; +} + +dib(r: ref RD, i: ref Draw->Image, l: int): int +{ + if(len codedbuf < l) + codedbuf = array[l] of byte; + + if(r.readn(codedbuf, l) != l) + return -1; + + case video.binfo.bitcount { + 8 => + for(k := 0; k < l; k++) + codedbuf[k] = cmap[int codedbuf[k]]; + + i.writepixels(pixelrec, codedbuf); + } + return 0; +} + +dibc(r: ref RD, i: ref Draw->Image, l: int): int +{ + if(len codedbuf < l) + codedbuf = array[l] of byte; + + if(r.readn(codedbuf, l) != l) + return -1; + + case video.binfo.compression { + avi->BI_RLE8 => + p := 0; + posn := 0; + x := 0; + y := video.binfo.height-1; + w := video.binfo.width; + decomp: while(p < l) { + n := int codedbuf[p++]; + if(n == 0) { + esc := int codedbuf[p++]; + case esc { + 0 => # end of line + x = 0; + y--; + 1 => # end of image + break decomp; + 2 => # Delta dx,dy + x += int codedbuf[p++]; + y -= int codedbuf[p++]; + * => + posn = x+y*w; + for(k := 0; k < esc; k++) + pixelbuf[posn++] = cmap[int codedbuf[p++]]; + x += esc; + if(p & 1) + p++; + }; + } + else { + posn = x+y*w; + v := cmap[int codedbuf[p++]]; + for(k := 0; k < n; k++) + pixelbuf[posn++] = v; + x += n; + } + } + i.writepixels(pixelrec, pixelbuf); + } + return 0; +} + +canvsize(t: ref Toplevel): Rect +{ + r: Rect; + + r.min.x = int tk->cmd(t, ".c cget -actx"); + r.min.y = int tk->cmd(t, ".c cget -acty"); + r.max.x = r.min.x + int tk->cmd(t, ".c cget -width"); + r.max.y = r.min.y + int tk->cmd(t, ".c cget -height"); + + return r; +} |
