source: python/trunk/Lib/plat-mac/videoreader.py

Last change on this file was 391, checked in by dmik, 11 years ago

python: Merge vendor 2.7.6 to trunk.

  • Property svn:eol-style set to native
File size: 10.2 KB
Line 
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
9from warnings import warnpy3k
10warnpy3k("In 3.x, the videoreader module is removed.", stacklevel=2)
11
12
13import sys
14from Carbon import Qt
15from Carbon import QuickTime
16from Carbon import Qd
17from Carbon import Qdoffs
18from Carbon import QDOffscreen
19from Carbon import Res
20try:
21 from Carbon import MediaDescr
22except ImportError:
23 def _audiodescr(data):
24 return None
25else:
26 def _audiodescr(data):
27 return MediaDescr.SoundDescription.decode(data)
28try:
29 from imgformat import macrgb
30except ImportError:
31 macrgb = "Macintosh RGB format"
32import os
33# import audio.format
34
35class VideoFormat:
36 def __init__(self, name, descr, width, height, format):
37 self.__name = name
38 self.__descr = descr
39 self.__width = width
40 self.__height = height
41 self.__format = format
42
43 def getname(self):
44 return self.__name
45
46 def getdescr(self):
47 return self.__descr
48
49 def getsize(self):
50 return self.__width, self.__height
51
52 def getformat(self):
53 return self.__format
54
55class _Reader:
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()
60 try:
61 self.audiotrack = self.movie.GetMovieIndTrackType(1,
62 QuickTime.AudioMediaCharacteristic, QuickTime.movieTrackCharacteristic)
63 self.audiomedia = self.audiotrack.GetTrackMedia()
64 except Qt.Error:
65 self.audiotrack = self.audiomedia = None
66 self.audiodescr = {}
67 else:
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()
73 del handle
74
75 try:
76 self.videotrack = self.movie.GetMovieIndTrackType(1,
77 QuickTime.VisualMediaCharacteristic, QuickTime.movieTrackCharacteristic)
78 self.videomedia = self.videotrack.GetTrackMedia()
79 except Qt.Error:
80 self.videotrack = self.videomedia = self.videotimescale = None
81 if self.videotrack:
82 self.videotimescale = self.videomedia.GetMediaTimeScale()
83 x0, y0, x1, y1 = self.movie.GetMovieBox()
84 self.videodescr = {'width':(x1-x0), 'height':(y1-y0)}
85 self._initgworld()
86 self.videocurtime = None
87 self.audiocurtime = None
88
89
90 def __del__(self):
91 self.audiomedia = None
92 self.audiotrack = None
93 self.videomedia = None
94 self.videotrack = None
95 self.movie = None
96
97 def _initgworld(self):
98 old_port, old_dev = Qdoffs.GetGWorld()
99 try:
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)
113 # XXXX framerate
114 finally:
115 Qdoffs.SetGWorld(old_port, old_dev)
116
117 def _gettrackduration_ms(self, track):
118 tracktime = track.GetTrackDuration()
119 return self._movietime_to_ms(tracktime)
120
121 def _movietime_to_ms(self, time):
122 value, d1, d2 = Qt.ConvertTimeScale((time, self.movietimescale, None), 1000)
123 return value
124
125 def _videotime_to_ms(self, time):
126 value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None), 1000)
127 return value
128
129 def _audiotime_to_ms(self, time):
130 value, d1, d2 = Qt.ConvertTimeScale((time, self.audiotimescale, None), 1000)
131 return value
132
133 def _videotime_to_movietime(self, time):
134 value, d1, d2 = Qt.ConvertTimeScale((time, self.videotimescale, None),
135 self.movietimescale)
136 return value
137
138 def HasAudio(self):
139 return not self.audiotrack is None
140
141 def HasVideo(self):
142 return not self.videotrack is None
143
144 def GetAudioDuration(self):
145 if not self.audiotrack:
146 return 0
147 return self._gettrackduration_ms(self.audiotrack)
148
149 def GetVideoDuration(self):
150 if not self.videotrack:
151 return 0
152 return self._gettrackduration_ms(self.videotrack)
153
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']
159 if nch == 1:
160 channels = ['mono']
161 elif nch == 2:
162 channels = ['left', 'right']
163 else:
164 channels = map(lambda x: str(x+1), range(nch))
165 if bps % 8:
166 # Funny bits-per sample. We pretend not to understand
167 blocksize = 0
168 fpb = 0
169 else:
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
173 fpb = 1
174 if self.audiodescr['dataFormat'] == 'raw ':
175 encoding = 'linear-excess'
176 elif self.audiodescr['dataFormat'] == 'twos':
177 encoding = 'linear-signed'
178 else:
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
183
184 def GetAudioFrameRate(self):
185 if not self.audiodescr:
186 return None
187 return int(self.audiodescr['sampleRate'])
188
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)
193
194 def GetVideoFrameRate(self):
195 tv = self.videocurtime
196 if tv is None:
197 tv = 0
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)
202
203 def ReadAudio(self, nframes, time=None):
204 if not time is 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
212 h = Res.Handle('')
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
218
219 def ReadVideo(self, time=None):
220 if not time is 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()
234
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']
240 start = 0
241 rv = []
242 for i in range(height):
243 nextline = Qdoffs.GetPixMapBytes(self.pixmap, start, width*4)
244 start = start + rowbytes
245 rv.append(nextline)
246 return ''.join(rv)
247
248def reader(url):
249 try:
250 rdr = _Reader(url)
251 except IOError:
252 return None
253 return rdr
254
255def _test():
256 import EasyDialogs
257 try:
258 from PIL import Image
259 except ImportError:
260 Image = None
261 import MacOS
262 Qt.EnterMovies()
263 path = EasyDialogs.AskFileForOpen(message='Video to convert')
264 if not path: sys.exit(0)
265 rdr = reader(path)
266 if not rdr:
267 sys.exit(1)
268 dstdir = EasyDialogs.AskFileForSave(message='Name for output folder')
269 if not dstdir: sys.exit(0)
270 num = 0
271 os.mkdir(dstdir)
272 videofmt = rdr.GetVideoFormat()
273 imgfmt = videofmt.getformat()
274 imgw, imgh = videofmt.getsize()
275 timestamp, data = rdr.ReadVideo()
276 while data:
277 fname = 'frame%04.4d.jpg'%num
278 num = num+1
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))
282 if Image:
283 img = Image.fromstring("RGBA", (imgw, imgh), data)
284 img.save(pname, 'JPEG')
285 timestamp, data = rdr.ReadVideo()
286 MacOS.SetCreatorAndType(pname, 'ogle', 'JPEG')
287 if num > 20:
288 print 'stopping at 20 frames so your disk does not fill up:-)'
289 break
290 print 'Total frames:', num
291
292if __name__ == '__main__':
293 _test()
294 sys.exit(1)
Note: See TracBrowser for help on using the repository browser.