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