add support for 480i and 576i... but keep in mind that not all TVs can
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / Videomode / VideoHardware.py
1 from Screens.Screen import Screen
2 from Plugins.Plugin import PluginDescriptor
3
4 from enigma import eTimer
5
6 from Components.ActionMap import ActionMap
7 from Components.Label import Label
8 from Components.Pixmap import Pixmap
9 from Screens.MessageBox import MessageBox
10 from Screens.Setup import SetupSummary
11 from Components.ConfigList import ConfigListScreen
12 from Components.config import getConfigListEntry, config, ConfigSelection, ConfigSubDict, ConfigYesNo
13
14 from Tools.CList import CList
15
16 # The "VideoHardware" is the interface to /proc/stb/video.
17 # It generates hotplug events, and gives you the list of 
18 # available and preferred modes, as well as handling the currently
19 # selected mode. No other strict checking is done.
20 class VideoHardware:
21         rates = { } # high-level, use selectable modes.
22
23         modes = { }  # a list of (high-level) modes for a certain port.
24
25         rates["PAL"] =                  { "50Hz":               { 50: "pal" },
26                                                                 "60Hz":         { 60: "pal60" },
27                                                                 "multi":        { 50: "pal", 60: "pal60" } }
28
29         rates["NTSC"] =                 { "60Hz":       { 60: "ntsc" } }
30
31         rates["Multi"] =                { "multi":      { 50: "pal", 60: "ntsc" } }
32
33         rates["480i"] =                 { "60Hz":       { 60: "480i" } }
34
35         rates["576i"] =                 { "50Hz":       { 50: "576i" } }
36
37         rates["480p"] =                 { "60Hz":       { 60: "480p" } }
38
39         rates["576p"] =                 { "50Hz":       { 50: "576p" } }
40
41         rates["720p"] =                 { "50Hz":       { 50: "720p50" },
42                                                                 "60Hz":         { 60: "720p" },
43                                                                 "multi":        { 50: "720p50", 60: "720p" } }
44
45         rates["1080i"] =                { "50Hz":               { 50: "1080i50" },
46                                                                 "60Hz":         { 60: "1080i" },
47                                                                 "multi":        { 50: "1080i50", 60: "1080i" } }
48
49         rates["PC"] = { 
50                 "1024x768": { 60: "1024x768"}, # not possible on DM7025
51                 "800x600" : { 60: "800x600"},  # also not possible
52                 "720x480" : { 60: "720x480"},
53                 "720x576" : { 60: "720x576"},
54                 "1280x720": { 60: "1280x720"},
55                 "1280x720 multi": { 50: "1280x720_50", 60: "1280x720"},
56                 "1920x1080": { 60: "1920x1080"},
57                 "1920x1080 multi": { 50: "1920x1080", 60: "1920x1080_50"},
58                 "1280x1024" : { 60: "1280x1024"},
59                 "1366x768" : { 60: "1366x768"},
60                 "1366x768 multi" : { 50: "1366x768", 60: "1366x768_50"},
61                 "1280x768": { 60: "1280x768"},
62                 "640x480" : { 60: "640x480"}
63         }
64
65         modes["Scart"] = ["PAL", "NTSC", "Multi"]
66         modes["YPbPr"] = ["720p", "1080i", "576p", "480p", "576i", "480i"]
67         modes["DVI"] = ["720p", "1080i", "576p", "480p", "576i", "480i"]
68         modes["DVI-PC"] = ["PC"]
69
70         widescreen_modes = set(["720p", "1080i"])
71
72         def __init__(self):
73                 self.last_modes_preferred =  [ ]
74                 self.on_hotplug = CList()
75                 self.standby = False
76                 self.current_mode = None
77                 self.current_port = None
78
79                 self.readAvailableModes()
80
81                 self.createConfig()
82 #               self.on_hotplug.append(self.createConfig)
83
84                 self.readPreferredModes()
85
86                 # take over old AVSwitch component :)
87                 from Components.AVSwitch import AVSwitch
88 #               config.av.colorformat.notifiers = [ ] 
89                 config.av.aspectratio.notifiers = [ ]
90                 config.av.tvsystem.notifiers = [ ]
91                 config.av.wss.notifiers = [ ]
92                 AVSwitch.setInput = self.AVSwitchSetInput
93
94                 config.av.aspect.addNotifier(self.updateAspect)
95                 config.av.wss.addNotifier(self.updateAspect)
96                 config.av.policy_169.addNotifier(self.updateAspect)
97                 config.av.policy_43.addNotifier(self.updateAspect)
98
99                 # until we have the hotplug poll socket
100 #               self.timer = eTimer()
101 #               self.timer.callback.append(self.readPreferredModes)
102 #               self.timer.start(1000)
103
104                 config.av.colorformat.addNotifier(self.updateFastblank) 
105
106         def AVSwitchSetInput(self, mode):
107                 self.standby = mode == "SCART"
108                 self.updateStandby()
109
110         def readAvailableModes(self):
111                 try:
112                         modes = open("/proc/stb/video/videomode_choices").read()[:-1]
113                 except IOError:
114                         print "couldn't read available videomodes."
115                         self.modes_available = [ ]
116                         return
117                 self.modes_available = modes.split(' ')
118
119         def readPreferredModes(self):
120                 try:
121                         modes = open("/proc/stb/video/videomode_preferred").read()[:-1]
122                         self.modes_preferred = modes.split(' ')
123                 except IOError:
124                         print "reading preferred modes failed, using all modes"
125                         self.modes_preferred = self.modes_available
126
127                 if self.modes_preferred != self.last_modes_preferred:
128                         self.last_modes_preferred = self.modes_preferred
129                         print "hotplug on dvi"
130                         self.on_hotplug("DVI") # must be DVI
131
132         # check if a high-level mode with a given rate is available.
133         def isModeAvailable(self, port, mode, rate):
134                 rate = self.rates[mode][rate]
135                 for mode in rate.values():
136                         # DVI modes must be in "modes_preferred"
137 #                       if port == "DVI":
138 #                               if mode not in self.modes_preferred and not config.av.edid_override.value:
139 #                                       print "no, not preferred"
140 #                                       return False
141                         if mode not in self.modes_available:
142                                 return False
143                 return True
144
145         def isWidescreenMode(self, port, mode):
146                 return mode in self.widescreen_modes
147
148         def setMode(self, port, mode, rate, force = None):
149                 print "setMode - port:", port, "mode:", mode, "rate:", rate
150                 # we can ignore "port"
151                 self.current_mode = mode
152                 self.current_port = port
153                 modes = self.rates[mode][rate]
154
155                 mode_50 = modes.get(50)
156                 mode_60 = modes.get(60)
157                 if mode_50 is None or force == 60:
158                         mode_50 = mode_60
159                 if mode_60 is None or force == 50: 
160                         mode_60 = mode_50
161
162                 try:
163                         open("/proc/stb/video/videomode_50hz", "w").write(mode_50)
164                         open("/proc/stb/video/videomode_60hz", "w").write(mode_60)
165                 except IOError:
166                         try:
167                                 # fallback if no possibility to setup 50/60 hz mode
168                                 open("/proc/stb/video/videomode", "w").write(mode_50)
169                         except IOError:
170                                 print "setting videomode failed."
171
172                 try:
173                         open("/etc/videomode", "w").write(mode_50) # use 50Hz mode (if available) for booting
174                 except IOError:
175                         print "writing initial videomode to /etc/videomode failed."
176
177                 self.updateAspect(None)
178
179         def saveMode(self, port, mode, rate):
180                 config.av.videoport.value = port
181                 config.av.videoport.save()
182                 config.av.videomode[port].value = mode
183                 config.av.videomode[port].save()
184                 config.av.videorate[mode].value = rate
185                 config.av.videorate[mode].save()
186
187         def isPortAvailable(self, port):
188                 # fixme
189                 return True
190
191         def isPortUsed(self, port):
192                 if port == "DVI":
193                         self.readPreferredModes()
194                         return len(self.modes_preferred) != 0
195                 else:
196                         return True
197
198         def getPortList(self):
199                 return [port for port in self.modes if self.isPortAvailable(port)]
200
201         # get a list with all modes, with all rates, for a given port.
202         def getModeList(self, port):
203                 print "getModeList for port", port
204                 res = [ ]
205                 for mode in self.modes[port]:
206                         # list all rates which are completely valid
207                         rates = [rate for rate in self.rates[mode] if self.isModeAvailable(port, mode, rate)]
208
209                         # if at least one rate is ok, add this mode
210                         if len(rates):
211                                 res.append( (mode, rates) )
212                 return res
213
214         def createConfig(self, *args):
215                 # create list of output ports
216                 portlist = self.getPortList()
217
218                 # create list of available modes
219                 config.av.videoport = ConfigSelection(choices = [(port, _(port)) for port in portlist])
220                 config.av.videomode = ConfigSubDict()
221                 config.av.videorate = ConfigSubDict()
222
223                 for port in portlist:
224                         modes = self.getModeList(port)
225                         if len(modes):
226                                 config.av.videomode[port] = ConfigSelection(choices = [mode for (mode, rates) in modes])
227                         for (mode, rates) in modes:
228                                 config.av.videorate[mode] = ConfigSelection(choices = rates)
229
230         def setConfiguredMode(self):
231                 port = config.av.videoport.value
232                 if port not in config.av.videomode:
233                         print "current port not available, not setting videomode"
234                         return
235
236                 mode = config.av.videomode[port].value
237
238                 if mode not in config.av.videorate:
239                         print "current mode not available, not setting videomode"
240                         return
241
242                 rate = config.av.videorate[mode].value
243                 self.setMode(port, mode, rate)
244
245         def updateAspect(self, cfgelement):
246                 # determine aspect = {any,4:3,16:9,16:10}
247                 # determine policy = {bestfit,letterbox,panscan,nonlinear}
248
249                 # based on;
250                 #   config.av.videoport.value: current video output device
251                 #     Scart: 
252                 #   config.av.aspect:
253                 #     4_3:            use policy_169
254                 #     16_9,16_10:     use policy_43
255                 #     auto            always "bestfit"
256                 #   config.av.policy_169
257                 #     letterbox       use letterbox
258                 #     panscan         use panscan
259                 #     scale           use bestfit
260                 #   config.av.policy_43
261                 #     pillarbox       use panscan
262                 #     panscan         use letterbox  ("panscan" is just a bad term, it's inverse-panscan)
263                 #     nonlinear       use nonlinear
264                 #     scale           use bestfit
265
266                 port = config.av.videoport.value
267                 if port not in config.av.videomode:
268                         print "current port not available, not setting videomode"
269                         return
270                 mode = config.av.videomode[port].value
271
272                 force_widescreen = self.isWidescreenMode(port, mode)
273
274                 is_widescreen = force_widescreen or config.av.aspect.value in ["16_9", "16_10"]
275                 is_auto = config.av.aspect.value == "auto"
276
277                 if is_widescreen:
278                         if force_widescreen:
279                                 aspect = "16:9"
280                         else:
281                                 aspect = {"16_9": "16:9", "16_10": "16:10"}[config.av.aspect.value]
282                         policy = {"pillarbox": "panscan", "panscan": "letterbox", "nonlinear": "nonlinear", "scale": "bestfit"}[config.av.policy_43.value]
283                 elif is_auto:
284                         aspect = "any"
285                         policy = "bestfit"
286                 else:
287                         aspect = "4:3"
288                         policy = {"letterbox": "letterbox", "panscan": "panscan", "scale": "bestfit"}[config.av.policy_169.value]
289
290                 if not config.av.wss.value:
291                         wss = "auto(4:3_off)"
292                 else:
293                         wss = "auto"
294
295                 print "-> setting aspect, policy, wss", aspect, policy, wss
296                 open("/proc/stb/video/aspect", "w").write(aspect)
297                 open("/proc/stb/video/policy", "w").write(policy)
298                 open("/proc/stb/denc/0/wss", "w").write(wss)
299                 self.updateSlowblank()
300                 self.updateFastblank()
301
302         def updateSlowblank(self):
303                 if self.standby:
304                         from Components.SystemInfo import SystemInfo
305                         if SystemInfo["ScartSwitch"]:
306                                 input = "scart"
307                                 sb = "vcr"
308                         else:
309                                 input = "off"
310                                 sb = "0"
311                 else:
312                         input = "encoder"
313                         sb = "auto"
314
315                 open("/proc/stb/avs/0/sb", "w").write(sb)
316                 open("/proc/stb/avs/0/input", "w").write(input)
317
318         def updateStandby(self):
319                 self.updateSlowblank()
320                 self.updateFastblank()
321
322         def updateFastblank(self, *args):
323                 if self.standby:
324                         from Components.SystemInfo import SystemInfo
325                         if SystemInfo["ScartSwitch"]:
326                                 fb = "vcr"
327                         else:
328                                 fb = "low"
329                 else:
330                         if self.current_port == "Scart" and config.av.colorformat.value == "rgb":
331                                 fb = "high"
332                         else:
333                                 fb = "low"
334                 open("/proc/stb/avs/0/fb", "w").write(fb)
335
336 config.av.edid_override = ConfigYesNo(default = False)
337 video_hw = VideoHardware()
338 video_hw.setConfiguredMode()