CogentRedTester / mpv-changerefresh

A script to automatically change the refresh rate of the current display to match the playing video
MIT License
35 stars 6 forks source link

Change refresh depending on monitor #4

Open Nojevah opened 2 years ago

Nojevah commented 2 years ago

Would it be possible to detect monitor when executing script ? I'd like the script not to go further (don't change refresh rate) depending on monitor. If I'm on my main monitor, I'd prefer not to change refresh rate. But when I launch mpv on my TV (in single monitor mode) I want this script to do its magic.

Active monitor can be found in regedit I think (from what I see in MultiMonitorTool from Nirsoft too).

CogentRedTester commented 2 years ago

Thank you for sharing that MultiMonitorTool program with me, it actually does solve the biggest obstacle I had with implementing that feature which was to link monitor display names to the display IDs that mpv uses. I am interested in doing a complete rewrite of this script to make everything work better, but it's a pretty large job and I'm not sure when I'll have time. I may work on a way to implement this beforehand though. I will ping this thread if/when I do.

Nojevah commented 2 years ago

I've read in aonther ticket that you wanted to rewrite it, but if indeed you managed to insert this functionality in the actual (not that bad to me) script, it would be great and I could use the "auto" option. But I understand you have other priorities. I'll just hope and wait !

CogentRedTester commented 2 years ago

Ok I have a solution for you. I have written a very basic script called display-name. What it does is track what display the mpv window is currently open on and updates a shared_script_property with the actual name of the monitor instead of the horrible ID that mpv uses. It requires that MultiMonitorTool.exe be available in the system path, so just put it in the same place as nircmd.exe.

Once you have that script installed and have updated change-refresh with the latest commits I made just earlier you should be good to go.

What you need to do is create conditional auto-profiles in mpv.conf for the display you want to use, for example:

[PC]
profile-cond=shared_script_properties['display_name'] ~= 'SAMSUNG'
script-opts-append=changerefresh-auto=no

[TV]
profile-cond=shared_script_properties['display_name'] == 'SAMSUNG'
script-opts-append=changerefresh-auto=yes

This will automatically automatically switch the TV if you open the video on it's display or move it to the TVs display during playback.

You could also use profile restoration instead as well (though there are a few caveats so if things start acting weird try the other way) for example:

[TV]
profile-cond=shared_script_properties['display_name'] == 'SAMSUNG'
profile-restore=copy-equal
script-opts-append=changerefresh-auto=yes

And naturally if you have multiple displays you want to switch on you can just add more profiles.

If you're not sure what the names of your displays are you can run mpv --script-opts=display_names=yes to get a list printed for you by the script. Edit: the name you want to use is the one on the right.

image

You can also use --script-opts-append=changerefresh-rates=23;24... in the profiles to set custom rates for individual displays.

Nojevah commented 2 years ago

Thanks for your work ! For now I don't manage to have _mpv --script-opts=displaynames=yes list my displays. image

I'm not sure where MultiMonitorTool should be. For nircmd, I use a personalized path. For MMT, I've tried all these without success: the same personalized path as nircmd; C:\Windows\System32; "mpv Profile"\; "mpv_Profile"\scripts\; mpv.exe folder.

I have display-name.lua in "scripts" folder, with (updated) change-refresh.lua. I've also tried to create a file named display-name.conf in script-opts with display_names=yes in it, also without success.

FWIW, there's another tool using cmd only, called "DisplayChanger" (dccmd.exe) if it's better to use/integrate (there's also regedit info on its webpage) ? Also another thing it'd be cool to have: don't launch .exe if the frequency is already what we want (for now, there's just a quick black flash fortunately). But I'm going off-topic, it's just for where you'll find motivation to rewrite it.

Nojevah commented 2 years ago

I also have errors when I launch a file.

image

CogentRedTester commented 2 years ago

For now I don't manage to have mpv --script-opts=display_names=yes list my displays.

Sorry I made a mistake, you need to use: mpv --script-opts=display_names=yes --idle=once.

I also have errors when I launch a file.

