~ubuntu-branches/ubuntu/lucid/pitivi/lucid

« back to all changes in this revision

Viewing changes to pitivi/utils.py

  • Committer: Bazaar Package Importer
  • Author(s): Sebastian Dröge
  • Date: 2009-05-27 14:22:49 UTC
  • mfrom: (1.2.1 upstream) (3.1.13 experimental)
  • Revision ID: james.westby@ubuntu.com-20090527142249-tj0qnkc37320ylml
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
# set of utility functions
24
24
 
25
 
import gst
 
25
import sys
 
26
import gobject
 
27
import gst, bisect
 
28
import os
 
29
from pitivi.signalinterface import Signallable
 
30
import pitivi.log.log as log
 
31
from gettext import ngettext
 
32
 
 
33
UNKNOWN_DURATION = 2 ** 63 - 1
 
34
 
 
35
def time_to_string(value):
 
36
    """
 
37
    Converts the given time in nanoseconds to a human readable string
 
38
 
 
39
    Format HH:MM:SS.XXX
 
40
    """
 
41
    if value == gst.CLOCK_TIME_NONE:
 
42
        return "--:--:--.---"
 
43
    ms = value / gst.MSECOND
 
44
    sec = ms / 1000
 
45
    ms = ms % 1000
 
46
    mins = sec / 60
 
47
    sec = sec % 60
 
48
    hours = mins / 60
 
49
    mins = mins % 60
 
50
    return "%02d:%02d:%02d.%03d" % (hours, mins, sec, ms)
 
51
 
 
52
def beautify_length(length):
 
53
    """
 
54
    Converts the given time in nanoseconds to a human readable string
 
55
 
 
56
    Format HHhMMmSSs
 
57
    """
 
58
    sec = length / gst.SECOND
 
59
    mins = sec / 60
 
60
    sec = sec % 60
 
61
    hours = mins / 60
 
62
    mins = mins % 60
 
63
 
 
64
    parts = []
 
65
    if hours:
 
66
        parts.append(ngettext("%d hour", "%d hours", hours) % hours)
 
67
 
 
68
    if mins:
 
69
        parts.append(ngettext("%d minute", "%d minutes", mins) % mins)
 
70
 
 
71
    if not hours and sec:
 
72
        parts.append(ngettext("%d second", "%d seconds", sec) % sec)
 
73
 
 
74
    return ", ".join(parts)
26
75
 
27
76
def bin_contains(bin, element):
28
77
    """ Returns True if the bin contains the given element, the search is recursive """
36
85
        if isinstance(elt, gst.Bin) and bin_contains(elt, element):
37
86
            return True
38
87
    return False
 
88
 
 
89
# Python re-implementation of binary search algorithm found here:
 
90
# http://en.wikipedia.org/wiki/Binary_search
 
91
#
 
92
# This is the iterative version without the early termination branch, which
 
93
# also tells us the element of A that are nearest to Value, if the element we
 
94
# want is not found. This is useful for implementing edge snaping in the UI,
 
95
# where we repeatedly search through a list of control points for the one
 
96
# closes to the cursor. Because we don't care whether the cursor position
 
97
# matches the list, this function returns the index of the lement closest to
 
98
# value in the array.
 
99
 
 
100
def binary_search(col, value):
 
101
    low = 0
 
102
    high = len(col)
 
103
    while (low < high):
 
104
        mid = (low + high)/2
 
105
        if (col[mid] < value):
 
106
            low = mid + 1
 
107
        else:
 
108
            #can't be high = mid-1: here col[mid] >= value,
 
109
            #so high can't be < mid if col[mid] == value
 
110
            high = mid;
 
111
    return low
 
112
 
 
113
# Returns the element of seq nearest to item, and the difference between them
 
114
 
 
115
def closest_item(seq, item, lo=0):
 
116
    index = bisect.bisect(seq, item, lo)
 
117
    if index >= len(seq):
 
118
        index = len(seq) - 1
 
119
    res = seq[index]
 
120
    diff = abs(res - item)
 
121
 
 
122
    # binary_search returns largest element closest to item.
 
123
    # if there is a smaller element...
 
124
    if index - 1 >= 0:
 
125
        res_a = seq[index - 1]
 
126
        # ...and it is closer to the pointer...
 
127
        diff_a = abs(res_a - item)
 
128
        if diff_a < diff:
 
129
            # ...use it instead.
 
130
            res = res_a
 
131
            diff = diff_a
 
132
            index = index - 1
 
133
 
 
134
    return res, diff, index
 
135
 
 
136
def argmax(func, seq):
 
137
    """return the element of seq that gives max(map(func, seq))"""
 
138
    def compare(a1, b1):
 
139
        if a1[0] > b1[0]:
 
140
            return a1
 
141
        return b1
 
142
    # using a generator expression here should save memory
 
143
    objs = ((func(val), val) for val in seq)
 
144
    return reduce(compare, objs)[1]
 
145
 
 
146
def same(seq):
 
147
    i = iter(seq)
 
148
    first = i.next()
 
149
    for item in i:
 
150
        if first != item:
 
151
            return None
 
152
    return first
 
153
 
 
154
def data_probe(pad, data, section=""):
 
155
    """Callback to use for gst.Pad.add_*_probe.
 
156
 
 
157
    The extra argument will be used to prefix the debug messages
 
158
    """
 
159
    if section == "":
 
160
        section = "%s:%s" % (pad.get_parent().get_name(), pad.get_name())
 
