diff --git a/android/build.gradle b/android/build.gradle index 2362dbadb..1c4c91d11 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { kotlin_version = '1.8.21' - agp_version = '8.0.2' + agp_version = '7.4.2' glide_version = '4.15.1' // AppGallery Connect plugin versions: https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-sdk-changenotes-0000001058732550 // TODO TLAD AppGallery Connect plugin v1.9.0.300 does not support Gradle 8+ diff --git a/lib/model/entry/entry.dart b/lib/model/entry/entry.dart index 13b1a5b5b..ab00acb3d 100644 --- a/lib/model/entry/entry.dart +++ b/lib/model/entry/entry.dart @@ -200,6 +200,7 @@ class AvesEntry with AvesEntryBase { _bestTitle = null; } + @override String? get path => _path; // directory path, without the trailing separator diff --git a/lib/model/entry/extensions/info.dart b/lib/model/entry/extensions/info.dart index 04b064a1d..6aa21abd3 100644 --- a/lib/model/entry/extensions/info.dart +++ b/lib/model/entry/extensions/info.dart @@ -71,7 +71,7 @@ extension ExtraAvesEntryInfo on AvesEntry { Future> _getStreamDirectories(BuildContext context) async { final directories = []; - final mediaInfo = await VideoMetadataFormatter.getVideoMetadata(this); + final mediaInfo = await videoMetadataFetcher.getMetadata(this); final formattedMediaTags = VideoMetadataFormatter.formatInfo(mediaInfo); if (formattedMediaTags.isNotEmpty) { diff --git a/lib/model/video/metadata.dart b/lib/model/video/metadata.dart index c7707fec5..2f4c51447 100644 --- a/lib/model/video/metadata.dart +++ b/lib/model/video/metadata.dart @@ -16,13 +16,10 @@ import 'package:aves/utils/math_utils.dart'; import 'package:aves/utils/string_utils.dart'; import 'package:aves/utils/time_utils.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:aves_video_ijk/aves_video_ijk.dart'; import 'package:collection/collection.dart'; -import 'package:fijkplayer/fijkplayer.dart'; import 'package:flutter/foundation.dart'; class VideoMetadataFormatter { - static bool _initializedFijkLog = false; static final _dateY4M2D2H2m2s2Pattern = RegExp(r'(\d{4})[-./:](\d{1,2})[-./:](\d{1,2})([ T](\d{1,2}):(\d{1,2}):(\d{1,2})( ([ap]\.? ?m\.?))?)?'); static final _ambiguousDatePatterns = { RegExp(r'^\d{2}[-/]\d{2}[-/]\d{4}$'), @@ -45,24 +42,8 @@ class VideoMetadataFormatter { Codecs.webm: 'WebM', }; - static Future getVideoMetadata(AvesEntry entry) async { - if (!_initializedFijkLog) { - _initializedFijkLog = true; - FijkLog.setLevel(FijkLogLevel.Warn); - } - final player = FijkPlayer(); - final info = await player.setDataSourceUntilPrepared(entry.uri).then((v) { - return player.getInfo(); - }).catchError((error) { - debugPrint('failed to get video metadata for entry=$entry, error=$error'); - return {}; - }); - await player.release(); - return info; - } - static Future> getLoadingMetadata(AvesEntry entry) async { - final mediaInfo = await getVideoMetadata(entry); + final mediaInfo = await videoMetadataFetcher.getMetadata(entry); final fields = {}; final streams = mediaInfo[Keys.streams]; @@ -87,7 +68,7 @@ class VideoMetadataFormatter { } static Future getCatalogMetadata(AvesEntry entry) async { - final mediaInfo = await getVideoMetadata(entry); + final mediaInfo = await videoMetadataFetcher.getMetadata(entry); // only consider values with at least 8 characters (yyyymmdd), // ignoring unset values like `0`, as well as year values like `2021` diff --git a/lib/services/common/services.dart b/lib/services/common/services.dart index 83c22a918..166ea147e 100644 --- a/lib/services/common/services.dart +++ b/lib/services/common/services.dart @@ -19,6 +19,8 @@ import 'package:aves_report/aves_report.dart'; import 'package:aves_report_platform/aves_report_platform.dart'; import 'package:aves_services/aves_services.dart'; import 'package:aves_services_platform/aves_services_platform.dart'; +import 'package:aves_video/aves_video.dart'; +import 'package:aves_video_ijk/aves_video_ijk.dart'; import 'package:get_it/get_it.dart'; import 'package:path/path.dart' as p; @@ -30,6 +32,8 @@ final SettingsStore settingsStore = SharedPrefSettingsStore(); final p.Context pContext = getIt(); final AvesAvailability availability = getIt(); final MetadataDb metadataDb = getIt(); +final AvesVideoControllerFactory videoControllerFactory = getIt(); +final AvesVideoMetadataFetcher videoMetadataFetcher = getIt(); final AppService appService = getIt(); final DeviceService deviceService = getIt(); @@ -50,6 +54,8 @@ void initPlatformServices() { getIt.registerLazySingleton(p.Context.new); getIt.registerLazySingleton(LiveAvesAvailability.new); getIt.registerLazySingleton(SqfliteMetadataDb.new); + getIt.registerLazySingleton(IjkVideoControllerFactory.new); + getIt.registerLazySingleton(IjkVideoMetadataFetcher.new); getIt.registerLazySingleton(PlatformAppService.new); getIt.registerLazySingleton(PlatformDeviceService.new); diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index acd7bd9c8..34b95099a 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -455,6 +455,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { settings.areAnimationsRemoved = await AccessibilityService.areAnimationsRemoved(); await _onTvLayoutChanged(); _monitorSettings(); + videoControllerFactory.init(); unawaited(_setupErrorReporting()); diff --git a/lib/widgets/viewer/action/video_action_delegate.dart b/lib/widgets/viewer/action/video_action_delegate.dart index e9a9d454f..9e49a2a60 100644 --- a/lib/widgets/viewer/action/video_action_delegate.dart +++ b/lib/widgets/viewer/action/video_action_delegate.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/location.dart'; @@ -56,7 +57,7 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix case EntryAction.videoTogglePlay: await _togglePlayPause(context, controller); case EntryAction.videoReplay10: - await controller.seekTo(controller.currentPosition - 10000); + await controller.seekTo(max(controller.currentPosition - 10000, 0)); case EntryAction.videoSkip10: await controller.seekTo(controller.currentPosition + 10000); case EntryAction.openVideo: @@ -189,6 +190,8 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix } Future _togglePlayPause(BuildContext context, AvesVideoController controller) async { + if (!context.mounted) return; + if (controller.isPlaying) { await controller.pause(); } else { diff --git a/lib/widgets/viewer/entry_viewer_stack.dart b/lib/widgets/viewer/entry_viewer_stack.dart index 03093cb45..baea7b6fe 100644 --- a/lib/widgets/viewer/entry_viewer_stack.dart +++ b/lib/widgets/viewer/entry_viewer_stack.dart @@ -579,11 +579,12 @@ class _EntryViewerStackState extends State with EntryViewContr required AvesVideoController controller, required EntryAction action, }) async { - await _videoActionDelegate.onActionSelected(context, entry, controller, action); if (action == EntryAction.videoToggleMute) { - final override = controller.isMuted; + final override = !controller.isMuted; videoMutedOverride = override; await context.read().muteAll(override); + } else { + await _videoActionDelegate.onActionSelected(context, entry, controller, action); } } diff --git a/lib/widgets/viewer/video/conductor.dart b/lib/widgets/viewer/video/conductor.dart index 67100990d..8557b20c2 100644 --- a/lib/widgets/viewer/video/conductor.dart +++ b/lib/widgets/viewer/video/conductor.dart @@ -8,7 +8,6 @@ import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/viewer/video/db_playback_state_handler.dart'; import 'package:aves_model/aves_model.dart'; import 'package:aves_video/aves_video.dart'; -import 'package:aves_video_ijk/aves_video_ijk.dart'; import 'package:collection/collection.dart'; class VideoConductor { @@ -37,7 +36,7 @@ class VideoConductor { if (controller != null) { _controllers.remove(controller); } else { - controller = IjkPlayerAvesVideoController( + controller = videoControllerFactory.buildController( entry, playbackStateHandler: playbackStateHandler, settings: settings, diff --git a/lib/widgets/viewer/video/flutter_vlc_player.dart b/lib/widgets/viewer/video/flutter_vlc_player.dart deleted file mode 100644 index 6fb16fe06..000000000 --- a/lib/widgets/viewer/video/flutter_vlc_player.dart +++ /dev/null @@ -1,107 +0,0 @@ -// import 'dart:async'; -// import 'dart:io'; -// -// import 'package:aves/model/entry.dart'; -// import 'package:aves_utils/aves_utils.dart'; -// import 'package:aves_video/aves_video.dart'; -// import 'package:flutter/material.dart'; -// import 'package:flutter/src/foundation/change_notifier.dart'; -// import 'package:flutter/src/widgets/framework.dart'; -// import 'package:flutter_vlc_player/flutter_vlc_player.dart'; -// import 'package:provider/provider.dart'; -// -// class VlcAvesVideoController extends AvesVideoController { -// VlcPlayerController _instance; -// final List _subscriptions = []; -// final StreamController _valueStreamController = StreamController.broadcast(); -// final AChangeNotifier _playFinishNotifier = AChangeNotifier(); -// -// Stream get _valueStream => _valueStreamController.stream; -// -// VlcAvesVideoController(); -// -// @override -// Future setDataSource(String uri, {int startMillis = 0}) async { -// _instance = VlcPlayerController.file( -// File(uri), -// ); -// _instance.addListener(_onValueChanged); -// _subscriptions.add(_valueStream.where((value) => value.isEnded).listen((_) => _playFinishNotifier.notifyListeners())); -// -// // update value stream to: -// // 1) trigger playability check -// // 2) show the `VlcPlayer` widget -// // 3) initialize its `PlatformView` -// // 4) complete `VlcPlayerController` initialization -// _valueStreamController.add(_instance.value); -// } -// -// @override -// void dispose() { -// _instance?.removeListener(_onValueChanged); -// _valueStreamController.close(); -// _subscriptions -// ..forEach((sub) => sub.cancel()) -// ..clear(); -// _instance?.dispose(); -// } -// -// void _onValueChanged() => _valueStreamController.add(_instance.value); -// -// @override -// Future play() => _instance.play(); -// -// @override -// Future pause() => _instance?.pause(); -// -// @override -// Future seekTo(int targetMillis) => _instance.seekTo(Duration(milliseconds: targetMillis)); -// -// @override -// Listenable get playCompletedListenable => _playFinishNotifier; -// -// @override -// VideoStatus get status => _instance?.value?.toAves ?? VideoStatus.idle; -// -// @override -// Stream get statusStream => _valueStream.map((value) => value.toAves); -// -// @override -// bool get isPlayable => _instance != null; -// -// @override -// int get duration => _instance?.value?.duration?.inMilliseconds; -// -// @override -// int get currentPosition => _instance?.value?.position?.inMilliseconds; -// -// @override -// Stream get positionStream => _valueStream.map((value) => value.position.inMilliseconds); -// -// @override -// Widget buildPlayerWidget(BuildContext context, AvesEntry entry) { -// // do not use `Magnifier` with `applyScale` enabled when using this widget, -// // as the original video size will be used to create the `PlatformView` -// // and a virtual display larger than the device screen may crash the app -// final mqWidth = MediaQuery.sizeOf(context).width; -// final displaySize = entry.displaySize; -// final ratio = mqWidth / displaySize.width; -// return SizedBox.fromSize( -// size: displaySize * ratio, -// child: VlcPlayer( -// controller: _instance, -// aspectRatio: entry.displayAspectRatio, -// ), -// ); -// } -// } -// -// extension ExtraVlcPlayerValue on VlcPlayerValue { -// VideoStatus get toAves { -// if (hasError) return VideoStatus.error; -// if (!isInitialized) return VideoStatus.idle; -// if (isEnded) return VideoStatus.completed; -// if (isPlaying) return VideoStatus.playing; -// return VideoStatus.paused; -// } -// } diff --git a/lib/widgets/viewer/video/video_player.dart b/lib/widgets/viewer/video/video_player.dart deleted file mode 100644 index 0aa9cf8c1..000000000 --- a/lib/widgets/viewer/video/video_player.dart +++ /dev/null @@ -1,83 +0,0 @@ -// import 'dart:async'; -// -// import 'package:aves/model/entry.dart'; -// import 'package:aves_utils/aves_utils.dart'; -// import 'package:aves_video/aves_video.dart'; -// import 'package:flutter/src/foundation/change_notifier.dart'; -// import 'package:flutter/src/widgets/framework.dart'; -// import 'package:video_player/video_player.dart'; -// -// class VideoPlayerAvesVideoController extends AvesVideoController { -// VideoPlayerController _instance; -// final List _subscriptions = []; -// final StreamController _valueStreamController = StreamController.broadcast(); -// final AChangeNotifier _playFinishNotifier = AChangeNotifier(); -// -// Stream get _valueStream => _valueStreamController.stream; -// -// VideoPlayerAvesVideoController(); -// -// @override -// Future setDataSource(String uri, {int startMillis = 0}) async { -// _instance = VideoPlayerController.network(uri); -// _instance.addListener(_onValueChanged); -// _subscriptions.add(_valueStream.where((value) => value.position > value.duration).listen((_) => _playFinishNotifier.notifyListeners())); -// -// await _instance.initialize(); -// await play(); -// } -// -// @override -// void dispose() { -// _instance?.removeListener(_onValueChanged); -// _valueStreamController.close(); -// _subscriptions -// ..forEach((sub) => sub.cancel()) -// ..clear(); -// _instance?.dispose(); -// } -// -// void _onValueChanged() => _valueStreamController.add(_instance.value); -// -// @override -// Future play() => _instance.play(); -// -// @override -// Future pause() => _instance?.pause(); -// -// @override -// Future seekTo(int targetMillis) => _instance.seekTo(Duration(milliseconds: targetMillis)); -// -// @override -// Listenable get playCompletedListenable => _playFinishNotifier; -// -// @override -// VideoStatus get status => _instance?.value?.toAves ?? VideoStatus.idle; -// -// @override -// Stream get statusStream => _valueStream.map((value) => value.toAves); -// -// @override -// bool get isPlayable => _instance != null && _instance.value.isInitialized && !_instance.value.hasError; -// -// @override -// int get duration => _instance?.value?.duration?.inMilliseconds; -// -// @override -// int get currentPosition => _instance?.value?.position?.inMilliseconds; -// -// @override -// Stream get positionStream => _valueStream.map((value) => value.position.inMilliseconds); -// -// @override -// Widget buildPlayerWidget(BuildContext context, AvesEntry entry) => VideoPlayer(_instance); -// } -// -// extension ExtraVideoPlayerValue on VideoPlayerValue { -// VideoStatus get toAves { -// if (hasError) return VideoStatus.error; -// if (!isInitialized) return VideoStatus.idle; -// if (isPlaying) return VideoStatus.playing; -// return VideoStatus.paused; -// } -// } diff --git a/lib/widgets/viewer/visual/controller_mixin.dart b/lib/widgets/viewer/visual/controller_mixin.dart index 52235df48..455337b33 100644 --- a/lib/widgets/viewer/visual/controller_mixin.dart +++ b/lib/widgets/viewer/visual/controller_mixin.dart @@ -128,7 +128,7 @@ mixin EntryViewControllerMixin on State { if (videoAutoPlayEnabled) { final resumeTimeMillis = await controller.getResumeTime(context); - await _playVideo(controller, () => entry == entryNotifier.value, resumeTimeMillis: resumeTimeMillis); + await _autoPlayVideo(controller, () => entry == entryNotifier.value, resumeTimeMillis: resumeTimeMillis); } } @@ -163,7 +163,7 @@ mixin EntryViewControllerMixin on State { final pageVideoController = videoConductor.getController(pageEntry); assert(pageVideoController != null); if (pageVideoController != null) { - await _playVideo(pageVideoController, () => entry == entryNotifier.value && page == multiPageController.page); + await _autoPlayVideo(pageVideoController, () => entry == entryNotifier.value && page == multiPageController.page); } } } @@ -192,7 +192,7 @@ mixin EntryViewControllerMixin on State { } } - Future _playVideo(AvesVideoController videoController, bool Function() isCurrent, {int? resumeTimeMillis}) async { + Future _autoPlayVideo(AvesVideoController videoController, bool Function() isCurrent, {int? resumeTimeMillis}) async { // video decoding may fail or have initial artifacts when the player initializes // during this widget initialization (because of the page transition and hero animation?) // so we play after a delay for increased stability @@ -204,9 +204,8 @@ mixin EntryViewControllerMixin on State { if (resumeTimeMillis != null) { await videoController.seekTo(resumeTimeMillis); - } else { - await videoController.play(); } + await videoController.play(); // playing controllers are paused when the entry changes, // but the controller may still be preparing (not yet playing) when this happens diff --git a/plugins/aves_magnifier/pubspec.lock b/plugins/aves_magnifier/pubspec.lock index 4ef092e2f..6f637c20f 100644 --- a/plugins/aves_magnifier/pubspec.lock +++ b/plugins/aves_magnifier/pubspec.lock @@ -41,10 +41,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_map/pubspec.lock b/plugins/aves_map/pubspec.lock index 74fd8aa4e..1f7d511c0 100644 --- a/plugins/aves_map/pubspec.lock +++ b/plugins/aves_map/pubspec.lock @@ -73,10 +73,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_map: dependency: "direct main" description: @@ -89,10 +89,10 @@ packages: dependency: transitive description: name: http - sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" http_parser: dependency: transitive description: diff --git a/plugins/aves_model/lib/src/entry/base.dart b/plugins/aves_model/lib/src/entry/base.dart index 01966327d..4765bb9c4 100644 --- a/plugins/aves_model/lib/src/entry/base.dart +++ b/plugins/aves_model/lib/src/entry/base.dart @@ -9,6 +9,8 @@ mixin AvesEntryBase { int? get pageId; + String? get path; + int? get sizeBytes; int? get durationMillis; diff --git a/plugins/aves_model/pubspec.lock b/plugins/aves_model/pubspec.lock index 269a124e9..d7c9f11b0 100644 --- a/plugins/aves_model/pubspec.lock +++ b/plugins/aves_model/pubspec.lock @@ -34,10 +34,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_platform_meta/pubspec.lock b/plugins/aves_platform_meta/pubspec.lock index f124f2410..814ba8455 100644 --- a/plugins/aves_platform_meta/pubspec.lock +++ b/plugins/aves_platform_meta/pubspec.lock @@ -26,10 +26,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_report/pubspec.lock b/plugins/aves_report/pubspec.lock index 36b4ccb1b..a03644380 100644 --- a/plugins/aves_report/pubspec.lock +++ b/plugins/aves_report/pubspec.lock @@ -26,10 +26,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_report_console/pubspec.lock b/plugins/aves_report_console/pubspec.lock index 7c92c7b0a..3e283356e 100644 --- a/plugins/aves_report_console/pubspec.lock +++ b/plugins/aves_report_console/pubspec.lock @@ -33,10 +33,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_report_crashlytics/pubspec.lock b/plugins/aves_report_crashlytics/pubspec.lock index aaf45ae2f..20ede7fb4 100644 --- a/plugins/aves_report_crashlytics/pubspec.lock +++ b/plugins/aves_report_crashlytics/pubspec.lock @@ -113,10 +113,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_test: dependency: transitive description: flutter diff --git a/plugins/aves_screen_state/pubspec.lock b/plugins/aves_screen_state/pubspec.lock index f124f2410..814ba8455 100644 --- a/plugins/aves_screen_state/pubspec.lock +++ b/plugins/aves_screen_state/pubspec.lock @@ -26,10 +26,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_services/pubspec.lock b/plugins/aves_services/pubspec.lock index 33f4d23e7..5a0473d2a 100644 --- a/plugins/aves_services/pubspec.lock +++ b/plugins/aves_services/pubspec.lock @@ -80,10 +80,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_map: dependency: transitive description: @@ -96,10 +96,10 @@ packages: dependency: transitive description: name: http - sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" http_parser: dependency: transitive description: diff --git a/plugins/aves_services_google/pubspec.lock b/plugins/aves_services_google/pubspec.lock index aad8b46e8..b2a8e4299 100644 --- a/plugins/aves_services_google/pubspec.lock +++ b/plugins/aves_services_google/pubspec.lock @@ -127,10 +127,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_map: dependency: transitive description: @@ -220,10 +220,10 @@ packages: dependency: transitive description: name: google_maps_flutter_web - sha256: "5f58d7c491240b0074f455e70ce8d9b038f92472559e49e3b611d9f39b8d51a7" + sha256: "280170a2dcac3364317b5786f0d2e3c4128fdb795bc0d87ffe56226b0cf1f57d" url: "https://pub.dev" source: hosted - version: "0.5.0+1" + version: "0.5.1" html: dependency: transitive description: @@ -236,10 +236,10 @@ packages: dependency: transitive description: name: http - sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" http_parser: dependency: transitive description: @@ -441,10 +441,10 @@ packages: dependency: transitive description: name: win32 - sha256: "1414f27dd781737e51afa9711f2ac2ace6ab4498ee98e20863fa5505aa00c58c" + sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee url: "https://pub.dev" source: hosted - version: "5.0.4" + version: "5.0.5" win32_registry: dependency: transitive description: diff --git a/plugins/aves_services_huawei/pubspec.lock b/plugins/aves_services_huawei/pubspec.lock index b4b26f4e8..1f54f3d74 100644 --- a/plugins/aves_services_huawei/pubspec.lock +++ b/plugins/aves_services_huawei/pubspec.lock @@ -94,10 +94,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_map: dependency: transitive description: @@ -110,10 +110,10 @@ packages: dependency: transitive description: name: http - sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" http_parser: dependency: transitive description: diff --git a/plugins/aves_services_none/pubspec.lock b/plugins/aves_services_none/pubspec.lock index ece40bb07..35094eafb 100644 --- a/plugins/aves_services_none/pubspec.lock +++ b/plugins/aves_services_none/pubspec.lock @@ -87,10 +87,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_map: dependency: transitive description: @@ -103,10 +103,10 @@ packages: dependency: transitive description: name: http - sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" http_parser: dependency: transitive description: diff --git a/plugins/aves_ui/pubspec.lock b/plugins/aves_ui/pubspec.lock index e352be627..33dada52b 100644 --- a/plugins/aves_ui/pubspec.lock +++ b/plugins/aves_ui/pubspec.lock @@ -26,10 +26,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_utils/pubspec.lock b/plugins/aves_utils/pubspec.lock index 71c318804..ae78041a2 100644 --- a/plugins/aves_utils/pubspec.lock +++ b/plugins/aves_utils/pubspec.lock @@ -26,10 +26,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_video/lib/aves_video.dart b/plugins/aves_video/lib/aves_video.dart index 8bafd4faa..68ff7c235 100644 --- a/plugins/aves_video/lib/aves_video.dart +++ b/plugins/aves_video/lib/aves_video.dart @@ -1,6 +1,7 @@ library aves_video; export 'src/controller.dart'; +export 'src/metadata.dart'; export 'src/settings/subtitles.dart'; export 'src/settings/video.dart'; export 'src/stream.dart'; diff --git a/plugins/aves_video/lib/src/controller.dart b/plugins/aves_video/lib/src/controller.dart index 834fe228f..c428cb370 100644 --- a/plugins/aves_video/lib/src/controller.dart +++ b/plugins/aves_video/lib/src/controller.dart @@ -1,11 +1,20 @@ import 'dart:async'; import 'package:aves_model/aves_model.dart'; -import 'package:aves_video/src/settings/video.dart'; -import 'package:aves_video/src/stream.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; +abstract class AvesVideoControllerFactory { + void init(); + + AvesVideoController buildController( + AvesEntryBase entry, { + required PlaybackStateHandler playbackStateHandler, + required VideoSettings settings, + }); +} + abstract class AvesVideoController { final AvesEntryBase _entry; final PlaybackStateHandler playbackStateHandler; @@ -44,7 +53,7 @@ abstract class AvesVideoController { Future seekTo(int targetMillis); - Future seekToProgress(double progress) => seekTo((duration * progress).toInt()); + Future seekToProgress(double progress) => seekTo((duration * progress.clamp(0, 1)).toInt()); Listenable get playCompletedListenable; @@ -58,6 +67,18 @@ abstract class AvesVideoController { bool get isReady; + Future get untilReady { + if (isReady) return Future.value(); + + final completer = Completer(); + late StreamSubscription sub; + sub = statusStream.where((_) => isReady).listen((_) { + sub.cancel(); + completer.complete(); + }); + return completer.future; + } + bool get isPlaying => status == VideoStatus.playing; int get duration; diff --git a/plugins/aves_video/lib/src/metadata.dart b/plugins/aves_video/lib/src/metadata.dart new file mode 100644 index 000000000..21f699059 --- /dev/null +++ b/plugins/aves_video/lib/src/metadata.dart @@ -0,0 +1,9 @@ +import 'dart:async'; + +import 'package:aves_model/aves_model.dart'; + +abstract class AvesVideoMetadataFetcher { + void init(); + + Future getMetadata(AvesEntryBase entry); +} diff --git a/plugins/aves_video/lib/src/stream.dart b/plugins/aves_video/lib/src/stream.dart index 93c4bb05b..1d7548869 100644 --- a/plugins/aves_video/lib/src/stream.dart +++ b/plugins/aves_video/lib/src/stream.dart @@ -18,5 +18,5 @@ class MediaStreamSummary { }); @override - String toString() => '$runtimeType#${shortHash(this)}{type: type, index: $index, codecName: $codecName, language: $language, title: $title, width: $width, height: $height}'; + String toString() => '$runtimeType#${shortHash(this)}{type: $type, index: $index, codecName: $codecName, language: $language, title: $title, width: $width, height: $height}'; } diff --git a/plugins/aves_video/pubspec.lock b/plugins/aves_video/pubspec.lock index 57255f687..333c2a21c 100644 --- a/plugins/aves_video/pubspec.lock +++ b/plugins/aves_video/pubspec.lock @@ -48,10 +48,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_video_ijk/lib/aves_video_ijk.dart b/plugins/aves_video_ijk/lib/aves_video_ijk.dart index 2d580f1d1..4488c8daa 100644 --- a/plugins/aves_video_ijk/lib/aves_video_ijk.dart +++ b/plugins/aves_video_ijk/lib/aves_video_ijk.dart @@ -1,3 +1,5 @@ library aves_video_ijk; export 'src/controller.dart'; +export 'src/factory.dart'; +export 'src/metadata.dart'; diff --git a/plugins/aves_video_ijk/lib/src/controller.dart b/plugins/aves_video_ijk/lib/src/controller.dart index 4afb2e0e1..f9cb4ef35 100644 --- a/plugins/aves_video_ijk/lib/src/controller.dart +++ b/plugins/aves_video_ijk/lib/src/controller.dart @@ -8,9 +8,7 @@ import 'package:fijkplayer/fijkplayer.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -class IjkPlayerAvesVideoController extends AvesVideoController { - static bool _initializedFijkLog = false; - +class IjkVideoController extends AvesVideoController { final EventChannel _eventChannel = const OptionalEventChannel('befovy.com/fijk/event'); late FijkPlayer _instance; @@ -21,7 +19,6 @@ class IjkPlayerAvesVideoController extends AvesVideoController { final StreamController _speedStreamController = StreamController.broadcast(); final AChangeNotifier _completedNotifier = AChangeNotifier(); Offset _macroBlockCrop = Offset.zero; - final List _streams = []; Timer? _initialPlayTimer; double _speed = 1; double _volume = 1; @@ -58,15 +55,11 @@ class IjkPlayerAvesVideoController extends AvesVideoController { static const gifLikeBitRateThreshold = 2 << 18; // 512kB/s (4Mb/s) static const captureFrameEnabled = true; - IjkPlayerAvesVideoController( + IjkVideoController( super.entry, { required super.playbackStateHandler, required super.settings, }) { - if (!_initializedFijkLog) { - _initializedFijkLog = true; - FijkLog.setLevel(FijkLogLevel.Warn); - } _instance = FijkPlayer(); _valueStream.map((value) => value.videoRenderStart).firstWhere((v) => v, orElse: () => false).then( (started) { @@ -123,7 +116,7 @@ class IjkPlayerAvesVideoController extends AvesVideoController { } sarNotifier.value = 1; - _streams.clear(); + streams.clear(); _applyOptions(startMillis); // calling `setDataSource()` with `autoPlay` starts as soon as possible, but often yields initial artifacts @@ -247,56 +240,6 @@ class IjkPlayerAvesVideoController extends AvesVideoController { return true; } - void _fetchStreams() async { - final mediaInfo = await _instance.getInfo(); - if (!mediaInfo.containsKey(Keys.streams)) return; - - var videoStreamCount = 0, audioStreamCount = 0, textStreamCount = 0; - - _streams.clear(); - final allStreams = (mediaInfo[Keys.streams] as List).cast(); - allStreams.forEach((stream) { - final type = ExtraStreamType.fromTypeString(stream[Keys.streamType]); - if (type != null) { - final width = stream[Keys.width] as int?; - final height = stream[Keys.height] as int?; - _streams.add(MediaStreamSummary( - type: type, - index: stream[Keys.index], - codecName: stream[Keys.codecName], - language: stream[Keys.language], - title: stream[Keys.title], - width: width, - height: height, - )); - switch (type) { - case MediaStreamType.video: - // check width/height to exclude image streams (that are included among video streams) - if (width != null && height != null) { - videoStreamCount++; - } - case MediaStreamType.audio: - audioStreamCount++; - case MediaStreamType.text: - textStreamCount++; - } - } - }); - - canSelectStreamNotifier.value = videoStreamCount > 1 || audioStreamCount > 1 || textStreamCount > 0; - - final selectedVideo = await getSelectedStream(MediaStreamType.video); - if (selectedVideo != null) { - final streamIndex = selectedVideo.index; - final streamInfo = allStreams.firstWhereOrNull((stream) => stream[Keys.index] == streamIndex); - if (streamInfo != null) { - final num = streamInfo[Keys.sarNum] ?? 0; - final den = streamInfo[Keys.sarDen] ?? 0; - sarNotifier.value = (num != 0 ? num : 1) / (den != 0 ? den : 1); - } - } - } - // cf https://developer.android.com/reference/android/media/AudioManager static const int _audioFocusLoss = -1; static const int _audioFocusRequestFailed = 0; @@ -321,7 +264,7 @@ class IjkPlayerAvesVideoController extends AvesVideoController { } void _onValueChanged() { - if (_instance.state == FijkState.prepared && _streams.isEmpty) { + if (_instance.state == FijkState.prepared && streams.isEmpty) { _fetchStreams(); } _valueStreamController.add(_instance.value); @@ -427,39 +370,6 @@ class IjkPlayerAvesVideoController extends AvesVideoController { // TODO TLAD [video] bug: setting speed fails when there is no audio stream or audio is disabled Future _applySpeed() => _instance.setSpeed(speed); - // When a stream is selected, the video accelerates to catch up with it. - // The duration of this acceleration phase depends on the player `min-frames` parameter. - // Calling `seekTo` after stream de/selection is a workaround to: - // 1) prevent video stream acceleration to catch up with audio - // 2) apply timed text stream - @override - Future selectStream(MediaStreamType type, MediaStreamSummary? selected) async { - final current = await getSelectedStream(type); - if (current != selected) { - if (selected != null) { - final newIndex = selected.index; - if (newIndex != null) { - await _instance.selectTrack(newIndex); - } - } else if (current != null) { - await _instance.deselectTrack(current.index!); - } - if (type == MediaStreamType.text) { - _timedTextStreamController.add(null); - } - await seekTo(currentPosition); - } - } - - @override - Future getSelectedStream(MediaStreamType type) async { - final currentIndex = await _instance.getSelectedTrack(type.code); - return currentIndex != -1 ? _streams.firstWhereOrNull((v) => v.index == currentIndex) : null; - } - - @override - List get streams => _streams; - @override Future captureFrame() { if (!_instance.value.videoRenderStart) { @@ -503,6 +413,93 @@ class IjkPlayerAvesVideoController extends AvesVideoController { return Alignment.topLeft; } } + + // streams (aka tracks) + + final List _streams = []; + + @override + List get streams => _streams; + + void _fetchStreams() async { + final mediaInfo = await _instance.getInfo(); + if (!mediaInfo.containsKey(Keys.streams)) return; + + var videoStreamCount = 0, audioStreamCount = 0, textStreamCount = 0; + + _streams.clear(); + final allStreams = (mediaInfo[Keys.streams] as List).cast(); + allStreams.forEach((stream) { + final type = ExtraStreamType.fromTypeString(stream[Keys.streamType]); + if (type != null) { + final width = stream[Keys.width] as int?; + final height = stream[Keys.height] as int?; + _streams.add(MediaStreamSummary( + type: type, + index: stream[Keys.index], + codecName: stream[Keys.codecName], + language: stream[Keys.language], + title: stream[Keys.title], + width: width, + height: height, + )); + switch (type) { + case MediaStreamType.video: + // check width/height to exclude image streams (that are included among video streams) + if (width != null && height != null) { + videoStreamCount++; + } + case MediaStreamType.audio: + audioStreamCount++; + case MediaStreamType.text: + textStreamCount++; + } + } + }); + + canSelectStreamNotifier.value = videoStreamCount > 1 || audioStreamCount > 1 || textStreamCount > 0; + + final selectedVideo = await getSelectedStream(MediaStreamType.video); + if (selectedVideo != null) { + final streamIndex = selectedVideo.index; + final streamInfo = allStreams.firstWhereOrNull((stream) => stream[Keys.index] == streamIndex); + if (streamInfo != null) { + final num = streamInfo[Keys.sarNum] ?? 0; + final den = streamInfo[Keys.sarDen] ?? 0; + sarNotifier.value = (num != 0 ? num : 1) / (den != 0 ? den : 1); + } + } + } + + @override + Future getSelectedStream(MediaStreamType type) async { + final currentIndex = await _instance.getSelectedTrack(type.code); + return currentIndex != -1 ? _streams.firstWhereOrNull((v) => v.index == currentIndex) : null; + } + + // When a stream is selected, the video accelerates to catch up with it. + // The duration of this acceleration phase depends on the player `min-frames` parameter. + // Calling `seekTo` after stream de/selection is a workaround to: + // 1) prevent video stream acceleration to catch up with audio + // 2) apply timed text stream + @override + Future selectStream(MediaStreamType type, MediaStreamSummary? selected) async { + final current = await getSelectedStream(type); + if (current == selected) return; + + if (selected != null) { + final newIndex = selected.index; + if (newIndex != null) { + await _instance.selectTrack(newIndex); + } + } else if (current != null) { + await _instance.deselectTrack(current.index!); + } + if (type == MediaStreamType.text) { + _timedTextStreamController.add(null); + } + await seekTo(currentPosition); + } } extension ExtraIjkStatus on FijkState { diff --git a/plugins/aves_video_ijk/lib/src/factory.dart b/plugins/aves_video_ijk/lib/src/factory.dart new file mode 100644 index 000000000..c1fcdfca1 --- /dev/null +++ b/plugins/aves_video_ijk/lib/src/factory.dart @@ -0,0 +1,21 @@ +import 'package:aves_model/aves_model.dart'; +import 'package:aves_video/aves_video.dart'; +import 'package:aves_video_ijk/aves_video_ijk.dart'; +import 'package:fijkplayer/fijkplayer.dart'; + +class IjkVideoControllerFactory extends AvesVideoControllerFactory { + @override + void init() => FijkLog.setLevel(FijkLogLevel.Warn); + + @override + AvesVideoController buildController( + AvesEntryBase entry, { + required PlaybackStateHandler playbackStateHandler, + required VideoSettings settings, + }) => + IjkVideoController( + entry, + playbackStateHandler: playbackStateHandler, + settings: settings, + ); +} diff --git a/plugins/aves_video_ijk/lib/src/metadata.dart b/plugins/aves_video_ijk/lib/src/metadata.dart new file mode 100644 index 000000000..5434030ba --- /dev/null +++ b/plugins/aves_video_ijk/lib/src/metadata.dart @@ -0,0 +1,23 @@ +import 'package:aves_model/aves_model.dart'; +import 'package:aves_video/aves_video.dart'; +import 'package:aves_video_ijk/aves_video_ijk.dart'; +import 'package:fijkplayer/fijkplayer.dart'; +import 'package:flutter/foundation.dart'; + +class IjkVideoMetadataFetcher extends AvesVideoMetadataFetcher { + @override + void init() => FijkLog.setLevel(FijkLogLevel.Warn); + + @override + Future getMetadata(AvesEntryBase entry) async { + final player = FijkPlayer(); + final info = await player.setDataSourceUntilPrepared(entry.uri).then((v) { + return player.getInfo(); + }).catchError((error) { + debugPrint('failed to get video metadata for entry=$entry, error=$error'); + return {}; + }); + await player.release(); + return info; + } +} diff --git a/plugins/aves_video_ijk/pubspec.lock b/plugins/aves_video_ijk/pubspec.lock index 360c9ce06..7c3c71b4d 100644 --- a/plugins/aves_video_ijk/pubspec.lock +++ b/plugins/aves_video_ijk/pubspec.lock @@ -64,10 +64,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" js: dependency: transitive description: diff --git a/plugins/aves_video_mpv/.gitignore b/plugins/aves_video_mpv/.gitignore new file mode 100644 index 000000000..28124a571 --- /dev/null +++ b/plugins/aves_video_mpv/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +#/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/plugins/aves_video_mpv/.metadata b/plugins/aves_video_mpv/.metadata new file mode 100644 index 000000000..9596faeed --- /dev/null +++ b/plugins/aves_video_mpv/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/plugins/aves_video_mpv/analysis_options.yaml b/plugins/aves_video_mpv/analysis_options.yaml new file mode 100644 index 000000000..f04c6cf0f --- /dev/null +++ b/plugins/aves_video_mpv/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../analysis_options.yaml diff --git a/plugins/aves_video_mpv/lib/aves_video_mpv.dart b/plugins/aves_video_mpv/lib/aves_video_mpv.dart new file mode 100644 index 000000000..7fb338e98 --- /dev/null +++ b/plugins/aves_video_mpv/lib/aves_video_mpv.dart @@ -0,0 +1,4 @@ +library aves_video_mpv; + +export 'src/controller.dart'; +export 'src/factory.dart'; diff --git a/plugins/aves_video_mpv/lib/src/controller.dart b/plugins/aves_video_mpv/lib/src/controller.dart new file mode 100644 index 000000000..7f8a26879 --- /dev/null +++ b/plugins/aves_video_mpv/lib/src/controller.dart @@ -0,0 +1,368 @@ +import 'dart:async'; + +import 'package:aves_model/aves_model.dart'; +import 'package:aves_utils/aves_utils.dart'; +import 'package:aves_video/aves_video.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:media_kit/media_kit.dart'; +import 'package:media_kit_video/media_kit_video.dart'; + +class MpvVideoController extends AvesVideoController { + late Player _instance; + late VideoController _controller; + late VideoStatus _status; + final List _subscriptions = []; + final StreamController _statusStreamController = StreamController.broadcast(); + final StreamController _timedTextStreamController = StreamController.broadcast(); + final AChangeNotifier _completedNotifier = AChangeNotifier(); + + @override + double get minSpeed => .25; + + @override + double get maxSpeed => 4; + + @override + final ValueNotifier canCaptureFrameNotifier = ValueNotifier(false); + + @override + final ValueNotifier canMuteNotifier = ValueNotifier(true); + + @override + final ValueNotifier canSetSpeedNotifier = ValueNotifier(true); + + @override + final ValueNotifier canSelectStreamNotifier = ValueNotifier(false); + + @override + final ValueNotifier sarNotifier = ValueNotifier(1); + + MpvVideoController( + super.entry, { + required super.playbackStateHandler, + required super.settings, + }) { + _status = VideoStatus.idle; + _statusStreamController.add(_status); + + _instance = Player( + configuration: const PlayerConfiguration( + logLevel: MPVLogLevel.warn, + ), + ); + _initController(); + _init(); + + // TODO TLAD listening + // canCaptureFrameNotifier.value = captureFrameEnabled && started; + _startListening(); + } + + @override + Future dispose() async { + await super.dispose(); + _stopListening(); + _stopStreamFetchTimer(); + await _statusStreamController.close(); + await _timedTextStreamController.close(); + await _instance.dispose(); + } + + void _startListening() { + _subscriptions.add(statusStream.listen((v) => _status = v)); + _subscriptions.add(_instance.stream.completed.listen((v) { + if (v) { + _statusStreamController.add(VideoStatus.completed); + _completedNotifier.notify(); + } + })); + _subscriptions.add(_instance.stream.playing.listen((v) { + if (status == VideoStatus.idle) return; + _statusStreamController.add(v ? VideoStatus.playing : VideoStatus.paused); + })); + _subscriptions.add(_instance.stream.log.listen((v) => debugPrint('libmpv log: $v'))); + _subscriptions.add(_instance.stream.error.listen((v) => debugPrint('libmpv error: $v'))); + _subscriptions.add(settings.updateStream.where((event) => event.key == SettingKeys.enableVideoHardwareAccelerationKey).listen((_) => _initController())); + _subscriptions.add(settings.updateStream.where((event) => event.key == SettingKeys.videoLoopModeKey).listen((_) => _applyLoop())); + } + + void _stopListening() { + _subscriptions + ..forEach((sub) => sub.cancel()) + ..clear(); + } + + Future _applyLoop() async { + final loopEnabled = settings.videoLoopMode.shouldLoop(entry.durationMillis); + await _instance.setPlaylistMode(loopEnabled ? PlaylistMode.single : PlaylistMode.none); + } + + Future _init({int startMillis = 0}) async { + final playing = _instance.state.playing; + + await _applyLoop(); + await _instance.open(Media(entry.uri), play: playing); + if (startMillis > 0) { + await seekTo(startMillis); + } + + _fetchStreams(); + _statusStreamController.add(_instance.state.playing ? VideoStatus.playing : VideoStatus.paused); + } + + void _initController() { + _controller = VideoController( + _instance, + configuration: VideoControllerConfiguration( + enableHardwareAcceleration: settings.enableVideoHardwareAcceleration, + ), + ); + } + + @override + void onVisualChanged() => _init(startMillis: currentPosition); + + @override + Future play() async { + await untilReady; + await _instance.play(); + } + + @override + Future pause() => _instance.pause(); + + @override + Future seekTo(int targetMillis) async { + if (!isReady) { + await untilReady; + // When the player gets ready, it can play from the beginning right away, + // but trying to seek then just plays from the start. + // There is no state or hook identifying readiness to seek on start, + // and `PlayerConfiguration.ready` hook is useless. + await Future.delayed(const Duration(milliseconds: 500)); + } + await _instance.seek(Duration(milliseconds: targetMillis)); + } + + @override + Listenable get playCompletedListenable => _completedNotifier; + + @override + VideoStatus get status => _status; + + @override + Stream get statusStream => _statusStreamController.stream; + + @override + Stream get volumeStream => _instance.stream.volume; + + @override + Stream get speedStream => _instance.stream.rate; + + @override + bool get isReady { + switch (_status) { + case VideoStatus.error: + case VideoStatus.idle: + case VideoStatus.initialized: + return false; + case VideoStatus.paused: + case VideoStatus.playing: + case VideoStatus.completed: + return true; + } + } + + @override + int get duration => _instance.state.duration.inMilliseconds; + + @override + int get currentPosition => _instance.state.position.inMilliseconds; + + @override + Stream get positionStream => _instance.stream.position.map((pos) => pos.inMilliseconds); + + @override + Stream get timedTextStream => _timedTextStreamController.stream; + + @override + bool get isMuted => _instance.state.volume == 0; + + @override + Future mute(bool muted) => _instance.setVolume(muted ? 0 : 100); + + @override + double get speed => _instance.state.rate; + + @override + set speed(double speed) => _instance.setRate(speed); + + @override + Future captureFrame() { + // TODO: implement captureFrame + throw UnimplementedError(); + } + + @override + Widget buildPlayerWidget(BuildContext context) { + // TODO TLAD handle SAR / DAR (media_kit Player.stream.width/height just gives raw video size, not rendered size) + return Video( + controller: _controller, + fit: BoxFit.cover, + alignment: Alignment.center, + controls: NoVideoControls, + wakelock: false, + ); + } + + // streams (aka tracks) + + // `auto` and `no` are the first 2 tracks in the player state track lists + static const int fakeTrackCount = 2; + + Tracks get _tracks => _instance.state.tracks; + + List get _videoTracks => _tracks.video.skip(fakeTrackCount).toList(); + + List get _audioTracks => _tracks.audio.skip(fakeTrackCount).toList(); + + List get _subtitleTracks => _tracks.subtitle.skip(fakeTrackCount).toList(); + + @override + List get streams { + return { + ..._videoTracks.mapIndexed((i, v) => v.toAves(i)), + ..._audioTracks.mapIndexed((i, v) => v.toAves(i)), + ..._subtitleTracks.mapIndexed((i, v) => v.toAves(i)), + }.toList(); + } + + Timer? _streamFetchTimer; + + void _stopStreamFetchTimer() { + _streamFetchTimer?.cancel(); + _streamFetchTimer = null; + } + + void _fetchStreams() { + _stopStreamFetchTimer(); + _streamFetchTimer = Timer.periodic(const Duration(milliseconds: 100), (_) { + if (status != VideoStatus.error) { + if (_videoTracks.isEmpty && _audioTracks.isEmpty) return; + + final videoStreamCount = _videoTracks.length; + final audioStreamCount = _audioTracks.length; + final textStreamCount = _subtitleTracks.length; + canSelectStreamNotifier.value = videoStreamCount > 1 || audioStreamCount > 1 || textStreamCount > 0; + } + _stopStreamFetchTimer(); + }); + } + + @override + Future getSelectedStream(MediaStreamType type) async { + final track = _instance.state.track; + switch (type) { + case MediaStreamType.video: + final video = track.video; + if (video != VideoTrack.no()) { + final index = video == VideoTrack.auto() ? 0 : _videoTracks.indexOf(video); + return video.toAves(index); + } + case MediaStreamType.audio: + final audio = track.audio; + if (audio != AudioTrack.no()) { + final index = audio == AudioTrack.auto() ? 0 : _audioTracks.indexOf(audio); + return audio.toAves(index); + } + case MediaStreamType.text: + final subtitle = track.subtitle; + if (subtitle != SubtitleTrack.no()) { + final index = subtitle == SubtitleTrack.auto() ? 0 : _subtitleTracks.indexOf(subtitle); + return subtitle.toAves(index); + } + } + return null; + } + + @override + Future selectStream(MediaStreamType type, MediaStreamSummary? selected) async { + final current = await getSelectedStream(type); + if (current == selected) return; + + if (selected != null) { + final newIndex = selected.index; + if (newIndex != null) { + // select track + switch (type) { + case MediaStreamType.video: + await _instance.setVideoTrack(_videoTracks[selected.index ?? 0]); + break; + case MediaStreamType.audio: + await _instance.setAudioTrack(_audioTracks[selected.index ?? 0]); + break; + case MediaStreamType.text: + await _instance.setSubtitleTrack(_subtitleTracks[selected.index ?? 0]); + break; + } + } + } else if (current != null) { + // deselect track + switch (type) { + case MediaStreamType.video: + await _instance.setVideoTrack(VideoTrack.no()); + break; + case MediaStreamType.audio: + await _instance.setAudioTrack(AudioTrack.no()); + break; + case MediaStreamType.text: + await _instance.setSubtitleTrack(SubtitleTrack.no()); + break; + } + } + } +} + +extension ExtraVideoTrack on VideoTrack { + MediaStreamSummary toAves(int index) { + return MediaStreamSummary( + type: MediaStreamType.video, + index: index, + codecName: null, + language: language, + title: title, + width: null, + height: null, + ); + } +} + +extension ExtraAudioTrack on AudioTrack { + MediaStreamSummary toAves(int index) { + return MediaStreamSummary( + type: MediaStreamType.audio, + index: index, + codecName: null, + language: language, + title: title, + width: null, + height: null, + ); + } +} + +extension ExtraSubtitleTrack on SubtitleTrack { + MediaStreamSummary toAves(int index) { + return MediaStreamSummary( + type: MediaStreamType.text, + index: index, + codecName: null, + language: language, + title: title, + width: null, + height: null, + ); + } +} diff --git a/plugins/aves_video_mpv/lib/src/factory.dart b/plugins/aves_video_mpv/lib/src/factory.dart new file mode 100644 index 000000000..2fdbd7485 --- /dev/null +++ b/plugins/aves_video_mpv/lib/src/factory.dart @@ -0,0 +1,21 @@ +import 'package:aves_model/aves_model.dart'; +import 'package:aves_video/aves_video.dart'; +import 'package:aves_video_mpv/aves_video_mpv.dart'; +import 'package:media_kit/media_kit.dart'; + +class MpvVideoControllerFactory extends AvesVideoControllerFactory { + @override + void init() => MediaKit.ensureInitialized(); + + @override + AvesVideoController buildController( + AvesEntryBase entry, { + required PlaybackStateHandler playbackStateHandler, + required VideoSettings settings, + }) => + MpvVideoController( + entry, + playbackStateHandler: playbackStateHandler, + settings: settings, + ); +} diff --git a/plugins/aves_video_mpv/pubspec.lock b/plugins/aves_video_mpv/pubspec.lock new file mode 100644 index 000000000..a7ee52b7d --- /dev/null +++ b/plugins/aves_video_mpv/pubspec.lock @@ -0,0 +1,362 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + aves_model: + dependency: "direct main" + description: + path: "../aves_model" + relative: true + source: path + version: "0.0.1" + aves_utils: + dependency: "direct main" + description: + path: "../aves_utils" + relative: true + source: path + version: "0.0.1" + aves_video: + dependency: "direct main" + description: + path: "../aves_video" + relative: true + source: path + version: "0.0.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + collection: + dependency: "direct main" + description: + name: collection + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + url: "https://pub.dev" + source: hosted + version: "1.17.1" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + ffi: + dependency: transitive + description: + name: ffi + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + media_kit: + dependency: "direct main" + description: + name: media_kit + sha256: "8c7d9417bed724a3fcaadd91c722fea042737cafb153aa1f1e6461a0fee683a3" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + media_kit_libs_android_video: + dependency: "direct main" + description: + name: media_kit_libs_android_video + sha256: "228c3b182831e194bb178d4d22a1839af812c917cb76fe87d6fdc9ea4328dc81" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + media_kit_native_event_loop: + dependency: "direct main" + description: + name: media_kit_native_event_loop + sha256: "5351f0c28124b5358756515d8619abad182cdefe967468d7fb5b274737cc2f59" + url: "https://pub.dev" + source: hosted + version: "1.0.6" + media_kit_video: + dependency: "direct main" + description: + name: media_kit_video + sha256: d31a0eab80cafadccdedb663d8a127750e38b8c75c1aa83d8943f8119b88cf99 + url: "https://pub.dev" + source: hosted + version: "1.0.2" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + safe_local_storage: + dependency: transitive + description: + name: safe_local_storage + sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440 + url: "https://pub.dev" + source: hosted + version: "1.0.2" + screen_brightness: + dependency: transitive + description: + name: screen_brightness + sha256: "62fd61a64e68b32b98b840bad7d8b6822bbc40e63c2b569a5f85528484c86b41" + url: "https://pub.dev" + source: hosted + version: "0.2.2" + screen_brightness_android: + dependency: transitive + description: + name: screen_brightness_android + sha256: "3df10961e3a9e968a5e076fe27e7f4741fa8a1d3950bdeb48cf121ed529d0caf" + url: "https://pub.dev" + source: hosted + version: "0.1.0+2" + screen_brightness_ios: + dependency: transitive + description: + name: screen_brightness_ios + sha256: "99adc3ca5490b8294284aad5fcc87f061ad685050e03cf45d3d018fe398fd9a2" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + screen_brightness_macos: + dependency: transitive + description: + name: screen_brightness_macos + sha256: "64b34e7e3f4900d7687c8e8fb514246845a73ecec05ab53483ed025bd4a899fd" + url: "https://pub.dev" + source: hosted + version: "0.1.0+1" + screen_brightness_platform_interface: + dependency: transitive + description: + name: screen_brightness_platform_interface + sha256: b211d07f0c96637a15fb06f6168617e18030d5d74ad03795dd8547a52717c171 + url: "https://pub.dev" + source: hosted + version: "0.1.0" + screen_brightness_windows: + dependency: transitive + description: + name: screen_brightness_windows + sha256: "80d90ecdc63fc0823f2ecb1be323471619287937e14210650d7b25ca181abd05" + url: "https://pub.dev" + source: hosted + version: "0.1.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc + url: "https://pub.dev" + source: hosted + version: "1.0.0+1" + uri_parser: + dependency: transitive + description: + name: uri_parser + sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + volume_controller: + dependency: transitive + description: + name: volume_controller + sha256: "189bdc7a554f476b412e4c8b2f474562b09d74bc458c23667356bce3ca1d48c9" + url: "https://pub.dev" + source: hosted + version: "2.0.7" + wakelock: + dependency: transitive + description: + name: wakelock + sha256: "769ecf42eb2d07128407b50cb93d7c10bd2ee48f0276ef0119db1d25cc2f87db" + url: "https://pub.dev" + source: hosted + version: "0.6.2" + wakelock_macos: + dependency: transitive + description: + name: wakelock_macos + sha256: "047c6be2f88cb6b76d02553bca5a3a3b95323b15d30867eca53a19a0a319d4cd" + url: "https://pub.dev" + source: hosted + version: "0.4.0" + wakelock_platform_interface: + dependency: transitive + description: + name: wakelock_platform_interface + sha256: "1f4aeb81fb592b863da83d2d0f7b8196067451e4df91046c26b54a403f9de621" + url: "https://pub.dev" + source: hosted + version: "0.3.0" + wakelock_web: + dependency: transitive + description: + name: wakelock_web + sha256: "1b256b811ee3f0834888efddfe03da8d18d0819317f20f6193e2922b41a501b5" + url: "https://pub.dev" + source: hosted + version: "0.4.0" + wakelock_windows: + dependency: transitive + description: + name: wakelock_windows + sha256: "857f77b3fe6ae82dd045455baa626bc4b93cb9bb6c86bf3f27c182167c3a5567" + url: "https://pub.dev" + source: hosted + version: "0.2.1" + win32: + dependency: transitive + description: + name: win32 + sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 + url: "https://pub.dev" + source: hosted + version: "3.1.4" +sdks: + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.7.0" diff --git a/plugins/aves_video_mpv/pubspec.yaml b/plugins/aves_video_mpv/pubspec.yaml new file mode 100644 index 000000000..256c002dc --- /dev/null +++ b/plugins/aves_video_mpv/pubspec.yaml @@ -0,0 +1,36 @@ +name: aves_video_mpv +version: 0.0.1 +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + +dependencies: + flutter: + sdk: flutter + aves_model: + path: ../aves_model + aves_video: + path: ../aves_video + aves_utils: + path: ../aves_utils + collection: + media_kit: + media_kit_video: + media_kit_native_event_loop: + media_kit_libs_android_video: + +dev_dependencies: + flutter_lints: + +#dependency_overrides: +# media_kit: +# path: ../../../media_kit/media_kit +# media_kit_video: +# path: ../../../media_kit/media_kit_video +# media_kit_native_event_loop: +# path: ../../../media_kit/media_kit_native_event_loop +# media_kit_libs_android_video: +# path: ../../../media_kit/media_kit_libs_android_video + +flutter: diff --git a/pubspec.lock b/pubspec.lock index 874bda926..6af38e662 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -299,10 +299,10 @@ packages: dependency: "direct main" description: name: dynamic_color - sha256: "74dff1435a695887ca64899b8990004f8d1232b0e84bfc4faa1fdda7c6f57cc1" + sha256: de4798a7069121aee12d5895315680258415de9b00e717723a1bd73d58f0126d url: "https://pub.dev" source: hosted - version: "1.6.5" + version: "1.6.6" equatable: dependency: "direct main" description: @@ -345,7 +345,7 @@ packages: source: hosted version: "2.0.2" fijkplayer: - dependency: "direct main" + dependency: transitive description: path: "." ref: aves @@ -413,19 +413,18 @@ packages: dependency: transitive description: name: flex_seed_scheme - sha256: e4168a6fc88a3e5bc3d6b7a748c6a6083eedc193d343ddc26bbad7fb1b258555 + sha256: "29c12aba221eb8a368a119685371381f8035011d18de5ba277ad11d7dfb8657f" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" floating: dependency: "direct main" description: - path: "." - ref: main - resolved-ref: b073419d48f099b5855816a7c6e04d397b1f1c37 - url: "https://github.com/wrbl606/floating.git" - source: git - version: "2.0.0" + name: floating + sha256: d9d563089e34fbd714ffdcdd2df447ec41b40c9226dacae6b4f78847aef8b991 + url: "https://pub.dev" + source: hosted + version: "2.0.1" fluster: dependency: "direct main" description: @@ -472,10 +471,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_localization_nn: dependency: "direct main" description: @@ -502,10 +501,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: dc6d5258653f6857135b32896ccda7f7af0c54dcec832495ad6835154c6c77c0 + sha256: "86b76dbf30496024d6c816bdc13b97de9449dce1f035a73ee7b4ab7f67eab70b" url: "https://pub.dev" source: hosted - version: "0.6.15" + version: "0.6.16" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -629,10 +628,10 @@ packages: dependency: transitive description: name: google_maps_flutter_web - sha256: "5f58d7c491240b0074f455e70ce8d9b038f92472559e49e3b611d9f39b8d51a7" + sha256: "280170a2dcac3364317b5786f0d2e3c4128fdb795bc0d87ffe56226b0cf1f57d" url: "https://pub.dev" source: hosted - version: "0.5.0+1" + version: "0.5.1" highlight: dependency: transitive description: @@ -653,10 +652,10 @@ packages: dependency: transitive description: name: http - sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -991,42 +990,42 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "1b6b3e73f0bcbc856548bbdfb1c33084a401c4f143e220629a9055233d76c331" + sha256: "37fcc3c3182ac0bf8856f3e973e11c7bef5556d69f1a0d8fb908f51019c2912d" url: "https://pub.dev" source: hosted - version: "10.3.0" + version: "10.4.1" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "8f6a95ccbca13766882f95d32684d7c9bfe6c45650c32bedba948ef1c6a4ddf7" + sha256: "3b61f3da3b1c83bc3fb6a2b431e8dab01d0e5b45f6a3d9c7609770ec88b2a89e" url: "https://pub.dev" source: hosted - version: "10.2.3" + version: "10.3.0" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "08dcb6ce628ac0b257e429944b4c652c2a4e6af725bdf12b498daa2c6b2b1edb" + sha256: "0d1f8007b17573ff1fbeae0f04b6c8e83e1d2f6c4fe8e8226d4d2456aa8ecffe" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.2" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: de20a5c3269229c1ae2e5a6b822f6cb59578b23e8255c93fbeebfc82116e6b11 + sha256: "79b36d93a41a4aecfd0d635d77552f327cb84227c718ce5e68b5f7b85546fe7e" url: "https://pub.dev" source: hosted - version: "3.10.0" + version: "3.11.0+1" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 url: "https://pub.dev" source: hosted - version: "0.1.2" + version: "0.1.3" petitparser: dependency: transitive description: @@ -1039,10 +1038,10 @@ packages: dependency: "direct main" description: name: pin_code_fields - sha256: c8652519d14688f3fe2a8288d86910a46aa0b9046d728f292d3bf6067c31b4c7 + sha256: "4c0db7fbc889e622e7c71ea54b9ee624bb70c7365b532abea0271b17ea75b729" url: "https://pub.dev" source: hosted - version: "7.4.0" + version: "8.0.1" platform: dependency: transitive description: @@ -1191,58 +1190,58 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "396f85b8afc6865182610c0a2fc470853d56499f75f7499e2a73a9f0539d23d0" + sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.2.0" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749" + sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb + sha256: "0dc5c49ad8a05ed358b991b60c7b0ba1a14e16dae58af9b420d6b9e82dc024ab" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.3.0" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" + sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.0" shared_preferences_platform_interface: dependency: "direct dev" description: name: shared_preferences_platform_interface - sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d + sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" + sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" + sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.0" shelf: dependency: transitive description: @@ -1570,13 +1569,13 @@ packages: source: hosted version: "1.2.0" win32: - dependency: transitive + dependency: "direct overridden" description: name: win32 - sha256: "1414f27dd781737e51afa9711f2ac2ace6ab4498ee98e20863fa5505aa00c58c" + sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee url: "https://pub.dev" source: hosted - version: "5.0.4" + version: "5.0.5" win32_registry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f7f048526..89200fdc6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,8 @@ dependencies: path: plugins/aves_video aves_video_ijk: path: plugins/aves_video_ijk +# aves_video_mpv: +# path: plugins/aves_video_mpv aves_ui: path: plugins/aves_ui aves_utils: @@ -65,16 +67,8 @@ dependencies: expansion_tile_card: git: url: https://github.com/deckerst/expansion_tile_card.git - fijkplayer: - git: - url: https://github.com/deckerst/fijkplayer.git - ref: aves flex_color_picker: floating: - git: - url: https://github.com/wrbl606/floating.git - # v2.0.0 is incompatible with AGP8 - ref: main fluster: flutter_displaymode: flutter_highlight: @@ -130,6 +124,19 @@ dev_dependencies: shared_preferences_platform_interface: test: +dependency_overrides: + # `media_kit v1.0.0` depends on `wakelock: ^0.6.2` + # which is incompatible with packages that moved on with Dart 3 + win32: ^5.0.0 +# media_kit: +# path: ../media_kit/media_kit +# media_kit_video: +# path: ../media_kit/media_kit_video +# media_kit_native_event_loop: +# path: ../media_kit/media_kit_native_event_loop +# media_kit_libs_android_video: +# path: ../media_kit/media_kit_libs_android_video + flutter: assets: - assets/