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