~ubuntu-branches/ubuntu/precise/gst0.10-python/precise

« back to all changes in this revision

Viewing changes to gst/extend/discoverer.py

  • Committer: Bazaar Package Importer
  • Author(s): Alexander Wirt
  • Date: 2008-09-08 20:22:47 UTC
  • mfrom: (4.1.15 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080908202247-nkwix5wpfasoygy0
Tags: 0.10.12-1.1
* Non-maintainer upload.
* Add dependency to python-libxml2 and change load order in
  the init of gst0.10-python (Closes: #449341)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
1
# -*- Mode: Python -*-
3
2
# vi:si:et:sw=4:sts=4:ts=4
4
3
 
5
4
# discoverer.py
6
 
# (c) 2005 Edward Hervey <edward at fluendo dot com>
 
5
# (c) 2005-2008 Edward Hervey <bilboed at bilboed dot com>
7
6
# Discovers multimedia information on files
8
7
 
9
8
# This library is free software; you can redistribute it and/or
10
9
# modify it under the terms of the GNU Lesser General Public
11
10
# License as published by the Free Software Foundation; either
12
11
# version 2.1 of the License, or (at your option) any later version.
13
 
 
12
#
14
13
# This library is distributed in the hope that it will be useful,
15
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
16
# Lesser General Public License for more details.
18
 
 
17
#
19
18
# You should have received a copy of the GNU Lesser General Public
20
19
# License along with this library; if not, write to the Free Software
21
20
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
26
25
 
27
26
import os.path
28
27
 
29
 
import pygtk
30
 
pygtk.require('2.0')
31
28
import gobject
32
29
 
33
 
import pygst
34
 
pygst.require('0.10')
35
30
import gst
36
31
 
37
32
from gst.extend.pygobject import gsignal
50
45
                        None,
51
46
                        (gobject.TYPE_BOOLEAN, ))
52
47
        }
53
 
    
 
48
 
54
49
    mimetype = None
55
50
 
56
51
    audiocaps = {}
75
70
    otherstreams = []
76
71
 
77
72
    finished = False
 
73
    sinknumber = 0
78
74
    tags = {}
79
75
 
80
76
 
81
 
    def __init__(self, filename):
 
77
    def __init__(self, filename, max_interleave=1.0, timeout=3000):
 
78
        """
 
79
        filename: str; absolute path of the file to be discovered.
 
80
        max_interleave: int or float; the maximum frame interleave in seconds.
 
81
            The value must be greater than the input file frame interleave
 
82
            or the discoverer may not find out all input file's streams.
 
83
            The default value is 1 second and you shouldn't have to change it,
 
84
            changing it mean larger discovering time and bigger memory usage.
 
85
        timeout: int; duration in ms for the discovery to complete.
 
86
        """
82
87
        gobject.GObject.__init__(self)
83
88
 
84
89
        self.mimetype = None
107
112
        self.finished = False
108
113
        self.tags = {}
109
114
        self._success = False
 
115
        self._nomorepads = False
110
116
 
111
117
        self._timeoutid = 0
112
 
        
 
118
        self._timeout = timeout
 
119
        self._max_interleave = max_interleave
 
120
 
113
121
        if not os.path.isfile(filename):
 
122
            self.debug("File '%s' does not exist, finished" % filename)
114
123
            self.finished = True
115
124
            return
116
 
        
 
125
 
117
126
        # the initial elements of the pipeline
118
127
        self.src = gst.element_factory_make("filesrc")
119
128
        self.src.set_property("location", filename)
126
135
        # callbacks
127
136
        self.typefind.connect("have-type", self._have_type_cb)
128
137
        self.dbin.connect("new-decoded-pad", self._new_decoded_pad_cb)
 
138
        self.dbin.connect("no-more-pads", self._no_more_pads_cb)
129
139
        self.dbin.connect("unknown-type", self._unknown_type_cb)
130
140
 
 
141
    def _timed_out_or_eos(self):
 
142
        if (not self.is_audio and not self.is_video) or \
 
143
                (self.is_audio and not self.audiocaps) or \
 
144
                (self.is_video and not self.videocaps):
 
145
            self._finished(False)
 
146
        else:
 
147
            self._finished(True)
 
148
 
131
149
    def _finished(self, success=False):
132
150
        self.debug("success:%d" % success)
133
151
        self._success = success
144
162
        self.set_state(gst.STATE_READY)
145
163
        self.debug("about to emit signal")
146
164
        self.emit('discovered', self._success)
147
 
        
148
165
 
149
166
    def _bus_message_cb(self, bus, message):
150
167
        if message.type == gst.MESSAGE_EOS:
151
 
            self._finished()
 
168
            self.debug("Got EOS")
 
169
            self._timed_out_or_eos()
152
170
        elif message.type == gst.MESSAGE_TAG:
153
171
            for key in message.parse_tag().keys():
154
172
                self.tags[key] = message.structure[key]
155
173
        elif message.type == gst.MESSAGE_ERROR:
 
174
            self.debug("Got error")
156
175
            self._finished()
157
176
 
