Skip to content

Instantly share code, notes, and snippets.

@ericek111
Created August 3, 2025 13:22
Show Gist options
  • Select an option

  • Save ericek111/60b4e0e04c11202b99615250dd343ca2 to your computer and use it in GitHub Desktop.

Select an option

Save ericek111/60b4e0e04c11202b99615250dd343ca2 to your computer and use it in GitHub Desktop.
Use LSFG in MPV in Linux with the interpolating factor set to the nearest value suitable for the currently used monitor.
// gcc mpv_lsfg.c -o mpv_lsfg -lX11 -lXrandr -lavformat -lavcodec -lavutil -lm
// Change the path to the Lossless.dll library in LSFG_DLL_PATH!
// You can get it by purchasing Lossless Scaling on Steam: https://store.steampowered.com/app/993090/Lossless_Scaling/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
double get_video_fps(const char *filename) {
AVFormatContext *fmt_ctx = NULL;
AVStream *video_stream = NULL;
int video_stream_index = -1;
double fps = 0.0;
if (avformat_open_input(&fmt_ctx, filename, NULL, NULL) != 0) {
fprintf(stderr, "Could not open file: %s\n", filename);
return 0.0;
}
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
fprintf(stderr, "Could not find stream info\n");
avformat_close_input(&fmt_ctx);
return 0.0;
}
// find the first video stream
for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_stream_index = i;
break;
}
}
if (video_stream_index == -1) {
fprintf(stderr, "No video stream found\n");
avformat_close_input(&fmt_ctx);
return 0.0;
}
video_stream = fmt_ctx->streams[video_stream_index];
// get FPS
AVRational frame_rate = av_guess_frame_rate(fmt_ctx, video_stream, NULL);
if (frame_rate.num > 0 && frame_rate.den > 0) {
fps = av_q2d(frame_rate);
}
avformat_close_input(&fmt_ctx);
return fps;
}
int startMpv(int multiplier, int argc, char* argv[]) {
int err = 0;
char multStr[32];
sprintf(multStr, "%d", (int) multiplier);
err |= setenv("ENABLE_LSFG", "0", 1);
err |= setenv("LSFG_LEGACY", "1", 1);
err |= setenv("LSFG_MULTIPLIER", multStr, 1);
printf("\n\n>>>>>>>>>>>>>>>>> LSFG_MULTIPLIER = %s\n\n", multStr);
err |= setenv("LSFG_FLOW_SCALE", "1.0", 1);
if (getenv("LSFG_DLL_PATH") == NULL)
err |= setenv("LSFG_DLL_PATH", "/home/erik/opt/Lossless.dll", 1);
// LSFG_PERFORMANCE_MODE, LSFG_HDR_MODE
if (err != 0) {
perror("setenv failed");
return 1;
}
char** mpv_argv = malloc(sizeof(char*) * (argc + 1));
if (!mpv_argv) {
perror("malloc failed");
return 2;
}
mpv_argv[0] = "mpv";
for (int i = 1; i < argc; ++i) {
mpv_argv[i] = argv[i];
}
mpv_argv[argc] = NULL; // NULL-terminated for execvp
extern char** environ; // pass the current envvars
if (execvpe(mpv_argv[0], mpv_argv, environ) < 0) {
// fprintf(stderr, "%s\n", explain_execvp(pathname, argv));
perror("execvp failed");
}
// if execvp returns, an error occurred
free(mpv_argv);
return 3;
}
int getRefreshRateOfMonitor(double* refreshRateTarget) {
if (!refreshRateTarget)
return 1;
Display *display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "Unable to open X display\n");
return 2;
}
Window root = DefaultRootWindow(display);
// Get the pointer (mouse cursor) location
Window returned_root, returned_child;
int root_x, root_y, win_x, win_y;
unsigned int mask;
if (!XQueryPointer(display, root, &returned_root, &returned_child,
&root_x, &root_y, &win_x, &win_y, &mask)) {
fprintf(stderr, "Failed to query pointer\n");
XCloseDisplay(display);
return 3;
}
XRRScreenResources *resources = XRRGetScreenResources(display, root);
if (!resources) {
fprintf(stderr, "Failed to get screen resources\n");
XCloseDisplay(display);
return 4;
}
RROutput target_output = None;
int ret_code = 1;
for (int i = 0; i < resources->noutput; ++i) {
XRRCrtcInfo* crtc_info = NULL;
XRROutputInfo* output_info = XRRGetOutputInfo(display, resources, resources->outputs[i]);
if (!output_info || output_info->connection == RR_Disconnected || output_info->crtc == 0) {
goto endloop;
}
crtc_info = XRRGetCrtcInfo(display, resources, output_info->crtc);
if (!crtc_info) {
goto endloop;
}
// check if mouse is within this output (monitor)
if (root_x >= crtc_info->x && root_x < crtc_info->x + crtc_info->width &&
root_y >= crtc_info->y && root_y < crtc_info->y + crtc_info->height) {
target_output = resources->outputs[i];
// find the active refresh rate
for (int j = 0; j < output_info->nmode; ++j) {
for (int k = 0; k < resources->nmode; ++k) {
auto mode = resources->modes[k];
if (mode.id == output_info->modes[j] && mode.id == crtc_info->mode) {
*refreshRateTarget = (double) mode.dotClock / (mode.hTotal * mode.vTotal);
ret_code = 0;
goto endloop;
}
}
}
}
endloop:
if (crtc_info)
XRRFreeCrtcInfo(crtc_info);
if (output_info)
XRRFreeOutputInfo(output_info);
}
if (target_output == None) {
fprintf(stderr, "Monitor under cursor not found\n");
ret_code = 5;
}
XRRFreeScreenResources(resources);
XCloseDisplay(display);
return ret_code;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s [...] <video file>\n", argv[0]);
return 1;
}
double videoFps = get_video_fps(argv[argc - 1]);
if (videoFps > 0.0) {
fprintf(stderr, "Video FPS: %.2f\n", videoFps);
} else {
fprintf(stderr, "Could not determine FPS.\n");
return 2;
}
double refreshRate = 0.0;
if (getRefreshRateOfMonitor(&refreshRate) == 0) {
int multiplier = (int) floor(refreshRate / videoFps);
printf("Refresh rate under mouse: %.2f Hz, final multiplier: %d\n", refreshRate, multiplier);
startMpv(multiplier, argc, argv);
} else {
fprintf(stderr, "Could not determine refresh rate\n");
}
return 0;
}
# Use the following command to copy the launcher to your local app folder -- change the path to the executable beforehand:
# desktop-file-install --dir=$HOME/.local/share/applications ~/Documents/foss/mpv_lsfg/mpv_lsfg.desktop
[Desktop Entry]
Type=Application
Version=1.0
Name=MPV with LSFG
Comment=Run MPV interpolated to the refresh rate of the currently focused monitor.
# vvv This is a path to the directory in which our executable resides. vvv
Path=/home/erik/Documents/foss/mpv_lsfg
Exec=mpv_lsfg --player-operation-mode=pseudo-gui -- %U
Icon=mpv
Terminal=false
StartupWMClass=mpv
Categories=AudioVideo;Audio;Video;Player;TV;
MimeType=application/mxf;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;video/mpeg;video/x-mpeg2;video/x-mpeg3;video/mp4v-es;video/x-m4v;video/mp4;application/x-extension-mp4;video/divx;video/vnd.divx;video/msvideo;video/x-msvideo;video/ogg;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;application/vnd.ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/avi;video/x-flic;video/fli;video/x-flc;video/flv;video/x-flv;video/x-theora;video/x-theora+ogg;video/x-matroska;video/mkv;application/x-matroska;video/webm;video/x-ogm;video/x-ogm+ogg;application/x-ogm;application/x-ogm-video;application/x-shorten;video/mp2t;application/x-mpegurl;video/vnd.mpegurl;application/vnd.apple.mpegurl;video/3gp;video/3gpp;video/3gpp2;video/vnd.avi;
Name[en_US]=mpv_lsfg.desktop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment