Martin Leduc
Martin Leduc

Reputation: 11

Changing Chroma Subsampling settings in Windows with a script (YCbCr 4:4:4 to RGB and vice versa)(Python)

A bit of context: I am writing a program to automate tests on monitors. To do so, I would like to change the display settings (Resolution, Refresh rate, etc.) on the fly without having to go through the NVIDIA Control Panel by hand every time.

I found that the NirCmd utility allows you to change some of these settings through the command line, so my first thought was to use that. Unfortunately, it can't modify more advanced parameters such as Chroma Subsampling ("Output color format" in NVIDIA Control Panel), Output dynamic range or Output color depth.

I then thought to use my program to directly modify registry values and update the settings with a call to ChangeDisplaySettings(). Using ProcMon on Windows, I identified which keys were modified by changing the display settings, and then changed them directly with Python before calling ChangeDisplaySettings() to read from the keys and display the new settings. Felt a bit like using a cannon to kill an ant, but it worked for the most part: I was able to change the Resolution, Refresh rate, Desktop color depth and Displayed window size using this method.

Here's the relevant part of the code I'm using (note that this code would need to be modified to run on a specific computer, as the "LAPTOP_ID" and "DESKTOP_ID" registry entries are unique):

from Tkinter import *
import tkFont
import time
import ctypes
import serial
import _winreg
import win32gui
import win32con
import win32api
import wmi

def get_reg(name, reg_path):
    registry_key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, reg_path, 0, _winreg.KEY_READ)
    value, regtype = _winreg.QueryValueEx(registry_key, name)
    _winreg.CloseKey(registry_key)
    return value

def set_reg(name, value, reg_path):
    registry_key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, reg_path, 0, _winreg.KEY_WRITE)
    _winreg.SetValueEx(registry_key, name, 0, _winreg.REG_DWORD, value)
    _winreg.CloseKey(registry_key)

def set_reg_bin(name, value, reg_path):
    registry_key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, reg_path, 0, _winreg.KEY_WRITE)
    _winreg.SetValueEx(registry_key, name, 0, _winreg.REG_BINARY, value)
    _winreg.CloseKey(registry_key)

def reg_enum(reg_path):
    registry_key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, reg_path, 0, _winreg.KEY_READ)
    lastkey=_winreg.EnumKey(registry_key, _winreg.QueryInfoKey(registry_key)[0]-1)
    firstkey=_winreg.EnumKey(registry_key, 0)
    print lastkey
    print firstkey
    return lastkey

def deleteSubkey(key0, key1, key2=""):
    if key2=="":
        currentkey = key1
    else:
        currentkey = key1+ "\\" +key2
    open_key = _winreg.OpenKey(key0, currentkey ,0,_winreg.KEY_ALL_ACCESS)
    infokey = _winreg.QueryInfoKey(open_key)
    for x in range(0, infokey[0]):
        subkey = _winreg.EnumKey(open_key, 0)
        try:
            _winreg.DeleteKey(open_key, subkey)
            print "Removed %s\\%s " % ( currentkey, subkey)
        except:
            deleteSubkey( key0, currentkey, subkey )
    _winreg.DeleteKey(open_key,"")
    open_key.Close()
    print "Removed %s" % (currentkey)
    return

