better aspect setting
[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", 60: "pal"},
26                                                                                                 "60Hz":         { 50: "pal60", 60: "pal60"},
27                                                                                                 "multi":        { 50: "pal", 60: "pal60"} }
28         rates["NTSC"] =                 { "60Hz":       { 50: "ntsc", 60: "ntsc"} }
29         rates["Multi"] =                { "multi":      { 50: "pal", 60: "ntsc"} }
30         rates["720p"] =                 {       "50Hz":         { 50: "720p50", 60: "720p50"},
31                                                                                                 "60Hz":         { 50: "720p", 60: "720p"},
32                                                                                                 "multi":        { 50: "720p50", 60: "720p"} }
33         rates["1080i"] =                { "50Hz":               { 50: "1080i50", 60: "1080i50"},
34                                                                                                 "60Hz":         { 50: "1080i", 60: "1080i"},
35                                                                                                 "multi":        { 50: "1080i50", 60: "1080i"} }
36         rates["PC"] = { 
37                 "1024x768": { 60: "1024x768"}, # not possible on DM7025
38                 "800x600" : { 60: "800x600"},  # also not possible
39                 "720x480" : { 60: "720x480"},
40                 "720x576" : { 60: "720x576"},
41                 "1280x720": { 60: "1280x720"},
42                 "1280x720 multi": { 50: "1280x720_50", 60: "1280x720"},
43                 "1920x1080": { 60: "1920x1080"},
44                 "1920x1080 multi": { 50: "1920x1080", 60: "1920x1080_50"},
45                 "1280x1024" : { 60: "1280x1024"},
46                 "1366x768" : { 60: "1366x768"},
47                 "1366x768 multi" : { 50: "1366x768", 60: "1366x768_50"},
48                 "1280x768": { 60: "1280x768"},
49                 "640x480" : { 60: "640x480"} 
50         }
51
52         modes["Scart"] = ["PAL", "NTSC", "Multi"]
53         modes["YPbPr"] = ["720p", "1080i"]
54         modes["DVI"] = ["720p", "1080i", "PC"]
55
56         widescreen_modes = set(["720p", "1080i"])
57
58         def __init__(self):
59                 self.last_modes_preferred =  [ ]
60                 self.on_hotplug = CList()
61
62                 self.readAvailableModes()
63
64                 self.createConfig()
65 #               self.on_hotplug.append(self.createConfig)
66
67                 self.readPreferredModes()
68
69                 config.av.aspect.addNotifier(self.updateAspect)
70                 config.av.policy_169.addNotifier(self.updateAspect)
71                 config.av.policy_43.addNotifier(self.updateAspect)
72
73                 # until we have the hotplug poll socket
74 #               self.timer = eTimer()
75 #               self.timer.callback.append(self.readPreferredModes)
76 #               self.timer.start(1000)
77
78         def readAvailableModes(self):
79                 try:
80                         modes = open("/proc/stb/video/videomode_choices").read()[:-1]
81                 except IOError:
82                         print "couldn't read available videomodes."
83                         self.modes_available = [ ]
84                         return
85                 self.modes_available = modes.split(' ')
86
87         def readPreferredModes(self):
88                 try:
89                         modes = open("/proc/stb/video/videomode_preferred").read()[:-1]
90                         self.modes_preferred = modes.split(' ')
91                 except IOError:
92                         print "reading preferred modes failed, using all modes"
93                         self.modes_preferred = self.modes_available
94
95                 if self.modes_preferred != self.last_modes_preferred:
96                         self.last_modes_preferred = self.modes_preferred
97                         print "hotplug on dvi"
98                         self.on_hotplug("DVI") # must be DVI
99
100         # check if a high-level mode with a given rate is available.
101         def isModeAvailable(self, port, mode, rate):
102                 rate = self.rates[mode][rate]
103                 for mode in rate.values():
104                         # DVI modes must be in "modes_preferred"
105 #                       if port == "DVI":
106 #                               if mode not in self.modes_preferred and not config.av.edid_override.value:
107 #                                       print "no, not preferred"
108 #                                       return False
109                         if mode not in self.modes_available:
110                                 return False
111                 return True
112
113         def isWidescreenMode(self, port, mode):
114                 return mode in self.widescreen_modes
115
116         def setMode(self, port, mode, rate, force = None):
117                 print "setMode - port:", port, "mode:", mode, "rate:", rate
118                 # we can ignore "port"
119                 self.current_mode = mode
120                 modes = self.rates[mode][rate]
121
122                 mode_50 = modes.get(50)
123                 mode_60 = modes.get(60)
124                 if mode_50 is None or force == 60:
125                         mode_50 = mode_60
126                 if mode_60 is None or force == 50: 
127                         mode_60 = mode_50
128
129                 try:
130                         open("/proc/stb/video/videomode_60hz", "w").write(mode_50)
131                         open("/proc/stb/video/videomode_50hz", "w").write(mode_60)
132                 except IOError:
133                         try:
134                                 # fallback if no possibility to setup 50/60 hz mode
135                                 open("/proc/stb/video/videomode", "w").write(mode_50)
136                         except IOError:
137                                 print "setting videomode failed."
138
139                 try:
140                         open("/etc/videomode", "w").write(mode_50) # use 50Hz mode (if available) for booting
141                 except IOError:
142                         print "writing initial videomode to /etc/videomode failed."
143
144                 self.updateAspect(None)
145
146         def saveMode(self, port, mode, rate):
147                 config.av.videoport.value = port
148                 config.av.videomode[port].value = mode
149                 config.av.videorate[mode].value = rate
150
151         def isPortAvailable(self, port):
152                 # fixme
153                 return True
154
155         def isPortUsed(self, port):
156                 if port == "DVI":
157                         self.readPreferredModes()
158                         return len(self.modes_preferred) != 0
159                 else:
160                         return True
161
162         def getPortList(self):
163                 return [port for port in self.modes if self.isPortAvailable(port)]
164
165         # get a list with all modes, with all rates, for a given port.
166         def getModeList(self, port):
167                 print "getModeList for port", port
168                 res = [ ]
169                 for mode in self.modes[port]:
170                         # list all rates which are completely valid
171                         rates = [rate for rate in self.rates[mode] if self.isModeAvailable(port, mode, rate)]
172
173                         # if at least one rate is ok, add this mode
174                         if len(rates):
175                                 res.append( (mode, rates) )
176                 return res
177
178         def createConfig(self, *args):
179                 # create list of output ports
180                 portlist = self.getPortList()
181
182                 # create list of available modes
183                 config.av.videoport = ConfigSelection(choices = [(port, _(port)) for port in portlist])
184                 config.av.videomode = ConfigSubDict()
185                 config.av.videorate = ConfigSubDict()
186
187                 for port in portlist:
188                         modes = self.getModeList(port)
189                         if len(modes):
190                                 config.av.videomode[port] = ConfigSelection(choices = [mode for (mode, rates) in modes])
191                         for (mode, rates) in modes:
192                                 config.av.videorate[mode] = ConfigSelection(choices = rates)
193
194         def setConfiguredMode(self):
195                 port = config.av.videoport.value
196                 if port not in config.av.videomode:
197                         print "current port not available, not setting videomode"
198                         return
199
200                 mode = config.av.videomode[port].value
201
202                 if mode not in config.av.videorate:
203                         print "current mode not available, not setting videomode"
204                         return
205
206                 rate = config.av.videorate[mode].value
207                 self.setMode(port, mode, rate)
208                 
209
210         def updateAspect(self, cfgelement):
211                 # determine aspect = {any,4:3,16:9,16:10}
212                 # determine policy = {bestfit,letterbox,panscan,nonlinear}
213
214                 # based on;
215                 #   config.av.videoport.value: current video output device
216                 #     Scart: 
217                 #   config.av.aspect:
218                 #     4_3:            use policy_169
219                 #     16_9,16_10:     use policy_43
220                 #     auto            always "bestfit"
221                 #   config.av.policy_169
222                 #     letterbox       use letterbox
223                 #     panscan         use panscan
224                 #     scale           use bestfit
225                 #   config.av.policy_43
226                 #     pillarbox       use panscan
227                 #     panscan         use letterbox  ("panscan" is just a bad term, it's inverse-panscan)
228                 #     nonlinear       use nonlinear
229                 #     scale           use bestfit
230
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                 mode = config.av.videomode[port].value
236
237                 force_widescreen = self.isWidescreenMode(port, mode)
238
239                 is_widescreen = force_widescreen or config.av.aspect.value in ["16_9", "16_10"]
240                 is_auto = config.av.aspect.value == "auto"
241
242                 if is_widescreen:
243                         if force_widescreen:
244                                 aspect = "16:9"
245                         else:
246                                 aspect = {"16_9": "16:9", "16_10": "16:10"}[config.av.aspect.value]
247                         policy = {"pillarbox": "panscan", "panscan": "letterbox", "nonlinear": "nonlinear", "scale": "bestfit"}[config.av.policy_43.value]
248                 elif is_auto:
249                         aspect = "any"
250                         policy = "bestfit"
251                 else:
252                         aspect = "4:3"
253                         policy = {"letterbox": "letterbox", "panscan": "panscan", "scale": "bestfit"}[config.av.policy_169.value]
254
255                 print "-> setting aspect, policy", aspect, policy
256                 open("/proc/stb/video/aspect", "w").write(aspect)
257                 open("/proc/stb/video/policy", "w").write(policy)
258
259 config.av.edid_override = ConfigYesNo(default = False)
260 video_hw = VideoHardware()
261 video_hw.setConfiguredMode()