Deployment of external libraries (like ffmpeg)

Hello everybody,
i am currently learning a bit of FFmpeg and i am trying to use it for a simple video application (for now just tests). I am linking it dynamically, so ffmpeg is correctly installed somewhere on my machine, i changed my juce project settings and i can compile and link. Seems to work nicely so far. I even read some frames from a file.
But now a crucial question for the long run came up: in my current setup ffmpeg has to be “correctly” installed (PATH variables set, etc.). Would it be possible to distribute FFmpeg together with my app (ideally in a subdirectory) without having the Path-variable touched? How do i have to set up my project, so that the debug version links to my main installation of FFmpeg but the release version links to copy off FFmpeg sitting in a relative path to my compiled application? Is this actually possible?
I hope i expressed my self clearly. It’s complicated stuff and i am still learning…
Clemens

It depends on the platform.

Windows:

  • Link against a shared ffmpeg library, provide that DLL with your binary; Typically the DLL has to live in the same directory.

macOS:

  • Bundle the library inside your app/plugin bundle. You might need to look into something called RPATH; it provides the runtime linker with a relative path to the library (usually in your ‘Frameworks’ bundle sub-directory).

Thanks for the quick reply, happy to hear that and i can’t wait to test it!

This worked pretty straight forward for windows.

On MacOS i was trying around an i am still confused. There is a lot about this on the forum and the rest of the internet, but somehow i still don’t get it.
In theory i know what i have to do:

  1. copy *.dylib files to a subfolder of my Application
  2. Tell the dynamic linker to look for them at the given location

Step 1 is easy. A simple build script does the job (mkdir, cp). But the rest is very confusing.

First weird fact is, that otool -l tells me that the libs are installed at my main ffmpeg installation /opt/homebrew/Cellar/ffmpeg. That would be understandable, if i hadn’t linked to a copy of ffmpeg (set in Extra Linker Flags) in my projucer project folder. I also changed the name of the original installation to be sure, that my application won’t use that one. Why is this still set to the main installation, even if told my Application to link to the copy?

Then i tried to manually set up an RPATH ( “-rpath @executable_path/…/libs” in Extra Linker Flags) and change the path to the dylib-files using install_name_tool with something like this:
install_name_tool -change /opt/homebrew/opt/ffmpeg/lib/libavformat.59.dylib "@rpath/libavformat.59.dylib" "${BUILT_PRODUCTS_DIR}/ffmpeg_test.app/Contents/MacOS/ffmpeg_test""

otool -l tells me now:

Load command 13
          cmd LC_LOAD_DYLIB
      cmdsize 56
         name @rpath/libavformat.59.dylib (offset 24)
   time stamp 2 Thu Jan  1 01:00:02 1970
      current version 59.27.100
compatibility version 59.0.0

This seems right to me. But i run my application now, i get the following error:

dyld: Library not loaded: /opt/homebrew/Cellar/ffmpeg/5.1.2/lib/libavcodec.59.dylib
  Referenced from: /Users/clemens/Documents/Git/ffmpeg_test/Builds/MacOSX/build/Debug/ffmpeg_test.app/Contents/libs/libavformat.59.dylib
  Reason: image not found

Looks like not only my app is referencing libavcodec.59.dylib, but also libavformat.59.dylib, and the linker looking for it at the main installation. Again! I am sure i am pretty close to solve this, but probably missed something. Does anybody know, what my mistake is?

I just had the idea to run otool -l on one of the dylib-files. It shows me that it depends not only on other ffmpeg files, but also on a whole bunch of other stuff in my opt/homebrew directory. This now really gives me a headache. This probably means, that my app won’t run on a computer that doesn’t have ffmpeg installed the same way mine does, even if i manage to get the linker to find the libraries inside the app.

Yes, the libraries reference each other and each one uses it’s own darned path which was baked in at build time.

I once wrote a script, but it was a very fragile workflow.
The better way is when you configure ffmpeg to set

--install-name-dir='@executable_path/../Resources/ffmpeg_libs'

But that means you cannot use prebuilt binaries. It’s a pain I haven’t been able to solve satisfactory

EDIT: I found the said script again, not sure it helps:

Thank you Daniel! The script will be of great help!

You mean compile it and it’s dependencies myself without homebrew? :open_mouth:

Either that or you just overwrite the paths within the libraries using install_name_tool.

I think you should consider compiling yourself since Homebrew will probably provide an FFmpeg version that can’t be redistributed in commercial applications.

Well…I adapted the sript to my needs and was indeed helpful to understand the whole process a bit better. As a side effect i refreshed my bash skills a bit :slight_smile:
But as I expected it cannot solve the problems with ffmpeg’s dependencies. I believe using install_name_tool on all lib-files as well is too tedious. So probably there is no way around compiling ffmpeg from scratch to get all the install names right.
And there are still some questions open around the dependencies of ffmpeg. as said before otool tells me that ffmpeg needs quite a few things from the homebrew directory, i.e. /opt/homebrew/opt/libx11/lib/libX11.6.dylib and many many more. Do i actually have to copy ALL of those into my app to make ffmpeg work? I don’t want to believe this, since on windows the dlls work as they are! Without hundreds of other libs…

