`strokers_for_mpv` MPV plugin Funscript player for T-Code devices (OSR, SR6) and `strokers` Rust library

I wrote a simple Rust library for controlling T-Code devices. Support for other types of devices could be added in the future.

I wrote this because I wanted to write an MPV plugin for playing funscripted videos
directly to my OSR2+, which I’m also ‘introducing’ today.

strokers_for_mpv

This is now in a state where it’s minimally useful, but still not slapping a
version number on it just yet.

Features:

  • Linux support. Maybe the other platforms work, I don’t know :smiley: — very happy to hear if anyone else tries. (I suspect Windows should work, but I am hesitant to promise!)
  • Multi-axis (of course!)
  • Configurable speed limits for safety
  • Min/Max axis limits
  • Simple text-based (TOML) config file for setting the serial port information and default limits
  • You can add MPV keybindings to change the axis min/max limits on the fly

Known Limitations:

  • If you have funscript variants, there’s no way to choose those in the MPV plugin currently — it will only open video.funscript for video.mp4, not
    video_hardmode.funscript (& equivalent for other axes). I want to address this in the future, just not sure of the best way.
  • If you install the plugin, it will always (try to) connect to your stroker when you open MPV, even if you’re watching an innocent video without any funscript. However I will address this in the future and it’s also probably harmless.

The MPV plugin is available at: https://codeberg.org/LaurenBoutin/strokers/src/branch/main/strokers_for_mpv
(Rust compiler required to compile; not hard but if this is a problem let me know)

At this point I wrote this ‘just’ for me, I don’t know if anyone else will have any interest in it, so if it is interesting to you but there’s something missing, let me know!

4 Likes

I can’t believe no one talk about it. I have tried to find something similar for months. Good work!

Unfortunately, I can’t get it working. Compiling and installing are fine

