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