forsyth20121205-174063a80cc13 years agoHistory
384 lines · 7.9 KB · Brainfuck
RawDownload
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;}