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
9 from warnings
import warnpy3k
10 warnpy3k("In 3.x, the videoreader module is removed.", stacklevel
=2)
15 from Carbon
import QuickTime
17 from Carbon
import Qdoffs
18 from Carbon
import QDOffscreen
19 from Carbon
import Res
21 from Carbon
import MediaDescr
23 def _audiodescr(data
):
26 def _audiodescr(data
):
27 return MediaDescr
.SoundDescription
.decode(data
)
29 from imgformat
import macrgb
31 macrgb
= "Macintosh RGB format"
36 def __init__(self
, name
, descr
, width
, height
, format
):
40 self
.__height
= height
41 self
.__format
= format
50 return self
.__width
, self
.__height
56 def __init__(self
, path
):
57 fd
= Qt
.OpenMovieFile(path
, 0)
58 self
.movie
, d1
, d2
= Qt
.NewMovieFromFile(fd
, 0, 0)
59 self
.movietimescale
= self
.movie
.GetMovieTimeScale()
61 self
.audiotrack
= self
.movie
.GetMovieIndTrackType(1,
62 QuickTime
.AudioMediaCharacteristic
, QuickTime
.movieTrackCharacteristic
)
63 self
.audiomedia
= self
.audiotrack
.GetTrackMedia()
65 self
.audiotrack
= self
.audiomedia
= None
68 handle
= Res
.Handle('')
69 n
= self
.audiomedia
.GetMediaSampleDescriptionCount()
70 self
.audiomedia
.GetMediaSampleDescription(1, handle
)
71 self
.audiodescr
= _audiodescr(handle
.data
)
72 self
.audiotimescale
= self
.audiomedia
.GetMediaTimeScale()
76 self
.videotrack
= self
.movie
.GetMovieIndTrackType(1,
77 QuickTime
.VisualMediaCharacteristic
, QuickTime
.movieTrackCharacteristic
)
78 self
.videomedia
= self
.videotrack
.GetTrackMedia()
80 self
.videotrack
= self
.videomedia
= self
.videotimescale
= None
82 self
.videotimescale
= self
.videomedia
.GetMediaTimeScale()
83 x0
, y0
, x1
, y1
= self
.movie
.GetMovieBox()
84 self
.videodescr
= {'width':(x1
-x0
), 'height':(y1
-y0
)}
86 self
.videocurtime
= None
87 self
.audiocurtime
= None
91 self
.audiomedia
= None
92 self
.audiotrack
= None
93 self
.videomedia
= None
94 self
.videotrack
= None
97 def _initgworld(self
):
98 old_port
, old_dev
= Qdoffs
.GetGWorld()
100 movie_w
= self
.videodescr
['width']
101 movie_h
= self
.videodescr
['height']
102 movie_rect
= (0, 0, movie_w
, movie_h
)
103 self
.gworld
= Qdoffs
.NewGWorld(32, movie_rect
, None, None, QDOffscreen
.keepLocal
)
104 self
.pixmap
= self
.gworld
.GetGWorldPixMap()
105 Qdoffs
.LockPixels(self
.pixmap
)
106 Qdoffs
.SetGWorld(self
.gworld
.as_GrafPtr(), None)
107 Qd
.EraseRect(movie_rect
)
108 self
.movie
.SetMovieGWorld(self
.gworld
.as_GrafPtr(), None)
109 self
.movie
.SetMovieBox(movie_rect
)
110 self
.movie
.SetMovieActive(1)
111 self
.movie
.MoviesTask(0)
112 self
.movie
.SetMoviePlayHints(QuickTime
.hintsHighQuality
, QuickTime
.hintsHighQuality
)
115 Qdoffs
.SetGWorld(old_port
, old_dev
)
117 def _gettrackduration_ms(self
, track
):
118 tracktime
= track
.GetTrackDuration()
119 return self
._movietime
_to
_ms
(tracktime
)
121 def _movietime_to_ms(self
, time
):
122 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.movietimescale
, None), 1000)
125 def _videotime_to_ms(self
, time
):
126 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.videotimescale
, None), 1000)
129 def _audiotime_to_ms(self
, time
):
130 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.audiotimescale
, None), 1000)
133 def _videotime_to_movietime(self
, time
):
134 value
, d1
, d2
= Qt
.ConvertTimeScale((time
, self
.videotimescale
, None),
139 return not self
.audiotrack
is None
142 return not self
.videotrack
is None
144 def GetAudioDuration(self
):
145 if not self
.audiotrack
:
147 return self
._gettrackduration
_ms
(self
.audiotrack
)
149 def GetVideoDuration(self
):
150 if not self
.videotrack
:
152 return self
._gettrackduration
_ms
(self
.videotrack
)
154 def GetAudioFormat(self
):
155 if not self
.audiodescr
:
156 return None, None, None, None, None
157 bps
= self
.audiodescr
['sampleSize']
158 nch
= self
.audiodescr
['numChannels']
162 channels
= ['left', 'right']
164 channels
= map(lambda x
: str(x
+1), range(nch
))
166 # Funny bits-per sample. We pretend not to understand
170 # QuickTime is easy (for as far as we support it): samples are always a whole
171 # number of bytes, so frames are nchannels*samplesize, and there's one frame per block.
172 blocksize
= (bps
/8)*nch
174 if self
.audiodescr
['dataFormat'] == 'raw ':
175 encoding
= 'linear-excess'
176 elif self
.audiodescr
['dataFormat'] == 'twos':
177 encoding
= 'linear-signed'
179 encoding
= 'quicktime-coding-%s'%self
.audiodescr
['dataFormat']
180 ## return audio.format.AudioFormatLinear('quicktime_audio', 'QuickTime Audio Format',
181 ## channels, encoding, blocksize=blocksize, fpb=fpb, bps=bps)
182 return channels
, encoding
, blocksize
, fpb
, bps
184 def GetAudioFrameRate(self
):
185 if not self
.audiodescr
:
187 return int(self
.audiodescr
['sampleRate'])
189 def GetVideoFormat(self
):
190 width
= self
.videodescr
['width']
191 height
= self
.videodescr
['height']
192 return VideoFormat('dummy_format', 'Dummy Video Format', width
, height
, macrgb
)
194 def GetVideoFrameRate(self
):
195 tv
= self
.videocurtime
198 flags
= QuickTime
.nextTimeStep|QuickTime
.nextTimeEdgeOK
199 tv
, dur
= self
.videomedia
.GetMediaNextInterestingTime(flags
, tv
, 1.0)
200 dur
= self
._videotime
_to
_ms
(dur
)
201 return int((1000.0/dur
)+0.5)
203 def ReadAudio(self
, nframes
, time
=None):
205 self
.audiocurtime
= time
206 flags
= QuickTime
.nextTimeStep|QuickTime
.nextTimeEdgeOK
207 if self
.audiocurtime
is None:
208 self
.audiocurtime
= 0
209 tv
= self
.audiomedia
.GetMediaNextInterestingTimeOnly(flags
, self
.audiocurtime
, 1.0)
210 if tv
< 0 or (self
.audiocurtime
and tv
< self
.audiocurtime
):
211 return self
._audiotime
_to
_ms
(self
.audiocurtime
), None
213 desc_h
= Res
.Handle('')
214 size
, actualtime
, sampleduration
, desc_index
, actualcount
, flags
= \
215 self
.audiomedia
.GetMediaSample(h
, 0, tv
, desc_h
, nframes
)
216 self
.audiocurtime
= actualtime
+ actualcount
*sampleduration
217 return self
._audiotime
_to
_ms
(actualtime
), h
.data
219 def ReadVideo(self
, time
=None):
221 self
.videocurtime
= time
222 flags
= QuickTime
.nextTimeStep
223 if self
.videocurtime
is None:
224 flags
= flags | QuickTime
.nextTimeEdgeOK
225 self
.videocurtime
= 0
226 tv
= self
.videomedia
.GetMediaNextInterestingTimeOnly(flags
, self
.videocurtime
, 1.0)
227 if tv
< 0 or (self
.videocurtime
and tv
<= self
.videocurtime
):
228 return self
._videotime
_to
_ms
(self
.videocurtime
), None
229 self
.videocurtime
= tv
230 moviecurtime
= self
._videotime
_to
_movietime
(self
.videocurtime
)
231 self
.movie
.SetMovieTimeValue(moviecurtime
)
232 self
.movie
.MoviesTask(0)
233 return self
._videotime
_to
_ms
(self
.videocurtime
), self
._getpixmapcontent
()
235 def _getpixmapcontent(self
):
236 """Shuffle the offscreen PixMap data, because it may have funny stride values"""
237 rowbytes
= Qdoffs
.GetPixRowBytes(self
.pixmap
)
238 width
= self
.videodescr
['width']
239 height
= self
.videodescr
['height']
242 for i
in range(height
):
243 nextline
= Qdoffs
.GetPixMapBytes(self
.pixmap
, start
, width
*4)
244 start
= start
+ rowbytes
258 from PIL
import Image
263 path
= EasyDialogs
.AskFileForOpen(message
='Video to convert')
264 if not path
: sys
.exit(0)
268 dstdir
= EasyDialogs
.AskFileForSave(message
='Name for output folder')
269 if not dstdir
: sys
.exit(0)
272 videofmt
= rdr
.GetVideoFormat()
273 imgfmt
= videofmt
.getformat()
274 imgw
, imgh
= videofmt
.getsize()
275 timestamp
, data
= rdr
.ReadVideo()
277 fname
= 'frame%04.4d.jpg'%num
279 pname
= os
.path
.join(dstdir
, fname
)
280 if not Image
: print 'Not',
281 print 'Writing %s, size %dx%d, %d bytes'%(fname
, imgw
, imgh
, len(data
))
283 img
= Image
.fromstring("RGBA", (imgw
, imgh
), data
)
284 img
.save(pname
, 'JPEG')
285 timestamp
, data
= rdr
.ReadVideo()
286 MacOS
.SetCreatorAndType(pname
, 'ogle', 'JPEG')
288 print 'stopping at 20 frames so your disk does not fill up:-)'
290 print 'Total frames:', num
292 if __name__
== '__main__':