[FileItem] Fix mimetype content lookup
[xbmc.git] / cmake / modules / FindFFMPEG.cmake
blob26dd6e8301da124588fcba6c7d2063bba3da3ae8
1 # FindFFMPEG
2 # --------
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
8 # --------
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
20 # --------
21 # This will define the following target:
23 # ${APP_NAME_LC}::FFMPEG  - The FFmpeg interface target
24 # --------
27 # Macro to build internal FFmpeg
28 # Refactoring to a macro allows simple fallthrough callback if system ffmpeg failure
29 macro(buildFFMPEG)
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!")
37   else()
38     set(FFMPEG_OPTIONS -DENABLE_DAV1D=ON)
39   endif()
41   set(MODULE_LC ffmpeg)
43   SETUP_BUILD_VARS()
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})
51   if(KODI_DEPENDSBUILD)
52     set(CROSS_ARGS -DDEPENDS_PATH=${DEPENDS_PATH}
53                    -DPKG_CONFIG_EXECUTABLE=${PKG_CONFIG_EXECUTABLE}
54                    -DCROSSCOMPILING=${CMAKE_CROSSCOMPILING}
55                    -DOS=${OS}
56                    -DCMAKE_AR=${CMAKE_AR})
57   endif()
59   if(USE_LTO)
60     list(APPEND FFMPEG_OPTIONS -DUSE_LTO=ON)
61   endif()
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}
75                  -DCPU=${CPU}
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}
83                  ${CROSS_ARGS}
84                  ${FFMPEG_OPTIONS}
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
88                     <SOURCE_DIR>)
90   if(CMAKE_GENERATOR STREQUAL Xcode)
91     set(FFMPEG_GENERATOR CMAKE_GENERATOR "Unix Makefiles")
92   endif()
94   BUILD_DEP_TARGET()
96   if(TARGET ${APP_NAME_LC}::Dav1d)
97     add_dependencies(ffmpeg ${APP_NAME_LC}::Dav1d)
98   endif()
100   find_program(BASH_COMMAND bash)
101   if(NOT BASH_COMMAND)
102     message(FATAL_ERROR "Internal FFmpeg requires bash.")
103   endif()
104   file(WRITE ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ffmpeg/ffmpeg-link-wrapper
105 "#!${BASH_COMMAND}
106 if [[ $@ == *${APP_NAME_LC}.bin* || $@ == *${APP_NAME_LC}${APP_BINARY_SUFFIX}* || $@ == *${APP_NAME_LC}.so* || $@ == *${APP_NAME_LC}-test* || $@ == *MacOS/Kodi* ]]
107 then
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
116 else
117   $@
118 fi")
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)
125   set(FFMPEG_FOUND 1)
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}")
160 endmacro()
163 # Allows building with external ffmpeg not found in system paths,
164 # without library version checks
165 if(WITH_FFMPEG)
166   set(FFMPEG_PATH ${WITH_FFMPEG})
167   message(STATUS "Warning: FFmpeg version checking disabled")
168   set(REQUIRED_FFMPEG_VERSION undef)
169 else()
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")
179 endif()
181 # Allows building with external ffmpeg not found in system paths,
182 # with library version checks
183 if(FFMPEG_PATH)
184   set(ENABLE_INTERNAL_FFMPEG OFF)
185 endif()
187 if(ENABLE_INTERNAL_FFMPEG)
188   buildFFMPEG()
189 else()
190   # external FFMPEG
191   if(FFMPEG_PATH)
192     list(APPEND CMAKE_PREFIX_PATH ${FFMPEG_PATH})
193   endif()
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})
203   if(NOT WIN32)
204     find_package(PkgConfig REQUIRED)
206     pkg_check_modules(PC_FFMPEG ${FFMPEG_PKGS})
207   endif()
209   if((PC_FFMPEG_FOUND
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)
217      OR WIN32)
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})
231     endmacro()
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
235               PATH_SUFFIXES ffmpeg
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})
242     endforeach()
244     include(FindPackageHandleStandardArgs)
245     find_package_handle_standard_args(FFMPEG
246                                       VERSION_VAR FFMPEG_VERSION
247                                       REQUIRED_VARS FFMPEG_INCLUDE_DIRS
248                                                     FFMPEG_LIBAVCODEC
249                                                     FFMPEG_LIBAVFILTER
250                                                     FFMPEG_LIBAVFORMAT
251                                                     FFMPEG_LIBAVUTIL
252                                                     FFMPEG_LIBSWSCALE
253                                                     FFMPEG_LIBSWRESAMPLE
254                                                     FFMPEG_LIBPOSTPROC
255                                                     FFMPEG_VERSION
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}")
280         endif()
281       endif()
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}")
289     endmacro()
291     foreach(_ffmpeg_pkg IN ITEMS ${FFMPEG_PKGS})
292       string(REGEX REPLACE ">=.*" "" _libname ${_ffmpeg_pkg})
293       ffmpeg_create_target(${_libname})
294     endforeach()
296   else()
297     if(FFMPEG_PATH)
298       message(FATAL_ERROR "FFmpeg not found, please consider using -DENABLE_INTERNAL_FFMPEG=ON")
299     else()
300       message(STATUS "FFmpeg ${REQUIRED_FFMPEG_VERSION} not found, falling back to internal build")
301       buildFFMPEG()
302     endif()
303   endif()
304 endif()
306 if(FFMPEG_FOUND)
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}")
314   endif()
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)
324   if(TARGET ffmpeg)
325     add_dependencies(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} ffmpeg)
326   endif()
327 endif()