Skip to content

Commit 2b73748

Browse files
committed
feat(yogurt): wire android native codec via jitpack
1 parent 90ec28d commit 2b73748

17 files changed

Lines changed: 367 additions & 79 deletions

File tree

.github/workflows/yogurt-dev-release.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ jobs:
4242
gradle-target: LinuxArm64
4343
platform: linux-arm64
4444

45+
- runs-on: ubuntu-latest
46+
target: androidNativeArm64
47+
gradle-target: AndroidNativeArm64
48+
platform: android-arm64
49+
4550
env:
4651
GITHUB_ACTIONS_BUILD_TARGET: ${{ matrix.target }}
4752

@@ -69,6 +74,19 @@ jobs:
6974
distribution: temurin
7075
java-version: 25
7176

77+
- name: Setup Android SDK
78+
if: matrix.target == 'androidNativeArm64'
79+
uses: android-actions/setup-android@v3
80+
81+
- name: Setup Android NDK
82+
if: matrix.target == 'androidNativeArm64'
83+
shell: bash
84+
run: |
85+
set -euo pipefail
86+
sdkmanager "ndk;27.2.12479018"
87+
echo "ANDROID_NDK_HOME=${ANDROID_SDK_ROOT}/ndk/27.2.12479018" >> "$GITHUB_ENV"
88+
echo "ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk/27.2.12479018" >> "$GITHUB_ENV"
89+
7290
- name: Build Yogurt for ${{ matrix.target }}
7391
run: ./gradlew :yogurt:linkReleaseExecutable${{ matrix.gradle-target }} --no-daemon
7492

