Kodi Community Forum
Linux Using Kodi's bundled ffmpeg from Python? - Printable Version

+- Kodi Community Forum (https://forum.kodi.tv)
+-- Forum: Development (https://forum.kodi.tv/forumdisplay.php?fid=32)
+--- Forum: Add-ons (https://forum.kodi.tv/forumdisplay.php?fid=26)
+--- Thread: Linux Using Kodi's bundled ffmpeg from Python? (/showthread.php?tid=377825)



Using Kodi's bundled ffmpeg from Python? - Mr Dini - 2024-06-05

Hey,

I would love to be able to convert from one format to another directly from a Python addon. For instance I have a folder with wav recordings which I would like to convert to MP3 using my Python addon. Pure Python would be slow and tedious, so I thought why not using FFmpeg. Shipping a standalone library/binary would be too much effort, and it wouldn't necessarily work on all platforms Kodi can run on. However I realized that the kodi binary already has ffmpeg (or rather libav) functions globally exported:

Code:
readelf -a --wide /usr/lib/kodi/kodi.bin | grep 'avformat_open_input'
17377: 00000000021fbff0  1759 FUNC    GLOBAL DEFAULT   12 avformat_open_input

I have grepped for all the functions I would need and figured that its technically doable. So then I tried using ctypes. First I wrote some simple code to check if I can get version info at least:

Code:
import ctypes
import sys

kodi_bin = ctypes.CDLL('/usr/lib/kodi/kodi.bin')

kodi_bin.av_version_info.restype = ctypes.c_char_p

def print_ffmpeg_version():
    version_info = kodi_bin.av_version_info()
    if version_info:
        version_string = version_info.decode('utf-8')
        print(version_string, file=sys.stderr)
    else:
        print("Could not retrieve FFmpeg version information", file=sys.stderr)

print_ffmpeg_version()

But then I got:
Code:

Error Type: <class 'OSError'>
Error Contents: /usr/lib/kodi/kodi.bin: cannot dynamically load position-independent executable
Traceback (most recent call last):
File "/home/mrdini/.kodi/addons/plugin.video.testaddon/default.py", line 9, in <module>
kodi_bin = ctypes.CDLL('/usr/lib/kodi/kodi.bin', use_errno=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/ctypes/__init__.py", line 379, in __init__
self._handle = _dlopen(self._name, mode)
^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: /usr/lib/kodi/kodi.bin: cannot dynamically load position-independent executable

That makes sense I guess since kodi was compiled without the no pie flag. Overall that's a great protection. But I am wondering, is there no other tricky way I could utilize so I could call these methods from Python somehow? Even if it requires a second Kodi process to run and using ptrace or some stuff to manipulate what's being run.

My goal would be to target Linux and Android hosts at least. Windows would be secondary and the rest I don't care about much.

Thanks for the ideas!


RE: Using Kodi's bundled ffmpeg from Python? - Mr Dini - 2024-06-05

Actually I realized that we have an embedded Python interpreter, which means my addon code runs in the same process like kodi. So there should be nothing to stop me from calling the functions I want!

I came up with this:

Code:
def get_kodi_base_address():
with open('/proc/self/maps', 'r') as maps_file:
for line in maps_file:
if 'kodi.bin' in line:
base_address = int(line.split('-')[0], 16)
return base_address
return None
FUNCTYPE = ctypes.CFUNCTYPE(ctypes.c_char_p)
func = FUNCTYPE(get_kodi_base_address() + 0x21d19a0)
def print_ffmpeg_version():
version_info = func()
if version_info:
version_string = version_info.decode('utf-8')
print(version_string, file=sys.stderr)
else:
print("Could not retrieve FFmpeg version information", file=sys.stderr)
print_ffmpeg_version()

0x21d19a0 is the address of av_version_info on my machine. I determined it using the readelf output.

And it actually works. I see this in kodi.log:
Quote:2024-06-05 16:51:43.285 T:7456 error <general>: 6.0.1-Kodi
So this is actually wonderful.

But this is definitely not platform and kodi build independent. Obtaining the base address seems to be the harder task from pure Python. On linux/Android I could probably rely on /proc/self/maps, but what about Windows? I actually haven't found a way to obtain the base address using ctypes or some other built-in. I can get the address of print or any other python built-in, but that's not what I need.

And getting the address of the function in the binary also seems to come with a twist. Ideally I would run some shellcode that attempts to find the function in memory using its fingerprint or something. But that means I would need to hardcode a specific shellcode for each architecture. Not too painful, but still painful.

Another way I see for linux is https://github.com/eliben/pyelftools. Haven't tested it, but it claims to be pure python. But that wouldn't work for Windows either.

Any ideas?