I think you should consider compiling yourself since Homebrew will probably provide an FFmpeg version that can’t be redistributed in commercial applications.

Thank you for that hint!

Turned out compiling ffmpeg is not as hard as i thought. Probably i was still horrfied from my first experiences with gentoo linux years ago. How i did it:

  • i installed mac ports
  • i used macports to install the dependencies
  • downloaded ffmpeg source code, then configure, make and so on.

I also found many useful configure options like --enable-gpl and --enable-nonfree (which i discarded) or --enable-rpath and --install-name-dir='@executable_path/../libs' which i added. Configure now gave me the output License: LGPL version 3 or later. Thats’s great. Even better: the install_name is now correctly set (checked with otool). I am still playing around with these options. Later i want to keep only stuff that is absolutly needed. Right now i use the options as follows:

./configure --prefix='../../osx' --enable-swscale --enable-avfilter --enable-libmp3lame --enable-libvorbis --enable-libopus --enable-librsvg --enable-libtheora --enable-libopenjpeg --enable-libmodplug --enable-libsoxr --enable-libspeex --enable-libass --enable-libbluray --enable-lzma --enable-gnutls --enable-fontconfig --enable-libfreetype --enable-libfribidi --disable-libjack --disable-libopencore-amrnb --disable-libopencore-amrwb --disable-libxcb --disable-libxcb-shm --disable-libxcb-xfixes --disable-indev=jack --enable-opencl --disable-outdev=xv --enable-audiotoolbox --enable-videotoolbox --enable-sdl2 --disable-securetransport --mandir=/opt/local/share/man --enable-pthreads --cc=/usr/bin/clang --enable-libdav1d --enable-postproc --enable-libfdk-aac --extra-ldflags=-L/opt/local/lib --enable-version3 --enable-static --enable-shared --install-name-dir='@executable_path/../libs' --enable-rpath

But i am still puzzled about all the dependencies. The libraries still link to stuff outside my new installation. Is this a problem? Can this be ignored? Or do i have to compile all the dependencies, too?

And one more question about --enable-rpath. Obviously this just “enables” rpath. But is there a way to set where the rpath is? Now it gets set to

Load command 44
          cmd LC_RPATH
      cmdsize 32
         path ../../osx/lib (offset 12)

which is actually the target directory for ffmpeg which i set via --prefix=‘…/…/osx’. If i understood everything right, i think this should be @executable_path/../libs, like the install_name, right? Probably it’s not a big deal, since i can use install_name_tool to correct this. But if am already compiling myself, i want to do it right!

I solved this with adding --extra-ldflags=-Wl,-rpath,'@executable_path/../libs' and removing --enable-rpath. My test program compiles and starts. But still fear that the library files still access dependencies that are installed elsewhere. Is there a way to see, which dependencies get loaded?

The command is ˋotool -L myBinaryˋ , which works similar to windows depends.exe.

Thank you really much so far. without your help i would not have gotten so far…
And, wow, what a rabbit hole :sweat_smile:
I was trying many things again to reduce the number of dependencies. An my newest attempt is to find a minimal configuration off ffmpeg that suits my needs: just playback of video files…so i decided to build ffmpeg with the most basic options i could think of:
./configure --disable-everything --prefix='../../osx_arm' --install-name-dir='@executable_path/../libs' --extra-ldflags=-Wl,-rpath,'@executable_path/../libs' --enable-version3 --enable-shared --disable-static --enable-swscale --enable-avfilter --enable-libfdk-aac --enable-audiotoolbox --enable-videotoolbox --enable-pthreads --cc=/usr/bin/clang --extra-ldflags=-L/opt/local/lib

This actually works, i can build my app and it starts, but - as expected - ffmpeg can’t do anything :wink: And i still got dependencies! For example libiconv and libX11. So i guess these are dependencies that i should definitly have in my App (even if i don’t know why the hell ffmpeg needs libX11).
make

Now i want to switch on the features i need for playback one by one and then look for dependencies. When i got everything, i want to compile everything my self and bundle it with my App. Sounds smart to me… Does anyone have a tip on which options are essential for video playback? Or are there tips on how to find the minimal configuration i need? There is sooooo many options.

I think i solved it. The Problem was actually installing the dependencies before via macports and linking to them ( --extra-ldflags=-L/opt/local/lib). None of them were needed. After deleting macports completly and removing that linker command everything compiled fine without any annoying dependencies.
Probably i will still play around with all the options, but right now i build FFmpeg using the following configuration, which gives me libs of about 15mb:

./configure --prefix='../../osx_arm' --install-name-dir='@executable_path/../libs' --extra-ldflags=-Wl,-rpath,'@executable_path/../libs' --cc=/usr/bin/clang --disable-filters --disable-programs --disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages --enable-version3 --disable-static --enable-shared --enable-avdevice --enable-avcodec --enable-avformat --enable-swresample --enable-swscale --enable-avfilter --disable-libjack --disable-libopencore-amrnb --disable-libopencore-amrwb --disable-libxcb --disable-libxcb-shm --disable-libxcb-xfixes --disable-indev=jack --disable-outdev=xv --enable-audiotoolbox --enable-videotoolbox --disable-securetransport --enable-pthreads --disable-protocols --enable-protocol='file'

Again, thanks for pushing me in the right direction!

1 Like