use another avif library that works on aarch64-linux

This commit is contained in:
Henry Hiles 2026-10-17 11:18:23 -04:00
commit 8180338d99
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
12 changed files with 93 additions and 196 deletions

View file

@ -16,23 +16,6 @@ subprojects {
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
if (name == "flutter_avif_android") {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
exclude("**/FlutterAvifPlugin.kt")
}
afterEvaluate {
extensions.configure<com.android.build.api.dsl.LibraryExtension> {
compileSdk = 36
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
}
}
}
}
subprojects {
project.evaluationDependsOn(":app")
}

View file

@ -1,8 +1,10 @@
import "dart:async";
import "dart:io";
import "dart:isolate";
import "dart:ui" as ui;
import "package:avif_native/avif_native.dart";
import "package:flutter/widgets.dart";
import "package:flutter_avif/flutter_avif.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client.dart";
import "package:nexus/models/requests/download_media.dart";
@ -24,13 +26,9 @@ class MxcImage(final WidgetRef ref, final DownloadMediaRequest request)
final bytes = await File(file.path).readAsBytes();
// Flutter can't decode AVIF on Linux/Windows (or older Android/iOS), so we do it ourselves
final avifType = isAvifFile(bytes.sublist(0, 16.clamp(0, bytes.length)));
if (avifType != .unknown) {
final codec = avifType == .avif
? SingleFrameAvifCodec(bytes: bytes)
: MultiFrameAvifCodec(key: hashCode, avifBytes: bytes);
await codec.ready();
return AvifCodecAdapter(codec);
if (String.fromCharCodes(bytes.skip(4).take(8))
case "ftypavif" || "ftypavis") {
return AvifCodec(await Isolate.run(() => decodeAvif(bytes)));
}
return decode(await .fromUint8List(bytes));
@ -44,21 +42,33 @@ class MxcImage(final WidgetRef ref, final DownloadMediaRequest request)
int get hashCode => request.hashCode;
}
class AvifCodecAdapter(final AvifCodec codec) implements ui.Codec {
@override
int get frameCount => codec.frameCount;
class AvifCodec(final AvifImageData data) implements ui.Codec {
var _frameIndex = 0;
@override
int get repetitionCount => frameCount > 1 ? -1 : 0;
int get frameCount => data.frames.length;
// libavif uses -2 for unknown, which we treat as infinite like browsers do
@override
int get repetitionCount =>
data.repetitionCount < 0 ? -1 : data.repetitionCount;
@override
Future<ui.FrameInfo> getNextFrame() async {
final frame = await codec.getNextFrame();
return AvifFrameInfo(frame.duration, frame.image);
Future<ui.FrameInfo> getNextFrame() {
final frame = data.frames[_frameIndex++ % frameCount];
final completer = Completer<ui.FrameInfo>();
ui.decodeImageFromPixels(
frame.rgba,
frame.width,
frame.height,
.rgba8888,
(image) => completer.complete(AvifFrameInfo(frame.duration, image)),
);
return completer.future;
}
@override
void dispose() => codec.dispose();
void dispose() {}
}
class AvifFrameInfo(

View file

@ -9,7 +9,6 @@
#include <app_links_linux/app_links_plugin_linux.h>
#include <dynamic_color/dynamic_color_plugin.h>
#include <file_selector_linux/file_selector_plugin.h>
#include <flutter_avif_linux/flutter_avif_linux_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h>
#include <pasteboard/pasteboard_plugin.h>
@ -28,9 +27,6 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) flutter_avif_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAvifLinuxPlugin");
flutter_avif_linux_plugin_register_with_registrar(flutter_avif_linux_registrar);
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);

View file

@ -6,7 +6,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
app_links_linux
dynamic_color
file_selector_linux
flutter_avif_linux
media_kit_libs_linux
media_kit_video
pasteboard

View file

@ -9,7 +9,6 @@ let
];
cmakeVersions = [ "3.22.1" ];
platformVersions = [
"31"
"34"
"36"
];
@ -48,17 +47,7 @@ pkgs.mkShell {
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${ANDROID_HOME}/build-tools/36.0.0/aapt2";
}
// lib.optionalAttrs pkgs.stdenv.isLinux (
let
arch = if pkgs.stdenv.hostPlatform.isAarch64 then "arm64" else "x64";
in
{
LD_LIBRARY_PATH = lib.concatStringsSep ":" [
"./build/native_assets/linux"
"./build/linux/${arch}/debug/bundle/lib"
"./build/linux/${arch}/release/bundle/lib"
(lib.makeLibraryPath [ pkgs.zlib ])
];
}
);
// lib.optionalAttrs pkgs.stdenv.isLinux {
LD_LIBRARY_PATH = "./build/native_assets/linux:${lib.makeLibraryPath [ pkgs.zlib ]}";
};
}

