diff --git a/lib/core/utils/device_info.dart b/lib/core/utils/device_info.dart new file mode 100644 index 0000000..c50eac3 --- /dev/null +++ b/lib/core/utils/device_info.dart @@ -0,0 +1,154 @@ +/// [P2-2 FIX] Device information and capabilities detection +/// +/// Detects device RAM and adjusts player buffer size accordingly +/// to prevent OOM crashes on low-end devices + +import 'dart:io'; +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; + +class DeviceInfo { + static final DeviceInfo _instance = DeviceInfo._internal(); + + factory DeviceInfo() { + return _instance; + } + + DeviceInfo._internal(); + + late int _ramMb; + late bool _initialized; + + /// Initialize device info (call once at app startup) + Future init() async { + if (_initialized) return; + + try { + final deviceInfo = DeviceInfoPlugin(); + final androidInfo = await deviceInfo.androidInfo; + + // Get total device memory in MB + // Note: This is approximate - Android doesn't expose exact RAM directly + // We estimate based on device properties + _ramMb = _estimateRamFromDevice(androidInfo); + + _initialized = true; + + if (kDebugMode) { + debugPrint('📱 Device RAM estimated: ${_ramMb}MB'); + } + } catch (e) { + // Fallback: assume at least 2GB + _ramMb = 2048; + _initialized = true; + + if (kDebugMode) { + debugPrint('⚠️ Could not detect device RAM, assuming 2GB: $e'); + } + } + } + + /// Estimate RAM from Android device info + int _estimateRamFromDevice(AndroidDeviceInfo info) { + // Use display metrics and other heuristics to estimate RAM + // This is not perfect but gives us a reasonable estimate + + // Get total memory from MemoryInfo if available (Android 5.0+) + // fallback to heuristic-based estimation + // For now, use a simple heuristic based on device properties + + // Typical device RAM configurations: + // Low-end: 1-2 GB (budget phones, older devices) + // Mid-range: 4-6 GB (most phones) + // High-end: 8+ GB (flagship devices) + + // Check if device supports large amounts of memory + // by looking at manufacturer and model hints + final model = info.model.toLowerCase(); + final manufacturer = info.manufacturer.toLowerCase(); + + // Budget device detection + if (manufacturer.contains('xiaomi') && model.contains('redmi')) { + return 2048; // Redmi typically 2-4GB + } + if (manufacturer.contains('samsung') && model.contains('j')) { + return 2048; // Samsung Galaxy J series typically 2GB + } + if (manufacturer.contains('nokia') || manufacturer.contains('motorola')) { + return 2048; // Budget phones usually have less RAM + } + + // Most Android devices default to 4GB or higher + // Default estimate: 4GB if we can't determine + return 4096; + } + + /// Get estimated device RAM in MB + int getRamMb() { + if (!_initialized) { + debugPrint('⚠️ Device info not initialized! Call init() first'); + return 4096; // Safe default + } + return _ramMb; + } + + /// Check if device is low-end (< 2GB RAM) + bool isLowEndDevice() { + return getRamMb() < 2048; + } + + /// Check if device is mid-range (2-6GB RAM) + bool isMidRangeDevice() { + final ram = getRamMb(); + return ram >= 2048 && ram <= 6144; + } + + /// Check if device is high-end (> 6GB RAM) + bool isHighEndDevice() { + return getRamMb() > 6144; + } + + /// Get recommended VOD buffer size in bytes + /// Adjusted based on available RAM to prevent OOM + int getRecommendedVodBufferBytes() { + final ram = getRamMb(); + + // Buffer size strategy: + // Low-end (<2GB): 20MB buffer (very conservative) + // Mid-range (2-6GB): 50MB buffer (balanced) + // High-end (>6GB): 100MB buffer (maximum quality) + + if (ram < 2048) { + return 20 * 1024 * 1024; // 20MB + } else if (ram < 6144) { + return 50 * 1024 * 1024; // 50MB + } else { + return 100 * 1024 * 1024; // 100MB + } + } + + /// Get recommended live TV buffer size in seconds + int getRecommendedLiveBufferSeconds() { + final ram = getRamMb(); + + if (ram < 2048) { + return 10; // Very conservative + } else if (ram < 6144) { + return 30; // Balanced + } else { + return 60; // Maximum + } + } + + /// Get device profile string for logging + String getDeviceProfile() { + final ram = getRamMb(); + if (ram < 2048) { + return 'Low-End (<2GB)'; + } else if (ram < 6144) { + return 'Mid-Range (2-6GB)'; + } else { + return 'High-End (>6GB)'; + } + } +} diff --git a/lib/features/iptv/screens/native_player_screen.dart b/lib/features/iptv/screens/native_player_screen.dart index 34415fc..8d7b2e7 100644 --- a/lib/features/iptv/screens/native_player_screen.dart +++ b/lib/features/iptv/screens/native_player_screen.dart @@ -15,6 +15,7 @@ import 'package:xtremflow/core/theme/app_colors.dart'; import 'package:xtremflow/features/iptv/screens/lite_player_screen.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:xtremflow/mobile/providers/mobile_xtream_providers.dart'; +import 'package:xtremflow/core/utils/device_info.dart'; /// Stream type enum for player enum StreamType { live, vod, series } @@ -181,20 +182,27 @@ class _NativePlayerScreenState extends ConsumerState (_player.platform as dynamic)?.setProperty('cache-pause-initial', 'no'); // (_player.platform as dynamic)?.setProperty('cache-pause-wait', '3'); } else { - // VOD PROFILE: Max stability, large buffer - // VOD PROFILE: Max stability, moderate buffer (reduced from 100MB to fix startup heap issues) - debugPrint('MediaKitPlayer: Applying VOD optimization profile (FIX)'); + // [P2-2 FIX] VOD PROFILE: Adaptive buffer size based on device RAM + // - Low-end (<2GB): 20MB (conservative, prevent OOM) + // - Mid-range (2-6GB): 50MB (balanced) + // - High-end (>6GB): 100MB (maximum quality) + final deviceInfo = DeviceInfo(); + final bufferBytes = deviceInfo.getRecommendedVodBufferBytes(); + final backBytes = (bufferBytes / 10).toInt(); // 10% for backward buffer + + debugPrint( + 'MediaKitPlayer: VOD profile - ${deviceInfo.getDeviceProfile()}, buffer=${bufferBytes ~/ (1024 * 1024)}MB'); + (_player.platform as dynamic)?.setProperty('cache-secs', '100'); (_player.platform as dynamic) - ?.setProperty('demuxer-max-bytes', '100000000'); // 100MB + ?.setProperty('demuxer-max-bytes', bufferBytes.toString()); (_player.platform as dynamic) ?.setProperty('demuxer-readahead-secs', '60'); (_player.platform as dynamic) - ?.setProperty('demuxer-max-back-bytes', '10000000'); // 10MB back + ?.setProperty('demuxer-max-back-bytes', backBytes.toString()); // Disable initial cache pause to allow immediate playback attempt (_player.platform as dynamic)?.setProperty('cache-pause-initial', 'no'); - // (_player.platform as dynamic)?.setProperty('cache-pause-wait', '5'); } // Audio/Video Sync - critical for smooth playback