Convert tabs to spaces. Remove accidently committed debug line
[xiph/unicode.git] / souffleur / discoverer.py
blobf1a55ddc83cb94846544528ac90fa7f1ac5c6f81
1 #!/usr/bin/env python
2 # -*- Mode: Python -*-
3 # vi:si:et:sw=4:sts=4:ts=4
5 # discoverer.py
6 # (c) 2005 Edward Hervey <edward at fluendo dot com>
7 # Discovers multimedia information on files
9 # This library is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation; either
12 # version 2.1 of the License, or (at your option) any later version.
14 # This library is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 # Lesser General Public License for more details.
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with this library; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 # (c) 2006 Maxim Litvinov (aka DarakuTenshi) <otaky at ukr dot net>
24 # Adapt to Souffleur project use
27 """
28 Class and functions for getting multimedia information about files
29 """
31 import gst
32 import gobject
33 import os.path
34 from gst.extend.pygobject import gsignal
36 class Discoverer(gst.Pipeline):
37 """
38 Discovers information about files.
39 This class is event-based and needs a mainloop to work properly.
40 Emits the 'discovered' signal when discovery is finished.
42 The 'discovered' callback has one boolean argument, which is True if the
43 file contains decodable multimedia streams.
44 """
45 __gsignals__ = {
46 'discovered' : (gobject.SIGNAL_RUN_FIRST,
47 None,
48 (gobject.TYPE_BOOLEAN, ))
51 mimetype = None
53 audiocaps = {}
54 videocaps = {}
56 videowidth = 0
57 videoheight = 0
58 videorate = 0
60 audiofloat = False
61 audiorate = 0
62 audiodepth = 0
63 audiowidth = 0
64 audiochannels = 0
66 audiolength = 0L
67 videolength = 0L
69 is_video = False
70 is_audio = False
72 otherstreams = []
74 finished = False
75 tags = {}
78 def __init__(self, filename):
79 gobject.GObject.__init__(self)
82 self.filename = filename
83 self.mimetype = None
85 # self.audiocaps = {}
86 # self.videocaps = {}
88 # self.videowidth = 0
89 # self.videoheight = 0
90 # self.videorate = gst.Fraction(0,1)
92 # self.audiofloat = False
93 # self.audiorate = 0
94 # self.audiodepth = 0
95 # self.audiowidth = 0
96 # self.audiochannels = 0
98 # self.audiolength = 0L
99 # self.videolength = 0L
101 # self.is_video = False
102 # self.is_audio = False
104 # self.otherstreams = []
106 self.finished = False
107 # self.tags = {}
108 self._success = False
110 self.streams = {}
112 self._timeoutid = 0
114 if not os.path.isfile(filename):
115 self.finished = True
116 return
118 # the initial elements of the pipeline
119 self.src = gst.element_factory_make("filesrc")
120 self.src.set_property("location", filename)
121 self.src.set_property("blocksize", 1000000)
122 self.dbin = gst.element_factory_make("decodebin")
123 self.add(self.src, self.dbin)
124 self.src.link(self.dbin)
125 self.typefind = self.dbin.get_by_name("typefind")
127 # callbacks
128 self.typefind.connect("have-type", self._have_type_cb)
129 self.dbin.connect("new-decoded-pad", self._new_decoded_pad_cb)
130 self.dbin.connect("unknown-type", self._unknown_type_cb)
132 def _finished(self, success=False):
133 self.debug("success:%d" % success)
134 self._success = success
135 self.bus.remove_signal_watch()
136 if self._timeoutid:
137 gobject.source_remove(self._timeoutid)
138 self._timeoutid = 0
139 gobject.idle_add(self._stop)
140 return False
142 def _stop(self):
143 self.debug("success:%d" % self._success)
144 self.finished = True
145 self.set_state(gst.STATE_READY)
146 self.debug("about to emit signal")
147 self.emit('discovered', self._success)
150 def _bus_message_cb(self, bus, message):
151 if message.type == gst.MESSAGE_EOS:
152 self._finished()
153 # elif message.type == gst.MESSAGE_TAG:
154 # for key in message.parse_tag().keys():
155 # print "_bus_message_cb\t", bus.get_parent(), key, message.structure[key]
156 # self.tags[key] = message.structure[key]
157 elif message.type == gst.MESSAGE_ERROR:
158 self._finished()
160 def discover(self):
161 """Find the information on the given file asynchronously"""
162 self.debug("starting discovery")
163 if self.finished:
164 self.emit('discovered', False)
165 return
167 self.bus = self.get_bus()
168 self.bus.add_signal_watch()
169 self.bus.connect("message", self._bus_message_cb)
171 # 3s timeout
172 self._timeoutid = gobject.timeout_add(3000, self._finished)
174 self.info("setting to PLAY")
175 if not self.set_state(gst.STATE_PLAYING):
176 self._finished()
178 def _time_to_string(self, value):
180 transform a value in nanoseconds into a human-readable string
182 ms = value / gst.MSECOND
183 sec = ms / 1000
184 ms = ms % 1000
185 min = sec / 60
186 sec = sec % 60
187 return "%2dm %2ds %3d" % (min, sec, ms)
189 def print_info(self):
190 pass
191 # """prints out the information on the given file"""
192 # if not self.finished:
193 # return
194 # if not self.mimetype:
195 # print "Unknown media type"
196 # return
197 # print "Mime Type :\t", self.mimetype
198 # if not self.is_video and not self.is_audio:
199 # return
200 # print "Length :\t", self._time_to_string(max(self.audiolength, self.videolength))
201 # print "\tAudio:", self._time_to_string(self.audiolength), "\tVideo:", self._time_to_string(self.videolength)
202 # if self.is_video and self.videorate:
203 # print "Video :"
204 # print "\t%d x %d @ %d/%d fps" % (self.videowidth,
205 # self.videoheight,
206 # self.videorate.num, self.videorate.denom)
207 # if self.tags.has_key("video-codec"):
208 # print "\tCodec :", self.tags.pop("video-codec")
209 # if self.is_audio:
210 # print "Audio :"
211 # if self.audiofloat:
212 # print "\t%d channels(s) : %dHz @ %dbits (float)" % (self.audiochannels,
213 # self.audiorate,
214 # self.audiowidth)
215 # else:
216 # print "\t%d channels(s) : %dHz @ %dbits (int)" % (self.audiochannels,
217 # self.audiorate,
218 # self.audiodepth)
219 # if self.tags.has_key("audio-codec"):
220 # print "\tCodec :", self.tags.pop("audio-codec")
221 # for stream in self.otherstreams:
222 # if not stream == self.mimetype:
223 # print "Other unsuported Multimedia stream :", stream
224 # if self.tags:
225 # print "Additional information :"
226 # for tag in self.tags.keys():
227 # print "%20s :\t" % tag, self.tags[tag]
229 def _unknown_type_cb(self, dbin, pad, caps):
230 self.debug("unknown type : %s" % caps.to_string())
231 # print "_unknown_type_cb", caps.to_string()
232 # if we get an unknown type and we don't already have an
233 # audio or video pad, we are finished !
234 self.otherstreams.append(caps.to_string())
235 if not self.is_video and not self.is_audio:
236 self.finished = True
237 self._finished()
239 def _have_type_cb(self, typefind, prob, caps):
240 self.mimetype = caps.to_string()
242 def _notify_caps_cb(self, pad, args):
243 caps = pad.get_negotiated_caps()
244 if not caps:
245 pad.info("no negotiated caps available")
246 return
247 pad.info("caps:%s" % caps.to_string)
248 # the caps are fixed
249 # We now get the total length of that stream
250 q = gst.query_new_duration(gst.FORMAT_TIME)
251 pad.info("sending position query")
252 if pad.get_peer().query(q):
253 format, length = q.parse_duration()
254 pad.info("got position query answer : %d:%d" % (length, format))
255 else:
256 length = -1
257 gst.warning("position query didn't work")
259 # print "_notify_caps_cb", pad.get_name(), caps.to_string()
261 # We store the caps and length in the proper location
262 # if "audio" in caps.to_string():
263 # self.audiocaps = caps
264 # self.audiolength = length
265 # self.audiorate = caps[0]["rate"]
266 # self.audiowidth = caps[0]["width"]
267 # self.audiochannels = caps[0]["channels"]
268 # if "x-raw-float" in caps.to_string():
269 # self.audiofloat = True
270 # else:
271 # self.audiodepth = caps[0]["depth"]
272 # if (not self.is_video) or self.videocaps:
273 # self._finished(True)
274 # elif "video" in caps.to_string():
275 # self.videocaps = caps
276 # self.videolength = length
277 # self.videowidth = caps[0]["width"]
278 # self.videoheight = caps[0]["height"]
279 # self.videorate = caps[0]["framerate"]
280 # if (not self.is_audio) or self.audiocaps:
281 # self._finished(True)
283 def _new_decoded_pad_cb(self, dbin, pad, is_last):
284 # Does the file contain got audio or video ?
285 caps = pad.get_caps()
286 gst.info("caps:%s" % caps.to_string())
287 # print "_new_decoded_pad_cb", pad.get_name(),caps.to_string() ##!! get_name
288 self.streams[pad.get_name()] = caps.to_string()
289 # if "audio" in caps.to_string():
290 # self.is_audio = True
291 # elif "video" in caps.to_string():
292 # self.is_video = True
293 # else:
294 # self.warning("got a different caps.. %s" % caps.to_string())
295 # return
296 # if is_last and not self.is_video and not self.is_audio:
297 # self._finished(False)
298 # return
299 # we connect a fakesink to the new pad...
300 # pad.info("adding queue->fakesink")
301 # fakesink = gst.element_factory_make("fakesink")
302 # queue = gst.element_factory_make("queue")
303 # self.add(fakesink, queue)
304 # queue.link(fakesink)
305 # sinkpad = fakesink.get_pad("sink")
306 # queuepad = queue.get_pad("sink")
307 # ... and connect a callback for when the caps are fixed
308 # sinkpad.connect("notify::caps", self._notify_caps_cb)
309 # if pad.link(queuepad):
310 # pad.warning("##### Couldn't link pad to queue")
311 # queue.set_state(gst.STATE_PLAYING)
312 # fakesink.set_state(gst.STATE_PLAYING)
313 gst.info('finished here')
315 def getStreams(self):
316 return self.streams
318 def getMIME(self):
319 return self.mimetype
321 def getSource(self):
322 return self.filename