xue_hua_video_player 1.5.1
xue_hua_video_player: ^1.5.1 copied to clipboard
Cross-platform Flutter video player plugin decoding local/network video with GStreamer (native C core + Dart FFI) and rendering into Flutter external Texture widgets.
xue_hua_video_player #
English | 简体中文
A cross-platform Flutter video player plugin that decodes local and network
video with GStreamer (native C core + Dart FFI) and renders into
Flutter external Texture widgets via a custom native bridge (GStreamer
appsink on Apple/desktop; glimagesink + SurfaceProducer on Android).
- Repository: https://github.com/Matkurban/xue_hua_video_player
- Author: Matkurban <3496354336@qq.com>
Supported platforms: Android, iOS, macOS, Windows, Linux.
Scope: video playback only — open / play / pause / stop / seek / volume / mute / speed / looping, plus state / position / duration / resolution / buffering / EOS / error reporting. It does not do recording, streaming (as a server), or subtitle-track selection.
Table of contents #
- Features
- Platform support
- Installation
- Quick start
- Integrating into your app (read this first)
- Permissions & platform configuration
- Apple Release / FFI symbols (iOS & macOS)
- API reference
- Native GStreamer setup (per platform)
- Architecture
- Troubleshooting
- Maintainers
- License
Features #
- Local files, Flutter assets, and network URLs (
http(s)://,rtsp://, ...). - Play / pause / stop / seek / looping.
- Volume, mute, and playback speed control.
- Reactive state via fine-grained
signals: state, position, duration, video size, aspect ratio, buffering %, volume, speed, looping, muted, and errors. - A drop-in
XueHuaVideoViewwidget with a built-in, auto-hiding, themeable control bar (Material / Cupertino / adaptive). - GPU-friendly video via Flutter
Texture(Android GL intoSurfaceProducer; Apple/desktop pixel-buffer textures fed from GStreamerappsink).
Platform support #
| Platform | Min version | Architectures | GStreamer runtime |
|---|---|---|---|
| Android | API 24 (7.0) | arm64-v8a, armeabi-v7a, x86, x86_64 |
Auto-downloaded Android SDK + ndk-build at compile time |
| iOS | 13.0 | Physical arm64 device (no Simulator) |
GStreamer iOS SDK (static framework) |
| macOS | 10.13 | x86_64 / arm64 | Homebrew or GStreamer.framework |
| Windows | 10+ | x86_64 | GStreamer MSVC runtime |
| Linux | — | x86_64 | System GStreamer + GTK 3 |
The Apple-Silicon iOS Simulator is not supported because the prebuilt iOS SDK does not ship an arm64 simulator slice.
On Apple-Silicon macOS, the default Homebrew install is arm64-only. The plugin builds arm64-only in Homebrew debug mode; Mac App Store / universal release auto-downloads the official universal
GStreamer.frameworkto the user cache duringpod install.
Installation #
Add the package from pub.dev
to your app's pubspec.yaml:
dependencies:
xue_hua_video_player: ^1.5.1
Then:
flutter pub get
The native player core (native/) is compiled from C during your app build
(CMake / CocoaPods / NDK). No Rust toolchain is required. Each platform needs the
GStreamer SDK at build time (and its runtime libraries at run time where
applicable). On Android the official GStreamer Android SDK is downloaded
automatically on the first build and the umbrella libgstreamer_android.so is
produced via ndk-build (see Android below).
Quick start #
import 'package:flutter/material.dart';
import 'package:xue_hua_video_player/xue_hua_video_player.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Load the native library once at app start.
await XueHuaVideoPlayer.initialize();
runApp(const MyApp());
}
class PlayerPage extends StatefulWidget {
const PlayerPage({super.key});
@override
State<PlayerPage> createState() => _PlayerPageState();
}
class _PlayerPageState extends State<PlayerPage> {
final controller = XueHuaPlayerController();
@override
void initState() {
super.initState();
controller.initialize().then((_) {
controller.open(
VideoSource.network('https://example.com/video.mp4'),
autoPlay: true,
);
});
}
@override
void dispose() {
controller.dispose(); // always dispose to release the native player
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: XueHuaVideoView(controller: controller),
);
}
}
Sources can also be a local file or a bundled asset:
await controller.open(const VideoSource.file('/path/to/video.mp4'));
await controller.open(const VideoSource.asset('assets/sample.mp4'));
Bundled assets are loaded via Dart FFI bytes into a native temp file (not AppSrc).
Integrating into your app (read this first) #
- Call
XueHuaVideoPlayer.initialize()once before creating any controller (typically inmain()afterWidgetsFlutterBinding.ensureInitialized()). It is idempotent and safe to call again after a hot restart. - Create and
initialize()aXueHuaPlayerControllerper video surface. The controller owns a native player; it is created duringinitialize(). - Always
dispose()the controller when the surface goes away — this stops the pipeline, cancels the event stream, and releases native resources. Leaking a controller leaks a native pipeline. - Read state inside
SignalBuilder/Watch. Every state field is aReadonlySignal; reading.valueoutside a reactive builder will not rebuild your widget when it changes. - Android builds all four ABIs by default (
arm64-v8a,armeabi-v7a,x86,x86_64). You may optionally narrow this withabiFiltersto shrink your APK (see below). The first Android build downloads the GStreamer Android SDK and runs ndk-build (requires network). - All platforms require the GStreamer SDK at build time (and its runtime libraries at run time where applicable). See Native GStreamer setup.
Permissions & platform configuration #
Playback of network video requires per-platform configuration. Local files and assets work with no extra permissions (aside from normal file access).
Android #
Add the internet permission to your app's
android/app/src/main/AndroidManifest.xml (outside <application>):
<uses-permission android:name="android.permission.INTERNET"/>
To allow plaintext http:// (Android blocks cleartext by default on API 28+),
set usesCleartextTraffic on <application>:
<application
android:usesCleartextTraffic="true"
...>
The plugin ships all four ABIs (arm64-v8a, armeabi-v7a, x86, x86_64), so
no abiFilters are required. If you want to shrink your APK to only the ABIs you
ship, narrow the set in android/app/build.gradle(.kts):
android {
defaultConfig {
ndk {
// Optional: keep only the ABIs you need (arm64-v8a covers most
// modern phones; x86_64 is useful for emulators).
abiFilters += listOf("arm64-v8a", "x86_64")
}
}
}
Prefer per-ABI APKs or an Android App Bundle so each device only downloads its own ABI — the GStreamer runtime packaged per ABI is large (~13–18 MB each).
The plugin ships
x86(32-bit) libraries for completeness, but current Flutter no longer builds 32-bit x86 apps, so in practicearm64-v8a,armeabi-v7a, andx86_64are what a Flutter app will actually package.
Notes:
- The plugin's
AndroidManifest.xmlalready setsandroid:extractNativeLibs="true"; it merges into your app solibgstreamer_android.sois extracted to disk for the dynamic loader. - Some transitive plugins require a recent
compileSdk. If AAR metadata checks fail, forcecompileSdk = 36across subprojects (the example does this in itsandroid/build.gradle.kts).
Release builds (R8 / ProGuard)
Video renders into a Flutter Texture backed by SurfaceProducer; GStreamer
glimagesink binds via VideoOverlay to the producer's Surface. The plugin
ships consumer ProGuard rules in its AAR
(android/proguard-rules.pro). Keep GStreamer JNI helpers:
-keep class org.freedesktop.gstreamer.** { *; }
Ensure your release build type references proguard-rules.pro when
isMinifyEnabled = true.
v1.4.0+ uses Flutter external textures on all platforms (custom bridge, no
irondash_texture). Android keeps the
GStreamer Android video tutorial
glimagesink + ANativeWindow path via SurfaceProducer.
v1.0.19+ fixes Android SIGABRT by adopting the GStreamer Android tutorial
thread model: a dedicated xhvp-gst thread with an owned GMainContext
(MainContext::new(), not default()), MainLoop::run(), and all pipeline
operations marshalled onto that thread.
v1.0.18 (superseded by 1.0.19) attempted a default-context GMainLoop; do
not use.
v1.0.17+ streams network http(s):// URIs directly through GStreamer
(playbin3 / souphttpsrc); the plugin does not download to disk first.
v1.0.16+ when gst_is_initialized() is already true: syncs gstreamer-rs state
and does not call GLib thread-default APIs on the Flutter main thread.
Prefer a ready localPath from your media layer when available.
v1.0.11+ runs the entire create_player path on the Android main thread (fixes
crashes when FRB calls from a worker thread) and no longer calls
android_logger::init_once (safe alongside host SDKs such as xue_hua_sdk that
already own the global log logger). Panics are written via __android_log_write
(xue_hua_video_player tag).
v1.0.10+ additionally hardens frame upload (no assert! on buffer size
mismatch), refreshes ANativeWindow in onSurfaceAvailable, and logs Rust panic
backtraces to logcat (xue_hua_video_player tag). If the app still crashes,
build with a clean tree and symbolicate with
scripts/symbolicate_android_tombstone.sh.
Consumer integration checklist (e.g. chat / IM apps) #
- Plugin version — use 1.4.0+ for Texture rendering; run
flutter clean/ full reinstall after upgrading. - Initialization order —
XueHuaVideoPlayer.initialize()→controller.initialize()→ wait until media is on disk →open(...). - Video surface — embed
XueHuaVideoView(setshowControls: falseif you provide your own chrome). The widget registers a native texture automatically. - Release builds — keep ProGuard rules that merge from this plugin's AAR.
- Diagnosis — filter logcat for
xue_hua_video_playerandandroid overlay:.
iOS #
- Minimum deployment target: iOS 13.0. Physical
arm64device only (no Simulator). - GStreamer performs networking with its own sockets + OpenSSL, not through
NSURLSession, so App Transport Security (ATS) does not block playback and noNSAppTransportSecurityentry is required for GStreamer streams (bothhttp://andhttps://work). - The static iOS framework's plugins and TLS backend are registered in the C
core (
native/src/ios_plugins.c,native/src/ios_tls.c, called fromnative/src/runtime.c). HTTPS certificate verification is intentionally relaxed (ssl-strict = false) so streams from hosts without a bundled CA chain still play — see the security note.
macOS #
The Mac App Store requires App Sandbox. At runtime the C core configures
GST_PLUGIN_SYSTEM_PATH and GIO_MODULE_DIR for the embedded framework.
Important: when Flutter integrates this plugin via Swift Package Manager
(SPM — default when Package.swift is present), CocoaPods
vendored_frameworks does not run, so GStreamer is not copied into the
.app automatically. Without an embed step you get:
dyld: Library not loaded: @rpath/GStreamer.framework/Versions/1.0/lib/GStreamer
Embed GStreamer (required for SPM hosts with a Podfile)
In macos/Podfile, resolve the plugin and call the helper from post_install:
require 'json'
plugins = JSON.parse(File.read(File.expand_path('../.flutter-plugins-dependencies', __dir__)))
xhvp = plugins.dig('plugins', 'macos')&.find { |p| p['name'] == 'xue_hua_video_player' }
raise 'xue_hua_video_player not found; run flutter pub get first' unless xhvp
require File.expand_path('macos/gstreamer_podfile_helper.rb', xhvp['path'])
# ...
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
end
install_gstreamer_embed_script!(installer)
end
Then cd macos && pod install. This adds an Xcode Run Script that copies the
slim runtime into YourApp.app/Contents/Frameworks/GStreamer.framework.
Pure-SPM apps without a Podfile (this repo’s example): keep the existing
Runner Run Script that calls macos/scripts/embed_gstreamer_framework.sh.
Add at minimum to macos/Runner/DebugProfile.entitlements and
Release.entitlements:
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
See Mac App Store release (macOS) for the full checklist.
For local dev without the official framework, set
XUE_HUA_ALLOW_HOMEBREW_GSTREAMER=1 (not suitable for store submission).
Apple Release / FFI symbols (iOS & macOS) #
On Apple platforms Dart loads the C player via DynamicLibrary.process() and
resolves xhvp_* with dlsym. Release / Archive builds can strip those global
symbols, which surfaces as:
Failed to lookup symbol 'xhvp_init': dlsym(RTLD_DEFAULT, xhvp_init): symbol not found
The plugin already:
- Marks ABI exports
__attribute__((used))and callsxhvp_ffi_retain_symbols()from plugin registration (keeps symbols past dead-code strip). - CocoaPods: injects Runner
-force_loadoflibxue_hua_video_player.aandSTRIP_STYLE=non-globalviauser_target_xcconfig.
Host apps still need Strip Style = Non-Global Symbols so dlsym can see
global names after Archive. See also
Flutter C interop — Stripping symbols.
| Integration | Do you need to set Strip Style manually? |
|---|---|
CocoaPods (typical Flutter apps with a Podfile) |
Usually no. After upgrading this plugin, run cd ios && pod install and/or cd macos && pod install. Confirm Runner → Build Settings → Strip Style is Non-Global Symbols for Release/Profile. |
| Swift Package Manager (SPM) | Yes. Podspec settings do not apply. Configure Xcode as below. This repo’s example already sets STRIP_STYLE = non-global. |
SPM / manual Xcode steps
- Open
ios/Runner.xcworkspaceormacos/Runner.xcworkspace. - Select target Runner → Build Settings.
- Search Strip Style (or
STRIP_STYLE). - For Release and Profile, change All Symbols → Non-Global Symbols.
- Clean and rebuild:
flutter build ios --release/flutter build macos --release(or Archive).
Or add to the Release/Profile blocks in project.pbxproj:
STRIP_STYLE = non-global;
Verify symbols are present
# macOS Release .app
nm -gU YourApp.app/Contents/MacOS/YourApp | grep xhvp_init
# iOS (Runner binary inside the .app)
nm -gU Runner.app/Runner | grep xhvp_init
You should see _xhvp_init. An empty result means symbols were still stripped.
Windows #
- Install the GStreamer MSVC package (development files + runtime) from https://gstreamer.freedesktop.org/download/.
- Ensure the runtime DLLs are discoverable at run time — add
...\1.0\msvc_x86_64\bintoPATHor bundle the DLLs next to your.exe. - Plugin DLLs live under
lib\gstreamer-1.0; when packaging, pointGST_PLUGIN_SYSTEM_PATHat the bundled copy at startup.
Linux #
- No app permission is required. Install the system GStreamer development and plugin packages plus GTK 3 (see Native setup).
Security note on HTTPS #
To maximize compatibility with hosts whose certificate chains are not present in
the (minimal) bundled trust store, the pipeline sets ssl-strict = false on the
HTTP source, which skips server-certificate verification. This removes
protection against man-in-the-middle attacks. If you need strict verification,
bundle a CA certificate database and configure GLib's default GTlsDatabase
(open an issue if you want this made configurable).
API reference #
XueHuaVideoPlayer.initialize() #
Static, one-time initialization of the native library / Rust bridge. Call once before using any controller.
XueHuaVideoPlayer.captureThumbnail(VideoSource, {Duration? at, int maxWidth}) #
One-shot cover extraction via a headless GStreamer pipeline in C
(xhvp_thumbnail_capture). Returns PNG Uint8List. Does not require an open
controller. When at is null, native picks ~5% of duration (or 1s).
XueHuaPlayerController #
| Method | Description |
|---|---|
initialize() |
Creates the native player and subscribes to events. |
open(VideoSource, {bool autoPlay}) |
Loads a source; optionally starts playback. |
play() / pause() / stop() |
Playback transport. |
togglePlayPause() |
Play if paused, pause if playing. |
seek(Duration) |
Seek to a position. |
setVolume(double) |
Volume in 0.0..1.0. |
setMuted(bool) / toggleMuted() |
Mute control. |
setSpeed(double) |
Playback speed multiplier. |
setLooping(bool) |
Loop at end-of-stream. |
captureCurrentFrame() |
Latest decoded frame as PNG (xhvp_player_capture_frame). |
queryPosition() / queryDuration() |
Query the pipeline directly. |
dispose() |
Tear down the player and release all resources. |
Reactive state (all ReadonlySignals; read .value in a SignalBuilder):
state, position, duration, videoSize, aspectRatio, bufferingPercent,
volume, speed, looping, muted, isPlaying, isCompleted, error,
playerId, initialized.
PlayerState: idle, ready, buffering, playing, paused, stopped,
completed, error.
VideoSource #
VideoSource.network(String url)VideoSource.file(String path)(accepts a plain path or afile://URI)VideoSource.asset(String assetKey)
XueHuaVideoView #
A StatelessWidget that embeds a Flutter Texture for the controller's video and,
by default, an adaptive control bar.
| Parameter | Default | Description |
|---|---|---|
controller |
required | The XueHuaPlayerController to render. |
aspectRatioMode |
AspectRatioMode.fit |
GStreamer sink scaling (fit / fill / stretch). Also available via controller.setAspectRatioMode. |
backgroundColor |
black | Letterbox / background color. |
showControls |
true |
Overlay the built-in control bar. |
controlsStyle |
adaptive |
adaptive / material / cupertino. |
Theming the controls #
Register a VideoControlsTheme in ThemeData.extensions to customize the
control bar, or rely on the built-in VideoControlsTheme.material() /
VideoControlsTheme.cupertino() presets:
MaterialApp(
theme: ThemeData(
extensions: const [/* your */ VideoControlsTheme.material()],
),
);
Native GStreamer setup (per platform) #
The Rust core links GStreamer at build time, so a GStreamer development install must be discoverable while building, and the runtime libraries must be available (bundled or system-installed) when running.
macOS #
Mac App Store / sandboxed release requires the official universal
GStreamer.framework (x86_64 + arm64). Homebrew dylibs cannot be loaded from
/opt/homebrew inside the sandbox.
On first pod install, runtime + devel are downloaded automatically into the
user cache (~800MB–1GB download, no sudo):
~/Library/Caches/xue_hua_video_player/gstreamer/1.28.4/
GStreamer.framework— full SDK (for build/link)GStreamerRuntime.framework— runtime snapshot (embedded into.app; consumers do not need to configure this)
The final .app embeds a trimmed Slim Runtime (v1.0.5+, ~350–450MB
universal, or ~175–280MB per-arch) with only the plugins needed for playbin3,
HTTPS/HLS/RTSP, and applemedia hardware decode. Multiple Flutter projects share
the same download cache.
Optional per-arch builds: set XUE_HUA_GSTREAMER_ARCH=arm64 or x86_64 before
pod install / flutter build macos (default universal) to ship separate Apple
Silicon and Intel packages.
Optional env vars: XUE_HUA_GSTREAMER_ROOT, GSTREAMER_FRAMEWORK_SRC (offline /
custom paths). Maintainers may still run sh tool/setup_gstreamer_macos.sh --system to install under /Library/Frameworks.
Consumers: build setup
- Enable App Sandbox +
com.apple.security.network.client(see Permissions). - Run
flutter pub get. - Wire the GStreamer embed helper into
macos/Podfilepost_install(see macOS — required under SPM), thencd macos && pod install(first run downloads the GStreamer cache if needed). - Run
flutter build macos --releaseand verifyYourApp.app/Contents/Frameworks/GStreamer.frameworkexists:
ls YourApp.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer
If that path is missing, dyld will fail at launch with
Library not loaded: @rpath/GStreamer.framework/....
The C core sets GST_PLUGIN_SYSTEM_PATH, GIO_MODULE_DIR, and a writable
GST_REGISTRY before gst_init() (xhvp_setup_macos_env() in
native/src/apple_env.c).
Local Homebrew dev (not for MAS)
export XUE_HUA_ALLOW_HOMEBREW_GSTREAMER=1
brew install pkg-config gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-libav
Mac App Store release (macOS) #
- Enable sandbox +
network.clientinmacos/Runner/*entitlements. flutter build macos --releaseor Archive in Xcode (first build downloads the GStreamer cache automatically).- Verify:
YourApp.app/Contents/Frameworks/GStreamer.frameworkis presentcodesign -vvv --deep --strict YourApp.apppasses- Network playback works with sandbox enabled
- Validate App → Upload to App Store Connect.
Linux #
sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \
libgtk-3-dev
Runtime uses the system GStreamer libraries. libgtk-3-dev is required for
Flutter Linux texture registration (GTK/GL interop in the engine).
Windows #
GStreamer 1.28+ ships a single Inno Setup
.exeinstaller (the older.msipackages, including the separate-develpackage, were removed). One installer bundles both runtime and development files.
- Download
gstreamer-1.0-msvc-x86_64-<version>.exe(the MSVC build) from https://gstreamer.freedesktop.org/download/ and run it. Choose the "Runtime and development headers" setup type so the headers/.lib/.pcfiles are installed. - The GUI installer sets
GSTREAMER_1_0_ROOT_MSVC_X86_64, whichwindows/CMakeLists.txtuses to locate headers/libs and to bundle the runtime DLLs next to the app. (In headless/silent installs this variable may not be set reliably — set it manually if needed.) - A
pkg-configmust be onPATH(e.g.choco install pkgconfiglite). - Plugin DLLs live under
lib/gstreamer-1.0; setGST_PLUGIN_SYSTEM_PATHto the bundled copy at startup when packaging.
iOS (physical device) #
Targets a physical arm64 iPhone. The prebuilt SDK does not ship an arm64 Simulator slice, so the Apple-Silicon iOS Simulator is not supported.
-
Download and install the GStreamer iOS SDK (
devel), matching your desktop GStreamer major/minor version:curl -fLO https://gstreamer.freedesktop.org/data/pkg/ios/1.28.4/gstreamer-1.0-devel-1.28.4-ios-universal.pkg # user-domain install (no sudo); double-clicking the .pkg also works installer -pkg gstreamer-1.0-devel-1.28.4-ios-universal.pkg -target CurrentUserHomeDirectoryThis installs
GStreamer.frameworkunder~/Library/Developer/GStreamer/iPhone.sdk(override withGSTREAMER_ROOT_IOS). -
ios/xue_hua_video_player.podspecalready exports thesystem-depsoverrides andPKG_CONFIG_ALLOW_CROSS=1for the Rust cross-build, injectsRUSTFLAGSviaios/scripts/ios_rust_link_flags.sh(UIKit/OpenGLES/QuartzCore, etc. for the Cargokit staticlib link step), and links the umbrella-framework GStreamerinOTHER_LDFLAGSfor the final Xcode link. -
Build/run on a connected device:
flutter run -d <device>(orflutter build ios --no-codesignto verify the build).
Because the iOS GStreamer.framework is static, its plugins are not
auto-discovered. The C core registers the ones needed for playback and the
OpenSSL TLS backend (xhvp_register_ios_static_plugins() /
xhvp_register_ios_tls_backend() in native/src/runtime.c, iOS-only), and
prepares the runtime environment before gst_init(). Add to the plugin list in
native/src/ios_plugins.c if you need an element that isn't registered yet.
Android (all ABIs) #
Supports arm64-v8a, armeabi-v7a, x86, and x86_64. On every Android
build the plugin:
- Downloads the official GStreamer Android universal SDK (if not already cached)
- Runs ndk-build to produce the umbrella
libgstreamer_android.soper ABI - Compiles the C
libxue_hua_video_player.sovia NDK CMake (native/+ JNI)
The umbrella library (all of GStreamer + its plugins, linked statically) and
libc++_shared.so land in android/build/gstreamer/jniLibs/<abi>/ and are
packaged into the plugin AAR. The first build needs network access; later
builds reuse the cache at
~/Library/Caches/xue_hua_video_player/gstreamer/android/<version>/.
Environment variables:
| Variable | Purpose |
|---|---|
GST_VER |
GStreamer version (default 1.28.4) |
GSTREAMER_ROOT_ANDROID |
SDK root (skip auto-download when pre-populated) |
XUE_HUA_GSTREAMER_ROOT |
Alias for custom SDK/cache root |
The GStreamer Android runtime is initialized automatically at process startup by
GStreamerInitProvider (a ContentProvider in the plugin's
android/src/main/java/), which runs System.loadLibrary("gstreamer_android")
(so the library's JNI_OnLoad captures the JavaVM) and GStreamer.init(context)
(so the app Context/ClassLoader are set). This is required so the
androidmedia MediaCodec decoders can enumerate/register - without it playback
fails with not-linked / No streams to output. The Rust core does NOT register
plugins on Android (it would run before the Java init and without the JavaVM).
A consuming app builds for all four ABIs by default. To reduce APK size, narrow
abiFilters (see Permissions & platform configuration) or ship an
App Bundle.
Customizing the plugin set
Edit GSTREAMER_PLUGINS in
android/gstreamer_build/jni/Android.mk
to add codecs, then rebuild. The next Android build regenerates the umbrella
libraries automatically.
Manual SDK / umbrella rebuild (optional)
Gradle runs
android/scripts/ensure_gstreamer_android.sh
and
android/scripts/build_gstreamer_umbrella.sh
for you. To run them by hand:
sh android/scripts/ensure_gstreamer_android.sh
sh android/scripts/build_gstreamer_umbrella.sh \
"$HOME/Library/Android/sdk/ndk/<ndk-version>" \
/tmp/gstreamer-jniLibs \
arm64-v8a armeabi-v7a x86 x86_64
Architecture #
Dart: XueHuaPlayerController ──FFI──► FfiPlayerCommandPort (xhvp_player_*)
XueHuaVideoView (Texture) ◄──native texture── GStreamer sink
C: native/ playbin3 ─► appsink (Apple/desktop) or glimagesink (Android)
│ bus ─► XhvpEventCallback ─► Dart Stream
- Decoding:
playbin3with platform video sink (appsinkorglimagesink). - Rendering: Flutter
Texture+ nativeTextureRegistry; Android usesSurfaceProducer+ VideoOverlay; Apple/desktop pull BGRA frames via C ABI (xhvp_texture_*). - Control plane: Dart FFI → narrow
xhvp_player_*API (seenative/include/xhvp_player.h).
Regenerating FFI bindings #
After changing native/include/xhvp_player.h:
dart run ffigen --config ffigen.yaml
GStreamer upstream patches #
When GStreamer C itself must change, use the
Matkurban/gstreamer fork as the patch
source (XHVP_GSTREAMER_SRC). See third_party/gstreamer.md.
Troubleshooting #
- Release crash:
Failed to lookup symbol 'xhvp_init'(iOS/macOS only): global FFI symbols were stripped. See Apple Release / FFI symbols (Strip Style = Non-Global Symbols; CocoaPods usually injects this afterpod install). - iOS launch
g_dir_open_with_errno/g_filename_to_utf8/ ORC mmap errors: GLib/GStreamer need writableHOME/XDG_*/GST_REGISTRY, ORC JIT is blocked by the Hardened Runtime, and static iOS builds must not scan a NULL plugin path. The C core sets these beforegst_init(native/src/apple_env.c:ORC_CODE=backup, emptyGST_PLUGIN_SYSTEM_PATH). Do not addallow-jit. Can't typefind stream/Stream doesn't contain enough dataon network video (iOS): usually a failed TLS handshake when no CA database is configured. The C core registers the OpenSSL TLS backend (native/src/ios_tls.c) and relaxesssl-strictonsouphttpsrcvia playbinsource-setup.- APK too large: all four Android ABIs each carry a large GStreamer runtime.
Narrow
abiFiltersor ship per-ABI APKs / an App Bundle. - Android first build fails / no network: the GStreamer Android SDK is
downloaded on first build. Pre-populate
GSTREAMER_ROOT_ANDROIDfor offline CI, or cache~/Library/Caches/xue_hua_video_player/gstreamer/android/. - Windows
pkg-configcannot findglib-2.0: confirm the development files were installed andPKG_CONFIG_PATHpoints at...\1.0\msvc_x86_64\lib\pkgconfig. - macOS black screen / dyld cannot load GStreamer: confirm
.app/Contents/Frameworks/GStreamer.frameworkexists. Under SPM this requiresinstall_gstreamer_embed_script!(installer)inmacos/Podfilepost_install(see macOS), thenpod installand rebuild. Enable sandboxnetwork.client; for Homebrew-only dev setXUE_HUA_ALLOW_HOMEBREW_GSTREAMER=1(store builds require the official framework).
Maintainers #
- Author: Matkurban <3496354336@qq.com>
- Repository: https://github.com/Matkurban/xue_hua_video_player
- Issues: https://github.com/Matkurban/xue_hua_video_player/issues
License #
See LICENSE.