From 50aba059f07c16b8fda45efe6235a7286c8f29c7 Mon Sep 17 00:00:00 2001 From: Emily <36363495+em1lyy@users.noreply.github.com> Date: Sat, 27 Nov 2021 15:26:44 +0100 Subject: [PATCH 1/6] add a "Use Vorbis" switch AAC is currently hard-coded as the codec used for transcoding. In low-bandwidth environments, where transcoding is usually a must, AAC performs very badly though, especially the built-in FFmpeg AAC encoder. qaac and maybe FDK AAC are a bit better, but due to codec limitations, even those high-performance codecs are outperformed by Vorbis on low bitrates (also using those would require more patching in Jellyfin, and using non-free software, so that wouldn't really work). Vorbis is a free (as in freedom) audio format, and the libvorbis codec implementation is also free (also as in freedom). Jellyfin supports using it out-of-the-box. While opus would've been a better choice, I and some friends currently have some trouble getting it to work properly on the Jellyfin server side of things, so I figured many other users do as well, which is why Opus, even if it performs even better, hasn't been chosen. A switch has been added into the Transcoding Settings Menu that toggles between the widely-supported AAC and the higher-quality Vorbis. Settings are stored in the Hive Database (I do not have Dart and Flutter installed, so 1) this is untested and 2) the FinampModels.g.dart hasn't been re-generated yet). --- .../VorbisSwitch.dart | 34 +++++++++++++++++++ lib/models/FinampModels.dart | 6 ++++ lib/screens/TranscodingSettingsScreen.dart | 10 ++---- lib/services/MusicPlayerBackgroundTask.dart | 4 ++- 4 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 lib/components/TranscodingSettingsScreen/VorbisSwitch.dart diff --git a/lib/components/TranscodingSettingsScreen/VorbisSwitch.dart b/lib/components/TranscodingSettingsScreen/VorbisSwitch.dart new file mode 100644 index 000000000..6496936bb --- /dev/null +++ b/lib/components/TranscodingSettingsScreen/VorbisSwitch.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; + +import '../../services/FinampSettingsHelper.dart'; +import '../../models/FinampModels.dart'; + +class VorbisSwitch extends StatelessWidget { + const VorbisSwitch({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: FinampSettingsHelper.finampSettingsListener, + builder: (context, box, child) { + bool? useVorbis = box.get("FinampSettings")?.useVorbis; + + return SwitchListTile( + title: const Text("Use Vorbis Codec"), + subtitle: const Text( + "If enabled, music streams will be transcoded to Vorbis and not AAC by the server."), + value: useVorbis ?? false, + onChanged: useVorbis == null + ? null + : (value) { + FinampSettings finampSettingsTemp = + box.get("FinampSettings")!; + finampSettingsTemp.useVorbis = value; + box.put("FinampSettings", finampSettingsTemp); + }, + ); + }, + ); + } +} diff --git a/lib/models/FinampModels.dart b/lib/models/FinampModels.dart index 77dd0353c..6d2bbe309 100644 --- a/lib/models/FinampModels.dart +++ b/lib/models/FinampModels.dart @@ -68,6 +68,7 @@ class FinampSettings { _contentGridViewCrossAxisCountLandscape, this.showTextOnGridView = _showTextOnGridView, this.sleepTimerSeconds = _sleepTimerSeconds, + this.useVorbis = false, }); @HiveField(0) @@ -123,6 +124,11 @@ class FinampSettings { @HiveField(14, defaultValue: _sleepTimerSeconds) int sleepTimerSeconds; + /// Whether or not to request Vorbis as the transcoding codec. + /// If false, AAC will be used. + @HiveField(15) + bool useVorbis; + static Future create() async { Directory internalSongDir = await getInternalSongDir(); return FinampSettings( diff --git a/lib/screens/TranscodingSettingsScreen.dart b/lib/screens/TranscodingSettingsScreen.dart index e09ab379c..528ae087d 100644 --- a/lib/screens/TranscodingSettingsScreen.dart +++ b/lib/screens/TranscodingSettingsScreen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../components/TranscodingSettingsScreen/TranscodeSwitch.dart'; import '../components/TranscodingSettingsScreen/BitrateSelector.dart'; +import '../components/TranscodingSettingsScreen/VorbisSwitch.dart'; class TranscodingSettingsScreen extends StatelessWidget { const TranscodingSettingsScreen({Key? key}) : super(key: key); @@ -17,14 +18,7 @@ class TranscodingSettingsScreen extends StatelessWidget { children: [ const TranscodeSwitch(), const BitrateSelector(), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "Jellyfin uses AAC for transcoding.", - style: Theme.of(context).textTheme.caption, - textAlign: TextAlign.center, - ), - ), + const VorbisSwitch(), ], ), ), diff --git a/lib/services/MusicPlayerBackgroundTask.dart b/lib/services/MusicPlayerBackgroundTask.dart index 2ef0e3781..f577a4757 100644 --- a/lib/services/MusicPlayerBackgroundTask.dart +++ b/lib/services/MusicPlayerBackgroundTask.dart @@ -524,7 +524,9 @@ class MusicPlayerBackgroundTask extends BaseAudioHandler { "MaxStreamingBitrate": mediaItem.extras!["shouldTranscode"] ? FinampSettingsHelper.finampSettings.transcodeBitrate.toString() : "999999999", - "AudioCodec": "aac", + "AudioCodec": mediaItem.extras!["useVorbis"] + ? "vorbis" + : "aac", "TranscodingContainer": "ts", "TranscodingProtocol": mediaItem.extras!["shouldTranscode"] ? "hls" : "http", From e7b202fd328d5e38ac0c44e8833e5ed12d8234d5 Mon Sep 17 00:00:00 2001 From: UnicornsOnLSD <44349936+UnicornsOnLSD@users.noreply.github.com> Date: Sat, 27 Nov 2021 18:58:05 +0000 Subject: [PATCH 2/6] Set default useVorbis value in Hive, generate Hive files --- lib/models/FinampModels.dart | 5 +++-- lib/models/FinampModels.g.dart | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/models/FinampModels.dart b/lib/models/FinampModels.dart index 6d2bbe309..2412d3bcb 100644 --- a/lib/models/FinampModels.dart +++ b/lib/models/FinampModels.dart @@ -44,6 +44,7 @@ const _contentGridViewCrossAxisCountPortrait = 2; const _contentGridViewCrossAxisCountLandscape = 3; const _showTextOnGridView = true; const _sleepTimerSeconds = 1800; // 30 Minutes +const _useVorbis = false; @HiveType(typeId: 28) class FinampSettings { @@ -68,7 +69,7 @@ class FinampSettings { _contentGridViewCrossAxisCountLandscape, this.showTextOnGridView = _showTextOnGridView, this.sleepTimerSeconds = _sleepTimerSeconds, - this.useVorbis = false, + this.useVorbis = _useVorbis, }); @HiveField(0) @@ -126,7 +127,7 @@ class FinampSettings { /// Whether or not to request Vorbis as the transcoding codec. /// If false, AAC will be used. - @HiveField(15) + @HiveField(15, defaultValue: _useVorbis) bool useVorbis; static Future create() async { diff --git a/lib/models/FinampModels.g.dart b/lib/models/FinampModels.g.dart index fb823e7bb..823dd1e37 100644 --- a/lib/models/FinampModels.g.dart +++ b/lib/models/FinampModels.g.dart @@ -178,13 +178,14 @@ class FinampSettingsAdapter extends TypeAdapter { fields[12] == null ? 3 : fields[12] as int, showTextOnGridView: fields[13] == null ? true : fields[13] as bool, sleepTimerSeconds: fields[14] == null ? 1800 : fields[14] as int, + useVorbis: fields[15] == null ? false : fields[15] as bool, ); } @override void write(BinaryWriter writer, FinampSettings obj) { writer - ..writeByte(15) + ..writeByte(16) ..writeByte(0) ..write(obj.isOffline) ..writeByte(1) @@ -214,7 +215,9 @@ class FinampSettingsAdapter extends TypeAdapter { ..writeByte(13) ..write(obj.showTextOnGridView) ..writeByte(14) - ..write(obj.sleepTimerSeconds); + ..write(obj.sleepTimerSeconds) + ..writeByte(15) + ..write(obj.useVorbis); } @override From 543b2aca70ffcd80e842ffe9127ab7460a1f5dda Mon Sep 17 00:00:00 2001 From: UnicornsOnLSD <44349936+UnicornsOnLSD@users.noreply.github.com> Date: Sat, 27 Nov 2021 19:46:52 +0000 Subject: [PATCH 3/6] Set useVorbis and change codec option to libvorbis --- lib/services/AudioServiceHelper.dart | 1 + lib/services/MusicPlayerBackgroundTask.dart | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/services/AudioServiceHelper.dart b/lib/services/AudioServiceHelper.dart index 906115ab6..e1c94128f 100644 --- a/lib/services/AudioServiceHelper.dart +++ b/lib/services/AudioServiceHelper.dart @@ -234,6 +234,7 @@ class AudioServiceHelper { "shouldTranscode": FinampSettingsHelper.finampSettings.shouldTranscode, "downloadedSongJson": (await _getDownloadedSong(item.id))?.toJson(), "isOffline": FinampSettingsHelper.finampSettings.isOffline, + "useVorbis": FinampSettingsHelper.finampSettings.useVorbis, // TODO: Maybe add transcoding bitrate here? }, rating: Rating.newHeartRating(item.userData?.isFavorite ?? false), diff --git a/lib/services/MusicPlayerBackgroundTask.dart b/lib/services/MusicPlayerBackgroundTask.dart index f577a4757..968e8ea91 100644 --- a/lib/services/MusicPlayerBackgroundTask.dart +++ b/lib/services/MusicPlayerBackgroundTask.dart @@ -524,9 +524,7 @@ class MusicPlayerBackgroundTask extends BaseAudioHandler { "MaxStreamingBitrate": mediaItem.extras!["shouldTranscode"] ? FinampSettingsHelper.finampSettings.transcodeBitrate.toString() : "999999999", - "AudioCodec": mediaItem.extras!["useVorbis"] - ? "vorbis" - : "aac", + "AudioCodec": mediaItem.extras!["useVorbis"] ? "libvorbis" : "aac", "TranscodingContainer": "ts", "TranscodingProtocol": mediaItem.extras!["shouldTranscode"] ? "hls" : "http", From abc9299114bdb2c66309132374e524ec744a0c07 Mon Sep 17 00:00:00 2001 From: UnicornsOnLSD <44349936+UnicornsOnLSD@users.noreply.github.com> Date: Sat, 27 Nov 2021 19:53:27 +0000 Subject: [PATCH 4/6] Hide Vorbis option on iOS and macOS --- lib/screens/TranscodingSettingsScreen.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/screens/TranscodingSettingsScreen.dart b/lib/screens/TranscodingSettingsScreen.dart index 528ae087d..b42d31d99 100644 --- a/lib/screens/TranscodingSettingsScreen.dart +++ b/lib/screens/TranscodingSettingsScreen.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import '../components/TranscodingSettingsScreen/TranscodeSwitch.dart'; @@ -18,7 +20,7 @@ class TranscodingSettingsScreen extends StatelessWidget { children: [ const TranscodeSwitch(), const BitrateSelector(), - const VorbisSwitch(), + if (!(Platform.isIOS || Platform.isMacOS)) const VorbisSwitch(), ], ), ), From 1730222b4644d181dc090bb5e682e69489632307 Mon Sep 17 00:00:00 2001 From: Emily <36363495+em1lyy@users.noreply.github.com> Date: Sat, 27 Nov 2021 22:58:54 +0100 Subject: [PATCH 5/6] revert changing audioCodec option to libvorbis --- lib/services/MusicPlayerBackgroundTask.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/MusicPlayerBackgroundTask.dart b/lib/services/MusicPlayerBackgroundTask.dart index 968e8ea91..10e524fea 100644 --- a/lib/services/MusicPlayerBackgroundTask.dart +++ b/lib/services/MusicPlayerBackgroundTask.dart @@ -524,7 +524,7 @@ class MusicPlayerBackgroundTask extends BaseAudioHandler { "MaxStreamingBitrate": mediaItem.extras!["shouldTranscode"] ? FinampSettingsHelper.finampSettings.transcodeBitrate.toString() : "999999999", - "AudioCodec": mediaItem.extras!["useVorbis"] ? "libvorbis" : "aac", + "AudioCodec": mediaItem.extras!["useVorbis"] ? "vorbis" : "aac", "TranscodingContainer": "ts", "TranscodingProtocol": mediaItem.extras!["shouldTranscode"] ? "hls" : "http", From 7f3a5f22baeaf4327ade2bfce2b5f424be6e5694 Mon Sep 17 00:00:00 2001 From: Emily <36363495+em1lyy@users.noreply.github.com> Date: Mon, 29 Nov 2021 14:56:09 +0100 Subject: [PATCH 6/6] only use HLS if useVorbis is false --- lib/services/MusicPlayerBackgroundTask.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/services/MusicPlayerBackgroundTask.dart b/lib/services/MusicPlayerBackgroundTask.dart index 10e524fea..2700b6042 100644 --- a/lib/services/MusicPlayerBackgroundTask.dart +++ b/lib/services/MusicPlayerBackgroundTask.dart @@ -473,7 +473,8 @@ class MusicPlayerBackgroundTask extends BaseAudioHandler { return Future.error( "Offline mode enabled but downloaded song not found."); } else { - if (mediaItem.extras!["shouldTranscode"] == true) { + if (mediaItem.extras!["shouldTranscode"] == true && + mediaItem.extras!["useVorbis"] == false) { return HlsAudioSource(await _songUri(mediaItem)); } else { return AudioSource.uri(await _songUri(mediaItem)); @@ -527,7 +528,7 @@ class MusicPlayerBackgroundTask extends BaseAudioHandler { "AudioCodec": mediaItem.extras!["useVorbis"] ? "vorbis" : "aac", "TranscodingContainer": "ts", "TranscodingProtocol": - mediaItem.extras!["shouldTranscode"] ? "hls" : "http", + mediaItem.extras!["shouldTranscode"] ? (mediaItem.extras!["useVorbis"] ? "http" : "hls") : "http", "ApiKey": _jellyfinApiData.currentUser!.accessToken, }, );