Initial implementation of MediaKit for desktop platforms

codemagic-setup
Hank Grabowski 2023-04-03 17:11:25 -04:00
rodzic 97d2b7ed7e
commit df77a23452
13 zmienionych plików z 326 dodań i 94 usunięć

Wyświetl plik

@ -0,0 +1,127 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:relatica/controls/audio_video/media_kit_av_control.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../globals.dart';
import '../../services/setting_service.dart';
import '../../utils/snackbar_builder.dart';
import 'video_player_lib_av_control.dart';
final _shownVideos = <String>{};
final _useVideoPlayer = kIsWeb || Platform.isAndroid || Platform.isIOS;
final _useMediaKit = Platform.isWindows || Platform.isMacOS || Platform.isLinux;
class AVControl extends StatefulWidget {
final String videoUrl;
final String description;
final double width;
final double height;
const AVControl({
super.key,
required this.videoUrl,
required this.width,
required this.height,
required this.description,
});
@override
State<AVControl> createState() => _AVControlState();
}
class _AVControlState extends State<AVControl> {
void showVideo() {
_shownVideos.add(widget.videoUrl);
setState(() {});
}
@override
Widget build(BuildContext context) {
final shown = !context.watch<SettingsService>().lowBandwidthMode ||
_shownVideos.contains(widget.videoUrl);
final placeHolderBox = SizedBox(
width: widget.width,
height: widget.height,
child: const Card(
color: Colors.black12,
shape: RoundedRectangleBorder(),
child: Icon(Icons.movie)),
);
if (!shown) {
return GestureDetector(
onTap: showVideo,
child: placeHolderBox,
);
}
if (_useVideoPlayer) {
return VideoPlayerLibAvControl(
videoUrl: widget.videoUrl,
width: widget.width,
height: widget.height,
);
}
if (_useMediaKit) {
return MediaKitAvControl(
videoUrl: widget.videoUrl,
width: widget.width,
height: widget.height,
);
}
return buildStandinWidget(context);
}
Widget buildStandinWidget(BuildContext context) {
return GestureDetector(
onTap: () async {
final confirm = await showYesNoDialog(
context, 'Open Link in external app? ${widget.videoUrl}');
if (confirm != true) {
return;
}
if (await canLaunchUrl(Uri.parse(widget.videoUrl))) {
if (context.mounted) {
buildSnackbar(
context,
'Attempting to launch video: ${widget.videoUrl}',
);
}
await launchUrl(Uri.parse(widget.videoUrl));
} else {
if (context.mounted) {
buildSnackbar(
context, 'Unable to launch video: ${widget.videoUrl}');
}
}
},
child: SizedBox(
height: widget.height,
width: widget.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
widget.description.isNotEmpty
? widget.description
: 'Video: ${widget.videoUrl}',
softWrap: true,
),
),
),
],
),
));
}
}

Wyświetl plik

@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
class MediaKitAvControl extends StatefulWidget {
final String videoUrl;
final double width;
final double height;
const MediaKitAvControl({
super.key,
required this.videoUrl,
required this.width,
required this.height,
});
@override
State<MediaKitAvControl> createState() => _MediaKitAvControlState();
}
class _MediaKitAvControlState extends State<MediaKitAvControl> {
static final _logger = Logger('$MediaKitAvControl');
final player = Player();
VideoController? controller;
@override
void initState() {
super.initState();
Future.microtask(() async {
_logger.info('initializing');
controller = await VideoController.create(player.handle);
if (!context.mounted) {
return;
}
setState(() {});
_logger.info('opening player');
await player.open(Media(widget.videoUrl), play: false);
if (!context.mounted) {
return;
}
setState(() {});
_logger.info('done');
});
}
@override
void dispose() {
Future.microtask(() async {
await controller?.dispose();
await player.dispose();
});
super.dispose();
}
@override
void deactivate() {
player.pause();
super.deactivate();
}
void toggleVideoPlay() async {
player.playOrPause();
setState(() {});
}
void resetPlay() async {
await player.pause();
await player.seek(Duration.zero);
await player.play();
setState(() {});
}
@override
Widget build(BuildContext context) {
print('Building MediaKit Control');
if (controller == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
return GestureDetector(
onTap: toggleVideoPlay,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Video(
controller: controller,
width: widget.width,
height: widget.height,
),
Row(
children: [
IconButton(
icon: player.state.playing
? const Icon(Icons.pause)
: const Icon(Icons.play_arrow),
onPressed: toggleVideoPlay,
),
IconButton(onPressed: resetPlay, icon: const Icon(Icons.replay)),
],
)
],
),
);
}
}

