update .gitattributes
[enigma2-plugins.git] / autoresolution / src / plugin.py
1 # encoding: utf-8
2 from Screens.Screen import Screen
3 from Screens.Setup import SetupSummary
4 from Screens.MessageBox import MessageBox
5 from Components.ConfigList import ConfigList, ConfigListScreen
6 from Components.config import config, getConfigListEntry, ConfigSelection, ConfigSubsection, ConfigYesNo, ConfigSubDict, ConfigNothing
7 from Components.ServiceEventTracker import ServiceEventTracker
8 from Components.ActionMap import ActionMap
9 from Components.Label import Label
10 from Components.Sources.StaticText import StaticText
11 from enigma import iPlayableService, iServiceInformation, eTimer, getDesktop
12 from Plugins.Plugin import PluginDescriptor
13 from Plugins.SystemPlugins.Videomode.VideoHardware import video_hw # depends on Videomode Plugin
14
15 usable = False
16 preferedmodes = None
17 default = None
18 port = None
19 videoresolution_dictionary = {}
20 resolutionlabel = None
21
22 resolutions = (('sd_i_50', (_("SD 25/50HZ Interlace Mode"))), ('sd_i_60', (_("SD 30/60HZ Interlace Mode"))), \
23                         ('sd_p_50', (_("SD 25/50HZ Progressive Mode"))), ('sd_p_60', (_("SD 30/60HZ Progressive Mode"))), \
24                         ('hd_i', (_("HD Interlace Mode"))), \
25                         ('hd_p', (_("HD Progressive Mode"))), ('fhd_p', (_("FHD Progressive Mode"))), \
26                         ('p720_24', (_("Enable 720p24 Mode"))), ('p1080_24', (_("Enable 1080p24 Mode"))), \
27                         ('p1080_25', (_("Enable 1080p25 Mode"))), ('p1080_30', (_("Enable 1080p30 Mode"))), \
28                         ('uhd_p', (_("UHD Progressive Mode"))), ('p2160_24', (_("Enable 2160p24 Mode"))), \
29                         ('p2160_25', (_("Enable 2160p25 Mode"))), ('p2160_30', (_("Enable 2160p30 Mode"))))
30
31 config.plugins.autoresolution = ConfigSubsection()
32 config.plugins.autoresolution.enable = ConfigYesNo(default = False)
33 config.plugins.autoresolution.showinfo = ConfigYesNo(default = True)
34 config.plugins.autoresolution.testmode = ConfigYesNo(default = False)
35 config.plugins.autoresolution.deinterlacer = ConfigSelection(default = "auto", choices =
36                 [("off", _("off")), ("auto", _("auto")), ("on", _("on")), ("bob", _("bob"))])
37 config.plugins.autoresolution.deinterlacer_progressive = ConfigSelection(default = "auto", choices =
38                 [("off", _("off")), ("auto", _("auto")), ("on", _("on")), ("bob", _("bob"))])
39 config.plugins.autoresolution.delay_switch_mode = ConfigSelection(default = "1000", choices = [
40                 ("50", "0.05 " + _("seconds")), ("500", "0.5 " + _("seconds")),
41                 ("1000", "1 " + _("second")), ("2000", "2 " + _("seconds")), ("3000", "3 " + _("seconds")),
42                 ("4000", "4 " + _("seconds")), ("5000", "5 " + _("seconds")), ("6000", "6 " + _("seconds")), ("7000", "7 " + _("seconds")),
43                 ("8000", "8 " + _("seconds")), ("9000", "9 " + _("seconds")), ("10000", "10 " + _("seconds")),("60000", "60 " + _("seconds"))])
44
45 def setDeinterlacer(mode):
46         print "[AutoRes] switch deinterlacer mode to %s" % mode
47         f = open('/proc/stb/vmpeg/deinterlace' , "w")
48         f.write("%s\n" % mode)
49         f.close()
50
51 frqdic = { 23976: '24', \
52                 24000: '24', \
53                 25000: '25', \
54                 29970: '30', \
55                 30000: '30', \
56                 50000: '50', \
57                 59940: '60', \
58                 60000: '60'}
59
60 class AutoRes(Screen):
61         def __init__(self, session):
62                 global port
63                 Screen.__init__(self, session)
64                 self.__event_tracker = ServiceEventTracker(screen = self, eventmap =
65                         {
66                                 iPlayableService.evVideoSizeChanged: self.__evVideoSizeChanged,
67                                 iPlayableService.evVideoProgressiveChanged: self.__evVideoProgressiveChanged,
68                                 iPlayableService.evVideoFramerateChanged: self.__evVideoFramerateChanged,
69                                 iPlayableService.evUpdatedInfo: self.__evUpdatedInfo,
70                                 iPlayableService.evStart: self.__evStart
71                         })
72                 self.timer = eTimer()
73                 self.timer_conn = self.timer.timeout.connect(self.determineContent)
74                 if config.av.videoport.value in config.av.videomode:
75                         self.lastmode = config.av.videomode[config.av.videoport.value].value
76                 config.av.videoport.addNotifier(self.defaultModeChanged)
77                 config.plugins.autoresolution.enable.addNotifier(self.enableChanged, initial_call = False)
78                 config.plugins.autoresolution.deinterlacer.addNotifier(self.enableChanged, initial_call = False)
79                 config.plugins.autoresolution.deinterlacer_progressive.addNotifier(self.enableChanged, initial_call = False)
80                 if default:
81                         self.setMode(default[0], False)
82                 self.after_switch_delay = False
83                 self.newService = False
84                 if "720p" in config.av.videorate:
85                         config.av.videorate["720p"].addNotifier(self.__videorate_720p_changed, initial_call = False, immediate_feedback = False)
86                 if "1080i" in config.av.videorate:
87                         config.av.videorate["1080i"].addNotifier(self.__videorate_1080i_changed, initial_call = False, immediate_feedback = False)
88                 if "1080p" in config.av.videorate:
89                         config.av.videorate["1080p"].addNotifier(self.__videorate_1080p_changed, initial_call = False, immediate_feedback = False)
90                 if "2160p" in config.av.videorate:
91                         config.av.videorate["2160p"].addNotifier(self.__videorate_2160p_changed, initial_call = False, immediate_feedback = False)
92
93         def __videorate_720p_changed(self, configEntry):
94                 if self.lastmode == "720p":
95                         self.changeVideomode()
96
97         def __videorate_1080i_changed(self, configEntry):
98                 if self.lastmode == "1080i":
99                         self.changeVideomode()
100
101         def __videorate_1080p_changed(self, configEntry):
102                 if self.lastmode == "1080p":
103                         self.changeVideomode()
104
105         def __videorate_2160p_changed(self, configEntry):
106                 if self.lastmode == "2160p":
107                         self.changeVideomode()
108
109         def __evStart(self):
110                 self.newService = True
111
112         def __evUpdatedInfo(self):
113                 if self.newService:
114                         print "[AutoRes] service changed"
115                         self.after_switch_delay = False
116                         self.timer.start(int(config.plugins.autoresolution.delay_switch_mode.value))
117                         self.newService = False
118
119         def defaultModeChanged(self, configEntry):
120                 global preferedmodes
121                 global port
122                 global default
123                 global usable
124                 port_changed = configEntry == config.av.videoport
125                 if port_changed:
126                         print "port changed to", configEntry.value
127                         if port:
128                                 config.av.videomode[port].removeNotifier(self.defaultModeChanged)
129                         port = config.av.videoport.value
130                         if port in config.av.videomode:
131                                 config.av.videomode[port].addNotifier(self.defaultModeChanged)
132                         usable = config.plugins.autoresolution.enable.value and not port in ('DVI-PC', 'Scart')
133                 else: # videomode changed in normal av setup
134                         global videoresolution_dictionary
135                         print "mode changed to", configEntry.value
136                         default = (configEntry.value, _("default"))
137                         preferedmodes = [mode[0] for mode in video_hw.getModeList(port) if mode[0] != default[0]]
138                         preferedmodes.append(default)
139                         print "default", default
140                         print "preferedmodes", preferedmodes
141                         videoresolution_dictionary = {}
142                         config.plugins.autoresolution.videoresolution = ConfigSubDict()
143                         have_2160p = config.av.videorate.get("2160p", False)
144                         for mode in resolutions:
145                                 if have_2160p:
146                                         if mode[0].startswith('p2160'):
147                                                 choices = ['2160p24', '2160p25', '2160p30', '1080p24', '1080p25', '1080p30'] + preferedmodes
148                                         elif mode[0].startswith('p1080_24'):
149                                                 choices = ['1080p24', '2160p24'] + preferedmodes
150                                         elif mode[0].startswith('p1080'):
151                                                 choices = ['1080p24', '1080p25', '1080p30'] + preferedmodes
152                                         elif mode[0] == 'p720_24':
153                                                 choices = ['720p24', '1080p24', '2160p24'] + preferedmodes
154                                         else:
155                                                 choices = preferedmodes
156                                 else:
157                                         if mode[0].startswith('p1080'):
158                                                 choices = ['1080p24', '1080p25', '1080p30'] + preferedmodes
159                                         elif mode[0] == 'p720_24':
160                                                 choices = ['720p24', '1080p24'] + preferedmodes
161                                         else:
162                                                 choices = preferedmodes
163                                 config.plugins.autoresolution.videoresolution[mode[0]] = ConfigSelection(default = default[0], choices = choices)
164                                 config.plugins.autoresolution.videoresolution[mode[0]].addNotifier(self.modeConfigChanged, initial_call = False, immediate_feedback = False)
165                                 videoresolution_dictionary[mode[0]] = (config.plugins.autoresolution.videoresolution[mode[0]])
166
167         def modeConfigChanged(self, configElement):
168                 self.determineContent()
169
170         def enableChanged(self, configElement):
171                 global usable
172                 if configElement.value:
173                         usable = not port in ('DVI-PC', 'Scart')
174                         self.determineContent()
175                 else:
176                         usable = False
177                         self.changeVideomode()
178
179         def __evVideoFramerateChanged(self):
180                 print "[AutoRes] got event evFramerateChanged"
181                 if not self.timer.isActive() or self.after_switch_delay:
182                         self.timer.start(100) # give other pending events a chance..
183
184         def __evVideoSizeChanged(self):
185                 print "[AutoRes] got event evVideoSizeChanged"
186                 if not self.timer.isActive() or self.after_switch_delay:
187                         self.timer.start(100) # give other pending events a chance..
188
189         def __evVideoProgressiveChanged(self):
190                 print "[AutoRes] got event evVideoProgressiveChanged"
191                 if not self.timer.isActive() or self.after_switch_delay:
192                         self.timer.start(100) # give other pending events a chance..
193
194         def determineContent(self):
195                 print "[AutoRes] determineContent"
196                 self.timer.stop()
197                 self.after_switch_delay = True
198                 if usable:
199                         service = self.session.nav.getCurrentService()
200                         info = service and service.info()
201                         height = info and info.getInfo(iServiceInformation.sVideoHeight)
202                         width = info and info.getInfo(iServiceInformation.sVideoWidth)
203                         framerate = info and info.getInfo(iServiceInformation.sFrameRate)
204                         if height != -1 and width != -1 and framerate != -1:
205                                 have_1080p = config.av.videorate.get("1080p", False)
206                                 frate = str(framerate)[:2] #fallback?
207                                 if frqdic.has_key(framerate):
208                                         frate = frqdic[framerate]
209                                 progressive = info and info.getInfo(iServiceInformation.sProgressive)
210
211                                 prog = progressive == 1 and 'p' or 'i'
212
213                                 if (height >= 1800 or width >= 3200) and prog == 'p':   # 2160p content
214                                         if frate in ('24', '25', '30'):
215                                                 new_mode = 'p2160_%s' % frate
216                                         else:
217                                                 new_mode = 'uhd_p'
218                                 elif (height >= 900 or width >= 1600) and frate in ('24', '25', '30') and prog == 'p':  # 1080p content
219                                         new_mode = 'p1080_%s' % frate
220                                 elif (height > 576 or width > 720) and frate == '24' and prog == 'p':           # 720p24 detection
221                                         new_mode = 'p720_24'
222                                 elif (height <= 576) and (width <= 720) and frate in ('25', '50'):
223                                         new_mode = 'sd_%s_50' % prog
224                                 elif (height <= 480) and (width <= 720) and frate in ('24', '30', '60'):
225                                         new_mode = 'sd_%s_60' % prog
226                                 elif have_1080p and (height >= 900 or width >= 1600) and prog == 'p': # 1080p50/60 content
227                                         new_mode = 'fhd_p'
228                                 else:
229                                         new_mode = 'hd_%s' % prog
230
231                                 if progressive == 1:
232                                         setDeinterlacer(config.plugins.autoresolution.deinterlacer_progressive.value)
233                                 else:
234                                         setDeinterlacer(config.plugins.autoresolution.deinterlacer.value)
235
236                                 print "[AutoRes] new content is %sx%s%s%s" %(width, height, prog, frate)
237
238                                 if videoresolution_dictionary.has_key(new_mode):
239                                         new_mode = videoresolution_dictionary[new_mode].value
240                                         print '[AutoRes] determined videomode', new_mode
241                                         old = resolutionlabel["content"].getText()
242                                         resolutionlabel["content"].setText("Videocontent: %sx%s%s %sHZ" % (width, height, prog, frate))
243                                         if self.lastmode != new_mode:
244                                                 self.lastmode = new_mode
245                                                 self.changeVideomode()
246                                         elif old != resolutionlabel["content"].getText() and config.plugins.autoresolution.showinfo.value:
247                                                 resolutionlabel.show()
248
249         def changeVideomode(self):
250                 if usable:
251                         mode = self.lastmode
252                         if mode.find("0p30") != -1 or mode.find("0p24") != -1 or mode.find("0p25") != -1:
253                                 print "[AutoRes] switching to", mode
254                                 v = open('/proc/stb/video/videomode' , "w")
255                                 v.write("%s\n" % mode)
256                                 v.close()
257                                 resolutionlabel["restxt"].setText("Videomode: %s" % mode)
258                                 if config.plugins.autoresolution.showinfo.value:
259                                         resolutionlabel.show()
260                         else:
261                                 self.setMode(mode)
262                         if config.plugins.autoresolution.testmode.value and default[0] != mode:
263                                 resolutionlabeltxt = "Videomode: %s" % mode
264                                 self.session.openWithCallback(
265                                         self.confirm,
266                                         MessageBox,
267                                         _("Autoresolution Plugin Testmode:\nIs %s OK?") % (resolutionlabeltxt),
268                                         MessageBox.TYPE_YESNO,
269                                         timeout = 15,
270                                         default = False
271                                 )
272                 else:
273                         setDeinterlacer("auto")
274                         if self.lastmode != default[0]:
275                                 self.setMode(default[0])
276
277         def confirm(self, confirmed):
278                 if not confirmed:
279                         self.setMode(default[0])
280
281         def setMode(self, mode, set=True):
282                 rate = config.av.videorate[mode].value
283                 port_txt = "HDMI" if port == "DVI" else port
284                 resolutionlabel["restxt"].setText("Videomode: %s %s %s" % (port_txt, mode, rate))
285                 if set:
286                         print "[AutoRes] switching to %s %s %s" % (port_txt, mode, rate)
287                         if config.plugins.autoresolution.showinfo.value:
288                                 resolutionlabel.show()
289                         video_hw.setMode(port, mode, rate)
290                 self.lastmode = mode
291
292 class ResolutionLabel(Screen):
293         height = getDesktop(0).size().height()
294         if height == 2160:
295                 skin = """
296                         <screen position="150,120" size="750,108" flags="wfNoBorder">
297                                 <widget name="content" position="0,0" size="750,54" font="Regular;48" />
298                                 <widget name="restxt" position="0,54" size="750,54" font="Regular;48" />
299                         </screen>"""
300         elif height == 1080:
301                 skin = """
302                         <screen position="75,60" size="375,54" flags="wfNoBorder">
303                                 <widget name="content" position="0,0" size="375,27" font="Regular;24" />
304                                 <widget name="restxt" position="0,27" size="375,27" font="Regular;24" />
305                         </screen>"""
306         else:
307                 skin = """
308                         <screen position="50,40" size="250,36" flags="wfNoBorder">
309                                 <widget name="content" position="0,0" size="250,18" font="Regular;16" />
310                                 <widget name="restxt" position="0,18" size="250,18" font="Regular;16" />
311                         </screen>"""
312
313         def __init__(self, session):
314                 Screen.__init__(self, session)
315
316                 self["content"] = Label()
317                 self["restxt"] = Label()
318
319                 self.hideTimer = eTimer()
320                 self.hideTimer_conn = self.hideTimer.timeout.connect(self.hide)
321
322                 self.onShow.append(self.hide_me)
323
324         def hide_me(self):
325                 self.hideTimer.start(config.usage.infobar_timeout.index * 1500, True)
326
327
328 class AutoResSetupMenu(Screen, ConfigListScreen):
329         def __init__(self, session):
330                 Screen.__init__(self, session)
331                 self.skinName = [ "AutoResSetupMenu", "Setup" ]
332                 self.setup_title = _("Autoresolution videomode setup")
333
334                 self.onChangedEntry = [ ]
335                 self.list = [ ]
336                 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changedEntry)
337
338                 self["actions"] = ActionMap(["SetupActions"],
339                         {
340                                 "cancel": self.keyCancel,
341                                 "save": self.apply,
342                         }, -2)
343
344                 self["key_green"] = StaticText(_("OK"))
345                 self["key_red"] = StaticText(_("Cancel"))
346
347                 self.createSetup()
348                 self.onLayoutFinish.append(self.layoutFinished)
349
350         def layoutFinished(self):
351                 self.setTitle(_("Autoresolution settings"))
352
353         def createSetup(self):
354                 self.list = [ getConfigListEntry(_("Enable Autoresolution"), config.plugins.autoresolution.enable) ]
355                 if config.plugins.autoresolution.enable.value:
356                         if usable:
357                                 have_1080p = config.av.videorate.get("1080p", False)
358                                 have_2160p = config.av.videorate.get("2160p", False)
359                                 for mode, label in resolutions:
360                                         if (not mode.startswith("2160p") or have_2160p) and (mode != 'fhd_p' or have_1080p):
361                                                 self.list.append(getConfigListEntry(label, videoresolution_dictionary[mode]))
362                                 self.list.extend((
363                                         getConfigListEntry(_("Refresh Rate")+" 720p", config.av.videorate["720p"]),
364                                         getConfigListEntry(_("Refresh Rate")+" 1080i", config.av.videorate["1080i"])
365                                 ))
366                                 if have_1080p:
367                                         self.list.append(getConfigListEntry(_("Refresh Rate")+" 1080p", config.av.videorate["1080p"]))
368                                 if have_2160p:
369                                         self.list.append(getConfigListEntry(_("Refresh Rate")+" 2160p", config.av.videorate["2160p"]))
370                                 self.list.extend((
371                                         getConfigListEntry(_("Show info screen"), config.plugins.autoresolution.showinfo),
372                                         getConfigListEntry(_("Delay x seconds after service started"), config.plugins.autoresolution.delay_switch_mode),
373                                         getConfigListEntry(_("Running in testmode"), config.plugins.autoresolution.testmode),
374                                         getConfigListEntry(_("Deinterlacer mode for interlaced content"), config.plugins.autoresolution.deinterlacer),
375                                         getConfigListEntry(_("Deinterlacer mode for progressive content"), config.plugins.autoresolution.deinterlacer_progressive)
376                                 ))
377                         else:
378                                 self.list.append(getConfigListEntry(_("Autoresolution is not working in Scart/DVI-PC Mode"), ConfigNothing()))
379
380                 self["config"].list = self.list
381                 self["config"].setList(self.list)
382
383         def apply(self):
384                 for x in self["config"].list:
385                         x[1].save()
386                 self.close()
387
388         def keyLeft(self):
389                 ConfigListScreen.keyLeft(self)
390                 if self["config"].getCurrent()[1] == config.plugins.autoresolution.enable:
391                         self.createSetup()
392
393         def keyRight(self):
394                 ConfigListScreen.keyRight(self)
395                 if self["config"].getCurrent()[1] == config.plugins.autoresolution.enable:
396                         self.createSetup()
397
398         # for summary:
399         def changedEntry(self):
400                 for x in self.onChangedEntry:
401                         x()
402
403         def getCurrentEntry(self):
404                 return self["config"].getCurrent()[0]
405
406         def getCurrentValue(self):
407                 return str(self["config"].getCurrent()[1].getText())
408
409         def createSummary(self):
410                 return SetupSummary
411
412 def autostart(reason, **kwargs):
413         if "session" in kwargs and resolutionlabel is None:
414                 global resolutionlabel
415                 session = kwargs["session"]
416                 resolutionlabel = session.instantiateDialog(ResolutionLabel)
417                 AutoRes(session)
418
419 def startSetup(menuid):
420         if menuid != "osd_video_audio":
421                 return [ ]
422         return [(_("Autoresolution"), autoresSetup, "autores_setup", 45)]
423
424 def autoresSetup(session, **kwargs):
425         autostart(reason=0, session=session)
426         session.open(AutoResSetupMenu)
427
428 def Plugins(path, **kwargs):
429         return [PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART], fnc = autostart), \
430                 PluginDescriptor(name="Autoresolution", description=_("Autoresolution Switch"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup) ]