feat: add audio passthrough toggle for desktop
This commit is contained in:
@@ -806,7 +806,8 @@
|
||||
"subtitleSync": "Untertitel-Synchronisation",
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Audioausgabe",
|
||||
"performanceOverlay": "Leistungsanzeige"
|
||||
"performanceOverlay": "Leistungsanzeige",
|
||||
"audioPassthrough": "Audio-Durchleitung"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Externer Player",
|
||||
|
||||
@@ -806,7 +806,8 @@
|
||||
"subtitleSync": "Subtitle Sync",
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Audio Output",
|
||||
"performanceOverlay": "Performance Overlay"
|
||||
"performanceOverlay": "Performance Overlay",
|
||||
"audioPassthrough": "Audio Passthrough"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "External Player",
|
||||
|
||||
@@ -806,7 +806,8 @@
|
||||
"subtitleSync": "Sincronización de subtítulos",
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Salida de audio",
|
||||
"performanceOverlay": "Indicador de rendimiento"
|
||||
"performanceOverlay": "Indicador de rendimiento",
|
||||
"audioPassthrough": "Audio Passthrough"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Reproductor externo",
|
||||
|
||||
@@ -806,7 +806,8 @@
|
||||
"subtitleSync": "Synchronisation des sous-titres",
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Sortie audio",
|
||||
"performanceOverlay": "Superposition de performance"
|
||||
"performanceOverlay": "Superposition de performance",
|
||||
"audioPassthrough": "Audio Pass-Through"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Lecteur externe",
|
||||
|
||||
@@ -806,7 +806,8 @@
|
||||
"subtitleSync": "Sincronizzazione sottotitoli",
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Uscita audio",
|
||||
"performanceOverlay": "Overlay prestazioni"
|
||||
"performanceOverlay": "Overlay prestazioni",
|
||||
"audioPassthrough": "Audio Passthrough"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Lettore esterno",
|
||||
|
||||
@@ -806,7 +806,8 @@
|
||||
"subtitleSync": "자막 동기화",
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "오디오 출력",
|
||||
"performanceOverlay": "성능 오버레이"
|
||||
"performanceOverlay": "성능 오버레이",
|
||||
"audioPassthrough": "오디오 패스스루"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "외부 플레이어",
|
||||
|
||||
@@ -806,7 +806,8 @@
|
||||
"subtitleSync": "Ondertitel synchronisatie",
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Audio-uitvoer",
|
||||
"performanceOverlay": "Prestatie-overlay"
|
||||
"performanceOverlay": "Prestatie-overlay",
|
||||
"audioPassthrough": "Audio-doorvoer"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Externe speler",
|
||||
|
||||
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsDe implements TranslationsVideoSettingsEn {
|
||||
@override String get hdr => 'HDR';
|
||||
@override String get audioOutput => 'Audioausgabe';
|
||||
@override String get performanceOverlay => 'Leistungsanzeige';
|
||||
@override String get audioPassthrough => 'Audio-Durchleitung';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2019,6 +2020,7 @@ extension on TranslationsDe {
|
||||
'videoSettings.hdr' => 'HDR',
|
||||
'videoSettings.audioOutput' => 'Audioausgabe',
|
||||
'videoSettings.performanceOverlay' => 'Leistungsanzeige',
|
||||
'videoSettings.audioPassthrough' => 'Audio-Durchleitung',
|
||||
'externalPlayer.title' => 'Externer Player',
|
||||
'externalPlayer.useExternalPlayer' => 'Externen Player verwenden',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Videos in einer externen App statt im integrierten Player öffnen',
|
||||
|
||||
@@ -2249,6 +2249,9 @@ class TranslationsVideoSettingsEn {
|
||||
|
||||
/// en: 'Performance Overlay'
|
||||
String get performanceOverlay => 'Performance Overlay';
|
||||
|
||||
/// en: 'Audio Passthrough'
|
||||
String get audioPassthrough => 'Audio Passthrough';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -3620,6 +3623,7 @@ extension on Translations {
|
||||
'videoSettings.hdr' => 'HDR',
|
||||
'videoSettings.audioOutput' => 'Audio Output',
|
||||
'videoSettings.performanceOverlay' => 'Performance Overlay',
|
||||
'videoSettings.audioPassthrough' => 'Audio Passthrough',
|
||||
'externalPlayer.title' => 'External Player',
|
||||
'externalPlayer.useExternalPlayer' => 'Use External Player',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Open videos in an external app instead of the built-in player',
|
||||
|
||||
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsEs implements TranslationsVideoSettingsEn {
|
||||
@override String get hdr => 'HDR';
|
||||
@override String get audioOutput => 'Salida de audio';
|
||||
@override String get performanceOverlay => 'Indicador de rendimiento';
|
||||
@override String get audioPassthrough => 'Audio Passthrough';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2019,6 +2020,7 @@ extension on TranslationsEs {
|
||||
'videoSettings.hdr' => 'HDR',
|
||||
'videoSettings.audioOutput' => 'Salida de audio',
|
||||
'videoSettings.performanceOverlay' => 'Indicador de rendimiento',
|
||||
'videoSettings.audioPassthrough' => 'Audio Passthrough',
|
||||
'externalPlayer.title' => 'Reproductor externo',
|
||||
'externalPlayer.useExternalPlayer' => 'Usar reproductor externo',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Abrir vídeos en una app externa en lugar del reproductor integrado',
|
||||
|
||||
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsFr implements TranslationsVideoSettingsEn {
|
||||
@override String get hdr => 'HDR';
|
||||
@override String get audioOutput => 'Sortie audio';
|
||||
@override String get performanceOverlay => 'Superposition de performance';
|
||||
@override String get audioPassthrough => 'Audio Pass-Through';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2019,6 +2020,7 @@ extension on TranslationsFr {
|
||||
'videoSettings.hdr' => 'HDR',
|
||||
'videoSettings.audioOutput' => 'Sortie audio',
|
||||
'videoSettings.performanceOverlay' => 'Superposition de performance',
|
||||
'videoSettings.audioPassthrough' => 'Audio Pass-Through',
|
||||
'externalPlayer.title' => 'Lecteur externe',
|
||||
'externalPlayer.useExternalPlayer' => 'Utiliser un lecteur externe',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Ouvrir les vidéos dans une application externe au lieu du lecteur intégré',
|
||||
|
||||
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsIt implements TranslationsVideoSettingsEn {
|
||||
@override String get hdr => 'HDR';
|
||||
@override String get audioOutput => 'Uscita audio';
|
||||
@override String get performanceOverlay => 'Overlay prestazioni';
|
||||
@override String get audioPassthrough => 'Audio Passthrough';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2019,6 +2020,7 @@ extension on TranslationsIt {
|
||||
'videoSettings.hdr' => 'HDR',
|
||||
'videoSettings.audioOutput' => 'Uscita audio',
|
||||
'videoSettings.performanceOverlay' => 'Overlay prestazioni',
|
||||
'videoSettings.audioPassthrough' => 'Audio Passthrough',
|
||||
'externalPlayer.title' => 'Lettore esterno',
|
||||
'externalPlayer.useExternalPlayer' => 'Usa lettore esterno',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Apri i video in un\'app esterna invece del lettore integrato',
|
||||
|
||||
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsKo implements TranslationsVideoSettingsEn {
|
||||
@override String get hdr => 'HDR';
|
||||
@override String get audioOutput => '오디오 출력';
|
||||
@override String get performanceOverlay => '성능 오버레이';
|
||||
@override String get audioPassthrough => '오디오 패스스루';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2019,6 +2020,7 @@ extension on TranslationsKo {
|
||||
'videoSettings.hdr' => 'HDR',
|
||||
'videoSettings.audioOutput' => '오디오 출력',
|
||||
'videoSettings.performanceOverlay' => '성능 오버레이',
|
||||
'videoSettings.audioPassthrough' => '오디오 패스스루',
|
||||
'externalPlayer.title' => '외부 플레이어',
|
||||
'externalPlayer.useExternalPlayer' => '외부 플레이어 사용',
|
||||
'externalPlayer.useExternalPlayerDescription' => '내장 플레이어 대신 외부 앱에서 동영상 열기',
|
||||
|
||||
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsNl implements TranslationsVideoSettingsEn {
|
||||
@override String get hdr => 'HDR';
|
||||
@override String get audioOutput => 'Audio-uitvoer';
|
||||
@override String get performanceOverlay => 'Prestatie-overlay';
|
||||
@override String get audioPassthrough => 'Audio-doorvoer';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2019,6 +2020,7 @@ extension on TranslationsNl {
|
||||
'videoSettings.hdr' => 'HDR',
|
||||
'videoSettings.audioOutput' => 'Audio-uitvoer',
|
||||
'videoSettings.performanceOverlay' => 'Prestatie-overlay',
|
||||
'videoSettings.audioPassthrough' => 'Audio-doorvoer',
|
||||
'externalPlayer.title' => 'Externe speler',
|
||||
'externalPlayer.useExternalPlayer' => 'Externe speler gebruiken',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Open video\'s in een externe app in plaats van de ingebouwde speler',
|
||||
|
||||
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsSv implements TranslationsVideoSettingsEn {
|
||||
@override String get hdr => 'HDR';
|
||||
@override String get audioOutput => 'Ljudutgång';
|
||||
@override String get performanceOverlay => 'Prestandaöverlägg';
|
||||
@override String get audioPassthrough => 'Ljudgenomkoppling';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2019,6 +2020,7 @@ extension on TranslationsSv {
|
||||
'videoSettings.hdr' => 'HDR',
|
||||
'videoSettings.audioOutput' => 'Ljudutgång',
|
||||
'videoSettings.performanceOverlay' => 'Prestandaöverlägg',
|
||||
'videoSettings.audioPassthrough' => 'Ljudgenomkoppling',
|
||||
'externalPlayer.title' => 'Extern spelare',
|
||||
'externalPlayer.useExternalPlayer' => 'Använd extern spelare',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Öppna videor i en extern app istället för den inbyggda spelaren',
|
||||
|
||||
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsZh implements TranslationsVideoSettingsEn {
|
||||
@override String get hdr => 'HDR';
|
||||
@override String get audioOutput => '音频输出';
|
||||
@override String get performanceOverlay => '性能监控';
|
||||
@override String get audioPassthrough => '音频直通';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2019,6 +2020,7 @@ extension on TranslationsZh {
|
||||
'videoSettings.hdr' => 'HDR',
|
||||
'videoSettings.audioOutput' => '音频输出',
|
||||
'videoSettings.performanceOverlay' => '性能监控',
|
||||
'videoSettings.audioPassthrough' => '音频直通',
|
||||
'externalPlayer.title' => '外部播放器',
|
||||
'externalPlayer.useExternalPlayer' => '使用外部播放器',
|
||||
'externalPlayer.useExternalPlayerDescription' => '在外部应用中打开视频,而不是使用内置播放器',
|
||||
|
||||
@@ -806,7 +806,8 @@
|
||||
"subtitleSync": "Undertextsynkronisering",
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Ljudutgång",
|
||||
"performanceOverlay": "Prestandaöverlägg"
|
||||
"performanceOverlay": "Prestandaöverlägg",
|
||||
"audioPassthrough": "Ljudgenomkoppling"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Extern spelare",
|
||||
|
||||
@@ -806,7 +806,8 @@
|
||||
"subtitleSync": "字幕同步",
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "音频输出",
|
||||
"performanceOverlay": "性能监控"
|
||||
"performanceOverlay": "性能监控",
|
||||
"audioPassthrough": "音频直通"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "外部播放器",
|
||||
|
||||
@@ -433,6 +433,13 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
await player!.setProperty('audio-exclusive', 'yes');
|
||||
}
|
||||
|
||||
// Audio passthrough (desktop only - sends bitstream to receiver)
|
||||
if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
|
||||
if (settingsService.getAudioPassthrough()) {
|
||||
await player!.setAudioPassthrough(true);
|
||||
}
|
||||
}
|
||||
|
||||
// HDR is controlled via custom hdr-enabled property on iOS/macOS/Windows
|
||||
if (Platform.isIOS || Platform.isMacOS || Platform.isWindows) {
|
||||
final enableHDR = settingsService.getEnableHDR();
|
||||
|
||||
@@ -77,6 +77,7 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
static const String _keyCustomExternalPlayers = 'custom_external_players';
|
||||
static const String _keyConfirmExitOnBack = 'confirm_exit_on_back';
|
||||
static const String _keyAmbientLighting = 'ambient_lighting';
|
||||
static const String _keyAudioPassthrough = 'audio_passthrough';
|
||||
|
||||
SettingsService._();
|
||||
|
||||
@@ -1129,6 +1130,15 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
return prefs.getBool(_keyAmbientLighting) ?? false;
|
||||
}
|
||||
|
||||
// Audio Passthrough
|
||||
Future<void> setAudioPassthrough(bool enabled) async {
|
||||
await prefs.setBool(_keyAudioPassthrough, enabled);
|
||||
}
|
||||
|
||||
bool getAudioPassthrough() {
|
||||
return prefs.getBool(_keyAudioPassthrough) ?? false;
|
||||
}
|
||||
|
||||
// Reset all settings to defaults
|
||||
Future<void> resetAllSettings() async {
|
||||
await Future.wait([
|
||||
@@ -1184,6 +1194,7 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
prefs.remove(_keyCustomExternalPlayers),
|
||||
prefs.remove(_keyConfirmExitOnBack),
|
||||
prefs.remove(_keyAmbientLighting),
|
||||
prefs.remove(_keyAudioPassthrough),
|
||||
prefs.remove(_keyBufferSizeMigratedToAuto),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -115,6 +115,7 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
||||
bool _enableHDR = true;
|
||||
bool _showPerformanceOverlay = false;
|
||||
bool _autoPlayNextEpisode = true;
|
||||
bool _audioPassthrough = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -131,6 +132,7 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
||||
_enableHDR = settings.getEnableHDR();
|
||||
_showPerformanceOverlay = settings.getShowPerformanceOverlay();
|
||||
_autoPlayNextEpisode = settings.getAutoPlayNextEpisode();
|
||||
_audioPassthrough = settings.getAudioPassthrough();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -166,6 +168,17 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _toggleAudioPassthrough() async {
|
||||
final newValue = !_audioPassthrough;
|
||||
final settings = await SettingsService.getInstance();
|
||||
await settings.setAudioPassthrough(newValue);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_audioPassthrough = newValue;
|
||||
});
|
||||
await widget.player.setAudioPassthrough(newValue);
|
||||
}
|
||||
|
||||
void _navigateTo(_SettingsView view) {
|
||||
setState(() {
|
||||
_currentView = view;
|
||||
@@ -331,6 +344,23 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
||||
},
|
||||
),
|
||||
|
||||
// Audio Passthrough (Desktop only)
|
||||
if (isDesktop)
|
||||
ListTile(
|
||||
leading: AppIcon(
|
||||
Symbols.surround_sound_rounded,
|
||||
fill: 1,
|
||||
color: _audioPassthrough ? Colors.amber : tokens(context).textMuted,
|
||||
),
|
||||
title: Text(t.videoSettings.audioPassthrough),
|
||||
trailing: Switch(
|
||||
value: _audioPassthrough,
|
||||
onChanged: (_) => _toggleAudioPassthrough(),
|
||||
activeThumbColor: Colors.amber,
|
||||
),
|
||||
onTap: _toggleAudioPassthrough,
|
||||
),
|
||||
|
||||
// Shader Preset (MPV only)
|
||||
if (widget.shaderService != null && widget.shaderService!.isSupported)
|
||||
_SettingsMenuItem(
|
||||
|
||||
Reference in New Issue
Block a user