View file

@ -0,0 +1,49 @@
# avif_native's build hook downloads its prebuilt library, which the sandbox
# can't do, so bundle it and point the manifest at the local copy instead
{
stdenv,
fetchurl,
jq,
}:
let
artifacts = {
x86_64-linux = {
arch = "x64";
sha256 = "252b064f8ff4f0f265bc44294d4a7ed703dd7199688942c272dd8aa8d3039d5a";
};
aarch64-linux = {
arch = "arm64";
sha256 = "cdcf17cfd18b9b0f965b72615ddf11246121114577dc570d0bba25964e56c726";
};
};
inherit (artifacts.${stdenv.hostPlatform.system}) arch sha256;
key = "linux-${arch}-decode-dav1d";
in
{ version, src, ... }:
stdenv.mkDerivation {
pname = "avif_native";
inherit version src;
inherit (src) passthru;
nativeBuildInputs = [ jq ];
library = fetchurl {
url = "https://github.com/Zhangw1998/avif_native/releases/download/native-v0.1.0/${key}-libavif_native.so.gz";
inherit sha256;
};
# The hook checksums the library, so it must stay byte-identical
dontFixup = true;
installPhase = ''
runHook preInstall
cp -r . $out
chmod -R u+w $out
gunzip -c $library > $out/native/libavif_native.so
jq '.artifacts["${key}"].path = "libavif_native.so"' \
native/manifest.json > $out/native/manifest.json
runHook postInstall
'';
}

View file