After running RUST_BACKTRACE=full mpv --script=~/.config/mpv/scripts/libstrokers_for_mpv.so MOVFILE.mp4`,

I got this error:

2025-03-08T00:45:28.524416Z  INFO strokers_for_mpv: strokers plugin for MPV (libstrokers_for_mpv) is loaded!
thread '<unnamed>' panicked at /opt/cargo/registry/src/index.crates.io-6f17d22bba15001f/tracing-subscriber-0.3.18/src/util.rs:91:14:
failed to set global default subscriber: SetGlobalDefaultError("a global default trace dispatcher has already been set")
stack backtrace:
2025-03-08T00:45:28.524653Z ERROR strokers_for_mpv: On change, can't read time-pos/full as f64
   0:     0x7fe3dae221ea - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h5b6bd5631a6d1f6b
   1:     0x7fe3dae45353 - core::fmt::write::h7550c97b06c86515
   2:     0x7fe3dae1fbd3 - std::io::Write::write_fmt::h7b09c64fe0be9c84
   3:     0x7fe3dae22032 - std::sys::backtrace::BacktraceLock::print::h2395ccd2c84ba3aa
   4:     0x7fe3dae22fdc - std::panicking::default_hook::{{closure}}::he19d4c7230e07961
   5:     0x7fe3dae22e22 - std::panicking::default_hook::hf614597d3c67bbdb
   6:     0x7fe3dae235b7 - std::panicking::rust_panic_with_hook::h8942133a8b252070
   7:     0x7fe3dae2344a - std::panicking::begin_panic_handler::{{closure}}::hb5f5963570096b29
   8:     0x7fe3dae226c9 - std::sys::backtrace::__rust_end_short_backtrace::h6208cedc1922feda
   9:     0x7fe3dae230dc - rust_begin_unwind
  10:     0x7fe3dac7b410 - core::panicking::panic_fmt::h0c3082644d1bf418
  11:     0x7fe3dac7b7f6 - core::result::unwrap_failed::hd20b4aa073bda1e2
  12:     0x7fe3dacce23e - mpv_open_cplugin
  13:     0x5597a5af8fdb - <unknown>
  14:     0x5597a5aee80a - <unknown>
  15:     0x5597a5aee89d - <unknown>
  16:     0x7fe3ee094ac3 - <unknown>
  17:     0x7fe3ee126850 - <unknown>
  18:                0x0 - <unknown>
thread '<unnamed>' panicked at core/src/panicking.rs:221:5:
panic in a function that cannot unwind
stack backtrace:
   0:     0x7fe3dae221ea - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h5b6bd5631a6d1f6b
   1:     0x7fe3dae45353 - core::fmt::write::h7550c97b06c86515
   2:     0x7fe3dae1fbd3 - std::io::Write::write_fmt::h7b09c64fe0be9c84
   3:     0x7fe3dae22032 - std::sys::backtrace::BacktraceLock::print::h2395ccd2c84ba3aa
   4:     0x7fe3dae22fdc - std::panicking::default_hook::{{closure}}::he19d4c7230e07961
   5:     0x7fe3dae22e22 - std::panicking::default_hook::hf614597d3c67bbdb
   6:     0x7fe3dae235b7 - std::panicking::rust_panic_with_hook::h8942133a8b252070
   7:     0x7fe3dae23416 - std::panicking::begin_panic_handler::{{closure}}::hb5f5963570096b29
   8:     0x7fe3dae226c9 - std::sys::backtrace::__rust_end_short_backtrace::h6208cedc1922feda
   9:     0x7fe3dae230dc - rust_begin_unwind
  10:     0x7fe3dac7b44d - core::panicking::panic_nounwind_fmt::h357fc035dc231634
  11:     0x7fe3dac7b4e2 - core::panicking::panic_nounwind::hd0dad372654c389a
  12:     0x7fe3dac7b5a5 - core::panicking::panic_cannot_unwind::h65aefd062253eb19
  13:     0x7fe3dacce602 - mpv_open_cplugin
  14:     0x5597a5af8fdb - <unknown>
  15:     0x5597a5aee80a - <unknown>
  16:     0x5597a5aee89d - <unknown>
  17:     0x7fe3ee094ac3 - <unknown>
  18:     0x7fe3ee126850 - <unknown>
  19:                0x0 - <unknown>
thread caused non-unwinding panic. aborting.
Aborted

ls ~/.config/mpv/scripts/
libstrokers_for_mpv.d libstrokers_for_mpv.so

OS: Linux
cfg: use the same one in the site
serial_port: “/dev/ttyUSB0” (same as my XTPlayer)
hw: SR6

My XTPlayer works fine w/ serial Tcode0.3 in ttyUSB0.

If I remove the tracing_subscriber block in strokers/strokers_for_mpv/src/lib.rs at main - LaurenBoutin/strokers - Codeberg.org, I see the Illegal instruction error without any further information.

Any ideas?

I think your problem is that the plugin is being autoloaded by being in your scripts folder, plus you are loading it again by passing the --script= argument. This is causing the plugin library to be loaded once, but the plugin is being initialised twice?
Remove either the --script= argument, or move the .so somewhere else so it’s not autoloaded by default.

Illegal instruction is a weird one. What platform (particularly CPU) are you on?

I did try your removing script solution and unfortunately it doesn’t work. I still get Illegal instruction err.

uname -a

Linux 6.2.0-26-generic #26~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Jul 13 16:27:29 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

cat /proc/cpuinfo

processor	: 0
vendor_id	: AuthenticAMD
cpu family	: 25
model		: 97
model name	: AMD Ryzen 5 7600 6-Core Processor
stepping	: 2
microcode	: 0xa601206
cpu MHz		: 2993.977
cache size	: 1024 KB
physical id	: 0
siblings	: 12
core id		: 0
cpu cores	: 6
apicid		: 0
initial apicid	: 0
fpu		: yes
fpu_exception	: yes
cpuid level	: 16
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor smca fsrm flush_l1d
bugs		: sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
bogomips	: 7585.32
TLB size	: 3584 4K pages
clflush size	: 64
cache_alignment	: 64
address sizes	: 48 bits physical, 48 bits virtual
power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14]

Ubuntu 22.04 LTS

mpv --version

mpv 0.34.1 Copyright © 2000-2021 mpv/MPlayer/mplayer2 projects
 built on UNKNOWN
FFmpeg library versions:
   libavutil       56.70.100
   libavcodec      58.134.100
   libavformat     58.76.100
   libswscale      5.9.100
   libavfilter     7.110.100
   libswresample   3.9.100
FFmpeg version: 4.4.2-0ubuntu0.22.04.1

Let me know what extra information you need. Thanks.

do you get any other output than ‘Illegal instruction’?
I think my suggestion to you would have solved the first problem (that you patched out the tracing_subscriber block for).

Your illegal instruction error is surprising, and it’s not like you’re running some weird or old CPU that is likely to be missing support for an instruction…

I have updated the git repository; if you grab the updated version and build the plugin again, it will have limited debug info enabled.

Once you’ve done that, please can you run mpv (with the plugin) under gdb?

This looks like:

gdb mpv
run [your arguments to mpv here]

..... wait for illegal instruction crash

..... then run these gdb commands and send me the output of each one please:
bt
frame
x10/i $pc


..... then you can quit
quit

BTW, do you have any other MPV plugins that could somehow be interacting with this one?

Im not that surprised. MFP is well known for the osr2, and can easily connect with many player including mpv (which is also what i use).

Sure for convenience not needing mfp can be nice, but that still does depend on the t-code config options. You generaly want to restrict the movement of the osr so it cant damage itself or cause you to slip out. I dont know how this library will manage that.

But it being a rust library will obviously help to get it to work for more players in the future if needed. And in the end, you can only test how it works by making an implementation for it.

Yeah, it’s fair to say that if MFP works for someone then that is a way more mature option and likely has all the features you could possibly want.

This MPV plugin in this thread was born because I don’t think MFP works (or at least not satisfyingly?) on Linux.
There are things I’d like to change — for instance, it forgets all the manual overrides to settings when MPV exits and then you open it on a new video (even though you might conceptually be in the same session and want those settings preserved).

My current keymapping relies on a number pad to adjust the min/max axis position and it turns out I sometimes use a laptop without a numpad, so that’s an inconvenience (although this can be adjusted directly in MPV config).

I don’t have any experience with MFP so I actually don’t know what else I’m missing, but I’m sure there’ll be many aspects :slight_smile:

Sorry for the late reply. The site was migrating.

Your plugin is the only mpv plugin I had. After re-clone and rebuilt the plugin(hash: 5af569799deaa59760fbda627a05945984119666), I got the same error. I don’t have much knowledge of gdb. One of your command(x10/i $pc) yields error, so I change to x/10i $pc, not entirely sure it is right. Anyway, I tried it on a few videsos and they all give me similar debug output.

Here you go:

(gdb) run 'Slumber Party - Signature Brand PMVs.mp4' 
Starting program: /usr/bin/mpv 'Slumber Party - Signature Brand PMVs.mp4'
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffe9d20640 (LWP 327289)]
[New Thread 0x7fffe951f640 (LWP 327290)]
[New Thread 0x7fffe8d1e640 (LWP 327291)]
[New Thread 0x7fffe3fff640 (LWP 327292)]
[New Thread 0x7fffe37fe640 (LWP 327293)]
[New Thread 0x7fffe2ffd640 (LWP 327294)]
[New Thread 0x7fffe27fc640 (LWP 327295)]
[Thread 0x7fffe2ffd640 (LWP 327294) exited]
2025-03-30T22:10:58.888315Z  INFO strokers_for_mpv: strokers plugin for MPV (libstrokers_for_mpv) is loaded!
[New Thread 0x7fffe2ffd640 (LWP 327296)]
[New Thread 0x7fffe1b7e640 (LWP 327297)]
2025-03-30T22:10:58.888610Z  INFO strokers_for_mpv: New video starting: "Slumber Party - Signature Brand PMVs.mp4"
2025-03-30T22:10:58.888619Z ERROR strokers_for_mpv: On change, can't read time-pos/full as f64
[New Thread 0x7fffe197d640 (LWP 327298)]
[New Thread 0x7fffe197d640 (LWP 327299)]
[Thread 0x7fffe197d640 (LWP 327298) exited]
[New Thread 0x7fffe117c640 (LWP 327300)]

Thread 8 "mpv/SO plugin (" received signal SIGILL, Illegal instruction.
[Switching to Thread 0x7fffe27fc640 (LWP 327295)]
mpv_client::Event::from_ptr () at src/lib.rs:364
364	    unsafe fn from_ptr(event: *const mpv_event) -> Event {
(gdb) bt
#0  mpv_client::Event::from_ptr () at src/lib.rs:364
#1  mpv_client::Handle::wait_event () at src/lib.rs:209
#2  0x00007fffe1cafa49 in strokers_for_mpv::mpv_open_cplugin () at strokers_for_mpv/src/lib.rs:61
#3  0x000055555563bfdb in  ()
#4  0x000055555563180a in  ()
#5  0x000055555563189d in  ()
#6  0x00007ffff4a94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#7  0x00007ffff4b26850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) frame
#0  mpv_client::Event::from_ptr () at src/lib.rs:364
364	    unsafe fn from_ptr(event: *const mpv_event) -> Event {
(gdb) x/10i $pc
=> 0x7fffe1dffa17 <_ZN10mpv_client6Handle10wait_event17h648cce5db6e1f60aE+551>:	ud2    
   0x7fffe1dffa19:	nopl   0x0(%rax)
   0x7fffe1dffa20 <_ZN10mpv_client6Handle4name17h719b03d28ab87be6E>:	push   %rbx
   0x7fffe1dffa21 <_ZN10mpv_client6Handle4name17h719b03d28ab87be6E+1>:	sub    $0x20,%rsp
   0x7fffe1dffa25 <_ZN10mpv_client6Handle4name17h719b03d28ab87be6E+5>:	call   *0x168645(%rip)        # 0x7fffe1f68070
   0x7fffe1dffa2b <_ZN10mpv_client6Handle4name17h719b03d28ab87be6E+11>:	mov    %rax,%rbx
   0x7fffe1dffa2e <_ZN10mpv_client6Handle4name17h719b03d28ab87be6E+14>:	mov    %rax,%rdi
   0x7fffe1dffa31 <_ZN10mpv_client6Handle4name17h719b03d28ab87be6E+17>:	call   *0x167f51(%rip)        # 0x7fffe1f67988
   0x7fffe1dffa37 <_ZN10mpv_client6Handle4name17h719b03d28ab87be6E+23>:	lea    0x1(%rax),%rdx
   0x7fffe1dffa3b <_ZN10mpv_client6Handle4name17h719b03d28ab87be6E+27>:	lea    0x8(%rsp),%rdi
(gdb) quit

BTW, all videos for testing work fine in my XTPlayer and SR6. Does it help?? Let me know if you need additional info. Thanks for your reply.

PS: Although I love XTplayer, I feel XTPlayer is quite heavy in terms of resources and features. The app acts weird after using it for a while. XTPlayer is also unable to play some videos which are fine to be played by mpv. I have other apps to handle features like tagging, managing and more. I only need a good, light weight tcode media player. So I have high hope on this plugin :smiling_face_with_three_hearts:

Thanks for your debug info and sorry for the delay in getting back to you.

I installed a Ubuntu 22.04 VM and was able to reproduce your problem. It seems to be some incompatibility with the API that the bindings I use and the (older?) version of MPV provide.

In the end, I just updated the version of the MPV library I am using. Now it seems to generate bindings dynamically so you’ll need libmpv-dev and clang installed to compile. The good news is that this fixed the problem for me.

If you install those things and pull+build the latest version, I hope you should finally find it works! :slight_smile:

Thanks for your update. The task of compiling mpv on ubuntu 22.04 is a dead end for me after hours and hours of deep diving. I finally able to build a deb package after installing lots of pkg, dabugging and compiling by using GitHub - mpv-player/mpv-build: 🔨 Helper scripts to compile mpv on Linux 's ubuntu route. The final built deb depends on libc6 (>= 2.38), but ubuntu 22.04 update ‘s libc version is 2.35 (https://packages.ubuntu.com/jammy-updates/libc6). Because of personal reason, I cannot upgrade to ubuntu 24.04. I hope I can save some folks’ time.

Anyway, I choose the flatpak route and make it works.

Here is the workaround for ubuntu 22.04 folks:

sudo flatpak install flathub io.mpv.Mpv
STROKERS_CONFIG=/home/YOUR_USER_NAME/.config/strokers.toml  flatpak run io.mpv.Mpv --script=/YOUR_PLUGIN_PATH/libstrokers_for_mpv.so MOVIE_FILE

I thought STROKERS_CONFIG can be skipped since ~/.config/strokers.toml is the default location, but it won’t work. I am glad I can resolve this problem after reading the src and the err msg.

Also, I find out that the sample config doesn’t work for my case out of the box. I have to adjust the value to the following:

[limits.stroke]
speed = 5
default_min = 0.14
default_max = 0.82

[limits.surge]
speed = 5
default_min = 0.01
default_max = 0.99

[limits.sway]
speed = 5
default_min = 0.01
default_max = 0.99

[limits.twist]
speed = 5
default_min = 0.01
default_max = 0.99

[limits.roll]
speed = 5
default_min = 0.01
default_max = 0.99

[limits.pitch]
speed = 5
default_min = 0.01
default_max = 0.99

Not sure the new cfg is correct, but it seems work on my test video.

BTW, is it possible to allow user to pass script file name in the future version? Users might put lots of files in one directory and some scripts can be shared by multiple videos. I think mpv allows us to pass parameters to a plugin using --script-opts like mpv --script-opts=plugin-name-setting=value. If personalizing a video is needed, we can also use this pattern. If this feature is too complicated, forget about it.

Finally, thanks for your work.

Sorry, I meant to say that the latest version of the plugin will now work with ‘old’ mpv versions — sorry that you went through the trouble of trying to build a new mpv!!

I don’t think it should be necessary to override STROKERS_CONFIG but maybe this is because of using the flatpak’d mpv — flatpak’d applications seem to store their data somewhere else so it’s probably changing the location that the plugin searches for your config.

The default limits are totally nerfed so that you can choose the limits according to your needs, without having the machine go crazy on you :-).

Passing a script option to choose the funscript file sounds like a good approach, yeah.

1 Like

Oh, I will give a try on the old mpv version + ubuntu 22.04 later and report back to you. Looking forward for the script option for the newer version when you are free. Thx.