3 # vi:si:et:sw=4:sts=4:ts=4
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
28 Class and functions for getting multimedia information about files
34 from gst
.extend
.pygobject
import gsignal
36 class Discoverer(gst
.Pipeline
):
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.
46 'discovered' : (gobject
.SIGNAL_RUN_FIRST
,
48 (gobject
.TYPE_BOOLEAN
, ))
78 def __init__(self
, filename
):
79 gobject
.GObject
.__init
__(self
)
82 self
.filename
= filename
89 # self.videoheight = 0
90 # self.videorate = gst.Fraction(0,1)
92 # self.audiofloat = False
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
108 self
._success
= False
114 if not os
.path
.isfile(filename
):
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")
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()
137 gobject
.source_remove(self
._timeoutid
)
139 gobject
.idle_add(self
._stop
)
143 self
.debug("success:%d" % self
._success
)
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
:
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
:
161 """Find the information on the given file asynchronously"""
162 self
.debug("starting discovery")
164 self
.emit('discovered', False)
167 self
.bus
= self
.get_bus()
168 self
.bus
.add_signal_watch()
169 self
.bus
.connect("message", self
._bus
_message
_cb
)
172 self
._timeoutid
= gobject
.timeout_add(3000, self
._finished
)
174 self
.info("setting to PLAY")
175 if not self
.set_state(gst
.STATE_PLAYING
):
178 def _time_to_string(self
, value
):
180 transform a value in nanoseconds into a human-readable string
182 ms
= value
/ gst
.MSECOND
187 return "%2dm %2ds %3d" % (min, sec
, ms
)
189 def print_info(self
):
191 # """prints out the information on the given file"""
192 # if not self.finished:
194 # if not self.mimetype:
195 # print "Unknown media type"
197 # print "Mime Type :\t", self.mimetype
198 # if not self.is_video and not self.is_audio:
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:
204 # print "\t%d x %d @ %d/%d fps" % (self.videowidth,
206 # self.videorate.num, self.videorate.denom)
207 # if self.tags.has_key("video-codec"):
208 # print "\tCodec :", self.tags.pop("video-codec")
211 # if self.audiofloat:
212 # print "\t%d channels(s) : %dHz @ %dbits (float)" % (self.audiochannels,
216 # print "\t%d channels(s) : %dHz @ %dbits (int)" % (self.audiochannels,
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
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
:
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()
245 pad
.info("no negotiated caps available")
247 pad
.info("caps:%s" % caps
.to_string
)
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
))
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
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
294 # self.warning("got a different caps.. %s" % caps.to_string())
296 # if is_last and not self.is_video and not self.is_audio:
297 # self._finished(False)
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
):