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
24
Class and functions for getting multimedia information about files
30
from gst.extend.pygobject import gsignal
32
class Discoverer(gst.Pipeline):
34
Discovers information about files.
35
This class is event-based and needs a mainloop to work properly.
36
Emits the 'discovered' signal when discovery is finished.
38
The 'discovered' callback has one boolean argument, which is True if the
39
file contains decodable multimedia streams.
42
'discovered' : (gobject.SIGNAL_RUN_FIRST,
44
(gobject.TYPE_BOOLEAN, ))
74
def __init__(self, filename):
75
gobject.GObject.__init__(self)
84
self.videorate = gst.Fraction(0,1)
86
self.audiofloat = False
90
self.audiochannels = 0
98
self.otherstreams = []
100
self.finished = False
102
self._success = False
106
if not os.path.isfile(filename):
110
# the initial elements of the pipeline
111
self.src = gst.element_factory_make("filesrc")
112
self.src.set_property("location", filename)
113
self.src.set_property("blocksize", 1000000)
114
self.dbin = gst.element_factory_make("decodebin")
115
self.add(self.src, self.dbin)
116
self.src.link(self.dbin)
117
self.typefind = self.dbin.get_by_name("typefind")
120
self.typefind.connect("have-type", self._have_type_cb)
121
self.dbin.connect("new-decoded-pad", self._new_decoded_pad_cb)
122
self.dbin.connect("unknown-type", self._unknown_type_cb)
124
def _finished(self, success=False):
125
self.debug("success:%d" % success)
126
self._success = success
127
self.bus.remove_signal_watch()
129
gobject.source_remove(self._timeoutid)
131
gobject.idle_add(self._stop)
135
self.debug("success:%d" % self._success)
137
self.set_state(gst.STATE_READY)
138
self.debug("about to emit signal")
139
self.emit('discovered', self._success)
142
def _bus_message_cb(self, bus, message):
143
if message.type == gst.MESSAGE_EOS:
145
elif message.type == gst.MESSAGE_TAG:
146
for key in message.parse_tag().keys():
147
self.tags[key] = message.structure[key]
148
elif message.type == gst.MESSAGE_ERROR:
152
"""Find the information on the given file asynchronously"""
153
self.debug("starting discovery")
155
self.emit('discovered', False)
158
self.bus = self.get_bus()
159
self.bus.add_signal_watch()
160
self.bus.connect("message", self._bus_message_cb)
163
self._timeoutid = gobject.timeout_add(3000, self._finished)
165
self.info("setting to PLAY")
166
if not self.set_state(gst.STATE_PLAYING):
169
def _time_to_string(self, value):
171
transform a value in nanoseconds into a human-readable string
173
ms = value / gst.MSECOND
178
return "%2dm %2ds %3d" % (min, sec, ms)
180
def print_info(self):
181
"""prints out the information on the given file"""
182
if not self.finished:
184
if not self.mimetype:
185
print "Unknown media type"
187
print "Mime Type :\t", self.mimetype
188
if not self.is_video and not self.is_audio:
190
print "Length :\t", self._time_to_string(max(self.audiolength, self.videolength))
191
print "\tAudio:", self._time_to_string(self.audiolength), "\tVideo:", self._time_to_string(self.videolength)
192
if self.is_video and self.videorate:
194
print "\t%d x %d @ %d/%d fps" % (self.videowidth,
196
self.videorate.num, self.videorate.denom)
197
if self.tags.has_key("video-codec"):
198
print "\tCodec :", self.tags.pop("video-codec")
202
print "\t%d channels(s) : %dHz @ %dbits (float)" % (self.audiochannels,
206
print "\t%d channels(s) : %dHz @ %dbits (int)" % (self.audiochannels,
209
if self.tags.has_key("audio-codec"):
210
print "\tCodec :", self.tags.pop("audio-codec")
211
for stream in self.otherstreams:
212
if not stream == self.mimetype:
213
print "Other unsuported Multimedia stream :", stream
215
print "Additional information :"
216
for tag in self.tags.keys():
217
print "%20s :\t" % tag, self.tags[tag]
219
def _unknown_type_cb(self, dbin, pad, caps):
220
self.debug("unknown type : %s" % caps.to_string())
221
# if we get an unknown type and we don't already have an
222
# audio or video pad, we are finished !
223
self.otherstreams.append(caps.to_string())
224
if not self.is_video and not self.is_audio:
228
def _have_type_cb(self, typefind, prob, caps):
229
self.mimetype = caps.to_string()
231
def _notify_caps_cb(self, pad, args):
232
caps = pad.get_negotiated_caps()
234
pad.info("no negotiated caps available")
236
pad.info("caps:%s" % caps.to_string)
238
# We now get the total length of that stream
239
q = gst.query_new_duration(gst.FORMAT_TIME)
240
pad.info("sending position query")
241
if pad.get_peer().query(q):
242
format, length = q.parse_duration()
243
pad.info("got position query answer : %d:%d" % (length, format))
246
gst.warning("position query didn't work")
248
# We store the caps and length in the proper location
249
if "audio" in caps.to_string():
250
self.audiocaps = caps
251
self.audiolength = length
252
self.audiorate = caps[0]["rate"]
253
self.audiowidth = caps[0]["width"]
254
self.audiochannels = caps[0]["channels"]
255
if "x-raw-float" in caps.to_string():
256
self.audiofloat = True
258
self.audiodepth = caps[0]["depth"]
259
if (not self.is_video) or self.videocaps:
261
elif "video" in caps.to_string():
262
self.videocaps = caps
263
self.videolength = length
264
self.videowidth = caps[0]["width"]
265
self.videoheight = caps[0]["height"]
266
self.videorate = caps[0]["framerate"]
267
if (not self.is_audio) or self.audiocaps:
270
def _new_decoded_pad_cb(self, dbin, pad, is_last):
271
# Does the file contain got audio or video ?
272
caps = pad.get_caps()
273
gst.info("caps:%s" % caps.to_string())
274
if "audio" in caps.to_string():
276
elif "video" in caps.to_string():
279
self.warning("got a different caps.. %s" % caps.to_string())
281
if is_last and not self.is_video and not self.is_audio:
282
self._finished(False)
284
# we connect a fakesink to the new pad...
285
pad.info("adding queue->fakesink")
286
fakesink = gst.element_factory_make("fakesink")
287
queue = gst.element_factory_make("queue")
288
self.add(fakesink, queue)
290
sinkpad = fakesink.get_pad("sink")
291
queuepad = queue.get_pad("sink")
292
# ... and connect a callback for when the caps are fixed
293
sinkpad.connect("notify::caps", self._notify_caps_cb)
294
if pad.link(queuepad):
295
pad.warning("##### Couldn't link pad to queue")
296
queue.set_state(gst.STATE_PLAYING)
297
fakesink.set_state(gst.STATE_PLAYING)
298
gst.info('finished here')