mpv: screenshot, subtitle rendering

This commit is contained in:
Thibault Deckers 2023-07-16 00:46:21 +02:00
parent 25753e5274
commit 017264d2bc
9 changed files with 205 additions and 95 deletions

View file

@ -70,34 +70,37 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
} }
Future<void> _captureFrame(BuildContext context, AvesEntry entry, AvesVideoController controller) async { Future<void> _captureFrame(BuildContext context, AvesEntry entry, AvesVideoController controller) async {
final positionMillis = controller.currentPosition;
final bytes = await controller.captureFrame();
final destinationAlbum = androidFileUtils.avesVideoCapturesPath; final destinationAlbum = androidFileUtils.avesVideoCapturesPath;
if (!await checkStoragePermissionForAlbums(context, {destinationAlbum})) return; final positionMillis = controller.currentPosition;
final Map<String, dynamic> newFields = {};
if (!await checkFreeSpace(context, bytes.length, destinationAlbum)) return; final bytes = await controller.captureFrame();
if (bytes != null) {
if (!await checkStoragePermissionForAlbums(context, {destinationAlbum})) return;
final rotationDegrees = entry.rotationDegrees; if (!await checkFreeSpace(context, bytes.length, destinationAlbum)) return;
final dateTimeMillis = entry.catalogMetadata?.dateMillis;
final latLng = entry.latLng;
final exif = {
if (rotationDegrees != 0) 'rotationDegrees': rotationDegrees,
if (dateTimeMillis != null && dateTimeMillis != 0) 'dateTimeMillis': dateTimeMillis,
if (latLng != null) ...{
'latitude': latLng.latitude,
'longitude': latLng.longitude,
}
};
final newFields = await mediaEditService.captureFrame( final rotationDegrees = entry.rotationDegrees;
entry, final dateTimeMillis = entry.catalogMetadata?.dateMillis;
desiredName: '${entry.bestTitle}_${'$positionMillis'.padLeft(8, '0')}', final latLng = entry.latLng;
exif: exif, final exif = {
bytes: bytes, if (rotationDegrees != 0) 'rotationDegrees': rotationDegrees,
destinationAlbum: destinationAlbum, if (dateTimeMillis != null && dateTimeMillis != 0) 'dateTimeMillis': dateTimeMillis,
nameConflictStrategy: NameConflictStrategy.rename, if (latLng != null) ...{
); 'latitude': latLng.latitude,
'longitude': latLng.longitude,
}
};
newFields.addAll(await mediaEditService.captureFrame(
entry,
desiredName: '${entry.bestTitle}_${'$positionMillis'.padLeft(8, '0')}',
exif: exif,
bytes: bytes,
destinationAlbum: destinationAlbum,
nameConflictStrategy: NameConflictStrategy.rename,
));
}
final success = newFields.isNotEmpty; final success = newFields.isNotEmpty;
final l10n = context.l10n; final l10n = context.l10n;

View file

@ -134,9 +134,11 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
final videoController = context.read<VideoConductor>().getController(entry); final videoController = context.read<VideoConductor>().getController(entry);
if (videoController != null) { if (videoController != null) {
final bytes = await videoController.captureFrame(); final bytes = await videoController.captureFrame();
needOrientation = rotationDegrees != 0 || isFlipped; if (bytes != null) {
needCrop = true; needOrientation = rotationDegrees != 0 || isFlipped;
provider = MemoryImage(bytes); needCrop = true;
provider = MemoryImage(bytes);
}
} }
} else if (entry.canDecode) { } else if (entry.canDecode) {
if (entry.useTiles) { if (entry.useTiles) {

View file

@ -120,7 +120,7 @@ abstract class AvesVideoController {
List<MediaStreamSummary> get streams; List<MediaStreamSummary> get streams;
Future<Uint8List> captureFrame(); Future<Uint8List?> captureFrame();
Future<void> mute(bool muted); Future<void> mute(bool muted);

View file

@ -371,7 +371,7 @@ class IjkVideoController extends AvesVideoController {
Future<void> _applySpeed() => _instance.setSpeed(speed); Future<void> _applySpeed() => _instance.setSpeed(speed);
@override @override
Future<Uint8List> captureFrame() { Future<Uint8List?> captureFrame() {
if (!_instance.value.videoRenderStart) { if (!_instance.value.videoRenderStart) {
return Future.error('cannot capture frame when video is not rendered'); return Future.error('cannot capture frame when video is not rendered');
} }

View file

@ -25,7 +25,7 @@ class MpvVideoController extends AvesVideoController {
double get maxSpeed => 4; double get maxSpeed => 4;
@override @override
final ValueNotifier<bool> canCaptureFrameNotifier = ValueNotifier(false); final ValueNotifier<bool> canCaptureFrameNotifier = ValueNotifier(true);
@override @override
final ValueNotifier<bool> canMuteNotifier = ValueNotifier(true); final ValueNotifier<bool> canMuteNotifier = ValueNotifier(true);
@ -49,14 +49,13 @@ class MpvVideoController extends AvesVideoController {
_instance = Player( _instance = Player(
configuration: const PlayerConfiguration( configuration: const PlayerConfiguration(
libass: false,
logLevel: MPVLogLevel.warn, logLevel: MPVLogLevel.warn,
), ),
); );
_initController(); _initController();
_init(); _init();
// TODO TLAD listening
// canCaptureFrameNotifier.value = captureFrameEnabled && started;
_startListening(); _startListening();
} }
@ -185,7 +184,7 @@ class MpvVideoController extends AvesVideoController {
Stream<int> get positionStream => _instance.stream.position.map((pos) => pos.inMilliseconds); Stream<int> get positionStream => _instance.stream.position.map((pos) => pos.inMilliseconds);
@override @override
Stream<String?> get timedTextStream => _timedTextStreamController.stream; Stream<String?> get timedTextStream => _instance.stream.subtitle.map((v) => v.isEmpty ? null : v[0]);
@override @override
bool get isMuted => _instance.state.volume == 0; bool get isMuted => _instance.state.volume == 0;
@ -200,10 +199,7 @@ class MpvVideoController extends AvesVideoController {
set speed(double speed) => _instance.setRate(speed); set speed(double speed) => _instance.setRate(speed);
@override @override
Future<Uint8List> captureFrame() { Future<Uint8List?> captureFrame() => _instance.screenshot();
// TODO: implement captureFrame
throw UnimplementedError();
}
@override @override
Widget buildPlayerWidget(BuildContext context) { Widget buildPlayerWidget(BuildContext context) {
@ -214,6 +210,9 @@ class MpvVideoController extends AvesVideoController {
alignment: Alignment.center, alignment: Alignment.center,
controls: NoVideoControls, controls: NoVideoControls,
wakelock: false, wakelock: false,
subtitleViewConfiguration: const SubtitleViewConfiguration(
style: TextStyle(color: Colors.transparent),
),
); );
} }

View file

@ -1,6 +1,22 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
archive:
dependency: transitive
description:
name: archive
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
url: "https://pub.dev"
source: hosted
version: "3.3.7"
args:
dependency: transitive
description:
name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
version: "2.4.2"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -46,6 +62,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.1" version: "1.17.1"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
dbus:
dependency: transitive
description:
name: dbus
sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263"
url: "https://pub.dev"
source: hosted
version: "0.7.8"
equatable: equatable:
dependency: transitive dependency: transitive
description: description:
@ -96,6 +136,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
image:
dependency: transitive
description:
name: image
sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf
url: "https://pub.dev"
source: hosted
version: "4.0.17"
js: js:
dependency: transitive dependency: transitive
description: description:
@ -123,34 +171,38 @@ packages:
media_kit: media_kit:
dependency: "direct main" dependency: "direct main"
description: description:
name: media_kit path: media_kit
sha256: "8c7d9417bed724a3fcaadd91c722fea042737cafb153aa1f1e6461a0fee683a3" ref: main
url: "https://pub.dev" resolved-ref: "6d0f0401b8d87596a6167fd629912cff92003edc"
source: hosted url: "https://github.com/alexmercerind/media_kit"
source: git
version: "1.0.2" version: "1.0.2"
media_kit_libs_android_video: media_kit_libs_android_video:
dependency: "direct main" dependency: "direct main"
description: description:
name: media_kit_libs_android_video path: media_kit_libs_android_video
sha256: "228c3b182831e194bb178d4d22a1839af812c917cb76fe87d6fdc9ea4328dc81" ref: main
url: "https://pub.dev" resolved-ref: "6d0f0401b8d87596a6167fd629912cff92003edc"
source: hosted url: "https://github.com/alexmercerind/media_kit"
version: "1.1.1" source: git
version: "1.2.0"
media_kit_native_event_loop: media_kit_native_event_loop:
dependency: "direct main" dependency: "direct main"
description: description:
name: media_kit_native_event_loop path: media_kit_native_event_loop
sha256: "5351f0c28124b5358756515d8619abad182cdefe967468d7fb5b274737cc2f59" ref: main
url: "https://pub.dev" resolved-ref: "6d0f0401b8d87596a6167fd629912cff92003edc"
source: hosted url: "https://github.com/alexmercerind/media_kit"
source: git
version: "1.0.6" version: "1.0.6"
media_kit_video: media_kit_video:
dependency: "direct main" dependency: "direct main"
description: description:
name: media_kit_video path: media_kit_video
sha256: d31a0eab80cafadccdedb663d8a127750e38b8c75c1aa83d8943f8119b88cf99 ref: main
url: "https://pub.dev" resolved-ref: "6d0f0401b8d87596a6167fd629912cff92003edc"
source: hosted url: "https://github.com/alexmercerind/media_kit"
source: git
version: "1.0.2" version: "1.0.2"
meta: meta:
dependency: transitive dependency: transitive
@ -160,6 +212,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
package_info_plus:
dependency: transitive
description:
name: package_info_plus
sha256: ceb027f6bc6a60674a233b4a90a7658af1aebdea833da0b5b53c1e9821a78c7b
url: "https://pub.dev"
source: hosted
version: "4.0.2"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -168,6 +236,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.3" version: "1.8.3"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
url: "https://pub.dev"
source: hosted
version: "5.4.0"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -176,6 +252,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
url: "https://pub.dev"
source: hosted
version: "3.7.3"
safe_local_storage: safe_local_storage:
dependency: transitive dependency: transitive
description: description:
@ -309,54 +393,38 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.7" version: "2.0.7"
wakelock: wakelock_plus:
dependency: transitive dependency: transitive
description: description:
name: wakelock name: wakelock_plus
sha256: "769ecf42eb2d07128407b50cb93d7c10bd2ee48f0276ef0119db1d25cc2f87db" sha256: aac3f3258f01781ec9212df94eecef1eb9ba9350e106728def405baa096ba413
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.2" version: "1.1.1"
wakelock_macos: wakelock_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: wakelock_macos name: wakelock_plus_platform_interface
sha256: "047c6be2f88cb6b76d02553bca5a3a3b95323b15d30867eca53a19a0a319d4cd" sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.4.0" version: "1.1.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: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.4" version: "5.0.5"
xml:
dependency: transitive
description:
name: xml
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
url: "https://pub.dev"
source: hosted
version: "6.3.0"
sdks: sdks:
dart: ">=3.0.0 <4.0.0" dart: ">=3.0.0 <4.0.0"
flutter: ">=3.7.0" flutter: ">=3.3.0"

View file

@ -23,7 +23,7 @@ dependencies:
dev_dependencies: dev_dependencies:
flutter_lints: flutter_lints:
#dependency_overrides: dependency_overrides:
# media_kit: # media_kit:
# path: ../../../media_kit/media_kit # path: ../../../media_kit/media_kit
# media_kit_video: # media_kit_video:
@ -32,5 +32,26 @@ dev_dependencies:
# path: ../../../media_kit/media_kit_native_event_loop # path: ../../../media_kit/media_kit_native_event_loop
# media_kit_libs_android_video: # media_kit_libs_android_video:
# path: ../../../media_kit/media_kit_libs_android_video # path: ../../../media_kit/media_kit_libs_android_video
media_kit:
git:
url: https://github.com/alexmercerind/media_kit
ref: main
path: media_kit
media_kit_video:
git:
url: https://github.com/alexmercerind/media_kit
ref: main
path: media_kit_video
media_kit_native_event_loop:
git:
url: https://github.com/alexmercerind/media_kit
ref: main
path: media_kit_native_event_loop
media_kit_libs_android_video:
git:
url: https://github.com/alexmercerind/media_kit
ref: main
path: media_kit_libs_android_video
flutter: flutter:

View file

@ -1569,7 +1569,7 @@ packages:
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
win32: win32:
dependency: "direct overridden" dependency: transitive
description: description:
name: win32 name: win32
sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee

View file

@ -124,10 +124,7 @@ dev_dependencies:
shared_preferences_platform_interface: shared_preferences_platform_interface:
test: test:
dependency_overrides: #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: # media_kit:
# path: ../media_kit/media_kit # path: ../media_kit/media_kit
# media_kit_video: # media_kit_video:
@ -136,6 +133,26 @@ dependency_overrides:
# path: ../media_kit/media_kit_native_event_loop # path: ../media_kit/media_kit_native_event_loop
# media_kit_libs_android_video: # media_kit_libs_android_video:
# path: ../media_kit/media_kit_libs_android_video # path: ../media_kit/media_kit_libs_android_video
# media_kit:
# git:
# url: https://github.com/alexmercerind/media_kit
# ref: main
# path: media_kit
# media_kit_video:
# git:
# url: https://github.com/alexmercerind/media_kit
# ref: main
# path: media_kit_video
# media_kit_native_event_loop:
# git:
# url: https://github.com/alexmercerind/media_kit
# ref: main
# path: media_kit_native_event_loop
# media_kit_libs_android_video:
# git:
# url: https://github.com/alexmercerind/media_kit
# ref: main
# path: media_kit_libs_android_video
flutter: flutter:
assets: assets: