diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 3ce3379..84000f3 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -1,13 +1,6 @@
name: "Build and Test"
on:
push:
- branches:
- - '*'
- tags-ignore:
- - '*'
- paths-ignore:
- - '*.md'
- - 'docs/**'
pull_request:
workflow_dispatch:
release:
@@ -16,33 +9,42 @@ permissions:
pull-requests: write
contents: write
jobs:
- linux-x86_64:
+ build-publish:
runs-on: ubuntu-24.04
steps:
- - shell: bash
- name: "Install mpv"
- run: |
- sudo apt-get update -y
- sudo apt-get install -y libmpv-dev libavformat-dev libavcodec-dev libavutil-dev libswscale-dev libavdevice-dev libavfilter-dev libswresample-dev libpostproc-dev libpipewire-0.3-dev
- - uses: silenium-dev/actions/jni-natives/ubuntu@main
+ - uses: actions/checkout@v6
+ - uses: cachix/install-nix-action@v31
with:
- gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- snapshot-repo-url: "https://reposilite.silenium.dev/snapshots"
- release-repo-url: "https://reposilite.silenium.dev/releases"
- repo-username: ${{ secrets.REPOSILITE_USERNAME }}
- repo-password: ${{ secrets.REPOSILITE_PASSWORD }}
- tests: true
- java-version: 11
- platform: ${{ github.job }}
- kotlin:
- runs-on: ubuntu-24.04
- steps:
+ nix_path: nixpkgs=channel:nixos-unstable
+ github_access_token: ${{ secrets.GITHUB_TOKEN }}
+ - name: Restore and save Nix store
+ uses: nix-community/cache-nix-action@v7
+ with:
+ # restore and save a cache using this key
+ primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
+ # if there's no cache hit, restore a cache by this prefix
+ restore-prefixes-first-match: nix-${{ runner.os }}-
+ # collect garbage until the Nix store size (in bytes) is at most this number
+ # before trying to save a new cache
+ # 1G = 1073741824
+ gc-max-store-size-linux: 4G
+ # do purge caches
+ purge: true
+ # purge all versions of the cache
+ purge-prefixes: nix-${{ runner.os }}-
+ # created more than this number of seconds ago
+ purge-created: 0
+ # or last accessed this duration (ISO 8601 duration format)
+ # before the start of the `Post Restore and save Nix store` phase
+ purge-last-accessed: P7D
+ # except any version with the key that is the same as the `primary-key`
+ purge-primary-key: never
- uses: silenium-dev/actions/kotlin@main
with:
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- snapshot-repo-url: "https://reposilite.silenium.dev/snapshots"
- release-repo-url: "https://reposilite.silenium.dev/releases"
- repo-username: ${{ secrets.REPOSILITE_USERNAME }}
- repo-password: ${{ secrets.REPOSILITE_PASSWORD }}
+ snapshot-repo-url: "https://repoflow.silenium.dev/api/maven/public/maven-snapshots"
+ release-repo-url: "https://repoflow.silenium.dev/api/maven/public/maven-releases"
+ repo-username: ${{ secrets.REPOFLOW_USERNAME }}
+ repo-password: ${{ secrets.REPOFLOW_PASSWORD }}
tests: false
- java-version: 11
+ java-version: 17
diff --git a/.gitignore b/.gitignore
index f87be73..4f5bfca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,7 +15,7 @@ rgb.png
texture.png
va_surface.png
textures/
-
+native/result
### IntelliJ IDEA ###
.idea/
diff --git a/.run/desktop.run.xml b/.run/desktop.run.xml
deleted file mode 100644
index 40830f1..0000000
--- a/.run/desktop.run.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts
index e51dbe2..7c90879 100644
--- a/.teamcity/settings.kts
+++ b/.teamcity/settings.kts
@@ -1,35 +1,18 @@
-import jetbrains.buildServer.configs.kotlin.*
+import jetbrains.buildServer.configs.kotlin.BuildType
import jetbrains.buildServer.configs.kotlin.buildFeatures.perfmon
+import jetbrains.buildServer.configs.kotlin.buildFeatures.pullRequests
+import jetbrains.buildServer.configs.kotlin.buildSteps.gradle
+import jetbrains.buildServer.configs.kotlin.project
import jetbrains.buildServer.configs.kotlin.projectFeatures.githubConnection
+import jetbrains.buildServer.configs.kotlin.triggers.VcsTrigger
import jetbrains.buildServer.configs.kotlin.triggers.vcs
-
-/*
-The settings script is an entry point for defining a TeamCity
-project hierarchy. The script should contain a single call to the
-project() function with a Project instance or an init function as
-an argument.
-
-VcsRoots, BuildTypes, Templates, and subprojects can be
-registered inside the project using the vcsRoot(), buildType(),
-template(), and subProject() methods respectively.
-
-To debug settings scripts in command-line, run the
-
- mvnDebug org.jetbrains.teamcity:teamcity-configs-maven-plugin:generate
-
-command and attach your debugger to the port 8000.
-
-To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
--> Tool Windows -> Maven Projects), find the generate task node
-(Plugins -> teamcity-configs -> teamcity-configs:generate), the
-'Debug' option is available in the context menu for the task.
-*/
+import jetbrains.buildServer.configs.kotlin.version
version = "2025.11"
project {
-
- buildType(Build)
+ buildType(BuildSnapshot)
+ buildType(BuildRelease)
features {
githubConnection {
@@ -41,16 +24,65 @@ project {
}
}
-object Build : BuildType({
- name = "Build"
+abstract class Build(
+ val publishRepository: String,
+ val publishUsername: String,
+ val publishPassword: String,
+ val publishVersion: String,
+ val trigger: VcsTrigger.() -> Unit,
+) : BuildType({
+ name = "Build and Publish"
triggers {
vcs {
+ branchFilter = "+:refs/heads/*"
}
}
features {
perfmon {
}
+
+ pullRequests {
+ provider = github {
+ authType = vcsRoot()
+ ignoreDrafts = true
+ }
+ }
+ }
+
+ requirements {
+ exists("system.nix.store")
+ }
+
+ steps {
+ gradle {
+ tasks = "build publish"
+ gradleParams = "-Pdeploy.version=${publishVersion} -Pdeploy.kotlin=true"
+ incremental = true
+ param("env.MAVEN_REPO_URL", publishRepository)
+ param("env.MAVEN_REPO_USERNAME", publishUsername)
+ param("env.MAVEN_REPO_PASSWORD", publishPassword)
+ }
}
})
+
+object BuildSnapshot : Build(
+ publishRepository = "https://repoflow.silenium.dev/api/maven/public/maven-snapshots",
+ publishUsername = "teamcitypublic",
+ publishPassword = "credentialsJSON:c8524851-3a17-4ea4-ac26-49a99c5c387c",
+ publishVersion = "%build.vcs.number%",
+ trigger = {
+ branchFilter = "+:refs/heads/*"
+ }
+)
+
+object BuildRelease : Build(
+ publishRepository = "https://repoflow.silenium.dev/api/maven/public/maven-releases",
+ publishUsername = "teamcitypublic",
+ publishPassword = "credentialsJSON:c8524851-3a17-4ea4-ac26-49a99c5c387c",
+ publishVersion = "%build.vcs.number%",
+ trigger = {
+ branchFilter = "+:refs/tags/*"
+ }
+)
diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts
new file mode 100644
index 0000000..9c64596
--- /dev/null
+++ b/build-logic/build.gradle.kts
@@ -0,0 +1,33 @@
+plugins {
+ `kotlin-dsl`
+ `java-gradle-plugin`
+}
+
+repositories {
+ maven("https://reposilite.silenium.dev/releases") {
+ name = "silenium-releases"
+ }
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+}
+
+dependencies {
+ implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
+ api(libs.bundles.gradle.plugins)
+ libs.bundles.kotlin.plugins.get().forEach {
+ api(variantOf(provider { it }) {
+ classifier("gradle813")
+ })
+ }
+ api(libs.jni.utils)
+}
+
+gradlePlugin {
+ plugins {
+ register("natives") {
+ id = "av-natives"
+ implementationClass = "dev.silenium.compose.av.build.NativesPlugin"
+ }
+ }
+}
diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts
new file mode 100644
index 0000000..792f951
--- /dev/null
+++ b/build-logic/settings.gradle.kts
@@ -0,0 +1,13 @@
+dependencyResolutionManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+
+ versionCatalogs {
+ create("libs") {
+ from(files("../gradle/libs.versions.toml"))
+ }
+ }
+}
diff --git a/build-logic/src/main/kotlin/dev/silenium/compose/av/build/DownloadFile.kt b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/DownloadFile.kt
new file mode 100644
index 0000000..ae7286a
--- /dev/null
+++ b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/DownloadFile.kt
@@ -0,0 +1,53 @@
+package dev.silenium.compose.av.build
+
+import org.gradle.api.logging.Logger
+import org.gradle.internal.logging.progress.ProgressLogger
+import java.io.OutputStream
+import java.net.URI
+import kotlin.time.Clock
+import kotlin.time.Duration.Companion.seconds
+import kotlin.time.ExperimentalTime
+
+@OptIn(ExperimentalTime::class)
+fun downloadFile(progressLogger: ProgressLogger, logger: Logger, uri: URI, outputStream: OutputStream) {
+ try {
+ val conn = uri.toURL().openConnection()
+ conn.connect()
+ val size = conn.contentLengthLong
+ progressLogger.progress("${size.toMB()} MB")
+
+ var downloaded = 0L
+ var lastLog = Clock.System.now()
+ val buf = ByteArray(1024)
+ conn.inputStream.use { input ->
+ while (true) {
+ if (Thread.currentThread().isInterrupted) {
+ throw InterruptedException("Download interrupted")
+ }
+ val read = input.read(buf)
+ if (read == -1) break
+ outputStream.write(buf, 0, read)
+
+ downloaded += read
+ if (downloaded > 0) {
+ progressLogger.progress("${downloaded.toMB()} MB / ${size.toMB()} MB (${downloaded partOf size}%)")
+ } else {
+ progressLogger.progress("${size.toMB()} MB")
+ }
+ val now = Clock.System.now()
+ if (now - lastLog > 1.seconds) {
+ lastLog = now
+ logger.lifecycle("${progressLogger.description}: ${downloaded.toMB()} MB / ${size.toMB()} MB (${downloaded partOf size}%)")
+ }
+ }
+ }
+ progressLogger.completed()
+ logger.lifecycle("${progressLogger.description}: ${downloaded.toMB()} MB / ${size.toMB()} MB (${downloaded partOf size}%)")
+ } catch (e: Exception) {
+ progressLogger.completed(e.message ?: "Unknown error", true)
+ throw e
+ }
+}
+
+private fun Long.toMB() = this / 1024 / 1024
+private infix fun Long.partOf(full: Long) = (this * 100) / full
diff --git a/build-logic/src/main/kotlin/dev/silenium/compose/av/build/NativesExtension.kt b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/NativesExtension.kt
new file mode 100644
index 0000000..9c01a8d
--- /dev/null
+++ b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/NativesExtension.kt
@@ -0,0 +1,15 @@
+package dev.silenium.compose.av.build
+
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+
+interface NativesExtension {
+ val sourceFiles: ConfigurableFileCollection
+ val libName: Property
+ val libVersion: Property
+
+ val nixFlake: RegularFileProperty
+ val nixFlakeLock: RegularFileProperty
+ val showLogs: Property
+}
diff --git a/build-logic/src/main/kotlin/dev/silenium/compose/av/build/NativesPlugin.kt b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/NativesPlugin.kt
new file mode 100644
index 0000000..1cefd54
--- /dev/null
+++ b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/NativesPlugin.kt
@@ -0,0 +1,87 @@
+package dev.silenium.compose.av.build
+
+import dev.silenium.libs.jni.NativeLoader
+import dev.silenium.libs.jni.Platform
+import org.gradle.api.JavaVersion
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.api.tasks.Delete
+import org.gradle.kotlin.dsl.apply
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.named
+import org.gradle.kotlin.dsl.register
+import org.gradle.language.jvm.tasks.ProcessResources
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
+
+@Suppress("unused") // used as plugin entrypoint
+class NativesPlugin : Plugin {
+ override fun apply(target: Project) {
+ target.apply()
+ target.configure {
+ compilerOptions.jvmTarget.set(JvmTarget.JVM_11)
+ }
+ target.configure {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ val ext = target.extensions.create("natives", NativesExtension::class.java)
+ ext.libVersion.convention(target.version.toString())
+ ext.libName.convention(target.name)
+ ext.nixFlakeLock.convention(target.layout.file(ext.nixFlake.map { it.asFile.resolveSibling("flake.lock") }))
+ val nixResultDir = target.layout.buildDirectory.dir("nix-result")
+
+ val nixClean = target.tasks.register("nixClean") {
+ delete(nixResultDir)
+ }
+ target.tasks.named("clean").configure {
+ dependsOn(nixClean)
+ }
+
+ val nixBuild = target.tasks.register("nixBuild") {
+ doFirst {
+ nixResultDir.get().asFile.deleteRecursively()
+ }
+ group = "build"
+ inputs.files(ext.sourceFiles)
+ inputs.files(ext.nixFlake, ext.nixFlakeLock)
+ libName.set(ext.libName)
+ resultDir.set(nixResultDir)
+ showLogs.set(ext.showLogs)
+ }
+
+ target.afterEvaluate {
+ tasks.named("processResources") {
+ val out = nixBuild.flatMap { it.resultDir.asFile }
+ val targets = mapOf(
+ Platform(Platform.OS.LINUX, Platform.Arch.X86_64) to "x86_64-linux",
+ Platform(Platform.OS.LINUX, Platform.Arch.ARM64) to "aarch64-linux",
+ Platform(Platform.OS.WINDOWS, Platform.Arch.X86_64) to "x86_64-windows",
+ )
+ targets.forEach { (platform, target) ->
+ val src = out.map { out ->
+ out.resolve("${ext.libName.get()}-${target}-${ext.libVersion.get()}").resolve("lib")
+ }
+ val platformFormat = NativeLoader.fileNameTemplate(platform)
+ from(src) {
+ include("*.so")
+ include("*.dll")
+ include("*.dylib")
+ rename {
+ val matches = platformFormat
+ .replace(".", "\\.")
+ .replace("%s", "(.*)")
+ .toRegex()
+ .find(it) ?: error("library name does not match platform pattern: $it")
+
+ NativeLoader.libPath(matches.groupValues[1], platform = platform)
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/build-logic/src/main/kotlin/dev/silenium/compose/av/build/NixBuildTask.kt b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/NixBuildTask.kt
new file mode 100644
index 0000000..c95553f
--- /dev/null
+++ b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/NixBuildTask.kt
@@ -0,0 +1,28 @@
+package dev.silenium.compose.av.build
+
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Exec
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.OutputDirectory
+
+// Never cache as read-only outputs break cache restore
+// Caching will instead happen on nix level
+abstract class NixBuildTask : Exec() {
+ @get:Input
+ abstract val libName: Property
+
+ @get:Input
+ abstract val showLogs: Property
+
+ @get:OutputDirectory
+ abstract val resultDir: DirectoryProperty
+
+ override fun exec() {
+ val logArgs = arrayOf("--print-build-logs").takeIf { showLogs.get() } ?: arrayOf()
+ commandLine("nix", "build", "-o", resultDir.get().asFile.absolutePath, *logArgs, ".#${libName.get()}")
+ standardOutput = System.out
+ errorOutput = System.out
+ super.exec()
+ }
+}
diff --git a/build-logic/src/main/kotlin/dev/silenium/compose/av/build/ProjectExtensions.kt b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/ProjectExtensions.kt
new file mode 100644
index 0000000..13291e6
--- /dev/null
+++ b/build-logic/src/main/kotlin/dev/silenium/compose/av/build/ProjectExtensions.kt
@@ -0,0 +1,7 @@
+package dev.silenium.compose.av.build
+
+import org.gradle.accessors.dm.LibrariesForLibs
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.the
+
+val Project.libs get() = the()
diff --git a/build.gradle.kts b/build.gradle.kts
index cec5232..4a4ac09 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,4 +1,3 @@
-import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.gradle.ext.ProjectSettings
import org.jetbrains.gradle.ext.TaskTriggersConfig
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
@@ -12,59 +11,29 @@ plugins {
`maven-publish`
}
-val deployNative = (findProperty("deploy.native") as String?)?.toBoolean() ?: true
val deployKotlin = (findProperty("deploy.kotlin") as String?)?.toBoolean() ?: true
-val skikoEGL = (findProperty("skiko.egl") as String?)?.toBoolean() ?: false
dependencies {
- // Note, if you develop a library, you should use compose.desktop.common.
- // compose.desktop.currentOs should be used in launcher-sourceSet
- // (in a separate module for demo project and in testMain).
- // With compose.desktop.common you will also lose @Preview functionality
- implementation(compose.desktop.currentOs)
- implementation(compose.material3)
- implementation(compose.materialIconsExtended)
- implementation("androidx.annotation:annotation-jvm:1.9.1")
+ implementation(libs.compose.desktop.common)
+ implementation(libs.compose.material3)
+ implementation(libs.compose.material.icons.extended)
+ implementation(libs.androidx.annotation)
implementation(libs.compose.gl)
implementation(libs.compose.gl.natives)
implementation(libs.jni.utils)
implementation(libs.jna)
implementation(libs.bundles.kotlinx)
implementation(libs.slf4j.api) // for logging
- if (deployNative) {
- implementation(project(":native"))
- }
+ implementation(project(":native"))
implementation(kotlin("reflect"))
- if (skikoEGL) {
- implementation(libs.bundles.skiko) {
- version {
- strictly(libs.skiko.awt.runtime.linux.x64.get().version!!)
- }
- }
- }
- testImplementation(compose.materialIconsExtended)
+ testImplementation(compose.desktop.currentOs)
+ testImplementation(libs.compose.material.icons.extended)
testImplementation(libs.bundles.kotest)
testImplementation(libs.mockk)
testImplementation(libs.logback.classic)
}
-configurations.all {
- resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
-}
-
-compose.desktop {
- application {
- mainClass = "MainKt"
-
- nativeDistributions {
- targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
- packageName = "gl-demo"
- packageVersion = "1.0.0"
- }
- }
-}
-
val templateSrc = layout.projectDirectory.dir("src/main/templates")
val templateDst = layout.buildDirectory.dir("generated/templates")
val templateProps = mapOf(
@@ -96,10 +65,8 @@ tasks {
kotlin {
compilerOptions {
- freeCompilerArgs.add("-Xcontext-receivers")
- jvmTarget = JvmTarget.JVM_11
+ jvmTarget = JvmTarget.JVM_17
}
- jvmToolchain(11)
}
sourceSets.main {
@@ -109,6 +76,9 @@ sourceSets.main {
}
java {
+ sourceCompatibility = kotlin.compilerOptions.jvmTarget.map { JavaVersion.toVersion(it.target) }.get()
+ targetCompatibility = sourceCompatibility
+
withSourcesJar()
withJavadocJar()
}
diff --git a/gradle.properties b/gradle.properties
index 815b4ff..39eede6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,4 @@
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
kotlin.code.style=official
-skiko.egl=false
+org.gradle.configuration-cache=true
+org.gradle.caching=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 2e0310c..53db166 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,23 +1,22 @@
[versions]
-kotlin = "2.1.0"
+kotlin = "2.3.10"
-kotlinx-coroutines = "1.10.1"
-kotlinx-serialization = "1.8.0"
-kotlinx-datetime = "0.6.1"
+kotlinx-coroutines = "1.10.2"
+kotlinx-serialization = "1.10.0"
+kotlinx-datetime = "0.7.1"
-compose = "1.7.3"
-compose-gl = "0.7.4"
-jni-utils = "0.1.6"
-skiko = "0.8.19-egl"
-jna = "5.16.0"
+compose = "1.11.0-alpha02"
+compose-gl = "0.9.1"
+jni-utils = "0.1.8"
+jna = "5.18.1"
-slf4j = "2.0.16"
-logback = "1.5.16"
+slf4j = "2.0.17"
+logback = "1.5.32"
-kotest = "5.9.1"
-mockk = "1.13.14"
+kotest = "6.1.3"
+mockk = "1.14.9"
-idea-ext = "1.1.9"
+idea-ext = "1.4.1"
[libraries]
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
@@ -32,16 +31,18 @@ kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-datetime" }
+compose-desktop-common = { group = "org.jetbrains.compose.desktop", name = "desktop", version.ref = "compose" }
+compose-material3 = { group = "org.jetbrains.compose.material3", name = "material3", version.ref = "compose" }
+compose-material-icons-extended = { group = "org.jetbrains.compose.material", name = "material-icons-extended", version = "1.7.3" }
+androidx-annotation = { group = "androidx.annotation", name = "annotation", version = "1.9.1" }
+
compose-gl = { group = "dev.silenium.compose.gl", name = "compose-gl", version.ref = "compose-gl" }
-compose-gl-natives = { group = "dev.silenium.compose.gl", name = "compose-gl-natives-linux-x86_64", version.ref = "compose-gl" }
+compose-gl-natives = { group = "dev.silenium.compose.gl", name = "compose-gl-natives-all", version.ref = "compose-gl" }
ffmpeg-natives = { group = "dev.silenium.libs", name = "ffmpeg-natives", version = "7.1+0.2.0" }
mpv-natives = { group = "dev.silenium.libs", name = "ffmpeg-natives", version = "0.39.0+0.1.2" }
jni-utils = { group = "dev.silenium.libs.jni", name = "jni-utils", version.ref = "jni-utils" }
jna = { group = "net.java.dev.jna", name = "jna", version.ref = "jna" }
-skiko-awt = { group = "org.jetbrains.skiko", name = "skiko-awt", version.ref = "skiko" }
-skiko-awt-runtime-linux-x64 = { group = "org.jetbrains.skiko", name = "skiko-awt-runtime-linux-x64", version.ref = "skiko" }
-
slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
@@ -52,6 +53,12 @@ kotest-assertions-json = { group = "io.kotest", name = "kotest-assertions-json",
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
+compose-gradle-plugin = { group = "org.jetbrains.compose", name = "compose-gradle-plugin", version.ref = "compose" }
+kotlin-jvm-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
+kotlin-compose-gradle-plugin = { group = "org.jetbrains.kotlin", name = "compose-compiler-gradle-plugin", version.ref = "kotlin" }
+kotlin-serialization-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" }
+idea-ext = { group = "org.jetbrains.gradle.plugin.idea-ext", name = "org.jetbrains.gradle.plugin.idea-ext.gradle.plugin", version.ref = "idea-ext" }
+
[plugins]
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
@@ -79,7 +86,12 @@ kotest = [
"kotest-assertions-json",
]
-skiko = [
- "skiko-awt",
- "skiko-awt-runtime-linux-x64",
+kotlin-plugins = [
+ "kotlin-jvm-gradle-plugin",
+ "kotlin-compose-gradle-plugin",
+ "kotlin-serialization-gradle-plugin",
+]
+gradle-plugins = [
+ "compose-gradle-plugin",
+ "idea-ext",
]
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1e2fbf0..2f2958b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/native/.gitignore b/native/.gitignore
index db5c42d..c4988be 100644
--- a/native/.gitignore
+++ b/native/.gitignore
@@ -1,3 +1,5 @@
.idea/
*.iml
-cmake-build-*/
\ No newline at end of file
+cmake-build-*/
+/subprojects
+buildDir/
diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt
deleted file mode 100644
index 4c82b27..0000000
--- a/native/CMakeLists.txt
+++ /dev/null
@@ -1,97 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-if (NOT DEFINED PROJECT_NAME)
- set(PROJECT_NAME "compose-av")
-endif ()
-if (NOT DEFINED NATIVE_PLATFORM)
- message(FATAL_ERROR "NATIVE_PLATFORM must be defined")
-else ()
- message(STATUS "NATIVE_PLATFORM: ${NATIVE_PLATFORM}")
-endif ()
-if (NOT DEFINED FFMPEG_VERSION)
- message(FATAL_ERROR "FFMPEG_VERSION must be defined")
-else ()
- message(STATUS "FFMPEG_VERSION: ${FFMPEG_VERSION}")
-endif ()
-if (NOT DEFINED MPV_VERSION)
- message(FATAL_ERROR "MPV_VERSION must be defined")
-else ()
- message(STATUS "MPV_VERSION: ${MPV_VERSION}")
-endif ()
-if (NOT DEFINED JAVA_HOME)
- message(FATAL_ERROR "JAVA_HOME must be defined")
-else ()
- message(STATUS "JAVA_HOME: ${JAVA_HOME}")
-endif ()
-
-project(${PROJECT_NAME} LANGUAGES CXX)
-
-set(CMAKE_CXX_STANDARD 20)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_POSITION_INDEPENDENT_CODE ON)
-option(USE_SYSTEM_FFMPEG "Link dynamically against system ffmpeg instead of using static prebuilt libraries" OFF)
-option(USE_SYSTEM_MPV "Link dynamically against system mpv instead of using static prebuilt libraries" OFF)
-
-include(ExternalProject)
-if (NOT USE_SYSTEM_FFMPEG)
- include(thirdparty/ffmpeg.cmake)
-endif ()
-if (NOT USE_SYSTEM_MPV)
- include(thirdparty/mpv.cmake)
-endif ()
-
-set(SOURCES
- src/cpp/util/Errors.cpp
- src/cpp/helper/errors.hpp
- src/cpp/helper/errors.cpp
- src/cpp/mpv/MPV.cpp
-)
-
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- if (NOT DEFINED JAVA_HOME)
- set(JAVA_HOME "/usr/lib/jvm/java-11-openjdk")
- endif ()
-
-# list(APPEND SOURCES)
-elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
-endif ()
-
-add_library(${PROJECT_NAME} SHARED ${SOURCES})
-target_include_directories(${PROJECT_NAME} PRIVATE "src/cpp/")
-
-message(STATUS "JAVA_HOME: ${JAVA_HOME}")
-target_include_directories(${PROJECT_NAME} PUBLIC "${JAVA_HOME}/include")
-if (NOT USE_SYSTEM_FFMPEG)
- target_link_libraries(${PROJECT_NAME} PUBLIC -Wl,--push-state,--whole-archive,--allow-multiple-definition ffmpeg -Wl,--pop-state)
-endif ()
-if (NOT USE_SYSTEM_MPV)
- target_link_libraries(${PROJECT_NAME} PUBLIC -Wl,--push-state,--whole-archive,--allow-multiple-definition mpv -Wl,--pop-state)
-endif ()
-add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND objcopy --localize-hidden --strip-all --strip-unneeded $)
-
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- target_compile_definitions(${PROJECT_NAME} PRIVATE -D_LINUX)
- find_package(PkgConfig REQUIRED)
- pkg_check_modules(GL REQUIRED IMPORTED_TARGET egl glx)
- target_link_libraries(${PROJECT_NAME} PUBLIC PkgConfig::GL)
- target_include_directories(${PROJECT_NAME} PUBLIC "${JAVA_HOME}/include/linux")
- add_compile_options(-static -static-libstdc++ -static-libgcc)
- add_link_options(-static -static-libstdc++ -static-libgcc)
- if (USE_SYSTEM_FFMPEG)
- pkg_check_modules(FFMPEG REQUIRED IMPORTED_TARGET libavcodec libavformat libavutil libswscale libavfilter)
- target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::FFMPEG)
- endif ()
- if (USE_SYSTEM_MPV)
- pkg_check_modules(MPV REQUIRED IMPORTED_TARGET mpv)
- target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::MPV)
- endif ()
-elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
- target_compile_definitions(${PROJECT_NAME} PRIVATE -D_WINDOWS)
- target_link_libraries(${PROJECT_NAME} PUBLIC opengl32 dxgi d3d11 d3dcompiler)
- target_include_directories(${PROJECT_NAME} PUBLIC "${JAVA_HOME}/include/win32")
-endif ()
-
-#add_executable(test main.cpp)
-#target_link_libraries(test PRIVATE PkgConfig::FFMPEG)
-#target_link_libraries(test PRIVATE ${PROJECT_NAME})
-#target_link_libraries(test PRIVATE X11 GLX xcb va va-glx va-x11)
-#target_include_directories(test PRIVATE "src/cpp/")
diff --git a/native/build.gradle.kts b/native/build.gradle.kts
index 4129e56..0cbd48f 100644
--- a/native/build.gradle.kts
+++ b/native/build.gradle.kts
@@ -1,105 +1,22 @@
-import dev.silenium.libs.jni.NativeLoader
-import dev.silenium.libs.jni.NativePlatform
-import dev.silenium.libs.jni.Platform
-
-buildscript {
- repositories {
- maven("https://reposilite.silenium.dev/releases") {
- name = "silenium-releases"
- }
- }
- dependencies {
- classpath(libs.jni.utils)
- }
-}
-
plugins {
- alias(libs.plugins.kotlin)
- `maven-publish`
-}
-
-val libName = rootProject.name
-val deployNative = (findProperty("deploy.native") as String?)?.toBoolean() ?: true
-
-val withGPL: Boolean = findProperty("ffmpeg.gpl").toString().toBoolean()
-val platformExtension = "-gpl".takeIf { withGPL }.orEmpty()
-val platformString = findProperty("ffmpeg.platform")?.toString()
-val platform = platformString?.let { Platform(it, platformExtension) } ?: NativePlatform.platform(platformExtension)
-
-val cmakeExe = findProperty("cmake.executable") as? String ?: "cmake"
-val generateMakefile = tasks.register("generateMakefile") {
- workingDir = layout.buildDirectory.dir("cmake").get().asFile.apply { mkdirs() }
- val additionalFlags = mutableListOf(
- "-DJAVA_HOME=${System.getProperty("java.home")}",
- "-DPROJECT_NAME=${libName}",
- "-DNATIVE_PLATFORM=${platform.osArch}",
- "-DFFMPEG_PLATFORM_EXTENSION=${platform.extension}",
- "-DFFMPEG_VERSION=${libs.ffmpeg.natives.get().version}",
- "-DMPV_VERSION=${libs.mpv.natives.get().version}",
- )
- commandLine(
- cmakeExe,
- *additionalFlags.toTypedArray(),
- layout.projectDirectory.asFile.absolutePath,
- )
-
- inputs.file(layout.projectDirectory.file("CMakeLists.txt"))
- inputs.properties(
- "JAVA_HOME" to System.getProperty("java.home"),
- "PROJECT_NAME" to libName,
- "NATIVE_PLATFORM" to platform.osArch,
- "FFMPEG_PLATFORM_EXTENSION" to platform.extension,
- "FFMPEG_VERSION" to libs.ffmpeg.natives.get().version,
- "MPV_VERSION" to libs.mpv.natives.get().version,
- )
- outputs.dir(workingDir)
- standardOutput = System.out
-}
-
-val compileNative = tasks.register("compileNative") {
- workingDir = layout.buildDirectory.dir("cmake").get().asFile
- commandLine(cmakeExe, "--build", ".", "-j", Runtime.getRuntime().availableProcessors().toString())
- dependsOn(generateMakefile)
-
- standardOutput = System.out
- val fileNameTemplate = NativeLoader.fileNameTemplate(platform)
- when (platform.os) {
- Platform.OS.WINDOWS -> {
- outputs.files(layout.buildDirectory.file("cmake/Debug/${fileNameTemplate.format(libName)}"))
- }
-
- Platform.OS.LINUX, Platform.OS.DARWIN -> {
- outputs.files(layout.buildDirectory.file("cmake/${fileNameTemplate.format(libName)}"))
- }
- }
- inputs.file(layout.buildDirectory.file("cmake/CMakeCache.txt"))
- inputs.dir(layout.projectDirectory.dir("src"))
- inputs.file(layout.projectDirectory.file("CMakeLists.txt"))
- outputs.cacheIf { true }
+ id("av-natives")
}
-tasks.processResources {
- dependsOn(compileNative)
- // Required for configuration cache
- val libName = rootProject.name
- val platformString = findProperty("ffmpeg.platform")?.toString()
- val withGPL: Boolean = findProperty("ffmpeg.gpl").toString().toBoolean()
- val platformExtension = "-gpl".takeIf { withGPL }.orEmpty()
- val platform = platformString?.let { Platform(it, platformExtension) } ?: NativePlatform.platform(platformExtension)
+val deployKotlin = (findProperty("deploy.kotlin") as String?)?.toBoolean() ?: true
- from(compileNative.get().outputs.files) {
- rename {
- NativeLoader.libPath(libName, platform = platform)
- }
- }
+natives {
+ libName = rootProject.name
+ libVersion = "0.1.0"
+ nixFlake = file("flake.nix")
+ sourceFiles.from("src", "meson.build", "subprojects.tpl")
+ showLogs = providers.environmentVariable("CI").orElse("false").map { it != "false" }
}
publishing {
publications {
- if (deployNative) {
- create("natives${platform.capitalized}") {
- from(components["java"])
- artifactId = "$libName-natives-$platform"
+ if (deployKotlin) {
+ create("maven") {
+ from(components["kotlin"])
}
}
}
diff --git a/native/flake.lock b/native/flake.lock
new file mode 100644
index 0000000..6931c16
--- /dev/null
+++ b/native/flake.lock
@@ -0,0 +1,97 @@
+{
+ "nodes": {
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": "nixpkgs-lib"
+ },
+ "locked": {
+ "lastModified": 1772408722,
+ "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "jni-utils": {
+ "inputs": {
+ "flake-parts": "flake-parts",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1772752807,
+ "narHash": "sha256-/j59dcF9R1pwmBLTnODWx88kS+sp+92fnAb7InVYMEM=",
+ "owner": "silenium-dev",
+ "repo": "jni-utils",
+ "rev": "a7698b4c08eaa05bc2f490c911bc9b1f111e4d2c",
+ "type": "github"
+ },
+ "original": {
+ "owner": "silenium-dev",
+ "ref": "nix-base",
+ "repo": "jni-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1771848320,
+ "narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "2fc6539b481e1d2569f25f8799236694180c0993",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-lib": {
+ "locked": {
+ "lastModified": 1772328832,
+ "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=",
+ "owner": "nix-community",
+ "repo": "nixpkgs.lib",
+ "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "nixpkgs.lib",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1772624091,
+ "narHash": "sha256-QKyJ0QGWBn6r0invrMAK8dmJoBYWoOWy7lN+UHzW1jc=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "80bdc1e5ce51f56b19791b52b2901187931f5353",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "jni-utils": "jni-utils",
+ "nixpkgs": "nixpkgs_2"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/native/flake.nix b/native/flake.nix
new file mode 100644
index 0000000..ee23a5a
--- /dev/null
+++ b/native/flake.nix
@@ -0,0 +1,212 @@
+{
+ description = "jni build environment";
+
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
+ jni-utils.url = "github:silenium-dev/jni-utils?ref=nix-base";
+ };
+
+ outputs = { nixpkgs, jni-utils, ... }:
+ let
+ pkgs = nixpkgs.legacyPackages."x86_64-linux";
+ fs = nixpkgs.lib.fileset;
+ in
+ {
+ packages."x86_64-linux" = jni-utils.lib.buildJNILib {
+ name = "compose-av";
+ version = "0.1.0";
+ mesonTarget = "compose-av";
+ buildType = "release";
+ libName = "compose-av";
+ libDir = "src";
+
+ additionalNativeInputs = targetSystem: pkgs: [ pkgs.p7zip pkgs.curl pkgs.cacert pkgs.python3 ];
+ additionalInputs = targetSystem: pkgs:
+ if targetSystem == "x86_64-windows" then [ ]
+ else [ pkgs.libxcb pkgs.libxau pkgs.libxdmcp ];
+ sources = targetSystem:
+ let
+ mpv = import ./nix/mpv-config.nix { inherit pkgs; };
+ ffmpeg = import ./nix/ffmpeg-config.nix { inherit pkgs; };
+ sourceFiles = fs.unions [
+ ./src
+ ./meson.build
+ ./subprojects.tpl
+ ];
+ in
+ (if targetSystem == "x86_64-windows" then
+ [
+ (builtins.fetchurl {
+ url = mpv.${targetSystem}.source_url;
+ sha256 = mpv.${targetSystem}.source_hash;
+ name = "mpv.7z";
+ })
+ ]
+ else [
+ (import ./nix/linux-sources.nix { inherit pkgs; })
+ (builtins.fetchurl {
+ url = ffmpeg.${targetSystem}.source_url;
+ sha256 = ffmpeg.${targetSystem}.source_hash;
+ name = "ffmpeg.tar.xz";
+ })
+ ]) ++ [
+ (builtins.path {
+ path = fs.toSource {
+ root = ./.;
+ fileset = sourceFiles;
+ };
+ name = "compose-av";
+ })
+ ];
+
+ unpack = targetSystem: ''
+ runHook preUnpack
+
+ # Process each source
+ for src in $srcs; do
+ srcName=$(stripHash "$src")
+
+ case "$src" in
+ *.tar.gz|*.tar.xz)
+ dirName="''${srcName%.tar.*}"
+ echo "Extracting tar archive into: $dirName"
+ mkdir -p "$dirName"
+ tar xf "$src" -C "$dirName"
+
+ # Check if archive only contained one directory
+ shopt -s nullglob dotglob
+ contents=("$dirName"/*)
+ if [ ''${#contents[@]} -eq 1 ] && [ -d "''${contents[0]}" ]; then
+ echo "Flattening single directory structure"
+ mv "''${contents[0]}"/* "$dirName"/
+ rmdir "''${contents[0]}"
+ fi
+
+ chmod -R +w "$dirName"
+
+ for i in "$dirName"/**/*.py "$dirName"/*.py; do
+ echo "patching shebangs in $i"
+ patchShebangs --build "$i"
+ done
+
+ tar cf "$dirName.tar" -C "$dirName" .
+ rm -rf "$dirName"
+ ;;
+ *.zip)
+ dirName="''${srcName%.zip}"
+ echo "Extracting tar archive into: $dirName"
+ mkdir -p "$dirName"
+ 7z x -o"$dirName" "$src"
+
+ # Check if archive only contained one directory
+ shopt -s nullglob dotglob
+ contents=("$dirName"/*)
+ if [ ''${#contents[@]} -eq 1 ] && [ -d "''${contents[0]}" ]; then
+ echo "Flattening single directory structure"
+ mv "''${contents[0]}"/* "$dirName"/
+ rmdir "''${contents[0]}"
+ fi
+
+ chmod -R +w "$dirName"
+
+ for i in "$dirName"/**/*.py "$dirName"/*.py; do
+ echo "patching shebangs in $i"
+ patchShebangs --build "$i"
+ done
+
+ tar cf "$dirName.tar" -C "$dirName" .
+ rm -rf "$dirName"
+ ;;
+ *.7z)
+ dirName="''${srcName%.7z}"
+ echo "Extracting 7z archive into: $dirName"
+ mkdir -p "$dirName"
+ 7z x -o"$dirName" "$src"
+
+ chmod -R +w "$dirName"
+ tar cf "$dirName.tar" -C "$dirName" .
+ ;;
+ *compose-av)
+ echo "Copying: $srcName"
+ cp -r "$src" "$srcName"
+ chmod -R +w "$srcName"
+ ;;
+ *)
+ dirName="''${srcName}"
+ echo "Compressing: $srcName"
+ cp -r "$src" "$srcName"
+ chmod -R +w "$srcName"
+
+ for i in "$dirName"/**/*.py "$dirName"/*.py; do
+ echo "patching shebangs in $i"
+ patchShebangs --build "$i"
+ done
+
+ tar cf "$dirName.tar" -C "$srcName" .
+ rm -rf "$srcName"
+ ;;
+ esac
+ done
+
+ runHook postUnpack
+ '';
+ postUnpackPhase = targetSystem: ''
+ shopt -s globstar
+ case "${targetSystem}" in
+ *-windows)
+ mkdir -p compose-av/subprojects/mpv
+ tar xf mpv.tar -C compose-av/subprojects/mpv/
+ cp compose-av/subprojects.tpl/windows/packagefiles/mpv/* compose-av/subprojects/mpv/
+ ;;
+ *-linux)
+ for p in ./*-patch.tar; do
+ echo "patching ''${p%-patch.tar}"
+ tar --concatenate --file="''${p%-patch.tar}.tar" "$p"
+ rm "$p"
+ done
+
+ mkdir -p compose-av/subprojects/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles
+ mkdir -p compose-av/subprojects/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles/libfontconfig/subprojects/packagefiles
+ mv libfontconfig.tar compose-av/subprojects/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles
+ mv libxml2.tar compose-av/subprojects/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles/libfontconfig/subprojects/packagefiles
+ mv gperf.tar compose-av/subprojects/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles/libfontconfig/subprojects/packagefiles
+
+ mkdir -p compose-av/subprojects/packagefiles/mpv/subprojects/packagefiles/harfbuzz/subprojects/packagefiles
+ mv icu.tar compose-av/subprojects/packagefiles/mpv/subprojects/packagefiles/harfbuzz/subprojects/packagefiles
+
+ mv mpv.tar compose-av/subprojects/packagefiles
+
+ mv *.tar compose-av/subprojects/packagefiles/mpv/subprojects/packagefiles
+
+ for i in compose-av/subprojects.tpl/linux/**/*; do
+ if [ -d "$i" ]; then
+ echo "creating dir $i"
+ mkdir -p "compose-av/subprojects/''${i#compose-av/subprojects.tpl/linux/}"
+ else
+ echo "Expanding $i:"
+ cat "$i" \
+ | sed 's,''${cav_archive_base},'"$(pwd)"',g' \
+ > "compose-av/subprojects/''${i#compose-av/subprojects.tpl/linux/}"
+ fi
+ done
+ ;;
+ esac
+
+ sourceRoot="$(pwd)/compose-av"
+ echo "changing source root: $sourceRoot"
+ cd "$sourceRoot"
+ '';
+
+ postInstallPhase = targetSystem: ''
+ case "${targetSystem}" in
+ *-linux)
+ cp -d subprojects/ffmpeg/lib/{libavcodec,libavdevice,libavfilter,libavformat,libavutil,libswresample,libswscale}.so* $out/lib
+ ;;
+ *-windows)
+ cp subprojects/mpv/libmpv-2.dll $out/lib
+ ;;
+ esac
+ '';
+ };
+ };
+}
diff --git a/native/hack/assemble-deps.sh b/native/hack/assemble-deps.sh
new file mode 100644
index 0000000..38be9c7
--- /dev/null
+++ b/native/hack/assemble-deps.sh
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+
+function assemble_deps() {
+ local wrap_file="$1"
+ depName=$(basename "$wrap_file" .wrap)
+ dir=$(dirname "$wrap_file")
+ wrap_type=$(grep -oP '(?<=\[wrap-)[^\]]+(?=\])' "$wrap_file")
+ case "$wrap_type" in
+ file)
+ file_url=$(grep -oP '(?<=source_url = )[^\n]+' "$wrap_file")
+ file_name=$(grep -oP '(?<=source_filename = )[^\n]+' "$wrap_file")
+ file_hash=sha256:$(grep -oP '(?<=source_hash = )[^\n]+' "$wrap_file")
+ if ! file_hash_nix=$(nix hash convert "$file_hash" 2>/dev/null); then
+ return
+ fi
+ file_ext="${file_name##*.}"
+ cat </dev/null)
+ if [ -z "$patch_hash_nix" ]; then
+ echo "Failed to convert patch hash: $patch_hash"
+ return
+ fi
+ cat <=1.2.0',
+)
+
+add_global_arguments('-fPIC', language : 'c')
+add_global_arguments('-fPIC', language : 'cpp')
+add_global_link_arguments(
+ language : 'cpp',
+)
+
+mpv = subproject('mpv',
+ default_options : {
+ 'libmpv' : true,
+ 'drm' : 'enabled',
+ 'openal' : 'enabled',
+ 'wrap_mode' : 'forcefallback',
+ 'default_library' : 'static',
+ 'prefer_static' : true,
+ }
+)
+subdir('src')
diff --git a/native/nix/ffmpeg-config.nix b/native/nix/ffmpeg-config.nix
new file mode 100644
index 0000000..a0d1d4a
--- /dev/null
+++ b/native/nix/ffmpeg-config.nix
@@ -0,0 +1,20 @@
+{ pkgs
+}:
+let
+ ffmpeg = { arch, hash }: rec {
+ source_url = "https://repoflow.silenium.dev/api/universal/personal/github-releases/BtbN/FFmpeg-Builds/autobuild-2026-02-25-13-05/ffmpeg-n8.0.1-64-g15504610b0-${arch}-gpl-shared-8.0.tar.xz";
+ source_hash = hash;
+ source_filename = pkgs.lib.lists.last (pkgs.lib.strings.split "/" source_url);
+ directory = builtins.elemAt (pkgs.lib.strings.split "." source_filename) 0;
+ };
+in
+{
+ "x86_64-linux" = ffmpeg {
+ arch = "linux64";
+ hash = "sha256-uwnRytgBbJLjzjI0gRRPGL70H/1d6B76QaHQcdvLtS8=";
+ };
+ "aarch64-linux" = ffmpeg {
+ arch = "linuxarm64";
+ hash = "sha256-tK1z9Uenfae52UZhliptXTEqlF9Lyz+Cx5CycwzixS8=";
+ };
+}
diff --git a/native/nix/linux-sources.nix b/native/nix/linux-sources.nix
new file mode 100644
index 0000000..eb4f027
--- /dev/null
+++ b/native/nix/linux-sources.nix
@@ -0,0 +1,170 @@
+{ pkgs }: [
+ (pkgs.fetchurl {
+ name = "expat.tar.xz";
+ url = "https://github.com/libexpat/libexpat/releases/download/R_2_7_3/expat-2.7.3.tar.xz";
+ hash = "sha256-cd+PQHBqe7CoClNnB56nXZHaT4xlxY7Fm837997Nq58=";
+ })
+ (pkgs.fetchurl {
+ name = "expat-patch.zip";
+ url = "https://wrapdb.mesonbuild.com/v2/expat_2.7.3-1/get_patch";
+ hash = "sha256-6HDrSy48FCzh0Y7SQwmqQOB8bJEaRiX6HG/Ek7oYFTo=";
+ })
+
+ (pkgs.fetchgit {
+ name = "fmt";
+ url = "https://github.com/fmtlib/fmt.git";
+ rev = "12.0.0";
+ hash = "sha256-AZDmIeU1HbadC+K0TIAGogvVnxt0oE9U6ocpawIgl6g=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchgit {
+ name = "freetype2";
+ url = "https://gitlab.freedesktop.org/freetype/freetype.git";
+ rev = "VER-2-13-3";
+ hash = "sha256-fAefpGvNWM9t8XPS8HOCmXLO/76pwp2TV5yHDSfjc/4=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchgit {
+ name = "fribidi";
+ url = "https://github.com/fribidi/fribidi.git";
+ rev = "v1.0.16";
+ hash = "sha256-VXDgyqpgjeNHnKfihWW0Oe1yzwIv1Bw8mrs8wLBJZgw=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchurl {
+ name = "harfbuzz.tar.xz";
+ url = "https://github.com/harfbuzz/harfbuzz/releases/download/12.1.0/harfbuzz-12.1.0.tar.xz";
+ hash = "sha256-5cgbf24LEC37AAz6QkU4uOiWq3ii9Lil7IyuYqtDNp4=";
+ })
+
+ (pkgs.fetchgit {
+ name = "libass";
+ url = "https://github.com/libass/libass";
+ rev = "0.17.4";
+ hash = "sha256-GgeJ9W1x5IOSyIcbR8F4D5BmAqrkG6xXs85wlVXVsig=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchgit {
+ name = "libfontconfig";
+ url = "https://gitlab.freedesktop.org/fontconfig/fontconfig.git";
+ rev = "2.17.1";
+ hash = "sha256-RCYZctF3Nheopx8RojjlyE8Z2w7C7Nfyi2aobVD9o5Q=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchgit {
+ name = "libdisplay-info";
+ url = "https://gitlab.freedesktop.org/emersion/libdisplay-info.git";
+ rev = "0.3.0";
+ hash = "sha256-nXf2KGovNKvcchlHlzKBkAOeySMJXgxMpbi5z9gLrdc=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchurl {
+ name = "libjpeg-turbo.tar.gz";
+ url = "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.2/libjpeg-turbo-3.1.2.tar.gz";
+ hash = "sha256-jwASI0tGTOUIkMSQ8YGU+ROnsfTmoD1mRBefoPhn0M8=";
+ })
+
+ (pkgs.fetchgit {
+ name = "libplacebo";
+ url = "https://github.com/haasn/libplacebo";
+ rev = "v7.360.0";
+ hash = "sha256-KGPjP2Aia033XLopnlftkhESEXjmOG2j5mHLgpG5dCY=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchurl {
+ name = "libpng.tar.gz";
+ url = "https://github.com/pnggroup/libpng/archive/v1.6.50.tar.gz";
+ hash = "sha256-cRWOU8/fKHe8mbyrM2QdeN8/SObg2q0DCv6cuMAxqkY=";
+ })
+ (pkgs.fetchurl {
+ name = "libpng-patch.zip";
+ url = "https://wrapdb.mesonbuild.com/v2/libpng_1.6.50-2/get_patch";
+ hash = "sha256-qck2K7jLtCLIZIB6F0l33sOGjN/4yjgOJ7q27hyqIuo=";
+ })
+
+ (pkgs.fetchgit {
+ name = "mpv";
+ url = "https://github.com/mpv-player/mpv.git";
+ rev = "v0.41.0";
+ hash = "sha256-gJWqfvPE6xOKlgj2MzZgXiyOKxksJlY/tL6T/BeG19c=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchgit {
+ name = "libuchardet";
+ url = "https://gitlab.freedesktop.org/uchardet/uchardet.git";
+ rev = "v0.0.8";
+ hash = "sha256-5HSwFaclje5JkzOZKILgy2BGxLyFeDq/9p24KiTlTzE=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchurl {
+ name = "openal-soft.tar.gz";
+ url = "https://github.com/kcat/openal-soft/archive/refs/tags/1.24.3.tar.gz";
+ hash = "sha256-fh/s3rRef3hyK3dsXPML0zk0uWHX/SoR4ElOBkzGMc4=";
+ })
+ (pkgs.fetchurl {
+ name = "openal-soft-patch.zip";
+ url = "https://wrapdb.mesonbuild.com/v2/openal-soft_1.24.3-1/get_patch";
+ hash = "sha256-mmwWYizH3bCKga+qnESSXlR1GMlIXymmjRUC4OvlTRY=";
+ })
+
+ (pkgs.fetchurl {
+ name = "zlib.tar.gz";
+ url = "http://zlib.net/fossils/zlib-1.3.1.tar.gz";
+ hash = "sha256-mpOyt9/ax3zrpaVYpYDnRmfdb+3kWFuR7vtg8Dty3yM=";
+ })
+ (pkgs.fetchurl {
+ name = "zlib-patch.zip";
+ url = "https://wrapdb.mesonbuild.com/v2/zlib_1.3.1-2/get_patch";
+ hash = "sha256-nKzqAuERmWS8Uekt0jWbFN9yOjbP4N8ceNVdnJ8nY64=";
+ })
+
+ (pkgs.fetchurl {
+ name = "icu.tar.gz";
+ url = "https://github.com/unicode-org/icu/releases/download/release-77-1/icu4c-77_1-src.tgz";
+ hash = "sha256-WI5DH3cyfDkDH/u4hDwOO8EiwhE3RIX6h9xfP6/yQGE=";
+ })
+ (pkgs.fetchurl {
+ name = "icu-patch.zip";
+ url = "https://wrapdb.mesonbuild.com/v2/icu_77.1-3/get_patch";
+ hash = "sha256-3yOnIOzkzYx271C8r+DYAMjBEReAa/IH1llzqV8Nzqc=";
+ })
+
+ (pkgs.fetchurl {
+ name = "libxml2.tar.xz";
+ url = "https://download.gnome.org/sources/libxml2/2.12/libxml2-2.12.6.tar.xz";
+ hash = "sha256-iJxZOogaPbX92WzJMYyH3zTrZI7fxFgnKtRv1gc1P7s=";
+ })
+ (pkgs.fetchurl {
+ name = "libxml2-patch.zip";
+ url = "https://wrapdb.mesonbuild.com/v2/libxml2_2.12.6-1/get_patch";
+ hash = "sha256-d3vn/9xBZ+TQs/QF4TSSZky97y5s9vaojMVeyAESYUw=";
+ })
+
+ (pkgs.fetchgit {
+ name = "gperf";
+ url = "https://gitlab.freedesktop.org/tpm/gperf.git";
+ rev = "c24359b4eab86d71c655c3b3fc969f13aac879ce";
+ hash = "sha256-IbWwGTSFRTnPxGJHaQIA0ywvZOCec6gpdrobXtZ6TO4=";
+ deepClone = false;
+ })
+
+ (pkgs.fetchurl {
+ name = "brotli.tar.gz";
+ url = "https://github.com/google/brotli/archive/v1.1.0.tar.gz";
+ hash = "sha256-5yCmyilCi4A/StFlNxdx9TmPq6OX7fZ3iDehhZnqE/8=";
+ })
+ (pkgs.fetchurl {
+ name = "brotli-patch.zip";
+ url = "https://wrapdb.mesonbuild.com/v2/google-brotli_1.1.0-3/get_patch";
+ hash = "sha256-d+daO7AZpLZTGVoVrxfMZcVvuMztJRkVT85Oa7Q8dOI=";
+ })
+]
diff --git a/native/nix/mpv-config.nix b/native/nix/mpv-config.nix
new file mode 100644
index 0000000..e0855b9
--- /dev/null
+++ b/native/nix/mpv-config.nix
@@ -0,0 +1,20 @@
+{ pkgs
+}:
+let
+ mpv = { arch, hash }: rec {
+ source_url = "https://repoflow.silenium.dev/api/universal/personal/github-releases/shinchiro/mpv-winbuild-cmake/20260225/mpv-dev-${arch}-20260225-git-92ed2d2.7z";
+ source_hash = hash;
+ source_filename = pkgs.lib.lists.last (pkgs.lib.strings.split "/" source_url);
+ directory = builtins.elemAt (pkgs.lib.strings.split "." source_filename) 0;
+ };
+in
+{
+ "x86_64-windows" = mpv {
+ arch = "x86_64";
+ hash = "sha256-XSZqaJm4uxdaaFfJPFNnnwQJUxZ6K+1luD7bA+C0i2U=";
+ };
+ "aarch64-windows" = {
+ arch = "aarch64";
+ hash = "sha256-eDtgyMqU7adllvKzVfhg813/5vVqouN2Ly/H/T9zBNI=";
+ };
+}
diff --git a/native/src/cpp/util/Errors.cpp b/native/src/cpp/util/Errors.cpp
deleted file mode 100644
index 3627694..0000000
--- a/native/src/cpp/util/Errors.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-// Created by silenium-dev on 7/21/24.
-//
-
-#include
-#include
-#include
-
-extern "C" {
-JNIEXPORT jstring JNICALL Java_dev_silenium_multimedia_core_util_ErrorsKt_mpvErrorStringN(
- JNIEnv *env,
- jobject thiz,
- const jint error) {
- return env->NewStringUTF(mpv_error_string(error));
-}
-
-JNIEXPORT jstring JNICALL Java_dev_silenium_multimedia_core_util_ErrorsKt_glErrorStringN(
- JNIEnv *env,
- jobject thiz,
- const jint error) {
- switch (error) {
- case GL_NO_ERROR:
- return env->NewStringUTF("GL_NO_ERROR");
- case GL_INVALID_ENUM:
- return env->NewStringUTF("GL_INVALID_ENUM");
- case GL_INVALID_VALUE:
- return env->NewStringUTF("GL_INVALID_VALUE");
- case GL_INVALID_OPERATION:
- return env->NewStringUTF("GL_INVALID_OPERATION");
- case GL_INVALID_FRAMEBUFFER_OPERATION:
- return env->NewStringUTF("GL_INVALID_FRAMEBUFFER_OPERATION");
- case GL_OUT_OF_MEMORY:
- return env->NewStringUTF("GL_OUT_OF_MEMORY");
- case GL_STACK_UNDERFLOW:
- return env->NewStringUTF("GL_STACK_UNDERFLOW");
- case GL_STACK_OVERFLOW:
- return env->NewStringUTF("GL_STACK_OVERFLOW");
- default:
- return env->NewStringUTF("Unknown");
- }
-}
-}
diff --git a/native/src/cpp/helper/errors.cpp b/native/src/helper/errors.cpp
similarity index 91%
rename from native/src/cpp/helper/errors.cpp
rename to native/src/helper/errors.cpp
index 3774fdf..c23ba83 100644
--- a/native/src/cpp/helper/errors.cpp
+++ b/native/src/helper/errors.cpp
@@ -4,29 +4,28 @@
#include "errors.hpp"
-#include
#include
#include
-jobject boxedLong(JNIEnv *env, const long value) {
+jobject boxedLong(JNIEnv *env, const jlong value) {
const auto boxedClass = env->FindClass("java/lang/Long");
const auto constructor = env->GetMethodID(boxedClass, "", "(J)V");
return env->NewObject(boxedClass, constructor, value);
}
-jobject boxedDouble(JNIEnv *env, const double value) {
+jobject boxedDouble(JNIEnv *env, const jdouble value) {
const auto boxedClass = env->FindClass("java/lang/Double");
const auto constructor = env->GetMethodID(boxedClass, "", "(D)V");
return env->NewObject(boxedClass, constructor, value);
}
-jobject boxedBool(JNIEnv *env, const bool value) {
+jobject boxedBool(JNIEnv *env, const jboolean value) {
const auto boxedClass = env->FindClass("java/lang/Boolean");
const auto constructor = env->GetMethodID(boxedClass, "", "(Z)V");
return env->NewObject(boxedClass, constructor, value);
}
-jobject boxedInt(JNIEnv *env, const int value) {
+jobject boxedInt(JNIEnv *env, const jint value) {
const auto boxedClass = env->FindClass("java/lang/Integer");
const auto constructor = env->GetMethodID(boxedClass, "", "(I)V");
return env->NewObject(boxedClass, constructor, value);
@@ -38,13 +37,13 @@ jobject pair(JNIEnv *env, jobject first, jobject second) {
return env->NewObject(pairClass, constructor, first, second);
}
-jobject resultSuccess(JNIEnv *env, const long value, const long secondValue) {
+jobject resultSuccess(JNIEnv *env, const jlong value, const jlong secondValue) {
const auto boxed = boxedLong(env, value);
const auto boxed2 = boxedLong(env, secondValue);
return pair(env, boxed, boxed2);
}
-jobject resultSuccess(JNIEnv *env, const long value) {
+jobject resultSuccess(JNIEnv *env, const jlong value) {
const auto boxed = boxedLong(env, value);
return boxed;
}
@@ -53,12 +52,12 @@ jobject resultSuccess(JNIEnv *env, const char *value) {
return env->NewStringUTF(value);
}
-jobject resultSuccess(JNIEnv *env, const double value) {
+jobject resultSuccess(JNIEnv *env, const jdouble value) {
const auto boxed = boxedDouble(env, value);
return boxed;
}
-jobject resultSuccess(JNIEnv *env, const bool value) {
+jobject resultSuccess(JNIEnv *env, const jboolean value) {
const auto boxed = boxedBool(env, value);
return boxed;
}
@@ -106,6 +105,7 @@ jobject glResultFailure(JNIEnv *env, const char *operation, const GLenum returnC
const auto errorResult = env->NewObject(resultClass, resultConstructor, error);
return errorResult;
}
+
jobject vaResultFailure(JNIEnv *env, const char *operation, const int returnCode) {
const auto resultClass = env->FindClass("kotlin/Result$Failure");
const auto errorClass = env->FindClass("dev/silenium/multimedia/core/util/VAException");
diff --git a/native/src/cpp/helper/errors.hpp b/native/src/helper/errors.hpp
similarity index 52%
rename from native/src/cpp/helper/errors.hpp
rename to native/src/helper/errors.hpp
index 397aab5..5a7c3e7 100644
--- a/native/src/cpp/helper/errors.hpp
+++ b/native/src/helper/errors.hpp
@@ -7,24 +7,30 @@
#include
#include
+#include
std::string avErrorString(int error);
-jobject boxedLong(JNIEnv *env, long value);
+jobject boxedLong(JNIEnv *env, jlong value);
+jobject boxedDouble(JNIEnv *env, jdouble value);
-jobject boxedInt(JNIEnv *env, int value);
+jobject boxedBool(JNIEnv *env, jboolean value);
+jobject boxedInt(JNIEnv *env, jint value);
jobject pair(JNIEnv *env, jobject first, jobject second);
-jobject resultSuccess(JNIEnv *env, long value, long secondValue);
-jobject resultSuccess(JNIEnv *env, long value);
+jobject resultSuccess(JNIEnv *env, jlong value, jlong secondValue);
+jobject resultSuccess(JNIEnv *env, jlong value);
jobject resultSuccess(JNIEnv *env, const char *value);
-jobject resultSuccess(JNIEnv *env, double value);
-jobject resultSuccess(JNIEnv *env, bool value);
+jobject resultSuccess(JNIEnv *env, jdouble value);
+jobject resultSuccess(JNIEnv *env, jboolean value);
jobject resultSuccess(JNIEnv *env);
jobject resultSuccessNull();
+#ifdef TARGET_LINUX
jobject eglResultFailure(JNIEnv *env, const char *operation, long returnCode);
-jobject glResultFailure(JNIEnv *env, const char *operation, uint returnCode);
+#endif
+
+jobject glResultFailure(JNIEnv *env, const char *operation, GLenum returnCode);
jobject vaResultFailure(JNIEnv *env, const char *operation, int returnCode);
diff --git a/native/src/meson.build b/native/src/meson.build
new file mode 100644
index 0000000..2f8be65
--- /dev/null
+++ b/native/src/meson.build
@@ -0,0 +1,36 @@
+mpv_dep = mpv.get_variable('libmpv_dep')
+jni = dependency('jni')
+
+deps = [jni, mpv_dep]
+
+sources = [
+ 'compose-av',
+ 'util/Errors.cpp',
+ 'helper/errors.hpp',
+ 'helper/errors.cpp',
+ 'mpv/MPV.cpp',
+ 'mpv/mpv_platform.hpp',
+]
+
+os = host_machine.system()
+if os == 'linux'
+ egl = dependency('egl')
+ glx = dependency('glx')
+ x11 = dependency('x11')
+ va = dependency('libva')
+ deps += [egl, glx, x11, va]
+ sources += ['mpv/mpv_linux.cpp']
+ add_project_arguments('-DTARGET_LINUX', language: 'cpp')
+elif os == 'windows'
+ sources += ['mpv/mpv_windows.cpp']
+ cc = meson.get_compiler('cpp')
+ gl = cc.find_library('opengl32')
+ deps += [gl]
+ add_project_arguments('-DTARGET_WINDOWS', language: 'cpp')
+endif
+
+shared_library(
+ sources,
+ dependencies : deps,
+ cpp_args : ['-Wimplicit-fallthrough'],
+)
diff --git a/native/src/cpp/mpv/MPV.cpp b/native/src/mpv/MPV.cpp
similarity index 69%
rename from native/src/cpp/mpv/MPV.cpp
rename to native/src/mpv/MPV.cpp
index 547eeb6..2d36b76 100644
--- a/native/src/cpp/mpv/MPV.cpp
+++ b/native/src/mpv/MPV.cpp
@@ -3,21 +3,20 @@
//
#include "helper/errors.hpp"
+#include "mpv_platform.hpp"
-
-#include
#include
-#include
-#include
#include
#include
#include
#include
#include
#include
+#include
+#include
+#include
#include
-#include
#include
#include
@@ -28,7 +27,7 @@ auto fnptr_(Callable &&c, Ret (*)(Args...)) {
if (used) {
using type = decltype(storage);
storage.~type();
- new (&storage) type(std::forward(c));
+ new(&storage) type(std::forward(c));
}
used = true;
@@ -49,6 +48,12 @@ struct MpvContext {
mpv_handle *handle;
jobject object;
jmethodID method;
+
+ std::thread eventLoop;
+ std::mutex mtx;
+ std::condition_variable cv;
+ std::atomic wakeup{false};
+ std::atomic running{true};
};
jobject eventDataToJava(JNIEnv *env, mpv_event_property *prop) {
@@ -126,6 +131,7 @@ void eventCallback(JNIEnv *env, const mpv_event *event, jobject callback) {
return;
}
env->CallVoidMethod(callback, method, name, value);
+ break;
}
case MPV_EVENT_GET_PROPERTY_REPLY: {
const auto prop = static_cast(event->data);
@@ -152,6 +158,7 @@ void eventCallback(JNIEnv *env, const mpv_event *event, jobject callback) {
return;
}
env->CallVoidMethod(callback, method, static_cast(event->reply_userdata), value);
+ break;
}
case MPV_EVENT_SET_PROPERTY_REPLY: {
jobject value = nullptr;
@@ -172,6 +179,7 @@ void eventCallback(JNIEnv *env, const mpv_event *event, jobject callback) {
return;
}
env->CallVoidMethod(callback, method, static_cast(event->reply_userdata), value);
+ break;
}
case MPV_EVENT_COMMAND_REPLY: {
// const auto reply = static_cast(event->data);
@@ -193,26 +201,60 @@ void eventCallback(JNIEnv *env, const mpv_event *event, jobject callback) {
return;
}
env->CallVoidMethod(callback, method, static_cast(event->reply_userdata), value);
+ break;
}
default:
break;
}
}
-void handle_mpv_events(void *ctx) {
+// void handle_mpv_events(void *ctx) {
+// const auto context = static_cast(ctx);
+// JNIEnv *env;
+// const auto res = context->jvm->AttachCurrentThread(reinterpret_cast(&env), nullptr);
+// if (res != JNI_OK) {
+// std::cerr << "Failed to attach current thread" << std::endl;
+// return;
+// }
+// while (true) {
+// const auto event = mpv_wait_event(context->handle, 0);
+// if (event->event_id == MPV_EVENT_NONE) {
+// break;
+// }
+// eventCallback(env, event, context->object);
+// }
+// context->jvm->DetachCurrentThread();
+// }
+
+void handle_mpv_wakeup(void *ctx) {
const auto context = static_cast(ctx);
+ {
+ std::lock_guard lock(context->mtx);
+ context->wakeup = true;
+ }
+ context->cv.notify_one();
+}
+
+void mpv_event_loop(MpvContext *context) {
JNIEnv *env;
const auto res = context->jvm->AttachCurrentThread(reinterpret_cast(&env), nullptr);
if (res != JNI_OK) {
std::cerr << "Failed to attach current thread" << std::endl;
return;
}
- while (true) {
- const auto event = mpv_wait_event(context->handle, 0);
- if (event->event_id == MPV_EVENT_NONE) {
- break;
+ while (context->running) {
+ std::unique_lock lock(context->mtx);
+ context->cv.wait(lock, [&] { return context->wakeup.load() || !context->running.load(); });
+ context->wakeup = false;
+ lock.unlock();
+
+ if (!context->running.load()) break;
+
+ while (context->running) {
+ const auto event = mpv_wait_event(context->handle, 0);
+ if (event->event_id == MPV_EVENT_NONE) break;
+ eventCallback(env, event, context->object);
}
- eventCallback(env, event, context->object);
}
context->jvm->DetachCurrentThread();
}
@@ -220,18 +262,21 @@ void handle_mpv_events(void *ctx) {
extern "C" {
// Client
JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_createN(JNIEnv *env, jobject thiz) {
+ setlocale(LC_NUMERIC, "C");
const auto handle = mpv_create();
if (handle == nullptr) {
return mpvResultFailure(env, "mpv_create", MPV_ERROR_NOMEM);
}
- return resultSuccess(env, reinterpret_cast(handle));
+ return resultSuccess(env, reinterpret_cast(handle));
}
-JNIEXPORT void JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_destroyN(JNIEnv *env, jobject thiz, const jlong handle) {
+JNIEXPORT void JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_destroyN(
+ JNIEnv *env, jobject thiz, const jlong handle) {
mpv_terminate_destroy(reinterpret_cast(handle));
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setOptionStringN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, jstring value) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setOptionStringN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, jstring value) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
const char *valueStr = env->GetStringUTFChars(value, nullptr);
const auto ret = mpv_set_option_string(reinterpret_cast(handle), nameStr, valueStr);
@@ -243,7 +288,8 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setOptionS
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setCallbackN(JNIEnv *env, jobject thiz, const jlong handle_, jobject callback) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setCallbackN(
+ JNIEnv *env, jobject thiz, const jlong handle_, jobject callback) {
const auto handle = reinterpret_cast(handle_);
JavaVM *jvm;
if (const auto ret = env->GetJavaVM(&jvm); ret != JNI_OK) {
@@ -251,18 +297,27 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setCallbac
return mpvResultFailure(env, "GetJavaVM", MPV_ERROR_GENERIC);
}
const auto ctx = new MpvContext{jvm, handle, env->NewGlobalRef(callback)};
- mpv_set_wakeup_callback(handle, handle_mpv_events, ctx);
- return resultSuccess(env, reinterpret_cast(ctx));
+ mpv_set_wakeup_callback(handle, handle_mpv_wakeup, ctx);
+ // mpv_set_wakeup_callback(handle, handle_mpv_events, ctx);
+
+ std::thread eventLoopThread{mpv_event_loop, ctx};
+ ctx->eventLoop = std::move(eventLoopThread);
+ return resultSuccess(env, reinterpret_cast(ctx));
}
-JNIEXPORT void JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_unsetCallbackN(JNIEnv *env, jobject thiz, const jlong ctx) {
+JNIEXPORT void JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_unsetCallbackN(
+ JNIEnv *env, jobject thiz, const jlong ctx) {
const auto context = reinterpret_cast(ctx);
+ context->running = false;
+ context->cv.notify_all();
+ context->eventLoop.join();
mpv_set_wakeup_callback(context->handle, nullptr, nullptr);
env->DeleteGlobalRef(context->object);
delete context;
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_commandAsyncN(JNIEnv *env, jobject thiz, const jlong handle, jobjectArray args, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_commandAsyncN(
+ JNIEnv *env, jobject thiz, const jlong handle, jobjectArray args, const jlong replyUserdata) {
const auto size = env->GetArrayLength(args);
std::vector argv(size + 1);
for (auto i = 0; i < size; i++) {
@@ -282,7 +337,8 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_commandAsy
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_commandN(JNIEnv *env, jobject thiz, const jlong handle, jobjectArray args) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_commandN(
+ JNIEnv *env, jobject thiz, const jlong handle, jobjectArray args) {
const auto size = env->GetArrayLength(args);
std::vector argv(size + 1);
for (auto i = 0; i < size; i++) {
@@ -305,7 +361,8 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_commandN(J
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_commandStringN(JNIEnv *env, jobject thiz, const jlong handle, jstring command) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_commandStringN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring command) {
const char *commandStr = env->GetStringUTFChars(command, nullptr);
const auto ret = mpv_command_string(reinterpret_cast(handle), commandStr);
env->ReleaseStringUTFChars(command, commandStr);
@@ -315,9 +372,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_commandStr
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyStringAsyncN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyStringAsyncN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_get_property_async(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_STRING);
+ const auto ret = mpv_get_property_async(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_STRING);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_get_property_async string", ret);
@@ -325,9 +384,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyLongAsyncN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyLongAsyncN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_get_property_async(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_INT64);
+ const auto ret = mpv_get_property_async(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_INT64);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_get_property_async int64", ret);
@@ -335,9 +396,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyDoubleAsyncN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyDoubleAsyncN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_get_property_async(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_DOUBLE);
+ const auto ret = mpv_get_property_async(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_DOUBLE);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_get_property_async double", ret);
@@ -345,9 +408,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyFlagAsyncN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyFlagAsyncN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_get_property_async(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_FLAG);
+ const auto ret = mpv_get_property_async(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_FLAG);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_get_property_async flag", ret);
@@ -356,7 +421,8 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropert
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyStringN(JNIEnv *env, jobject thiz, const jlong handle, jstring name) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyStringN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
char *value;
const auto ret = mpv_get_property(reinterpret_cast(handle), nameStr, MPV_FORMAT_STRING, &value);
@@ -372,9 +438,10 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropert
return result;
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyLongN(JNIEnv *env, jobject thiz, const jlong handle, jstring name) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyLongN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- long value;
+ jlong value;
const auto ret = mpv_get_property(reinterpret_cast(handle), nameStr, MPV_FORMAT_INT64, &value);
env->ReleaseStringUTFChars(name, nameStr);
if (ret == MPV_ERROR_PROPERTY_UNAVAILABLE) {
@@ -386,7 +453,8 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropert
return resultSuccess(env, value);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyDoubleN(JNIEnv *env, jobject thiz, const jlong handle, jstring name) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyDoubleN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
double value;
const auto ret = mpv_get_property(reinterpret_cast(handle), nameStr, MPV_FORMAT_DOUBLE, &value);
@@ -400,9 +468,10 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropert
return resultSuccess(env, value);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyFlagN(JNIEnv *env, jobject thiz, const jlong handle, jstring name) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropertyFlagN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- bool value;
+ jboolean value;
const auto ret = mpv_get_property(reinterpret_cast(handle), nameStr, MPV_FORMAT_FLAG, &value);
env->ReleaseStringUTFChars(name, nameStr);
if (ret == MPV_ERROR_PROPERTY_UNAVAILABLE) {
@@ -414,10 +483,12 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_getPropert
return resultSuccess(env, value);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyStringAsyncN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, jstring value, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyStringAsyncN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, jstring value, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
auto valueStr = const_cast(env->GetStringUTFChars(value, nullptr));
- const auto ret = mpv_set_property_async(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_STRING, &valueStr);
+ const auto ret = mpv_set_property_async(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_STRING, &valueStr);
env->ReleaseStringUTFChars(name, nameStr);
env->ReleaseStringUTFChars(value, valueStr);
if (ret < 0) {
@@ -426,9 +497,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyLongAsyncN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, jlong value, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyLongAsyncN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, jlong value, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_set_property_async(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_INT64, &value);
+ const auto ret = mpv_set_property_async(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_INT64, &value);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_set_property_async int64", ret);
@@ -436,9 +509,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyDoubleAsyncN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, jdouble value, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyDoubleAsyncN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, jdouble value, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_set_property_async(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_DOUBLE, &value);
+ const auto ret = mpv_set_property_async(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_DOUBLE, &value);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_set_property_async double", ret);
@@ -446,9 +521,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyFlagAsyncN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, jboolean value, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyFlagAsyncN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, jboolean value, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_set_property_async(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_FLAG, &value);
+ const auto ret = mpv_set_property_async(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_FLAG, &value);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_set_property_async flag", ret);
@@ -456,7 +533,8 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyStringN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, jstring value) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyStringN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, jstring value) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
const char *valueStr = env->GetStringUTFChars(value, nullptr);
const auto ret = mpv_set_property_string(reinterpret_cast(handle), nameStr, valueStr);
@@ -468,7 +546,8 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyLongN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, jlong value) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyLongN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, jlong value) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
const auto ret = mpv_set_property(reinterpret_cast(handle), nameStr, MPV_FORMAT_INT64, &value);
env->ReleaseStringUTFChars(name, nameStr);
@@ -478,7 +557,8 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyDoubleN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, jdouble value) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyDoubleN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, jdouble value) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
const auto ret = mpv_set_property(reinterpret_cast(handle), nameStr, MPV_FORMAT_DOUBLE, &value);
env->ReleaseStringUTFChars(name, nameStr);
@@ -488,7 +568,8 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyFlagN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, jboolean value) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropertyFlagN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, jboolean value) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
const auto ret = mpv_set_property(reinterpret_cast(handle), nameStr, MPV_FORMAT_FLAG, &value);
env->ReleaseStringUTFChars(name, nameStr);
@@ -498,9 +579,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_setPropert
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePropertyStringN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePropertyStringN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_observe_property(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_STRING);
+ const auto ret = mpv_observe_property(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_STRING);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_observe_property", ret);
@@ -508,9 +591,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePro
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePropertyLongN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePropertyLongN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_observe_property(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_INT64);
+ const auto ret = mpv_observe_property(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_INT64);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_observe_property", ret);
@@ -518,9 +603,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePro
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePropertyDoubleN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePropertyDoubleN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_observe_property(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_DOUBLE);
+ const auto ret = mpv_observe_property(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_DOUBLE);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_observe_property", ret);
@@ -528,9 +615,11 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePro
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePropertyFlagN(JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePropertyFlagN(
+ JNIEnv *env, jobject thiz, const jlong handle, jstring name, const jlong replyUserdata) {
const char *nameStr = env->GetStringUTFChars(name, nullptr);
- const auto ret = mpv_observe_property(reinterpret_cast(handle), replyUserdata, nameStr, MPV_FORMAT_FLAG);
+ const auto ret = mpv_observe_property(reinterpret_cast(handle), replyUserdata, nameStr,
+ MPV_FORMAT_FLAG);
env->ReleaseStringUTFChars(name, nameStr);
if (ret < 0) {
return mpvResultFailure(env, "mpv_observe_property", ret);
@@ -538,14 +627,16 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_observePro
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_unobservePropertyN(JNIEnv *env, jobject thiz, const jlong handle, const jlong replyUserdata) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_unobservePropertyN(
+ JNIEnv *env, jobject thiz, const jlong handle, const jlong replyUserdata) {
if (const auto ret = mpv_unobserve_property(reinterpret_cast(handle), replyUserdata); ret < 0) {
return mpvResultFailure(env, "mpv_unobserve_property", ret);
}
return resultSuccess(env);
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_initializeN(JNIEnv *env, jobject thiz, const jlong handle) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_initializeN(
+ JNIEnv *env, jobject thiz, const jlong handle) {
if (const auto ret = mpv_initialize(reinterpret_cast(handle)); ret < 0) {
return mpvResultFailure(env, "mpv_initialize", ret);
}
@@ -555,23 +646,17 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_initialize
struct RenderContext {
mpv_render_context *handle;
jobject gref;
- Display *display;
+ std::shared_ptr platformContext;
};
// Rendering
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_createRenderN(JNIEnv *env, jobject thiz, const jlong mpvHandle, jobject self, const jboolean advancedControl) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_createRenderN(
+ JNIEnv *env, jobject thiz, const jlong mpvHandle, jobject self, const jboolean advancedControl) {
std::vector params{
- {MPV_RENDER_PARAM_API_TYPE, const_cast(MPV_RENDER_API_TYPE_OPENGL)},
+ {MPV_RENDER_PARAM_API_TYPE, const_cast(MPV_RENDER_API_TYPE_OPENGL)},
};
- Display *display{nullptr};
- if (const auto glxDisplay = glXGetCurrentDisplay(); glxDisplay != nullptr) {
- params.emplace_back(MPV_RENDER_PARAM_X11_DISPLAY, glxDisplay);
- } else if (const auto eglDisplay = eglGetCurrentDisplay(); eglDisplay != EGL_NO_DISPLAY) {
- // Compose always runs on X11
- display = XOpenDisplay(nullptr);
- params.emplace_back(MPV_RENDER_PARAM_X11_DISPLAY, display);
- }
+ const auto platformContext = populatePlatformMpvParams(env, params);
JavaVM *jvm;
if (const auto ret = env->GetJavaVM(&jvm); ret != JNI_OK) {
@@ -581,87 +666,91 @@ JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_createRend
const auto object = env->NewGlobalRef(self);
mpv_opengl_init_params gl_params{
- .get_proc_address = fnptr([jvm](void *opaque, const char *name) -> void * {
- const auto javaRender = static_cast(opaque);
- JNIEnv *jni_env;
- const auto res = jvm->AttachCurrentThread(reinterpret_cast(&jni_env), nullptr);
- if (res != JNI_OK) {
- std::cerr << "Failed to attach current thread" << std::endl;
- return nullptr;
- }
+ .get_proc_address = fnptr([jvm](void *opaque, const char *name) -> void * {
+ const auto javaRender = static_cast(opaque);
+ JNIEnv *jni_env;
+ const auto res = jvm->AttachCurrentThread(reinterpret_cast(&jni_env), nullptr);
+ if (res != JNI_OK) {
+ std::cerr << "Failed to attach current thread" << std::endl;
+ return nullptr;
+ }
- const auto glProcMethod = jni_env->GetMethodID(jni_env->GetObjectClass(javaRender), "getGlProc", "(Ljava/lang/String;)J");
- if (glProcMethod == nullptr) {
- std::cerr << "Method not found: getGlProc" << std::endl;
- return nullptr;
- }
+ const auto glProcMethod = jni_env->GetMethodID(jni_env->GetObjectClass(javaRender), "getGlProc",
+ "(Ljava/lang/String;)J");
+ if (glProcMethod == nullptr) {
+ std::cerr << "Method not found: getGlProc" << std::endl;
+ return nullptr;
+ }
- const auto nameStr = jni_env->NewStringUTF(name);
- const auto ret = jni_env->CallLongMethod(javaRender, glProcMethod, nameStr);
- jni_env->DeleteLocalRef(nameStr);
- jvm->DetachCurrentThread();
- return reinterpret_cast(ret);
- }),
- .get_proc_address_ctx = object,
+ const auto nameStr = jni_env->NewStringUTF(name);
+ const auto ret = jni_env->CallLongMethod(javaRender, glProcMethod, nameStr);
+ jni_env->DeleteLocalRef(nameStr);
+ jvm->DetachCurrentThread();
+ return reinterpret_cast(ret);
+ }),
+ .get_proc_address_ctx = object,
};
- params.emplace_back(MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_params);
+ params.push_back({MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_params});
int advControl = advancedControl ? 1 : 0;
- params.emplace_back(MPV_RENDER_PARAM_ADVANCED_CONTROL, &advControl);
- params.emplace_back(MPV_RENDER_PARAM_INVALID, nullptr);
+ params.push_back({MPV_RENDER_PARAM_ADVANCED_CONTROL, &advControl});
+ params.push_back({MPV_RENDER_PARAM_INVALID, nullptr});
mpv_render_context *handle{nullptr};
- if (const auto ret = mpv_render_context_create(&handle, reinterpret_cast(mpvHandle), params.data()); ret < 0) {
+ if (const auto ret = mpv_render_context_create(&handle, reinterpret_cast(mpvHandle), params.data());
+ ret < 0) {
env->DeleteGlobalRef(object);
return mpvResultFailure(env, "mpv_render_context_create", ret);
}
- const auto ctx = new RenderContext{handle, object, display};
+ const auto ctx = new RenderContext{handle, object, std::move(platformContext)};
mpv_render_context_set_update_callback(
- handle,
- fnptr([jvm](void *opaque) {
- const auto render_context = static_cast(opaque);
- JNIEnv *jni_env;
- const auto res = jvm->AttachCurrentThread(reinterpret_cast(&jni_env), nullptr);
- if (res != JNI_OK) {
- std::cerr << "Failed to attach current thread" << std::endl;
- return;
- }
- const auto updateMethod = jni_env->GetMethodID(jni_env->GetObjectClass(render_context->gref), "requestUpdate", "()V");
- if (updateMethod == nullptr) {
- std::cerr << "Method not found: requestUpdate" << std::endl;
- return;
- }
- jni_env->CallVoidMethod(render_context->gref, updateMethod);
- jvm->DetachCurrentThread();
- }),
- ctx);
+ handle,
+ fnptr([jvm](void *opaque) {
+ const auto render_context = static_cast(opaque);
+ JNIEnv *jni_env;
+ const auto res = jvm->AttachCurrentThread(reinterpret_cast(&jni_env), nullptr);
+ if (res != JNI_OK) {
+ std::cerr << "Failed to attach current thread" << std::endl;
+ return;
+ }
+ const auto updateMethod = jni_env->GetMethodID(jni_env->GetObjectClass(render_context->gref),
+ "requestUpdate", "()V");
+ if (updateMethod == nullptr) {
+ std::cerr << "Method not found: requestUpdate" << std::endl;
+ return;
+ }
+ jni_env->CallVoidMethod(render_context->gref, updateMethod);
+ jvm->DetachCurrentThread();
+ }),
+ ctx);
return resultSuccess(env, reinterpret_cast(ctx));
}
-JNIEXPORT void JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_destroyRenderN(JNIEnv *env, jobject thiz, const jlong handle) {
+JNIEXPORT void JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_destroyRenderN(
+ JNIEnv *env, jobject thiz, const jlong handle) {
const auto context = reinterpret_cast(handle);
mpv_render_context_free(context->handle);
env->DeleteGlobalRef(context->gref);
- if (context->display != nullptr) {
- XCloseDisplay(context->display);
- }
+ context->platformContext.reset();
delete context;
}
-JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_renderN(JNIEnv *env, jobject thiz, const jlong handle, const GLint fbo, const jint width, const jint height, const jint glInternalFormat) {
+JNIEXPORT jobject JNICALL Java_dev_silenium_multimedia_core_mpv_MPVKt_renderN(
+ JNIEnv *env, jobject thiz, const jlong handle, const GLint fbo, const jint width, const jint height,
+ const jint glInternalFormat) {
const auto context = reinterpret_cast(handle);
const auto flags = mpv_render_context_update(context->handle);
if (flags & MPV_RENDER_UPDATE_FRAME) {
mpv_opengl_fbo fboData{
- .fbo = fbo,
- .w = width,
- .h = height,
- .internal_format = glInternalFormat,
+ .fbo = fbo,
+ .w = width,
+ .h = height,
+ .internal_format = glInternalFormat,
};
int flipY{0};
mpv_render_param params[]{
- {MPV_RENDER_PARAM_OPENGL_FBO, &fboData},
- {MPV_RENDER_PARAM_FLIP_Y, &flipY},
- {MPV_RENDER_PARAM_INVALID, nullptr},
+ {MPV_RENDER_PARAM_OPENGL_FBO, &fboData},
+ {MPV_RENDER_PARAM_FLIP_Y, &flipY},
+ {MPV_RENDER_PARAM_INVALID, nullptr},
};
if (const auto ret = mpv_render_context_render(context->handle, params); ret < 0) {
return mpvResultFailure(env, "mpv_render_context_render", ret);
diff --git a/native/src/mpv/mpv_linux.cpp b/native/src/mpv/mpv_linux.cpp
new file mode 100644
index 0000000..abe29cd
--- /dev/null
+++ b/native/src/mpv/mpv_linux.cpp
@@ -0,0 +1,31 @@
+#include "mpv_platform.hpp"
+
+#include
+#include
+
+class LinuxMpvPlatformContext : public MpvPlatformContext {
+public:
+ explicit LinuxMpvPlatformContext(Display *x_display) : display(x_display) {
+ }
+
+ ~LinuxMpvPlatformContext() override {
+ if (display != nullptr) {
+ XCloseDisplay(display);
+ }
+ }
+
+private:
+ Display *display;
+};
+
+std::shared_ptr populatePlatformMpvParams(JNIEnv *env, std::vector ¶ms) {
+ Display *display{nullptr};
+ if (const auto glxDisplay = glXGetCurrentDisplay(); glxDisplay != nullptr) {
+ params.push_back({MPV_RENDER_PARAM_X11_DISPLAY, glxDisplay});
+ } else if (const auto eglDisplay = eglGetCurrentDisplay(); eglDisplay != EGL_NO_DISPLAY) {
+ // Compose always runs on X11
+ display = XOpenDisplay(nullptr);
+ params.push_back({MPV_RENDER_PARAM_X11_DISPLAY, display});
+ }
+ return std::make_shared(display);
+}
diff --git a/native/src/mpv/mpv_platform.hpp b/native/src/mpv/mpv_platform.hpp
new file mode 100644
index 0000000..51e6818
--- /dev/null
+++ b/native/src/mpv/mpv_platform.hpp
@@ -0,0 +1,16 @@
+#ifndef NATIVE_MPV_PLATFORM_HPP
+#define NATIVE_MPV_PLATFORM_HPP
+
+#include
+#include
+#include
+#include
+
+class MpvPlatformContext {
+public:
+ virtual ~MpvPlatformContext() = default;
+};
+
+std::shared_ptr populatePlatformMpvParams(JNIEnv *env, std::vector ¶ms);
+
+#endif //NATIVE_MPV_PLATFORM_HPP
diff --git a/native/src/mpv/mpv_windows.cpp b/native/src/mpv/mpv_windows.cpp
new file mode 100644
index 0000000..5fac235
--- /dev/null
+++ b/native/src/mpv/mpv_windows.cpp
@@ -0,0 +1,12 @@
+#include "mpv_platform.hpp"
+
+class WindowsMpvPlatformContext : public MpvPlatformContext {
+public:
+ WindowsMpvPlatformContext() = default;
+
+ ~WindowsMpvPlatformContext() override = default;
+};
+
+std::shared_ptr populatePlatformMpvParams(JNIEnv *env, std::vector ¶ms) {
+ return std::make_shared();
+}
diff --git a/native/src/util/Errors.cpp b/native/src/util/Errors.cpp
new file mode 100644
index 0000000..381c3c3
--- /dev/null
+++ b/native/src/util/Errors.cpp
@@ -0,0 +1,93 @@
+//
+// Created by silenium-dev on 7/21/24.
+//
+
+#include
+#include
+#include
+
+#ifdef TARGET_LINUX
+#include
+#include
+#define CASE_STR(value) case value: return #value;
+
+const char *eglGetErrorString(const long error) {
+ switch (error) {
+ CASE_STR(EGL_SUCCESS)
+ CASE_STR(EGL_NOT_INITIALIZED)
+ CASE_STR(EGL_BAD_ACCESS)
+ CASE_STR(EGL_BAD_ALLOC)
+ CASE_STR(EGL_BAD_ATTRIBUTE)
+ CASE_STR(EGL_BAD_CONTEXT)
+ CASE_STR(EGL_BAD_CONFIG)
+ CASE_STR(EGL_BAD_CURRENT_SURFACE)
+ CASE_STR(EGL_BAD_DISPLAY)
+ CASE_STR(EGL_BAD_SURFACE)
+ CASE_STR(EGL_BAD_MATCH)
+ CASE_STR(EGL_BAD_PARAMETER)
+ CASE_STR(EGL_BAD_NATIVE_PIXMAP)
+ CASE_STR(EGL_BAD_NATIVE_WINDOW)
+ CASE_STR(EGL_CONTEXT_LOST)
+ default:
+ return "Unknown";
+ }
+}
+
+#undef CASE_STR
+#endif
+
+extern "C" {
+JNIEXPORT jstring JNICALL Java_dev_silenium_multimedia_core_util_ErrorsKt_mpvErrorStringN(
+ JNIEnv *env,
+ jobject thiz,
+ const jint error) {
+ return env->NewStringUTF(mpv_error_string(error));
+}
+
+JNIEXPORT jstring JNICALL Java_dev_silenium_multimedia_core_util_ErrorsKt_glErrorStringN(
+ JNIEnv *env,
+ jobject thiz,
+ const jint error) {
+ switch (error) {
+ case GL_NO_ERROR:
+ return env->NewStringUTF("GL_NO_ERROR");
+ case GL_INVALID_ENUM:
+ return env->NewStringUTF("GL_INVALID_ENUM");
+ case GL_INVALID_VALUE:
+ return env->NewStringUTF("GL_INVALID_VALUE");
+ case GL_INVALID_OPERATION:
+ return env->NewStringUTF("GL_INVALID_OPERATION");
+#ifdef GL_INVALID_FRAMEBUFFER_OPERATION
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ return env->NewStringUTF("GL_INVALID_FRAMEBUFFER_OPERATION");
+#endif
+ case GL_OUT_OF_MEMORY:
+ return env->NewStringUTF("GL_OUT_OF_MEMORY");
+ case GL_STACK_UNDERFLOW:
+ return env->NewStringUTF("GL_STACK_UNDERFLOW");
+ case GL_STACK_OVERFLOW:
+ return env->NewStringUTF("GL_STACK_OVERFLOW");
+ default:
+ return env->NewStringUTF("Unknown");
+ }
+}
+
+#ifdef TARGET_LINUX
+
+JNIEXPORT jstring JNICALL Java_dev_silenium_multimedia_util_ErrorsKt_eglErrorStringN(
+ JNIEnv *env,
+ jobject thiz,
+ jint error
+) {
+ return env->NewStringUTF(eglGetErrorString(error));
+}
+
+JNIEXPORT jstring JNICALL Java_dev_silenium_multimedia_util_ErrorsKt_vaErrorStringN(
+ JNIEnv *env,
+ jobject thiz,
+ jint error
+) {
+ return env->NewStringUTF(vaErrorStr(error));
+}
+#endif
+}
diff --git a/native/subprojects.tpl/linux/mpv.wrap b/native/subprojects.tpl/linux/mpv.wrap
new file mode 100644
index 0000000..c70e148
--- /dev/null
+++ b/native/subprojects.tpl/linux/mpv.wrap
@@ -0,0 +1,5 @@
+[wrap-file]
+directory = mpv
+source_filename = mpv.tar
+patch_directory = mpv
+lead_directory_missing = true
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/expat.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/expat.wrap
new file mode 100644
index 0000000..cadc76f
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/expat.wrap
@@ -0,0 +1,7 @@
+[wrap-file]
+directory = expat
+source_filename = expat.tar
+lead_directory_missing = true
+
+[provide]
+expat = expat_dep
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/ffmpeg.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/ffmpeg.wrap
new file mode 100644
index 0000000..0c28952
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/ffmpeg.wrap
@@ -0,0 +1,8 @@
+[wrap-file]
+directory = ffmpeg
+source_filename = ffmpeg.tar
+patch_directory = ffmpeg
+lead_directory_missing = true
+
+[provide]
+dependency_names = libavcodec, libavfilter, libavformat, libavutil, libswresample, libswscale
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/fmt.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/fmt.wrap
new file mode 100644
index 0000000..e08b9c8
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/fmt.wrap
@@ -0,0 +1,9 @@
+[wrap-file]
+directory = fmt
+source_filename = fmt.tar
+method = cmake
+diff_files = fmt/pic.patch
+lead_directory_missing = true
+
+[provide]
+fmt = fmt_dep
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/freetype2.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/freetype2.wrap
new file mode 100644
index 0000000..0bffe56
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/freetype2.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+source_filename = freetype2.tar
+lead_directory_missing = true
+
+[provide]
+dependency_names = freetype2
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/fribidi.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/fribidi.wrap
new file mode 100644
index 0000000..2173e27
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/fribidi.wrap
@@ -0,0 +1,3 @@
+[wrap-file]
+source_filename = fribidi.tar
+lead_directory_missing = true
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/google-brotli.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/google-brotli.wrap
new file mode 100644
index 0000000..5d09fb2
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/google-brotli.wrap
@@ -0,0 +1,9 @@
+[wrap-file]
+directory = brotli
+source_filename = brotli.tar
+lead_directory_missing = true
+patch_filename = brotli-patch.zip
+
+[provide]
+program_names = brotli
+dependency_names = libbrotlicommon, libbrotlidec, libbrotlienc
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/harfbuzz.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/harfbuzz.wrap
new file mode 100644
index 0000000..3f66e07
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/harfbuzz.wrap
@@ -0,0 +1,7 @@
+[wrap-file]
+source_filename = harfbuzz.tar
+lead_directory_missing = true
+patch_directory = harfbuzz
+
+[provide]
+dependency_names = harfbuzz, harfbuzz-cairo, harfbuzz-gobject, harfbuzz-icu, harfbuzz-subset
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libass.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libass.wrap
new file mode 100644
index 0000000..1201ab8
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libass.wrap
@@ -0,0 +1,4 @@
+[wrap-file]
+source_filename = libass.tar
+lead_directory_missing = true
+patch_directory = libass
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libdbus.wrap.disabled b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libdbus.wrap.disabled
new file mode 100644
index 0000000..6e6b101
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libdbus.wrap.disabled
@@ -0,0 +1,8 @@
+[wrap-git]
+url = https://gitlab.freedesktop.org/dbus/dbus.git
+revision = dbus-1.16.2
+depth = 1
+clone-recursive = true
+
+[provide]
+dependency_names = dbus-1
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libdisplay-info.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libdisplay-info.wrap
new file mode 100644
index 0000000..3bd40ae
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libdisplay-info.wrap
@@ -0,0 +1,4 @@
+[wrap-file]
+source_filename = libdisplay-info.tar
+lead_directory_missing = true
+diff_files = libdisplay-info/hwdata.patch
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libjpeg-turbo.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libjpeg-turbo.wrap
new file mode 100644
index 0000000..4c397fd
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libjpeg-turbo.wrap
@@ -0,0 +1,7 @@
+[wrap-file]
+source_filename = libjpeg-turbo.tar
+lead_directory_missing = true
+patch_directory = libjpeg-turbo
+
+[provide]
+dependency_names = libjpeg, libturbojpeg
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libpipewire-0.3.wrap.disabled b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libpipewire-0.3.wrap.disabled
new file mode 100644
index 0000000..4691d10
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libpipewire-0.3.wrap.disabled
@@ -0,0 +1,8 @@
+[wrap-git]
+url = https://gitlab.freedesktop.org/pipewire/pipewire.git
+revision = 1.4.9
+depth = 1
+clone-recursive = true
+
+[provide]
+dependency_names = libpipewire-0.3
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libplacebo.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libplacebo.wrap
new file mode 100644
index 0000000..ea56418
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libplacebo.wrap
@@ -0,0 +1,3 @@
+[wrap-file]
+source_filename = libplacebo.tar
+lead_directory_missing = true
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libpng.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libpng.wrap
new file mode 100644
index 0000000..6b83165
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libpng.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+source_filename = libpng.tar
+lead_directory_missing = true
+
+[provide]
+libpng = libpng_dep
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libuchardet.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libuchardet.wrap
new file mode 100644
index 0000000..6fd029b
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/libuchardet.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+source_filename = libuchardet.tar
+lead_directory_missing = true
+
+[provide]
+uchardet = libuchardet_dep
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/openal-soft.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/openal-soft.wrap
new file mode 100644
index 0000000..e583a11
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/openal-soft.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+source_filename = openal-soft.tar
+lead_directory_missing = true
+
+[provide]
+openal = openal_dep
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/ffmpeg/collect_versions.sh b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/ffmpeg/collect_versions.sh
new file mode 100644
index 0000000..c620567
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/ffmpeg/collect_versions.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+for shared_object in "${MESON_SOURCE_ROOT}/${MESON_SUBDIR}"/lib/*.so.*.*.*; do
+ filename=$(basename "$shared_object")
+ lib_name=$(basename "${filename//\.so.*/}")
+ lib_name="${lib_name//lib/}"
+ lib_version=${filename//*.so\./}
+ echo "${lib_name}:${lib_version}"
+done
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/ffmpeg/meson.build b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/ffmpeg/meson.build
new file mode 100644
index 0000000..791782c
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/ffmpeg/meson.build
@@ -0,0 +1,26 @@
+project('ffmpeg', 'c')
+
+cc = meson.get_compiler('c')
+
+versions_command = run_command('bash', 'collect_versions.sh', capture : true, check : true)
+versions = versions_command.stdout().strip().split('\n')
+
+library_names = ['avcodec', 'avfilter', 'avformat', 'avutil', 'swresample', 'swscale']
+
+foreach lib_name : library_names
+ foreach version : versions
+ version_name = version.split(':')[0].strip()
+ version_value = version.split(':')[1].strip()
+
+ if version_name == lib_name
+ message(version)
+ dep = declare_dependency(
+ version : version_value,
+ dependencies : cc.find_library(lib_name, dirs : meson.current_source_dir() + '/lib'),
+ include_directories : include_directories('include'),
+ )
+ meson.override_dependency('lib' + lib_name, dep)
+ break
+ endif
+ endforeach
+endforeach
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/fmt/pic.patch b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/fmt/pic.patch
new file mode 100644
index 0000000..eca72f8
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/fmt/pic.patch
@@ -0,0 +1,13 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 3c05513..5306154 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -5,6 +5,8 @@ if (${CMAKE_VERSION} VERSION_LESS 3.12)
+ cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
+ endif ()
+
++set(CMAKE_POSITION_INDEPENDENT_CODE ON)
++
+ # Determine if fmt is built as a subproject (using add_subdirectory)
+ # or if it is the master project.
+ if (NOT DEFINED FMT_MASTER_PROJECT)
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/harfbuzz/subprojects/icu.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/harfbuzz/subprojects/icu.wrap
new file mode 100644
index 0000000..134a005
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/harfbuzz/subprojects/icu.wrap
@@ -0,0 +1,3 @@
+[wrap-file]
+directory = icu
+source_filename = icu.tar
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/libfontconfig.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/libfontconfig.wrap
new file mode 100644
index 0000000..3f1523c
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/libfontconfig.wrap
@@ -0,0 +1,8 @@
+[wrap-file]
+directory = libfontconfig
+source_filename = libfontconfig.tar
+lead_directory_missing = true
+patch_directory = libfontconfig
+
+[provide]
+fontconfig = fontconfig_dep
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles/libfontconfig/subprojects/gperf.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles/libfontconfig/subprojects/gperf.wrap
new file mode 100644
index 0000000..082a2a5
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles/libfontconfig/subprojects/gperf.wrap
@@ -0,0 +1,7 @@
+[wrap-file]
+directory=gperf
+source_filename=gperf.tar
+lead_directory_missing = true
+
+[provide]
+program_names=gperf
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles/libfontconfig/subprojects/libxml2.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles/libfontconfig/subprojects/libxml2.wrap
new file mode 100644
index 0000000..6576d03
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libass/subprojects/packagefiles/libfontconfig/subprojects/libxml2.wrap
@@ -0,0 +1,7 @@
+[wrap-file]
+directory = libxml2
+source_filename = libxml2.tar
+lead_directory_missing = true
+
+[provide]
+libxml-2.0 = libxml2_dep
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libdisplay-info/hwdata.patch b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libdisplay-info/hwdata.patch
new file mode 100644
index 0000000..5eed955
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libdisplay-info/hwdata.patch
@@ -0,0 +1,13 @@
+diff --git a/meson.build b/meson.build
+index 4012669..7bcf805 100644
+--- a/meson.build
++++ b/meson.build
+@@ -16,7 +16,7 @@ version_major = version.split('.')[0]
+ version_minor = version.split('.')[1]
+ assert(version_major == '0')
+
+-dep_hwdata = dependency('hwdata', required: false, native: true)
++dep_hwdata = dependency('hwdata', required: false)
+ if dep_hwdata.found()
+ hwdata_dir = dep_hwdata.get_variable(pkgconfig: 'pkgdatadir')
+ pnp_ids = files(hwdata_dir / 'pnp.ids')
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/LICENSE.build b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/LICENSE.build
new file mode 100644
index 0000000..b59833d
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/LICENSE.build
@@ -0,0 +1,19 @@
+Copyright (c) 2021 The Meson development team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/meson.build b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/meson.build
new file mode 100644
index 0000000..e84a024
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/meson.build
@@ -0,0 +1,150 @@
+project(
+ 'libjpeg-turbo',
+ 'c',
+ version : '3.1.2',
+ license : 'BSD-3-Clause AND IJG',
+ meson_version : '>= 0.64.0',
+)
+
+add_project_arguments('-D_POSIX_C_SOURCE=200809L', language : 'c')
+add_project_arguments('-D_DEFAULT_SOURCE', language : 'c')
+
+pkg = import('pkgconfig')
+python = import('python').find_installation()
+
+cc = meson.get_compiler('c')
+host_system = host_machine.system()
+host_cpu = host_machine.cpu_family()
+
+cdata = configuration_data()
+cdata.set('CMAKE_PROJECT_NAME', meson.project_name())
+cdata.set('VERSION', meson.project_version())
+cdata.set('COPYRIGHT_YEAR', '1991-2025')
+
+jpeg_lib_version = 80
+so_major = jpeg_lib_version / 10
+so_age = 3
+so_minor = 2
+so_version = '@0@.@1@.@2@'.format(so_major, so_age, so_minor)
+vs_defs = files('win/jpeg8.def')[0]
+
+version_arr = meson.project_version().split('.')
+version_major = version_arr[0]
+version_minor = version_arr[1]
+version_revision = version_arr[2]
+
+# Add padding to build an integer: 2.1.0 -> 2001000
+if version_major.to_int() < 10
+ version_major += '00'
+elif version_major.to_int() < 100
+ version_major += '0'
+endif
+
+if version_minor.to_int() < 10
+ version_minor += '00'
+elif version_minor.to_int() < 100
+ version_minor += '0'
+endif
+
+cdata.set('JPEG_LIB_VERSION', jpeg_lib_version)
+cdata.set(
+ 'LIBJPEG_TURBO_VERSION_NUMBER',
+ version_major + version_minor + version_revision,
+)
+
+cdata.set('C_ARITH_CODING_SUPPORTED', true)
+cdata.set('D_ARITH_CODING_SUPPORTED', true)
+
+check_headers = ['local.h', 'stddef.h', 'stdlib.h', 'sys/types.h']
+if cc.get_id() == 'msvc'
+ check_headers += 'intrin.h'
+endif
+
+foreach header : check_headers
+ have_header = cc.has_header(header)
+ cdata.set('HAVE_' + header.underscorify().to_upper(), have_header)
+ cdata.set('NEED_' + header.underscorify().to_upper(), have_header)
+endforeach
+
+has_memset = cc.has_header_symbol('string.h', 'memset')
+has_memcpy = cc.has_header_symbol('string.h', 'memcpy')
+cdata.set('NEED_BSD_STRINGS', not has_memset or not has_memcpy)
+
+size_t = cc.sizeof('size_t')
+unsigned_long = cc.sizeof('unsigned long')
+cdata.set('SIZE_T', size_t)
+cdata.set('HAVE_UNSIGNED_CHAR', cc.sizeof('unsigned char') != -1)
+cdata.set('HAVE_UNSIGNED_SHORT', cc.sizeof('unsigned short') != -1)
+if size_t == unsigned_long
+ cdata.set('HAVE_BUILTIN_CTZL', cc.has_function('__builtin_ctzl'))
+endif
+
+code = 'int main(void) { typedef struct undefined_structure *undef_struct_ptr; undef_struct_ptr ptr = 0; return ptr != 0; }'
+cdata.set('INCOMPLETE_TYPES_BROKEN', not cc.links(code))
+
+if meson.can_run_host_binaries()
+ code = '''
+ #include
+ #include
+ static int is_shifting_signed (long arg) {
+ long res = arg >> 4;
+ if (res == -0x7F7E80CL)
+ return 1; /* right shift is signed */
+ /* see if unsigned-shift hack will fix it. */
+ /* we can't just test exact value since it depends on width of long... */
+ res |= 0xFFFFFFFFL << (32-4);
+ if (res == -0x7F7E80CL)
+ return 0; /* right shift is unsigned */
+ printf(\"Right shift isn't acting as I expect it to.\\\\n\");
+ printf(\"I fear the JPEG software will not work at all.\\\\n\\\\n\");
+ return 0; /* try it with unsigned anyway */
+ }
+ int main (void) {
+ exit(is_shifting_signed(-0x7F7E80B1L));
+ }
+ '''
+ cdata.set('RIGHT_SHIFT_IS_UNSIGNED', cc.run(code).returncode() == 0)
+else
+ cdata.set('RIGHT_SHIFT_IS_UNSIGNED', false)
+endif
+
+p = run_command(
+ python,
+ '-c',
+ 'import datetime; print(datetime.datetime.now().strftime("%Y%m%d"))',
+ check : true,
+)
+cdata.set('BUILD', p.stdout().strip())
+
+if cc.get_id() == 'msvc'
+ cdata.set('THREAD_LOCAL', '__declspec(thread)')
+else
+ cdata.set('THREAD_LOCAL', '__thread')
+endif
+
+cdata.set('HIDDEN', cc.has_function_attribute('visibility:hidden') ? '__attribute__((visibility("hidden")))'
+ : '')
+
+if get_option('force_inline')
+ if cc.get_id() == 'msvc'
+ cdata.set('INLINE', '__forceinline')
+ else
+ cdata.set('INLINE', 'inline __attribute__((always_inline))')
+ endif
+else
+ cdata.set('INLINE', 'inline')
+endif
+
+if host_system == 'windows'
+ add_project_arguments(
+ '-DDLLDEFINE',
+ language : 'c',
+ )
+endif
+
+incdir = include_directories('src')
+
+subdir('simd')
+cdata.set('WITH_SIMD', have_simd)
+
+subdir('src')
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/meson_options.txt b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/meson_options.txt
new file mode 100644
index 0000000..41ed4bb
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/meson_options.txt
@@ -0,0 +1,20 @@
+option(
+ 'force_inline',
+ type : 'boolean',
+)
+option(
+ 'turbojpeg',
+ type : 'feature',
+)
+option(
+ 'simd',
+ type : 'feature',
+)
+option(
+ 'tests',
+ type : 'feature',
+)
+option(
+ 'neon-intrinsics',
+ type : 'feature',
+)
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/simd/arm/meson.build b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/simd/arm/meson.build
new file mode 100644
index 0000000..124b74a
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/simd/arm/meson.build
@@ -0,0 +1,6 @@
+neon_compat_h = configure_file(
+ input : 'neon-compat.h.in',
+ output : 'neon-compat.h',
+ format : 'cmake',
+ configuration : cdata_neon,
+)
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/simd/meson.build b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/simd/meson.build
new file mode 100644
index 0000000..7c2306d
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/simd/meson.build
@@ -0,0 +1,338 @@
+simd_src_x86_64 = [
+ 'x86_64/jccolor-avx2.asm',
+ 'x86_64/jccolor-sse2.asm',
+ 'x86_64/jcgray-avx2.asm',
+ 'x86_64/jcgray-sse2.asm',
+ 'x86_64/jchuff-sse2.asm',
+ 'x86_64/jcphuff-sse2.asm',
+ 'x86_64/jcsample-avx2.asm',
+ 'x86_64/jcsample-sse2.asm',
+ 'x86_64/jdcolor-avx2.asm',
+ 'x86_64/jdcolor-sse2.asm',
+ 'x86_64/jdmerge-avx2.asm',
+ 'x86_64/jdmerge-sse2.asm',
+ 'x86_64/jdsample-avx2.asm',
+ 'x86_64/jdsample-sse2.asm',
+ 'x86_64/jfdctflt-sse.asm',
+ 'x86_64/jfdctfst-sse2.asm',
+ 'x86_64/jfdctint-avx2.asm',
+ 'x86_64/jfdctint-sse2.asm',
+ 'x86_64/jidctflt-sse2.asm',
+ 'x86_64/jidctfst-sse2.asm',
+ 'x86_64/jidctint-avx2.asm',
+ 'x86_64/jidctint-sse2.asm',
+ 'x86_64/jidctred-sse2.asm',
+ 'x86_64/jquantf-sse2.asm',
+ 'x86_64/jquanti-avx2.asm',
+ 'x86_64/jquanti-sse2.asm',
+ 'x86_64/jsimdcpu.asm',
+]
+
+simd_src_i386 = [
+ 'i386/jccolor-avx2.asm',
+ 'i386/jccolor-mmx.asm',
+ 'i386/jccolor-sse2.asm',
+ 'i386/jcgray-avx2.asm',
+ 'i386/jcgray-mmx.asm',
+ 'i386/jcgray-sse2.asm',
+ 'i386/jchuff-sse2.asm',
+ 'i386/jcphuff-sse2.asm',
+ 'i386/jcsample-avx2.asm',
+ 'i386/jcsample-mmx.asm',
+ 'i386/jcsample-sse2.asm',
+ 'i386/jdcolor-avx2.asm',
+ 'i386/jdcolor-mmx.asm',
+ 'i386/jdcolor-sse2.asm',
+ 'i386/jdmerge-avx2.asm',
+ 'i386/jdmerge-mmx.asm',
+ 'i386/jdmerge-sse2.asm',
+ 'i386/jdsample-avx2.asm',
+ 'i386/jdsample-mmx.asm',
+ 'i386/jdsample-sse2.asm',
+ 'i386/jfdctflt-3dn.asm',
+ 'i386/jfdctflt-sse.asm',
+ 'i386/jfdctfst-mmx.asm',
+ 'i386/jfdctfst-sse2.asm',
+ 'i386/jfdctint-avx2.asm',
+ 'i386/jfdctint-mmx.asm',
+ 'i386/jfdctint-sse2.asm',
+ 'i386/jidctflt-3dn.asm',
+ 'i386/jidctflt-sse2.asm',
+ 'i386/jidctflt-sse.asm',
+ 'i386/jidctfst-mmx.asm',
+ 'i386/jidctfst-sse2.asm',
+ 'i386/jidctint-avx2.asm',
+ 'i386/jidctint-mmx.asm',
+ 'i386/jidctint-sse2.asm',
+ 'i386/jidctred-mmx.asm',
+ 'i386/jidctred-sse2.asm',
+ 'i386/jquant-3dn.asm',
+ 'i386/jquantf-sse2.asm',
+ 'i386/jquanti-avx2.asm',
+ 'i386/jquanti-sse2.asm',
+ 'i386/jquant-mmx.asm',
+ 'i386/jquant-sse.asm',
+ 'i386/jsimdcpu.asm',
+]
+
+simd_src_arm = files(
+ 'arm/jcgray-neon.c',
+ 'arm/jcphuff-neon.c',
+ 'arm/jcsample-neon.c',
+ 'arm/jdmerge-neon.c',
+ 'arm/jdsample-neon.c',
+ 'arm/jfdctfst-neon.c',
+ 'arm/jidctred-neon.c',
+ 'arm/jquanti-neon.c',
+)
+
+simd_opt = get_option('simd')
+have_simd = false
+simd = []
+
+if host_cpu in ['x86', 'x86_64']
+ have_simd = add_languages(
+ 'nasm',
+ required : simd_opt,
+ native : false,
+ )
+ if have_simd
+ add_project_arguments(
+ '-DPIC',
+ language : 'nasm',
+ )
+ x64 = host_cpu == 'x86_64'
+ # simulate upstream's "is ELF" check by excluding non-ELF OSes
+ if x64 and host_machine.system() not in ['cygwin', 'darwin', 'windows']
+ if cc.compiles(
+ '''
+ #if (__CET__ & 3) == 0
+ #error \"CET not enabled\"
+ #endif
+ int main(void) { return 0; }
+ ''',
+ )
+ add_project_arguments(
+ '-D__CET__',
+ language : 'nasm',
+ )
+ endif
+ endif
+ dir = x64 ? 'x86_64' : 'i386'
+ simd_src = x64 ? simd_src_x86_64 : simd_src_i386
+ simd = static_library(
+ 'simd',
+ dir / 'jsimd.c',
+ simd_src,
+ include_directories : [incdir, 'nasm', dir],
+ pic : get_option('default_library') != 'static',
+ )
+ endif
+elif host_cpu in ['arm', 'aarch64']
+ aarch = host_cpu == 'aarch64' ? 'aarch64' : 'aarch32'
+ dir = 'arm' / aarch
+ cdata_neon = configuration_data()
+
+ needs_softfp_for_intrinsics = host_cpu == 'arm' and cc.compiles(
+ '''
+ #if defined(__ARM_NEON__) || (!defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__))
+ #error \"Neon run-time auto-detection will not be used\"
+ #endif
+ #if __ARM_PCS_VFP == 1
+ #error \"float ABI = hard\"
+ #endif
+ #if __SOFTFP__ != 1
+ #error \"float ABI = softfp\"
+ #endif
+ int main(void) { return 0; }"
+ ''',
+ name : 'does not need softfp for Neon intrinsics',
+ )
+ neon_flags = []
+ if host_cpu == 'arm'
+ neon_flags += ['-mfpu=neon']
+ endif
+ if needs_softfp_for_intrinsics
+ neon_flags += ['-mfloatabi=softfp']
+ endif
+ if host_cpu == 'arm'
+ have_simd = cc.compiles(
+ '''#include
+ int main(int argc, char **argv) {
+ uint16x8_t input = vdupq_n_u16((uint16_t)argc);
+ uint8x8_t output = vmovn_u16(input);
+ return (int)output[0];
+ }''',
+ args : neon_flags,
+ name : 'supports Neon',
+ )
+ if not have_simd
+ if simd_opt.enabled()
+ error('SIMD extensions not available for this architecture')
+ else
+ warning('SIMD extensions not available for this architecture')
+ endif
+ endif
+ else
+ have_simd = true
+ endif
+
+ if have_simd
+ cdata_neon.set(
+ 'HAVE_VLD1_S16_X3',
+ cc.compiles(
+ '''#include
+ int main(int argc, char **argv) {
+ int16_t input[12];
+ int16x4x3_t output;
+ int i;
+ for (i = 0; i < 12; i++) input[i] = (int16_t)argc;
+ output = vld1_s16_x3(input);
+ vst3_s16(input, output);
+ return (int)input[0];
+ }''',
+ args : neon_flags,
+ name : 'supports vld1_s16_x3 intrinsic',
+ ),
+ )
+ cdata_neon.set(
+ 'HAVE_VLD1_U16_X2',
+ cc.compiles(
+ '''
+ #include
+ int main(int argc, char **argv) {
+ uint16_t input[8];
+ uint16x4x2_t output;
+ int i;
+ for (i = 0; i < 8; i++) input[i] = (uint16_t)argc;
+ output = vld1_u16_x2(input);
+ vst2_u16(input, output);
+ return (int)input[0];
+ }''',
+ args : neon_flags,
+ name : 'supports vld1_u16_x2 intrinsic',
+ ),
+ )
+ cdata_neon.set(
+ 'HAVE_VLD1Q_U8_X4',
+ cc.compiles(
+ '''
+ #include
+ int main(int argc, char **argv) {
+ uint8_t input[64];
+ uint8x16x4_t output;
+ int i;
+ for (i = 0; i < 64; i++) input[i] = (uint8_t)argc;
+ output = vld1q_u8_x4(input);
+ vst4q_u8(input, output);
+ return (int)input[0];
+ }''',
+ args : neon_flags,
+ name : 'supports vld1q_u8_x4 intrinsic',
+ ),
+ )
+
+ subdir('arm')
+ simd_src_arm += [neon_compat_h]
+
+ # GCC 11 and earlier and some older versions of Clang do not have a full or
+ # optimal set of Neon intrinsics, so for performance reasons, when using those
+ # compilers, we default to using the older GAS implementation of the Neon SIMD
+ # extensions for certain algorithms. The presence or absence of the three
+ # intrinsics we tested above is a reasonable proxy for this, except with GCC 10
+ # and 11.
+
+ default_neon_intrinsics = cdata_neon.get('HAVE_VLD1_S16_X3') and cdata_neon.get(
+ 'HAVE_VLD1_U16_X2',
+ ) and cdata_neon.get(
+ 'HAVE_VLD1Q_U8_X4',
+ ) and (
+ cc.get_id() != 'gcc' or cc.version().version_compare('>= 12.0.0')
+ )
+
+ neon_intrinsics = get_option('neon-intrinsics').disable_auto_if(
+ not default_neon_intrinsics,
+ ).allowed()
+
+ # It is possible to run compile checks on generated files, however,
+ # Meson versions earlier than 1.2.0 do not set the lookup path
+ # correctly, causing Python to fail opening it.
+ # https://github.com/mesonbuild/meson/issues/11983
+ if meson.version().version_compare('>= 1.2.0') and not neon_intrinsics
+ if (host_cpu == 'armv7')
+ gastest = '''
+ .text
+ .fpu neon
+ .arch armv7a
+ .object_arch armv4
+ .arm
+ pld [r0]
+ vmovn.u16 d0, q0
+ '''
+ else
+ gastest = '''
+ .text
+ MYVAR .req x0
+ movi v0.16b, #100
+ mov MYVAR, #100
+ .unreq MYVAR
+ '''
+ endif
+ # cc.compiles() can't pass inline assembly to the C compiler
+ # https://github.com/mesonbuild/meson/issues/12395
+ f = configure_file(
+ command : [
+ python,
+ '-c',
+ 'import sys; print(sys.argv[1])',
+ '@0@'.format(gastest),
+ ],
+ output : 'gastest.S',
+ capture : true,
+ )
+ if not cc.compiles(
+ f,
+ args : neon_flags,
+ name : 'can use the partial Neon SIMD intrinsics implementation',
+ )
+ neon_intrinsics = true
+ endif
+ endif
+
+ summary('Neon SIMD intrinsics', neon_intrinsics ? 'full' : 'partial')
+
+ if neon_intrinsics
+ add_project_arguments(
+ '-DNEON_INTRINSICS',
+ language : 'c',
+ )
+ simd_src_arm += files('arm/jccolor-neon.c', 'arm/jidctint-neon.c')
+ endif
+
+ if neon_intrinsics or host_cpu == 'aarch64'
+ simd_src_arm += files('arm/jidctfst-neon.c')
+ endif
+
+ if neon_intrinsics or host_cpu == 'arm'
+ simd_src_arm += files(
+ dir / 'jchuff-neon.c',
+ 'arm/jdcolor-neon.c',
+ 'arm/jfdctint-neon.c',
+ )
+ endif
+
+ if not neon_intrinsics
+ simd_src_arm += files(dir / 'jsimd_neon.S')
+ endif
+
+ simd = static_library(
+ 'simd',
+ simd_src_arm + files(dir / 'jsimd.c'),
+ pic : get_option('default_library') != 'static',
+ include_directories : [incdir, 'arm', dir],
+ c_args : neon_flags,
+ )
+ endif
+elif simd_opt.enabled()
+ error('SIMD enabled, but CPU family not supported')
+endif
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/src/meson.build b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/src/meson.build
new file mode 100644
index 0000000..b03a0f8
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/packagefiles/libjpeg-turbo/src/meson.build
@@ -0,0 +1,265 @@
+jconfig_h = configure_file(
+ input : 'jconfig.h.in',
+ output : 'jconfig.h',
+ format : 'cmake@',
+ configuration : cdata,
+)
+
+configure_file(
+ input : 'jconfigint.h.in',
+ output : 'jconfigint.h',
+ format : 'cmake@',
+ configuration : cdata,
+)
+
+configure_file(
+ input : 'jversion.h.in',
+ output : 'jversion.h',
+ format : 'cmake@',
+ configuration : cdata,
+)
+
+install_headers('jmorecfg.h', 'jerror.h', 'jpeglib.h', jconfig_h)
+
+sources = files(
+ 'jcapimin.c',
+ 'jchuff.c',
+ 'jcicc.c',
+ 'jcinit.c',
+ 'jclhuff.c',
+ 'jcmarker.c',
+ 'jcmaster.c',
+ 'jcomapi.c',
+ 'jcparam.c',
+ 'jcphuff.c',
+ 'jctrans.c',
+ 'jdapimin.c',
+ 'jdatadst.c',
+ 'jdatasrc.c',
+ 'jdhuff.c',
+ 'jdicc.c',
+ 'jdinput.c',
+ 'jdlhuff.c',
+ 'jdmarker.c',
+ 'jdmaster.c',
+ 'jdphuff.c',
+ 'jdtrans.c',
+ 'jerror.c',
+ 'jfdctflt.c',
+ 'jmemmgr.c',
+ 'jmemnobs.c',
+ 'jpeg_nbits.c',
+ 'wrapper/jcapistd-12.c',
+ 'wrapper/jcapistd-16.c',
+ 'wrapper/jcapistd-8.c',
+ 'wrapper/jccoefct-12.c',
+ 'wrapper/jccoefct-8.c',
+ 'wrapper/jccolor-12.c',
+ 'wrapper/jccolor-16.c',
+ 'wrapper/jccolor-8.c',
+ 'wrapper/jcdctmgr-12.c',
+ 'wrapper/jcdctmgr-8.c',
+ 'wrapper/jcdiffct-12.c',
+ 'wrapper/jcdiffct-16.c',
+ 'wrapper/jcdiffct-8.c',
+ 'wrapper/jclossls-12.c',
+ 'wrapper/jclossls-16.c',
+ 'wrapper/jclossls-8.c',
+ 'wrapper/jcmainct-12.c',
+ 'wrapper/jcmainct-16.c',
+ 'wrapper/jcmainct-8.c',
+ 'wrapper/jcprepct-12.c',
+ 'wrapper/jcprepct-16.c',
+ 'wrapper/jcprepct-8.c',
+ 'wrapper/jcsample-12.c',
+ 'wrapper/jcsample-16.c',
+ 'wrapper/jcsample-8.c',
+ 'wrapper/jdapistd-12.c',
+ 'wrapper/jdapistd-16.c',
+ 'wrapper/jdapistd-8.c',
+ 'wrapper/jdcoefct-12.c',
+ 'wrapper/jdcoefct-8.c',
+ 'wrapper/jdcolor-12.c',
+ 'wrapper/jdcolor-16.c',
+ 'wrapper/jdcolor-8.c',
+ 'wrapper/jddctmgr-12.c',
+ 'wrapper/jddctmgr-8.c',
+ 'wrapper/jddiffct-12.c',
+ 'wrapper/jddiffct-16.c',
+ 'wrapper/jddiffct-8.c',
+ 'wrapper/jdlossls-12.c',
+ 'wrapper/jdlossls-16.c',
+ 'wrapper/jdlossls-8.c',
+ 'wrapper/jdmainct-12.c',
+ 'wrapper/jdmainct-16.c',
+ 'wrapper/jdmainct-8.c',
+ 'wrapper/jdmerge-12.c',
+ 'wrapper/jdmerge-8.c',
+ 'wrapper/jdpostct-12.c',
+ 'wrapper/jdpostct-16.c',
+ 'wrapper/jdpostct-8.c',
+ 'wrapper/jdsample-12.c',
+ 'wrapper/jdsample-16.c',
+ 'wrapper/jdsample-8.c',
+ 'wrapper/jfdctfst-12.c',
+ 'wrapper/jfdctfst-8.c',
+ 'wrapper/jfdctint-12.c',
+ 'wrapper/jfdctint-8.c',
+ 'wrapper/jidctflt-12.c',
+ 'wrapper/jidctflt-8.c',
+ 'wrapper/jidctfst-12.c',
+ 'wrapper/jidctfst-8.c',
+ 'wrapper/jidctint-12.c',
+ 'wrapper/jidctint-8.c',
+ 'wrapper/jidctred-12.c',
+ 'wrapper/jidctred-8.c',
+ 'wrapper/jquant1-12.c',
+ 'wrapper/jquant1-8.c',
+ 'wrapper/jquant2-12.c',
+ 'wrapper/jquant2-8.c',
+ 'wrapper/jutils-12.c',
+ 'wrapper/jutils-16.c',
+ 'wrapper/jutils-8.c',
+)
+
+sources += files(
+ # TODO: `with_arith_dec` / `with_arith_enc` only.
+ 'jaricom.c',
+ # TODO: `with_arith_enc` only
+ 'jcarith.c',
+ # TODO: `with_arith_dec` only
+ 'jdarith.c',
+)
+
+jpeg = library(
+ 'jpeg',
+ sources,
+ link_whole : simd,
+ soversion : so_version,
+ vs_module_defs : vs_defs,
+ install : true,
+)
+
+pkg.generate(
+ jpeg,
+ description : 'A SIMD-accelerated JPEG codec that provides the libjpeg API',
+ name : 'libjpeg',
+)
+
+jpeg_dep = declare_dependency(
+ include_directories : incdir,
+ link_with : jpeg,
+)
+meson.override_dependency('libjpeg', jpeg_dep)
+
+if get_option('turbojpeg').allowed()
+ install_headers('turbojpeg.h')
+
+ turbojpeg = library(
+ 'turbojpeg',
+ sources,
+ files(
+ 'jdatadst-tj.c',
+ 'jdatasrc-tj.c',
+ 'rdbmp.c',
+ 'transupp.c',
+ 'turbojpeg.c',
+ 'wrapper/rdppm-12.c',
+ 'wrapper/rdppm-16.c',
+ 'wrapper/rdppm-8.c',
+ 'wrapper/wrppm-12.c',
+ 'wrapper/wrppm-16.c',
+ 'wrapper/wrppm-8.c',
+ 'wrbmp.c',
+ ),
+ c_args : ['-DBMP_SUPPORTED', '-DPPM_SUPPORTED'],
+ install : true,
+ link_whole : simd,
+ soversion : '0.4.0',
+ )
+
+ pkg.generate(
+ turbojpeg,
+ description : 'A SIMD-accelerated JPEG codec that provides the TurboJPEG API',
+ name : 'libturbojpeg',
+ )
+
+ turbojpeg_dep = declare_dependency(
+ include_directories : incdir,
+ link_with : turbojpeg,
+ )
+ meson.override_dependency('libturbojpeg', turbojpeg_dep)
+endif
+
+if get_option('tests').require(
+ get_option('turbojpeg').allowed(),
+ error_message : 'turbojpeg feature needed',
+).allowed()
+ tjunittest = executable(
+ 'tjunittest',
+ ['tjunittest.c', 'tjutil.c', 'md5/md5.c', 'md5/md5hl.c'],
+ dependencies : turbojpeg_dep,
+ )
+
+ foreach _test, _args : {
+ 'tjunittest' : '',
+ 'tjunittest-alloc' : '-alloc',
+ 'tjunittest-yuv' : '-yuv',
+ 'tjunittest-yuv-alloc' : '-yuv -alloc',
+ 'tjunittest-yuv-nopad' : '-yuv -noyuvpad',
+ 'tjunittest-lossless' : '-lossless',
+ 'tjunittest-lossless-alloc' : '-lossless -alloc',
+ 'tjunittest-bmp' : '-bmp',
+ 'tjunittest12' : '-precision 12',
+ 'tjunittest12-alloc' : '-precision 12 -alloc',
+ 'tjunittest12-lossless' : '-precision 12 -lossless',
+ 'tjunittest12-lossless-alloc' : '-precision 12 -lossless -alloc',
+ 'tjunittest12-bmp' : '-precision 12 -bmp',
+ 'tjunittest2-lossless' : '-precision 2',
+ 'tjunittest2-lossless-alloc' : '-precision 2 -alloc',
+ 'tjunittest2-bmp' : '-precision 2 -bmp',
+ 'tjunittest3-lossless' : '-precision 3',
+ 'tjunittest3-lossless-alloc' : '-precision 3 -alloc',
+ 'tjunittest3-bmp' : '-precision 3 -bmp',
+ 'tjunittest4-lossless' : '-precision 4',
+ 'tjunittest4-lossless-alloc' : '-precision 4 -alloc',
+ 'tjunittest4-bmp' : '-precision 4 -bmp',
+ 'tjunittest5-lossless' : '-precision 5',
+ 'tjunittest5-lossless-alloc' : '-precision 5 -alloc',
+ 'tjunittest5-bmp' : '-precision 5 -bmp',
+ 'tjunittest6-lossless' : '-precision 6',
+ 'tjunittest6-lossless-alloc' : '-precision 6 -alloc',
+ 'tjunittest6-bmp' : '-precision 6 -bmp',
+ 'tjunittest7-lossless' : '-precision 7',
+ 'tjunittest7-lossless-alloc' : '-precision 7 -alloc',
+ 'tjunittest7-bmp' : '-precision 7 -bmp',
+ 'tjunittest9-lossless' : '-precision 9',
+ 'tjunittest9-lossless-alloc' : '-precision 9 -alloc',
+ 'tjunittest9-bmp' : '-precision 9 -bmp',
+ 'tjunittest10-lossless' : '-precision 10',
+ 'tjunittest10-lossless-alloc' : '-precision 10 -alloc',
+ 'tjunittest10-bmp' : '-precision 10 -bmp',
+ 'tjunittest11-lossless' : '-precision 11',
+ 'tjunittest11-lossless-alloc' : '-precision 11 -alloc',
+ 'tjunittest11-bmp' : '-precision 11 -bmp',
+ 'tjunittest13-lossless' : '-precision 13',
+ 'tjunittest13-lossless-alloc' : '-precision 13 -alloc',
+ 'tjunittest13-bmp' : '-precision 13 -bmp',
+ 'tjunittest14-lossless' : '-precision 14',
+ 'tjunittest14-lossless-alloc' : '-precision 14 -alloc',
+ 'tjunittest14-bmp' : '-precision 14 -bmp',
+ 'tjunittest15-lossless' : '-precision 15',
+ 'tjunittest15-lossless-alloc' : '-precision 15 -alloc',
+ 'tjunittest15-bmp' : '-precision 15 -bmp',
+ 'tjunittest16-lossless' : '-precision 16',
+ 'tjunittest16-lossless-alloc' : '-precision 16 -alloc',
+ 'tjunittest16-bmp' : '-precision 16 -bmp',
+ }
+ test(
+ _test,
+ tjunittest,
+ args : _args.split(),
+ timeout : 120,
+ )
+ endforeach
+endif
diff --git a/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/zlib.wrap b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/zlib.wrap
new file mode 100644
index 0000000..a581b1e
--- /dev/null
+++ b/native/subprojects.tpl/linux/packagefiles/mpv/subprojects/zlib.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+source_filename = zlib.tar
+lead_directory_missing = true
+
+[provide]
+zlib = zlib_dep
diff --git a/native/subprojects.tpl/windows/packagefiles/mpv/meson.build b/native/subprojects.tpl/windows/packagefiles/mpv/meson.build
new file mode 100644
index 0000000..7c12e24
--- /dev/null
+++ b/native/subprojects.tpl/windows/packagefiles/mpv/meson.build
@@ -0,0 +1,13 @@
+project(
+ 'mpv', 'c',
+ meson_version : '>=1.2.0',
+)
+
+cc = meson.get_compiler('c')
+
+libmpv_dep = declare_dependency(
+ version : '2',
+ dependencies : cc.find_library('mpv', dirs : meson.current_source_dir()),
+ include_directories : include_directories('include'),
+)
+meson.override_dependency('libmpv', libmpv_dep)
diff --git a/native/subprojects.tpl/windows/packagefiles/mpv/meson.options b/native/subprojects.tpl/windows/packagefiles/mpv/meson.options
new file mode 100644
index 0000000..811b382
--- /dev/null
+++ b/native/subprojects.tpl/windows/packagefiles/mpv/meson.options
@@ -0,0 +1,3 @@
+option('libmpv', type: 'boolean', value: true)
+option('drm', type: 'feature', value: 'auto')
+option('openal', type: 'feature', value: 'auto')
diff --git a/native/thirdparty/ffmpeg.cmake b/native/thirdparty/ffmpeg.cmake
deleted file mode 100644
index cc8da95..0000000
--- a/native/thirdparty/ffmpeg.cmake
+++ /dev/null
@@ -1,71 +0,0 @@
-set(FFMPEG_URL "https://reposilite.silenium.dev/releases/dev/silenium/libs/ffmpeg/ffmpeg-natives-${NATIVE_PLATFORM}${FFMPEG_PLATFORM_EXTENSION}/${FFMPEG_VERSION}/ffmpeg-natives-${NATIVE_PLATFORM}${FFMPEG_PLATFORM_EXTENSION}-${FFMPEG_VERSION}.zip")
-set(FFMPEG_URL_SHA256 "https://reposilite.silenium.dev/releases/dev/silenium/libs/ffmpeg/ffmpeg-natives-${NATIVE_PLATFORM}${FFMPEG_PLATFORM_EXTENSION}/${FFMPEG_VERSION}/ffmpeg-natives-${NATIVE_PLATFORM}${FFMPEG_PLATFORM_EXTENSION}-${FFMPEG_VERSION}.zip.sha256")
-set(FFMPEG_PREFIX "${CMAKE_BINARY_DIR}/ffmpeg")
-message(STATUS "Downloading ffmpeg from ${FFMPEG_URL}")
-
-file(DOWNLOAD "${FFMPEG_URL_SHA256}" "${CMAKE_BINARY_DIR}/ffmpeg.zip.sha256")
-file(READ "${CMAKE_BINARY_DIR}/ffmpeg.zip.sha256" FFMPEG_SHA256)
-file(DOWNLOAD "${FFMPEG_URL}" "${CMAKE_BINARY_DIR}/ffmpeg.zip" EXPECTED_HASH SHA256=${FFMPEG_SHA256} SHOW_PROGRESS)
-file(ARCHIVE_EXTRACT INPUT "${CMAKE_BINARY_DIR}/ffmpeg.zip" DESTINATION "${FFMPEG_PREFIX}")
-
-set(FFMPEG_INCLUDE_DIR "${FFMPEG_PREFIX}/include")
-set(FFMPEG_LIB_DIR "${FFMPEG_PREFIX}/lib")
-set(FFMPEG_LIBRARIES
- aom
- crypto
- freetype
- mp3lame
- opencore-amrnb
- opencore-amrwb
- openh264
- opus
- sharpyuv
- speex
- srt
- ssl
- SvtAv1Dec
- SvtAv1Enc
- vo-amrwbenc
- vpx
- webp
- webpdemux
- webpmux
- x264
- x265
- xml2
- z
- zimg
- avcodec
- avdevice
- avfilter
- avformat
- avutil
- postproc
- swresample
- swscale)
-add_library(ffmpeg STATIC IMPORTED)
-
-set(FFMPEG_MRI "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg.mri")
-file(WRITE "${FFMPEG_MRI}" "CREATE libffmpeg.a\n")
-message(STATUS "Checking for ffmpeg libraries")
-foreach (FFMPEG_LIBRARY ${FFMPEG_LIBRARIES})
- set(LIB_PATH "${FFMPEG_LIB_DIR}/lib${FFMPEG_LIBRARY}.a")
- if (NOT EXISTS ${LIB_PATH})
- message(STATUS " ${FFMPEG_LIBRARY} not found")
- continue()
- endif ()
- message(STATUS " Found ${FFMPEG_LIBRARY}")
- file(APPEND "${FFMPEG_MRI}" "ADDLIB ${LIB_PATH}\n")
-endforeach ()
-file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg.mri" "SAVE\nEND\n")
-
-add_custom_target(ffmpeg_custom
- COMMAND ar -M < "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg.mri"
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/libffmpeg.a
-)
-add_dependencies(ffmpeg ffmpeg_custom)
-set_target_properties(ffmpeg PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/libffmpeg.a")
-
-target_include_directories(ffmpeg INTERFACE "${FFMPEG_INCLUDE_DIR}")
-target_link_options(ffmpeg INTERFACE "-Wl,-Bsymbolic")
diff --git a/native/thirdparty/mpv.cmake b/native/thirdparty/mpv.cmake
deleted file mode 100644
index 53612b2..0000000
--- a/native/thirdparty/mpv.cmake
+++ /dev/null
@@ -1,46 +0,0 @@
-set(MPV_URL "https://reposilite.silenium.dev/releases/dev/silenium/libs/mpv/mpv-natives-${NATIVE_PLATFORM}/${MPV_VERSION}/mpv-natives-${NATIVE_PLATFORM}-${MPV_VERSION}.zip")
-set(MPV_URL_SHA256 "https://reposilite.silenium.dev/releases/dev/silenium/libs/mpv/mpv-natives-${NATIVE_PLATFORM}/${MPV_VERSION}/mpv-natives-${NATIVE_PLATFORM}-${MPV_VERSION}.zip.sha256")
-set(MPV_PREFIX "${CMAKE_BINARY_DIR}/mpv")
-message(STATUS "Downloading mpv from ${MPV_URL}")
-
-file(DOWNLOAD "${MPV_URL_SHA256}" "${CMAKE_BINARY_DIR}/mpv.zip.sha256")
-file(READ "${CMAKE_BINARY_DIR}/mpv.zip.sha256" MPV_SHA256)
-file(DOWNLOAD "${MPV_URL}" "${CMAKE_BINARY_DIR}/mpv.zip" EXPECTED_HASH SHA256=${MPV_SHA256} SHOW_PROGRESS)
-file(ARCHIVE_EXTRACT INPUT "${CMAKE_BINARY_DIR}/mpv.zip" DESTINATION "${MPV_PREFIX}")
-
-set(MPV_INCLUDE_DIR "${MPV_PREFIX}/include")
-set(MPV_LIB_DIR "${MPV_PREFIX}/lib")
-set(MPV_LIBRARIES
- mpv
- ass
- placebo)
-add_library(mpv STATIC IMPORTED)
-
-set(MPV_MRI "${CMAKE_CURRENT_BINARY_DIR}/mpv.mri")
-file(WRITE "${MPV_MRI}" "CREATE libmpv.a\n")
-message(STATUS "Checking for mpv libraries")
-foreach (MPV_LIBRARY ${MPV_LIBRARIES})
- set(LIB_PATH "${MPV_LIB_DIR}/lib${MPV_LIBRARY}.a")
- if (NOT EXISTS ${LIB_PATH})
- message(STATUS " ${MPV_LIBRARY} not found")
- continue()
- endif ()
- message(STATUS " Found ${MPV_LIBRARY}")
- file(APPEND "${MPV_MRI}" "ADDLIB ${LIB_PATH}\n")
-endforeach ()
-file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/mpv.mri" "SAVE\nEND\n")
-
-add_custom_target(mpv_custom
- COMMAND ar -M < "${CMAKE_CURRENT_BINARY_DIR}/mpv.mri"
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/libmpv.a
-)
-add_dependencies(mpv mpv_custom)
-set_target_properties(mpv PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/libmpv.a")
-
-target_include_directories(mpv INTERFACE "${MPV_INCLUDE_DIR}")
-target_link_options(mpv INTERFACE "-Wl,-Bsymbolic")
-
-find_package(PkgConfig REQUIRED)
-pkg_check_modules(MPV_deps REQUIRED IMPORTED_TARGET libva libva-drm libdrm libva-glx libva-x11 libpipewire-0.3)
-target_link_libraries(mpv INTERFACE PkgConfig::MPV_deps)
diff --git a/settings.gradle.kts b/settings.gradle.kts
index d7fceb0..43c8ba3 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,12 +1,25 @@
+pluginManagement {
+ repositories {
+ maven("https://reposilite.silenium.dev/releases") {
+ name = "silenium-releases"
+ }
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
}
-val deployNative = if (extra.has("deploy.native")) {
- extra.get("deploy.native")?.toString()?.toBoolean() ?: true
+val deployKotlin = if (extra.has("deploy.kotlin")) {
+ extra.get("deploy.kotlin")?.toString()?.toBoolean() ?: true
} else true
-if (deployNative) {
+if (deployKotlin) {
include(":native")
}
rootProject.name = "compose-av"
+
+includeBuild("build-logic")
diff --git a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoPlayer.kt b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoPlayer.kt
index 1d7dcc5..1e1cec3 100644
--- a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoPlayer.kt
+++ b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoPlayer.kt
@@ -1,9 +1,8 @@
package dev.silenium.multimedia.compose.player
import androidx.compose.runtime.*
-import dev.silenium.compose.gl.surface.GLDrawScope
-import dev.silenium.compose.gl.surface.GLSurface
-import dev.silenium.compose.gl.surface.GLSurfaceState
+import dev.silenium.compose.gl.canvas.GLCanvasState
+import dev.silenium.compose.gl.canvas.GLDrawScope
import dev.silenium.multimedia.compose.util.deferredFlowStateOf
import dev.silenium.multimedia.compose.util.mapState
import dev.silenium.multimedia.core.annotation.InternalMultimediaApi
@@ -12,6 +11,7 @@ import org.lwjgl.opengl.GL30.*
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
+@Stable
class VideoPlayer(hwdec: Boolean = false) : AutoCloseable {
class Config(pixelPerfect: Boolean = false) {
var pixelPerfect by mutableStateOf(pixelPerfect)
@@ -19,7 +19,6 @@ class VideoPlayer(hwdec: Boolean = false) : AutoCloseable {
val config: Config = Config()
- internal var surface: GLSurface? = null
private var initialized = false
@PublishedApi
@@ -68,21 +67,21 @@ class VideoPlayer(hwdec: Boolean = false) : AutoCloseable {
return mpv
}
- private fun initialize(state: GLSurfaceState) {
+ private fun initialize(state: GLCanvasState, onInitialized: () -> Unit) {
if (initialized) return
render = mpv.createRender(advancedControl = true, state::requestUpdate)
initialized = true
+ onInitialized()
}
- fun onRender(scope: GLDrawScope, state: GLSurfaceState) {
- initialize(state)
+ fun onRender(scope: GLDrawScope, state: GLCanvasState, onInitialized: () -> Unit = {}) {
+ initialize(state, onInitialized)
// TODO: fix render block if screen is disconnected and reconnected
glClearColor(0f, 0f, 0f, 0f)
glClear(GL_COLOR_BUFFER_BIT)
render?.render(scope.fbo)?.getOrThrow()
- scope.redrawAfter(null)
}
override fun close() {
@@ -90,6 +89,11 @@ class VideoPlayer(hwdec: Boolean = false) : AutoCloseable {
mpv.close()
}
+ fun onCanvasDispose() {
+ render?.close()
+ render = null
+ }
+
companion object {
private val defaultOptions = mapOf(
"terminal" to "yes",
diff --git a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoPlayerControls.kt b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoPlayerControls.kt
index cf71d97..9ad74c3 100644
--- a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoPlayerControls.kt
+++ b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoPlayerControls.kt
@@ -4,7 +4,6 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
-import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -29,7 +28,6 @@ import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds
@OptIn(InternalMultimediaApi::class)
-@Preview
@Composable
fun VideoSurfaceControls(
player: VideoPlayer,
@@ -115,7 +113,7 @@ fun VideoSurfaceControls(
) {
AnimatedContent(paused, transitionSpec = {
ContentTransform(fadeIn(), fadeOut())
- }) { it ->
+ }) {
if (it != false) {
Icon(
Icons.Default.PlayArrow,
@@ -177,7 +175,7 @@ fun VideoSurfaceControls(
) {
AnimatedContent(fullscreenProvider.isFullscreen, transitionSpec = {
ContentTransform(fadeIn(), fadeOut())
- }) { it ->
+ }) {
if (it) {
Icon(
Icons.Default.FullscreenExit,
diff --git a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurface.kt b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurface.kt
index 428f50f..6a0071b 100644
--- a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurface.kt
+++ b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurface.kt
@@ -5,49 +5,50 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
-import androidx.compose.runtime.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
-import dev.silenium.compose.gl.surface.*
-import dev.silenium.multimedia.core.annotation.InternalMultimediaApi
+import dev.silenium.compose.gl.canvas.GLCanvas
+import dev.silenium.compose.gl.canvas.rememberGLCanvasState
import org.jetbrains.skia.Paint
-@Composable
-private fun createGLSurface(
- player: VideoPlayer,
- surfaceState: GLSurfaceState = rememberGLSurfaceState(),
- onInitialized: () -> Unit = {},
-): GLSurface {
- var initialized by remember { mutableStateOf(false) }
-
- @OptIn(InternalMultimediaApi::class)
- val dwidth by player.property("dwidth")
-
- @OptIn(InternalMultimediaApi::class)
- val dheight by player.property("dheight")
- val fboSizeOverride = remember(player.config.pixelPerfect, dwidth, dheight) {
- if (!player.config.pixelPerfect) return@remember null
- dwidth?.let { w ->
- dheight?.let { h ->
- FBOSizeOverride(w.toInt(), h.toInt())
- }
- }
- }
- return rememberGLSurface(
- surfaceState,
- presentMode = GLSurface.PresentMode.MAILBOX,
- swapChainSize = 3,
- fboSizeOverride = fboSizeOverride,
- draw = {
- player.onRender(this, surfaceState)
- if (!initialized) {
- initialized = true
- onInitialized()
- }
- }
- )
-}
+//@Composable
+//private fun createGLSurface(
+// player: VideoPlayer,
+// surfaceState: GLSurfaceState = rememberGLSurfaceState(),
+// onInitialized: () -> Unit = {},
+//): GLSurface {
+// var initialized by remember { mutableStateOf(false) }
+//
+// @OptIn(InternalMultimediaApi::class)
+// val dwidth by player.property("dwidth")
+//
+// @OptIn(InternalMultimediaApi::class)
+// val dheight by player.property("dheight")
+// val fboSizeOverride = remember(player.config.pixelPerfect, dwidth, dheight) {
+// if (!player.config.pixelPerfect) return@remember null
+// dwidth?.let { w ->
+// dheight?.let { h ->
+// FBOSizeOverride(w.toInt(), h.toInt())
+// }
+// }
+// }
+// return rememberGLSurface(
+// surfaceState,
+// presentMode = GLSurface.PresentMode.MAILBOX,
+// swapChainSize = 3,
+// fboSizeOverride = fboSizeOverride,
+// draw = {
+// player.onRender(this, surfaceState)
+// if (!initialized) {
+// initialized = true
+// onInitialized()
+// }
+// }
+// )
+//}
@Composable
fun VideoSurface(
@@ -55,14 +56,17 @@ fun VideoSurface(
showStats: Boolean = false,
modifier: Modifier = Modifier,
paint: Paint = Paint(),
+ onInitialized: () -> Unit = {},
) {
- val surfaceState = rememberGLSurfaceState()
+ val surfaceState = rememberGLCanvasState()
BoxWithConstraints(modifier = modifier) {
- GLSurfaceView(
- surface = player.surface!!,
+ GLCanvas(
modifier = Modifier.matchParentSize(),
- paint = paint,
- )
+ state = surfaceState,
+// onDispose = player::onCanvasDispose,
+ ) {
+ player.onRender(this, surfaceState, onInitialized)
+ }
if (showStats) {
Surface(
modifier = Modifier.padding(6.dp).width(360.dp),
@@ -77,19 +81,17 @@ fun VideoSurface(
@Composable
fun rememberVideoPlayer(
- surfaceState: GLSurfaceState = rememberGLSurfaceState(),
hwdec: Boolean = true,
- onInitialized: () -> Unit = {},
): VideoPlayer {
val player = remember { VideoPlayer(hwdec) }
- val surface = createGLSurface(player, surfaceState, onInitialized)
+// val surface = createGLSurface(player, surfaceState, onInitialized)
- DisposableEffect(player, surface) {
- player.surface = surface
- onDispose {
- player.surface = null
- player.close()
- }
- }
+// DisposableEffect(player, surface) {
+// player.surface = surface
+// onDispose {
+// player.surface = null
+// player.close()
+// }
+// }
return player
}
diff --git a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceEvents.kt b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceEvents.kt
index bf10d46..d385653 100644
--- a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceEvents.kt
+++ b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceEvents.kt
@@ -13,11 +13,12 @@ import dev.silenium.multimedia.core.annotation.InternalMultimediaApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-import kotlinx.datetime.Clock
-import kotlinx.datetime.Instant
+import kotlin.time.Clock
import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.ExperimentalTime
+import kotlin.time.Instant
-@OptIn(ExperimentalComposeUiApi::class, InternalMultimediaApi::class)
+@OptIn(ExperimentalComposeUiApi::class, InternalMultimediaApi::class, ExperimentalTime::class)
@Composable
fun Modifier.handleInputs(player: VideoPlayer, focusRequester: FocusRequester? = null): Modifier {
val coroutineScope = rememberCoroutineScope()
diff --git a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceStats.kt b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceStats.kt
index 57990eb..523cd5c 100644
--- a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceStats.kt
+++ b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceStats.kt
@@ -9,13 +9,13 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
-import dev.silenium.compose.gl.surface.GLSurfaceState
-import dev.silenium.compose.gl.surface.Stats
+import dev.silenium.compose.gl.canvas.GLCanvasState
+import dev.silenium.compose.gl.canvas.Stats
import dev.silenium.multimedia.compose.format.format
import dev.silenium.multimedia.compose.util.deferredFlowStateOf
@Composable
-fun VideoSurfaceStats(player: VideoPlayer, state: GLSurfaceState, textColor: Color = Color.White) {
+fun VideoSurfaceStats(player: VideoPlayer, state: GLCanvasState, textColor: Color = Color.White) {
Column(modifier = Modifier.padding(6.dp)) {
val position by deferredFlowStateOf(player::position)
val duration by deferredFlowStateOf(player::duration)
diff --git a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceWithControls.kt b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceWithControls.kt
index f237708..4dbb176 100644
--- a/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceWithControls.kt
+++ b/src/main/kotlin/dev/silenium/multimedia/compose/player/VideoSurfaceWithControls.kt
@@ -15,6 +15,7 @@ fun VideoSurfaceWithControls(
showStats: Boolean = false,
controlFocusRequester: FocusRequester? = null,
paint: Paint = Paint(),
+ onInitialized: () -> Unit = {},
) {
BoxWithConstraints(modifier) {
VideoSurface(
@@ -26,6 +27,7 @@ fun VideoSurfaceWithControls(
maxHeight = maxHeight,
),
paint = paint,
+ onInitialized = onInitialized,
)
VideoSurfaceControls(player, Modifier.matchParentSize(), controlFocusRequester)
}
diff --git a/src/main/kotlin/dev/silenium/multimedia/core/mpv/MPV.kt b/src/main/kotlin/dev/silenium/multimedia/core/mpv/MPV.kt
index 055c1aa..de6b5b6 100644
--- a/src/main/kotlin/dev/silenium/multimedia/core/mpv/MPV.kt
+++ b/src/main/kotlin/dev/silenium/multimedia/core/mpv/MPV.kt
@@ -400,6 +400,7 @@ class MPV : NativeCleanable, MPVAsyncListener {
}
override fun close() {
+ callback?.let(::unsetCallbackN)
initialized.set(false)
super.close()
}
diff --git a/src/main/kotlin/dev/silenium/multimedia/core/util/Errors.kt b/src/main/kotlin/dev/silenium/multimedia/core/util/Errors.kt
index 9b328db..ae16746 100644
--- a/src/main/kotlin/dev/silenium/multimedia/core/util/Errors.kt
+++ b/src/main/kotlin/dev/silenium/multimedia/core/util/Errors.kt
@@ -1,8 +1,5 @@
package dev.silenium.multimedia.core.util
-class AVException(val operation: String, val error: Int) :
- Exception("FFmpeg error during $operation: ${error.asAVErrorString()}")
-
class EGLException(val operation: String, val error: Long) :
Exception("EGL error during $operation: ${error.asEGLErrorString()}")
@@ -15,31 +12,19 @@ class VAException(val operation: String, val error: Int) :
class MPVException(val operation: String, val error: Int) :
Exception("MPV error during $operation: ${error.asMPVErrorString()}")
-fun Int.asAVError(operation: String = "ffmpeg call"): Exception = AVException(operation, this)
-fun Int.asAVErrorString(): String = avErrorStringN(this)
fun Long.asEGLError(operation: String = "EGL call"): Exception = EGLException(operation, this)
fun Long.asEGLErrorString(): String = eglErrorStringN(this)
+
fun Int.asGLError(operation: String = "GL call"): Exception = GLException(operation, this)
fun Int.asGLErrorString(): String = glErrorStringN(this)
+
fun Int.asVAError(operation: String = "VA call"): Exception = VAException(operation, this)
fun Int.asVAErrorString(): String = vaErrorStringN(this)
+
fun Int.asMPVError(operation: String = "MPV call"): Exception = MPVException(operation, this)
fun Int.asMPVErrorString(): String = mpvErrorStringN(this)
-private external fun avErrorStringN(error: Int): String
-private external fun eglErrorStringN(error: Long): String
+private external fun mpvErrorStringN(error: Int): String
private external fun glErrorStringN(error: Int): String
+private external fun eglErrorStringN(error: Long): String
private external fun vaErrorStringN(error: Int): String
-private external fun mpvErrorStringN(error: Int): String
-
-val Throwable.shouldIgnore
- get() = when (this) {
- is AVException -> when (error) {
- -11 -> true // EAGAIN
- -12 -> true // ENOMEM
- -541478725 -> true // AVERROR_EOF
- else -> false
- }
-
- else -> false
- }
diff --git a/src/main/kotlin/dev/silenium/multimedia/core/util/Natives.kt b/src/main/kotlin/dev/silenium/multimedia/core/util/Natives.kt
index b4a5cce..44e748d 100644
--- a/src/main/kotlin/dev/silenium/multimedia/core/util/Natives.kt
+++ b/src/main/kotlin/dev/silenium/multimedia/core/util/Natives.kt
@@ -1,15 +1,38 @@
package dev.silenium.multimedia.core.util
import dev.silenium.libs.jni.NativeLoader
+import dev.silenium.libs.jni.Platform
import dev.silenium.multimedia.build.BuildConstants
object Natives {
private var loaded = false
+ private val platformDeps = mapOf(
+ Platform.OS.LINUX to setOf(
+ "avcodec",
+ "avdevice",
+ "avfilter",
+ "avformat",
+ "avutil",
+ "compose-av",
+ "swresample",
+ "swscale",
+ ),
+ Platform.OS.WINDOWS to setOf(
+ "compose-av",
+ "libmpv-2",
+ ),
+ )
+
@Synchronized
fun ensureLoaded() {
if (!loaded) {
+ val deps = platformDeps[NativeLoader.nativePlatform.os]
+ ?: error("Unsupported platform: ${NativeLoader.nativePlatform}")
+ deps.forEach {
+ NativeLoader.loadLibraryFromClasspath(it).getOrThrow()
+ }
NativeLoader.loadLibraryFromClasspath(BuildConstants.LIBRARY_NAME).getOrThrow()
loaded = true
}
diff --git a/src/main/kotlin/dev/silenium/multimedia/core/util/Results.kt b/src/main/kotlin/dev/silenium/multimedia/core/util/Results.kt
deleted file mode 100644
index 6ed07d0..0000000
--- a/src/main/kotlin/dev/silenium/multimedia/core/util/Results.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package dev.silenium.multimedia.core.util
-
-fun Int.asUnitResult() = if (this != 0) {
- Result.failure(this.asAVError())
-} else {
- Result.success(Unit)
-}
diff --git a/src/test/kotlin/dev/silenium/multimedia/compose/Main.kt b/src/test/kotlin/dev/silenium/multimedia/compose/Main.kt
index e20ec19..39296b0 100644
--- a/src/test/kotlin/dev/silenium/multimedia/compose/Main.kt
+++ b/src/test/kotlin/dev/silenium/multimedia/compose/Main.kt
@@ -15,7 +15,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
-import androidx.compose.ui.window.awaitApplication
+import androidx.compose.ui.window.application
import dev.silenium.multimedia.compose.player.VideoSurfaceWithControls
import dev.silenium.multimedia.compose.player.rememberVideoPlayer
import dev.silenium.multimedia.compose.util.LocalFullscreenProvider
@@ -25,7 +25,6 @@ import java.nio.file.Files
import kotlin.io.path.absolutePathString
import kotlin.io.path.outputStream
import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
@OptIn(InternalMultimediaApi::class)
@Composable
@@ -40,11 +39,7 @@ fun App() {
}
var ready by remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()
- val player = rememberVideoPlayer(
- onInitialized = {
- ready = true
- },
- )
+ val player = rememberVideoPlayer()
DisposableEffect(Unit) {
onDispose {
ready = false
@@ -63,12 +58,12 @@ fun App() {
) {
var visible by remember { mutableStateOf(true) }
var wasPaused by remember { mutableStateOf("no") }
- LaunchedEffect(Unit) {
- while (isActive) {
- delay(2.seconds)
- visible = !visible
- }
- }
+// LaunchedEffect(Unit) {
+// while (isActive) {
+// delay(2.seconds)
+// visible = !visible
+// }
+// }
val modifier = when {
fullscreen -> Modifier.size(
this@BoxWithConstraints.maxWidth,
@@ -94,6 +89,9 @@ fun App() {
modifier = Modifier.fillParentMaxSize().animateItem(),
showStats = true,
controlFocusRequester = remember { FocusRequester() },
+ onInitialized = {
+ ready = true
+ },
)
DisposableEffect(Unit) {
coroutineScope.launch {
@@ -147,7 +145,7 @@ fun App() {
}
}
-suspend fun main(): Unit = awaitApplication {
+fun main(): Unit = application {
val state = LocalFullscreenProvider.current.windowState
Window(state = state, onCloseRequest = ::exitApplication) {
App()
diff --git a/src/test/kotlin/dev/silenium/multimedia/simple/Main.kt b/src/test/kotlin/dev/silenium/multimedia/simple/Main.kt
index 0b02a94..4594a7e 100644
--- a/src/test/kotlin/dev/silenium/multimedia/simple/Main.kt
+++ b/src/test/kotlin/dev/silenium/multimedia/simple/Main.kt
@@ -1,16 +1,14 @@
package dev.silenium.multimedia.simple
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.pager.VerticalPager
-import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.Window
-import androidx.compose.ui.window.awaitApplication
+import androidx.compose.ui.window.application
import java.nio.file.Files
import kotlin.io.path.outputStream
-suspend fun main() = awaitApplication {
+fun main() = application {
val file = remember {
val videoFile = Files.createTempFile("video", ".webm")
Thread.currentThread().contextClassLoader.getResourceAsStream("1080p.webm").use {
@@ -20,9 +18,6 @@ suspend fun main() = awaitApplication {
}
Window(onCloseRequest = this::exitApplication) {
- val state = rememberPagerState { 1000 }
- VerticalPager(state = state, modifier = Modifier.fillMaxSize(), beyondViewportPageCount = 2) {
- VideoPlayer(file = file, suspend = state.currentPage != it, modifier = Modifier.fillMaxSize())
- }
+ VideoPlayer(file = file, modifier = Modifier.fillMaxSize())
}
}
diff --git a/src/test/kotlin/dev/silenium/multimedia/simple/VideoPlayer.kt b/src/test/kotlin/dev/silenium/multimedia/simple/VideoPlayer.kt
index 272feb3..1490934 100644
--- a/src/test/kotlin/dev/silenium/multimedia/simple/VideoPlayer.kt
+++ b/src/test/kotlin/dev/silenium/multimedia/simple/VideoPlayer.kt
@@ -2,9 +2,8 @@ package dev.silenium.multimedia.simple
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
-import dev.silenium.compose.gl.surface.GLSurface
-import dev.silenium.compose.gl.surface.GLSurfaceView
-import dev.silenium.compose.gl.surface.rememberGLSurfaceState
+import dev.silenium.compose.gl.canvas.GLCanvas
+import dev.silenium.compose.gl.canvas.rememberGLCanvasState
import dev.silenium.multimedia.core.mpv.MPV
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@@ -42,8 +41,8 @@ fun VideoPlayer(file: Path, suspend: Boolean = false, modifier: Modifier = Modif
mpv.commandAsync("set", "pause", if (suspend) "yes" else "no").getOrThrow()
}
}
- val state = rememberGLSurfaceState()
- GLSurfaceView(state, modifier = modifier, presentMode = GLSurface.PresentMode.MAILBOX, swapChainSize = 3) {
+ val state = rememberGLCanvasState()
+ GLCanvas(state, modifier = modifier) {
if (!ready) {
render = mpv.createRender(advancedControl = true, state::requestUpdate)
ready = true