3 # Finds FFmpeg libraries
5 # This module will first look for the required library versions on the system.
6 # If they are not found, it will fall back to downloading and building kodi's own version
9 # the following variables influence behaviour:
10 # ENABLE_INTERNAL_FFMPEG - if enabled, kodi's own version will always be built
12 # FFMPEG_PATH - use external ffmpeg not found in system paths
13 # usage: -DFFMPEG_PATH=/path/to/ffmpeg_install_prefix
15 # WITH_FFMPEG - use external ffmpeg not found in system paths
16 # WARNING: this option is for developers as it will _disable ffmpeg version checks_!
17 # Consider using FFMPEG_PATH instead, which _does_ check library versions
18 # usage: -DWITH_FFMPEG=/path/to/ffmpeg_install_prefix
21 # This will define the following target:
23 # ${APP_NAME_LC}::FFMPEG - The FFmpeg interface target
27 # Macro to build internal FFmpeg
28 # Refactoring to a macro allows simple fallthrough callback if system ffmpeg failure
30 include(cmake/scripts/common/ModuleHelpers.cmake)
32 # Check for dependencies - Must be done before SETUP_BUILD_VARS
33 get_libversion_data("dav1d" "target")
34 find_package(Dav1d ${LIB_DAV1D_VER} MODULE)
35 if(NOT TARGET ${APP_NAME_LC}::Dav1d)
36 message(STATUS "dav1d not found, internal ffmpeg build will be missing AV1 support!")
38 set(FFMPEG_OPTIONS -DENABLE_DAV1D=ON)
45 list(APPEND FFMPEG_OPTIONS -DENABLE_CCACHE=${ENABLE_CCACHE}
46 -DCCACHE_PROGRAM=${CCACHE_PROGRAM}
47 -DENABLE_VAAPI=${ENABLE_VAAPI}
48 -DENABLE_VDPAU=${ENABLE_VDPAU}
49 -DEXTRA_FLAGS=${FFMPEG_EXTRA_FLAGS})
52 set(CROSS_ARGS -DDEPENDS_PATH=${DEPENDS_PATH}
53 -DPKG_CONFIG_EXECUTABLE=${PKG_CONFIG_EXECUTABLE}
54 -DCROSSCOMPILING=${CMAKE_CROSSCOMPILING}
56 -DCMAKE_AR=${CMAKE_AR})
60 list(APPEND FFMPEG_OPTIONS -DUSE_LTO=ON)
63 set(LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS})
64 list(APPEND LINKER_FLAGS ${SYSTEM_LDFLAGS})
66 # Some list shenanigans not being passed through without stringify/listify
67 # externalproject_add allows declaring list separator to generate a list for the target
68 string(REPLACE ";" "|" FFMPEG_MODULE_PATH "${CMAKE_MODULE_PATH}")
69 set(FFMPEG_LIST_SEPARATOR LIST_SEPARATOR |)
71 set(CMAKE_ARGS -DCMAKE_MODULE_PATH=${FFMPEG_MODULE_PATH}
72 -DFFMPEG_VER=${FFMPEG_VER}
73 -DCORE_SYSTEM_NAME=${CORE_SYSTEM_NAME}
74 -DCORE_PLATFORM_NAME=${CORE_PLATFORM_NAME_LC}
76 -DENABLE_NEON=${ENABLE_NEON}
77 -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
78 -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
79 -DENABLE_CCACHE=${ENABLE_CCACHE}
80 -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
81 -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
82 -DCMAKE_EXE_LINKER_FLAGS=${LINKER_FLAGS}
85 -DPKG_CONFIG_PATH=${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/lib/pkgconfig)
86 set(PATCH_COMMAND ${CMAKE_COMMAND} -E copy
87 ${CMAKE_SOURCE_DIR}/tools/depends/target/ffmpeg/CMakeLists.txt
90 if(CMAKE_GENERATOR STREQUAL Xcode)
91 set(FFMPEG_GENERATOR CMAKE_GENERATOR "Unix Makefiles")
96 if(TARGET ${APP_NAME_LC}::Dav1d)
97 add_dependencies(ffmpeg ${APP_NAME_LC}::Dav1d)
100 find_program(BASH_COMMAND bash)
102 message(FATAL_ERROR "Internal FFmpeg requires bash.")
104 file(WRITE ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ffmpeg/ffmpeg-link-wrapper
106 if [[ $@ == *${APP_NAME_LC}.bin* || $@ == *${APP_NAME_LC}${APP_BINARY_SUFFIX}* || $@ == *${APP_NAME_LC}.so* || $@ == *${APP_NAME_LC}-test* || $@ == *MacOS/Kodi* ]]
108 avcodec=`PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig ${PKG_CONFIG_EXECUTABLE} --libs --static libavcodec`
109 avformat=`PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig ${PKG_CONFIG_EXECUTABLE} --libs --static libavformat`
110 avfilter=`PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig ${PKG_CONFIG_EXECUTABLE} --libs --static libavfilter`
111 avutil=`PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig ${PKG_CONFIG_EXECUTABLE} --libs --static libavutil`
112 swscale=`PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig ${PKG_CONFIG_EXECUTABLE} --libs --static libswscale`
113 swresample=`PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig ${PKG_CONFIG_EXECUTABLE} --libs --static libswresample`
114 gnutls=`PKG_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig/ ${PKG_CONFIG_EXECUTABLE} --libs-only-l --static --silence-errors gnutls`
115 $@ $avcodec $avformat $avcodec $avfilter $swscale $swresample -lpostproc $gnutls
119 file(COPY ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ffmpeg/ffmpeg-link-wrapper
120 DESTINATION ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}
121 FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
123 set(FFMPEG_LINK_EXECUTABLE "${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ffmpeg-link-wrapper <CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" PARENT_SCOPE)
124 set(FFMPEG_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/include)
126 set(FFMPEG_VERSION ${FFMPEG_VER})
128 # Whilst we use ffmpeg-link-wrapper, we only need INTERFACE at most, and possibly
129 # just not at all. However this gives target consistency with external FFMPEG usage
130 # The benefit and reason to continue to use the wrapper is to automate the collection
131 # of the actual linker flags from pkg-config lookup
133 add_library(ffmpeg::libavcodec INTERFACE IMPORTED)
134 set_target_properties(ffmpeg::libavcodec PROPERTIES
135 INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}")
137 add_library(ffmpeg::libavfilter INTERFACE IMPORTED)
138 set_target_properties(ffmpeg::libavfilter PROPERTIES
139 INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}")
141 add_library(ffmpeg::libavformat INTERFACE IMPORTED)
142 set_target_properties(ffmpeg::libavformat PROPERTIES
143 INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}")
145 add_library(ffmpeg::libavutil INTERFACE IMPORTED)
146 set_target_properties(ffmpeg::libavutil PROPERTIES
147 INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}")
149 add_library(ffmpeg::libswscale INTERFACE IMPORTED)
150 set_target_properties(ffmpeg::libswscale PROPERTIES
151 INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}")
153 add_library(ffmpeg::libswresample INTERFACE IMPORTED)
154 set_target_properties(ffmpeg::libswresample PROPERTIES
155 INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}")
157 add_library(ffmpeg::libpostproc INTERFACE IMPORTED)
158 set_target_properties(ffmpeg::libpostproc PROPERTIES
159 INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIR}")
163 # Allows building with external ffmpeg not found in system paths,
164 # without library version checks
166 set(FFMPEG_PATH ${WITH_FFMPEG})
167 message(STATUS "Warning: FFmpeg version checking disabled")
168 set(REQUIRED_FFMPEG_VERSION undef)
170 # required ffmpeg library versions
171 set(REQUIRED_FFMPEG_VERSION 6.0.0)
172 set(_avcodec_ver ">=60.2.100")
173 set(_avfilter_ver ">=9.3.100")
174 set(_avformat_ver ">=60.3.100")
175 set(_avutil_ver ">=58.2.100")
176 set(_postproc_ver ">=57.1.100")
177 set(_swresample_ver ">=4.10.100")
178 set(_swscale_ver ">=7.1.100")
181 # Allows building with external ffmpeg not found in system paths,
182 # with library version checks
184 set(ENABLE_INTERNAL_FFMPEG OFF)
187 if(ENABLE_INTERNAL_FFMPEG)
192 list(APPEND CMAKE_PREFIX_PATH ${FFMPEG_PATH})
195 set(FFMPEG_PKGS libavcodec${_avcodec_ver}
196 libavfilter${_avfilter_ver}
197 libavformat${_avformat_ver}
198 libavutil${_avutil_ver}
199 libswscale${_swscale_ver}
200 libswresample${_swresample_ver}
201 libpostproc${_postproc_ver})
204 find_package(PkgConfig REQUIRED)
206 pkg_check_modules(PC_FFMPEG ${FFMPEG_PKGS})
210 AND PC_FFMPEG_libavcodec_VERSION
211 AND PC_FFMPEG_libavfilter_VERSION
212 AND PC_FFMPEG_libavformat_VERSION
213 AND PC_FFMPEG_libavutil_VERSION
214 AND PC_FFMPEG_libswscale_VERSION
215 AND PC_FFMPEG_libswresample_VERSION
216 AND PC_FFMPEG_libpostproc_VERSION)
218 set(FFMPEG_VERSION ${REQUIRED_FFMPEG_VERSION})
220 # macro for find_library usage
221 # arg1: lowercase libname (eg libavcodec, libpostproc, etc)
222 macro(ffmpeg_find_lib libname)
223 string(TOUPPER ${libname} libname_UPPER)
224 string(REPLACE "lib" "" name ${libname})
226 find_library(FFMPEG_${libname_UPPER}
227 NAMES ${name} ${libname}
228 PATH_SUFFIXES ffmpeg/${libname}
229 HINTS ${DEPENDS_PATH}/lib ${PC_FFMPEG_${libname}_LIBDIR}
230 ${${CORE_PLATFORM_LC}_SEARCH_CONFIG})
233 find_path(FFMPEG_INCLUDE_DIRS libavcodec/avcodec.h libavfilter/avfilter.h libavformat/avformat.h
234 libavutil/avutil.h libswscale/swscale.h libpostproc/postprocess.h
236 HINTS ${DEPENDS_PATH}/include
237 ${${CORE_PLATFORM_LC}_SEARCH_CONFIG})
239 foreach(_ffmpeg_pkg IN ITEMS ${FFMPEG_PKGS})
240 string(REGEX REPLACE ">=.*" "" _libname ${_ffmpeg_pkg})
241 ffmpeg_find_lib(${_libname})
244 include(FindPackageHandleStandardArgs)
245 find_package_handle_standard_args(FFMPEG
246 VERSION_VAR FFMPEG_VERSION
247 REQUIRED_VARS FFMPEG_INCLUDE_DIRS
256 FAIL_MESSAGE "FFmpeg ${REQUIRED_FFMPEG_VERSION} not found, please consider using -DENABLE_INTERNAL_FFMPEG=ON")
258 # Macro to populate target
259 # arg1: lowercase libname (eg libavcodec, libpostproc, etc)
260 macro(ffmpeg_create_target libname)
261 string(TOUPPER ${libname} libname_UPPER)
262 string(REPLACE "lib" "" name ${libname})
264 if(PKG_CONFIG_FOUND AND NOT WIN32)
265 # We have to run the check against the single lib a second time, as when
266 # pkg_check_modules is run with a list, the only *_LDFLAGS set is a concatenated
267 # list of all checked modules. Ideally we want each target to only have the LDFLAGS
268 # required for that specific module
269 pkg_check_modules(PC_FFMPEG_${libname} ${libname}${_${name}_ver} QUIET)
271 # pkg-config LDFLAGS always seem to have -l<name> listed. We dont need that, as
272 # the target gets a direct path to the physical lib
273 list(REMOVE_ITEM PC_FFMPEG_${libname}_LDFLAGS "-l${name}")
275 # Darwin platforms return a list that cmake splits "framework libname" into separate
276 # items, therefore the link arguments become -framework -libname causing link failures
277 # we just force concatenation of these instances, so cmake passes it as "-framework libname"
278 if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
279 string(REGEX REPLACE "framework;" "framework " PC_FFMPEG_${libname}_LDFLAGS "${PC_FFMPEG_${libname}_LDFLAGS}")
283 add_library(ffmpeg::${libname} UNKNOWN IMPORTED)
284 set_target_properties(ffmpeg::${libname} PROPERTIES
285 FOLDER "FFMPEG - External Projects"
286 IMPORTED_LOCATION "${FFMPEG_${libname_UPPER}}"
287 INTERFACE_LINK_LIBRARIES "${PC_FFMPEG_${libname}_LDFLAGS}"
288 INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIRS}")
291 foreach(_ffmpeg_pkg IN ITEMS ${FFMPEG_PKGS})
292 string(REGEX REPLACE ">=.*" "" _libname ${_ffmpeg_pkg})
293 ffmpeg_create_target(${_libname})
298 message(FATAL_ERROR "FFmpeg not found, please consider using -DENABLE_INTERNAL_FFMPEG=ON")
300 message(STATUS "FFmpeg ${REQUIRED_FFMPEG_VERSION} not found, falling back to internal build")
307 set(_ffmpeg_definitions FFMPEG_VER_SHA=${FFMPEG_VERSION})
309 if(NOT TARGET ${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME})
310 add_library(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE IMPORTED)
311 set_target_properties(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} PROPERTIES
312 INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIRS}"
313 INTERFACE_COMPILE_DEFINITIONS "${_ffmpeg_definitions}")
316 target_link_libraries(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE ffmpeg::libavcodec)
317 target_link_libraries(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE ffmpeg::libavfilter)
318 target_link_libraries(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE ffmpeg::libavformat)
319 target_link_libraries(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE ffmpeg::libavutil)
320 target_link_libraries(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE ffmpeg::libswscale)
321 target_link_libraries(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE ffmpeg::libswresample)
322 target_link_libraries(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} INTERFACE ffmpeg::libpostproc)
325 add_dependencies(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} ffmpeg)