~mblayman/entertainer/clean-up-1.0

« back to all changes in this revision

Viewing changes to entertainerlib/thumbnailer.py

  • Committer: Paul Hummer
  • Date: 2008-06-21 06:10:59 UTC
  • mto: This revision was merged to the branch mainline in revision 256.
  • Revision ID: paul@ubuntu.com-20080621061059-a55dg8p5hlr0k1jr
Moves many files and directories

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2
 
'''Thumbnailer classes for image and video.'''
3
 
# pylint: disable-msg=C0301
4
 
 
5
 
import os
6
 
from threading import Event
7
 
import hashlib
8
 
 
9
 
import gobject
10
 
import gst
11
 
import Image
12
 
import ImageStat
13
 
 
14
 
from entertainerlib.exceptions import (ImageThumbnailerException,
15
 
    ThumbnailerException, VideoThumbnailerException)
16
 
from entertainerlib.configuration import Configuration
17
 
 
18
 
 
19
 
class Thumbnailer(object):
20
 
    '''Thumbnailer base class for video and image thumbnailers.'''
21
 
 
22
 
    MAX_SIZE = 512
23
 
    THUMB_QUALITY = 85
24
 
 
25
 
    def __init__(self, filename, thumb_type):
26
 
 
27
 
        self.config = Configuration()
28
 
        thumb_dir = os.path.join(self.config.THUMB_DIR, thumb_type)
29
 
        self.filename = filename
30
 
        filehash = hashlib.md5()
31
 
        filehash.update(self.filename)
32
 
        self.filename_hash = filehash.hexdigest()
33
 
 
34
 
        if not os.path.exists(self.filename):
35
 
            raise ThumbnailerException(
36
 
                'File to thumbnail does not exist : %s' % self.filename)
37
 
 
38
 
        if os.path.exists(thumb_dir):
39
 
            if os.path.isfile(filename):
40
 
                self._thumb_file =  os.path.join(thumb_dir,
41
 
                    self.filename_hash + '.jpg')
42
 
            else:
43
 
                raise ThumbnailerException(
44
 
                    'Thumbnailer filename is a folder : %s' % self.filename)
45
 
        else:
46
 
            raise ThumbnailerException('Unknown thumbnail type : %s' % (
47
 
                thumb_type))
48
 
 
49
 
    def get_hash(self):
50
 
        '''Get the hash of the filename'''
51
 
        return self.filename_hash
52
 
 
53
 
    def create_thumbnail(self):
54
 
        """
55
 
        Implement this method in deriving classes.
56
 
 
57
 
        Method should create a new thumbnail and save it to the Entertainer's
58
 
        thumbnail directory in JPEG format. Thumbnail filename should be a MD5
59
 
        hash of the absolute path of the original file.
60
 
        """
61
 
        raise NotImplementedError
62
 
 
63
 
 
64
 
class ImageThumbnailer(Thumbnailer):
65
 
    """Thumbnailer for image files."""
66
 
 
67
 
    def __init__(self, filename):
68
 
        """Create a new Image thumbnailer"""
69
 
        Thumbnailer.__init__(self, filename, 'image')
70
 
        try:
71
 
            self.im = Image.open(self.filename)
72
 
        except:
73
 
            raise ImageThumbnailerException(
74
 
                'Error while opening file : %s' % self.filename)
75
 
 
76
 
 
77
 
    def create_thumbnail(self):
78
 
        """
79
 
        Method creates a new thumbnail and saves it to the Entertainer's
80
 
        thumbnail directory in PNG format. Thumbnail filename is a MD5
81
 
        hash of the given filename.
82
 
        """
83
 
 
84
 
        # Calculate new size here
85
 
        original_width = self.im.size[0]
86
 
        original_height = self.im.size[1]
87
 
        if original_width <= self.MAX_SIZE and (
88
 
            original_height <= self.MAX_SIZE):
89
 
            try:
90
 
                self.im.save(self._thumb_file, "JPEG",
91
 
                    quality=self.THUMB_QUALITY)
92
 
            except:
93
 
                raise ImageThumbnailerException('Error saving thumbnail')
94
 
 
95
 
        else:
96
 
            if original_width > original_height:
