- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 61
Open
Description
Describe the bug
I started using the latest version (from git) on Windows 11, with latest NVidia drivers. My code used to have smooth playback on windows 10 (please note I am not sure win11 upgrade is to blame but I thought I might mention it). but now I have  a lot of logs in my console that says:
00000242E60421D0>500 00:00:00.5/01:45:02 cache 0v 3.6s/5732KB 982KB/s |+3|<4ms update 40.8ms 25.0fps draw 0/2 +0ms
00000242E60421D0>626 00:00:00.6/01:45:02 cache 0v 3.6s/6152KB 994KB/s |+0|<4ms update 41.3ms 23.9fps draw 0/2 +0ms
00000242E60421D0>755 00:00:00.7/01:45:02 cache 0v 3.6s/6224KB 1203KB/s |-5|>4ms update 41.6ms 24.1fps draw 0/2 +0ms
00000242E60421D0>876 00:00:00.8/01:45:02 cache 0v 3.7s/6626KB 988KB/s |+0|<4ms update 41.4ms 24.0fps draw 0/2 +0ms
00000242E60421D0>1004 00:00:01.0/01:45:02 cache 0v 3.7s/7135KB 869KB/s |-3|<4ms update 42.0ms 24.0fps draw 0/0 +0ms
00000242E60421D0>1126 00:00:01.1/01:45:02 cache 0v 3.6s/7190KB 1035KB/s |+0|<4ms update 41.8ms 23.9fps draw 0/0 +0ms
00000242E60421D0>1255 00:00:01.2/01:45:02 cache 0v 3.7s/7677KB 1067KB/s |-4|>4ms update 41.9ms 24.0fps draw 0/0 +0ms
00000242E60421D0>1376 00:00:01.3/01:45:02 cache 0v 3.5s/8131KB 9869KB/s |+0|<4ms update 41.7ms 23.8fps draw 0/0 +0ms
00000242E60421D0>1505 00:00:01.5/01:45:02 cache 0v 3.7s/8204KB 1165KB/s |-4|>4ms update 42.0ms 23.9fps draw 0/0 +0ms
00000242E60421D0>1627 00:00:01.6/01:45:02 cache 0v 3.6s/8655KB 1094KB/s |+1|<4ms update 41.3ms 23.8fps draw 0/0 +0ms
It is not very clear what they mean but since the playback is choppy, I am assuming it is related
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:fvp/fvp.dart' as fvp;
import 'package:fvp/src/video_player_mdk.dart';
import 'package:fvp/src/global.dart';
import 'package:video_player_platform_interface/video_player_platform_interface.dart';
import 'base_player.dart';
/// FVP (libmpv/mdk) implementation of BasePlayerController
class FvpPlayerImpl extends BasePlayerController {
  final MdkVideoPlayer _player = MdkVideoPlayer();
  final List<VoidCallback> _listeners = [];
  Timer? _pollTimer;
  StreamSubscription<VideoEvent>? _eventSubscription;
  Size? _textureSize;
  Duration _position = Duration.zero;
  Duration _duration = Duration.zero;
  bool _isPlaying = false;
  FvpPlayerImpl(String url) {
    print('[FVP] Creating player for URL: $url');
    // Configure video decoders based on platform
    const vd = {
      'windows': ['CUDA', "D3D11", 'FFmpeg'],
      'macos': ['VT', 'FFmpeg'],
      'ios': ['VT', 'FFmpeg'],
      'linux': ['VAAPI', 'CUDA', 'VDPAU', 'FFmpeg'],
      'android': ['AMediaCodec', 'FFmpeg'],
    };
    final platform = Platform.operatingSystem.toLowerCase();
    _player.videoDecoders = vd[platform]!;
    print('[FVP] Video decoders set for $platform: ${_player.videoDecoders}');
    // Set media URL - this must be done before updateTexture()
    _player.media = url;
    print('[FVP] Media URL set: ${_player.media}');
    // Configure buffer range for smooth playback
    _player.setBufferRange(min: 3000, max: 30000);
    // print('[FVP] Buffer range configured: 3s - 30s');
    // Listen to video events
    _eventSubscription = _player.streamCtl.stream.listen((event) {
      switch (event.eventType) {
        case VideoEventType.initialized:
          _duration = event.duration ?? Duration.zero;
          _notifyListeners();
          break;
        case VideoEventType.completed:
          _position = _duration;
          _isPlaying = false;
          _notifyListeners();
          break;
        case VideoEventType.isPlayingStateUpdate:
          _isPlaying = event.isPlaying ?? false;
          _notifyListeners();
          break;
        default:
          break;
      }
    });
    _pollTimer = Timer.periodic(Duration(milliseconds: 100), (_) {
      _updatePosition();
    });
  }
  void _updatePosition() {
    final newPosition = Duration(milliseconds: _player.position);
    if (newPosition != _position) {
      _position = newPosition;
      _notifyListeners();
    }
  }
  void _notifyListeners() {
    for (final listener in _listeners) {
      listener();
    }
  }
  @override
  Future<void> initialize() async {
    print('[FVP_INIT] Initializing FvpPlayerImpl');
    // Register FVP plugin
    fvp.registerWith();
    // Initialize subtitle tracks
    _player.activeSubtitleTracks = [];
    // Temporarily set to playing to trigger media loading
    print('[FVP] Setting state to playing to trigger loading...');
    _player.state = PlaybackState.playing;
    // Update texture - this triggers texture creation
    _player.updateTexture();
  }
  @override
  Future<void> play() async {
    _player.state = PlaybackState.playing;
  }
  @override
  Future<void> pause() async {
    _player.state = PlaybackState.paused;
  }
  @override
  Future<void> seekTo(Duration position) async {
    print('[FVP] Seeking to ${position.inSeconds}s');
    _player.seek(position: position.inSeconds);
    // Force position update and notify listeners
    _updatePosition();
  }
  @override
  Future<void> dispose() async {
    _pollTimer?.cancel();
    await _eventSubscription?.cancel();
    _player.dispose();
  }
  @override
  bool get isPlaying => _isPlaying;
  @override
  Duration get position => _position;
  @override
  Duration get duration => _duration;
  @override
  double get aspectRatio {
    if (_textureSize != null && _textureSize!.height > 0) {
      return _textureSize!.width / _textureSize!.height;
    }
    return 16 / 9;
  }
  @override
  void addListener(VoidCallback listener) {
    _listeners.add(listener);
  }
  @override
  void removeListener(VoidCallback listener) {
    _listeners.remove(listener);
  }
  @override
  Future<void> setSubtitleTrack(String? url) async {
    if (url == null) {
      _player.activeSubtitleTracks = [];
    } else {
      _player.setMedia(url, MediaType.subtitle);
    }
  }
  @override
  Future<void> setSubtitleTrackByIndex(int trackIndex) async {
    if (trackIndex == -1) {
      _player.activeSubtitleTracks = [];
    } else {
      _player.activeSubtitleTracks = [trackIndex];
    }
  }
  @override
  List<SubtitleTrackInfo> get availableSubtitles {
    final subtitleTracks = _player.mediaInfo.subtitle ?? [];
    return subtitleTracks.asMap().entries.map((entry) {
      final track = entry.value;
      final title = track.metadata['title'] as String?;
      final language = track.metadata['language'] as String?;
      return SubtitleTrackInfo(
        title: title,
        language: language,
        index: entry.key,
      );
    }).toList();
  }
  @override
  List<AudioTrackInfo> get availableAudioTracks {
    final audioTracks = _player.mediaInfo.audio ?? [];
    return audioTracks.asMap().entries.map((entry) {
      final track = entry.value;
      final title = track.metadata['title'] as String?;
      final language = track.metadata['language'] as String?;
      final channels = track.metadata['channels'] as int?;
      return AudioTrackInfo(
        title: title,
        language: language,
        channels: channels,
        index: entry.key,
      );
    }).toList();
  }
  @override
  Future<void> setAudioTrack(int trackIndex) async {
    _player.activeAudioTracks = [trackIndex];
  }
  @override
  Widget createVideoWidget() {
    return RepaintBoundary(
              child: VideoTexture(
                player: _player,
              ),
            );  
  }
}
/// Separate widget to handle FVP texture rendering
class VideoTexture extends StatefulWidget {
  final MdkVideoPlayer player;
  const VideoTexture({required this.player, super.key});
  @override
  State<VideoTexture> createState() => _VideoTextureState();
}
class _VideoTextureState extends State<VideoTexture> {
  Size? _textureSize;
  @override
  void initState() {
    super.initState();
    _getTextureSize();
  }
  Future<void> _getTextureSize() async {
    final videoPlayerPlatform =
        VideoPlayerPlatform.instance as MdkVideoPlayerPlatform;
    if (widget.player != null) {
      final size = await widget.player.textureSize;
      if (size != null && mounted) {
        setState(() {
          _textureSize = size;
        });
      }
    }
  }
  @override
  Widget build(BuildContext context) {
    if (_textureSize == null) {
      return const Center(child: CircularProgressIndicator());
    }
    Future.delayed(
      const Duration(milliseconds: 15),
      () => setState(() {}),
    );
    return AspectRatio(
      aspectRatio: _textureSize!.width / _textureSize!.height,
      child: Texture(
        // filterQuality: FilterQuality.none,
        textureId: widget.player.textureId.value ?? 0,
      ),
    );
  }
}
Metadata
Metadata
Assignees
Labels
No labels