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