mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-04 10:36:56 +00:00
feat: libmpv bridge for windows
This commit is contained in:
parent
ab9fdc571a
commit
64193ea410
6 changed files with 2031 additions and 4 deletions
|
|
@ -5,8 +5,11 @@ import org.gradle.api.file.RegularFileProperty
|
|||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.Sync
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
|
@ -151,6 +154,21 @@ fun readXcconfigValue(file: File, key: String): String? {
|
|||
|
||||
fun shellQuote(value: String): String = "'${value.replace("'", "'\"'\"'")}'"
|
||||
|
||||
fun cmdQuote(value: String): String = "\"${value.replace("\"", "\"\"")}\""
|
||||
|
||||
fun psSingleQuote(value: String): String = "'${value.replace("'", "''")}'"
|
||||
|
||||
fun semanticVersionSortKey(value: String): String =
|
||||
value.split('.', '-', '_')
|
||||
.joinToString(".") { part ->
|
||||
part.toIntOrNull()?.toString()?.padStart(8, '0') ?: part
|
||||
}
|
||||
|
||||
fun newestDirectory(root: File): File? =
|
||||
root.takeIf(File::exists)
|
||||
?.listFiles(File::isDirectory)
|
||||
?.maxByOrNull { semanticVersionSortKey(it.name) }
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.androidApplication)
|
||||
|
|
@ -211,6 +229,7 @@ val generateRuntimeConfigs = tasks.register<GenerateRuntimeConfigsTask>("generat
|
|||
}
|
||||
|
||||
val isMacHost = System.getProperty("os.name").contains("mac", ignoreCase = true)
|
||||
val isWindowsHost = System.getProperty("os.name").contains("win", ignoreCase = true)
|
||||
val mpvKitDir = providers.gradleProperty("nuvio.mpvkit.dir")
|
||||
.orElse(rootProject.layout.projectDirectory.dir("MPVKit").asFile.absolutePath)
|
||||
val macosPlayerBridgeSource = layout.projectDirectory.file("src/desktopMain/native/macos/player_bridge.mm")
|
||||
|
|
@ -312,6 +331,204 @@ val buildMacosPlayerBridge = tasks.register<Exec>("buildMacosPlayerBridge") {
|
|||
commandLine(macosPlayerBridgeCommand)
|
||||
}
|
||||
|
||||
val windowsPlayerBridgeArch = when (System.getProperty("os.arch").lowercase()) {
|
||||
"aarch64", "arm64" -> "arm64"
|
||||
"x86" -> "x86"
|
||||
else -> "x64"
|
||||
}
|
||||
val windowsPlayerBridgeSource = layout.projectDirectory.file("src/desktopMain/native/windows/player_bridge.cpp")
|
||||
val windowsPlayerBridgeOutput = layout.buildDirectory.file("native/windows/player_bridge.dll")
|
||||
val windowsPlayerBridgeImportLib = layout.buildDirectory.file("native/windows/player_bridge.lib")
|
||||
val windowsPlayerBridgePdb = layout.buildDirectory.file("native/windows/player_bridge.pdb")
|
||||
val windowsPlayerBridgeObj = layout.buildDirectory.file("native/windows/player_bridge.obj")
|
||||
val windowsPlayerBridgeScript = layout.buildDirectory.file("native/windows/build-player-bridge.bat")
|
||||
val windowsPlayerRuntimeOutput = layout.buildDirectory.dir("native/windows-runtime")
|
||||
if (isWindowsHost) {
|
||||
windowsPlayerBridgeOutput.get().asFile.parentFile.mkdirs()
|
||||
}
|
||||
val windowsWebView2Root = providers.gradleProperty("nuvio.webview2.dir").orNull
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?.let(::File)
|
||||
?: newestDirectory(File(System.getProperty("user.home"), ".nuget/packages/microsoft.web.webview2"))
|
||||
?: File("__missing_webview2__")
|
||||
val windowsWebView2IncludeDir = File(windowsWebView2Root, "build/native/include")
|
||||
val windowsWebView2NativeDir = File(windowsWebView2Root, "build/native/$windowsPlayerBridgeArch")
|
||||
val windowsWebView2LoaderLib = File(windowsWebView2NativeDir, "WebView2Loader.dll.lib")
|
||||
val windowsWebView2LoaderDll = File(windowsWebView2NativeDir, "WebView2Loader.dll")
|
||||
val windowsLibmpvRuntimeDir = providers.gradleProperty("nuvio.windows.libmpv.runtimeDir").orNull
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?.let(::File)
|
||||
?: listOf(
|
||||
File("C:/Program Files (x86)/Nuvio/app/native"),
|
||||
File("C:/Program Files/Nuvio/app/native"),
|
||||
).firstOrNull { File(it, "libmpv-2.dll").exists() }
|
||||
val windowsLibmpvDll = providers.gradleProperty("nuvio.windows.libmpv.dll").orNull
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?.let(::File)
|
||||
?: windowsLibmpvRuntimeDir?.resolve("libmpv-2.dll")
|
||||
?: listOf(
|
||||
File("C:/msys64/ucrt64/bin/libmpv-2.dll"),
|
||||
File("C:/msys64/mingw64/bin/libmpv-2.dll"),
|
||||
).firstOrNull(File::exists)
|
||||
val windowsVsWhere = File("C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe")
|
||||
val windowsVcvarsRelativePath = when (windowsPlayerBridgeArch) {
|
||||
"x86" -> "VC\\Auxiliary\\Build\\vcvars32.bat"
|
||||
"arm64" -> "VC\\Auxiliary\\Build\\vcvarsarm64.bat"
|
||||
else -> "VC\\Auxiliary\\Build\\vcvars64.bat"
|
||||
}
|
||||
val windowsVcvarsPath = providers.gradleProperty("nuvio.windows.vcvars.path").orNull
|
||||
?.takeIf { it.isNotBlank() }
|
||||
val windowsPlayerBridgeJavaHome = providers.systemProperty("java.home").get()
|
||||
val missingWindowsPlayerBridgeInputs = listOfNotNull(
|
||||
"WebView2.h".takeUnless { windowsWebView2IncludeDir.resolve("WebView2.h").exists() },
|
||||
"WebView2Loader.dll.lib".takeUnless { windowsWebView2LoaderLib.exists() },
|
||||
)
|
||||
val missingWindowsPlayerBridgeMessage = """
|
||||
Windows desktop player bridge inputs are missing: ${missingWindowsPlayerBridgeInputs.joinToString()}.
|
||||
Install the Microsoft.Web.WebView2 NuGet package or pass -Pnuvio.webview2.dir=C:/path/to/microsoft.web.webview2/version.
|
||||
libmpv is loaded at runtime; pass -Pnuvio.windows.libmpv.runtimeDir=C:/path/to/mpv-dlls to bundle it.
|
||||
""".trimIndent()
|
||||
val windowsPlayerBridgeCommand = if (missingWindowsPlayerBridgeInputs.isNotEmpty()) {
|
||||
listOf(
|
||||
"cmd",
|
||||
"/c",
|
||||
"echo ${missingWindowsPlayerBridgeMessage.replace("\n", " ")} 1>&2 && exit /b 1",
|
||||
)
|
||||
} else {
|
||||
val sourceFile = windowsPlayerBridgeSource.asFile
|
||||
val outputFile = windowsPlayerBridgeOutput.get().asFile
|
||||
val importLibFile = windowsPlayerBridgeImportLib.get().asFile
|
||||
val pdbFile = windowsPlayerBridgePdb.get().asFile
|
||||
val objFile = windowsPlayerBridgeObj.get().asFile
|
||||
val javaIncludeDir = File(windowsPlayerBridgeJavaHome, "include")
|
||||
val javaWin32IncludeDir = File(javaIncludeDir, "win32")
|
||||
val compileCommand = listOf(
|
||||
"cl",
|
||||
"/nologo",
|
||||
"/EHsc",
|
||||
"/std:c++17",
|
||||
"/LD",
|
||||
"/DUNICODE",
|
||||
"/D_UNICODE",
|
||||
"/DNOMINMAX",
|
||||
"/DWIN32_LEAN_AND_MEAN",
|
||||
"/permissive-",
|
||||
cmdQuote(sourceFile.absolutePath),
|
||||
"/I${cmdQuote(javaIncludeDir.absolutePath)}",
|
||||
"/I${cmdQuote(javaWin32IncludeDir.absolutePath)}",
|
||||
"/I${cmdQuote(windowsWebView2IncludeDir.absolutePath)}",
|
||||
"/Fo${cmdQuote(objFile.absolutePath)}",
|
||||
"/Fd${cmdQuote(pdbFile.absolutePath)}",
|
||||
"/Fe${cmdQuote(outputFile.absolutePath)}",
|
||||
"/link",
|
||||
"/NOLOGO",
|
||||
"/INCREMENTAL:NO",
|
||||
"/IMPLIB:${cmdQuote(importLibFile.absolutePath)}",
|
||||
cmdQuote(windowsWebView2LoaderLib.absolutePath),
|
||||
"Ole32.lib",
|
||||
"User32.lib",
|
||||
"Gdi32.lib",
|
||||
).joinToString(" ")
|
||||
val powershellCompileCommand = compileCommand.replace("\"", "__DQ__")
|
||||
val powershellCommand = """
|
||||
${'$'}ErrorActionPreference = 'Stop'
|
||||
${'$'}dq = [char]34
|
||||
${'$'}vcvars = ${psSingleQuote(windowsVcvarsPath.orEmpty())}
|
||||
if ([string]::IsNullOrWhiteSpace(${'$'}vcvars)) {
|
||||
${'$'}vswhere = ${psSingleQuote(windowsVsWhere.absolutePath)}
|
||||
if (Test-Path -LiteralPath ${'$'}vswhere) {
|
||||
${'$'}vcvars = & ${'$'}vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -find ${psSingleQuote(windowsVcvarsRelativePath)} | Select-Object -First 1
|
||||
}
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace(${'$'}vcvars) -or -not (Test-Path -LiteralPath ${'$'}vcvars)) {
|
||||
Write-Error 'Visual Studio C++ toolchain was not found. Install MSVC or pass -Pnuvio.windows.vcvars.path=C:\path\to\vcvars64.bat.'
|
||||
exit 1
|
||||
}
|
||||
${'$'}vcvars = ([string]${'$'}vcvars).Trim()
|
||||
${'$'}bat = ${psSingleQuote(windowsPlayerBridgeScript.get().asFile.absolutePath)}
|
||||
${'$'}compile = ${psSingleQuote(powershellCompileCommand)}.Replace('__DQ__', ${'$'}dq)
|
||||
${'$'}lines = @(
|
||||
'@echo off',
|
||||
('set {0}VCVARS={1}{0}' -f ${'$'}dq, ${'$'}vcvars),
|
||||
('call {0}%VCVARS%{0} >nul' -f ${'$'}dq),
|
||||
'if errorlevel 1 exit /b %errorlevel%',
|
||||
${'$'}compile,
|
||||
'exit /b %ERRORLEVEL%'
|
||||
)
|
||||
Set-Content -LiteralPath ${'$'}bat -Value ${'$'}lines -Encoding ASCII
|
||||
& cmd.exe /d /c ${'$'}bat
|
||||
${'$'}code = ${'$'}LASTEXITCODE
|
||||
if (${'$'}code -ne 0) { exit ${'$'}code }
|
||||
""".trimIndent()
|
||||
listOf(
|
||||
"powershell",
|
||||
"-NoProfile",
|
||||
"-ExecutionPolicy",
|
||||
"Bypass",
|
||||
"-Command",
|
||||
powershellCommand,
|
||||
)
|
||||
}
|
||||
val buildWindowsPlayerBridge = tasks.register<Exec>("buildWindowsPlayerBridge") {
|
||||
notCompatibleWithConfigurationCache("Builds a host-local player bridge against WebView2 and libmpv for Windows.")
|
||||
enabled = isWindowsHost
|
||||
inputs.file(windowsPlayerBridgeSource)
|
||||
if (windowsWebView2IncludeDir.exists()) {
|
||||
inputs.dir(windowsWebView2IncludeDir)
|
||||
}
|
||||
if (windowsWebView2LoaderLib.exists()) {
|
||||
inputs.file(windowsWebView2LoaderLib)
|
||||
}
|
||||
outputs.file(windowsPlayerBridgeOutput)
|
||||
outputs.file(windowsPlayerBridgeImportLib)
|
||||
outputs.file(windowsPlayerBridgePdb)
|
||||
commandLine(windowsPlayerBridgeCommand)
|
||||
}
|
||||
|
||||
val prepareWindowsPlayerRuntime = tasks.register<Sync>("prepareWindowsPlayerRuntime") {
|
||||
enabled = isWindowsHost
|
||||
into(windowsPlayerRuntimeOutput)
|
||||
if (windowsWebView2LoaderDll.exists()) {
|
||||
from(windowsWebView2LoaderDll)
|
||||
}
|
||||
when {
|
||||
windowsLibmpvRuntimeDir?.exists() == true -> {
|
||||
from(windowsLibmpvRuntimeDir) {
|
||||
include("*.dll")
|
||||
}
|
||||
}
|
||||
windowsLibmpvDll?.exists() == true -> {
|
||||
from(windowsLibmpvDll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val generateWindowsPlayerRuntimeIndex = tasks.register<GenerateNativeRuntimeIndexTask>("generateWindowsPlayerRuntimeIndex") {
|
||||
enabled = isWindowsHost
|
||||
dependsOn(prepareWindowsPlayerRuntime)
|
||||
runtimeDir.set(windowsPlayerRuntimeOutput)
|
||||
indexFile.set(windowsPlayerRuntimeOutput.map { it.file("runtime-files.txt") })
|
||||
}
|
||||
|
||||
abstract class GenerateNativeRuntimeIndexTask : DefaultTask() {
|
||||
@get:InputDirectory
|
||||
abstract val runtimeDir: DirectoryProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val indexFile: RegularFileProperty
|
||||
|
||||
@TaskAction
|
||||
fun generate() {
|
||||
val dir = runtimeDir.get().asFile
|
||||
val files = dir
|
||||
.listFiles { file -> file.isFile && file.name != indexFile.get().asFile.name }
|
||||
.orEmpty()
|
||||
.map { it.name }
|
||||
.sorted()
|
||||
indexFile.get().asFile.writeText(files.joinToString(separator = "\n", postfix = "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Jar>().configureEach {
|
||||
if (isMacHost && name == "desktopJar") {
|
||||
dependsOn(buildMacosPlayerBridge)
|
||||
|
|
@ -319,6 +536,43 @@ tasks.withType<Jar>().configureEach {
|
|||
into("native/macos")
|
||||
}
|
||||
}
|
||||
if (isWindowsHost && name == "desktopJar") {
|
||||
dependsOn(buildWindowsPlayerBridge, prepareWindowsPlayerRuntime, generateWindowsPlayerRuntimeIndex)
|
||||
from(windowsPlayerBridgeOutput) {
|
||||
into("native/windows")
|
||||
}
|
||||
from(windowsPlayerRuntimeOutput) {
|
||||
into("native/windows")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isWindowsHost) {
|
||||
val desktopNativePlayerTasks = setOf(
|
||||
"run",
|
||||
"runRelease",
|
||||
"desktopRun",
|
||||
"runDistributable",
|
||||
"runReleaseDistributable",
|
||||
"desktopRunHot",
|
||||
"hotRunDesktop",
|
||||
"hotRunDesktopAsync",
|
||||
"hotDevDesktop",
|
||||
"hotDevDesktopAsync",
|
||||
"createDistributable",
|
||||
"createReleaseDistributable",
|
||||
"createRuntimeImage",
|
||||
"package",
|
||||
"packageDistributionForCurrentOS",
|
||||
"packageMsi",
|
||||
"packageUberJarForCurrentOS",
|
||||
"packageReleaseDistributionForCurrentOS",
|
||||
"packageReleaseMsi",
|
||||
"packageReleaseUberJarForCurrentOS",
|
||||
)
|
||||
tasks.matching { it.name in desktopNativePlayerTasks }.configureEach {
|
||||
dependsOn(buildWindowsPlayerBridge, prepareWindowsPlayerRuntime, generateWindowsPlayerRuntimeIndex)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompilationTask<*>>().configureEach {
|
||||
|
|
@ -446,6 +700,7 @@ compose.desktop {
|
|||
"--add-opens=java.desktop/java.awt=ALL-UNNAMED",
|
||||
"--add-opens=java.desktop/sun.lwawt=ALL-UNNAMED",
|
||||
"--add-opens=java.desktop/sun.lwawt.macosx=ALL-UNNAMED",
|
||||
"--add-opens=java.desktop/sun.awt.windows=ALL-UNNAMED",
|
||||
smokePlayerUrl?.takeIf { it.isNotBlank() }?.let { "-Dnuvio.desktop.smokePlayerUrl=$it" },
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ actual fun PlatformPlayerSurface(
|
|||
onSnapshot: (PlayerPlaybackSnapshot) -> Unit,
|
||||
onError: (String?) -> Unit,
|
||||
) {
|
||||
if (DesktopHostOs.current == DesktopHostOs.MACOS) {
|
||||
if (DesktopHostOs.current == DesktopHostOs.MACOS || DesktopHostOs.current == DesktopHostOs.WINDOWS) {
|
||||
NativePlayerSurface(
|
||||
sourceUrl = sourceUrl,
|
||||
sourceHeaders = sourceHeaders,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ internal object AwtNativeViewResolver {
|
|||
fun resolveNativeViewPointer(component: Component): Long =
|
||||
when (DesktopHostOs.current) {
|
||||
DesktopHostOs.MACOS -> MacosAwtViewResolver.resolveNativeViewPointer(component)
|
||||
DesktopHostOs.WINDOWS -> WindowsAwtViewResolver.resolveNativeViewPointer(component)
|
||||
else -> error("Native desktop playback is not implemented for ${DesktopHostOs.current}.")
|
||||
}
|
||||
}
|
||||
|
|
@ -48,3 +49,34 @@ private object MacosAwtViewResolver {
|
|||
private fun invokeLong(target: Any, methodName: String): Long =
|
||||
(findMethod(target.javaClass, methodName).invoke(target) as Number).toLong()
|
||||
}
|
||||
|
||||
private object WindowsAwtViewResolver {
|
||||
private val componentPeerField: Field by lazy {
|
||||
Component::class.java.getDeclaredField("peer").apply { isAccessible = true }
|
||||
}
|
||||
|
||||
fun resolveNativeViewPointer(component: Component): Long {
|
||||
val peer = componentPeerField.get(component)
|
||||
?: error("AWT component peer is not ready for native playback.")
|
||||
|
||||
val pointer = invokeLong(peer, "getHWnd")
|
||||
if (pointer == 0L) {
|
||||
error("Windows AWT HWND pointer was zero.")
|
||||
}
|
||||
return pointer
|
||||
}
|
||||
|
||||
private fun findMethod(type: Class<*>, name: String): Method {
|
||||
var current: Class<*>? = type
|
||||
while (current != null) {
|
||||
runCatching {
|
||||
return current.getDeclaredMethod(name).apply { isAccessible = true }
|
||||
}
|
||||
current = current.superclass
|
||||
}
|
||||
error("Method $name was not found on ${type.name}.")
|
||||
}
|
||||
|
||||
private fun invokeLong(target: Any, methodName: String): Long =
|
||||
(findMethod(target.javaClass, methodName).invoke(target) as Number).toLong()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,13 +80,14 @@ internal object NativePlayerBridge {
|
|||
|
||||
private fun loadNativeLibrary() {
|
||||
val platform = DesktopHostOs.current
|
||||
require(platform == DesktopHostOs.MACOS) {
|
||||
require(platform == DesktopHostOs.MACOS || platform == DesktopHostOs.WINDOWS) {
|
||||
"Native desktop playback is not implemented for $platform yet."
|
||||
}
|
||||
|
||||
val libraryName = nativeLibraryName(platform)
|
||||
val platformDir = nativeDirectoryName(platform)
|
||||
findLocalBuildLibrary(platformDir, libraryName)?.let { localLibrary ->
|
||||
copyLocalRuntimeResources(platformDir, localLibrary.parentFile)
|
||||
System.load(localLibrary.absolutePath)
|
||||
return
|
||||
}
|
||||
|
|
@ -98,12 +99,44 @@ internal object NativePlayerBridge {
|
|||
val suffix = libraryName.substringAfter("player_bridge", ".dylib")
|
||||
val file = Files.createTempFile(dir.toPath(), "player-bridge-", suffix).toFile()
|
||||
file.deleteOnExit()
|
||||
extractBundledRuntimeResources(platformDir, dir)
|
||||
input.use { source ->
|
||||
file.outputStream().use { target -> source.copyTo(target) }
|
||||
}
|
||||
System.load(file.absolutePath)
|
||||
}
|
||||
|
||||
private fun extractBundledRuntimeResources(platformDir: String, dir: File) {
|
||||
val runtimeNames = bundledRuntimeResourceNames(platformDir)
|
||||
runtimeNames.forEach { name ->
|
||||
val resource = "/native/$platformDir/$name"
|
||||
val input = NativePlayerBridge::class.java.getResourceAsStream(resource) ?: return@forEach
|
||||
val target = dir.resolve(name)
|
||||
input.use { source ->
|
||||
target.outputStream().use { output -> source.copyTo(output) }
|
||||
}
|
||||
target.deleteOnExit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun bundledRuntimeResourceNames(platformDir: String): List<String> {
|
||||
val indexResource = "/native/$platformDir/runtime-files.txt"
|
||||
val indexed = NativePlayerBridge::class.java.getResourceAsStream(indexResource)
|
||||
?.bufferedReader()
|
||||
?.useLines { lines ->
|
||||
lines.map(String::trim)
|
||||
.filter { it.isNotEmpty() && !it.startsWith("#") }
|
||||
.toList()
|
||||
}
|
||||
.orEmpty()
|
||||
if (indexed.isNotEmpty()) return indexed
|
||||
|
||||
return when (platformDir) {
|
||||
"windows" -> listOf("libmpv-2.dll")
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun findLocalBuildLibrary(platformDir: String, libraryName: String): File? {
|
||||
val candidates = listOf(
|
||||
File("composeApp/build/native/$platformDir/$libraryName"),
|
||||
|
|
@ -112,6 +145,21 @@ internal object NativePlayerBridge {
|
|||
return candidates.firstOrNull { it.exists() }
|
||||
}
|
||||
|
||||
private fun copyLocalRuntimeResources(platformDir: String, targetDir: File) {
|
||||
val runtimeDirs = listOf(
|
||||
File("composeApp/build/native/$platformDir-runtime"),
|
||||
File("build/native/$platformDir-runtime"),
|
||||
)
|
||||
runtimeDirs.firstOrNull(File::isDirectory)
|
||||
?.listFiles { file -> file.isFile }
|
||||
?.forEach { runtimeFile ->
|
||||
val target = targetDir.resolve(runtimeFile.name)
|
||||
if (runtimeFile.absolutePath != target.absolutePath) {
|
||||
runCatching { runtimeFile.copyTo(target, overwrite = true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun nativeDirectoryName(platform: DesktopHostOs): String =
|
||||
when (platform) {
|
||||
DesktopHostOs.MACOS -> "macos"
|
||||
|
|
@ -163,7 +211,7 @@ internal object NativePlayerBridge {
|
|||
}
|
||||
|
||||
internal fun preloadNativePlayerBridgeAsync() {
|
||||
if (DesktopHostOs.current == DesktopHostOs.MACOS) {
|
||||
if (DesktopHostOs.current == DesktopHostOs.MACOS || DesktopHostOs.current == DesktopHostOs.WINDOWS) {
|
||||
NativePlayerBridge.preloadAsync()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1687
composeApp/src/desktopMain/native/windows/player_bridge.cpp
Normal file
1687
composeApp/src/desktopMain/native/windows/player_bridge.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -2446,7 +2446,12 @@
|
|||
|
||||
const send = (type, value = 0) => {
|
||||
const bridge = window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.player;
|
||||
if (bridge) bridge.postMessage({ type, value });
|
||||
if (bridge) {
|
||||
bridge.postMessage({ type, value });
|
||||
return;
|
||||
}
|
||||
const webViewBridge = window.chrome && window.chrome.webview;
|
||||
if (webViewBridge) webViewBridge.postMessage({ type, value });
|
||||
};
|
||||
|
||||
const animationDelay = ms => new Promise(resolve => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue