~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): Loic Minier
  • Date: 2006-06-25 19:37:45 UTC
  • Revision ID: james.westby@ubuntu.com-20060625193745-9yeg0wq56r24n57x
Tags: upstream-0.10.4
ImportĀ upstreamĀ versionĀ 0.10.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- Mode: Python -*-
 
3
# vi:si:et:sw=4:sts=4:ts=4
 
4
 
 
5
# discoverer.py
 
6
# (c) 2005 Edward Hervey <edward at fluendo dot com>
 
7
# Discovers multimedia information on files
 
8
 
 
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.
 
13
 
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.
 
18
 
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
 
22
 
 
23
"""
 
24
Class and functions for getting multimedia information about files
 
25
"""
 
26
 
 
27
import gst
 
28
import gobject
 
29
import os.path
 
30
from gst.extend.pygobject import gsignal
 
31
 
 
32
class Discoverer(gst.Pipeline):
 
33
    """
 
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.
 
37
 
 
38
    The 'discovered' callback has one boolean argument, which is True if the
 
39
    file contains decodable multimedia streams.
 
40
    """
 
41
    __gsignals__ = {
 
42
        'discovered' : (gobject.SIGNAL_RUN_FIRST,
 
43
                        None,
 
44
                        (gobject.TYPE_BOOLEAN, ))
 
45
        }
 
46
    
 
47
    mimetype = None
 
48
 
 
49
    audiocaps = {}
 
50
    videocaps = {}
 
51
 
 
52
    videowidth = 0
 
53
    videoheight = 0
 
54
    videorate = 0
 
55
 
 
56
    audiofloat = False
 
57
    audiorate = 0
 
58
    audiodepth = 0
 
59
    audiowidth = 0
 
60
    audiochannels = 0
 
61
 
 
62
    audiolength = 0L
 
63
    videolength = 0L
 
64
 
 
65
    is_video = False
 
66
    is_audio = False
 
67
 
 
68
    otherstreams = []
 
69
 
 
70
    finished = False
 
71
    tags = {}
 
72
 
 
73
 
 
74
    def __init__(self, filename):
 
75
        gobject.GObject.__init__(self)
 
76
 
 
77
        self.mimetype = None
 
78
 
 
79
        self.audiocaps = {}
 
80
        self.videocaps = {}
 
81
 
 
82
        self.videowidth = 0
 
83
        self.videoheight = 0
 
84
        self.videorate = gst.Fraction(0,1)
 
85
 
 
86
        self.audiofloat = False
 
87
        self.audiorate = 0
 
88
        self.audiodepth = 0
 
89
        self.audiowidth = 0
 
90
        self.audiochannels = 0
 
91
 
 
92
        self.audiolength = 0L
 
93
        self.videolength = 0L
 
94
 
 
95
        self.is_video = False
 
96
        self.is_audio = False
 
97
 
 
98
        self.otherstreams = []
 
99
 
 
100
        self.finished = False
 
101
        self.tags = {}
 
102
        self._success = False
 
103
 
 
104
        self._timeoutid = 0
 
105
        
 
106
        if not os.path.isfile(filename):
 
107
            self.finished = True
 
108
            return
 
109
        
 
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")
 
118
 
 
119
        # callbacks
 
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)
 
123
 
 
124
    def _finished(self, success=False):
 
125
        self.debug("success:%d" % success)
 
126
        self._success = success
 
127
        self.bus.remove_signal_watch()
 
128
        if self._timeoutid:
 
129
            gobject.source_remove(self._timeoutid)
 
130
            self._timeoutid = 0
 
131
        gobject.idle_add(self._stop)
 
132
        return False
 
133
 
 
134
    def _stop(self):
 
135
        self.debug("success:%d" % self._success)
 
136
        self.finished = True
 
137
        self.set_state(gst.STATE_READY)
 
138
        self.debug("about to emit signal")
 
139
        self.emit('discovered', self._success)
 
140
        
 
141
 
 
142
    def _bus_message_cb(self, bus, message):
 
