diff --git a/lib/screens/settings/playback_settings_screen.dart b/lib/screens/settings/playback_settings_screen.dart index d6778d8ef0..d96468faab 100644 --- a/lib/screens/settings/playback_settings_screen.dart +++ b/lib/screens/settings/playback_settings_screen.dart @@ -41,7 +41,10 @@ class PlaybackSettingsScreen extends StatelessWidget { builder: (context) { final svc = SettingsService.instance; final exoActive = Platform.isAndroid && svc.read(SettingsService.useExoPlayer); - final downmixOn = svc.read(SettingsService.audioDownmix); + // Tizen drives the native Capi backend, so every mpv-only tuning tile + // below points at a property that platform cannot accept. + final mpvTuning = PlatformDetector.supportsMpvTuning(); + final downmixOn = mpvTuning && svc.read(SettingsService.audioDownmix); final showDisplaySwitchDelay = PlatformDetector.isAppleTV() || (Platform.isWindows && @@ -57,8 +60,8 @@ class PlaybackSettingsScreen extends StatelessWidget { children: [ if (Platform.isAndroid) _playerBackendSelector(), if (PlatformDetector.supportsExternalPlayers()) _externalPlayerTile(), - if (!exoActive) _mpvConfigTile(), - _hardwareDecodingTile(), + if (!exoActive && mpvTuning) _mpvConfigTile(), + if (mpvTuning) _hardwareDecodingTile(), if (exoActive) _playbackBufferTile(), if (exoActive) _tunneledPlaybackTile(), if (PlatformDetector.supportsPictureInPicture()) _autoPipTile(), @@ -76,7 +79,7 @@ class PlaybackSettingsScreen extends StatelessWidget { if (Platform.isAndroid) _dvConversionModeTile(), // mpv-only (#2149): ExoPlayer has no filter chain, so the // tile disappears while the ExoPlayer backend is active. - if (!exoActive) _deinterlaceTile(), + if (!exoActive && mpvTuning) _deinterlaceTile(), // TODO: "Extend video into display cutout" toggle (#1769) // goes here, Android-only. ], @@ -86,10 +89,10 @@ class PlaybackSettingsScreen extends StatelessWidget { title: t.settings.audio, children: [ if (PlatformDetector.supportsAudioPassthrough()) _audioPassthroughTile(), - _audioDownmixTile(), + if (mpvTuning) _audioDownmixTile(), if (downmixOn) _downmixCenterBoostTile(), if (downmixOn) _downmixNormalizeTile(), - _maxVolumeTile(), + if (mpvTuning) _maxVolumeTile(), ], ), diff --git a/lib/utils/platform_detector.dart b/lib/utils/platform_detector.dart index 9d246979f0..55e7ea9c28 100644 --- a/lib/utils/platform_detector.dart +++ b/lib/utils/platform_detector.dart @@ -224,6 +224,17 @@ class PlatformDetector { static bool supportsGamepads() => !_tizen; static bool supportsSystemMediaControls() => !_tizen; + /// Whether the mpv-backed playback tuning settings mean anything here. + /// + /// Tizen plays through the native Capi backend, and `PlayerTizen.setProperty` + /// rejects every mpv option but `pause` and `sub-visibility`. The settings + /// that exist only to reach mpv — the `mpv.conf` editor, `hwdec`, the + /// deinterlace and downmix filters and `volume-max` — have nothing to act on: + /// `VideoPlayerScreen` already skips the whole property-writing block there, + /// so the screen settings are inert, and the in-player toggles fail their + /// write and snap back. + static bool supportsMpvTuning() => !_tizen; + static bool isAppleTV() { return TvDetectionService.isAppleTVSync(); } diff --git a/lib/widgets/video_controls/sheets/video_settings_sheet.dart b/lib/widgets/video_controls/sheets/video_settings_sheet.dart index d8c827b0ac..a79f4a2038 100644 --- a/lib/widgets/video_controls/sheets/video_settings_sheet.dart +++ b/lib/widgets/video_controls/sheets/video_settings_sheet.dart @@ -839,32 +839,37 @@ class _VideoSettingsSheetState extends State { // "not Dolby" when the system reports notApplicable. if (PlatformDetector.isAppleTV()) _AudioRenderingModeItem(player: widget.player), - _SettingsToggleItem( - pref: SettingsService.audioNormalization, - icon: Symbols.graphic_eq_rounded, - title: t.videoSettings.audioNormalization, - // Normalization wins over passthrough; say so where passthrough - // exists. Android also folds the track to stereo ahead of loudnorm - // (PlayerBase._loudnormFilter), which a surround owner must hear - // about before flipping it. - subtitle: PlatformDetector.supportsAudioPassthrough() - ? Platform.isAndroid - ? t.videoSettings.audioNormalizationStereoMix - : t.videoSettings.audioNormalizationDisablesPassthrough - : null, - onAfterWrite: widget.player.setAudioNormalization, - ), + // Both audio filters go through mpv's `af`/swresample options, which + // PlayerTizen rejects: the write throws, is logged, and the switch + // snaps back. Do not offer a control that cannot hold its value. + if (PlatformDetector.supportsMpvTuning()) ...[ + _SettingsToggleItem( + pref: SettingsService.audioNormalization, + icon: Symbols.graphic_eq_rounded, + title: t.videoSettings.audioNormalization, + // Normalization wins over passthrough; say so where passthrough + // exists. Android also folds the track to stereo ahead of loudnorm + // (PlayerBase._loudnormFilter), which a surround owner must hear + // about before flipping it. + subtitle: PlatformDetector.supportsAudioPassthrough() + ? Platform.isAndroid + ? t.videoSettings.audioNormalizationStereoMix + : t.videoSettings.audioNormalizationDisablesPassthrough + : null, + onAfterWrite: widget.player.setAudioNormalization, + ), - _SettingsToggleItem( - pref: SettingsService.audioDownmix, - icon: Symbols.headphones_rounded, - title: t.videoSettings.audioDownmix, - onAfterWrite: (enabled) => widget.player.setAudioDownmix( - enabled: enabled, - centerBoostDb: SettingsService.instance.read(SettingsService.downmixCenterBoost), - normalize: SettingsService.instance.read(SettingsService.audioDownmixNormalize), + _SettingsToggleItem( + pref: SettingsService.audioDownmix, + icon: Symbols.headphones_rounded, + title: t.videoSettings.audioDownmix, + onAfterWrite: (enabled) => widget.player.setAudioDownmix( + enabled: enabled, + centerBoostDb: SettingsService.instance.read(SettingsService.downmixCenterBoost), + normalize: SettingsService.instance.read(SettingsService.audioDownmixNormalize), + ), ), - ), + ], // Shader Preset (MPV only) if (_state.shaderService != null && _state.shaderService!.isSupported) diff --git a/test/tizen/platform_test.dart b/test/tizen/platform_test.dart index 9e3b49c2ff..971d2917b1 100644 --- a/test/tizen/platform_test.dart +++ b/test/tizen/platform_test.dart @@ -9,4 +9,17 @@ void main() { expect(PlatformDetector.supportsExternalPlayers(), isFalse); expect(PlatformDetector.supportsAudioPassthrough(), isFalse); }); + + test('Tizen offers no mpv tuning settings', () { + // PlayerTizen.setProperty rejects every mpv option, so the mpv.conf + // editor, hwdec, deinterlace, the downmix filters and volume-max all + // point at something that cannot accept them. + if (!const bool.fromEnvironment('TIZEN_BUILD')) return; + expect(PlatformDetector.supportsMpvTuning(), isFalse); + }); + + test('a non-Tizen build keeps its mpv tuning settings', () { + if (const bool.fromEnvironment('TIZEN_BUILD')) return; + expect(PlatformDetector.supportsMpvTuning(), isTrue); + }); }