1 # Video file reader, using QuickTime
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.
6 # Jack Jansen, August 2000
22 def __init__(self
, name
, descr
, width
, height
, format
):
26 self
.__height
= height
27 self
.__format
= format
36 return self
.__width
, self
.__height
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()
48 self
.audiotrack
= self
.movie
.GetMovieIndTrackType(1,
49 QuickTime
.AudioMediaCharacteristic
, QuickTime
.movieTrackCharacteristic
)
50 self
.audiomedia
= self
.audiotrack
.GetTrackMedia()
52 self
.audiotrack
= self
.audiomedia
= None
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()
63 self
.videotrack
= self
.movie
.GetMovieIndTrackType(1,
64 QuickTime
.VisualMediaCharacteristic
, QuickTime
.movieTrackCharacteristic
)
65 self
.videomedia
= self
.videotrack
.GetTrackMedia()
67 self
.videotrack
= self
.videomedia
= self
.videotimescale
= None
69 self
.videotimescale
= self
.videomedia
.GetMediaTimeScale()
70 x0
, y0
, x1
, y1
= self
.movie
.GetMovieBox()
71 self
.videodescr
= {'width':(x1
-x0
), 'height':(y1
-y0
)}
73 self
.videocurtime
= None
74 self
.audiocurtime
= None
78 self
.audiomedia
= None
79 self
.audiotrack
= None
80 self
.videomedia
= None
81 self
.videotrack
= None
84 def _initgworld(self
):
85 old_port
, old_dev
= Qdoffs
.GetGWorld()
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
)
102 Qdoffs
.SetGWorld(old_port
, old_dev
)
104 def _gettrackduration_ms(self
, track
):
105 tracktime
= track
.GetTrackDuration()
106 return self
._movietime
_to
_ms
(tracktime
)
108 def _movietime_to_ms(self
, time
):
109 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.movietimescale
, None), 1000)
112 def _videotime_to_ms(self
, time
):
113 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.videotimescale
, None), 1000)
116 def _audiotime_to_ms(self
, time
):
117 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.audiotimescale
, None), 1000)
120 def _videotime_to_movietime(self
, time
):
121 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.videotimescale
, None),
126 return not self
.audiotrack
is None
129 return not self
.videotrack
is None
131 def GetAudioDuration(self
):
132 if not self
.audiotrack
:
134 return self
._gettrackduration
_ms
(self
.audiotrack
)
136 def GetVideoDuration(self
):
137 if not self
.videotrack
:
139 return self
._gettrackduration
_ms
(self
.videotrack
)
141 def GetAudioFormat(self
):
142 bps
= self
.audiodescr
['sampleSize']
143 nch
= self
.audiodescr
['numChannels']
147 channels
= ['left', 'right']
149 channels
= map(lambda x
: str(x
+1), range(nch
))
151 # Funny bits-per sample. We pretend not to understand
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
159 if self
.audiodescr
['dataFormat'] == 'raw ':
160 encoding
= 'linear-excess'
161 elif self
.audiodescr
['dataFormat'] == 'twos':
162 encoding
= 'linear-signed'
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
169 def GetAudioFrameRate(self
):
170 return int(self
.audiodescr
['sampleRate'])
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
)
177 def GetVideoFrameRate(self
):
178 tv
= self
.videocurtime
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)
186 def ReadAudio(self
, nframes
, time
=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
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
202 def ReadVideo(self
, time
=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
()
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']
225 for i
in range(height
):
226 nextline
= Qdoffs
.GetPixMapBytes(self
.pixmap
, start
, width
*4)
227 start
= start
+ rowbytes
242 fss
, ok
= macfs
.PromptGetFile('Video to convert')
243 if not ok
: sys
.exit(0)
244 path
= fss
.as_pathname()
248 dstfss
, ok
= macfs
.StandardPutFile('Name for output folder')
249 if not ok
: sys
.exit(0)
250 dstdir
= dstfss
.as_pathname()
253 videofmt
= rdr
.GetVideoFormat()
254 imgfmt
= videofmt
.getformat()
255 imgw
, imgh
= videofmt
.getsize()
256 timestamp
, data
= rdr
.ReadVideo()
258 fname
= 'frame%04.4d.jpg'%num
260 pname
= os
.path
.join(dstdir
, fname
)
261 print 'Writing', fname
, imgw
, imgh
, len(data
)
262 wrt
= img
.writer(imgfmt
, pname
)
266 timestamp
, data
= rdr
.ReadVideo()
267 MacOS
.SetCreatorAndType(pname
, 'ogle', 'JPEG')
269 print 'stopping at 20 frames so your disk does not fill up:-)'
271 print 'Total frames:', num
273 if __name__
== '__main__':