Created Vedit.py, the video editor.  This uses the classes in Viewer.py.
Viewer.py in turn requires changes to VFile.py (unfortunately that file
is now a complete mess...).
diff --git a/Demo/sgi/video/Viewer.py b/Demo/sgi/video/Viewer.py
new file mode 100755
index 0000000..6203562
--- /dev/null
+++ b/Demo/sgi/video/Viewer.py
@@ -0,0 +1,242 @@
+import gl, GL
+import VFile
+import os
+
+
+class InputViewer:
+
+	def init(self, filename, title, qsize):
+		try:
+			self.vin = VFile.VinFile().init(filename)
+		except (EOFError, VFile.Error):
+			raise IOError, 'bad video input file'
+		if not title:
+			title = os.path.split(filename)[1]
+		self.filename = filename
+		self.title = title
+		self.qsize = qsize
+		gl.foreground()
+		gl.prefsize(self.vin.width, self.vin.height)
+		self.wid = -1
+		self.reset()
+		return self
+
+	def close(self):
+		self.vin.close()
+		if self.wid > 0:
+			gl.winclose(self.wid)
+
+	def rewind(self):
+		self.vin.rewind()
+		self.reset()
+
+	def getinfo(self):
+		return self.vin.getinfo()
+
+	# Internal
+	def reset(self):
+		if self.wid > 0:
+			gl.winset(self.wid)
+			gl.clear()
+			self.vin.initcolormap()
+		self.queue = []
+		self.qindex = 0
+		self.lost = 0
+		self.lastt = 0
+		self.eofread = 0
+
+	# Internal
+	def fillq(self):
+		if self.qindex < len(self.queue) or self.eofread: return
+		try:
+			t, d, cd = self.vin.getnextframe()
+		except EOFError:
+			self.eofread = 1
+			return
+		dt = t - self.lastt
+		self.lastt = t
+		self.queue.append(dt, d, cd)
+		while len(self.queue) > self.qsize:
+			del self.queue[0]
+			self.qindex = self.qindex - 1
+			self.lost = self.lost + 1
+
+	def show(self):
+		if self.wid < 0:
+			gl.foreground()
+			gl.prefsize(self.vin.width, self.vin.height)
+			self.wid = gl.winopen(self.title)
+			gl.clear()
+			self.vin.initcolormap()
+		self.fillq()
+		gl.winset(self.wid)
+		if self.qindex >= len(self.queue):
+			gl.clear()
+			return
+		dt, d, cd = self.queue[self.qindex]
+		self.vin.showframe(d, cd)
+
+	def redraw(self, wid):
+		if wid == self.wid >= 0:
+			gl.winset(self.wid)
+			gl.reshapeviewport()
+			self.show()
+
+	def get(self):
+		if self.qindex >= len(self.queue):
+			self.fillq()
+			if self.eofread:
+				return None
+		item = self.queue[self.qindex]
+		self.qindex = self.qindex + 1
+		return item
+
+	def backup(self):
+		if self.qindex == 0:
+			return 0
+		self.qindex = self.qindex - 1
+		return 1
+
+	def tell(self):
+		return self.lost + self.qindex
+
+	def qsizes(self):
+		return self.qindex, len(self.queue) - self.qindex
+
+
+class OutputViewer:
+
+	def init(self, filename, title, qsize):
+		try:
+			self.vout = VFile.VoutFile().init(filename)
+		except (EOFError, VFile.Error):
+			raise IOError, 'bad video output file'
+		if not title:
+			title = os.path.split(filename)[1]
+		self.filename = filename
+		self.title = title
+		self.qsize = qsize
+		gl.foreground()
+		self.wid = -1
+		self.reset()
+		return self
+
+	def close(self):
+		while self.queue:
+			self.flushq()
+		self.vout.close()
+		if self.wid > 0:
+			gl.winclose(self.wid)
+
+	def rewind(self):
+		info = self.vout.getinfo()
+		self.vout.close()
+		self.vout = VFile.VoutFile().init(self.filename)
+		self.vout.setinfo(info)
+		self.reset()
+
+	def getinfo(self):
+		return self.vout.getinfo()
+
+	def setinfo(self, info):
+		if info == self.getinfo(): return # No change
+		self.vout.setinfo(info)
+		if self.wid > 0:
+			gl.winclose(self.wid)
+			self.wid = -1
+
+	# Internal
+	def reset(self):
+		if self.wid > 0:
+			gl.winset(self.wid)
+			gl.clear()
+			self.vout.initcolormap()
+		self.queue = []
+		self.spares = []
+		self.written = 0
+		self.lastt = 0
+
+	# Internal
+	def flushq(self):
+		if self.written == 0:
+			self.vout.writeheader()
+		dt, d, cd = self.queue[0]
+		self.lastt = self.lastt + dt
+		self.vout.writeframe(self.lastt, d, cd)
+		del self.queue[0]
+		self.written = self.written + 1
+
+	def show(self):
+		if self.wid < 0:
+			gl.foreground()
+			gl.prefsize(self.vout.width, self.vout.height)
+			self.wid = gl.winopen(self.title)
+			gl.clear()
+			self.vout.initcolormap()
+		gl.winset(self.wid)
+		if not self.queue:
+			gl.clear()
+			return
+		dt, d, cd = self.queue[-1]
+		self.vout.showframe(d, cd)
+
+	def redraw(self, wid):
+		if wid == self.wid >= 0:
+			gl.winset(self.wid)
+			gl.reshapeviewport()
+			self.show()
+
+	def backup(self):
+		if len(self.queue) < 1: return 0
+		self.spares.insert(0, self.queue[-1])
+		del self.queue[-1]
+		return 1
+
+	def forward(self):
+		if not self.spares: return 0
+		self.queue.append(self.spares[0])
+		del self.spares[0]
+		return 1
+
+	def put(self, item):
+		self.queue.append(item)
+		self.spares = []
+		while len(self.queue) > self.qsize:
+			self.flushq()
+
+	def tell(self):
+		return self.written + len(self.queue)
+
+	def qsizes(self):
+		return len(self.queue), len(self.spares)
+
+
+def test():
+	import sys
+	a = InputViewer().init(sys.argv[1], '')
+	b = OutputViewer().init(sys.argv[2], '')
+	b.setinfo(a.getinfo())
+	
+	while 1:
+		a.show()
+		data = a.get()
+		if data is None:
+			break
+		b.put(data)
+		b.show()
+
+	while a.backup():
+		data = a.get()
+		b.put(data)
+		b.show()
+		if a.backup(): a.show()
+	
+	while 1:
+		data = a.get()
+		if data is None:
+			break
+		b.put(data)
+		b.show()
+		a.show()
+
+	b.close()