97
 
                width = self.MAX_SIZE
98
 
                height = (width * original_height) / original_width
99
 
            else:
100
 
                height = self.MAX_SIZE
101
 
                width = (height * original_width) / original_height
102
 
            try:
103
 
                self.im.thumbnail((width, height), Image.ANTIALIAS)
104
 
                self.im.save(self._thumb_file, "JPEG",
105
 
                    quality=self.THUMB_QUALITY)
106
 
            except:
107
 
                raise ImageThumbnailerException('Error saving thumbnail')
108
 
 
109
 
 
110
 
class VideoThumbnailer(Thumbnailer):
111
 
    '''Create thumbnails from videos.'''
112
 
    # I think it's important to note that the methodology of this code (and
113
 
    # some actual EXACT snippets of code) were based on Elisa
114
 
    # (http://elisa.fluendo.com/)
115
 
 
116
 
 
117
 
    class VideoSinkBin(gst.Bin):
118
 
        '''A gstreamer sink bin'''
119
 
 
120
 
        def __init__(self, needed_caps):
121
 
            self.reset()
122
 
            gst.Bin.__init__(self)
123
 
            self._capsfilter = gst.element_factory_make(
124
 
                'capsfilter', 'capsfilter')
125
 
 
126
 
            self.set_caps(needed_caps)
127
 
            self.add(self._capsfilter)
128
 
 
129
 
            fakesink = gst.element_factory_make('fakesink', 'fakesink')
130
 
            fakesink.set_property("sync", False)
131
 
            self.add(fakesink)
132
 
            self._capsfilter.link(fakesink)
133
 
 
134
 
            pad = self._capsfilter.get_pad("sink")
135
 
            ghostpad = gst.GhostPad("sink", pad)
136
 
 
137
 
            pad2probe = fakesink.get_pad("sink")
138
 
            pad2probe.add_buffer_probe(self.buffer_probe)
139
 
 
140
 
            self.add_pad(ghostpad)
141
 
            self.sink = self._capsfilter
142
 
 
143
 
        def set_current_frame(self, value):
144
 
            '''Set the current frame'''
145
 
            self._current_frame = value
146
 
 
147
 
        def set_caps(self, caps):
148
 
            '''Set the bin caps'''
149
 
            gst_caps = gst.caps_from_string(caps)
150
 
            self._capsfilter.set_property("caps", gst_caps)
151
 
 
152
 
        def get_current_frame(self):
153
 
            '''Gets the current frame'''
154
 
            frame = self._current_frame
155
 
            self._current_frame = None
156
 
            return frame
157
 
 
158
 
        def buffer_probe(self, pad, buff):
159
 
            '''Buffer the probe'''
160
 
            caps = buff.caps
161
 
            if caps != None:
162
 
                s = caps[0]
163
 
                self.width = s['width']
164
 
                self.height = s['height']
165
 
            if self.width != None and self.height != None and buff != None:
166
 
                self.set_current_frame(buff.data)
167
 
            return True
168
 
 
169
 
        def reset(self):
170
 
            '''Reset the bin'''
171
 
            self.width = None
172
 
            self.height = None
173
 
            self.set_current_frame(None)
174
 
 
175
 
 
176
 
    def __init__(self, filename, src="video"):
177
 
 
178
 
        Thumbnailer.__init__(self, filename, src)
179
 
        self._fileuri = 'file://%s' % (self.filename)
180
 
 
181
 
        #Initialize and use the gstreamer pipeline
182
 
        self._pipeline = gst.element_factory_make('playbin', 'playbin')
183
 
        self._sink = self.VideoSinkBin('video/x-raw-rgb,bpp=24,depth=24')
184
 
        self._blocker = Event()
185
 
        self._pipeline.set_property('video-sink', self._sink)
186
 
        self._pipeline.set_property('volume', 0)
187
 
 
188
 
 
189
 
        self.BORING_THRESHOLD = 2000
190
 
        self.HOLES_SIZE = (9, 35)
191
 
        self.HOLES_DATA = '\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x9a\x9a\x9a\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x91\x91\x91\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x9a\x9a\x9a\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x91\x91\x91\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x9a\x9a\x9a\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x91\x91\x91\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x9a\x9a\x9a\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x91\x91\x91\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x9a\x9a\x9a\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x91\x91\x91\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\xff\xff\xff\xa6\x91\x91\x91\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6\x00\x00\x00\xa6'
