viewer: improved video controller disposing

This commit is contained in:
Thibault Deckers 2025-04-03 22:59:48 +02:00
parent 1fd3d77bf9
commit 969187444b
2 changed files with 19 additions and 12 deletions

View file

@ -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<AvesVideoController> _controllers = [];
final List<StreamSubscription> _subscriptions = [];
final Map<AvesVideoController, StreamSubscription> _subscriptions = {};
final PlaybackStateHandler _playbackStateHandler = DatabasePlaybackStateHandler();
final ValueNotifier<AvesVideoController?> 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<AvesVideoController> 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<void> _applyToAll(FutureOr Function(AvesVideoController controller) action) => Future.forEach<AvesVideoController>(_controllers, action);
Future<void> _disposeAll() => _applyToAll((controller) => controller.dispose());
Future<void> _disposeAll() => _applyToAll(_disposeController);
Future<void> pauseAll() => _applyToAll((controller) => controller.pause());
Future<void> muteAll(bool muted) => _applyToAll((controller) => controller.mute(muted));
Future<void> _disposeController(AvesVideoController controller) async {
await _subscriptions.remove(controller)?.cancel();
await controller.dispose();
}
}

View file

@ -130,7 +130,7 @@ mixin EntryViewControllerMixin<T extends StatefulWidget> on State<T> {
}
Future<void> _initVideoController(AvesEntry entry) async {
final controller = context.read<VideoConductor>().getOrCreateController(entry);
final controller = await context.read<VideoConductor>().getOrCreateController(entry);
setState(() {});
if (videoAutoPlayEnabled || entry.isAnimated) {
@ -157,7 +157,9 @@ mixin EntryViewControllerMixin<T extends StatefulWidget> on State<T> {
if (videoPageEntries.isNotEmpty) {
// init video controllers for all pages that could need it
final videoConductor = context.read<VideoConductor>();
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<void> _onPageChanged() async {