jaseg / python-mpv

Python interface to the awesome mpv media player
https://git.jaseg.de/python-mpv.git
Other
543 stars 68 forks source link

Static libmpv support #164

Closed ghost closed 3 years ago

ghost commented 3 years ago

Can i use python-mpv with static libmpv (libmpv.a)? I really need this. If not, please add this capability.

neinseg commented 3 years ago

Hey there!

As python-mpv uses python's built-in ctypes module to load libmpv, it is fundamentally impossible for it to load it as a static library. Think about it this way: Statically libraries need to be linked into the application when the application is compiled. Now, python scripts are not compiled. Instead, they are directly interpreted. In case of python-mpv, the compiled part instead is your python interpreter, python.exe or however it may be called on your system. Of course, it would not make sense to link libmpv into the python interpreter itself because then it would be loaded with every python program you run (and not just with your script and python-mpv).

I would recommend you download a dynamic library (.dll, .so or .dylib) version of libmpv. On linux, you can usually install that through apt, pacman or any other package manager. For MacOS, Windows and Linux systems where for whatever reason you can't install it through the system package manger you can find downloads linked on mpv.io.

Finally, as a last resort, you can in fact convert a static library to a dynamic library with little effort as shown in this stackoverflow post. However, I do not recommend doing that. Most likely, there is a better way to get what you want.

Ask away if you have further questions. If you describe what you want to do with python-mpv in more detail, maybe I can give you a hint on possible solutions.

ghost commented 3 years ago

Finally, as a last resort, you can in fact convert a static library to a dynamic library with little effort

Converting:

unar libmpv.a && cd libmpv && gcc -shared -o ../libmpv_static.so *.o

And patching mpv.py:

--- mpv.py 2021-05-28 22:06:27.558109048 +0300
+++ mpv.py      2021-05-28 22:07:56.664857965 +0300
@@ -44,7 +44,7 @@
     # still better than segfaulting, we are setting LC_NUMERIC to "C".
     locale.setlocale(locale.LC_NUMERIC, 'C')

-    sofile = ctypes.util.find_library('mpv')
+    sofile = './libmpv_static.so'
     if sofile is None:
         raise OSError("Cannot find libmpv in the usual places. Depending on your distro, you may try installing an "
                 "mpv-devel or mpv-libs package. If you have libmpv around but this script can't find it, consult "
patch -p0 < mpv_static.patch

And got this error:

Traceback (most recent call last):
  File "./test.py", line 2, in <module>
    import mpv
  File "./mpv.py", line 53, in <module>
    backend = CDLL(sofile)
  File "/usr/lib/python3.7/ctypes/__init__.py", line 356, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: ./libmpv_static.so: undefined symbol: osd_style_conf

Maybe i'm doing something wrong?

If you describe what you want to do with python-mpv in more detail, maybe I can give you a hint on possible solutions.

Yeah. I need portable python-mpv, working on every Linux distibution, without many dependencies:

$ ldd /usr/lib/x86_64-linux-gnu/libmpv.so.1 | awk '{print $1}' | wc -l
289
neinseg commented 3 years ago

Maybe i'm doing something wrong?

That error looks like your libmpv.a is missing some symbols (variable declarations). These might come from a header libmpv.a expects the host program to use. I would say that in this case, libmpv.a is not suitable for that simple conversion method.

Yeah. I need portable python-mpv, working on every Linux distibution, without many dependencies:

In that case, simply converting libmpv will not be enough and whether you are linking it statically or dynamically does not matter. The static libmpv really only includes libmpv, and does not contain any of libmpv's dependencies. This is the same for both static .a and dynamic .so variants. All of those dependencies you have to link separately, no matter which one you use.

You best chance of getting this to work would be to build everything yourself, starting from libmpv's dependencies like ffmpeg and going up the stack from there. A good base for this would be PyPA's manylinux build environment. Anything built in that environment is likely to be binary-compatible with most linuxes you will find out there as long as you don't rely on anything except for libc.

Going this route will be a lot of work, which is probably why nobody seems to have done it yet. Compiling all the dependencies is going to take days, and the resulting binary bundle will be enormous (>100MB). It would be a cool proof of concept, though.

neinseg commented 3 years ago

You can't easily do that, since python does not expose the interpreter's internal symbols through the ctypes API that python-mpv uses. Also, it does not actually solve your problem, which is that you would have to create a self-contained environment that includes not just libmpv but also all its dependencies such as ffmpeg. You will have to compile all of these dependencies into shared objects. These .so files you can then just ship with python-mpv. By setting LD_LIBRARY_PATH, python-mpv will then pick them up without any nasty hacks being necessary. The issue here is creating all these .so files.

ghost commented 3 years ago

Compiling all the dependencies is going to take days, and the resulting binary bundle will be enormous (>100MB). It would be a cool proof of concept, though.

Here you go... https://gitlab.com/astroncia/python-mpv-portable

ghost commented 3 years ago

I also had problems with LD_LIBRARY_PATH, so I used

patchelf --force-rpath --set-rpath ./ ./libmpv.so.1