From 969187444ba77cc5aa8ac3e5fd1fd2cdceef0887 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Thu, 3 Apr 2025 22:59:48 +0200 Subject: [PATCH] viewer: improved video controller disposing --- lib/widgets/viewer/video/conductor.dart | 25 +++++++++++-------- .../viewer/visual/controller_mixin.dart | 6 +++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/widgets/viewer/video/conductor.dart b/lib/widgets/viewer/video/conductor.dart index cf409183d..519e3eceb 100644 --- a/lib/widgets/viewer/video/conductor.dart +++ b/lib/widgets/viewer/video/conductor.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/props.dart'; @@ -15,7 +16,7 @@ import 'package:leak_tracker/leak_tracker.dart'; class VideoConductor { final CollectionLens? _collection; final List _controllers = []; - final List _subscriptions = []; + final Map _subscriptions = {}; final PlaybackStateHandler _playbackStateHandler = DatabasePlaybackStateHandler(); final ValueNotifier playingVideoControllerNotifier = ValueNotifier(null); @@ -36,9 +37,6 @@ class VideoConductor { if (kFlutterMemoryAllocationsEnabled) { LeakTracking.dispatchObjectDisposed(object: this); } - _subscriptions - ..forEach((sub) => sub.cancel()) - ..clear(); await _disposeAll(); playingVideoControllerNotifier.dispose(); _controllers.clear(); @@ -47,22 +45,24 @@ class VideoConductor { } } - AvesVideoController getOrCreateController(AvesEntry entry, {int? maxControllerCount}) { + Future getOrCreateController(AvesEntry entry, {int? maxControllerCount}) async { var controller = getController(entry); if (controller != null) { _controllers.remove(controller); } else { + maxControllerCount = max(_defaultMaxControllerCount, maxControllerCount ?? 0); + while (_controllers.length >= maxControllerCount) { + await _disposeController(_controllers.removeLast()); + } + await deviceService.requestGarbageCollection(); controller = videoControllerFactory.buildController( entry, playbackStateHandler: _playbackStateHandler, settings: settings, ); - _subscriptions.add(controller.statusStream.listen((event) => _onControllerStatusChanged(entry, controller!, event))); + _subscriptions[controller] = controller.statusStream.listen((event) => _onControllerStatusChanged(entry, controller!, event)); } _controllers.insert(0, controller); - while (_controllers.length > (maxControllerCount ?? _defaultMaxControllerCount)) { - _controllers.removeLast().dispose(); - } return controller; } @@ -99,9 +99,14 @@ class VideoConductor { Future _applyToAll(FutureOr Function(AvesVideoController controller) action) => Future.forEach(_controllers, action); - Future _disposeAll() => _applyToAll((controller) => controller.dispose()); + Future _disposeAll() => _applyToAll(_disposeController); Future pauseAll() => _applyToAll((controller) => controller.pause()); Future muteAll(bool muted) => _applyToAll((controller) => controller.mute(muted)); + + Future _disposeController(AvesVideoController controller) async { + await _subscriptions.remove(controller)?.cancel(); + await controller.dispose(); + } } diff --git a/lib/widgets/viewer/visual/controller_mixin.dart b/lib/widgets/viewer/visual/controller_mixin.dart index c521144da..f96ed8f05 100644 --- a/lib/widgets/viewer/visual/controller_mixin.dart +++ b/lib/widgets/viewer/visual/controller_mixin.dart @@ -130,7 +130,7 @@ mixin EntryViewControllerMixin on State { } Future _initVideoController(AvesEntry entry) async { - final controller = context.read().getOrCreateController(entry); + final controller = await context.read().getOrCreateController(entry); setState(() {}); if (videoAutoPlayEnabled || entry.isAnimated) { @@ -157,7 +157,9 @@ mixin EntryViewControllerMixin on State { if (videoPageEntries.isNotEmpty) { // init video controllers for all pages that could need it final videoConductor = context.read(); - videoPageEntries.forEach((entry) => videoConductor.getOrCreateController(entry, maxControllerCount: videoPageEntries.length)); + await Future.forEach(videoPageEntries, (entry) async { + await videoConductor.getOrCreateController(entry, maxControllerCount: videoPageEntries.length); + }); // auto play/pause when changing page Future _onPageChanged() async {