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
10 from Carbon
import QuickTime
12 from Carbon
import Qdoffs
13 from Carbon
import QDOffscreen
14 from Carbon
import Res
18 def _audiodescr(data
):
21 def _audiodescr(data
):
22 return MediaDescr
.SoundDescription
.decode(data
)
24 from imgformat
import macrgb
26 macrgb
= "Macintosh RGB format"
31 def __init__(self
, name
, descr
, width
, height
, format
):
35 self
.__height
= height
36 self
.__format
= format
45 return self
.__width
, self
.__height
51 def __init__(self
, path
):
52 fd
= Qt
.OpenMovieFile(path
, 0)
53 self
.movie
, d1
, d2
= Qt
.NewMovieFromFile(fd
, 0, 0)
54 self
.movietimescale
= self
.movie
.GetMovieTimeScale()
56 self
.audiotrack
= self
.movie
.GetMovieIndTrackType(1,
57 QuickTime
.AudioMediaCharacteristic
, QuickTime
.movieTrackCharacteristic
)
58 self
.audiomedia
= self
.audiotrack
.GetTrackMedia()
60 self
.audiotrack
= self
.audiomedia
= None
63 handle
= Res
.Handle('')
64 n
= self
.audiomedia
.GetMediaSampleDescriptionCount()
65 self
.audiomedia
.GetMediaSampleDescription(1, handle
)
66 self
.audiodescr
= _audiodescr(handle
.data
)
67 self
.audiotimescale
= self
.audiomedia
.GetMediaTimeScale()
71 self
.videotrack
= self
.movie
.GetMovieIndTrackType(1,
72 QuickTime
.VisualMediaCharacteristic
, QuickTime
.movieTrackCharacteristic
)
73 self
.videomedia
= self
.videotrack
.GetTrackMedia()
75 self
.videotrack
= self
.videomedia
= self
.videotimescale
= None
77 self
.videotimescale
= self
.videomedia
.GetMediaTimeScale()
78 x0
, y0
, x1
, y1
= self
.movie
.GetMovieBox()
79 self
.videodescr
= {'width':(x1
-x0
), 'height':(y1
-y0
)}
81 self
.videocurtime
= None
82 self
.audiocurtime
= None
86 self
.audiomedia
= None
87 self
.audiotrack
= None
88 self
.videomedia
= None
89 self
.videotrack
= None
92 def _initgworld(self
):
93 old_port
, old_dev
= Qdoffs
.GetGWorld()
95 movie_w
= self
.videodescr
['width']
96 movie_h
= self
.videodescr
['height']
97 movie_rect
= (0, 0, movie_w
, movie_h
)
98 self
.gworld
= Qdoffs
.NewGWorld(32, movie_rect
, None, None, QDOffscreen
.keepLocal
)
99 self
.pixmap
= self
.gworld
.GetGWorldPixMap()
100 Qdoffs
.LockPixels(self
.pixmap
)
101 Qdoffs
.SetGWorld(self
.gworld
.as_GrafPtr(), None)
102 Qd
.EraseRect(movie_rect
)
103 self
.movie
.SetMovieGWorld(self
.gworld
.as_GrafPtr(), None)
104 self
.movie
.SetMovieBox(movie_rect
)
105 self
.movie
.SetMovieActive(1)
106 self
.movie
.MoviesTask(0)
107 self
.movie
.SetMoviePlayHints(QuickTime
.hintsHighQuality
, QuickTime
.hintsHighQuality
)
110 Qdoffs
.SetGWorld(old_port
, old_dev
)
112 def _gettrackduration_ms(self
, track
):
113 tracktime
= track
.GetTrackDuration()
114 return self
._movietime
_to
_ms
(tracktime
)
116 def _movietime_to_ms(self
, time
):
117 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.movietimescale
, None), 1000)
120 def _videotime_to_ms(self
, time
):
121 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.videotimescale
, None), 1000)
124 def _audiotime_to_ms(self
, time
):
125 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.audiotimescale
, None), 1000)
128 def _videotime_to_movietime(self
, time
):
129 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.videotimescale
, None),
134 return not self
.audiotrack
is None
137 return not self
.videotrack
is None
139 def GetAudioDuration(self
):
140 if not self
.audiotrack
:
142 return self
._gettrackduration
_ms
(self
.audiotrack
)
144 def GetVideoDuration(self
):
145 if not self
.videotrack
:
147 return self
._gettrackduration
_ms
(self
.videotrack
)
149 def GetAudioFormat(self
):
150 if not self
.audiodescr
:
151 return None, None, None, None, None
152 bps
= self
.audiodescr
['sampleSize']
153 nch
= self
.audiodescr
['numChannels']
157 channels
= ['left', 'right']
159 channels
= map(lambda x
: str(x
+1), range(nch
))
161 # Funny bits-per sample. We pretend not to understand
165 # QuickTime is easy (for as far as we support it): samples are always a whole
166 # number of bytes, so frames are nchannels*samplesize, and there's one frame per block.
167 blocksize
= (bps
/8)*nch
169 if self
.audiodescr
['dataFormat'] == 'raw ':
170 encoding
= 'linear-excess'
171 elif self
.audiodescr
['dataFormat'] == 'twos':
172 encoding
= 'linear-signed'
174 encoding
= 'quicktime-coding-%s'%self
.audiodescr
['dataFormat']
175 ## return audio.format.AudioFormatLinear('quicktime_audio', 'QuickTime Audio Format',
176 ## channels, encoding, blocksize=blocksize, fpb=fpb, bps=bps)
177 return channels
, encoding
, blocksize
, fpb
, bps
179 def GetAudioFrameRate(self
):
180 if not self
.audiodescr
:
182 return int(self
.audiodescr
['sampleRate'])
184 def GetVideoFormat(self
):
185 width
= self
.videodescr
['width']
186 height
= self
.videodescr
['height']
187 return VideoFormat('dummy_format', 'Dummy Video Format', width
, height
, macrgb
)
189 def GetVideoFrameRate(self
):
190 tv
= self
.videocurtime
193 flags
= QuickTime
.nextTimeStep|QuickTime
.nextTimeEdgeOK
194 tv
, dur
= self
.videomedia
.GetMediaNextInterestingTime(flags
, tv
, 1.0)
195 dur
= self
._videotime
_to
_ms
(dur
)
196 return int((1000.0/dur
)+0.5)
198 def ReadAudio(self
, nframes
, time
=None):
200 self
.audiocurtime
= time
201 flags
= QuickTime
.nextTimeStep|QuickTime
.nextTimeEdgeOK
202 if self
.audiocurtime
== None:
203 self
.audiocurtime
= 0
204 tv
= self
.audiomedia
.GetMediaNextInterestingTimeOnly(flags
, self
.audiocurtime
, 1.0)
205 if tv
< 0 or (self
.audiocurtime
and tv
< self
.audiocurtime
):
206 return self
._audiotime
_to
_ms
(self
.audiocurtime
), None
208 desc_h
= Res
.Handle('')
209 size
, actualtime
, sampleduration
, desc_index
, actualcount
, flags
= \
210 self
.audiomedia
.GetMediaSample(h
, 0, tv
, desc_h
, nframes
)
211 self
.audiocurtime
= actualtime
+ actualcount
*sampleduration
212 return self
._audiotime
_to
_ms
(actualtime
), h
.data
214 def ReadVideo(self
, time
=None):
216 self
.videocurtime
= time
217 flags
= QuickTime
.nextTimeStep
218 if self
.videocurtime
== None:
219 flags
= flags | QuickTime
.nextTimeEdgeOK
220 self
.videocurtime
= 0
221 tv
= self
.videomedia
.GetMediaNextInterestingTimeOnly(flags
, self
.videocurtime
, 1.0)
222 if tv
< 0 or (self
.videocurtime
and tv
<= self
.videocurtime
):
223 return self
._videotime
_to
_ms
(self
.videocurtime
), None
224 self
.videocurtime
= tv
225 moviecurtime
= self
._videotime
_to
_movietime
(self
.videocurtime
)
226 self
.movie
.SetMovieTimeValue(moviecurtime
)
227 self
.movie
.MoviesTask(0)
228 return self
._videotime
_to
_ms
(self
.videocurtime
), self
._getpixmapcontent
()
230 def _getpixmapcontent(self
):
231 """Shuffle the offscreen PixMap data, because it may have funny stride values"""
232 rowbytes
= Qdoffs
.GetPixRowBytes(self
.pixmap
)
233 width
= self
.videodescr
['width']
234 height
= self
.videodescr
['height']
237 for i
in range(height
):
238 nextline
= Qdoffs
.GetPixMapBytes(self
.pixmap
, start
, width
*4)
239 start
= start
+ rowbytes
258 path
= EasyDialogs
.AskFileForOpen(message
='Video to convert')
259 if not path
: sys
.exit(0)
263 dstdir
= EasyDialogs
.AskFileForSave(message
='Name for output folder')
264 if not dstdir
: sys
.exit(0)
267 videofmt
= rdr
.GetVideoFormat()
268 imgfmt
= videofmt
.getformat()
269 imgw
, imgh
= videofmt
.getsize()
270 timestamp
, data
= rdr
.ReadVideo()
272 fname
= 'frame%04.4d.jpg'%num
274 pname
= os
.path
.join(dstdir
, fname
)
275 if not img
: print 'Not',
276 print 'Writing %s, size %dx%d, %d bytes'%(fname
, imgw
, imgh
, len(data
))
278 wrt
= img
.writer(imgfmt
, pname
)
282 timestamp
, data
= rdr
.ReadVideo()
283 MacOS
.SetCreatorAndType(pname
, 'ogle', 'JPEG')
285 print 'stopping at 20 frames so your disk does not fill up:-)'
287 print 'Total frames:', num
289 if __name__
== '__main__':