- Gate buildFfmpegJni + jniLibs packaging on `ux: enable_ffmpeg` in the consuming app's pubspec (default off) -- no LGPL / H.264-patent exposure unless explicitly enabled - appInfoBuilder generates kUxEnableFfmpeg from the same flag so apps register the FFmpeg LGPL notice eagerly, pubspec-only (no dart-define) - Add registerFfmpegLicense() + bundled LGPL-2.1 text asset - FFmpeg compliance docs (LICENSES-3RDPARTY.md, android/ffmpeg/README.md) - Network video streaming: XVideoPlayerController.network
185 lines
6.9 KiB
Groovy
185 lines
6.9 KiB
Groovy
group 'io.swipelab.ux'
|
|
version '1.0-SNAPSHOT'
|
|
|
|
buildscript {
|
|
ext.kotlin_version = '1.9.22'
|
|
repositories {
|
|
google()
|
|
mavenCentral()
|
|
}
|
|
|
|
dependencies {
|
|
classpath 'com.android.tools.build:gradle:8.1.0'
|
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
}
|
|
}
|
|
|
|
rootProject.allprojects {
|
|
repositories {
|
|
google()
|
|
mavenCentral()
|
|
}
|
|
}
|
|
|
|
apply plugin: 'com.android.library'
|
|
apply plugin: 'kotlin-android'
|
|
|
|
// FFmpeg software H.264 decoder — OPT-IN, disabled by default.
|
|
//
|
|
// The decoder (a fallback for devices whose hardware decoder fails) carries
|
|
// LGPL + H.264 patent obligations, so it is only bundled when the consuming
|
|
// app asks for it, via a flag in its pubspec.yaml:
|
|
//
|
|
// ux:
|
|
// enable_ffmpeg: true
|
|
//
|
|
// (or -Pux.enable_ffmpeg=true on the Gradle command line as a CI override).
|
|
// When off: buildFfmpegJni is skipped, no libffmpegJNI.so is packaged,
|
|
// FfmpegLibrary.isAvailable() returns false, the renderer is never added, and
|
|
// playback uses the platform MediaCodec decoders only. See android/ffmpeg/README.md.
|
|
//
|
|
// This pubspec flag is the only switch: ux's build_runner builder also reads
|
|
// `ux: enable_ffmpeg` and generates a `kUxEnableFfmpeg` constant the app uses
|
|
// to register the LGPL license notice — no --dart-define needed.
|
|
def ffmpegEnabled = {
|
|
// CI / command-line override takes precedence over the pubspec flag.
|
|
def prop = project.findProperty('ux.enable_ffmpeg')
|
|
if (prop != null) return prop.toString().trim().equalsIgnoreCase('true')
|
|
|
|
// rootProject is the consuming app's android/ project; its pubspec.yaml
|
|
// sits one directory up. Match a top-level `ux:` block (not the indented
|
|
// `ux:` dependency entry) and its nested `enable_ffmpeg:` value.
|
|
def pubspec = new File(rootProject.projectDir.parentFile, 'pubspec.yaml')
|
|
if (!pubspec.exists()) return false
|
|
boolean inUxBlock = false
|
|
boolean enabled = false
|
|
pubspec.eachLine { raw ->
|
|
def hash = raw.indexOf('#')
|
|
def line = hash >= 0 ? raw.substring(0, hash) : raw
|
|
if (line.trim().isEmpty()) return
|
|
if (!Character.isWhitespace(line.charAt(0))) {
|
|
// A new top-level key: we're in the config block only if it's `ux:`.
|
|
inUxBlock = line.trim().startsWith('ux:')
|
|
} else if (inUxBlock) {
|
|
def t = line.trim()
|
|
if (t.startsWith('enable_ffmpeg:')) {
|
|
enabled = t.substring('enable_ffmpeg:'.length()).trim().equalsIgnoreCase('true')
|
|
}
|
|
}
|
|
}
|
|
return enabled
|
|
}()
|
|
|
|
android {
|
|
namespace 'io.swipelab.ux'
|
|
// media3 1.9.2 requires compileSdk >= 35; match the app/Flutter default (36).
|
|
compileSdk 36
|
|
// Match Flutter's pinned NDK (flutter_tools gradle_utils.dart) and the
|
|
// app; AGP 8.1's default NDK (25.x) isn't installed on dev machines.
|
|
ndkVersion '28.2.13676358'
|
|
|
|
defaultConfig {
|
|
minSdk 21
|
|
externalNativeBuild {
|
|
cmake {
|
|
cppFlags ""
|
|
}
|
|
}
|
|
}
|
|
|
|
externalNativeBuild {
|
|
cmake {
|
|
path "src/main/jni/CMakeLists.txt"
|
|
}
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility JavaVersion.VERSION_1_8
|
|
targetCompatibility JavaVersion.VERSION_1_8
|
|
}
|
|
|
|
kotlinOptions {
|
|
jvmTarget = '1.8'
|
|
}
|
|
|
|
sourceSets {
|
|
main {
|
|
// libffmpegJNI.so is built by the buildFfmpegJni task into
|
|
// build/jniLibs/<abi>/. Only reference it when FFmpeg is enabled,
|
|
// so a stale .so from a previous enabled build is not packaged
|
|
// into a now-disabled build. Lets AGP package the .so into the
|
|
// AAR without committing native binaries to the repo.
|
|
if (ffmpegEnabled) {
|
|
jniLibs.srcDirs += "$buildDir/jniLibs"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// FFmpeg video decoder build (opt-in — see the `ffmpegEnabled` flag near the
|
|
// top of this file and android/ffmpeg/README.md). When enabled, the first
|
|
// build for a checkout clones Media3 + FFmpeg into build/ffmpeg-work/ and
|
|
// produces libffmpegJNI.so per ABI (~30 min for the FFmpeg static-lib step the
|
|
// first time, fast after); Gradle UP-TO-DATE checking skips it when the
|
|
// vendored JNI source + CMakeLists are unchanged.
|
|
def ffmpegSrcDir = file("$projectDir/ffmpeg")
|
|
def ffmpegWorkDir = file("$buildDir/ffmpeg-work")
|
|
def ffmpegOutDir = file("$buildDir/jniLibs")
|
|
def ndkCmakeBin = "${android.sdkDirectory}/cmake/3.22.1/bin"
|
|
def supportedAbis = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']
|
|
|
|
task buildFfmpegJni(type: Exec) {
|
|
group = 'build'
|
|
description = 'Clones Media3 + FFmpeg if needed, builds libffmpegJNI.so per Android ABI'
|
|
|
|
inputs.file "$ffmpegSrcDir/ffmpeg_jni.cc"
|
|
inputs.file "$ffmpegSrcDir/CMakeLists.txt"
|
|
inputs.file "$ffmpegSrcDir/build_ffmpeg.sh"
|
|
supportedAbis.each { abi ->
|
|
outputs.file "$ffmpegOutDir/$abi/libffmpegJNI.so"
|
|
}
|
|
|
|
workingDir ffmpegSrcDir
|
|
commandLine 'bash', "$ffmpegSrcDir/build_ffmpeg.sh"
|
|
environment 'JNI_SRC', ffmpegSrcDir.absolutePath
|
|
environment 'NDK_PATH', android.ndkDirectory.absolutePath
|
|
environment 'CMAKE_PATH', ndkCmakeBin
|
|
environment 'OUTPUT_DIR', ffmpegOutDir.absolutePath
|
|
environment 'WORK_DIR', ffmpegWorkDir.absolutePath
|
|
}
|
|
|
|
afterEvaluate {
|
|
if (ffmpegEnabled) {
|
|
preBuild.dependsOn buildFfmpegJni
|
|
logger.lifecycle("ux: FFmpeg software H.264 decoder ENABLED (ux: enable_ffmpeg: true).")
|
|
} else {
|
|
logger.lifecycle("ux: FFmpeg software H.264 decoder disabled (default). " +
|
|
"Add 'ux:\\n enable_ffmpeg: true' to the app pubspec.yaml to bundle it.")
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
// CameraX for scanner preview + frame analysis + ux.camera.
|
|
// 1.4.0+ ships 16-KB-aligned `libimage_processing_util_jni.so`
|
|
// (Android 15 requirement); 1.3.x failed the elf-alignment check.
|
|
def cameraxVersion = '1.4.2'
|
|
implementation "androidx.camera:camera-core:$cameraxVersion"
|
|
implementation "androidx.camera:camera-camera2:$cameraxVersion"
|
|
implementation "androidx.camera:camera-lifecycle:$cameraxVersion"
|
|
implementation "androidx.camera:camera-view:$cameraxVersion"
|
|
implementation "androidx.camera:camera-video:$cameraxVersion"
|
|
// ProcessLifecycleOwner so CameraInstance can release the camera
|
|
// when the host app backgrounds (and re-acquire on foreground).
|
|
// camera-lifecycle pulls in lifecycle-common but not the process
|
|
// observer; this adds it.
|
|
implementation 'androidx.lifecycle:lifecycle-process:2.7.0'
|
|
// Pure-Kotlin/Java QR decoder. ~470 KB jar, no Play Services dep.
|
|
implementation 'com.google.zxing:core:3.5.3'
|
|
// Media3 for ux.video_player. Same version line video_player_android
|
|
// 2.9.5 pulls in (1.9.x) so the spike fork can coexist during the
|
|
// Phase 2/3 migration without dragging in two ExoPlayer copies.
|
|
def media3Version = '1.9.2'
|
|
implementation "androidx.media3:media3-exoplayer:$media3Version"
|
|
implementation "androidx.media3:media3-common:$media3Version"
|
|
}
|