import 'dart:async'; import 'package:fvp/mdk.dart' as mdk; import '../../shared/utils/app_logger.dart'; import 'media_capability_analyzer.dart'; class MediaProbeService { static final MediaProbeService instance = MediaProbeService._(); MediaProbeService._(); static const _tag = 'MediaProbe'; final Map _cache = {}; Future probe( String url, { Duration timeout = const Duration(seconds: 5), }) async { final cached = _cache[url]; if (cached != null) { AppLogger.debug(_tag, 'probe: cache hit url=${_truncate(url)}'); return cached; } AppLogger.debug(_tag, 'probe: start url=${_truncate(url)}'); mdk.Player? player; try { player = mdk.Player() ..setProperty('avio.reconnect', '1') ..setProperty('avformat.extension_picky', '0') ..setProperty('avformat.allowed_segment_extensions', 'ALL'); player.media = url; final ret = await player.prepare().timeout(timeout, onTimeout: () => -10); ProbeResult? result; if (ret >= 0) { final video = player.mediaInfo.video; if (video != null && video.isNotEmpty) { final codec = video.first.codec; result = ProbeResult( videoCodec: codec.codec.isNotEmpty ? codec.codec : null, ); } } else { AppLogger.warn( _tag, 'probe: prepare failed ret=$ret url=${_truncate(url)}', ); } if (result != null) { _cache[url] = result; AppLogger.debug( _tag, 'probe: success url=${_truncate(url)} codec=${result.videoCodec}', ); } return result; } catch (e) { AppLogger.warn(_tag, 'probe: error url=${_truncate(url)}', e); return null; } finally { player?.dispose(); } } static String _truncate(String url) { return url.length > 50 ? '${url.substring(0, 50)}...' : url; } }