code: purgatorio

ref: 71292834bbafcf6bc409cfd7691f9e6650df025b
dir: /appl/wm/mpeg/mpeg.b/

View raw version
implement WmMpeg;

include "sys.m";
	sys: Sys;

include "draw.m";
	draw: Draw;
	Point, Rect, Display, Image: import draw;

include "tk.m";
	tk: Tk;
	Toplevel: import tk;

include	"tkclient.m";
	tkclient: Tkclient;
	ctxt: ref Draw->Context;

include "dialog.m";
	dialog: Dialog;

include "selectfile.m";
	selectfile: Selectfile;

include "mpegio.m";

include "arg.m";

mio: Mpegio;
decode: Mpegd;
remap: Remap;
Mpegi: import mio;

WmMpeg: module
{
	init:	fn(ctxt: ref Draw->Context, argv: list of string);
};

Stopped, Playing, Stepping, Paused: con iota;
state	:= Stopped;
depth := -1;
sdepth: int;
cvt: ref Image;

pixelrec: Draw->Rect;

decoders := array[] of {
1=>	Mpegd->PATH4,
2=>	Mpegd->PATH4,
4=>	Mpegd->PATH4,
8 or 16 or 24 or 32 =>	Mpegd->PATH,
};

remappers := array[] of {
1=>	Remap->PATH1,
2=>	Remap->PATH2,
4=>	Remap->PATH4,
8 or 16 or 24 or 32 =>	Remap->PATH,
};

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.Step -text Step -command {send cmd step}",
	"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.Step .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, argv: 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;
	tkclient->init();
	dialog->init();
	selectfile->init();

	darg, tkarg: string;
	arg := load Arg Arg->PATH;
	arg->init(argv);
	while((c := arg->opt()) != 0)
		case c {
		'x' =>
			tkarg = arg->arg();
		'd' =>
			darg = arg->arg();
		}
	args := arg->argv();
	arg = nil;
	if(darg != nil)
		depth = int darg;
	sdepth = ctxt.display.image.depth;
	if (depth < 0 || depth > sdepth)
		depth = sdepth;
	(t, menubut) := tkclient->toplevel(ctxt, tkarg, "MPEG Player", 0);

	cmd := chan of string;
	tk->namechan(t, cmd, "cmd");

	for(i:=0; i<len task_cfg; i++)
		tk->cmd(t, task_cfg[i]);

	tk->cmd(t, "bind . <Configure> {send cmd resize}");
	tk->cmd(t, "update");
	tkclient->onscreen(t, nil);
	tkclient->startinput(t, "kbd"::"ptr"::nil);

	mio = load Mpegio Mpegio->PATH;
	decode = load Mpegd decoders[depth];
	remap = load Remap remappers[depth];
	if(mio == nil || decode == nil || remap == nil) {
		dialog->prompt(ctxt, t.image, "error -fg red", "Loading Interfaces",
			"Failed to load the MPEG\ninterface: "+sys->sprint("%r"),
			0, "Exit"::nil);
		return;
	}
	mio->init();

	fname := "";
	ctl := chan of string;
	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 =>
		tkclient->wmctl(t, s);
	s := <-menubut =>
		if(s == "exit"){
			state = Stopped;
			return;
		}
		tkclient->wmctl(t, s);
	press := <-cmd =>
		case press {
		"file" =>
			state = Stopped;
			patterns := list of {
				"*.mpg (MPEG movie files)",
				"* (All Files)"
			};
			fname = selectfile->filename(ctxt, t.image, "Locate MPEG 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);
			}
		"step" =>
			if (state != Stopped) {
				state = Stepping;
				continue;
			}
			if(fname != nil) {
				state = Stepping;
				spawn play(t, fname);
			}
		"pause" =>
			if(state == Playing)
				state = Paused;
		"stop" =>
			state = Stopped;
		}
	}
}

play(t: ref Toplevel, file: string)
{
	sp := list of { "Stop Play" };

	fd := sys->open(file, Sys->OREAD);
	if(fd == nil) {
		dialog->prompt(ctxt, t.image, "error -fg red", "Open MPEG file", sys->sprint("%r"), 0, sp);
		return;
	}
	m := mio->prepare(fd, file);
	m.streaminit(Mpegio->VIDEO_STR0);
	p := m.getpicture(1);
	decode->init(m);
	remap->init(m);

	canvr := canvsize(t);
	o := Point(0, 0);
	dx := canvr.dx();
	if(dx > m.width)
		o.x = (dx - m.width)/2;
	dy := canvr.dy();
	if(dy > m.height)
		o.y = (dy - m.height)/2;
	canvr.min = canvr.min.add(o);
	canvr.max = canvr.min.add(Point(m.width, m.height));

	if (depth != sdepth){
		chans := Draw->CMAP8;
		case depth {
		0 =>	chans = Draw->GREY1;
		1 =>	chans = Draw->GREY2;
		2 =>	chans = Draw->GREY4;
		3 =>	chans = Draw->CMAP8;
		4 =>	chans = Draw->RGB16;
		5 =>	chans = Draw->RGB24;	# ?
		}
		cvt = ctxt.display.newimage(Rect((0, 0), (m.width, m.height)), chans, 0, 0);
	}

	f, pf: ref Mpegio->YCbCr;
	for(;;) {
		if(state == Stopped)
			break;
		case p.ptype {
		Mpegio->IPIC =>
			f = decode->Idecode(p);
		Mpegio->PPIC =>
			f = decode->Pdecode(p);
		Mpegio->BPIC =>
			f = decode->Bdecode(p);
		}
		while(state == Paused)
			sys->sleep(0);
		if (p.ptype == Mpegio->BPIC) {
			writepixels(t, canvr, remap->remap(f));
			if(state == Stepping)
				state = Paused;
		} else {
			if (pf != nil) {
				writepixels(t, canvr, remap->remap(pf));
				if(state == Stepping)
					state = Paused;
			}
			pf = f;
		}
		if ((p = m.getpicture(1)) == nil) {
			writepixels(t, canvr, remap->remap(pf));
			break;
		}
	}
	state = Stopped;
}

writepixels(t: ref Toplevel, r: Rect, b: array of byte)
{
	if (cvt != nil) {
		cvt.writepixels(cvt.r, b);
		t.image.draw(r, cvt, nil, (0, 0));
	} else
		t.image.writepixels(r, b);
}

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;
}