158
177
    def discover(self):
167
186
        self.bus.connect("message", self._bus_message_cb)
168
187
 
169
188
        # 3s timeout
170
 
        self._timeoutid = gobject.timeout_add(3000, self._finished)
171
 
        
 
189
        self._timeoutid = gobject.timeout_add(self._timeout, self._timed_out_or_eos)
 
190
 
172
191
        self.info("setting to PLAY")
173
192
        if not self.set_state(gst.STATE_PLAYING):
174
193
            self._finished()
223
242
            for tag in self.tags.keys():
224
243
                print "%20s :\t" % tag, self.tags[tag]
225
244
 
 
245
    def _no_more_pads_cb(self, dbin):
 
246
        self.info("no more pads")
 
247
        self._nomorepads = True
 
248
 
226
249
    def _unknown_type_cb(self, dbin, pad, caps):
227
250
        self.debug("unknown type : %s" % caps.to_string())
228
251
        # if we get an unknown type and we don't already have an
240
263
        if not caps:
241
264
            pad.info("no negotiated caps available")
242
265
            return
243
 
        pad.info("caps:%s" % caps.to_string)
 
266
        pad.info("caps:%s" % caps.to_string())
244
267
        # the caps are fixed
245
268
        # We now get the total length of that stream
246
269
        q = gst.query_new_duration(gst.FORMAT_TIME)
247
 
        pad.info("sending position query")
 
270
        pad.info("sending duration query")
248
271
        if pad.get_peer().query(q):
249
272
            format, length = q.parse_duration()
250
 
            pad.info("got position query answer : %d:%d" % (length, format))
 
273
            if format == gst.FORMAT_TIME:
 
274
                pad.info("got duration (time) : %s" % (gst.TIME_ARGS(length),))
 
275
            else:
 
276
                pad.info("got duration : %d [format:%d]" % (length, format))
251
277
        else:
252
278
            length = -1
253
 
            gst.warning("position query didn't work")
 
279
            gst.warning("duration query failed")
254
280
 
255
281
        # We store the caps and length in the proper location
256
282
        if "audio" in caps.to_string():
263
289
                self.audiofloat = True
264
290
            else:
265
291
                self.audiodepth = caps[0]["depth"]
266
 
            if (not self.is_video) or self.videocaps:
 
292
            if self._nomorepads and ((not self.is_video) or self.videocaps):
267
293
                self._finished(True)
268
294
        elif "video" in caps.to_string():
269
295
            self.videocaps = caps
271
297
            self.videowidth = caps[0]["width"]
272
298
            self.videoheight = caps[0]["height"]
273
299
            self.videorate = caps[0]["framerate"]
274
 
            if (not self.is_audio) or self.audiocaps:
 
300
            if self._nomorepads and ((not self.is_audio) or self.audiocaps):
275
301
                self._finished(True)
276
302
 
277
303
    def _new_decoded_pad_cb(self, dbin, pad, is_last):
286
312
            self.warning("got a different caps.. %s" % caps.to_string())
287
313
            return
288
314
        if is_last and not self.is_video and not self.is_audio:
 
315
            self.debug("is last, not video or audio")
289
316
            self._finished(False)
290
317
            return
291
318
        # we connect a fakesink to the new pad...
292
319
        pad.info("adding queue->fakesink")
293
 
        fakesink = gst.element_factory_make("fakesink")
 
320
        fakesink = gst.element_factory_make("fakesink", "fakesink%d-%s" % 
 
321
            (self.sinknumber, "audio" in caps.to_string() and "audio" or "video"))
 
322
        self.sinknumber += 1
294
323
        queue = gst.element_factory_make("queue")
 
324
        # we want the queue to buffer up to the specified amount of data 
 
325
        # before outputting. This enables us to cope with formats 
 
326
        # that don't create their source pads straight away, 
 
327
        # but instead wait for the first buffer of that stream.
 
328
        # The specified time must be greater than the input file
 
329
        # frame interleave for the discoverer to work properly.
 
330
        queue.props.min_threshold_time = int(self._max_interleave * gst.SECOND)
 
331
        queue.props.max_size_time = int(2 * self._max_interleave * gst.SECOND)
 
332
        queue.props.max_size_bytes = 0
 
333
 
 
334
        # If durations are bad on the buffers (common for video decoders), we'll
 
335
        # never reach the min_threshold_time or max_size_time. So, set a
 
336
        # max size in buffers, and if reached, disable the min_threshold_time.
 
337
        # This ensures we don't fail to discover with various ffmpeg 
 
338
        # demuxers/decoders that provide bogus (or no) duration.
 
339
        queue.props.max_size_buffers = int(100 * self._max_interleave)
 
340
        def _disable_min_threshold_cb(queue):
 
341
            queue.props.min_threshold_time = 0
 
342
            queue.disconnect(signal_id)
 
343
        signal_id = queue.connect('overrun', _disable_min_threshold_cb)
 
344
 
295
345
        self.add(fakesink, queue)
296
346
        queue.link(fakesink)
297
347
        sinkpad = fakesink.get_pad("sink")