if __name__ =="__main__":
    DESKTOP_ID = "{########-####-####-####-############}"
    LAPTOP_ID = "{########-####-####-####-############}"
    REG_PATH_SCRID = r"SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Configuration"
    deleteSubkey(_winreg.HKEY_LOCAL_MACHINE, REG_PATH_SCRID)
    try:
        input = raw_input('In Windows Display settings, under "Multiple displays", choose "Show only on 2", then press ENTER to continue.')
    except NameError:
        pass
    win32api.ChangeDisplaySettings(None, 0)
    ColorRGB = chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x0A)+chr(0x01)+chr(0x08)+chr(0x00)
    Color422 = chr(0x01)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x0A)+chr(0x01)+chr(0x08)+chr(0x00)
    Color444 = chr(0x02)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x0A)+chr(0x01)+chr(0x08)+chr(0x00)
    Color420 = chr(0x03)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x0A)+chr(0x01)+chr(0x08)+chr(0x00)
    ColorRGB_DriverExtra = chr(0x60)+chr(0xEA)+chr(0x00)+chr(0x00)+chr(0xE8)+chr(0x03)+chr(0x00)+chr(0x00)+chr(0x15)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x04)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x06)+chr(0x00)+chr(0x00)+chr(0x00)
    Color444_DriverExtra = chr(0x60)+chr(0xEA)+chr(0x00)+chr(0x00)+chr(0xE8)+chr(0x03)+chr(0x00)+chr(0x00)+chr(0x15)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x04)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x06)+chr(0x00)+chr(0x00)+chr(0x00)
    Color422_DriverExtra = chr(0x60)+chr(0xEA)+chr(0x00)+chr(0x00)+chr(0xE8)+chr(0x03)+chr(0x00)+chr(0x00)+chr(0x15)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x04)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x06)+chr(0x00)+chr(0x00)+chr(0x00)
    Color422_DriverExtra = chr(0x60)+chr(0xEA)+chr(0x00)+chr(0x00)+chr(0xE8)+chr(0x03)+chr(0x00)+chr(0x00)+chr(0x15)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x04)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x06)+chr(0x00)+chr(0x00)+chr(0x00)
    SCREEN_ID = reg_enum(REG_PATH_SCRID)
    print SCREEN_ID
    SCREEN_ID_SHORT = SCREEN_ID.partition("^")[0]
    print SCREEN_ID_SHORT
    REG_PATH = r"SYSTEM\CurrentControlSet\Hardware Profiles\UnitedVideo\CONTROL\VIDEO"+"\\"+LAPTOP_ID+r"\0000"
    REG_PATH2 = r"SYSTEM\CurrentControlSet\Hardware Profiles\UnitedVideo\CONTROL\VIDEO"+"\\"+LAPTOP_ID+r"\0001"
    REG_PATH3 = r"SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Configuration"+"\\"+SCREEN_ID+r"\00\00"
    REG_PATH4 = r"SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Configuration"+"\\"+SCREEN_ID+r"\00"
    REG_PATH5 = r"SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Configuration"+"\\"+SCREEN_ID
    REG_PATH_COLOR = r"SYSTEM\CurrentControlSet\Services\nvlddmkm\DisplayDatabase"+"\\"+SCREEN_ID_SHORT

    set_reg('DefaultSettings.XResolution', 3840, REG_PATH)
    set_reg('DefaultSettings.YResolution', 2160, REG_PATH)
    set_reg_bin('DefaultSettings.DriverExtra', ColorRGB_DriverExtra, REG_PATH)
    set_reg('DefaultSettings.VRefresh', 60, REG_PATH)
    set_reg_bin('ColorformatConfig', ColorRGB, REG_PATH_COLOR)
    set_reg('Attach.ToDesktop', 1, REG_PATH)
    set_reg('VSyncFreq.Numerator', 60000, REG_PATH3)
    set_reg('VSyncFreq.Denominator', 1000, REG_PATH3)
    set_reg('PrimSurfSize.cx', 3840, REG_PATH3)
    set_reg('PrimSurfSize.cy', 2160, REG_PATH3)
    set_reg('DwmClipBox.left', 0, REG_PATH3)
    set_reg('DwmClipBox.top', 0, REG_PATH3)
    set_reg('DwmClipBox.right', 3840, REG_PATH3)
    set_reg('DwmClipBox.bottom', 2160, REG_PATH3)
    set_reg('PrimSurfSize.cx', 3840, REG_PATH4)
    set_reg('PrimSurfSize.cy', 2160, REG_PATH4)
    win32api.ChangeDisplaySettings(None, 0)

However, I didn't get much further as I was still unable to find and change the keys which control the more advanced display settings. It seems they are not stored by Nvidia in the registry, so I'm stuck. As you can see, I did find a key called "ColorformatConfig" that is being modified when the Output color format is changed in the NVIDIA Control Panel, but it does not actually seem to be used directly.

I also gave the Nvidia SDK a try, but found it a bit beyond my capabilities as a programmer...

Any ideas are welcome! (including a way to get rid of the unsightly successive chr() casts)

Upvotes: 1

Views: 2526

Answers (1)

Katie
Katie

Reputation: 1270

SetDisplayConfig offers much greater control over the source and target modes. If you're a bit overwhelmed by the nested structures involved, I recommend manually putting the system in the configuration you want then using QueryDisplayConfig to get the current configuration. Save each configuration somewhere, then have your test app load them up and pass them into SetDisplayConfig.

Upvotes: 0

Related Questions