From 06f6adc69c7cd947fb21292a6bf9113b28ab7729 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 12 May 2023 08:32:56 +0600 Subject: [PATCH] refactor(windows,linux): replace audioplayers with media_kit --- lib/generated_plugin_registrant.dart | 2 - lib/main.dart | 5 + lib/services/audio_player.dart | 195 +++++++++--------- linux/flutter/generated_plugin_registrant.cc | 8 +- linux/flutter/generated_plugins.cmake | 3 +- macos/Flutter/GeneratedPluginRegistrant.swift | 2 - pubspec.lock | 104 +++++----- pubspec.yaml | 5 +- .../flutter/generated_plugin_registrant.cc | 6 +- windows/flutter/generated_plugins.cmake | 3 +- 10 files changed, 163 insertions(+), 170 deletions(-) diff --git a/lib/generated_plugin_registrant.dart b/lib/generated_plugin_registrant.dart index 938e48db..dc0cbb0b 100644 --- a/lib/generated_plugin_registrant.dart +++ b/lib/generated_plugin_registrant.dart @@ -8,7 +8,6 @@ import 'package:audio_service_web/audio_service_web.dart'; import 'package:audio_session/audio_session_web.dart'; -import 'package:audioplayers_web/audioplayers_web.dart'; import 'package:file_picker/_internal/file_picker_web.dart'; import 'package:shared_preferences_web/shared_preferences_web.dart'; import 'package:url_launcher_web/url_launcher_web.dart'; @@ -19,7 +18,6 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart'; void registerPlugins(Registrar registrar) { AudioServiceWeb.registerWith(registrar); AudioSessionWeb.registerWith(registrar); - AudioplayersPlugin.registerWith(registrar); FilePickerWeb.registerWith(registrar); SharedPreferencesPlugin.registerWith(registrar); UrlLauncherPlugin.registerWith(registrar); diff --git a/lib/main.dart b/lib/main.dart index 916b745a..f682cfba 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:media_kit/media_kit.dart'; import 'package:metadata_god/metadata_god.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -75,6 +76,10 @@ Future main(List rawArgs) async { FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + if (DesktopTools.platform.isWindows || DesktopTools.platform.isLinux) { + MediaKit.ensureInitialized(); + } + await DesktopTools.ensureInitialized( DesktopWindowOptions( hideTitleBar: true, diff --git a/lib/services/audio_player.dart b/lib/services/audio_player.dart index cc0a17ea..519764b4 100644 --- a/lib/services/audio_player.dart +++ b/lib/services/audio_player.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:audioplayers/audioplayers.dart' as ap; +import 'package:media_kit/media_kit.dart' as mk; import 'package:just_audio/just_audio.dart' as ja; import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; @@ -13,19 +13,6 @@ enum AudioPlaybackState { buffering, stopped; - static AudioPlaybackState fromApPlayerState(ap.PlayerState state) { - switch (state) { - case ap.PlayerState.playing: - return AudioPlaybackState.playing; - case ap.PlayerState.paused: - return AudioPlaybackState.paused; - case ap.PlayerState.stopped: - return AudioPlaybackState.stopped; - case ap.PlayerState.completed: - return AudioPlaybackState.completed; - } - } - static AudioPlaybackState fromJaPlayerState(ja.PlayerState state) { if (state.playing) { return AudioPlaybackState.playing; @@ -43,50 +30,24 @@ enum AudioPlaybackState { return AudioPlaybackState.buffering; } } - - // static PlayerState fromAapPlayerState(aap.PlayerState state) { - // switch (state) { - // case aap.PlayerState.play: - // return PlayerState.playing; - // case aap.PlayerState.pause: - // return PlayerState.paused; - // case aap.PlayerState.stop: - // return PlayerState.stopped; - // } - // } - - ap.PlayerState get asAudioPlayerPlayerState { - switch (this) { - case AudioPlaybackState.playing: - return ap.PlayerState.playing; - case AudioPlaybackState.paused: - return ap.PlayerState.paused; - case AudioPlaybackState.stopped: - return ap.PlayerState.stopped; - case AudioPlaybackState.completed: - return ap.PlayerState.completed; - case AudioPlaybackState.buffering: - return ap.PlayerState.paused; - } - } } class SpotubeAudioPlayer { - final ap.AudioPlayer? _audioPlayer; + final MkPlayerWithState? _mkPlayer; final ja.AudioPlayer? _justAudio; SpotubeAudioPlayer() - : _audioPlayer = apSupportedPlatform ? ap.AudioPlayer() : null, - _justAudio = !apSupportedPlatform ? ja.AudioPlayer() : null; + : _mkPlayer = mkSupportedPlatform ? MkPlayerWithState() : null, + _justAudio = !mkSupportedPlatform ? ja.AudioPlayer() : null; /// Whether the current platform supports the audioplayers plugin - static final bool apSupportedPlatform = + static final bool mkSupportedPlatform = DesktopTools.platform.isWindows || DesktopTools.platform.isLinux; // stream getters Stream get durationStream { - if (apSupportedPlatform) { - return _audioPlayer!.onDurationChanged.asBroadcastStream(); + if (mkSupportedPlatform) { + return _mkPlayer!.streams.duration.asBroadcastStream(); } else { return _justAudio!.durationStream .where((event) => event != null) @@ -96,25 +57,25 @@ class SpotubeAudioPlayer { } Stream get positionStream { - if (apSupportedPlatform) { - return _audioPlayer!.onPositionChanged.asBroadcastStream(); + if (mkSupportedPlatform) { + return _mkPlayer!.streams.position.asBroadcastStream(); } else { return _justAudio!.positionStream.asBroadcastStream(); } } Stream get bufferedPositionStream { - if (apSupportedPlatform) { + if (mkSupportedPlatform) { // audioplayers doesn't have the capability to get buffered position - return const Stream.empty().asBroadcastStream(); + return _mkPlayer!.streams.buffer.asBroadcastStream(); } else { return _justAudio!.bufferedPositionStream.asBroadcastStream(); } } Stream get completedStream { - if (apSupportedPlatform) { - return _audioPlayer!.onPlayerComplete.asBroadcastStream(); + if (mkSupportedPlatform) { + return _mkPlayer!.streams.completed.asBroadcastStream(); } else { return _justAudio!.playerStateStream .where( @@ -124,17 +85,15 @@ class SpotubeAudioPlayer { } Stream get playingStream { - if (apSupportedPlatform) { - return _audioPlayer!.onPlayerStateChanged.map((state) { - return state == ap.PlayerState.playing; - }).asBroadcastStream(); + if (mkSupportedPlatform) { + return _mkPlayer!.streams.playing.asBroadcastStream(); } else { - return _justAudio!.playingStream; + return _justAudio!.playingStream.asBroadcastStream(); } } Stream get bufferingStream { - if (apSupportedPlatform) { + if (mkSupportedPlatform) { return Stream.value(false).asBroadcastStream(); } else { return _justAudio!.playerStateStream @@ -148,10 +107,8 @@ class SpotubeAudioPlayer { } Stream get playerStateStream { - if (apSupportedPlatform) { - return _audioPlayer!.onPlayerStateChanged - .map((state) => AudioPlaybackState.fromApPlayerState(state)) - .asBroadcastStream(); + if (mkSupportedPlatform) { + return _mkPlayer!.playerStateStream.asBroadcastStream(); } else { return _justAudio!.playerStateStream .map(AudioPlaybackState.fromJaPlayerState) @@ -162,23 +119,23 @@ class SpotubeAudioPlayer { // regular info getter Future get duration async { - if (apSupportedPlatform) { - return await _audioPlayer!.getDuration(); + if (mkSupportedPlatform) { + return _mkPlayer!.state.duration; } else { return _justAudio!.duration; } } Future get position async { - if (apSupportedPlatform) { - return await _audioPlayer!.getCurrentPosition(); + if (mkSupportedPlatform) { + return _mkPlayer!.state.position; } else { return _justAudio!.position; } } Future get bufferedPosition async { - if (apSupportedPlatform) { + if (mkSupportedPlatform) { // audioplayers doesn't have the capability to get buffered position return null; } else { @@ -187,8 +144,8 @@ class SpotubeAudioPlayer { } bool get hasSource { - if (apSupportedPlatform) { - return _audioPlayer!.source != null; + if (mkSupportedPlatform) { + return _mkPlayer!.state.playlist.medias.isNotEmpty; } else { return _justAudio!.audioSource != null; } @@ -196,39 +153,39 @@ class SpotubeAudioPlayer { // states bool get isPlaying { - if (apSupportedPlatform) { - return _audioPlayer!.state == ap.PlayerState.playing; + if (mkSupportedPlatform) { + return _mkPlayer!.state.playing; } else { return _justAudio!.playing; } } bool get isPaused { - if (apSupportedPlatform) { - return _audioPlayer!.state == ap.PlayerState.paused; + if (mkSupportedPlatform) { + return !_mkPlayer!.state.playing; } else { return !isPlaying; } } bool get isStopped { - if (apSupportedPlatform) { - return _audioPlayer!.state == ap.PlayerState.stopped; + if (mkSupportedPlatform) { + return !hasSource; } else { return _justAudio!.processingState == ja.ProcessingState.idle; } } Future get isCompleted async { - if (apSupportedPlatform) { - return _audioPlayer!.state == ap.PlayerState.completed; + if (mkSupportedPlatform) { + return _mkPlayer!.state.completed; } else { return _justAudio!.processingState == ja.ProcessingState.completed; } } bool get isBuffering { - if (apSupportedPlatform) { + if (mkSupportedPlatform) { // audioplayers doesn't have the capability to get buffering state return false; } else { @@ -238,12 +195,8 @@ class SpotubeAudioPlayer { } Object _resolveUrlType(String url) { - if (apSupportedPlatform) { - if (url.startsWith("https")) { - return ap.UrlSource(url); - } else { - return ap.DeviceFileSource(url); - } + if (mkSupportedPlatform) { + return mk.Media(url); } else { if (url.startsWith("https")) { return ja.AudioSource.uri(Uri.parse(url)); @@ -254,19 +207,20 @@ class SpotubeAudioPlayer { } Future preload(String url) async { - final urlType = _resolveUrlType(url); - if (apSupportedPlatform && urlType is ap.Source) { - // audioplayers doesn't have the capability to preload - return; - } else { - return; - } + throw UnimplementedError(); + // final urlType = _resolveUrlType(url); + // if (mkSupportedPlatform && urlType is ap.Source) { + // // audioplayers doesn't have the capability to preload + // return; + // } else { + // return; + // } } Future play(String url) async { final urlType = _resolveUrlType(url); - if (apSupportedPlatform && urlType is ap.Source) { - await _audioPlayer?.play(urlType); + if (mkSupportedPlatform && urlType is mk.Media) { + await _mkPlayer?.open(urlType, play: true); } else { if (_justAudio?.audioSource is ja.ProgressiveAudioSource && (_justAudio?.audioSource as ja.ProgressiveAudioSource) @@ -286,37 +240,78 @@ class SpotubeAudioPlayer { } Future pause() async { - await _audioPlayer?.pause(); + await _mkPlayer?.pause(); await _justAudio?.pause(); } Future resume() async { - await _audioPlayer?.resume(); + await _mkPlayer?.play(); await _justAudio?.play(); } Future stop() async { - await _audioPlayer?.stop(); + await _mkPlayer?.pause(); await _justAudio?.stop(); } Future seek(Duration position) async { - await _audioPlayer?.seek(position); + await _mkPlayer?.seek(position); await _justAudio?.seek(position); } Future setVolume(double volume) async { - await _audioPlayer?.setVolume(volume); + await _mkPlayer?.setVolume(volume); await _justAudio?.setVolume(volume); } Future setSpeed(double speed) async { - await _audioPlayer?.setPlaybackRate(speed); + await _mkPlayer?.setRate(speed); await _justAudio?.setSpeed(speed); } Future dispose() async { - await _audioPlayer?.dispose(); + await _mkPlayer?.dispose(); await _justAudio?.dispose(); } } + +/// MediaKit [mk.Player] by default doesn't have a state stream. +class MkPlayerWithState extends mk.Player { + final StreamController _playerStateStream; + + late final List _subscriptions; + + MkPlayerWithState({super.configuration}) + : _playerStateStream = StreamController.broadcast() { + _subscriptions = [ + streams.buffering.listen((event) { + _playerStateStream.add(AudioPlaybackState.buffering); + }), + streams.playing.listen((playing) { + if (playing) { + _playerStateStream.add(AudioPlaybackState.playing); + } else { + _playerStateStream.add(AudioPlaybackState.paused); + } + }), + streams.completed.listen((event) { + _playerStateStream.add(AudioPlaybackState.completed); + }), + streams.playlist.listen((event) { + if (event.medias.isEmpty) { + _playerStateStream.add(AudioPlaybackState.stopped); + } + }), + ]; + } + + Stream get playerStateStream => _playerStateStream.stream; + + @override + FutureOr dispose({int code = 0}) { + for (var element in _subscriptions) { + element.cancel(); + } + return super.dispose(code: code); + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 8a77c9e5..eef22b2f 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,10 +6,10 @@ #include "generated_plugin_registrant.h" -#include #include #include #include +#include #include #include #include @@ -18,9 +18,6 @@ #include void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); - audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); g_autoptr(FlPluginRegistrar) catcher_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "CatcherPlugin"); catcher_plugin_register_with_registrar(catcher_registrar); @@ -30,6 +27,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) local_notifier_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "LocalNotifierPlugin"); local_notifier_plugin_register_with_registrar(local_notifier_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) screen_retriever_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index a7f4fb3f..1d7de67a 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,10 +3,10 @@ # list(APPEND FLUTTER_PLUGIN_LIST - audioplayers_linux catcher flutter_secure_storage_linux local_notifier + media_kit_libs_linux screen_retriever system_theme system_tray @@ -16,6 +16,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + media_kit_native_event_loop metadata_god ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 03d2712f..7210ed75 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,7 +7,6 @@ import Foundation import audio_service import audio_session -import audioplayers_darwin import catcher import device_info_plus import flutter_secure_storage_macos @@ -27,7 +26,6 @@ import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) - AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) CatcherPlugin.register(with: registry.registrar(forPlugin: "CatcherPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) diff --git a/pubspec.lock b/pubspec.lock index eefd5bd0..728bef1d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -137,62 +137,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.13" - audioplayers: - dependency: "direct main" - description: - name: audioplayers - sha256: "6063c05f987596ba7a3dad9bb9a5d8adfa5e7c07b9bae5301b27c11d0b3a239f" - url: "https://pub.dev" - source: hosted - version: "4.0.1" - audioplayers_android: - dependency: transitive - description: - name: audioplayers_android - sha256: fb6bca878ad175d8f6ddc0e0a2d4226d81fa7c10747c12db420e96c7a096b2cc - url: "https://pub.dev" - source: hosted - version: "3.0.1" - audioplayers_darwin: - dependency: transitive - description: - name: audioplayers_darwin - sha256: c4a56c49347b2e85ac4e1efea218948ca0fba87f04d2a3d3de07ce2410037038 - url: "https://pub.dev" - source: hosted - version: "4.0.1" - audioplayers_linux: - dependency: transitive - description: - name: audioplayers_linux - sha256: "897e24f190232a3fbb88134b062aa83a9240f55789b5e8d17c114283284ef56b" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - audioplayers_platform_interface: - dependency: transitive - description: - name: audioplayers_platform_interface - sha256: "3a90a46198d375fc7d47bc1d3070c8fd8863b6469b7d87ca80f953efb090f976" - url: "https://pub.dev" - source: hosted - version: "5.0.0" - audioplayers_web: - dependency: transitive - description: - name: audioplayers_web - sha256: "4f5dcbfec0bf98ea09e243d5f5b64ea43a4e6710a2f292724bed16cdba3c691e" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - audioplayers_windows: - dependency: transitive - description: - name: audioplayers_windows - sha256: "010f575653c01ccbe9756050b18df83d89426740e04b684f6438aa26c775a965" - url: "https://pub.dev" - source: hosted - version: "2.0.1" auto_size_text: dependency: "direct main" description: @@ -1086,6 +1030,38 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0" + media_kit: + dependency: "direct main" + description: + name: media_kit + sha256: d9a32b3f6eafdfbba6aff2e37045a3a80009b6dfbdeec638d51d85e8b254a6a2 + url: "https://pub.dev" + source: hosted + version: "0.0.7+1" + media_kit_libs_linux: + dependency: "direct main" + description: + name: media_kit_libs_linux + sha256: "21acc71cbae3518b3aeef9023a6a3a3decb579a40153764333814987ccd61040" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + media_kit_libs_windows_audio: + dependency: "direct main" + description: + name: media_kit_libs_windows_audio + sha256: f6ad5a1b910a6748a7350360756cd99a718fc9661a9583a7fd86a308d068dd81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + media_kit_native_event_loop: + dependency: "direct main" + description: + name: media_kit_native_event_loop + sha256: ed87140ad4b64156b2b470c8105f48d8cad7923c952ca05d23e02d28978d2cb3 + url: "https://pub.dev" + source: hosted + version: "1.0.3" meta: dependency: transitive description: @@ -1423,6 +1399,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" screen_retriever: dependency: transitive description: @@ -1765,6 +1749,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + 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: diff --git a/pubspec.yaml b/pubspec.yaml index cd6a016f..9e83d357 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,6 @@ dependencies: async: ^2.9.0 audio_service: ^0.18.9 audio_session: ^0.1.13 - audioplayers: ^4.0.1 auto_size_text: ^3.0.0 badges: ^2.0.3 buttons_tabbar: ^1.3.6 @@ -58,6 +57,10 @@ dependencies: json_serializable: ^6.6.0 just_audio: ^0.9.32 logger: ^1.1.0 + media_kit: ^0.0.7+1 + media_kit_libs_linux: ^1.0.2 + media_kit_libs_windows_audio: ^1.0.3 + media_kit_native_event_loop: ^1.0.3 metadata_god: ^0.4.1 mime: ^1.0.2 mpris_service: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index d1be5b60..b8983c9c 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,10 +6,10 @@ #include "generated_plugin_registrant.h" -#include #include #include #include +#include #include #include #include @@ -19,14 +19,14 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { - AudioplayersWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); CatcherPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("CatcherPlugin")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); LocalNotifierPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("LocalNotifierPlugin")); + MediaKitLibsWindowsAudioPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("MediaKitLibsWindowsAudioPluginCApi")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); ScreenRetrieverPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 127491eb..e066d223 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,10 +3,10 @@ # list(APPEND FLUTTER_PLUGIN_LIST - audioplayers_windows catcher flutter_secure_storage_windows local_notifier + media_kit_libs_windows_audio permission_handler_windows screen_retriever system_theme @@ -17,6 +17,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + media_kit_native_event_loop metadata_god smtc_windows )