diff --git a/CMakeLists.txt b/CMakeLists.txt index adf3573..9e78352 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,8 @@ project(rp_soundboard) include(files.cmake) +find_package(Python3 REQUIRED COMPONENTS Interpreter) + # Get prebuilt ffmpeg folders for windows builds set(ffmpegIncHint "ffmpeg/include") @@ -17,11 +19,12 @@ if (WIN32) set(defLibSuffix "_win32.dll") endif() set(defPluginDir "%appdata%/TS3Client/plugins") -elseif(APPLE) # untested thanks to MacOS being a pain in the ass to set up in VM - # mac has no 32 bit variant - set(defFFmpegLibHint "ffmpeg/lib_mac_x64") - set(defLibSuffix "_mac.so") - set(defPluginDir "~/.ts3client/plugins") +elseif(APPLE) + # Build for native arch by default. Override with -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" + # for universal binary (requires universal Qt libs). + set(defFFmpegLibHint "ffmpeg/lib_mac_universal") + set(defLibSuffix "_mac.dylib") + set(defPluginDir "$ENV{HOME}/Library/Application Support/TeamSpeak 3/plugins") else() if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(defFFmpegLibHint "ffmpeg/lib_lin_x64") @@ -65,7 +68,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_custom_target(autogenFiles ALL BYPRODUCTS "src/version/version.h" "deploy/package.ini" COMMENT "running autogen file script" - COMMAND python version.py + COMMAND ${Python3_EXECUTABLE} version.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) @@ -78,6 +81,23 @@ target_link_libraries(rp_soundboard Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Network if (WIN32) # only link this way on Windows. Linux requires VERY special linker commands, see below target_link_libraries(rp_soundboard ${avcodec} ${avformat} ${avutil} ${swresample}) +elseif(APPLE) + target_link_libraries(rp_soundboard ${avcodec} ${avformat} ${avutil} ${swresample}) + # System frameworks required by FFmpeg on macOS + # All are needed because the pre-built static libs contain symbols from these + # frameworks, and -Wl,-all_load pulls in all object files. + find_library(AUDIOTOOLBOX AudioToolbox) + find_library(COREAUDIO CoreAudio) + find_library(COREFOUNDATION CoreFoundation) + find_library(COREMEDIA CoreMedia) + find_library(COREVIDEO CoreVideo) + find_library(VIDEOTOOLBOX VideoToolbox) + find_library(SECURITY Security) + target_link_libraries(rp_soundboard + ${AUDIOTOOLBOX} ${COREAUDIO} ${COREFOUNDATION} + ${COREMEDIA} ${COREVIDEO} ${VIDEOTOOLBOX} ${SECURITY} + bz2 z iconv + ) endif() target_include_directories(rp_soundboard PUBLIC "pluginsdk/include" ${ffmpegIncludeDir}) @@ -92,13 +112,15 @@ if (MSVC) target_sources(rp_soundboard PRIVATE "src/windows/resource.h" "src/windows/Resource.rc") target_link_libraries(rp_soundboard wsock32 ws2_32 secur32) # some windows stuff target_compile_options(rp_soundboard PRIVATE /MP) # multiprocessor compiling +elseif(APPLE) + target_compile_definitions(rp_soundboard PRIVATE "MACOS") + # macOS uses ld64 which has different flags than GNU ld + set_target_properties(rp_soundboard PROPERTIES + LINK_FLAGS "-Wl,-all_load" + ) else() - if(APPLE) - target_compile_definitions(rp_soundboard PRIVATE "MACOS") - else() - target_compile_definitions(rp_soundboard PRIVATE "LINUX") - endif() - # Compile options that are required to NOT get the "recompile with -fPIC" + target_compile_definitions(rp_soundboard PRIVATE "LINUX") + # GNU ld flags required to NOT get the "recompile with -fPIC" # error when linking the ffmpeg libs. Took me many hours to find this out... set_target_properties(rp_soundboard PROPERTIES LINK_FLAGS "-Wl,-Bsymbolic -Wl,--whole-archive \ diff --git a/create-pluginfile.cmake b/create-pluginfile.cmake index dc2af8a..4ef2838 100644 --- a/create-pluginfile.cmake +++ b/create-pluginfile.cmake @@ -6,8 +6,26 @@ execute_process( ) string(STRIP "${gitVersionRaw}" gitVersion) -message("Running 7zip to create final plugin package in ${RPSB_PLUGINFILE_OUTPUT_DIR}") -execute_process( - COMMAND "C:/Program Files/7-Zip/7z.exe" a "${RPSB_PLUGINFILE_OUTPUT_DIR}/rp_soundboard_${gitVersion}.ts3_plugin" -tzip -mx=9 -mm=Deflate "*" - WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}" -) +set(PLUGIN_FILENAME "rp_soundboard_${gitVersion}.ts3_plugin") +set(PLUGIN_OUTPUT "${RPSB_PLUGINFILE_OUTPUT_DIR}/${PLUGIN_FILENAME}") + +message("Creating final plugin package in ${RPSB_PLUGINFILE_OUTPUT_DIR}") + +if(WIN32) + execute_process( + COMMAND "C:/Program Files/7-Zip/7z.exe" a "${PLUGIN_OUTPUT}" -tzip -mx=9 -mm=Deflate "*" + WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}" + RESULT_VARIABLE zip_result + ) +else() + # Use zip on Linux/macOS + execute_process( + COMMAND zip -r -9 "${PLUGIN_OUTPUT}" . + WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}" + RESULT_VARIABLE zip_result + ) +endif() + +if(NOT zip_result EQUAL 0) + message(FATAL_ERROR "Failed to create plugin package (exit code: ${zip_result})") +endif() diff --git a/ffmpeg/build-scripts/build_ffmpeg.sh b/ffmpeg/build-scripts/build_ffmpeg.sh index 01c8f1e..1cecb6f 100755 --- a/ffmpeg/build-scripts/build_ffmpeg.sh +++ b/ffmpeg/build-scripts/build_ffmpeg.sh @@ -18,19 +18,31 @@ else toolchainopt="" fi - -if [[ "$1" == "x86" ]]; then +# On macOS, build universal binary (arm64 + x86_64) by default +if [[ "$machine" == "Mac" && "$1" == "" ]]; then + echo "Machine = Mac (Universal Binary: arm64 + x86_64)" + build_mac_universal=true +elif [[ "$1" == "x86" ]]; then archprefix="x86" arch="x86" + build_mac_universal=false +elif [[ "$1" == "arm64" || "$1" == "aarch64" ]]; then + archprefix="arm64" + arch="aarch64" + build_mac_universal=false elif [[ "$1" == "x64" || "$1" == "x86_64" || "$1" == "amd64" || "$(uname -m)" == "x86_64" ]]; then archprefix="x64" arch="x86_64" + build_mac_universal=false else archprefix="x86" arch="x86" + build_mac_universal=false fi -echo "Machine =" $machine $arch +if [[ "$build_mac_universal" != "true" ]]; then + echo "Machine =" $machine $arch +fi opts="\ --enable-pic \ @@ -320,13 +332,71 @@ disabled_decs="\ --disable-decoder=webvtt \ --disable-decoder=xsub" -cmd="./configure --arch=$arch --prefix=$archprefix $toolchainopt $opts $disabled_decs" +njobs=$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 8) +if [ "$njobs" -gt 16 ]; then + njobs=16 +fi + +build_single_arch() { + local build_arch="$1" + local prefix="$2" + local cc_override="$3" + local extra_configure="$4" + + local -a configure_args=(--arch=$build_arch --prefix=$prefix) + if [[ -n "$cc_override" ]]; then + configure_args+=(--cc="$cc_override") + fi + if [[ "$machine" == "Mac" ]]; then + configure_args+=(--extra-cflags="-Wno-incompatible-function-pointer-types") + fi + + pushd ../ffmpeg + echo "=== Configuring FFmpeg for $build_arch ===" + ./configure "${configure_args[@]}" \ + $extra_configure $toolchainopt $opts $disabled_decs + make clean && make -j$njobs && make install + popd +} -pushd ../ffmpeg -echo "Command: $cmd" -$cmd -make clean && make -j8 && make install +if [[ "$build_mac_universal" == "true" ]]; then + host_arch="$(uname -m)" + cross_compile_flags="--enable-cross-compile --target-os=darwin" -popd + # Build arm64 (cross-compile on Intel, native on Apple Silicon) + echo "=== Building FFmpeg for arm64 ===" + if [[ "$host_arch" == "x86_64" ]]; then + build_single_arch "aarch64" "arm64" "clang -arch arm64" "$cross_compile_flags" + else + build_single_arch "aarch64" "arm64" "clang -arch arm64" "" + fi -./copy_binaries.sh + # Build x86_64 (native on Intel, cross-compile on Apple Silicon) + echo "=== Building FFmpeg for x86_64 ===" + if [[ "$host_arch" == "arm64" ]]; then + build_single_arch "x86_64" "x64" "clang -arch x86_64" "$cross_compile_flags" + else + build_single_arch "x86_64" "x64" "clang -arch x86_64" "" + fi + + # Combine into universal binaries using lipo + echo "=== Creating universal binaries with lipo ===" + mkdir -p ../ffmpeg/universal/lib + cp -R ../ffmpeg/x64/include ../ffmpeg/universal/include + + for lib in libavcodec.a libavformat.a libavutil.a libswresample.a; do + lipo -create \ + "../ffmpeg/arm64/lib/$lib" \ + "../ffmpeg/x64/lib/$lib" \ + -output "../ffmpeg/universal/lib/$lib" + echo "Created universal $lib" + done + + ./copy_binaries.sh +else + # Remove stale universal artifacts to prevent copy_binaries.sh from + # copying outdated libs into lib_mac_universal + rm -rf ../ffmpeg/universal + build_single_arch "$arch" "$archprefix" "" "" + ./copy_binaries.sh +fi diff --git a/ffmpeg/build-scripts/copy_binaries.sh b/ffmpeg/build-scripts/copy_binaries.sh index d5a161a..10b33ad 100755 --- a/ffmpeg/build-scripts/copy_binaries.sh +++ b/ffmpeg/build-scripts/copy_binaries.sh @@ -25,6 +25,16 @@ esac pushd .. +# Universal binary (macOS only) +if [ -d "ffmpeg/universal" ]; then + mkdir -p "${prefix}_universal" + cp ffmpeg/universal/lib/${infix}avcodec.$postfix ${prefix}_universal/${infix}avcodec.$postfix + cp ffmpeg/universal/lib/${infix}avformat.$postfix ${prefix}_universal/${infix}avformat.$postfix + cp ffmpeg/universal/lib/${infix}avutil.$postfix ${prefix}_universal/${infix}avutil.$postfix + cp ffmpeg/universal/lib/${infix}swresample.$postfix ${prefix}_universal/${infix}swresample.$postfix + cp -R ffmpeg/universal/include ${prefix}_universal/include +fi + if [ -d "ffmpeg/x64" ]; then mkdir -p "${prefix}_x64" cp ffmpeg/x64/lib/libavcodec.a ${prefix}_x64/${infix}avcodec.$postfix diff --git a/src/ConfigModel.cpp b/src/ConfigModel.cpp index 7645a63..4bfcd08 100644 --- a/src/ConfigModel.cpp +++ b/src/ConfigModel.cpp @@ -221,7 +221,7 @@ void ConfigModel::setSoundInfo( int itemId, const SoundInfo &info ) QString ConfigModel::GetConfigPath() { // Find config path for config class - char* configPath = (char*)malloc(PATH_BUFSIZE); + char configPath[PATH_BUFSIZE]; ts3Functions.getConfigPath(configPath, PATH_BUFSIZE); return QString::fromUtf8(configPath); } @@ -433,7 +433,7 @@ void ConfigModel::setBubbleColsBuild(int build) //--------------------------------------------------------------- std::vector ConfigModel::getInitialSounds() { - char* pluginPath = (char*)malloc(PATH_BUFSIZE); + char pluginPath[PATH_BUFSIZE]; ts3Functions.getPluginPath(pluginPath, PATH_BUFSIZE, getPluginID()); QString fullPath = QString::fromUtf8(pluginPath); QChar last = fullPath[fullPath.count() - 1]; diff --git a/src/buildinfo.c b/src/buildinfo.c index a4a547f..b746fb9 100644 --- a/src/buildinfo.c +++ b/src/buildinfo.c @@ -17,8 +17,10 @@ #define BUILD_NAME "Linux 64-bit" #elif defined(LINUX) && defined(__i386__) #define BUILD_NAME "Linux 32-bit" +#elif defined(MACOS) && defined(__aarch64__) +#define BUILD_NAME "MacOS arm64" #elif defined(MACOS) -#define BUILD_NAME "MacOS 64-bit" +#define BUILD_NAME "MacOS x86_64" #else #define BUILD_NAME "Unknown Build" #endif diff --git a/src/package.ini.in b/src/package.ini.in index d399122..d719091 100644 --- a/src/package.ini.in +++ b/src/package.ini.in @@ -2,7 +2,7 @@ Name = RP Soundboard Type = Plugin Author = Marius Gräfe Version = @version@ -Platforms = win32, win64, linux_amd64 +Platforms = win32, win64, linux_amd64, mac Description = Easy to use soundboard to annoy your friends. Play any sound file to all people in your channel (and yourself). No external tools and no changes to Teamspeaks settings required!