I have uploaded a new version of display-name.lua that should print some more useful error messages. Most probably you don't have MultiMonitorTool.exe in the correct location.

Nojevah commented 2 years ago

FWIW, I've ended editing your script in order to suit my need:

local var = {
    video_fps = 0,
    display_hz = 0
}

-- change refresh rate based on video fps and monitor

function matchVideo()
    var.video_fps = mp.get_property_number('estimated-vf-fps', 0)
    var.video_fps_trunc = math.floor(var.video_fps)
    var.display_hz = mp.get_property_number('display-fps')

    if (var.video_fps_trunc ~= math.floor(var.display_hz) and var.display_hz < 90) then
        mp.set_property_bool("pause", true)
        local process2 = mp.command_native({
            name = 'subprocess',
            playback_only = false,
            capture_stdout = true,
            args = {
                "C:\\PLB\\Common\\Apps\\Display_Changer_x64\\dccmd.exe",
                "-monitor=SamsungTV",
                "-refresh=" .. var.video_fps_trunc
            }
        })
        if (string.match(process2.stdout, "There is no monitor with that name")) then
            mp.osd_message("!! Monitor not found !!",5)
        else
            mp.add_timeout(2, function()
                var.video_fps = mp.get_property_number('estimated-vf-fps', 0)
                var.display_hz = mp.get_property_number('display-fps')
                if (math.floor(var.video_fps) ~= math.floor(var.display_hz)) then
                    mp.osd_message("!!!! PROBLEM !!!!! Video: " .. var.video_fps .. "fps Display: " .. var.display_hz .. "Hz",8)
                else
                    mp.osd_message("Video: " .. var.video_fps .. "fps Display: " .. var.display_hz .. "Hz",5)
                    mp.set_property_bool("pause", false)
                end
                end)
        end
    end
end

mp.add_timeout(0.2, matchVideo)

mp.add_key_binding("f10", "match-video", matchVideo)

It works because I have a monitor with 120+ Hz. But it can be adapted to a 60 Hz monitor:

local var = {
    video_fps = 0,
    display_hz = 0
}

-- change refresh rate based on video fps and monitor name

function matchVideo()
    var.video_fps = mp.get_property_number('estimated-vf-fps', 0)
    var.video_fps_trunc = math.floor(var.video_fps)
    var.display_hz = mp.get_property_number('display-fps')

    if (var.video_fps_trunc ~= math.floor(var.display_hz)) then
        local process = mp.command_native({
            name = 'subprocess',
            playback_only = false,
            capture_stdout = true,
            args = {
                "C:\\PLB\\Common\\Apps\\Display_Changer_x64\\dccmd.exe",
                "-monitor=SamsungTV"
            }
        })
        if (string.match(process.stdout, "There is no monitor with that name")) then
            print("Monitor not found")
        else
            mp.set_property_bool("pause", true)
            local process2 = mp.command_native({
            name = 'subprocess',
            playback_only = false,
            capture_stdout = true,
            args = {
                "C:\\PLB\\Common\\Apps\\Display_Changer_x64\\dccmd.exe",
                "-monitor=SamsungTV",
                "-refresh=" .. var.video_fps_trunc
            }
        })

        mp.add_timeout(2, function()
            var.video_fps = mp.get_property_number('estimated-vf-fps', 0)
            var.display_hz = mp.get_property_number('display-fps')
            if (math.floor(var.video_fps) ~= math.floor(var.display_hz)) then
                mp.osd_message("!!!! PROBLEM !!!!! Video: " .. var.video_fps .. "fps Display: " .. var.display_hz .. "Hz",8)
            else
                mp.osd_message("Video: " .. var.video_fps .. "fps Display: " .. var.display_hz .. "Hz",5)
                mp.set_property_bool("pause", false)
            end
            end)
        end
    end
end

mp.add_timeout(0.2, matchVideo)

mp.add_key_binding("f10", "match-video", matchVideo)

It's far less powerful than your script, but it's easier to maintain to me. Thanks for you useful script which helped a lot !

CogentRedTester commented 2 years ago

Cool, I'm glad you've found a solution.