~ubuntu-branches/ubuntu/natty/miro/natty

« back to all changes in this revision

Viewing changes to windows/plat/renderers/vlc.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-01-22 02:46:33 UTC
  • mfrom: (1.4.10 upstream) (1.7.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20110122024633-kjme8u93y2il5nmf
Tags: 3.5.1-1ubuntu1
* Merge from debian.  Remaining ubuntu changes:
  - Use python 2.7 instead of python 2.6
  - Relax dependency on python-dbus to >= 0.83.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Miro - an RSS based video player application
 
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
17
#
 
18
# In addition, as a special exception, the copyright holders give
 
19
# permission to link the code of portions of this program with the OpenSSL
 
20
# library.
 
21
#
 
22
# You must obey the GNU General Public License in all respects for all of
 
23
# the code used other than OpenSSL. If you modify file(s) with this
 
24
# exception, you may extend this exception to your version of the file(s),
 
25
# but you are not obligated to do so. If you do not wish to do so, delete
 
26
# this exception statement from your version. If you delete this exception
 
27
# statement from all source files in the program, then also delete it here.
 
28
 
 
29
import ctypes
 
30
import logging
 
31
import os
 
32
import traceback
 
33
import urllib
 
34
import logging
 
35
 
 
36
import gtk
 
37
import gobject
 
38
 
 
39
from miro.gtcache import gettext as _
 
40
from miro.plat import resources
 
41
from miro import app
 
42
from miro import config
 
43
from miro import prefs
 
44
from miro.frontends.widgets import menus
 
45
from miro.frontends.widgets.widgetconst import MAX_VOLUME
 
46
from miro.util import copy_subtitle_file
 
47
 
 
48
# load the DLL
 
49
libvlc = ctypes.cdll.libvlc
 
50
libvlccore = ctypes.cdll.libvlccore
 
51
 
 
52
# Pick out functions that start with "__".  It's very awkward to use them
 
53
# inside classes.
 
54
vlc_object_release = libvlccore.__vlc_object_release
 
55
 
 
56
config_GetInt = libvlccore.__config_GetInt
 
57
config_PutInt = libvlccore.__config_PutInt
 
58
config_GetFloat = libvlccore.__config_GetFloat
 
59
config_PutFloat = libvlccore.__config_PutFloat
 
60
config_GetPsz = libvlccore.__config_GetPsz
 
61
config_PutPsz = libvlccore.__config_PutPsz
 
62
 
 
63
 
 
64
# set up the function signatures
 
65
libvlc_MediaStateChanged = 5
 
66
 
 
67
(libvlc_NothingSpecial,
 
68
 libvlc_Opening,
 
69
 libvlc_Buffering,
 
70
 libvlc_Playing,
 
71
 libvlc_Paused,
 
72
 libvlc_Stopped,
 
73
 libvlc_Ended,
 
74
 libvlc_Error) = range(8)
 
75
 
 
76
# Win32 Function
 
77
EnableWindow = ctypes.windll.user32.EnableWindow
 
78
 
 
79
class VLCError(Exception):
 
80
    pass
 
81
 
 
82
class VLCException(ctypes.Structure):
 
83
    _fields_ = [
 
84
            ('raised', ctypes.c_int),
 
85
            ('code', ctypes.c_int),
 
86
            ('message', ctypes.c_char_p)
 
87
    ]
 
88
 
 
89
    def __init__(self):
 
90
        ctypes.Structure.__init__(self)
 
91
        libvlc.libvlc_exception_init(self.ref())
 
92
 
 
93
    def ref(self):
 
94
        return ctypes.byref(self)
 
95
 
 
96
    def check(self):
 
97
        if self.raised:
 
98
            msg = self.message
 
99
            libvlc.libvlc_exception_clear(self.ref())
 
100
            raise VLCError(repr(self.code) + " " + repr(msg))
 
101
 
 
102
class VLCEvent(ctypes.Structure):
 
103
    _fields_ = [
 
104
        ('type', ctypes.c_int),
 
105
        ('p_obj', ctypes.c_void_p),
 
106
        ('arg1', ctypes.c_int),
 
107
        ('arg2', ctypes.c_int),
 
108
    ]
 
109
 
 
110
class VLCTrackDescription(ctypes.Structure):
 
111
    # The libvlc_track_description_t structure type is
 
112
    # self-referencing so we have to specify the fields after the
 
113
    # class is defined.
 
114
    pass
 
115
 
 
116
VLCTrackDescription._fields_ = [
 
117
    ('id', ctypes.c_int),
 
118
    ('name', ctypes.c_char_p),
 
119
    ('next', ctypes.POINTER(VLCTrackDescription))
 
120
    ]
 
121
 
 
122
libvlc.libvlc_video_get_spu_description.restype = ctypes.POINTER(
 
123
    VLCTrackDescription)
 
124
libvlc.libvlc_video_get_track_description.restype = ctypes.POINTER(
 
125
    VLCTrackDescription)
 
126
 
 
127
VLC_EVENT_CALLBACK = ctypes.CFUNCTYPE(
 
128
    None, ctypes.POINTER(VLCEvent), ctypes.c_void_p)
 
129
 
 
130
def make_string_list(args):
 
131
    ArgsArray = ctypes.c_char_p * len(args)
 
132
    return ArgsArray(*args)
 
133
 
 
134
STOPPED, PAUSED, PLAYING = range(3)
 
135
 
 
136
class VLCSniffer(object):
 
137
    def __init__(self):
 
138
        plugin_dir = os.path.join(resources.appRoot(), 'vlc-plugins')
 
139
        self.exc = VLCException()
 
140
 
 
141
        # Note: if you need vlc output to stdout, remove the --quiet
 
142
        # from the list of arguments.  Also, you can add -vvv.
 
143
        vlc_args = [
 
144
            "vlc", '--quiet',
 
145
            '--nostats', '--intf', 'dummy', '--volume=0',
 
146
            '--no-video-title-show', '--plugin-path', plugin_dir
 
147
        ]
 
148
        self.vlc = libvlc.libvlc_new(
 
149
            len(vlc_args), make_string_list(vlc_args), self.exc.ref())
 
150
        self.exc.check()
 
151
        self.media_player = libvlc.libvlc_media_player_new(
 
152
            self.vlc, self.exc.ref())
 
153
        self.exc.check()
 
154
        self._callback_ref = VLC_EVENT_CALLBACK(self.event_callback)
 
155
        self._filename = None
 
156
        self.media_playing = None
 
157
        self.callback_info = None
 
158
 
 
159
        self._hidden_window = gtk.gdk.Window(
 
160
            None, x=0, y=0, width=1, height=1,
 
161
            window_type=gtk.gdk.WINDOW_TOPLEVEL,
 
162
            wclass=gtk.gdk.INPUT_OUTPUT, event_mask=0)
 
163
        libvlc.libvlc_media_player_set_hwnd(
 
164
            self.media_player, self._hidden_window.handle, self.exc.ref())
 
165
        self.exc.check()
 
166
 
 
167
    def shutdown(self):
 
168
        logging.info("shutting down VLC Sniffer")
 
169
        libvlc.libvlc_media_player_release(self.media_player)
 
170
        libvlc.libvlc_release(self.vlc)
 
171
 
 
172
    def event_callback(self, p_event, p_user_data):
 
173
        event = p_event[0]
 
174
        # Copy the values from event, the memory might be freed by the
 
175
        # time handle_event gets called.
 
176
        obj = event.p_obj
 
177
        type_ = event.type
 
178
        arg1 = event.arg1
 
179
        arg2 = event.arg2
 
180
        gobject.idle_add(self.handle_event, obj, type_, arg1, arg2)
 
181
 
 
182
    def handle_event(self, obj, type_, state, arg2):
 
183
        if type_ != libvlc_MediaStateChanged:
 
184
            return 
 
185
        if obj != self.media_playing:
 
186
            return
 
187
        if self.callback_info is None:
 
188
            # We the video has already been opened (successfully or
 
189
            # not)
 
190
            if state == libvlc_Ended:
 
191
                app.playback_manager.on_movie_finished()
 
192
 
 
193
        else:
 
194
            # We are waiting to see if the video opens successfully
 
195
            if state in (libvlc_Error, libvlc_Ended):
 
196
                self._open_failure()
 
197
            elif state == libvlc_Playing:
 
198
                libvlc.libvlc_media_player_pause(
 
199
                    self.media_player, self.exc.ref())
 
200
                self.exc.check()
 
201
                self._open_success()
 
202
 
 
203
    def _open_success(self):
 
204
        # FIXME - sometimes _open_success is called, but callback_info
 
205
        # is None.  not sure why this happens.
 
206
        item_type = "failure"
 
207
        if self.callback_info:
 
208
            video_tracks = libvlc.libvlc_video_get_track_count(
 
209
                self.media_player, self.exc.ref())
 
210
            try:
 
211
                self.exc.check()
 
212
            except VLCError:
 
213
                video_tracks = 0
 
214
            audio_tracks = libvlc.libvlc_audio_get_track_count(
 
215
                self.media_player, self.exc.ref())
 
216
            try:
 
217
                self.exc.check()
 
218
            except VLCError:
 
219
                audio_tracks = 0
 
220
 
 
221
            if video_tracks > 0:
 
222
                item_type = "video"
 
223
            elif audio_tracks > 0:
 
224
                item_type = "audio"
 
225
            else:
 
226
                item_type = "unplayable"
 
227
        try:
 
228
            libvlc.libvlc_media_player_stop(self.media_player, self.exc.ref())
 
229
            self.exc.check()
 
230
        except VLCError, vlce:
 
231
            logging.warning("sniffer reset failed: %s", vlce)
 
232
        self.callback_info[0](item_type)
 
233
        self.callback_info = None
 
234
        self.media_playing = None
 
235
 
 
236
    def _open_failure(self):
 
237
        try:
 
238
            libvlc.libvlc_media_player_stop(self.media_player, self.exc.ref())
 
239
            self.exc.check()
 
240
        except VLCError, vlce:
 
241
            logging.warning("sniffer reset failed: %s", vlce)
 
242
        self.callback_info[1]()
 
243
        self.callback_info = None
 
244
        self.media_playing = None
 
245
 
 
246
    def select_file(self, iteminfo, success_callback, error_callback):
 
247
        """starts playing the specified file"""
 
248
        filename = iteminfo.video_path
 
249
 
 
250
        # filenames coming in are unicode objects, VLC expects utf-8
 
251
        # strings.
 
252
        filename = filename.encode('utf-8')
 
253
        self._filename = filename
 
254
        self.callback_info = (success_callback, error_callback)
 
255
        self.play_state = STOPPED
 
256
 
 
257
        media = libvlc.libvlc_media_new(self.vlc, ctypes.c_char_p(filename),
 
258
                self.exc.ref())
 
259
        self.exc.check()
 
260
        if media is None:
 
261
            raise AssertionError(
 
262
                "libvlc_media_new returned NULL for %s" % filename)
 
263
        event_manager = libvlc.libvlc_media_event_manager(media, 
 
264
                                                          self.exc.ref())
 
265
        self.exc.check()
 
266
        libvlc.libvlc_event_attach(event_manager, 
 
267
                                   libvlc_MediaStateChanged,
 
268
                                   self._callback_ref, 
 
269
                                   None, 
 
270
                                   self.exc.ref())
 
271
        self.exc.check()
 
272
        try:
 
273
            libvlc.libvlc_media_player_set_media(self.media_player, 
 
274
                                                 media,
 
275
                                                 self.exc.ref())
 
276
            self.exc.check()
 
277
        finally:
 
278
            libvlc.libvlc_media_release(media)
 
279
        self.media_playing = media
 
280
        # We want to load the media to test if we can play it.  The
 
281
        # best way that I can see to do that is to play it, then pause
 
282
        # once we see it's opened in the event_callack method.
 
283
        libvlc.libvlc_media_player_play(self.media_player, self.exc.ref())
 
284
        self.exc.check()
 
285
 
 
286
        libvlc.libvlc_media_player_pause(self.media_player, self.exc.ref())
 
287
        self.exc.check()
 
288
 
 
289
class VLCRenderer(object):
 
290
    def __init__(self):
 
291
        logging.info("Initializing VLC")
 
292
        plugin_dir = os.path.join(resources.appRoot(), 'vlc-plugins')
 
293
        self.exc = VLCException()
 
294
 
 
295
        # Note: if you need vlc output to stdout, remove the --quiet
 
296
        # from the list of arguments.
 
297
        vlc_args = [
 
298
            "vlc", '--quiet', '--nostats', '--intf', 'dummy',
 
299
            '--no-video-title-show', '--plugin-path', plugin_dir
 
300
        ]
 
301
        self.vlc = libvlc.libvlc_new(len(vlc_args),
 
302
                make_string_list(vlc_args), self.exc.ref())
 
303
        self.exc.check()
 
304
        self.vlc_instance = libvlc.libvlc_get_vlc_instance(self.vlc)
 
305
        self.media_player = libvlc.libvlc_media_player_new(self.vlc,
 
306
                self.exc.ref())
 
307
        self.exc.check()
 
308
        self._callback_ref = VLC_EVENT_CALLBACK(self.event_callback)
 
309
        self.play_from_time = None
 
310
        self.play_state = STOPPED
 
311
        self._duration = None
 
312
        self._filename = None
 
313
        self._rate = 1.0
 
314
        self.media_playing = None
 
315
        self.callback_info = None
 
316
        self._change_subtitle_timout = None
 
317
        self.subtitle_info = []
 
318
        self._hidden_window = gtk.gdk.Window(
 
319
            None, x=0, y=0, width=1, height=1,
 
320
            window_type=gtk.gdk.WINDOW_TOPLEVEL,
 
321
            wclass=gtk.gdk.INPUT_OUTPUT, event_mask=0)
 
322
        self.unset_widget()
 
323
 
 
324
    def shutdown(self):
 
325
        logging.info("shutting down VLC")
 
326
        self.reset()
 
327
        libvlc.libvlc_media_player_release(self.media_player)
 
328
        vlc_object_release(self.vlc_instance)
 
329
        libvlc.libvlc_release(self.vlc)
 
330
        _sniffer.shutdown()
 
331
 
 
332
    def event_callback(self, p_event, p_user_data):
 
333
        event = p_event[0]
 
334
        # Copy the values from event, the memory might be freed by the
 
335
        # time handle_event gets called.
 
336
        obj = event.p_obj
 
337
        type_ = event.type
 
338
        arg1 = event.arg1
 
339
        arg2 = event.arg2
 
340
        gobject.idle_add(self.handle_event, obj, type_, arg1, arg2)
 
341
 
 
342
    def handle_event(self, obj, type_, arg1, arg2):
 
343
        if type_ == libvlc_MediaStateChanged:
 
344
            self._handle_state_change(obj, arg1)
 
345
        else:
 
346
            logging.warn("Unknown VLC event type: %s", type_)
 
347
 
 
348
    def _handle_state_change(self, obj, state):
 
349
        if obj != self.media_playing:
 
350
            return
 
351
        if self.callback_info is None:
 
352
            # We the video has already been opened (successfully or
 
353
            # not)
 
354
            if state == libvlc_Ended:
 
355
                app.playback_manager.on_movie_finished()
 
356
 
 
357
        else:
 
358
            # We are waiting to see if the video opens successfully
 
359
            if state in (libvlc_Error, libvlc_Ended):
 
360
                self._open_failure()
 
361
            elif state == libvlc_Playing:
 
362
                libvlc.libvlc_media_player_pause(self.media_player,
 
363
                        self.exc.ref())
 
364
                self.exc.check()
 
365
                self._length_check()
 
366
 
 
367
    def _length_check(self, attempt=0):
 
368
        # sometimes garbage data will appear to open, but it VLC won't
 
369
        # actually play anything.  Use the length to double check that
 
370
        # we actually will play.  We try three attempts because
 
371
        # sometimes it takes a bit to figure out the length.
 
372
        if attempt > 3:
 
373
            self._open_failure()
 
374
            return
 
375
        if self._file_type != 'video':
 
376
            # for items the user has marked as audio, disable video
 
377
            # output #12692
 
378
            self._disable_video()
 
379
 
 
380
        length = libvlc.libvlc_media_player_get_length(
 
381
            self.media_player, self.exc.ref())
 
382
        self.exc.check()
 
383
 
 
384
        if length > 0:
 
385
            self._open_success()
 
386
        else:
 
387
            gobject.timeout_add(500, self._length_check, attempt+1)
 
388
 
 
389
    def _open_success(self):
 
390
        # FIXME - sometimes _open_success is called, but callback_info
 
391
        # is None.  not sure why this happens.
 
392
        self.setup_subtitles()
 
393
        if self.callback_info:
 
394
            self.callback_info[0]()
 
395
        self.callback_info = None
 
396
 
 
397
    def _open_failure(self):
 
398
        logging.info("_open_failure\n%s", "".join(traceback.format_stack()))
 
399
        self.callback_info[1]()
 
400
        self.callback_info = None
 
401
        self.media_playing = None
 
402
 
 
403
    def _disable_video(self):
 
404
        desc = libvlc.libvlc_video_get_track_description(
 
405
                self.media_player, self.exc.ref())
 
406
        self.exc.check()
 
407
        # the 1st description should be "Disable"
 
408
        if desc:
 
409
            track_id = desc.contents.id
 
410
            libvlc.libvlc_track_description_release(desc)
 
411
            libvlc.libvlc_video_set_track(self.media_player, track_id,
 
412
                    self.exc.ref())
 
413
            self.exc.check()
 
414
 
 
415
    def set_widget(self, widget):
 
416
        hwnd = widget.persistent_window.handle
 
417
        libvlc.libvlc_media_player_set_hwnd(self.media_player, hwnd,
 
418
                                                self.exc.ref())
 
419
        self.exc.check()
 
420
 
 
421
        widget.add_events(gtk.gdk.EXPOSURE_MASK)
 
422
        widget.connect('expose-event', self._on_expose)
 
423
        EnableWindow(hwnd, 0)
 
424
 
 
425
    def unset_widget(self):
 
426
        libvlc.libvlc_media_player_set_hwnd(
 
427
            self.media_player, self._hidden_window.handle, self.exc.ref())
 
428
        self.exc.check()
 
429
 
 
430
    def _on_expose(self, widget, event):
 
431
        gc = widget.style.black_gc
 
432
        widget.persistent_window.draw_rectangle(
 
433
            gc, True, event.area.x, event.area.y, 
 
434
            event.area.width, event.area.height)
 
435
 
 
436
    def select_file(self, iteminfo, callback, errback):
 
437
        """starts playing the specified file"""
 
438
        filename = iteminfo.video_path
 
439
 
 
440
        # filenames coming in are unicode objects, VLC expects utf-8
 
441
        # strings.
 
442
        filename = filename.encode('utf-8')
 
443
        self._filename = filename
 
444
        self._file_type = iteminfo.file_type
 
445
        self.subtitle_info = []
 
446
        self.callback_info = (callback, errback)
 
447
        self.play_from_time = None
 
448
        self.play_state = STOPPED
 
449
 
 
450
        media = libvlc.libvlc_media_new(self.vlc, ctypes.c_char_p(filename),
 
451
                                        self.exc.ref())
 
452
        self.exc.check()
 
453
        if media is None:
 
454
            raise AssertionError(
 
455
                "libvlc_media_new returned NULL for %s" % filename)
 
456
        event_manager = libvlc.libvlc_media_event_manager(media, 
 
457
                                                          self.exc.ref())
 
458
        self.exc.check()
 
459
        libvlc.libvlc_event_attach(event_manager, libvlc_MediaStateChanged,
 
460
                                   self._callback_ref, None, self.exc.ref())
 
461
        self.exc.check()
 
462
        try:
 
463
            libvlc.libvlc_media_player_set_media(self.media_player, media,
 
464
                                                 self.exc.ref())
 
465
            self.exc.check()
 
466
        finally:
 
467
            libvlc.libvlc_media_release(media)
 
468
        self.media_playing = media
 
469
        self.setup_subtitle_font()
 
470
        # We want to load the media to test if we can play it.  The
 
471
        # best way that I can see to do that is to play it, then pause
 
472
        # once we see it's opened in the event_callack method.
 
473
        libvlc.libvlc_media_player_play(self.media_player, self.exc.ref())
 
474
        self.exc.check()
 
475
        # For unknown reasons, sometimes we don't see the state
 
476
        # changed event if they happen quickly enough.  To work around
 
477
        # that, check the initial state of the media player.
 
478
        state = libvlc.libvlc_media_player_get_state(self.media_player,
 
479
                self.exc.ref())
 
480
        self.exc.check()
 
481
        self._handle_state_change(self.media_playing, state)
 
482
 
 
483
    def play(self):
 
484
        if self.play_state == PLAYING:
 
485
            return
 
486
        libvlc.libvlc_media_player_play(self.media_player, self.exc.ref())
 
487
        self.exc.check()
 
488
        self.play_state = PLAYING
 
489
        if self.play_from_time is not None:
 
490
            self.set_current_time(self.play_from_time)
 
491
            self.play_from_time = None
 
492
 
 
493
    def pause(self):
 
494
        if self.play_state == PAUSED:
 
495
            return
 
496
        libvlc.libvlc_media_player_pause(self.media_player, self.exc.ref())
 
497
        self.exc.check()
 
498
        self.play_state = PAUSED
 
499
 
 
500
    def stop(self):
 
501
        if self.play_state == STOPPED:
 
502
            return
 
503
        self.callback_info = None
 
504
        self.media_playing = None
 
505
        libvlc.libvlc_media_player_stop(self.media_player, self.exc.ref())
 
506
        self.exc.check()
 
507
        self.play_state = STOPPED
 
508
        self.subtitle_info = []
 
509
 
 
510
    def reset(self):
 
511
        self.stop()
 
512
        self.play_from_time = None
 
513
        self.play_state = STOPPED
 
514
 
 
515
    def get_current_time(self):
 
516
        t = libvlc.libvlc_media_player_get_time(
 
517
            self.media_player, self.exc.ref())
 
518
        try:
 
519
            self.exc.check()
 
520
        except VLCError, e:
 
521
            logging.warn("exception getting time: %s" % e)
 
522
            return None
 
523
 
 
524
        return t / 1000.0
 
525
 
 
526
    def set_current_time(self, seconds):
 
527
        if not self.play_state == PLAYING:
 
528
            self.play_from_time = seconds
 
529
            return
 
530
        t = int(seconds * 1000)
 
531
        if t == 0:
 
532
            # I have no clue why this this is, but setting time=1
 
533
            # (1/1000th of a second) fixes #15079
 
534
            t = 1
 
535
        libvlc.libvlc_media_player_set_time(
 
536
            self.media_player, ctypes.c_longlong(t), self.exc.ref())
 
537
        try:
 
538
            self.exc.check()
 
539
        except VLCError, e:
 
540
            logging.warn("exception setting current time %s" % e)
 
541
 
 
542
    def get_duration(self):
 
543
        # self._duration = (filename, duration)
 
544
        if self._duration and self._duration[0] == self._filename:
 
545
            return self._duration[1]
 
546
 
 
547
        length = libvlc.libvlc_media_player_get_length(
 
548
            self.media_player, self.exc.ref())
 
549
        try:
 
550
            self.exc.check()
 
551
        except VLCError, e:
 
552
            logging.warn("exception getting duration: %s" % e)
 
553
            return None
 
554
 
 
555
        self._duration = (self._filename, length / 1000.0)
 
556
        return self._duration[1]
 
557
 
 
558
    def set_volume(self, volume):
 
559
        volume = int(200 * volume / MAX_VOLUME)
 
560
        libvlc.libvlc_audio_set_volume(self.vlc, volume, self.exc.ref())
 
561
        self.exc.check()
 
562
 
 
563
    def get_volume(self, volume):
 
564
        rv = libvlc.libvlc_audio_get_volume(self.vlc, self.exc.ref())
 
565
        self.exc.check()
 
566
        return rv / 100.0
 
567
 
 
568
    def set_rate(self, rate):
 
569
        logging.info("set_rate: rate %s", rate)
 
570
        if self._rate == rate:
 
571
            return
 
572
        self._rate = rate
 
573
        libvlc.libvlc_media_player_set_rate(self.media_player, 
 
574
                ctypes.c_float(rate), self.exc.ref())
 
575
        try:
 
576
            self.exc.check()
 
577
        except VLCError, e:
 
578
            logging.warn("exception setting rate: %s" % e)
 
579
            return None
 
580
 
 
581
    def get_rate(self):
 
582
        pass
 
583
 
 
584
    def setup_subtitles(self):
 
585
        self.setup_subtitle_info()
 
586
        if config.get(prefs.ENABLE_SUBTITLES):
 
587
            track_index = self.get_enabled_subtitle_track()
 
588
            if track_index == 0:
 
589
                count = libvlc.libvlc_video_get_spu_count(
 
590
                    self.media_player, self.exc.ref())
 
591
                if count > 1:
 
592
                    self.enable_subtitle_track(1)
 
593
        else:
 
594
            self.disable_subtitles()
 
595
 
 
596
    def setup_subtitle_font(self):
 
597
        font_path = config.get(prefs.SUBTITLE_FONT)
 
598
        config_PutPsz(self.vlc_instance,
 
599
                ctypes.c_char_p('freetype-font'),
 
600
                ctypes.c_char_p(font_path))
 
601
        logging.info("Setting VLC subtitle font: %s", font_path)
 
602
        
 
603
    def get_subtitle_tracks(self):
 
604
        return self.subtitle_info
 
605
 
 
606
    def setup_subtitle_info(self):
 
607
        self.subtitle_info = list()
 
608
        try:
 
609
            desc = libvlc.libvlc_video_get_spu_description(
 
610
                self.media_player, self.exc.ref())
 
611
            self.exc.check()
 
612
            count = libvlc.libvlc_video_get_spu_count(
 
613
                self.media_player, self.exc.ref())
 
614
            self.exc.check()
 
615
            first_desc = desc
 
616
            for i in range(0, count):
 
617
                if i > 0: # track 0 is "disabled", don't include it
 
618
                    self.subtitle_info.append((i, desc.contents.name))
 
619
                desc = desc.contents.next
 
620
            libvlc.libvlc_track_description_release(first_desc)
 
621
        except VLCError, e:
 
622
            logging.warn("exception when getting list of subtitle tracks")
 
623
 
 
624
    def get_enabled_subtitle_track(self):
 
625
        track_index = libvlc.libvlc_video_get_spu(
 
626
            self.media_player, self.exc.ref())
 
627
        try:
 
628
            self.exc.check()
 
629
        except VLCError, e:
 
630
            logging.warn("exception when getting enabled subtitle track")
 
631
            return None
 
632
        return track_index
 
633
 
 
634
    def enable_subtitle_track(self, track_index):
 
635
        if self._change_subtitle_timout:
 
636
            gobject.source_remove(self._change_subtitle_timout)
 
637
            self._change_subtitle_timout = None
 
638
        self._set_active_subtitle_track(track_index)
 
639
 
 
640
    def disable_subtitles(self):
 
641
        self._set_active_subtitle_track(0)
 
642
        
 
643
    def _set_active_subtitle_track(self, track_index):
 
644
        count = libvlc.libvlc_video_get_spu_count(
 
645
            self.media_player, self.exc.ref())
 
646
        self.exc.check()
 
647
 
 
648
        # if we're disabling subtitles but there aren't any, we
 
649
        # just return
 
650
        if track_index == 0 and count == 0:
 
651
            return
 
652
 
 
653
        if track_index >= count:
 
654
            logging.warn("Subtitle track too high: %s (count: %s)",
 
655
                    track_index, count)
 
656
 
 
657
        libvlc.libvlc_video_set_spu(self.media_player, track_index, 
 
658
                                    self.exc.ref())
 
659
        try:
 
660
            self.exc.check()
 
661
        except VLCError, e:
 
662
            logging.warn("exception when setting subtitle track: %s", e)
 
663
 
 
664
    def select_subtitle_file(self, item, sub_path, handle_successful_select):
 
665
        try:
 
666
            sub_path = copy_subtitle_file(sub_path, item.video_path)
 
667
        except WindowsError:
 
668
            # FIXME - need a better way to deal with this.  when this
 
669
            # happens, then the subtitle file isn't in the right place
 
670
            # for VLC to pick it up on the next playback forcing the
 
671
            # user to select it again.
 
672
            # This is bug 12813.
 
673
            logging.exception("exception thrown when copying subtitle file")
 
674
 
 
675
        sub_path = sub_path.encode('utf-8')
 
676
        res = libvlc.libvlc_video_set_subtitle_file(
 
677
            self.media_player, ctypes.c_char_p(sub_path), self.exc.ref())
 
678
        try:
 
679
            self.exc.check()
 
680
        except VLCError, e:
 
681
            logging.warn("exception when setting subtitle track to file: %s", e)
 
682
        else:
 
683
            handle_successful_select()
 
684
            # 1 is the track of the external file, don't select it quite yet
 
685
            # because VLC might not be ready.  (#12858)
 
686
            self._change_subtitle_timout = gobject.timeout_add(100,
 
687
                    self.handle_change_subtitle_timout)
 
688
 
 
689
    def handle_change_subtitle_timout(self):
 
690
        self._change_subtitle_timout = None
 
691
        self.setup_subtitle_info()
 
692
        self.enable_subtitle_track(1)
 
693
 
 
694
    def select_subtitle_encoding(self, encoding):
 
695
        if self.media_playing is None:
 
696
            return
 
697
        if encoding is None:
 
698
            encoding = ''
 
699
        config_PutPsz(self.vlc_instance,
 
700
                ctypes.c_char_p('subsdec-encoding'),
 
701
                ctypes.c_char_p(encoding))
 
702
        logging.info("Setting VLC subtitle encoding: %s", encoding)
 
703
 
 
704
    def setup_subtitle_encoding_menu(self, menubar):
 
705
        menus.add_subtitle_encoding_menu(menubar, _('Eastern European'),
 
706
                ('Latin-7', _('Baltic')),
 
707
                ('Windows-1257', _('Baltic')),
 
708
                ('Latin-2', _('Eastern European')),
 
709
                ('Windows-1250', _('Eastern European')),
 
710
                ('KOI8-R', _('Russian')),
 
711
                ('Latin-10', _('South-Eastern European')),
 
712
                ('KOI8-U', _('Ukrainian')),
 
713
        )
 
714
 
 
715
        menus.add_subtitle_encoding_menu(menubar, _('Western European'),
 
716
                ('Latin-8', _('Celtic')),
 
717
                ('Windows-1252', _('Western European')),
 
718
                ('Latin-3', _('Esperanto')),
 
719
                ('ISO 8859-7', _('Greek')),
 
720
                ('Windows-1253', _('Greek')),
 
721
        )
 
722
 
 
723
        menus.add_subtitle_encoding_menu(menubar, _('East Asian'),
 
724
                ('GB18030', _('Universal Chinese')),
 
725
                ('ISO-2022-CN-EXT', _('Simplified Chinese')),
 
726
                ('EUC-CN', _('Simplified Chinese Unix')),
 
727
                ('7-bits JIS/ISO-2022-JP-2', _('Japanese')),
 
728
                ('EUC-JP', _('Japanese Unix')),
 
729
                ('Shift JIS', _('Japanese')),
 
730
                ('EUC-KR/CP949', _('Korean')),
 
731
                ('ISO-2022-KR', _('Korean')),
 
732
                ('Big5', _('Traditional Chinese')),
 
733
                ('EUC-TW', _('Traditional Chinese Unix')),
 
734
                ('HKSCS', _('Hong-Kong Supplementary')),
 
735
        )
 
736
 
 
737
        menus.add_subtitle_encoding_menu(menubar, _('SE and SW Asian'),
 
738
                ('ISO 8859-9', _('Turkish')),
 
739
                ('Windows-1254', _('Turkish')),
 
740
                ('Windows-874', _('Thai')),
 
741
                ('VISCII', _('Vietnamese')),
 
742
                ('Windows-1258', _('Vietnamese')),
 
743
        )
 
744
 
 
745
        menus.add_subtitle_encoding_menu(menubar, _('Middle Eastern'),
 
746
                ('ISO 8859-6', _('Arabic')),
 
747
                ('Windows-1256', _('Arabic')),
 
748
                ('ISO 8859-8', _('Hebrew')),
 
749
                ('Windows-1255', _('Hebrew')),
 
750
        )
 
751
 
 
752
        menus.add_subtitle_encoding_menu(menubar, _('Unicode'),
 
753
                ('UTF-8', _('Universal')),
 
754
                ('UTF-16', _('Universal')),
 
755
                ('UTF-16BE', _('Universal')),
 
756
                ('UTF-16LE', _('Universal')),
 
757
        )
 
758
 
 
759
 
 
760
 
 
761
_sniffer = VLCSniffer()
 
762
 
 
763
def get_item_type(item_info, success_callback, error_callback):
 
764
    _sniffer.select_file(item_info, success_callback, error_callback)