Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a647f57

Browse files
committed
Read a QuickTime movie and return the frames.
1 parent 344ecdb commit a647f57

1 file changed

Lines changed: 276 additions & 0 deletions

File tree

Mac/Lib/videoreader.py

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
# Video file reader, using QuickTime
2+
#
3+
# This module was quickly ripped out of another software package, so there is a good
4+
# chance that it does not work as-is and it needs some hacking.
5+
#
6+
# Jack Jansen, August 2000
7+
#
8+
import sys
9+
import Qt
10+
import QuickTime
11+
import Qd
12+
import Qdoffs
13+
import QDOffscreen
14+
import Res
15+
import MediaDescr
16+
import imgformat
17+
import os
18+
# import audio.format
19+
import macfs
20+
21+
class VideoFormat:
22+
def __init__(self, name, descr, width, height, format):
23+
self.__name = name
24+
self.__descr = descr
25+
self.__width = width
26+
self.__height = height
27+
self.__format = format
28+
29+
def getname(self):
30+
return self.__name
31+
32+
def getdescr(self):
33+
return self.__descr
34+
35+
def getsize(self):
36+
return self.__width, self.__height
37+
38+
def getformat(self):
39+
return self.__format
40+
41+
class _Reader:
42+
def __init__(self, path):
43+
fsspec = macfs.FSSpec(path)
44+
fd = Qt.OpenMovieFile(fsspec, 0)
45+
self.movie, d1, d2 = Qt.NewMovieFromFile(fd, 0, 0)
46+
self.movietimescale = self.movie.GetMovieTimeScale()
47+
try:
48+
self.audiotrack = self.movie.GetMovieIndTrackType(1,
49+
QuickTime.AudioMediaCharacteristic, QuickTime.movieTrackCharacteristic)
50+
self.audiomedia = self.audiotrack.GetTrackMedia()
51+
except Qt.Error:
52+
self.audiotrack = self.audiomedia = None
53+
self.audiodescr = {}
54+
else:
55+
handle = Res.Handle('')
56+
n = self.audiomedia.GetMediaSampleDescriptionCount()
57+
self.audiomedia.GetMediaSampleDescription(1, handle)
58+
self.audiodescr = MediaDescr.SoundDescription.decode(handle.data)
59+
self.audiotimescale = self.audiomedia.GetMediaTimeScale()
60+
del handle
61+
62+
try:
63+
self.videotrack = self.movie.GetMovieIndTrackType(1,
64+
QuickTime.VisualMediaCharacteristic, QuickTime.movieTrackCharacteristic)
65+
self.videomedia = self.videotrack.GetTrackMedia()
66+
except Qt.Error:
67+
self.videotrack = self.videomedia = self.videotimescale = None
68+
if self.videotrack:
69+
self.videotimescale = self.videomedia.GetMediaTimeScale()
70+
x0, y0, x1, y1 = self.movie.GetMovieBox()
71+
self.videodescr = {'width':(x1-x0), 'height':(y1-y0)}
72+
self._initgworld()
73+
self.videocurtime = None
74+
self.audiocurtime = None
75+
76+
77+
def __del__(self):
78+
self.audiomedia = None
79+
self.audiotrack = None
80+
self.videomedia = None
81+
self.videotrack = None
82+
self.movie = None
83+
84+
def _initgworld(self):
85+
old_port, old_dev = Qdoffs.GetGWorld()
86+
try:
87+
movie_w = self.videodescr['width']
88+
movie_h = self.videodescr['height']
89+
movie_rect = (0, 0, movie_w, movie_h)
90+
self.gworld = Qdoffs.NewGWorld(32, movie_rect, None, None, QDOffscreen.keepLocal)
91+
self.pixmap = self.gworld.GetGWorldPixMap()
92+
Qdoffs.LockPixels(self.pixmap)
93+
Qdoffs.SetGWorld(self.gworld.as_GrafPtr(), None)
94+
Qd.EraseRect(movie_rect)
95+
self.movie.SetMovieGWorld(self.gworld.as_GrafPtr(), None)
96+
self.movie.SetMovieBox(movie_rect)
97+
self.movie.SetMovieActive(1)
98+
self.movie.MoviesTask(0)
99+
self.movie.SetMoviePlayHints(QuickTime.hintsHighQuality, QuickTime.hintsHighQuality)
100+
# XXXX framerate
101+
finally:
102+
Qdoffs.SetGWorld(old_port, old_dev)
103+
104+
def _gettrackduration_ms(self, track):
105+
tracktime = track.GetTrackDuration()
106+
return self._movietime_to_ms(tracktime)
107+
108+
def _movietime_to_ms(self, time):
109+
value, d1, d2 = Qt.ConvertTimeScale((time, self.movietimescale, None), 1000)
110+
return value
111+
112+
def _videotime_to_ms(self, time):
113+
value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None), 1000)
114+
return value
115+
116+
def _audiotime_to_ms(self, time):
117+
value, d1, d2 = Qt.ConvertTimeScale((time, self.audiotimescale, None), 1000)
118+
return value
119+
120+
def _videotime_to_movietime(self, time):
121+
value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None),
122+
self.movietimescale)
123+
return value
124+
125+
def HasAudio(self):
126+
return not self.audiotrack is None
127+
128+
def HasVideo(self):
129+
return not self.videotrack is None
130+
131+
def GetAudioDuration(self):
132+
if not self.audiotrack:
133+
return 0
134+
return self._gettrackduration_ms(self.audiotrack)
135+
136+
def GetVideoDuration(self):
137+
if not self.videotrack:
138+
return 0
139+
return self._gettrackduration_ms(self.videotrack)
140+
141+
def GetAudioFormat(self):
142+
bps = self.audiodescr['sampleSize']
143+
nch = self.audiodescr['numChannels']
144+
if nch == 1:
145+
channels = ['mono']
146+
elif nch == 2:
147+
channels = ['left', 'right']
148+
else:
149+
channels = map(lambda x: str(x+1), range(nch))
150+
if bps % 8:
151+
# Funny bits-per sample. We pretend not to understand
152+
blocksize = 0
153+
fpb = 0
154+
else:
155+
# QuickTime is easy (for as far as we support it): samples are always a whole
156+
# number of bytes, so frames are nchannels*samplesize, and there's one frame per block.
157+
blocksize = (bps/8)*nch
158+
fpb = 1
159+
if self.audiodescr['dataFormat'] == 'raw ':
160+
encoding = 'linear-excess'
161+
elif self.audiodescr['dataFormat'] == 'twos':
162+
encoding = 'linear-signed'
163+
else:
164+
encoding = 'quicktime-coding-%s'%self.audiodescr['dataFormat']
165+
## return audio.format.AudioFormatLinear('quicktime_audio', 'QuickTime Audio Format',
166+
## channels, encoding, blocksize=blocksize, fpb=fpb, bps=bps)
167+
return channels, encoding, blocksize, fpb, bps
168+
169+
def GetAudioFrameRate(self):
170+
return int(self.audiodescr['sampleRate'])
171+
172+
def GetVideoFormat(self):
173+
width = self.videodescr['width']
174+
height = self.videodescr['height']
175+
return VideoFormat('dummy_format', 'Dummy Video Format', width, height, imgformat.macrgb)
176+
177+
def GetVideoFrameRate(self):
178+
tv = self.videocurtime
179+
if tv == None:
180+
tv = 0
181+
flags = QuickTime.nextTimeStep|QuickTime.nextTimeEdgeOK
182+
tv, dur = self.videomedia.GetMediaNextInterestingTime(flags, tv, 1.0)
183+
dur = self._videotime_to_ms(dur)
184+
return int((1000.0/dur)+0.5)
185+
186+
def ReadAudio(self, nframes, time=None):
187+
if not time is None:
188+
self.audiocurtime = time
189+
flags = QuickTime.nextTimeStep|QuickTime.nextTimeEdgeOK
190+
if self.audiocurtime == None:
191+
self.audiocurtime = 0
192+
tv = self.audiomedia.GetMediaNextInterestingTimeOnly(flags, self.audiocurtime, 1.0)
193+
if tv < 0 or (self.audiocurtime and tv < self.audiocurtime):
194+
return self._audiotime_to_ms(self.audiocurtime), None
195+
h = Res.Handle('')
196+
desc_h = Res.Handle('')
197+
size, actualtime, sampleduration, desc_index, actualcount, flags = \
198+
self.audiomedia.GetMediaSample(h, 0, tv, desc_h, nframes)
199+
self.audiocurtime = actualtime + actualcount*sampleduration
200+
return self._audiotime_to_ms(actualtime), h.data
201+
202+
def ReadVideo(self, time=None):
203+
if not time is None:
204+
self.videocurtime = time
205+
flags = QuickTime.nextTimeStep
206+
if self.videocurtime == None:
207+
flags = flags | QuickTime.nextTimeEdgeOK
208+
self.videocurtime = 0
209+
tv = self.videomedia.GetMediaNextInterestingTimeOnly(flags, self.videocurtime, 1.0)
210+
if tv < 0 or (self.videocurtime and tv <= self.videocurtime):
211+
return self._videotime_to_ms(self.videocurtime), None
212+
self.videocurtime = tv
213+
moviecurtime = self._videotime_to_movietime(self.videocurtime)
214+
self.movie.SetMovieTimeValue(moviecurtime)
215+
self.movie.MoviesTask(0)
216+
return self._videotime_to_ms(self.videocurtime), self._getpixmapcontent()
217+
218+
def _getpixmapcontent(self):
219+
"""Shuffle the offscreen PixMap data, because it may have funny stride values"""
220+
rowbytes = Qdoffs.GetPixRowBytes(self.pixmap)
221+
width = self.videodescr['width']
222+
height = self.videodescr['height']
223+
start = 0
224+
rv = ''
225+
for i in range(height):
226+
nextline = Qdoffs.GetPixMapBytes(self.pixmap, start, width*4)
227+
start = start + rowbytes
228+
rv = rv + nextline
229+
return rv
230+
231+
def reader(url):
232+
try:
233+
rdr = _Reader(url)
234+
except IOError:
235+
return None
236+
return rdr
237+
238+
def _test():
239+
import img
240+
import MacOS
241+
Qt.EnterMovies()
242+
fss, ok = macfs.PromptGetFile('Video to convert')
243+
if not ok: sys.exit(0)
244+
path = fss.as_pathname()
245+
rdr = reader(path)
246+
if not rdr:
247+
sys.exit(1)
248+
dstfss, ok = macfs.StandardPutFile('Name for output folder')
249+
if not ok: sys.exit(0)
250+
dstdir = dstfss.as_pathname()
251+
num = 0
252+
os.mkdir(dstdir)
253+
videofmt = rdr.GetVideoFormat()
254+
imgfmt = videofmt.getformat()
255+
imgw, imgh = videofmt.getsize()
256+
timestamp, data = rdr.ReadVideo()
257+
while data:
258+
fname = 'frame%04.4d.jpg'%num
259+
num = num+1
260+
pname = os.path.join(dstdir, fname)
261+
print 'Writing', fname, imgw, imgh, len(data)
262+
wrt = img.writer(imgfmt, pname)
263+
wrt.width = imgw
264+
wrt.height = imgh
265+
wrt.write(data)
266+
timestamp, data = rdr.ReadVideo()
267+
MacOS.SetCreatorAndType(pname, 'ogle', 'JPEG')
268+
if num > 20:
269+
print 'stopping at 20 frames so your disk does not fill up:-)'
270+
break
271+
print 'Total frames:', num
272+
273+
if __name__ == '__main__':
274+
_test()
275+
sys.exit(1)
276+

0 commit comments

Comments
 (0)