Wyświetl plik

@ -1,17 +1,14 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:video_player/video_player.dart';
import '../services/setting_service.dart';
final _shownVideos = <String>{};
class VideoControl extends StatefulWidget {
class VideoPlayerLibAvControl extends StatefulWidget {
final String videoUrl;
final double width;
final double height;
const VideoControl({
const VideoPlayerLibAvControl({
super.key,
required this.videoUrl,
required this.width,
@ -19,14 +16,16 @@ class VideoControl extends StatefulWidget {
});
@override
State<VideoControl> createState() => _VideoControlState();
State<VideoPlayerLibAvControl> createState() =>
_VideoPlayerLibAvControlState();
}
class _VideoControlState extends State<VideoControl> {
class _VideoPlayerLibAvControlState extends State<VideoPlayerLibAvControl> {
late final VideoPlayerController videoPlayerController;
@override
void initState() {
super.initState();
videoPlayerController = VideoPlayerController.network(widget.videoUrl)
..initialize().then((_) {
setState(() {});
@ -35,8 +34,8 @@ class _VideoControlState extends State<VideoControl> {
@override
void dispose() {
super.dispose();
videoPlayerController.dispose();
super.dispose();
}
@override
@ -45,11 +44,6 @@ class _VideoControlState extends State<VideoControl> {
super.deactivate();
}
void showVideo() {
_shownVideos.add(widget.videoUrl);
setState(() {});
}
void toggleVideoPlay() {
videoPlayerController.value.isPlaying
? videoPlayerController.pause()
@ -59,34 +53,25 @@ class _VideoControlState extends State<VideoControl> {
@override
Widget build(BuildContext context) {
final shown = !context.watch<SettingsService>().lowBandwidthMode ||
_shownVideos.contains(widget.videoUrl);
final size = videoPlayerController.value.size;
final horizontalScale = size.width / widget.width;
final verticalScale = size.height / widget.height;
final scaling = min(horizontalScale, verticalScale);
final videoWidth = scaling * size.width;
final videoHeight = scaling * size.height;
final placeHolderBox = SizedBox(
width: widget.width,
height: widget.height,
child: const Card(
if (!videoPlayerController.value.isInitialized) {
return SizedBox(
width: videoWidth,
height: videoHeight,
child: const Card(
color: Colors.black12,
shape: RoundedRectangleBorder(),
child: Icon(Icons.movie)),
);
if (!shown) {
return GestureDetector(
onTap: showVideo,
child: placeHolderBox,
child: CircularProgressIndicator(),
),
);
}
_shownVideos.add(widget.videoUrl);
if (!videoPlayerController.value.isInitialized) {
return placeHolderBox;
}
final size = videoPlayerController.value.size;
final videoWidth = size.width <= widget.width ? size.width : widget.width;
final videoHeight = size.width <= widget.width
? size.height
: size.height * videoWidth / size.width;
return GestureDetector(
onTap: toggleVideoPlay,
child: Column(

Wyświetl plik

@ -1,13 +1,10 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../globals.dart';
import '../models/attachment_media_type_enum.dart';
import '../models/media_attachment.dart';
import '../screens/image_viewer_screen.dart';
import '../utils/snackbar_builder.dart';
import 'audio_video/av_control.dart';
import 'image_control.dart';
import 'video_control.dart';
class MediaAttachmentViewerControl extends StatefulWidget {
final List<MediaAttachment> attachments;
@ -32,54 +29,12 @@ class _MediaAttachmentViewerControlState
const width = 250.0;
const height = 250.0;
if (item.explicitType == AttachmentMediaType.video) {
if (useVideoPlayer) {
return VideoControl(
videoUrl: item.uri.toString(),
width: width,
height: height,
);
}
return GestureDetector(
onTap: () async {
final confirm = await showYesNoDialog(
context, 'Open Video Link in external app? ${item.uri}');
if (confirm != true) {
return;
}
if (await canLaunchUrl(item.uri)) {
if (mounted) {
buildSnackbar(
context,
'Attempting to launch video: ${item.uri}',
);
}
await launchUrl(item.uri);
} else {
if (mounted) {
buildSnackbar(context, 'Unable to launch video: ${item.uri}');
}
}
},
child: SizedBox(
height: height,
width: width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
item.description.isNotEmpty
? item.description
: 'Video: ${item.uri}',
softWrap: true,
),
),
),
],
),
));
return AVControl(
videoUrl: item.uri.toString(),
width: width,
height: height,
description: item.description,
);
}
if (item.explicitType != AttachmentMediaType.image) {
return Text('${item.explicitType}: ${item.uri}');

Wyświetl plik

@ -155,7 +155,7 @@ class _StatusControlState extends State<FlattenedTreeEntryControl> {
return const SizedBox();
}
return SizedBox(
height: 250.0,
height: 300.0,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {

Wyświetl plik

@ -15,9 +15,7 @@ final platformHasCamera = Platform.isIOS || Platform.isAndroid;
final useImagePicker = kIsWeb || Platform.isAndroid || Platform.isIOS;
final useVideoPlayer = kIsWeb || Platform.isAndroid || Platform.isIOS;
final usePhpDebugging = true;
const usePhpDebugging = true;
Future<bool?> showConfirmDialog(BuildContext context, String caption) {
return showDialog<bool>(

Wyświetl plik

@ -8,6 +8,8 @@
#include <desktop_window/desktop_window_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h>
#include <objectbox_flutter_libs/objectbox_flutter_libs_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
@ -18,6 +20,12 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);
g_autoptr(FlPluginRegistrar) media_kit_video_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin");
media_kit_video_plugin_register_with_registrar(media_kit_video_registrar);
g_autoptr(FlPluginRegistrar) objectbox_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ObjectboxFlutterLibsPlugin");
objectbox_flutter_libs_plugin_register_with_registrar(objectbox_flutter_libs_registrar);

Wyświetl plik

@ -5,6 +5,8 @@
list(APPEND FLUTTER_PLUGIN_LIST
desktop_window
flutter_secure_storage_linux
media_kit_libs_linux
media_kit_video
objectbox_flutter_libs
url_launcher_linux
)

Wyświetl plik

@ -9,6 +9,7 @@ import desktop_window
import device_info_plus
import flutter_secure_storage_macos
import flutter_web_auth_2
import media_kit_video
import objectbox_flutter_libs
import path_provider_foundation
import shared_preferences_foundation
@ -20,6 +21,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
ObjectboxFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "ObjectboxFlutterLibsPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

Wyświetl plik

@ -665,6 +665,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.0"
media_kit:
dependency: "direct main"
description:
name: media_kit
sha256: baaaaa025bffba908e9b4a1c824181a8b3ce44bbf77c82be1728427dbaa848d4
url: "https://pub.dev"
source: hosted
version: "0.0.2"
media_kit_libs_linux:
dependency: "direct main"
description:
name: media_kit_libs_linux
sha256: "7310b17dd2abc2e7363f78a273086445c2216c7b6dfb60933ca3814031d03814"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
media_kit_video:
dependency: "direct main"
description:
name: media_kit_video
sha256: f9987d92182f42852fa77e5ca98d11a91482b9f45f7a559ca3453f258026041b
url: "https://pub.dev"
source: hosted
version: "0.0.2"
meta:
dependency: transitive
description:
@ -905,6 +929,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.27.7"
safe_local_storage:
dependency: transitive
description:
name: safe_local_storage
sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440
url: "https://pub.dev"
source: hosted
version: "1.0.2"
scrollable_positioned_list:
dependency: "direct main"
description:
@ -1118,6 +1150,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
uri_parser:
dependency: transitive
description:
name: uri_parser
sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
url_launcher:
dependency: "direct main"
description:
@ -1288,4 +1328,4 @@ packages:
version: "3.1.1"
sdks:
dart: ">=2.19.0 <3.0.0"
flutter: ">=3.7.0-0"
flutter: ">=3.7.0"

Wyświetl plik

@ -30,6 +30,9 @@ dependencies:
image_picker: ^0.8.6
logging: ^1.1.0
markdown: ^7.0.1
media_kit: ^0.0.2
media_kit_video: ^0.0.2
media_kit_libs_linux: ^1.0.1
metadata_fetch: ^0.4.1
multi_trigger_autocomplete: ^0.1.1
network_to_file_image: ^4.0.1

Wyświetl plik

@ -8,6 +8,7 @@
#include <desktop_window/desktop_window_plugin.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <objectbox_flutter_libs/objectbox_flutter_libs_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
@ -16,6 +17,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DesktopWindowPlugin"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
MediaKitVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi"));
ObjectboxFlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ObjectboxFlutterLibsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(

Wyświetl plik

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
desktop_window
flutter_secure_storage_windows
media_kit_video
objectbox_flutter_libs
url_launcher_windows
)