import 'dart:io'; import 'package:flutter/material.dart'; import '../mpv/mpv.dart'; import '../services/pip_service.dart'; import '../utils/app_logger.dart'; class VideoPIPManager { final Player player; VideoPIPManager({required this.player}); Size? _playerSize; Size? get playerSize => _playerSize; /// Callback to prepare video filter before entering PiP VoidCallback? onBeforeEnterPip; /// Update player size for PiP aspect ratio calculation void updatePlayerSize(Size size) { _playerSize = size; } ValueNotifier get isPipActive => PipService().isPipActive; /// Get current video dimensions (display or storage or fallback to viewport) Future<(int? width, int? height)> _getVideoDimensions() async { int? width; int? height; try { final dwidth = await player.getProperty('dwidth'); final dheight = await player.getProperty('dheight'); if (dwidth != null && dheight != null) { width = int.tryParse(dwidth); height = int.tryParse(dheight); } } catch (e) { appLogger.d('VideoPipManager: dwidth/dheight unavailable', error: e); } if (width == null || height == null) { try { final videoWidth = await player.getProperty('width'); final videoHeight = await player.getProperty('height'); if (videoWidth != null && videoHeight != null) { width = int.tryParse(videoWidth); height = int.tryParse(videoHeight); } } catch (e) { appLogger.d('VideoPipManager: width/height unavailable', error: e); } } width ??= _playerSize?.width.toInt(); height ??= _playerSize?.height.toInt(); return (width, height); } Future<(bool success, String? error)> togglePIP() async { final supported = await PipService.isSupported(); if (!supported) return (false, 'PiP not supported on this device'); // If PiP is already active, exit it if (isPipActive.value) { await PipService.exit(); return (true, null); } // Reset video filter to contain mode before entering PiP. Android, iOS, // and macOS all reuse the inline video surface/layer for PiP. if (Platform.isAndroid || Platform.isIOS || Platform.isMacOS) { onBeforeEnterPip?.call(); // Wait a frame for the filter change to take effect await Future.delayed(const Duration(milliseconds: 50)); } final dims = await _getVideoDimensions(); return await PipService.enter(width: dims.$1, height: dims.$2); } Future updateAutoPipState({required bool isPlaying}) async { if (!isPlaying) { await PipService.setAutoPipReady(ready: false); return; } final dims = await _getVideoDimensions(); await PipService.setAutoPipReady(ready: true, width: dims.$1, height: dims.$2); } /// Disable auto-PiP (called on dispose or when leaving player) Future disableAutoPip() async { await PipService.setAutoPipReady(ready: false); } }