192
 
 
193
 
    def add_holes(self, img):
194
 
        '''Add the holes image'''
195
 
        holes = Image.fromstring('RGBA', self.HOLES_SIZE, self.HOLES_DATA)
196
 
        holes_h = holes.size[1]
197
 
        remain = img.size[1] % holes_h
198
 
 
199
 
        i = 0
200
 
        nbands = 0
201
 
        while i < (img.size[1] - remain):
202
 
            left_box = (0, i, holes.size[0], (nbands+1) * holes.size[1])
203
 
            img.paste(holes, left_box)
204
 
 
205
 
            right_box = (img.size[0] - holes.size[0], i,
206
 
                         img.size[0], (nbands+1) * holes.size[1])
207
 
            img.paste(holes, right_box)
208
 
 
209
 
            i += holes_h
210
 
            nbands += 1
211
 
 
212
 
        remain_holes = holes.crop((0, 0, holes.size[0], remain))
213
 
        remain_holes.load()
214
 
        img.paste(remain_holes, (0, i, holes.size[0], img.size[1]))
215
 
        img.paste(remain_holes, (img.size[0] - holes.size[0], i,
216
 
                                 img.size[0], img.size[1]))
217
 
        return img
218
 
 
219
 
    def interesting_image(self, img):
220
 
        '''
221
 
        Checks an image to see if it has the characteristics of an
222
 
        'interesting' image, i.e. whether or not the image is worth
223
 
        examining for thumbnailing
224
 
        '''
225
 
        stat = ImageStat.Stat(img)
226
 
        return True in [ i > self.BORING_THRESHOLD for i in stat.var ]
227
 
 
228
 
    def set_pipeline_state(self, pipeline, state):
229
 
        '''
230
 
        Does exactly what it claims : sets the state of the pipeline, and
231
 
        returns the boolean value of whether or not is successfully changed
232
 
        state
233
 
        '''
234
 
        status = pipeline.set_state(state)
235
 
        if status == gst.STATE_CHANGE_ASYNC:
236
 
 
237
 
            result = [False]
238
 
            max_try = 100
239
 
            nb_try = 0
240
 
            while not result[0] == gst.STATE_CHANGE_SUCCESS:
241
 
                if nb_try > max_try:
242
 
                    #State change failed
243
 
                    return False
244
 
                nb_try += 1
245
 
                result = pipeline.get_state(50*gst.MSECOND)
246
 
 
247
 
            return True
248
 
        elif status == gst.STATE_CHANGE_SUCCESS:
249
 
            return True
250
 
        else:
251
 
            return False
252
 
 
253
 
 
254
 
    def create_thumbnail(self):
255
 
        '''
256
 
        Creates the new thumbnail based on the information provided through the
257
 
        gstreamer API.  The file is an md5 of the video's file name, followed
258
 
        by the .jpg extension
259
 
        '''
260
 
 
261
 
        if os.path.exists(self._thumb_file):
262
 
            return
263
 
 
264
 
        self.set_pipeline_state(self._pipeline, gst.STATE_NULL)
265
 
        self._pipeline.set_property('uri', self._fileuri)
266
 
 
267
 
        if not self.set_pipeline_state(self._pipeline, gst.STATE_PAUSED):
268
 
            self.set_pipeline_state(self._pipeline, gst.STATE_NULL)
269
 
            raise VideoThumbnailerException('Cannot start the pipeline')
270
 
 
271
 
        if self._sink.width == None or self._sink.height == None:
272
 
            self.set_pipeline_state(self._pipeline, gst.STATE_NULL)
273
 
            raise VideoThumbnailerException('Unable to determine media size')
274
 
        sink_size = (self._sink.width, self._sink.height)
275
 
 
276
 
        try:
277
 
            duration = self._pipeline.query_duration(gst.FORMAT_TIME)[0]
278
 
        except AssertionError:
279
 
            #Gstreamer cannot determine the media duration using
280
 
            #playing-thumbnailing for file
281
 
            self.set_pipeline_state(self._pipeline, gst.STATE_NULL)