buildSrc/src/main/kotlin/kotlin-multiplatform.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ kotlin {
1111
when (actionsTarget) {
1212
// is run from GitHub Actions - build Yogurt, improving dependency pulling time
1313
"jvm" -> {} // already added
14+
"androidNativeArm64" -> androidNativeArm64()
1415
"mingwX64" -> mingwX64()
1516
"macosArm64" -> macosArm64()
1617
"linuxX64" -> linuxX64()

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ktor = "3.4.1"
1313

1414
milky = "1.2.0-RC4"
1515
acidify-codec = "0.1.0"
16+
acidify-codec-android-jitpack = "1e7b7ee"
1617
kompress = "1.3.1"
1718
qr-matrix = "0.1.0"
1819
xmlutil = "1.0.0-rc2"
@@ -57,6 +58,7 @@ ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx
5758
milky-types = { module = "org.ntqqrev:milky-kt-types", version.ref = "milky" }
5859

5960
acidify-codec = { module = "org.ntqqrev:acidify-codec", version.ref = "acidify-codec" }
61+
acidify-codec-androidnativearm64 = { module = "com.github.sealdice.acidify-codec:acidify-codec-androidnativearm64", version.ref = "acidify-codec-android-jitpack" }
6062
kompress = { module = "dev.karmakrafts.kompress:kompress-core", version.ref = "kompress" }
6163
qr-matrix = { module = "org.ntqqrev:qr-matrix", version.ref = "qr-matrix" }
6264
xmlutil-core = { module = "io.github.pdvrieze.xmlutil:core", version.ref = "xmlutil" }

settings.gradle.kts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ dependencyResolutionManagement {
77
// Use Maven Central as the default repository (where Gradle will download dependencies) in all subprojects.
88
@Suppress("UnstableApiUsage")
99
repositories {
10+
exclusiveContent {
11+
forRepository {
12+
maven("https://jitpack.io")
13+
}
14+
filter {
15+
includeGroup("com.github.sealdice.acidify-codec")
16+
}
17+
}
1018
mavenCentral()
1119
}
1220
}
@@ -23,4 +31,4 @@ include(
2331
":yogurt-jvm",
2432
)
2533

26-
rootProject.name = "acidify"
34+
rootProject.name = "acidify"

yogurt/build.gradle.kts

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,87 @@ version = "0.1.0"
1515

1616
kotlin {
1717
sourceSets {
18+
val commonMain = getByName("commonMain")
1819
commonMain.dependencies {
1920
implementation(project(":acidify-core"))
2021
implementation(libs.kotlinx.datetime)
2122
implementation(libs.bundles.ktor.client)
2223
implementation(libs.bundles.ktor.server)
2324
implementation(libs.ktor.serialization.kotlinx.json)
2425
implementation(libs.milky.types)
25-
implementation(libs.acidify.codec)
2626
implementation(libs.qr.matrix)
27-
implementation(libs.mordant)
2827
}
29-
jvmMain.dependencies {
30-
implementation(libs.ktor.client.cio)
31-
implementation(libs.logback.classic)
28+
29+
val codecMain by creating {
30+
dependsOn(commonMain)
31+
32+
dependencies {
33+
implementation(libs.acidify.codec)
34+
}
35+
}
36+
37+
val nativeMain = if (
38+
findByName("androidNativeArm64Main") != null ||
39+
findByName("linuxX64Main") != null ||
40+
findByName("linuxArm64Main") != null ||
41+
findByName("macosArm64Main") != null ||
42+
findByName("mingwX64Main") != null
43+
) {
44+
maybeCreate("nativeMain").apply {
45+
dependsOn(commonMain)
46+
}
47+
} else {
48+
null
49+
}
50+
51+
findByName("jvmMain")?.apply {
52+
dependsOn(codecMain)
53+
54+
dependencies {
55+
implementation(libs.ktor.client.cio)
56+
implementation(libs.logback.classic)
57+
}
3258
}
33-
mingwMain.dependencies {
34-
implementation(libs.ktor.client.winhttp)
59+
60+
if (findByName("mingwX64Main") != null) {
61+
val mingwMain = maybeCreate("mingwMain")
62+
nativeMain?.let { mingwMain.dependsOn(it) }
63+
mingwMain.dependsOn(codecMain)
64+
mingwMain.dependencies {
65+
implementation(libs.ktor.client.winhttp)
66+
}
67+
findByName("mingwX64Main")?.dependsOn(mingwMain)
68+
}
69+
70+
if (findByName("macosArm64Main") != null) {
71+
val appleMain = maybeCreate("appleMain")
72+
nativeMain?.let { appleMain.dependsOn(it) }
73+
appleMain.dependsOn(codecMain)
74+
appleMain.dependencies {
75+
implementation(libs.ktor.client.darwin)
76+
}
77+
findByName("macosArm64Main")?.dependsOn(appleMain)
3578
}
36-
appleMain.dependencies {
37-
implementation(libs.ktor.client.darwin)
79+
80+
if (findByName("linuxX64Main") != null || findByName("linuxArm64Main") != null) {
81+
val linuxMain = maybeCreate("linuxMain")
82+
nativeMain?.let { linuxMain.dependsOn(it) }
83+
linuxMain.dependsOn(codecMain)
84+
linuxMain.dependencies {
85+
implementation(libs.ktor.client.curl)
86+
}
87+
findByName("linuxX64Main")?.dependsOn(linuxMain)
88+
findByName("linuxArm64Main")?.dependsOn(linuxMain)
3889
}
39-
linuxMain.dependencies {
40-
implementation(libs.ktor.client.curl)
90+
91+
if (findByName("androidNativeArm64Main") != null) {
92+
val androidNativeMain = maybeCreate("androidNativeMain")
93+
nativeMain?.let { androidNativeMain.dependsOn(it) } ?: androidNativeMain.dependsOn(commonMain)
94+
androidNativeMain.dependencies {
95+
implementation(libs.ktor.client.cio)
96+
implementation(libs.acidify.codec.androidnativearm64)
97+
}
98+
findByName("androidNativeArm64Main")?.dependsOn(androidNativeMain)
4199
}
42100
}
43101

@@ -49,14 +107,16 @@ kotlin {
49107
}
50108
}
51109

52-
mingwX64 {
53-
binaries.all {
54-
linkerOpts(
55-
"-Wl,-Bstatic",
56-
"-lstdc++",
57-
"-lgcc",
58-
"-Wl,-Bdynamic",
59-
)
110+
targets.withType<KotlinNativeTarget>().configureEach {
111+
if (name == "mingwX64") {
112+
binaries.all {
113+
linkerOpts(
114+
"-Wl,-Bstatic",
115+
"-lstdc++",
116+
"-lgcc",
117+
"-Wl,-Bdynamic",
118+
)
119+
}
60120
}
61121
}
62122
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.ntqqrev.yogurt
2+
3+
import io.ktor.server.engine.*
4+
5+
actual fun EmbeddedServer<*, *>.onSigint(hook: () -> Unit) = Unit
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.ntqqrev.yogurt.util
2+
3+
import kotlinx.coroutines.sync.Mutex
4+
import kotlinx.coroutines.sync.withLock
5+
6+
private fun org.ntqqrev.acidify.codec.ImageFormat.toCodecImageFormat() = when (this) {
7+
org.ntqqrev.acidify.codec.ImageFormat.PNG -> CodecImageFormat.PNG
8+
org.ntqqrev.acidify.codec.ImageFormat.GIF -> CodecImageFormat.GIF
9+
org.ntqqrev.acidify.codec.ImageFormat.JPEG -> CodecImageFormat.JPEG
10+
org.ntqqrev.acidify.codec.ImageFormat.BMP -> CodecImageFormat.BMP
11+
org.ntqqrev.acidify.codec.ImageFormat.WEBP -> CodecImageFormat.WEBP
12+
org.ntqqrev.acidify.codec.ImageFormat.TIFF -> CodecImageFormat.TIFF
13+
}
14+
15+
actual object FFMpegCodec {
16+
private val ffmpegMutex = Mutex()
17+
18+
actual suspend fun audioToPcm(input: ByteArray): ByteArray = ffmpegMutex.withLock {
19+
org.ntqqrev.acidify.codec.audioToPcm(input)
20+
}
21+
22+
actual suspend fun silkDecode(input: ByteArray): ByteArray = ffmpegMutex.withLock {
23+
org.ntqqrev.acidify.codec.silkDecode(input)
24+
}
25+
26+
actual suspend fun silkEncode(input: ByteArray): ByteArray = ffmpegMutex.withLock {
27+
org.ntqqrev.acidify.codec.silkEncode(input)
28+
}
29+
30+
actual suspend fun getVideoInfo(videoData: ByteArray): CodecVideoInfo = ffmpegMutex.withLock {
31+
org.ntqqrev.acidify.codec.getVideoInfo(videoData).let {
32+
CodecVideoInfo(
33+
width = it.width,
34+
height = it.height,
35+
duration = it.duration,
36+
)
37+
}
38+
}
39+
40+
actual suspend fun getVideoFirstFrameJpg(videoData: ByteArray): ByteArray = ffmpegMutex.withLock {
41+
org.ntqqrev.acidify.codec.getVideoFirstFrameJpg(videoData)
42+
}
43+
}
44+
45+
actual fun getCodecImageInfo(input: ByteArray): CodecImageInfo {
46+
val imageInfo = org.ntqqrev.acidify.codec.getImageInfo(input)
47+
return CodecImageInfo(
48+
format = imageInfo.format.toCodecImageFormat(),
49+
width = imageInfo.width,
50+
height = imageInfo.height,
51+
)
52+
}
53+
54+
actual fun calculatePcmDurationCompat(input: ByteArray) = org.ntqqrev.acidify.codec.calculatePcmDuration(input)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.ntqqrev.yogurt.util
2+
3+
import kotlinx.io.buffered
4+
import kotlinx.io.files.Path
5+
import kotlinx.io.files.SystemFileSystem
6+
import kotlinx.io.readByteArray
7+
8+
actual fun readByteArrayFromFilePath(path: String): ByteArray =
9+
SystemFileSystem.source(Path(path)).buffered().use {
10+
it.readByteArray()
11+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.ntqqrev.yogurt.util
2+
3+
import kotlinx.coroutines.sync.Mutex
4+
import kotlinx.coroutines.sync.withLock
5+
6+
private fun org.ntqqrev.acidify.codec.ImageFormat.toCodecImageFormat() = when (this) {
7+
org.ntqqrev.acidify.codec.ImageFormat.PNG -> CodecImageFormat.PNG
8+
org.ntqqrev.acidify.codec.ImageFormat.GIF -> CodecImageFormat.GIF
9+
org.ntqqrev.acidify.codec.ImageFormat.JPEG -> CodecImageFormat.JPEG
10+
org.ntqqrev.acidify.codec.ImageFormat.BMP -> CodecImageFormat.BMP
11+
org.ntqqrev.acidify.codec.ImageFormat.WEBP -> CodecImageFormat.WEBP
12+
org.ntqqrev.acidify.codec.ImageFormat.TIFF -> CodecImageFormat.TIFF
13+
}
14+
15+
actual object FFMpegCodec {
16+
private val ffmpegMutex = Mutex()
17+
18+
actual suspend fun audioToPcm(input: ByteArray): ByteArray = ffmpegMutex.withLock {
19+
org.ntqqrev.acidify.codec.audioToPcm(input)
20+
}
21+
22+
actual suspend fun silkDecode(input: ByteArray): ByteArray = ffmpegMutex.withLock {
23+
org.ntqqrev.acidify.codec.silkDecode(input)
24+
}
25+
26+
actual suspend fun silkEncode(input: ByteArray): ByteArray = ffmpegMutex.withLock {
27+
org.ntqqrev.acidify.codec.silkEncode(input)
28+
}
29+
30+
actual suspend fun getVideoInfo(videoData: ByteArray): CodecVideoInfo = ffmpegMutex.withLock {
31+
org.ntqqrev.acidify.codec.getVideoInfo(videoData).let {
32+
CodecVideoInfo(
33+
width = it.width,
34+
height = it.height,
35+
duration = it.duration,
36+
)
37+
}
38+
}
39+
40+
actual suspend fun getVideoFirstFrameJpg(videoData: ByteArray): ByteArray = ffmpegMutex.withLock {
41+
org.ntqqrev.acidify.codec.getVideoFirstFrameJpg(videoData)
42+
}
43+
}
44+
45+
actual fun getCodecImageInfo(input: ByteArray): CodecImageInfo {
46+
val imageInfo = org.ntqqrev.acidify.codec.getImageInfo(input)
47+
return CodecImageInfo(
48+
format = imageInfo.format.toCodecImageFormat(),
49+
width = imageInfo.width,
50+
height = imageInfo.height,
51+
)
52+
}
53+
54+
actual fun calculatePcmDurationCompat(input: ByteArray) = org.ntqqrev.acidify.codec.calculatePcmDuration(input)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.github.ajalt.mordant.rendering
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
enum class AnsiLevel {
7+
NONE,
8+
ANSI16,
9+
ANSI256,
10+
TRUECOLOR,
11+
}

0 commit comments

Comments
 (0)