How to ship a prebuilt third-party .so next to a Turbo Module (FFmpeg LGPL, dynamic linking)?

Hi,

I have a Turbo Module (basic-turbo-module template, SDK 0.24.12112, RN 0.83) that links against FFmpeg 9.0.2 built as shared libraries (libavcodec.so.63, libavutil.so.61, libswresample.so.7), cross-compiled with the SDK toolchains for x86_64, armv7 and aarch64. Their NEEDED entries are only libc and libm.

FFmpeg is LGPL 2.1, so I need to link it dynamically and keep the .so files separate.

In the module CMakeLists.txt I do what the linker namespacing page shows for libcurl:

install(FILES ${ff_real} DESTINATION lib RENAME libavcodec.so.63)

The files end up in build/-/lib next to libvodbridge.so. But dist/kepler//lib only contains libvodbridge.so, and the app .vpkg only gets lib//libvodbridge.so. The app build then fails ABI validation, because libavcodec.so.63 is not found.

Copying the .so files into dist/kepler/*/lib by hand and packing with --ignore-scripts does not help: the app still only takes the library named in react-native.config.js.

Questions:

  1. What is the supported way for a Turbo Module package to ship extra prebuilt shared libraries into the app package (lib//)?
  2. Can react-native.config.js autolink declare more than one library?
  3. If this is not supported yet, is static linking the recommended path for third-party libraries on Vega?

Thanks!

Hi @Ciriac

Welcome to Amazon Developer Community!!

Thanks for the detailed report. Here’s what’s going on and how to fix it - your dynamic-linking approach is correct, you just need one adjustment.

Why this happens
The packaging and autolinking steps don’t restrict themselves to the single library named by libraryName in react-native.config.js - libraryName is only used to verify that your main library is present, not to filter what gets bundled. Both steps instead collect files by name pattern:

  • Packaging copies files matching *.{so,so.debug} from your module’s build output into dist/kepler/<variant>/lib.
  • App-side autolinking collects every discovered file whose name ends in .so and copies them into the app’s lib/<arch>/.

The issue is that both of these only match names ending in .so (or .so.debug) - they do not match versioned sonames like .so.63. Your libraries are named libavcodec.so.63, libavutil.so.61, and libswresample.so.7, so they’re silently skipped at both steps, never reach lib/<arch>/, and ABI validation then can’t find libavcodec.so.63. This is also why manually copying them into dist/ didn’t help - the app-side collector filters by name regardless of how the files got there.

The fix

  1. Give the FFmpeg libraries unversioned sonames so their filenames end in .so:
  • Either build FFmpeg with unversioned sonames (patch SLIBNAME_WITH_MAJOR in FFmpeg’s configure), or run patchelf --set-soname libavcodec.so <file> after the build.
  • Update the NEEDED entries in libvodbridge.so (and any inter-FFmpeg references) to match, e.g. patchelf --replace-needed libavcodec.so.63 libavcodec.so libvodbridge.so, or relink against the renamed libraries.
  1. Install them under the unversioned name:
    install(FILES ${ff_avcodec}    DESTINATION lib RENAME libavcodec.so)
    install(FILES ${ff_avutil}     DESTINATION lib RENAME libavutil.so)
    install(FILES ${ff_swresample} DESTINATION lib RENAME libswresample.so)

No change to react-native.config.js is needed - autolinking picks up all .so-named files automatically.

  1. Rebuild the module and app, and confirm the three libraries now appear in dist/kepler/<variant>/lib and in the .vpkg under lib/<arch>/, and that the app launches on device.

Your other two questions

  • Can react-native.config.js autolink declare more than one library? There’s no field for it, and you don’t need one - the pipeline bundles every .so-named file alongside your main library automatically.
  • Is static linking the recommended path? No - I’d steer you away from it. Since you’re dynamically linking LGPL 2.1 code, you can rely on the shared-library mechanism which is the simpler compliance path.

Let us know if renaming to unversioned .so doesn’t fully clear the error - happy to dig in further with your build output.

Warm Regards,
Ivy

Thanks Ivy, that was exactly it.

I went with the configure route: I override SLIBNAME_WITH_MAJOR (and the install names) when running make, so FFmpeg builds libavcodec.so, libavutil.so and libswresample.so with unversioned sonames and the NEEDED entries between them match. No patchelf needed.

The three libs now show up in dist/kepler//lib and in the .vpkg under lib//, and ABI validation passes for x86_64, armv7 and aarch64. On the virtual device the module loads and the AC-3 to AAC transcoding works during playback. I haven’t tried a real Fire TV yet, I’ll post back if anything differs there.

Good to know static linking isn’t the way to go, I’m dropping it.

Thanks @Ciriac for letting me know, great to know your issue is resolved.

Just curious, would you mind letting me know your use case? I can check internally if we can do anything more to help ease your process :slight_smile:

Warm Regards,
Ivy