From 37dde5cb387215f19524e46b6bfccf0e125366a4 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Thu, 8 Apr 2021 10:36:57 +0900 Subject: [PATCH] video: package study, HW acceleration macroblock fix for fijk --- android/app/build.gradle | 2 + .../video/{video.dart => controller.dart} | 6 +- lib/widgets/common/video/fijkplayer.dart | 43 +++++-- .../common/video/flutter_ijkplayer.dart | 6 +- .../common/video/flutter_vlc_player.dart | 119 ++++++++++++++++++ lib/widgets/common/video/video_player.dart | 95 ++++++++++++++ .../viewer/entry_horizontal_pager.dart | 2 +- lib/widgets/viewer/entry_vertical_pager.dart | 2 +- lib/widgets/viewer/entry_viewer_stack.dart | 5 +- lib/widgets/viewer/info/maps/leaflet_map.dart | 2 +- .../info/metadata/metadata_dir_tile.dart | 2 +- lib/widgets/viewer/overlay/video.dart | 21 ++-- .../viewer/visual/entry_page_view.dart | 2 +- lib/widgets/viewer/visual/video.dart | 9 +- pubspec.lock | 2 +- 15 files changed, 275 insertions(+), 43 deletions(-) rename lib/widgets/common/video/{video.dart => controller.dart} (81%) create mode 100644 lib/widgets/common/video/flutter_vlc_player.dart create mode 100644 lib/widgets/common/video/video_player.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 60ead7f84..de11a53bb 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -58,6 +58,7 @@ android { versionCode flutterVersionCode.toInteger() versionName flutterVersionName manifestPlaceholders = [googleApiKey: keystoreProperties['googleApiKey']] + multiDexEnabled true } signingConfigs { @@ -106,6 +107,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' implementation 'androidx.core:core-ktx:1.5.0-rc01' // v1.5.0-alpha02+ for ShortcutManagerCompat.setDynamicShortcuts implementation 'androidx.exifinterface:exifinterface:1.3.2' + implementation "androidx.multidex:multidex:2.0.1" implementation 'com.commonsware.cwac:document:0.4.1' implementation 'com.drewnoakes:metadata-extractor:2.15.0' implementation 'com.github.deckerst:Android-TiffBitmapFactory:876e53870a' // forked, built by JitPack diff --git a/lib/widgets/common/video/video.dart b/lib/widgets/common/video/controller.dart similarity index 81% rename from lib/widgets/common/video/video.dart rename to lib/widgets/common/video/controller.dart index cdabb17cf..518aa74a8 100644 --- a/lib/widgets/common/video/video.dart +++ b/lib/widgets/common/video/controller.dart @@ -1,13 +1,9 @@ import 'package:aves/model/entry.dart'; -import 'package:aves/widgets/common/video/fijkplayer.dart'; -// import 'package:aves/widgets/common/video/flutter_ijkplayer.dart'; import 'package:flutter/material.dart'; abstract class AvesVideoController { AvesVideoController(); - factory AvesVideoController.ijkPlayer() => IjkPlayerAvesVideoController(); - void dispose(); Future setDataSource(String uri); @@ -44,7 +40,7 @@ abstract class AvesVideoController { Stream get positionStream; - Widget buildPlayerWidget(AvesEntry entry); + Widget buildPlayerWidget(BuildContext context, AvesEntry entry); } class AvesVideoInfo { diff --git a/lib/widgets/common/video/fijkplayer.dart b/lib/widgets/common/video/fijkplayer.dart index ec722d7a0..b2eae99fe 100644 --- a/lib/widgets/common/video/fijkplayer.dart +++ b/lib/widgets/common/video/fijkplayer.dart @@ -1,9 +1,10 @@ import 'dart:async'; +import 'dart:ui'; import 'package:aves/model/entry.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/utils/change_notifier.dart'; -import 'package:aves/widgets/common/video/video.dart'; +import 'package:aves/widgets/common/video/controller.dart'; import 'package:fijkplayer/fijkplayer.dart'; import 'package:flutter/material.dart'; @@ -12,10 +13,11 @@ class IjkPlayerAvesVideoController extends AvesVideoController { final List _subscriptions = []; final StreamController _valueStreamController = StreamController.broadcast(); final AChangeNotifier _playFinishNotifier = AChangeNotifier(); + Offset _macroBlockCrop = Offset.zero; Stream get _valueStream => _valueStreamController.stream; - IjkPlayerAvesVideoController() { + IjkPlayerAvesVideoController(AvesEntry entry) { _instance = FijkPlayer(); // FFmpeg options @@ -30,15 +32,27 @@ class IjkPlayerAvesVideoController extends AvesVideoController { // `framedrop`: drop frames when cpu is too slow, default: 0, in [-1, 120] option.setPlayerOption('framedrop', 5); - final hwAccel = settings.isVideoHardwareAccelerationEnabled ? 1 : 0; + final _hwAccelerationEnabled = settings.isVideoHardwareAccelerationEnabled; + if (_hwAccelerationEnabled) { + // crop HW acceleration macroblock misalignment for videos with dimensions that do not fit 16x + final s = entry.displaySize % 16 * -1 % 16; + _macroBlockCrop = Offset(s.width, s.height); + } // `mediacodec-all-videos`: MediaCodec: enable all videos, default: 0, in [0, 1] // TODO TLAD enabling `mediacodec-all-videos` randomly fails to render some videos, e.g. MP2TS/h264(HDPR) - option.setPlayerOption('mediacodec-all-videos', hwAccel); + option.setPlayerOption('mediacodec-all-videos', _hwAccelerationEnabled ? 1 : 0); // option.setPlayerOption('analyzemaxduration', 200 * 1024); // option.setPlayerOption('analyzeduration', 200 * 1024); // option.setPlayerOption('probesize', 1024 * 1024); + // CJL options + // option.setPlayerOption('reconnect', 5); + // option.setPlayerOption('mediacodec', 1); + // option.setPlayerOption('packet-buffering', 1); + // option.setPlayerOption('soundtouch', 1); + // option.setPlayerOption('start-on-prepared', 1); + // TODO TLAD check looping // option.setPlayerOption('loop', 42); @@ -95,9 +109,6 @@ class IjkPlayerAvesVideoController extends AvesVideoController { @override Stream get isVideoReadyStream => _valueStream.map((value) => value.videoRenderStart); - // we check whether video info is ready instead of checking for `noDatasource` status, - // as the controller could also be uninitialized with the `pause` status - // (e.g. when switching between video entries without playing them the first time) @override bool get isPlayable => _instance.isPlayable(); @@ -111,11 +122,19 @@ class IjkPlayerAvesVideoController extends AvesVideoController { Stream get positionStream => _instance.onCurrentPosUpdate.map((pos) => pos.inMilliseconds); @override - Widget buildPlayerWidget(AvesEntry entry) => FijkView( - player: _instance, - panelBuilder: (player, data, context, viewSize, texturePos) => SizedBox(), - color: Colors.transparent, - ); + Widget buildPlayerWidget(BuildContext context, AvesEntry entry) { + return FijkView( + player: _instance, + fit: FijkFit( + sizeFactor: 1.0, + aspectRatio: -1, + alignment: Alignment.topLeft, + macroBlockCrop: _macroBlockCrop, + ), + panelBuilder: (player, data, context, viewSize, texturePos) => SizedBox(), + color: Colors.transparent, + ); + } } extension ExtraIjkStatus on FijkState { diff --git a/lib/widgets/common/video/flutter_ijkplayer.dart b/lib/widgets/common/video/flutter_ijkplayer.dart index 321f1d55b..c27852efc 100644 --- a/lib/widgets/common/video/flutter_ijkplayer.dart +++ b/lib/widgets/common/video/flutter_ijkplayer.dart @@ -2,7 +2,7 @@ // // import 'package:aves/model/entry.dart'; // import 'package:aves/utils/change_notifier.dart'; -// import 'package:aves/widgets/common/video/video.dart'; +// import 'package:aves/widgets/common/video/controller.dart'; // import 'package:flutter/material.dart'; // import 'package:flutter_ijkplayer/flutter_ijkplayer.dart'; // @@ -57,7 +57,7 @@ // // as the controller could also be uninitialized with the `pause` status // // (e.g. when switching between video entries without playing them the first time) // @override -// bool get isPlayable => _videoInfo.hasData; +// bool get isPlayable => _videoInfo.hasData && [VideoStatus.prepared, VideoStatus.playing, VideoStatus.paused, VideoStatus.completed].contains(status); // // @override // bool get isVideoReady => _instance.textureId != null; @@ -79,7 +79,7 @@ // Stream get positionStream => _instance.videoInfoStream.map((info) => info.currentPositionMillis); // // @override -// Widget buildPlayerWidget(AvesEntry entry) => IjkPlayer( +// Widget buildPlayerWidget(BuildContext context, AvesEntry entry) => IjkPlayer( // mediaController: _instance, // controllerWidgetBuilder: (controller) => SizedBox.shrink(), // statusWidgetBuilder: (context, controller, status) => SizedBox.shrink(), diff --git a/lib/widgets/common/video/flutter_vlc_player.dart b/lib/widgets/common/video/flutter_vlc_player.dart new file mode 100644 index 000000000..424f8a46f --- /dev/null +++ b/lib/widgets/common/video/flutter_vlc_player.dart @@ -0,0 +1,119 @@ +// import 'dart:async'; +// import 'dart:io'; +// +// import 'package:aves/model/entry.dart'; +// import 'package:aves/utils/change_notifier.dart'; +// import 'package:aves/widgets/common/video/controller.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) 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 refreshVideoInfo() => null; +// +// @override +// Future play() => _instance.play(); +// +// @override +// Future pause() => _instance?.pause(); +// +// @override +// Future seekTo(int targetMillis) => _instance.seekTo(Duration(milliseconds: targetMillis)); +// +// @override +// Future seekToProgress(double progress) => _instance.seekTo(Duration(milliseconds: (duration * progress).toInt())); +// +// @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 isVideoReady => _instance != null && _instance.value.isInitialized && !_instance.value.hasError; +// +// @override +// Stream get isVideoReadyStream => _valueStream.map((value) => value.isInitialized && !value.hasError); +// +// @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 = context.select((mq) => mq.size.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/common/video/video_player.dart b/lib/widgets/common/video/video_player.dart new file mode 100644 index 000000000..efd614d85 --- /dev/null +++ b/lib/widgets/common/video/video_player.dart @@ -0,0 +1,95 @@ +// import 'dart:async'; +// +// import 'package:aves/model/entry.dart'; +// import 'package:aves/utils/change_notifier.dart'; +// import 'package:aves/widgets/common/video/controller.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) 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 refreshVideoInfo() => null; +// +// @override +// Future play() => _instance.play(); +// +// @override +// Future pause() => _instance?.pause(); +// +// @override +// Future seekTo(int targetMillis) => _instance.seekTo(Duration(milliseconds: targetMillis)); +// +// @override +// Future seekToProgress(double progress) => _instance.seekTo(Duration(milliseconds: (duration * progress).toInt())); +// +// @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 isVideoReady => _instance != null && _instance.value.isInitialized && !_instance.value.hasError; +// +// @override +// Stream get isVideoReadyStream => _valueStream.map((value) => value.isInitialized && !value.hasError); +// +// @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/entry_horizontal_pager.dart b/lib/widgets/viewer/entry_horizontal_pager.dart index 7760eab22..9721d84be 100644 --- a/lib/widgets/viewer/entry_horizontal_pager.dart +++ b/lib/widgets/viewer/entry_horizontal_pager.dart @@ -3,7 +3,7 @@ import 'package:aves/model/multipage.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/widgets/common/magnifier/pan/gesture_detector_scope.dart'; import 'package:aves/widgets/common/magnifier/pan/scroll_physics.dart'; -import 'package:aves/widgets/common/video/video.dart'; +import 'package:aves/widgets/common/video/controller.dart'; import 'package:aves/widgets/viewer/multipage.dart'; import 'package:aves/widgets/viewer/visual/entry_page_view.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/viewer/entry_vertical_pager.dart b/lib/widgets/viewer/entry_vertical_pager.dart index db60c230a..e2ed06d2e 100644 --- a/lib/widgets/viewer/entry_vertical_pager.dart +++ b/lib/widgets/viewer/entry_vertical_pager.dart @@ -3,7 +3,7 @@ import 'dart:math'; import 'package:aves/model/entry.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/widgets/common/magnifier/pan/scroll_physics.dart'; -import 'package:aves/widgets/common/video/video.dart'; +import 'package:aves/widgets/common/video/controller.dart'; import 'package:aves/widgets/viewer/entry_horizontal_pager.dart'; import 'package:aves/widgets/viewer/info/info_page.dart'; import 'package:aves/widgets/viewer/info/notifications.dart'; diff --git a/lib/widgets/viewer/entry_viewer_stack.dart b/lib/widgets/viewer/entry_viewer_stack.dart index 57ac87c87..5b18d8251 100644 --- a/lib/widgets/viewer/entry_viewer_stack.dart +++ b/lib/widgets/viewer/entry_viewer_stack.dart @@ -11,7 +11,8 @@ import 'package:aves/theme/durations.dart'; import 'package:aves/utils/change_notifier.dart'; import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/common/basic/insets.dart'; -import 'package:aves/widgets/common/video/video.dart'; +import 'package:aves/widgets/common/video/controller.dart'; +import 'package:aves/widgets/common/video/fijkplayer.dart'; import 'package:aves/widgets/viewer/entry_action_delegate.dart'; import 'package:aves/widgets/viewer/entry_vertical_pager.dart'; import 'package:aves/widgets/viewer/hero.dart'; @@ -499,7 +500,7 @@ class _EntryViewerStackState extends State with SingleTickerPr _initViewSpecificController( uri, _videoControllers, - () => AvesVideoController.ijkPlayer(), + () => IjkPlayerAvesVideoController(entry), (_) => _.dispose(), ); } diff --git a/lib/widgets/viewer/info/maps/leaflet_map.dart b/lib/widgets/viewer/info/maps/leaflet_map.dart index 0ac426413..631678837 100644 --- a/lib/widgets/viewer/info/maps/leaflet_map.dart +++ b/lib/widgets/viewer/info/maps/leaflet_map.dart @@ -1,4 +1,3 @@ -import 'package:provider/provider.dart'; import 'package:aves/model/settings/enums.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; @@ -9,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:latlong/latlong.dart'; +import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; class EntryLeafletMap extends StatefulWidget { diff --git a/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart b/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart index ad1319723..ad0a7776e 100644 --- a/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart +++ b/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart @@ -6,8 +6,8 @@ import 'package:aves/services/svg_metadata_service.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/utils/color_utils.dart'; import 'package:aves/utils/constants.dart'; -import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/viewer/info/common.dart'; import 'package:aves/widgets/viewer/info/metadata/metadata_section.dart'; import 'package:aves/widgets/viewer/info/metadata/metadata_thumbnail.dart'; diff --git a/lib/widgets/viewer/overlay/video.dart b/lib/widgets/viewer/overlay/video.dart index fbbd43fb1..9ac0a908f 100644 --- a/lib/widgets/viewer/overlay/video.dart +++ b/lib/widgets/viewer/overlay/video.dart @@ -8,7 +8,7 @@ import 'package:aves/utils/time_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/fx/blurred.dart'; import 'package:aves/widgets/common/fx/borders.dart'; -import 'package:aves/widgets/common/video/video.dart'; +import 'package:aves/widgets/common/video/controller.dart'; import 'package:aves/widgets/viewer/overlay/common.dart'; import 'package:flutter/material.dart'; @@ -175,14 +175,17 @@ class _VideoControlOverlayState extends State with SingleTi Text(entry.durationText), ], ), - StreamBuilder( - stream: controller.positionStream, - builder: (context, snapshot) { - // do not use stream snapshot because it is obsolete when switching between videos - var progress = controller.progress; - if (!progress.isFinite) progress = 0.0; - return LinearProgressIndicator(value: progress); - }), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: StreamBuilder( + stream: controller.positionStream, + builder: (context, snapshot) { + // do not use stream snapshot because it is obsolete when switching between videos + var progress = controller.progress; + if (!progress.isFinite) progress = 0.0; + return LinearProgressIndicator(value: progress); + }), + ), ], ), ), diff --git a/lib/widgets/viewer/visual/entry_page_view.dart b/lib/widgets/viewer/visual/entry_page_view.dart index 0be786967..d439106c5 100644 --- a/lib/widgets/viewer/visual/entry_page_view.dart +++ b/lib/widgets/viewer/visual/entry_page_view.dart @@ -12,7 +12,7 @@ import 'package:aves/widgets/common/magnifier/magnifier.dart'; import 'package:aves/widgets/common/magnifier/scale/scale_boundaries.dart'; import 'package:aves/widgets/common/magnifier/scale/scale_level.dart'; import 'package:aves/widgets/common/magnifier/scale/state.dart'; -import 'package:aves/widgets/common/video/video.dart'; +import 'package:aves/widgets/common/video/controller.dart'; import 'package:aves/widgets/viewer/hero.dart'; import 'package:aves/widgets/viewer/visual/error.dart'; import 'package:aves/widgets/viewer/visual/raster.dart'; diff --git a/lib/widgets/viewer/visual/video.dart b/lib/widgets/viewer/visual/video.dart index fb8dec25c..730db595b 100644 --- a/lib/widgets/viewer/visual/video.dart +++ b/lib/widgets/viewer/visual/video.dart @@ -4,7 +4,7 @@ import 'package:aves/model/entry.dart'; import 'package:aves/model/entry_images.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/collection/collection_page.dart'; -import 'package:aves/widgets/common/video/video.dart'; +import 'package:aves/widgets/common/video/controller.dart'; import 'package:flutter/material.dart'; class VideoView extends StatefulWidget { @@ -53,17 +53,14 @@ class _VideoViewState extends State { widget.controller.playCompletedListenable.removeListener(_onPlayCompleted); } - bool isPlayable(VideoStatus status) => controller != null && [VideoStatus.prepared, VideoStatus.playing, VideoStatus.paused, VideoStatus.completed].contains(status); - @override Widget build(BuildContext context) { if (controller == null) return SizedBox(); return StreamBuilder( stream: widget.controller.statusStream, builder: (context, snapshot) { - final status = snapshot.data; - return isPlayable(status) - ? controller.buildPlayerWidget(entry) + return controller?.isPlayable == true + ? controller.buildPlayerWidget(context, entry) : Image( image: entry.getBestThumbnail(settings.getTileExtent(CollectionPage.routeName)), fit: BoxFit.contain, diff --git a/pubspec.lock b/pubspec.lock index b247f0820..c9333c8e3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -211,7 +211,7 @@ packages: description: path: "." ref: aves - resolved-ref: c48e515a98851e55c857308d5482cd7bf5c9faad + resolved-ref: "7171c3ede20f407b523c18692572cbcd12acc169" url: "git://github.com/deckerst/fijkplayer.git" source: git version: "0.8.7"