@ -34,6 +34,8 @@ flutter.buildFlutterApplication {
autoPubspecLock = src + "/pubspec.lock";
customSourceBuilders.avif_native = callPackage ./avif_native.nix { };
gitHashes = {
linkify = "sha256-TpMD6+0zyY6i9l+6d8ErnVufmepCv362rCtnbOht/z4=";
fab_m3e = "sha256-5V9m6yY0jimx2gh7lQHYg4HDKQ4Wrl0ATmBhw5DkQMI=";

View file

@ -8,7 +8,6 @@ import Foundation
import app_links
import dynamic_color
import file_selector_macos
import flutter_avif_macos
import flutter_local_notifications
import media_kit_libs_macos_video
import media_kit_video
@ -16,7 +15,6 @@ import package_info_plus
import pasteboard
import screen_retriever_macos
import shared_preferences_foundation
import sqflite_darwin
import url_launcher_macos
import wakelock_plus
import webcrypto
@ -26,7 +24,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterAvifPlugin.register(with: registry.registrar(forPlugin: "FlutterAvifPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
@ -34,7 +31,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
WebcryptoPlugin.register(with: registry.registrar(forPlugin: "WebcryptoPlugin"))

View file

@ -105,6 +105,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.13.1"
avif_native:
dependency: "direct main"
description:
name: avif_native
sha256: "0a374a7ddc6e48d671792bd33efd9f46f199389cdcd42561210bd886facbc62e"
url: "https://pub.dev"
source: hosted
version: "0.1.1"
boolean_selector:
dependency: transitive
description:
@ -313,14 +321,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
exif:
dependency: transitive
description:
name: exif
sha256: a7980fdb3b7ffcd0b035e5b8a5e1eef7cadfe90ea6a4e85ebb62f87b96c7a172
url: "https://pub.dev"
source: hosted
version: "3.3.0"
fab_m3e:
dependency: "direct overridden"
description:
@ -455,70 +455,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_avif:
dependency: "direct main"
description:
name: flutter_avif
sha256: "6035f073189c1ae134affa73338c2eca79bf412e9abdb8f8628fdefcccff4444"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_avif_android:
dependency: transitive
description:
name: flutter_avif_android
sha256: "1da135ea0d74225fae3154f954f9ba4cf7aa5e56b30d9851fcfc0ddecab9d8c5"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_avif_ios:
dependency: transitive
description:
name: flutter_avif_ios
sha256: "3daaa599d8fe0193d3ade6cafa1fd4f166d4063b8d499c7c311ce1d170b2759f"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_avif_linux:
dependency: transitive
description:
name: flutter_avif_linux
sha256: dd6179edca12c720761b3acc678d7f15208eb8647adbac5a6a90f52d4ba56e2d
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_avif_macos:
dependency: transitive
description:
name: flutter_avif_macos
sha256: "8e39f365dfc5713d4527f07a92399184857ea30eced85f82171bfd0a45e2c73b"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_avif_platform_interface:
dependency: transitive
description:
name: flutter_avif_platform_interface
sha256: f5c110bdb7de2d4ab4902bfe2ec331fe24bc19b6d54f00ac4fa165effc0edcda
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_avif_web:
dependency: transitive
description:
name: flutter_avif_web
sha256: db2ff395b08cbfe9116e48ac65c428ba21a9159972ea019f8d7be580ff6cf6e8
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_avif_windows:
dependency: transitive
description:
name: flutter_avif_windows
sha256: e0c7722df305a6621f4ecdd83098025c705bcf0e8ce9519e03c6f515abcf351d
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_blurhash:
dependency: "direct main"
description:
@ -527,14 +463,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.9.1"
flutter_cache_manager:
dependency: transitive
description:
name: flutter_cache_manager
sha256: f94fc6f5480977e3eb7b622ed488f671ba96c971bd0e274e6bd0f0ba9b044816
url: "https://pub.dev"
source: hosted
version: "3.4.5"
flutter_file_dialog:
dependency: "direct main"
description:
@ -1265,14 +1193,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.6"
protobuf:
dependency: transitive
description:
name: protobuf
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
pub_semver:
dependency: transitive
description:
@ -1486,54 +1406,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.2"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "273218a1678360a3127fcf15e543381d9b813d413d783bc6e9a53eed4d2eb8fe"
url: "https://pub.dev"
source: hosted
version: "2.4.4+2"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: e4f659c14bdd14464127fcd234a7a96f61fe0f539b8568e5a38f99f5a568a835
url: "https://pub.dev"
source: hosted
version: "2.4.4+1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: b80e94ff0d551d5f1f8c7d14348d29b576cca3ae30f6e195a7c380552c32b1e0
url: "https://pub.dev"
source: hosted
version: "2.5.14"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "8de68fc306040f992aaf378750c16f95e4298c4bb150dd45091d75e42b1f08ee"
url: "https://pub.dev"
source: hosted
version: "2.4.4+1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: c9177b2f3150ed0647b90a94307c28452fe0a5401863044216aed83addd6c7c5
url: "https://pub.dev"
source: hosted
version: "2.4.2"
stack_trace:
dependency: transitive
description:

View file

@ -109,7 +109,7 @@ dependencies:
pasteboard: 0.5.0
flutter_file_dialog: 3.3.3
mime: 2.1.0
flutter_avif: 3.1.0
avif_native: 0.1.1
dev_dependencies:
build_runner: 2.16.2
flutter_lints: 6.0.0
@ -133,3 +133,8 @@ flutter_launcher_icons:
macos:
generate: true
image_path: assets/mobile.png
hooks:
user_defines:
avif_native:
features: decode

View file

@ -9,7 +9,6 @@
#include <app_links/app_links_plugin_c_api.h>
#include <dynamic_color/dynamic_color_plugin_c_api.h>
#include <file_selector_windows/file_selector_windows.h>
#include <flutter_avif_windows/flutter_avif_windows_plugin.h>
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <pasteboard/pasteboard_plugin.h>
@ -25,8 +24,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterAvifWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterAvifWindowsPlugin"));
MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
MediaKitVideoPluginCApiRegisterWithRegistrar(

View file

@ -6,7 +6,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
app_links
dynamic_color
file_selector_windows
flutter_avif_windows
media_kit_libs_windows_video
media_kit_video
pasteboard