161
    if isinstance(data, gst.Buffer):
 
162
        log.debug("probe","%s BUFFER timestamp:%s , duration:%s , size:%d , offset:%d , offset_end:%d",
 
163
                  section, gst.TIME_ARGS(data.timestamp), gst.TIME_ARGS(data.duration),
 
164
                  data.size, data.offset, data.offset_end)
 
165
        if data.flags & gst.BUFFER_FLAG_DELTA_UNIT:
 
166
            log.debug("probe","%s DELTA_UNIT", section)
 
167
        if data.flags & gst.BUFFER_FLAG_DISCONT:
 
168
            log.debug("probe","%s DISCONT", section)
 
169
        if data.flags & gst.BUFFER_FLAG_GAP:
 
170
            log.debug("probe","%s GAP", section)
 
171
        log.debug("probe","%s flags:%r", section, data.flags)
 
172
    else:
 
173
        log.debug("probe","%s EVENT %s", section, data.type)
 
174
        if data.type == gst.EVENT_NEWSEGMENT:
 
175
            log.debug("probe","%s %r", section, list(data.parse_new_segment()))
 
176
    return True
 
177
 
 
178
def linkDynamic(element, target):
 
179
 
 
180
    def pad_added(bin, pad, target):
 
181
        if target.get_compatible_pad(pad):
 
182
            element.link(target)
 
183
    element.connect("pad-added", pad_added, target)
 
184
 
 
185
def element_make_many(*args):
 
186
    return tuple((gst.element_factory_make(arg) for arg in args))
 
187
 
 
188
def pipeline(graph):
 
189
    E = graph.iteritems()
 
190
    V = graph.iterkeys()
 
191
    p = gst.Pipeline()
 
192
    p.add(*V)
 
193
    for u, v in E:
 
194
        if v:
 
195
            try:
 
196
                u.link(v)
 
197
            except gst.LinkError:
 
198
                linkDynamic(u, v)
 
199
    return p
 
200
 
 
201
def filter_(caps):
 
202
    f = gst.element_factory_make("capsfilter")
 
203
    f.props.caps = gst.caps_from_string(caps)
 
204
    return f
 
205
 
 
206
 
 
207
## URI functions
 
208
 
 
209
 
 
210
def uri_is_valid(uri):
 
211
    """Checks if the given uri is a valid uri (of type file://)
 
212
 
 
213
    Will also check if the size is valid (> 0).
 
214
 
 
215
    @param uri: The location to check
 
216
    @type uri: C{URI}
 
217
    """
 
218
    res = gst.uri_is_valid(uri) and gst.uri_get_protocol(uri) == "file"
 
219
    if res:
 
220
        return len(os.path.basename(gst.uri_get_location(uri))) > 0
 
221
    return res
 
222
 
 
223
def uri_is_reachable(uri):
 
224
    """ Check whether the given uri is reachable and we can read/write
 
225
    to it.
 
226
 
 
227
    @param uri: The location to check
 
228
    @type uri: C{URI}
 
229
    @return: C{True} if the uri is reachable.
 
230
    @rtype: C{bool}
 
231
    """
 
232
    if not uri_is_valid(uri):
 
233
        raise NotImplementedError(
 
234
            _("%s doesn't yet handle non local projects") % APPNAME)
 
235
    return os.path.isfile(gst.uri_get_location(uri))
 
236
 
 
237
class PropertyChangeTracker(Signallable):
 
238
    def __init__(self, timeline_object):
 
239
        self.properties = {}
 
240
 
 
241
        for property_name in self.property_names:
 
242
            self.properties[property_name] = \
 
243
                    getattr(timeline_object, property_name)
 
244
 
 
245
            timeline_object.connect(property_name + '-changed',
 
246
                    self._propertyChangedCb, property_name)
 
247
 
 
248
    def _propertyChangedCb(self, timeline_object, value, property_name):
 
249
        old_value = self.properties[property_name]
 
250
        self.properties[property_name] = value
 
251
 
 
252
        self.emit(property_name + '-changed', timeline_object, old_value, value)
 
253
 
 
254
class Seeker(Signallable):
 
255
    __signals__ = {'seek': ['position', 'format']}
 
256
 
 
257
    def __init__(self, timeout):
 
258
        self.timeout = timeout
 
259
        self.pending_seek_id = None
 
260
        self.position = None
 
261
        self.format = None
 
262
 
 
263
    def seek(self, position, format=gst.FORMAT_TIME, on_idle=False):
 
264
        if self.pending_seek_id is None:
 
265
            self.position = position
 
266
            self.format = format
 
267
            if on_idle:
 
268
                gobject.idle_add(self._seekTimeoutCb)
 
269
            else:
 
270
                self._seekTimeoutCb()
 
271
            self.pending_seek_id = self._scheduleSeek(self.timeout,
 
272
                    self._seekTimeoutCb)
 
273
        else:
 
274
            self.position = position
 
275
            self.format = format
 
276
 
 
277
    def _scheduleSeek(self, timeout, callback):
 
278
        return gobject.timeout_add(timeout, callback)
 
279
 
 
280
    def _seekTimeoutCb(self):
 
281
        self.pending_seek_id = None
 
282
        if self.position != None and self.format != None:
 
283
            position, self.position = self.position, None
 
284
            format, self.format = self.format, None
 
285
            self.emit('seek', position, format)
 
286
        return False
 
287
 
 
288
def get_filesystem_encoding():
 
289
    return sys.getfilesystemencoding() or "utf-8"