diff --git a/doc/source/apis.rst b/doc/source/apis.rst index 3404c472d7..baecb6c5bf 100644 --- a/doc/source/apis.rst +++ b/doc/source/apis.rst @@ -72,7 +72,7 @@ unless you blacklist it. Use it in your app like this:: from android.permissions import request_permissions, Permission request_permissions([Permission.WRITE_EXTERNAL_STORAGE]) -The available permissions are listed here: +The available permissions are listed here:/^^^ https://developer.android.com/reference/android/Manifest.permission @@ -80,6 +80,66 @@ https://developer.android.com/reference/android/Manifest.permission Other common tasks ------------------ +Running executables +~~~~~~~~~~~~~~~~~~~ + +Android enforces strict restrictions on executing files from application +data directories. + +The ``android`` module works around this by creating symlinks to a small +set of supported executables in an internal, executable directory. This +directory is added to ``PATH``, allowing these executables to be invoked +from anywhere. + +Currently supported executables are: + +- ``python`` +- ``python3`` +- ``ffmpeg`` + +Importing the ``android`` module is relatively expensive (≈1–2 seconds +on low-end devices), so it should be avoided during early startup unless +required. + +Minimal FFmpeg example +^^^^^^^^^^^^^^^^^^^^^^ + +The following example performs a minimal FFmpeg sanity check using an +in-memory test source and discarding the output:: + + import android + import subprocess + + subprocess.run( + ["ffmpeg", "-f", "lavfi", "-i", "testsrc", "-t", "1", "-f", "null", "-"], + check=True + ) + +This can be used to verify that ``ffmpeg`` is available and executable on +the device. + +Requirements +^^^^^^^^^^^^ + +If you plan to use ``ffmpeg``, ensure it is included in your build +requirements. + +If video encoding is required, the following codec options must also be +enabled in the build configuration: + +- ``av_codecs`` +- ``libx264`` + +Without these, FFmpeg may be present but lack the necessary codec +support. + +See also +^^^^^^^^ + +.. _APK native library execution restrictions: + https://github.com/agnostic-apollo/Android-Docs/blob/master/site/pages/en/projects/docs/apps/processes/app-data-file-execute-restrictions.md#apk-native-library + + Dismissing the splash screen ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/pythonforandroid/androidndk.py b/pythonforandroid/androidndk.py index 83cb355740..7cd26f4c4e 100644 --- a/pythonforandroid/androidndk.py +++ b/pythonforandroid/androidndk.py @@ -66,6 +66,10 @@ def llvm_readelf(self): def llvm_strip(self): return f"{self.llvm_binutils_prefix}strip" + @property + def llvm_nm(self): + return f"{self.llvm_binutils_prefix}nm" + @property def sysroot(self): return os.path.join(self.llvm_prebuilt_dir, "sysroot") diff --git a/pythonforandroid/bootstraps/common/build/jni/application/src/start.c b/pythonforandroid/bootstraps/common/build/jni/application/src/start.c index a494c77dae..7a796cf743 100644 --- a/pythonforandroid/bootstraps/common/build/jni/application/src/start.c +++ b/pythonforandroid/bootstraps/common/build/jni/application/src/start.c @@ -9,6 +9,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -30,8 +33,13 @@ #define ENTRYPOINT_MAXLEN 128 #define LOG(n, x) __android_log_write(ANDROID_LOG_INFO, (n), (x)) -#define LOGP(x) LOG("python", (x)) #define P4A_MIN_VER 11 +static void LOGP(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + __android_log_vprint(ANDROID_LOG_INFO, "python", fmt, args); + va_end(args); +} static PyObject *androidembed_log(PyObject *self, PyObject *args) { char *logstr = NULL; @@ -69,14 +77,125 @@ int dir_exists(char *filename) { } int file_exists(const char *filename) { - FILE *file; - if ((file = fopen(filename, "r"))) { - fclose(file); - return 1; - } - return 0; + return access(filename, F_OK) == 0; +} + +static void get_dirname(const char *path, char *dir, size_t size) { + strncpy(dir, path, size - 1); + dir[size - 1] = '\0'; + char *last_slash = strrchr(dir, '/'); + if (last_slash) *last_slash = '\0'; + else dir[0] = '\0'; +} + +// strip "lib" prefix and "bin.so" suffix +static void get_exe_name(const char *filename, char *out, size_t size) { + size_t len = strlen(filename); + if (len < 7) { // too short to be valid + strncpy(out, filename, size - 1); + out[size - 1] = '\0'; + return; + } + + const char *start = filename; + if (strncmp(filename, "lib", 3) == 0) start += 3; + size_t start_len = strlen(start); + + if (start_len > 6) { + size_t copy_len = start_len - 6; // remove "bin.so" + if (copy_len >= size) copy_len = size - 1; + strncpy(out, start, copy_len); + out[copy_len] = '\0'; + } else { + strncpy(out, start, size - 1); + out[size - 1] = '\0'; + } +} + +char *setup_symlinks() { + Dl_info info; + char lib_path[512]; + char *interpreter = NULL; + + if (!(dladdr((void*)setup_symlinks, &info) && info.dli_fname)) { + LOGP("symlinking failed: failed to get libdir"); + return interpreter; + } + + strncpy(lib_path, info.dli_fname, sizeof(lib_path) - 1); + lib_path[sizeof(lib_path) - 1] = '\0'; + + char native_lib_dir[512]; + get_dirname(lib_path, native_lib_dir, sizeof(native_lib_dir)); + if (native_lib_dir[0] == '\0') { + LOGP("symlinking failed: could not determine lib directory"); + return interpreter; + } + + const char *files_dir_env = getenv("ANDROID_APP_PATH"); + char bin_dir[512]; + + snprintf(bin_dir, sizeof(bin_dir), "%s/.bin", files_dir_env); + if (mkdir(bin_dir, 0755) != 0 && errno != EEXIST) { + LOGP("Failed to create .bin directory"); + return interpreter; + } + + DIR *dir = opendir(native_lib_dir); + if (!dir) { + LOGP("Failed to open native lib dir"); + return interpreter; + } + + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + const char *name = entry->d_name; + size_t len = strlen(name); + + if (len < 7) continue; + if (strcmp(name + len - 6, "bin.so") != 0) continue; // only bin.so at end + + // get cleaned executable name + char exe_name[128]; + get_exe_name(name, exe_name, sizeof(exe_name)); + + char src[512], dst[512]; + snprintf(src, sizeof(src), "%s/%s", native_lib_dir, name); + snprintf(dst, sizeof(dst), "%s/%s", bin_dir, exe_name); + + // interpreter found? + if (strcmp(exe_name, "python") == 0) { + interpreter = strdup(dst); + } + + struct stat st; + if (lstat(dst, &st) == 0) continue; // already exists + if (symlink(src, dst) == 0) { + LOGP("symlink: %s -> %s", name, exe_name); + } else { + LOGP("Symlink failed"); + } + } + + closedir(dir); + + // append bin_dir to PATH + const char *old_path = getenv("PATH"); + char new_path[1024]; + if (old_path && strlen(old_path) > 0) { + snprintf(new_path, sizeof(new_path), "%s:%s", old_path, bin_dir); + } else { + snprintf(new_path, sizeof(new_path), "%s", bin_dir); + } + setenv("PATH", new_path, 1); + + // set lib path + setenv("LD_LIBRARY_PATH", native_lib_dir, 1); + + return interpreter; } + /* int main(int argc, char **argv) { */ int main(int argc, char *argv[]) { @@ -87,7 +206,10 @@ int main(int argc, char *argv[]) { int ret = 0; FILE *fd; - LOGP("Initializing Python for Android"); + LOGP("======== Initializing P4A =========="); + + struct timespec start, end; + clock_gettime(CLOCK_MONOTONIC, &start); // Set a couple of built-in environment vars: setenv("P4A_BOOTSTRAP", bootstrap_name, 1); // env var to identify p4a to applications @@ -147,10 +269,11 @@ int main(int argc, char *argv[]) { LOGP("Warning: no p4a_env_vars.txt found / failed to open!"); } - LOGP("Changing directory to the one provided by ANDROID_ARGUMENT"); - LOGP(env_argument); + LOGP("Changing directory to '%s'", env_argument); chdir(env_argument); + char *interpreter = setup_symlinks(); + #if PY_MAJOR_VERSION < 3 Py_NoSiteFlag=1; #endif @@ -257,12 +380,20 @@ int main(int argc, char *argv[]) { "sys.path.append('%s/site-packages')", python_bundle_dir); - PyRun_SimpleString("import sys\n" - "sys.argv = ['notaninterpreterreally']\n" - "from os.path import realpath, join, dirname"); + PyRun_SimpleString("import sys, os\n" + "from os.path import realpath, join, dirname"); + + char buf_exec[512]; + char buf_argv[512]; + snprintf(buf_exec, sizeof(buf_exec), "sys.executable = '%s'\n", interpreter); + snprintf(buf_argv, sizeof(buf_argv), "sys.argv = ['%s']\n", interpreter); + PyRun_SimpleString(buf_exec); + PyRun_SimpleString(buf_argv); + PyRun_SimpleString(add_site_packages_dir); /* "sys.path.append(join(dirname(realpath(__file__)), 'site-packages'))") */ PyRun_SimpleString("sys.path = ['.'] + sys.path"); + PyRun_SimpleString("os.environ['PYTHONPATH'] = ':'.join(sys.path)"); } PyRun_SimpleString( @@ -280,23 +411,12 @@ int main(int argc, char *argv[]) { " androidembed.log(l.replace('\\x00', ''))\n" " self.__buffer = lines[-1]\n" "sys.stdout = sys.stderr = LogFile()\n" - "print('Android path', sys.path)\n" - "# import os\n" - "# print('os.environ is', os.environ)\n" "print('Android kivy bootstrap done. __name__ is', __name__)"); #if PY_MAJOR_VERSION < 3 PyRun_SimpleString("import site; print site.getsitepackages()\n"); #endif - LOGP("AND: Ran string"); - - /* run it ! - */ - LOGP("Run user program, change dir and execute entrypoint"); - - /* Get the entrypoint, search the .pyc then .py - */ char *dot = strrchr(env_entrypoint, '.'); char *ext = ".pyc"; if (dot <= 0) { @@ -346,6 +466,14 @@ int main(int argc, char *argv[]) { return -1; } + clock_gettime(CLOCK_MONOTONIC, &end); + double elapsed = + (end.tv_sec - start.tv_sec) + + (end.tv_nsec - start.tv_nsec) / 1e9; + + + LOGP("======= Initialized in %.3fs =======", elapsed); + /* run python ! */ ret = PyRun_SimpleFile(fd, entrypoint); @@ -361,34 +489,16 @@ int main(int argc, char *argv[]) { LOGP("Python for android ended."); - /* Shut down: since regular shutdown causes issues sometimes - (seems to be an incomplete shutdown breaking next launch) - we'll use sys.exit(ret) to shutdown, since that one works. - - Reference discussion: - - https://github.com/kivy/kivy/pull/6107#issue-246120816 - */ - char terminatecmd[256]; - snprintf( - terminatecmd, sizeof(terminatecmd), - "import sys; sys.exit(%d)\n", ret - ); - PyRun_SimpleString(terminatecmd); - - /* This should never actually be reached, but we'll leave the clean-up - * here just to be safe. - */ #if PY_MAJOR_VERSION < 3 Py_Finalize(); LOGP("Unexpectedly reached Py_FinalizeEx(), but was successful."); #else - if (Py_FinalizeEx() != 0) // properly check success on Python 3 + if (Py_FinalizeEx() != 0) { // properly check success on Python 3 LOGP("Unexpectedly reached Py_FinalizeEx(), and got error!"); - else - LOGP("Unexpectedly reached Py_FinalizeEx(), but was successful."); + } #endif + exit(ret); return ret; } diff --git a/pythonforandroid/recipes/ffmpeg/__init__.py b/pythonforandroid/recipes/ffmpeg/__init__.py index f7134b3384..38c4ff4660 100644 --- a/pythonforandroid/recipes/ffmpeg/__init__.py +++ b/pythonforandroid/recipes/ffmpeg/__init__.py @@ -1,15 +1,27 @@ from pythonforandroid.toolchain import Recipe, current_directory, shprint from os.path import exists, join, realpath import sh +from multiprocessing import cpu_count class FFMpegRecipe(Recipe): - version = 'n6.1.2' + version = '8.0.1' # Moved to github.com instead of ffmpeg.org to improve download speed - url = 'https://github.com/FFmpeg/FFmpeg/archive/{version}.zip' - depends = ['sdl2'] # Need this to build correct recipe order + url = 'https://www.ffmpeg.org/releases/ffmpeg-{version}.tar.xz' + depends = [('sdl2', 'sdl3')] # Need this to build correct recipe order opts_depends = ['openssl', 'ffpyplayer_codecs', 'av_codecs'] - patches = ['patches/configure.patch'] + patches = ['patches/configure.patch', 'patches/backport-Android15-MediaCodec-fix.patch'] + _libs = [ + "libavcodec.so", + "libavfilter.so", + "libavutil.so", + "libswscale.so", + "libavdevice.so", + "libavformat.so", + "libswresample.so", + "libffmpegbin.so", + ] + built_libraries = dict.fromkeys(_libs, "./lib") def should_build(self, arch): build_dir = self.get_build_dir(arch.arch) @@ -36,15 +48,15 @@ def build_arch(self, arch): if 'openssl' in self.ctx.recipe_build_order: flags += [ + '--enable-version3', '--enable-openssl', '--enable-nonfree', '--enable-protocol=https,tls_openssl', ] build_dir = Recipe.get_recipe( 'openssl', self.ctx).get_build_dir(arch.arch) - cflags += ['-I' + build_dir + '/include/', - '-DOPENSSL_API_COMPAT=0x10002000L'] - ldflags += ['-L' + build_dir] + cflags += ['-I' + build_dir + '/include/'] + ldflags += ['-L' + build_dir, '-lssl', '-lcrypto'] codecs_opts = {"ffpyplayer_codecs", "av_codecs"} if codecs_opts.intersection(self.ctx.recipe_build_order): @@ -98,9 +110,8 @@ def build_arch(self, arch): '--disable-symver', ] - # disable binaries / doc + # disable doc flags += [ - '--disable-programs', '--disable-doc', ] @@ -131,6 +142,7 @@ def build_arch(self, arch): '--cross-prefix={}-'.format(arch.target), '--arch={}'.format(arch_flag), '--strip={}'.format(self.ctx.ndk.llvm_strip), + '--nm={}'.format(self.ctx.ndk.llvm_nm), '--sysroot={}'.format(self.ctx.ndk.sysroot), '--enable-neon', '--prefix={}'.format(realpath('.')), @@ -138,6 +150,7 @@ def build_arch(self, arch): if arch_flag == 'arm': cflags += [ + '-Wno-error=incompatible-pointer-types', '-mfpu=vfpv3-d16', '-mfloat-abi=softfp', '-fPIC', @@ -148,11 +161,9 @@ def build_arch(self, arch): configure = sh.Command('./configure') shprint(configure, *flags, _env=env) - shprint(sh.make, '-j4', _env=env) + shprint(sh.make, '-j', f"{cpu_count()}", _env=env) shprint(sh.make, 'install', _env=env) - # copy libs: - sh.cp('-a', sh.glob('./lib/lib*.so'), - self.ctx.get_libs_dir(arch.arch)) + shprint(sh.cp, "ffmpeg", "./lib/libffmpegbin.so") recipe = FFMpegRecipe() diff --git a/pythonforandroid/recipes/ffmpeg/patches/backport-Android15-MediaCodec-fix.patch b/pythonforandroid/recipes/ffmpeg/patches/backport-Android15-MediaCodec-fix.patch new file mode 100644 index 0000000000..87a556dfc2 --- /dev/null +++ b/pythonforandroid/recipes/ffmpeg/patches/backport-Android15-MediaCodec-fix.patch @@ -0,0 +1,236 @@ +From 4531cc1b325b50a77fa22981f44a25fbce025a5e Mon Sep 17 00:00:00 2001 +From: Dmitrii Okunev +Date: Sat, 22 Nov 2025 19:57:19 +0000 +Subject: [PATCH] fftools: Fix MediaCodec on Android15+ + +On Android15+ MediaCodec HAL backend was switched from HIDL to AIDL. +As a result, MediaCodec operations started to hang, see: + + https://trac.ffmpeg.org/ticket/11363 + https://github.com/termux/termux-packages/issues/21264 + https://issuetracker.google.com/issues/382831999 + +To fix that it is necessary to initialize binder thread pool. + +Signed-off-by: Dmitrii Okunev +--- + compat/android/binder.c | 114 ++++++++++++++++++++++++++++++++++++++++ + compat/android/binder.h | 31 +++++++++++ + configure | 3 +- + fftools/Makefile | 1 + + fftools/ffmpeg.c | 7 +++ + 5 files changed, 155 insertions(+), 1 deletion(-) + create mode 100644 compat/android/binder.c + create mode 100644 compat/android/binder.h + +diff --git a/compat/android/binder.c b/compat/android/binder.c +new file mode 100644 +index 0000000000..a214d977cc +--- /dev/null ++++ b/compat/android/binder.c +@@ -0,0 +1,114 @@ ++/* ++ * Android Binder handler ++ * ++ * Copyright (c) 2025 Dmitrii Okunev ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++ ++#if defined(__ANDROID__) ++ ++#include ++#include ++#include ++ ++#include "libavutil/log.h" ++#include "binder.h" ++ ++#define THREAD_POOL_SIZE 1 ++ ++static void *dlopen_libbinder_ndk(void) ++{ ++ /* ++ * libbinder_ndk.so often does not contain the functions we need, so making ++ * this dependency optional, thus using dlopen/dlsym instead of linking. ++ * ++ * See also: https://source.android.com/docs/core/architecture/aidl/aidl-backends ++ */ ++ ++ void *h = dlopen("libbinder_ndk.so", RTLD_NOW | RTLD_LOCAL); ++ if (h != NULL) ++ return h; ++ ++ av_log(NULL, AV_LOG_WARNING, ++ "android/binder: unable to load libbinder_ndk.so: '%s'; skipping binder threadpool init (MediaCodec likely won't work)\n", ++ dlerror()); ++ return NULL; ++} ++ ++static void android_binder_threadpool_init(void) ++{ ++ typedef int (*set_thread_pool_max_fn)(uint32_t); ++ typedef void (*start_thread_pool_fn)(void); ++ ++ set_thread_pool_max_fn set_thread_pool_max = NULL; ++ start_thread_pool_fn start_thread_pool = NULL; ++ ++ void *h = dlopen_libbinder_ndk(); ++ if (h == NULL) ++ return; ++ ++ unsigned thead_pool_size = THREAD_POOL_SIZE; ++ ++ set_thread_pool_max = ++ (set_thread_pool_max_fn) dlsym(h, ++ "ABinderProcess_setThreadPoolMaxThreadCount"); ++ start_thread_pool = ++ (start_thread_pool_fn) dlsym(h, "ABinderProcess_startThreadPool"); ++ ++ if (start_thread_pool == NULL) { ++ av_log(NULL, AV_LOG_WARNING, ++ "android/binder: ABinderProcess_startThreadPool not found; skipping threadpool init (MediaCodec likely won't work)\n"); ++ return; ++ } ++ ++ if (set_thread_pool_max != NULL) { ++ int ok = set_thread_pool_max(thead_pool_size); ++ av_log(NULL, AV_LOG_DEBUG, ++ "android/binder: ABinderProcess_setThreadPoolMaxThreadCount(%u) => %s\n", ++ thead_pool_size, ok ? "ok" : "fail"); ++ } else { ++ av_log(NULL, AV_LOG_DEBUG, ++ "android/binder: ABinderProcess_setThreadPoolMaxThreadCount is unavailable; using the library default\n"); ++ } ++ ++ start_thread_pool(); ++ av_log(NULL, AV_LOG_DEBUG, ++ "android/binder: ABinderProcess_startThreadPool() called\n"); ++} ++ ++void android_binder_threadpool_init_if_required(void) ++{ ++#if __ANDROID_API__ >= 24 ++ if (android_get_device_api_level() < 35) { ++ // the issue with the thread pool was introduced in Android 15 (API 35) ++ av_log(NULL, AV_LOG_DEBUG, ++ "android/binder: API<35, thus no need to initialize a thread pool\n"); ++ return; ++ } ++ android_binder_threadpool_init(); ++#else ++ // android_get_device_api_level was introduced in API 24, so we cannot use it ++ // to detect the API level in API<24. For simplicity we just assume ++ // libbinder_ndk.so on the system running this code would have API level < 35; ++ av_log(NULL, AV_LOG_DEBUG, ++ "android/binder: is built with API<24, assuming this is not Android 15+\n"); ++#endif ++} ++ ++#endif /* __ANDROID__ */ +diff --git a/compat/android/binder.h b/compat/android/binder.h +new file mode 100644 +index 0000000000..2b1ca53fe8 +--- /dev/null ++++ b/compat/android/binder.h +@@ -0,0 +1,31 @@ ++/* ++ * Android Binder handler ++ * ++ * Copyright (c) 2025 Dmitrii Okunev ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef COMPAT_ANDROID_BINDER_H ++#define COMPAT_ANDROID_BINDER_H ++ ++/** ++ * Initialize Android Binder thread pool. ++ */ ++void android_binder_threadpool_init_if_required(void); ++ ++#endif // COMPAT_ANDROID_BINDER_H +diff --git a/configure b/configure +index af125bc115..098fe12f39 100755 +--- a/configure ++++ b/configure +@@ -7312,7 +7312,8 @@ enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt + check_pkg_config mbedtls mbedtls mbedtls/ssl.h mbedtls_ssl_init || + check_lib mbedtls mbedtls/ssl.h mbedtls_ssl_init -lmbedtls -lmbedx509 -lmbedcrypto || + die "ERROR: mbedTLS not found"; } +-enabled mediacodec && { enabled jni || die "ERROR: mediacodec requires --enable-jni"; } ++enabled mediacodec && { enabled jni || die "ERROR: mediacodec requires --enable-jni"; } && ++ add_compat android/binder.o + enabled mmal && { check_lib mmal interface/mmal/mmal.h mmal_port_connect -lmmal_core -lmmal_util -lmmal_vc_client -lbcm_host || + { ! enabled cross_compile && + add_cflags -isystem/opt/vc/include/ -isystem/opt/vc/include/interface/vmcs_host/linux -isystem/opt/vc/include/interface/vcos/pthreads -fgnu89-inline && +diff --git a/fftools/Makefile b/fftools/Makefile +index bdb44fc5ce..01b16fa8f4 100644 +--- a/fftools/Makefile ++++ b/fftools/Makefile +@@ -51,6 +51,7 @@ OBJS-ffprobe += \ + fftools/textformat/tw_buffer.o \ + fftools/textformat/tw_stdout.o \ + ++OBJS-ffmpeg += $(COMPAT_OBJS:%=compat/%) + OBJS-ffplay += fftools/ffplay_renderer.o + + define DOFFTOOL +diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c +index 444d027c15..c2c85d46bd 100644 +--- a/fftools/ffmpeg.c ++++ b/fftools/ffmpeg.c +@@ -78,6 +78,9 @@ + #include "libavdevice/avdevice.h" + + #include "cmdutils.h" ++#if CONFIG_MEDIACODEC ++#include "compat/android/binder.h" ++#endif + #include "ffmpeg.h" + #include "ffmpeg_sched.h" + #include "ffmpeg_utils.h" +@@ -1019,6 +1022,10 @@ int main(int argc, char **argv) + goto finish; + } + ++#if CONFIG_MEDIACODEC ++ android_binder_threadpool_init_if_required(); ++#endif ++ + current_time = ti = get_benchmark_time_stamps(); + ret = transcode(sch); + if (ret >= 0 && do_benchmark) { +-- +2.49.1 + diff --git a/pythonforandroid/recipes/ffmpeg/patches/configure.patch b/pythonforandroid/recipes/ffmpeg/patches/configure.patch index e274359cb7..504a79cad9 100644 --- a/pythonforandroid/recipes/ffmpeg/patches/configure.patch +++ b/pythonforandroid/recipes/ffmpeg/patches/configure.patch @@ -1,8 +1,6 @@ -diff --git a/configure b/configure -index 5af693c954..d1d0a4f0a2 100755 ---- a/configure -+++ b/configure -@@ -6800,7 +6800,7 @@ enabled librsvg && require_pkg_config librsvg librsvg-2.0 librsvg-2.0/ +--- ffmpeg-8.0/configure 2025-08-22 14:54:23.000000000 +0530 ++++ ffmpeg-8.0.mod/configure 2026-01-03 13:40:42.264702438 +0530 +@@ -7141,7 +7141,7 @@ enabled librtmp && require_pkg_config librtmp librtmp librtmp/rtmp.h RTMP_Socket enabled librubberband && require_pkg_config librubberband "rubberband >= 1.8.1" rubberband/rubberband-c.h rubberband_new -lstdc++ && append librubberband_extralibs "-lstdc++" enabled libshaderc && require_pkg_config spirv_compiler "shaderc >= 2019.1" shaderc/shaderc.h shaderc_compiler_initialize @@ -11,12 +9,42 @@ index 5af693c954..d1d0a4f0a2 100755 enabled libsmbclient && { check_pkg_config libsmbclient smbclient libsmbclient.h smbc_init || require libsmbclient libsmbclient.h smbc_init -lsmbclient; } enabled libsnappy && require libsnappy snappy-c.h snappy_compress -lsnappy -lstdc++ -@@ -6850,7 +6850,7 @@ enabled libvpx && { +@@ -7195,7 +7195,7 @@ enabled libwebp && { enabled libwebp_encoder && require_pkg_config libwebp "libwebp >= 0.2.0" webp/encode.h WebPGetEncoderVersion enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit; } -enabled libx264 && require_pkg_config libx264 x264 "stdint.h x264.h" x264_encoder_encode && +enabled libx264 && require "x264" "stdint.h x264.h" x264_encoder_encode && - require_cpp_condition libx264 x264.h "X264_BUILD >= 122" && { + require_cpp_condition libx264 x264.h "X264_BUILD >= 155" && { [ "$toolchain" != "msvc" ] || require_cpp_condition libx264 x264.h "X264_BUILD >= 158"; } && +@@ -7258,13 +7258,7 @@ + enabled omx && require_headers OMX_Core.h && \ + warn "The OpenMAX encoders are deprecated and will be removed in future versions" + +-enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0" openssl/ssl.h OPENSSL_init_ssl && +- { enabled gplv3 || ! enabled gpl || enabled nonfree || die "ERROR: OpenSSL >=3.0.0 requires --enable-version3"; }; } || +- { enabled gpl && ! enabled nonfree && die "ERROR: OpenSSL <3.0.0 is incompatible with the gpl"; } || +- check_pkg_config openssl "openssl >= 1.1.0" openssl/ssl.h OPENSSL_init_ssl || +- check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto || +- check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto -lws2_32 -lgdi32 || +- die "ERROR: openssl (>= 1.1.0) not found"; } ++enabled openssl && true + enabled pocketsphinx && require_pkg_config pocketsphinx pocketsphinx pocketsphinx/pocketsphinx.h ps_init + enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/rk_mpi.h mpp_create && + require_pkg_config rockchip_mpp "rockchip_mpp >= 1.3.7" rockchip/rk_mpi.h mpp_create && +@@ -7273,14 +7267,6 @@ + } + enabled vapoursynth && require_headers "vapoursynth/VSScript4.h vapoursynth/VapourSynth4.h" + +-enabled openssl && { +- enabled whip_muxer && { +- $pkg_config --exists --print-errors "openssl >= 1.0.1k" || +- require_pkg_config openssl "openssl >= 1.0.1k" openssl/ssl.h SSL_library_init || +- require_pkg_config openssl "openssl >= 1.0.1k" openssl/ssl.h OPENSSL_init_ssl +- } +-} +- + + if enabled gcrypt; then + GCRYPT_CONFIG="${cross_prefix}libgcrypt-config" diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index fb69b9a366..754d08cde3 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -183,6 +183,8 @@ class Python3Recipe(TargetPythonRecipe): disable_gil = False '''python3.13 experimental free-threading build''' + built_libraries = {"libpythonbin.so": "./android-build/"} + def __init__(self, *args, **kwargs): self._ctx = None super().__init__(*args, **kwargs) @@ -364,6 +366,8 @@ def build_arch(self, arch): 'INSTSONAME={lib_name}'.format(lib_name=self._libpython), _env=env ) + # rename executable + sh.cp('python', 'libpythonbin.so') # TODO: Look into passing the path to pyconfig.h in a # better way, although this is probably acceptable