blob: 0bab8fade294777838bbc6041fcc3e839d6886ad [file] [log] [blame]
Guido van Rossum094a9de1991-12-03 16:50:00 +00001# VFile -- two classes for Video Files.
2# VinFile -- for video input.
3# VoutFile -- for video output.
4
5import sys
6import gl
7import GL
8
9Error = 'VFile.Error' # Exception
10
11
12# Class VinFile represents a video file used for input.
13#
14# It has the following methods:
15# init(filename)
16# initfp(fp, filename)
17# rewind()
18# getframe()
19# skipframe()
20#
21# The following read-only data members provide public information:
22# version
23# filename
24# width, height
25# packfactor
26# ybits, ibits, qbits, chrompack
27#
28# These writable data members provide additional parametrization:
29# xorigin, yorigin
30
31class VinFile():
32
33 # init() and initfp() raise Error if the header is bad.
34 # init() raises whatever open() raises if the file can't be opened.
35
36 def init(self, filename):
37 if filename = '-':
38 return self.initfp(sys.stdin, filename)
39 return self.initfp(open(filename, 'r'), filename)
40
41 def initfp(self, (fp, filename)):
42 self.colormapinited = 0
43 self.magnify = 1.0
44 self.xorigin = self.yorigin = 0
45 self.fp = fp
46 self.filename = filename
47 #
48 line = self.fp.readline()
49 if line = 'CMIF video 1.0\n':
50 self.version = 1.0
51 elif line = 'CMIF video 2.0\n':
52 self.version = 2.0
53 else:
54 raise Error, self.filename + ': bad video format'
55 #
56 if self.version < 2.0:
57 self.ybits, self.ibits, self.qbits = 8, 0, 0
58 self.chrompack = 0
59 else:
60 line = self.fp.readline()
61 try:
62 self.ybits, self.ibits, self.qbits, \
63 self.chrompack = eval(line[:-1])
64 except:
65 raise Error, self.filename + ': bad color info'
66 #
67 line = self.fp.readline()
68 try:
69 x = eval(line[:-1])
70 if self.version > 1.0 or len(x) = 3:
71 self.width, self.height, self.packfactor = x
72 else:
73 sef.width, self.height = x
74 self.packfactor = 2
75 except:
76 raise Error, self.filename + ': bad (w,h,pf) info'
77 #
78 return self
79
80 # rewind() raises Error if the header is bad (which can only
81 # happen if the file was written to since opened).
82
83 def rewind(self):
84 self.fp.seek(0)
85 x = self.initfp(self.fp, self.filename)
86
87 # getnextframe() raises EOFError (built-in) if there is no next frame,
88 # or if the next frame is broken.
89 # So to getnextframeheader(), getnextframedata() and skipnextframe().
90
91 def getnextframe(self):
92 time, size, chromsize = self.getnextframeheader()
93 data, chromdata = self.getnextframedata(size, chromsize)
94 return time, data, chromdata
95
96 def getnextframedata(self, (size, chromsize)):
97 data = self.fp.read(size)
98 if len(data) <> size: raise EOFError
99 if chromsize:
100 chromdata = self.fp.read(chromsize)
101 if len(chromdata) <> chromsize: raise EOFError
102 else:
103 chromdata = None
104 #
105 return data, chromdata
106
107 def skipnextframe(self):
108 time, size, chromsize = self.getnextframeheader()
109 self.skipnextframedata(size, chromsize)
110 return time
111
112 def skipnextframedata(self, (size, chromsize)):
113 # Note that this won't raise EOFError for a partial frame.
114 try:
115 self.fp.seek(size + chromsize, 1) # Relative seek
116 except:
117 # Assume it's a pipe -- read the data to discard it
118 dummy = self.fp.read(size)
119 dummy = self.fp.read(chromsize)
120
121 def getnextframeheader(self):
122 line = self.fp.readline()
123 if not line:
124 raise EOFError
125 #
126 w, h, pf = self.width, self.height, self.packfactor
127 try:
128 x = eval(line[:-1])
129 if type(x) in (type(0), type(0.0)):
130 time = x
131 if pf = 0:
132 size = w * h * 4
133 else:
134 size = (w/pf) * (h/pf)
135 elif len(x) = 2:
136 time, size = x
137 cp = self.chrompack
138 if cp:
139 cw = (w + cp - 1) / cp
140 ch = (h + cp - 1) / cp
141 chromsize = 2 * cw * ch
142 else:
143 chromsize = 0
144 else:
145 time, size, chromsize = x
146 except:
147 raise Error, self.filename + ': bad frame header'
148 return time, size, chromsize
149
150 def shownextframe(self):
151 time, data, chromdata = self.getnextframe()
152 self.showframe(data, chromdata)
153 return time
154
155 def showframe(self, (data, chromdata)):
156 w, h, pf = self.width, self.height, self.packfactor
157 factor = self.magnify
158 if pf: factor = factor * pf
159 if chromdata:
160 cp = self.chrompack
161 cw = (w+cp-1)/cp
162 ch = (h+cp-1)/cp
163 gl.rectzoom(factor*cp, factor*cp)
164 gl.pixmode(GL.PM_SIZE, 16)
165 gl.writemask(0x7ff - ((1 << self.ybits) - 1))
166 gl.lrectwrite(self.xorigin, self.yorigin, \
167 self.xorigin + cw - 1, self.yorigin + ch - 1, \
168 chromdata)
169 #
170 if pf:
171 if not self.colormapinited:
172 self.initcolormap()
173 gl.writemask((1 << self.ybits) - 1)
174 gl.pixmode(GL.PM_SIZE, 8)
175 gl.rectzoom(factor, factor)
176 w = w/pf
177 h = h/pf
178 gl.lrectwrite(self.xorigin, self.yorigin, \
179 self.xorigin + w - 1, self.yorigin + h - 1, data)
180
181 def initcolormap(self):
182 initcmap(self.ybits, self.ibits, self.qbits, self.chrompack)
183 gl.color(0x800)
184 gl.clear()
185 self.colormapinited = 1
186
187
188def initcmap(ybits, ibits, qbits, chrompack):
189 if ybits+ibits+qbits > 11:
190 raise Error, 'Sorry, 11 bits max'
191 import colorsys
192 maxy = 1 << ybits
193 maxi = 1 << ibits
194 maxq = 1 << qbits
195 for i in range(2048, 4096-256):
196 gl.mapcolor(i, 0, 255, 0)
197 for y in range(maxy):
198 yv = y/float(maxy-1)
199 for i in range(maxi):
200 if maxi = 1:
201 iv = 0
202 else:
203 iv = (i/float(maxi-1))-0.5
204 for q in range(maxq):
205 if maxq = 1:
206 qv = 0
207 else:
208 qv = (q/float(maxq-1))-0.5
209 index = 2048 + y + \
210 (i << ybits) + (q << (ybits+ibits))
211 rv, gv, bv = colorsys.yiq_to_rgb(yv, iv, qv)
212 r, g, b = \
213 int(rv*255.0), int(gv*255.0), int(bv*255.0)
214 if index < 4096 - 256:
215 gl.mapcolor(index, r, g, b)
216
217
218def test():
219 import sys
220 filename = 'film.video'
221 if sys.argv[1:]: filename = sys.argv[1]
222 vin = VinFile().init(filename)
223 gl.foreground()
224 gl.prefsize(vin.width, vin.height)
225 wid = gl.winopen('VFile.test: ' + filename)
226 try:
227 while 1:
228 t = vin.shownextframe()
229 except EOFError:
230 pass