282
 
 
283
 
            img = self._play_for_thumb(sink_size, 0)
284
 
            if img:
285
 
                img.save(self._thumb_file)
286
 
                return
287
 
        else:
288
 
            duration /= gst.NSECOND
289
 
            try:
290
 
                img = self._seek_for_thumb(duration, sink_size)
291
 
                #Seek found image
292
 
                if img:
293
 
                    img.save(self._thumb_file)
294
 
                    return
295
 
            except VideoThumbnailerException:
296
 
                #Fallback: No Image found in seek_for, falling back to
297
 
                #play_for_thumb
298
 
                self.set_pipeline_state(self._pipeline, gst.STATE_NULL)
299
 
                img = self._play_for_thumb(sink_size, duration)
300
 
                #Fallback-Play found img
301
 
                if img:
302
 
                    img.save(self._thumb_file)
303
 
                    return
304
 
 
305
 
        self.set_pipeline_state(self._pipeline, gst.STATE_NULL)
306
 
        raise VideoThumbnailerException(
307
 
            'Unable to create thumbnail.  Please file a bug')
308
 
 
309
 
 
310
 
    def _play_for_thumb(self, sink_size, duration=0):
311
 
        '''
312
 
        Plays the video file to gather information for generating a thumbnail
313
 
        '''
314
 
        self._img = None
315
 
 
316
 
        if duration >= 250000:
317
 
            self._every = 25
318
 
        elif duration >= 200000:
319
 
            self._every = 15
320
 
        elif duration >= 10000:
321
 
            self._every = 10
322
 
        elif duration >= 5000:
323
 
            self._every = 5
324
 
        else:
325
 
            self._every = 1
326
 
 
327
 
        #Setting every-frame to self._every
328
 
 
329
 
        self._every_co = self._every
330
 
 
331
 
        ## How often Proceed?
332
 
        self._counter = 5
333
 
 
334
 
        self.set_state_blocking(self._pipeline, gst.STATE_PLAYING)
335
 
 
336
 
        self._blocker.wait()
337
 
        self._pipeline.set_state(gst.STATE_NULL)
338
 
        return self._img
339
 
 
340
 
    def _seek_for_thumb(self, duration, sink_size):
341
 
        '''
342
 
        Seeks through the video file to gather information for generating a
343
 
        thumbnail
344
 
        '''
345
 
        frame_locations = [ 1.0 / 3.0, 2.0 / 3.0, 0.1, 0.9, 0.5 ]
346
 
 
347
 
        for location in frame_locations:
348
 
            abs_location = int(location * duration)
349
 
 
350
 
            if abs_location == 0:
351
 
                raise VideoThumbnailerException(
352
 
                    self.filename, 'Video has a size of zero')
353
 
 
354
 
            event = self._pipeline.seek(1.0, gst.FORMAT_TIME,
355
 
                gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT,
356
 
                gst.SEEK_TYPE_SET, abs_location,
357
 
                gst.SEEK_TYPE_NONE, 0)
358
 
            if not event:
359
 
                raise VideoThumbnailerException(self.filename,
360
 
                    'Unable to seek through video')
361
 
 
362
 
            if not self.set_pipeline_state(self._pipeline, gst.STATE_PAUSED):
363
 
                raise VideoThumbnailerException(self.filename,
364
 
                    'Unable to pause video')
365
 
 
366
 
            frame = self._sink.get_current_frame()
367
 
 
368
 
            img = Image.frombuffer(
369
 
                "RGB", sink_size, frame, "raw", "RGB", 0, 1)
370
 
 
371
 
            if self.interesting_image(img):
372
 
                break
373
 
            else:
374
 
                pass
375
 
 
376
 
        self._sink.reset()
377
 
 
378
 
        if img:
379
 
            img.thumbnail((self.MAX_SIZE, self.MAX_SIZE), Image.BILINEAR)
380
 
            if img.mode != 'RGBA':
381
 
                img = img.convert(mode='RGBA')
382
 
            self.set_pipeline_state(self._pipeline, gst.STATE_NULL)
383
 
            return img
384
 
 
385
 
gobject.type_register(VideoThumbnailer.VideoSinkBin)