fix if initial audio track is ac-3
[vuplus_dvbapp-plugin] / vlcplayer / src / VlcPlayer.py
1 # -*- coding: ISO-8859-1 -*-
2 #===============================================================================
3 # VLC Player Plugin by A. Lätsch 2007
4 #
5 # This is free software; you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation; either version 2, or (at your option) any later
8 # version.
9 #===============================================================================
10
11 from enigma import iPlayableServicePtr
12 from time import time
13 from Screens.Screen import Screen
14 from Screens.MessageBox import MessageBox
15 from Screens.InfoBarGenerics import InfoBarNotifications, InfoBarAudioSelection
16 from Components.config import config
17 from enigma import eServiceReference
18 from Components.Sources.Source import Source
19 from Components.ServiceEventTracker import ServiceEventTracker
20 from enigma import iPlayableService
21 from enigma import eTimer
22 from Components.ActionMap import ActionMap
23 from VlcControlHttp import VlcControlHttp
24 import re
25
26 DEFAULT_VIDEO_PID = 0x44
27 DEFAULT_AUDIO_PID = 0x45
28
29 try:
30         import servicets
31 except Exception, e:
32         ENIGMA_SERVICE_ID = 0x1001
33         STOP_BEFORE_UNPAUSE = True
34         print "[VLC] use Gstreamer", e
35 else:
36         ENIGMA_SERVICE_ID = 0x1002
37         STOP_BEFORE_UNPAUSE = False
38         print "[VLC] use servicets.so"
39
40 def isDvdUrl(url):
41         return url.startswith("dvd://") or url.startswith("dvdsimple://")
42
43 def splitDvdUrl(url):
44         pos = url.rfind("@", len(url)-8)
45         if pos > 0:
46                 track = url[pos+1:]
47                 url = url[0:pos]
48                 if track.find(":") >= 0:
49                         track, chapter = track.split(":")
50                 else:
51                         chapter = None
52         else:
53                 track, chapter = (None, None)
54         return (url, track, chapter)
55
56 class VlcService(Source, iPlayableServicePtr):
57         refreshInterval = 3000
58         
59         class Info:
60                 def __init__(self, name=""):
61                         self.name = name
62                 def getName(self):
63                         return self.name
64                 def getInfoObject(self, *args, **kwargs):
65                         return { }
66                 def getInfo(self, what):
67                         return -1
68                 def getInfoString(self, *args, **kwargs):
69                         return self.name
70                 def isPlayable(self):
71                         return True
72                 def getEvent(self, what):
73                         return None
74
75         def __init__(self, player):
76                 Source.__init__(self)
77                 self.__info = VlcService.Info()
78                 self.vlccontrol = None
79                 self.service = self
80                 self.player = player
81                 self.lastrefresh = time()
82                 self.stats = None
83                 self.refreshTimer = eTimer()
84                 self.refreshTimer.timeout.get().append(self.__onRefresh)
85                 self.refreshTimer.start(self.refreshInterval)
86         
87         def setFilename(self, filename):
88                 i = filename.rfind("/")
89                 if i >= 0:
90                         filename = filename[i+1:]
91                 i = filename.rfind("\\")
92                 if i >= 0:
93                         filename = filename[i+1:]
94                 self.__info.name = filename
95                 self.setChanged()
96         
97         def setChanged(self):
98                 self.changed( (self.CHANGED_SPECIFIC, iPlayableService.evStart) )
99         
100         def setControl(self, control):
101                 self.vlccontrol = control
102                 
103         def __onRefresh(self):
104                 if self.vlccontrol is None: 
105                         self.stats = None
106                         return
107                 print "[VLC] refresh"
108                 try:
109                         self.stats = self.vlccontrol.status()
110                         self.lastrefresh = time()
111                 except Exception, e:
112                         print e
113         
114         def refresh(self):
115                 self.__onRefresh()
116         
117         def info(self):
118                 return self.__info
119         
120         # iSeekableService
121         def seek(self):
122                 return self
123         def getPlayPosition(self):
124                 if self.stats and self.stats.has_key("time"):
125                         pos = float(self.stats["time"])
126                         if self.player.state == VlcPlayer.STATE_PLAYING:
127                                 pos += time() - self.lastrefresh
128                         return (False, int(pos*90000))
129                 else:
130                         return (True, 0)
131         
132         def getLength(self):
133                 if self.stats and self.stats.has_key("length"):
134                         return (False, int(self.stats["length"])*90000)
135                 else:
136                         return (True, 0)
137         
138         # iPlayableService
139         def cueSheet(self): return None
140         def pause(self): return self.player
141         def audioTracks(self): 
142                 return self.player.audioTracks();
143         def audioChannel(self): return None
144         def subServices(self): return None
145         def frontendInfo(self): return None
146         def timeshift(self): return None
147         def subtitle(self): return None
148         def audioDelay(self): return None
149         def rdsDecoder(self): return None
150         def stream(self): return None
151         def start(self):
152                 self.player.play()
153         def stop(self):
154                 self.player.stop()
155
156 class VlcPlayer(Screen, InfoBarNotifications, InfoBarAudioSelection):
157         screen_timeout = 5000
158         
159         STATE_IDLE = 0
160         STATE_PLAYING = 1
161         STATE_PAUSED = 2
162         
163         def __init__(self, session, vlcfilelist):
164                 Screen.__init__(self, session)
165                 InfoBarNotifications.__init__(self)
166                 InfoBarAudioSelection.__init__(self)
167                 self.filelist = vlcfilelist
168                 self.skinName = "MoviePlayer"
169                 self.state = self.STATE_IDLE
170                 self.url = None
171                 self.oldservice = self.session.screen["CurrentService"]
172                 self.vlcservice = VlcService(self)
173                 self["CurrentService"] = self.vlcservice
174                 self.session.screen["CurrentService"] = self.vlcservice
175                 self.hidetimer = eTimer()
176                 self.hidetimer.timeout.get().append(self.ok)
177                 self.onClose.append(self.__onClose)
178
179                 class VlcPlayerActionMap(ActionMap):
180                         def __init__(self, player, contexts = [ ], actions = { }, prio=0):
181                                 ActionMap.__init__(self, contexts, actions, prio)
182                                 self.player = player
183                                 
184                         def action(self, contexts, action):
185                                 if action[:5] == "seek:":
186                                         time = int(action[5:])
187                                         self.player.seekRelative(time)
188                                         return 1
189                                 elif action[:8] == "seekdef:":
190                                         key = int(action[8:])
191                                         time = [-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
192                                                         -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
193                                                         -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value][key-1]
194                                         self.player.seekRelative(time)
195                                         return 1
196                                 else:
197                                         return ActionMap.action(self, contexts, action)
198                 
199                 self["actions"] = VlcPlayerActionMap(self, ["OkCancelActions", "TvRadioActions", "InfobarSeekActions", "MediaPlayerActions"],
200                 {
201                                 "ok": self.ok,
202                                 "cancel": self.cancel,
203                                 "keyTV": self.stop,
204                                 "pauseService": self.pause,
205                                 "unPauseService": self.play,
206                                 "seekFwd": self.seekFwd,
207                                 "seekBack": self.seekBack,
208                                 "seekFwdDown": self.seekFwd,
209                                 "seekBackDown": self.seekBack,
210                                 "next": self.playNextFile,
211                                 "previous": self.playPrevFile
212                         }, -2)
213
214                 print "evEOF=%d" % iPlayableService.evEOF
215                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
216                         {
217                                 iPlayableService.evEOF: self.__evEOF,
218 #                               iPlayableService.evSOF: self.__evSOF,
219                         })
220         def __onClose(self):
221                 self.session.screen["CurrentService"] = self.oldservice
222         
223         def __evEOF(self):
224                 print "[VLC] Event EOF"
225                 self.stop()
226         
227         def playfile(self, servernum, path):
228                 if self.state != self.STATE_IDLE:
229                         self.stop()
230
231                 cfg = config.plugins.vlcplayer.servers[servernum]
232                 self.vlccontrol = VlcControlHttp(servernum)
233                 streamName = VlcControlHttp.defaultStreamName
234                 self.vlcservice.setFilename(path)
235                 
236                 self.servernum = servernum
237                 self.url = "http://%s:%d/%s.ts" % (cfg.host.value, cfg.httpport.value, streamName)
238                 if path.lower().endswith(".iso") and not isDvdUrl(path):
239                         self.filename = "dvdsimple://" + path
240                 else:
241                         self.filename = path
242
243                 do_direct = isDvdUrl(self.filename) or re.match("(?i).*\.(mpg|mpeg|ts)$", self.filename)
244                 if do_direct and config.plugins.vlcplayer.notranscode.value:
245                         self.output = "#"
246                 else:
247                         transcode = "vcodec=%s,vb=%d,venc=ffmpeg{strict-rc=1},width=%s,height=%s,fps=%s,scale=1,acodec=%s,ab=%d,channels=%d,samplerate=%s" % (
248                                 config.plugins.vlcplayer.vcodec.value, 
249                                 config.plugins.vlcplayer.vb.value, 
250                                 config.plugins.vlcplayer.width.value, 
251                                 config.plugins.vlcplayer.height.value, 
252                                 config.plugins.vlcplayer.fps.value, 
253                                 config.plugins.vlcplayer.acodec.value, 
254                                 config.plugins.vlcplayer.ab.value, 
255                                 config.plugins.vlcplayer.channels.value,
256                                 config.plugins.vlcplayer.samplerate.value
257                         )
258                         if config.plugins.vlcplayer.aspect.value != "none":
259                                 transcode += ",canvas-width=%s,canvas-height=%s,canvas-aspect=%s" % (
260                                         config.plugins.vlcplayer.width.value, 
261                                         config.plugins.vlcplayer.height.value, 
262                                         config.plugins.vlcplayer.aspect.value
263                                 ) 
264                         if config.plugins.vlcplayer.soverlay.value:
265                                 transcode += ",soverlay"
266                         self.output = "#transcode{%s}:" % transcode
267                 mux="ts{pid-video=%d,pid-audio=%d}" % (DEFAULT_VIDEO_PID, DEFAULT_AUDIO_PID)
268                 self.output = self.output + "std{access=http,mux=%s,dst=/%s.ts} :sout-all" % (mux, streamName)
269                 self.play()
270
271         def play(self):
272                 if self.state == self.STATE_PAUSED:
273                         self.unpause()
274                         return
275                 elif self.state == self.STATE_IDLE and self.url is not None:
276                         print "[VLC] setupStream: " + self.filename + " " + self.output
277                         try:
278                                 self.vlccontrol.playfile(self.filename, self.output)
279                         except Exception, e:
280                                 self.session.open(
281                                         MessageBox, _("Error with VLC server:\n%s" % e), MessageBox.TYPE_ERROR)
282                                 return
283                         sref = eServiceReference(ENIGMA_SERVICE_ID, 0, self.url)
284                         print "sref valid=", sref.valid()
285                         sref.setData(0, DEFAULT_VIDEO_PID)
286                         sref.setData(1, DEFAULT_AUDIO_PID)
287                         self.session.nav.playService(sref)
288                         self.state = self.STATE_PLAYING
289                         if self.shown:
290                                 self.__setHideTimer()
291                 self.vlcservice.setControl(self.vlccontrol)
292                 self.vlcservice.refresh()
293
294         def pause(self):
295                 print "[VLC] pause"
296                 if self.state == self.STATE_PLAYING:
297                         self.session.nav.pause(True)
298                         self.vlccontrol.pause()
299                         self.state = self.STATE_PAUSED
300                         self.vlcservice.refresh()
301                         if not self.shown:
302                                 self.hidetimer.stop()
303                                 self.show()
304                 elif self.state == self.STATE_PAUSED:
305                         self.unpause()
306
307         def unpause(self):
308                 print "[VLC] unpause"
309                 try:
310                         self.vlccontrol.seek("-2")
311                         self.vlccontrol.play()
312                 except Exception, e:
313                         self.session.open(
314                                 MessageBox, _("Error with VLC server:\n%s" % e), MessageBox.TYPE_ERROR)
315                         self.stop()
316                         return
317                 if STOP_BEFORE_UNPAUSE:
318                         self.session.nav.stopService()
319                         sref = eServiceReference(ENIGMA_SERVICE_ID, 0, self.url)
320                         sref.setData(0, DEFAULT_VIDEO_PID)
321                         sref.setData(1, DEFAULT_AUDIO_PID)
322                         self.session.nav.playService(sref)
323                 else:
324                         self.session.nav.pause(False)
325                 self.state = self.STATE_PLAYING
326                 self.vlcservice.refresh()
327                 if self.shown:
328                         self.__setHideTimer()
329                 
330         def stop(self):
331                 print "[VLC] stop"
332                 self.session.nav.stopService()
333                 if self.state == self.STATE_IDLE:
334                         self.close()
335                         return
336                 if self.vlccontrol is not None:
337                         try:
338                                 self.vlccontrol.stop()
339                                 self.vlccontrol.delete()
340                         except Exception, e:
341                                 self.session.open(
342                                         MessageBox, _("Error with VLC server:\n%s" % e), MessageBox.TYPE_ERROR)
343                 self.state = self.STATE_IDLE
344                 self.vlcservice.setControl(None)
345                 self.vlcservice.refresh()
346                 self.show()
347
348         def __setHideTimer(self):
349                 self.hidetimer.start(self.screen_timeout)
350
351         def ok(self):
352                 if self.shown:
353                         self.hide()
354                         self.hidetimer.stop()
355                         self.vlcservice.refreshTimer.stop()
356                 else:
357                         self.vlcservice.refresh()
358                         self.show()
359                         if self.state == self.STATE_PLAYING:
360                                 self.__setHideTimer()
361                         else:
362                                 self.vlcservice.refreshTimer.start(self.vlcservice.refreshInterval)
363
364         def cancel(self):
365                 self.stop()
366                 self.close()
367         
368         def playNextFile(self):
369                 print "[VLC] playNextFile",self.filename
370                 if isDvdUrl(self.filename):
371                         url,track,chapter = splitDvdUrl(self.filename)
372                         if track is None:
373                                 track = 2
374                         else:
375                                 track = int(track) + 1
376                         url = "%s@%d" % (url, track)
377                         self.playfile(self.servernum, url)
378                 else:
379                         path = self.filelist.getNextFile()
380                         if path is None:
381                                 self.session.open(MessageBox, _("No more files in this directory"), MessageBox.TYPE_INFO)
382                         else:
383                                 servernum, path = path.split(":", 1)
384                                 self.playfile(int(servernum), path)
385
386         def playPrevFile(self):
387                 print "[VLC] playPrevFile"
388                 if isDvdUrl(self.filename):
389                         url,track,chapter = splitDvdUrl(self.filename)
390                         if track is not None and int(track) > 2:
391                                 track = int(track) - 1
392                                 url = "%s@%d" % (url, track)
393                         self.playfile(self.servernum, url)
394                 else:
395                         path = self.filelist.getPrevFile()
396                         if path is None:
397                                 self.session.open(MessageBox, _("No previous file in this directory"), MessageBox.TYPE_INFO)
398                         else:
399                                 servernum, path = path.split(":", 1)
400                                 self.playfile(int(servernum), path)
401
402         def audioTracks(self): 
403                 return self.session.nav.getCurrentService() and self.session.nav.getCurrentService().audioTracks();
404
405         def seekRelative(self, delta):
406                 """delta is seconds as integer number
407                 positive=forwards, negative=backwards"""
408                 if self.state != self.STATE_IDLE:
409                         if (delta >= 0):
410                                 self.vlccontrol.seek("+"+str(delta))
411                         else:
412                                 self.vlccontrol.seek(str(delta))
413                 self.vlcservice.refresh()
414                 if not self.shown:
415                         self.show()
416                         self.__setHideTimer()
417
418         def seekFwd(self):
419                 self.seekRelative(600)
420
421         def seekBack(self):
422                 self.seekRelative(-600)