143
        if message.type == gst.MESSAGE_EOS:
 
144
            self._finished()
 
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:
 
149
            self._finished()
 
150
 
 
151
    def discover(self):
 
152
        """Find the information on the given file asynchronously"""
 
153
        self.debug("starting discovery")
 
154
        if self.finished:
 
155
            self.emit('discovered', False)
 
156
            return
 
157
 
 
158
        self.bus = self.get_bus()
 
159
        self.bus.add_signal_watch()
 
160
        self.bus.connect("message", self._bus_message_cb)
 
161
 
 
162
        # 3s timeout
 
163
        self._timeoutid = gobject.timeout_add(3000, self._finished)
 
164
        
 
165
        self.info("setting to PLAY")
 
166
        if not self.set_state(gst.STATE_PLAYING):
 
167
            self._finished()
 
168
 
 
169
    def _time_to_string(self, value):
 
170
        """
 
171
        transform a value in nanoseconds into a human-readable string
 
172
        """
 
173
        ms = value / gst.MSECOND
 
174
        sec = ms / 1000
 
175
        ms = ms % 1000
 
176
        min = sec / 60
 
177
        sec = sec % 60
 
178
        return "%2dm %2ds %3d" % (min, sec, ms)
 
179
 
 
180
    def print_info(self):
 
181
        """prints out the information on the given file"""
 
182
        if not self.finished:
 
183
            return
 
184
        if not self.mimetype:
 
185
            print "Unknown media type"
 
186
            return
 
187
        print "Mime Type :\t", self.mimetype
 
188
        if not self.is_video and not self.is_audio:
 
189
            return
 
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:
 
193
            print "Video :"
 
194
            print "\t%d x %d @ %d/%d fps" % (self.videowidth,
 
195
                                            self.videoheight,
 
196
                                            self.videorate.num, self.videorate.denom)
 
197
            if self.tags.has_key("video-codec"):
 
198
                print "\tCodec :", self.tags.pop("video-codec")
 
199
        if self.is_audio:
 
200
            print "Audio :"
 
201
            if self.audiofloat:
 
202
                print "\t%d channels(s) : %dHz @ %dbits (float)" % (self.audiochannels,
 
203
                                                                    self.audiorate,
 
204
                                                                    self.audiowidth)
 
205
            else:
 
206
                print "\t%d channels(s) : %dHz @ %dbits (int)" % (self.audiochannels,
 
207
                                                                  self.audiorate,
 
208
                                                                  self.audiodepth)
 
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
 
214
        if self.tags:
 
215
            print "Additional information :"
 
216
            for tag in self.tags.keys():
 
217
                print "%20s :\t" % tag, self.tags[tag]
 
218
 
 
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:
 
225
            self.finished = True
 
226
            self._finished()
 
227
 
 
228
    def _have_type_cb(self, typefind, prob, caps):
 
229
        self.mimetype = caps.to_string()
 
230
 
 
231
    def _notify_caps_cb(self, pad, args):
 
232
        caps = pad.get_negotiated_caps()
 
233
        if not caps:
 
234
            pad.info("no negotiated caps available")
 
235
            return
 
236
        pad.info("caps:%s" % caps.to_string)
 
237
        # the caps are fixed
 
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))
 
244
        else:
 
245
            length = -1
 
246
            gst.warning("position query didn't work")
 
247
 
 
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
 
257
            else:
 
258
                self.audiodepth = caps[0]["depth"]
 
259
            if (not self.is_video) or self.videocaps:
 
260
                self._finished(True)
 
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:
 
268
                self._finished(True)
 
269
 
 
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():
 
275
            self.is_audio = True
 
276
        elif "video" in caps.to_string():
 
277
            self.is_video = True
 
278
        else:
 
279
            self.warning("got a different caps.. %s" % caps.to_string())
 
280
            return
 
281
        if is_last and not self.is_video and not self.is_audio:
 
282
            self._finished(False)
 
283
            return
 
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)
 
289
        queue.link(fakesink)
 
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')