feat(android): loudness normalization for exoplayer via audiofx
close #1289
This commit is contained in:
@@ -12,6 +12,7 @@ class PlayerAndroid extends PlayerBase {
|
||||
int? _bufferSizeBytes;
|
||||
bool _tunnelingEnabled = true;
|
||||
String _dvConversionMode = 'auto';
|
||||
bool _audioNormalizationEnabled = false;
|
||||
|
||||
/// The native plugin switched from ExoPlayer to its mpv fallback for this
|
||||
/// session. Sticky for the instance lifetime, mirroring the native flag
|
||||
@@ -259,6 +260,24 @@ class PlayerAndroid extends PlayerBase {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setAudioNormalization(bool enabled) async {
|
||||
if (disposed) return;
|
||||
_audioNormalizationEnabled = enabled;
|
||||
final initFuture = _initFuture;
|
||||
if (initialized) {
|
||||
await invoke('setAudioNormalization', {'enabled': enabled});
|
||||
} else if (initFuture != null) {
|
||||
await initFuture;
|
||||
if (!disposed && initialized && _audioNormalizationEnabled == enabled) {
|
||||
await invoke('setAudioNormalization', {'enabled': enabled});
|
||||
}
|
||||
}
|
||||
// Keep the mpv af property flowing through setMpvProperty so the plugin's
|
||||
// pendingMpvProperties replay applies loudnorm if exo falls back to mpv.
|
||||
await super.setAudioNormalization(enabled);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> getProperty(String name) async {
|
||||
if (disposed) return null;
|
||||
|
||||
@@ -209,6 +209,14 @@ abstract class Player {
|
||||
/// passed through to the audio device without decoding.
|
||||
Future<void> setAudioPassthrough(bool enabled);
|
||||
|
||||
/// Enable or disable loudness normalization.
|
||||
///
|
||||
/// mpv backends insert/remove the `loudnorm` audio filter. Android
|
||||
/// ExoPlayer attaches platform audio effects (DynamicsProcessing on
|
||||
/// API 28+, LoudnessEnhancer otherwise) and forces decoded non-tunneled
|
||||
/// PCM output while enabled so the effects can process the stream.
|
||||
Future<void> setAudioNormalization(bool enabled);
|
||||
|
||||
/// Show or hide the video rendering layer.
|
||||
///
|
||||
/// On macOS, this controls the Metal layer visibility.
|
||||
|
||||
@@ -668,6 +668,15 @@ abstract class PlayerBase with PlayerStreamControllersMixin implements Player {
|
||||
// ignore: no-empty-block - base no-op, overridden by platform subclasses
|
||||
Future<void> setAudioPassthrough(bool enabled) async {}
|
||||
|
||||
/// mpv loudnorm targeting streaming-style loudness; mirrored by the
|
||||
/// Android ExoPlayer effect parameters in AudioNormalizationEffect.kt.
|
||||
static const _loudnormFilter = 'loudnorm=I=-14:TP=-3:LRA=4';
|
||||
|
||||
@override
|
||||
Future<void> setAudioNormalization(bool enabled) async {
|
||||
await setProperty('af', enabled ? _loudnormFilter : '');
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: no-empty-block - base no-op, overridden by platform subclasses
|
||||
Future<void> setLogLevel(String level) async {}
|
||||
|
||||
Reference in New Issue
Block a user