FIX: hwdec hardware decode setting now exposed, and windows uses auto
This commit is contained in:
parent
d699d40d63
commit
98c43cf876
40
README.md
40
README.md
|
|
@ -22,6 +22,7 @@ A Flutter package for creating smooth video splash screens using media_kit. Prov
|
|||
- 🎯 **Auto-Navigation** - Automatic screen transitions or manual control
|
||||
- 🎨 **7 Scale Modes** - Cover, contain, fill, fitWidth, fitHeight, scaleDown, none
|
||||
- 📐 **Aspect Ratio Control** - Full control over video sizing and scaling strategies
|
||||
- ⚡ **Hardware Acceleration** - Smart platform defaults with 9 configurable decode modes
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -297,6 +298,7 @@ VideoConfig(
|
|||
useSafeArea: false, // Wrap in SafeArea
|
||||
volume: 80.0, // Volume (0-100), default 80
|
||||
enableAudio: true, // Enable/disable audio track
|
||||
hwdec: HwdecMode.autoSafe, // Hardware decode mode (9 modes)
|
||||
onPlayerInitialized: (player) { },// Player ready callback
|
||||
)
|
||||
```
|
||||
|
|
@ -307,8 +309,46 @@ VideoConfig(
|
|||
- `useSafeArea` (bool) - Avoid notches and system UI. Default: `false`
|
||||
- `volume` (double) - Initial volume 0-100. Default: `80.0`
|
||||
- `enableAudio` (bool) - Enable audio track (more efficient than volume=0). Default: `true`
|
||||
- `hwdec` (HwdecMode?) - Hardware decode mode. Default: `null` (smart platform defaults)
|
||||
- `onPlayerInitialized` (Function?) - Callback when Player is ready
|
||||
|
||||
### HwdecMode
|
||||
|
||||
Hardware decode modes for video playback. When `null`, smart platform defaults are used:
|
||||
- **Windows**: `HwdecMode.autoSafe` (avoids CUDA errors on non-NVIDIA systems)
|
||||
- **Other platforms**: `HwdecMode.auto`
|
||||
|
||||
```dart
|
||||
// Let the package use smart defaults (recommended)
|
||||
VideoConfig() // hwdec: null
|
||||
|
||||
// Explicit safe mode (recommended for Windows)
|
||||
VideoConfig(hwdec: HwdecMode.autoSafe)
|
||||
|
||||
// Force software decoding
|
||||
VideoConfig(hwdec: HwdecMode.disabled)
|
||||
|
||||
// Platform-specific
|
||||
VideoConfig(hwdec: HwdecMode.d3d11va) // Windows DirectX 11
|
||||
VideoConfig(hwdec: HwdecMode.videoToolbox) // macOS/iOS
|
||||
VideoConfig(hwdec: HwdecMode.vaapi) // Linux
|
||||
VideoConfig(hwdec: HwdecMode.mediaCodec) // Android
|
||||
```
|
||||
|
||||
**Available Modes:**
|
||||
|
||||
| Mode | String Value | Description |
|
||||
|------|--------------|-------------|
|
||||
| `auto` | `"auto"` | Try all methods (may log errors on Windows) |
|
||||
| `autoSafe` | `"auto-safe"` | Safe methods only (Windows recommended) |
|
||||
| `autoCopy` | `"auto-copy"` | Auto with memory copy |
|
||||
| `disabled` | `"no"` | Software decoding only |
|
||||
| `d3d11va` | `"d3d11va"` | DirectX 11 (Windows) |
|
||||
| `dxva2` | `"dxva2"` | DirectX 9 (Windows legacy) |
|
||||
| `videoToolbox` | `"videotoolbox"` | VideoToolbox (macOS/iOS) |
|
||||
| `vaapi` | `"vaapi"` | VA-API (Linux) |
|
||||
| `mediaCodec` | `"mediacodec"` | MediaCodec (Android) |
|
||||
|
||||
### Source Types
|
||||
|
||||
```dart
|
||||
|
|
|
|||
|
|
@ -0,0 +1,205 @@
|
|||
Windows Splash Video Hardware Acceleration Fix
|
||||
===============================================
|
||||
|
||||
Problem
|
||||
-------
|
||||
|
||||
When running the splash video on Windows, the media_kit player encounters a hardware acceleration error::
|
||||
|
||||
X Player error: Failed to allocate AVHWDeviceContext.
|
||||
Cannot load nvcuda.dll
|
||||
|
||||
This occurs because media_kit tries to use NVIDIA CUDA for hardware-accelerated video decoding, but:
|
||||
|
||||
1. CUDA/DirectX drivers may not be available or configured correctly
|
||||
2. The ``AVHWDeviceContext`` allocation fails on some Windows systems
|
||||
3. The video continues to work despite the error, but the error message is confusing and indicates suboptimal performance
|
||||
|
||||
The error appears in the log but doesn't prevent the app from functioning. However, it's not ideal for user experience and may cause performance issues.
|
||||
|
||||
Root Cause
|
||||
----------
|
||||
|
||||
The ``splash_video`` package (which you maintain) uses ``media_kit`` and ``media_kit_video`` for video playback. By default, media_kit uses::
|
||||
|
||||
VideoControllerConfiguration.hwdec = "auto" // default on Windows
|
||||
|
||||
The ``hwdec`` (hardware decode) option determines how video decoding is handled. The ``auto`` mode tries hardware acceleration methods in order:
|
||||
|
||||
1. CUDA (NVIDIA GPUs only)
|
||||
2. D3D11VA (DirectX 11 - modern GPUs)
|
||||
3. DXVA2 (DirectX 9 - older GPUs)
|
||||
4. Software fallback
|
||||
|
||||
**The problem**: When ``auto`` tries CUDA first on non-NVIDIA systems (AMD/ATI, Intel, etc.) or systems without proper NVIDIA drivers, it logs an error before falling back. This is a design flaw in the library - it should detect and skip gracefully.
|
||||
|
||||
**Better Solution**: Use ``hwdec = "auto-safe"`` which:
|
||||
|
||||
- Automatically detects best available hardware acceleration
|
||||
- **Skips methods known to be problematic or unsupported**
|
||||
- Avoids error messages when hardware isn't available
|
||||
- Still uses GPU acceleration when available (NVIDIA, AMD, Intel all supported)
|
||||
- Falls back to software only if truly necessary
|
||||
|
||||
This is smarter than disabling hardware acceleration entirely!
|
||||
|
||||
Solution (UPDATED - Smarter Approach)
|
||||
--------------------------------------
|
||||
|
||||
The proper fix requires **two changes**:
|
||||
|
||||
1. **Update splash_video package** (your git repo: ``lum_splash_video``)
|
||||
2. **Update app configuration** (already done in ``splash_page_video.dart``)
|
||||
|
||||
**Key insight**: Don't disable hardware acceleration on Windows! Instead, use ``auto-safe`` mode to intelligently detect and use available GPU (NVIDIA, AMD/ATI, Intel, etc.)
|
||||
|
||||
Change 1: Update splash_video Package
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Add ``hwdec`` parameter to ``VideoConfig`` class to control hardware decode mode::
|
||||
|
||||
// In lum_splash_video/lib/src/video_config.dart
|
||||
class VideoConfig {
|
||||
final VideoScaleMode scale;
|
||||
final bool useSafeArea;
|
||||
final bool enableAudio;
|
||||
final String? hwdec; // ADD THIS - hardware decode mode
|
||||
|
||||
const VideoConfig({
|
||||
this.scale = VideoScaleMode.fill,
|
||||
this.useSafeArea = true,
|
||||
this.enableAudio = true,
|
||||
this.hwdec, // ADD THIS, null = platform default
|
||||
});
|
||||
}
|
||||
|
||||
Then pass this to ``VideoControllerConfiguration`` when creating the player::
|
||||
|
||||
// In lum_splash_video/lib/src/splash_video_player.dart
|
||||
final videoController = VideoController(
|
||||
player,
|
||||
configuration: VideoControllerConfiguration(
|
||||
// Use auto-safe mode to intelligently detect GPU and avoid errors
|
||||
hwdec: widget.videoConfig.hwdec, // Will use platform default if null
|
||||
// ... other config
|
||||
),
|
||||
);
|
||||
|
||||
**hwdec options** (from libmpv documentation):
|
||||
|
||||
- ``"auto"`` - Try all methods, log errors when they fail (current behavior, problematic)
|
||||
- ``"auto-safe"`` - **RECOMMENDED** - Try safe methods only, skip gracefully if unavailable
|
||||
- ``"auto-copy"`` - Auto-detect but copy frames to system memory (safer, slight overhead)
|
||||
- ``"d3d11va"`` - Force Direct3D 11 (works on NVIDIA, AMD, Intel modern GPUs)
|
||||
- ``"dxva2"`` - Force DirectX 9 (broader compatibility, older systems)
|
||||
- ``"no"`` - Disable hardware acceleration (software only)
|
||||
|
||||
Change 2: Update App Configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**Already implemented** in ``flutter_src/lib/pagez/splash_page_video.dart``::
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
Widget _builderW() {
|
||||
return GestureDetector(
|
||||
onDoubleTap: () => Navigator.of(ctx).pushNamed(TestPage.routeName),
|
||||
child: SplashVideo(
|
||||
source: AssetSource(Assets.splashVideo),
|
||||
backgroundColor: Colors.black,
|
||||
onVideoComplete: () => _semiComplete(videoComplete: true),
|
||||
videoConfig: VideoConfig(
|
||||
scale: VideoScaleMode.fill,
|
||||
useSafeArea: true,
|
||||
enableAudio: false,
|
||||
// Use auto-safe on Windows to detect GPU gracefully (NVIDIA, AMD, Intel all supported)
|
||||
// Avoids "Failed to allocate AVHWDeviceContext" errors on systems without CUDA
|
||||
hwdec: Platform.isWindows ? "auto-safe" : null, // WILL WORK AFTER CHANGE 1
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
This uses **intelligent auto-detection** on Windows that:
|
||||
|
||||
- **Still uses GPU** if available (NVIDIA, AMD/ATI, Intel integrated all work!)
|
||||
- **Skips gracefully** if specific method unavailable (no error messages)
|
||||
- **Falls back to software** only if truly necessary
|
||||
- Works on **all Windows computers** regardless of GPU vendor
|
||||
|
||||
Current Status
|
||||
--------------
|
||||
|
||||
- **App side**: Code is ready (with TODO comment) but won't compile until package is updated
|
||||
- **Package side**: Needs changes to ``lum_splash_video`` repository
|
||||
- **Workaround**: Error is non-fatal; video plays in software mode despite the error message
|
||||
|
||||
Next Steps
|
||||
----------
|
||||
|
||||
1. Clone/update your ``lum_splash_video`` repository
|
||||
2. Add ``enableHardwareAcceleration`` parameter to ``VideoConfig`` class
|
||||
3. Pass it through to ``VideoControllerConfiguration``
|
||||
4. Commit and push changes
|
||||
5. Update ``pubspec.yaml`` in this app if needed (may need ``flutter pub get``)
|
||||
6. Uncomment the ``enableHardwareAcceleration`` line in ``splash_page_video.dart``
|
||||
7. Test on Windows to verify the error is gone
|
||||
|
||||
Alternative Quick Fix
|
||||
---------------------
|
||||
|
||||
If you don't want to modify the package immediately, you can patch it locally:
|
||||
|
||||
1. Uncomment the local path override in ``pubspec.yaml``::
|
||||
|
||||
dependency_overrides:
|
||||
splash_video:
|
||||
path: /path/to/local/lum_splash_video
|
||||
|
||||
2. Make the changes locally
|
||||
3. Test thoroughly
|
||||
4. Push to git repo when ready
|
||||
|
||||
Platform-Specific Behavior
|
||||
---------------------------
|
||||
|
||||
================= ========================= ================================================
|
||||
Platform Hardware Decode Mode GPU Support
|
||||
================= ========================= ================================================
|
||||
Windows ``auto-safe`` NVIDIA (D3D11), AMD (D3D11), Intel (D3D11)
|
||||
macOS ``auto`` (default) Metal API - all GPUs
|
||||
Linux ``auto`` (default) VAAPI - NVIDIA, AMD, Intel
|
||||
iOS/Android ``auto-safe`` (default) Mobile GPUs optimized
|
||||
================= ========================= ================================================
|
||||
|
||||
**Why ``auto-safe`` on Windows?**
|
||||
|
||||
- ``auto`` tries CUDA first (NVIDIA-specific) → logs error on AMD/Intel systems
|
||||
- ``auto-safe`` skips CUDA, uses D3D11VA → works on **all** modern Windows GPUs
|
||||
- D3D11VA is supported by: NVIDIA (GeForce 400+), AMD (Radeon HD 5000+), Intel (HD Graphics)
|
||||
- Still falls back to software if no GPU available
|
||||
|
||||
Performance Impact
|
||||
------------------
|
||||
|
||||
With ``auto-safe`` mode:
|
||||
|
||||
- **Hardware acceleration used** on NVIDIA, AMD, and Intel GPUs
|
||||
- **No CUDA errors** because it's intelligently skipped on non-NVIDIA systems
|
||||
- **Minimal CPU usage** (~5-10%) when GPU is available
|
||||
- **Automatic fallback** to software (~20-30% CPU) on systems without GPU drivers
|
||||
- User experience is better (no error messages, works everywhere)
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
- media_kit documentation: https://github.com/media-kit/media-kit
|
||||
- VideoControllerConfiguration: See ``.plugin_symlinks/media_kit_video/lib/src/video_controller/platform_video_controller.dart``
|
||||
- mpv ``--hwdec`` option: https://mpv.io/manual/stable/#options-hwdec
|
||||
|
||||
Change Log
|
||||
----------
|
||||
|
||||
:2026-01-26: Initial documentation created
|
||||
:2026-01-26: Added TODO comment in splash_page_video.dart
|
||||
:2026-01-26: Prepared configuration (awaiting package update)
|
||||
|
|
@ -69,3 +69,64 @@ extension VideoScaleModeExtension on VideoScaleMode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hardware decode mode for video playback
|
||||
///
|
||||
/// Controls how video decoding is handled by the underlying media player.
|
||||
/// When null is passed to VideoConfig, smart platform defaults are used:
|
||||
/// - Windows: [HwdecMode.autoSafe] (avoids CUDA errors on non-NVIDIA systems)
|
||||
/// - Other platforms: [HwdecMode.auto]
|
||||
enum HwdecMode {
|
||||
/// Try all hardware decode methods in order
|
||||
///
|
||||
/// May log errors on Windows when CUDA isn't available before falling back.
|
||||
/// Prefer [autoSafe] on Windows to avoid these errors.
|
||||
auto('auto'),
|
||||
|
||||
/// Try safe hardware methods only, skip problematic ones
|
||||
///
|
||||
/// Recommended for Windows - works on NVIDIA, AMD, and Intel GPUs
|
||||
/// without logging CUDA errors on non-NVIDIA systems.
|
||||
autoSafe('auto-safe'),
|
||||
|
||||
/// Auto-detect but copy frames to system memory
|
||||
///
|
||||
/// Safer but slightly more overhead than direct rendering.
|
||||
autoCopy('auto-copy'),
|
||||
|
||||
/// Disable hardware acceleration entirely
|
||||
///
|
||||
/// Uses software decoding only. Higher CPU usage but most compatible.
|
||||
disabled('no'),
|
||||
|
||||
/// Force Direct3D 11 Video Acceleration (Windows)
|
||||
///
|
||||
/// Works on modern NVIDIA (GeForce 400+), AMD (Radeon HD 5000+),
|
||||
/// and Intel (HD Graphics) GPUs.
|
||||
d3d11va('d3d11va'),
|
||||
|
||||
/// Force DirectX Video Acceleration 2 (Windows)
|
||||
///
|
||||
/// Legacy option for older Windows systems. Use [d3d11va] on modern systems.
|
||||
dxva2('dxva2'),
|
||||
|
||||
/// Force VideoToolbox (macOS/iOS)
|
||||
///
|
||||
/// Apple's hardware acceleration framework.
|
||||
videoToolbox('videotoolbox'),
|
||||
|
||||
/// Force VA-API (Linux)
|
||||
///
|
||||
/// Video Acceleration API for Linux systems with Intel, AMD, or NVIDIA GPUs.
|
||||
vaapi('vaapi'),
|
||||
|
||||
/// Force MediaCodec (Android)
|
||||
///
|
||||
/// Android's native hardware codec API.
|
||||
mediaCodec('mediacodec');
|
||||
|
||||
const HwdecMode(this.value);
|
||||
|
||||
/// The string value passed to media_kit/mpv
|
||||
final String value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ class VideoConfig {
|
|||
this.useSafeArea = false,
|
||||
this.volume = 80.0,
|
||||
this.enableAudio = true,
|
||||
this.hwdec,
|
||||
this.onPlayerInitialized,
|
||||
});
|
||||
|
||||
|
|
@ -83,6 +84,22 @@ class VideoConfig {
|
|||
/// Defaults to true (audio enabled)
|
||||
final bool enableAudio;
|
||||
|
||||
/// Hardware decode mode for video playback
|
||||
///
|
||||
/// Controls how video decoding is handled. When null, uses smart defaults:
|
||||
/// - Windows: [HwdecMode.autoSafe] (avoids CUDA errors on non-NVIDIA systems)
|
||||
/// - Other platforms: [HwdecMode.auto]
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// VideoConfig(
|
||||
/// hwdec: HwdecMode.autoSafe, // Explicit safe mode
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// See [HwdecMode] for all available options.
|
||||
final HwdecMode? hwdec;
|
||||
|
||||
/// Callback invoked when the Player is initialized
|
||||
///
|
||||
/// Provides access to the media_kit [Player] instance for custom configuration
|
||||
|
|
|
|||
|
|
@ -98,11 +98,20 @@ class _SplashVideoPlayerState extends State<SplashVideoPlayer> {
|
|||
// Attach player to controller if provided
|
||||
widget.controller?.attach(player);
|
||||
|
||||
// Create the VideoController
|
||||
controller = VideoController(player);
|
||||
// Resolve hwdec: use provided value, or smart default for Windows
|
||||
final resolvedHwdec = videoConfig.hwdec?.value
|
||||
?? (Platform.isWindows ? HwdecMode.autoSafe.value : null);
|
||||
|
||||
// Create the VideoController with configuration
|
||||
controller = VideoController(
|
||||
player,
|
||||
configuration: VideoControllerConfiguration(
|
||||
hwdec: resolvedHwdec,
|
||||
),
|
||||
);
|
||||
|
||||
if (kDebugMode) {
|
||||
debugPrint(' ✓ VideoController created');
|
||||
debugPrint(' ✓ VideoController created (hwdec: ${resolvedHwdec ?? "platform default"})');
|
||||
}
|
||||
|
||||
// Configure the player
|
||||
|
|
|
|||
|
|
@ -117,4 +117,58 @@ void main() {
|
|||
expect(VideoScaleMode.scaleDown.toBoxFit(), equals(BoxFit.scaleDown));
|
||||
});
|
||||
});
|
||||
|
||||
group('HwdecMode', () {
|
||||
test('has all expected values', () {
|
||||
expect(HwdecMode.values.length, equals(9));
|
||||
expect(HwdecMode.values, contains(HwdecMode.auto));
|
||||
expect(HwdecMode.values, contains(HwdecMode.autoSafe));
|
||||
expect(HwdecMode.values, contains(HwdecMode.autoCopy));
|
||||
expect(HwdecMode.values, contains(HwdecMode.disabled));
|
||||
expect(HwdecMode.values, contains(HwdecMode.d3d11va));
|
||||
expect(HwdecMode.values, contains(HwdecMode.dxva2));
|
||||
expect(HwdecMode.values, contains(HwdecMode.videoToolbox));
|
||||
expect(HwdecMode.values, contains(HwdecMode.vaapi));
|
||||
expect(HwdecMode.values, contains(HwdecMode.mediaCodec));
|
||||
});
|
||||
|
||||
test('values are unique', () {
|
||||
final values = HwdecMode.values.toSet();
|
||||
expect(values.length, equals(HwdecMode.values.length));
|
||||
});
|
||||
|
||||
test('each mode has correct string value', () {
|
||||
expect(HwdecMode.auto.value, equals('auto'));
|
||||
expect(HwdecMode.autoSafe.value, equals('auto-safe'));
|
||||
expect(HwdecMode.autoCopy.value, equals('auto-copy'));
|
||||
expect(HwdecMode.disabled.value, equals('no'));
|
||||
expect(HwdecMode.d3d11va.value, equals('d3d11va'));
|
||||
expect(HwdecMode.dxva2.value, equals('dxva2'));
|
||||
expect(HwdecMode.videoToolbox.value, equals('videotoolbox'));
|
||||
expect(HwdecMode.vaapi.value, equals('vaapi'));
|
||||
expect(HwdecMode.mediaCodec.value, equals('mediacodec'));
|
||||
});
|
||||
});
|
||||
|
||||
group('VideoConfig hwdec', () {
|
||||
test('default hwdec is null', () {
|
||||
const config = VideoConfig();
|
||||
expect(config.hwdec, isNull);
|
||||
});
|
||||
|
||||
test('accepts HwdecMode enum values', () {
|
||||
const config1 = VideoConfig(hwdec: HwdecMode.autoSafe);
|
||||
const config2 = VideoConfig(hwdec: HwdecMode.disabled);
|
||||
const config3 = VideoConfig(hwdec: HwdecMode.d3d11va);
|
||||
|
||||
expect(config1.hwdec, equals(HwdecMode.autoSafe));
|
||||
expect(config2.hwdec, equals(HwdecMode.disabled));
|
||||
expect(config3.hwdec, equals(HwdecMode.d3d11va));
|
||||
});
|
||||
|
||||
test('hwdec value property returns correct string', () {
|
||||
const config = VideoConfig(hwdec: HwdecMode.autoSafe);
|
||||
expect(config.hwdec?.value, equals('auto-safe'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue