Initial commit

This commit is contained in:
admin1
2026-07-14 11:11:36 +08:00
commit 656499cf94
604 changed files with 119518 additions and 0 deletions
@@ -0,0 +1,154 @@
// Media3 Engine plugin — Android-only ExoPlayer + FFmpeg audio decoder bridge.
//
// 依赖:
// - Maven Central: media3-exoplayer / media3-datasource / media3-decoder 1.10.1
// - 本地 AAR : media3-decoder-ffmpeg-1.10.1-arm64.aar
// 由 fileTree 从主 app 的 android/app/libs/ 加载。AAR 由
// scripts/build_media3_ffmpeg.sh 自编译产出,CHECKSUMS.txt
// 记录 SHA-256。
//
// AAR 缺失时 preBuild 任务会 fail-fast 抛出带恢复指引的错误(详见 README)。
import java.security.MessageDigest
plugins {
id("com.android.library")
id("kotlin-android")
}
group = "com.smplayer.media3_engine"
version = "0.0.1"
android {
namespace = "com.smplayer.media3_engine"
compileSdk = 36
defaultConfig {
minSdk = 24
ndk {
// 与主 app 保持一致:仅 arm64-v8a。其它 ABI 在 FFmpeg AAR 中也不存在,
// 即便 ABI filter 漏了也只是无声的浪费(不会 crash)。
abiFilters += listOf("arm64-v8a")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
buildFeatures {
buildConfig = false
}
}
repositories {
google()
mavenCentral()
}
// 自编译 FFmpeg audio decoder extensionarm64-v8a onlyLGPL 配置)。AAR 由
// scripts/build_media3_ffmpeg.sh 产出,放在 android/app/libs/。
// 这里只声明 compileOnlyAGP 8 起禁止 library module 直接 implementation 本地
// .aar"Direct local .aar file dependencies are not supported when building an
// AAR"),实际把 AAR 打入最终 APK 的责任落到主 app/build.gradle.kts 的
// `implementation(fileTree("libs"))`。本 plugin 编译期只需要 FfmpegAudioRenderer
// 等符号的可见性。
val media3Version = "1.10.1"
val ffmpegAarDir = file("../../../android/app/libs")
val ffmpegAarName = "media3-decoder-ffmpeg-$media3Version-arm64.aar"
dependencies {
implementation("androidx.media3:media3-exoplayer:$media3Version")
implementation("androidx.media3:media3-datasource:$media3Version")
implementation("androidx.media3:media3-decoder:$media3Version")
implementation("androidx.media3:media3-ui:$media3Version")
compileOnly(fileTree(mapOf("dir" to ffmpegAarDir, "include" to listOf(ffmpegAarName))))
}
// AAR 缺失 / 篡改 fail-fast:在 preBuild 之前校验文件存在性 + SHA-256 与
// `android/app/libs/CHECKSUMS.txt` 一致。CI/Clean checkout 友好。
val verifyMedia3FfmpegAar by tasks.registering {
group = "verification"
description = "Verify media3 FFmpeg decoder AAR exists and matches CHECKSUMS.txt."
val checksumsFile = ffmpegAarDir.resolve("CHECKSUMS.txt")
val aarFile = ffmpegAarDir.resolve(ffmpegAarName)
inputs.file(aarFile).withPropertyName("aar").withPathSensitivity(org.gradle.api.tasks.PathSensitivity.NONE)
inputs.file(checksumsFile).withPropertyName("checksums").withPathSensitivity(org.gradle.api.tasks.PathSensitivity.NONE)
doLast {
if (!aarFile.isFile) {
throw GradleException(
"""
|Media3 FFmpeg decoder AAR is missing:
| expected: ${aarFile.absolutePath}
|
|This AAR is self-built (LGPL config, arm64-v8a,
|codecs: dca/ac3/eac3/truehd/opus/vorbis/flac).
|To produce it, run from the repo root:
|
| scripts/build_media3_ffmpeg.sh
|
|Prerequisites: NDK r26b, JDK 17, ~30-60min wall-clock. See
|plugins/media3_engine/README.md for full instructions and Docker recipe.
|
|If you only build the desktop (Windows/macOS) target, this Android
|module is not on the dependency path and you can ignore this error.
""".trimMargin()
)
}
if (!checksumsFile.isFile) {
throw GradleException(
"Media3 AAR checksum manifest is missing: ${checksumsFile.absolutePath}\n" +
"Restore CHECKSUMS.txt from git or rerun scripts/build_media3_ffmpeg.sh."
)
}
// CHECKSUMS.txt 行格式:`<sha256> <filename>`,可有 # 开头注释 / 空行
val expectedSha = checksumsFile.readLines()
.map { it.trim() }
.firstOrNull { it.isNotEmpty() && !it.startsWith("#") && it.endsWith(ffmpegAarName) }
?.substringBefore(' ')
?.trim()
?.lowercase()
if (expectedSha.isNullOrBlank() || !expectedSha.matches(Regex("^[0-9a-f]{64}$"))) {
throw GradleException(
"Media3 AAR checksum entry missing or malformed for $ffmpegAarName in " +
"${checksumsFile.absolutePath}. Expected a line: '<sha256> $ffmpegAarName'."
)
}
val md = MessageDigest.getInstance("SHA-256")
aarFile.inputStream().use { input ->
val buffer = ByteArray(8192)
while (true) {
val n = input.read(buffer)
if (n <= 0) break
md.update(buffer, 0, n)
}
}
val actualSha = md.digest().joinToString("") { "%02x".format(it) }
if (actualSha != expectedSha) {
throw GradleException(
"""
|Media3 FFmpeg decoder AAR checksum mismatch — refusing to build.
| file : ${aarFile.absolutePath}
| expected : $expectedSha (from CHECKSUMS.txt)
| actual : $actualSha
|
|The AAR has been replaced/corrupted since CHECKSUMS.txt was written.
|If you intentionally rebuilt it, scripts/build_media3_ffmpeg.sh will
|refresh CHECKSUMS.txt automatically; otherwise restore the file from git.
""".trimMargin()
)
}
logger.lifecycle("[verifyMedia3FfmpegAar] OK — sha256 $actualSha")
}
}
afterEvaluate {
tasks.named("preBuild").configure {
dependsOn(verifyMedia3FfmpegAar)
}
}