-
Notifications
You must be signed in to change notification settings - Fork 11
Fix: 旧版 libmpv(armeabi-v7a Android TV)播放含独立音频流的视频时黑屏无声音 #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
03f7237
280bf83
7febc89
745f2a2
cbacef0
460561b
826f9cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -924,35 +924,47 @@ class PlPlayerController with BlockConfigMixin { | |
| if (onlyPlayAudio.value) { | ||
| video = audio; | ||
| } else { | ||
| extras['audio-files'] = | ||
| '"${Platform.isWindows ? audio.replaceAll(';', r'\;') : audio.replaceAll(':', r'\:')}"'; | ||
| final escapedAudio = Platform.isWindows | ||
| ? audio.replaceAll(';', r'\;') | ||
| : audio.replaceAll(':', r'\:'); | ||
| // Android 上旧版 libmpv(如部分 Android TV armeabi-v7a 设备)不支持 loadfile 的 options 参数, | ||
| // 传入 extras 会导致整个 loadfile 命令失败(视频和音频都无法加载)。 | ||
| // 因此 Android 上不使用 extras 传递 audio-files,改为通过 change-list 命令方式(pre-open + post-open 双重保障)。 | ||
| // 注意:change-list 对 audio-files 属性值的 URL 中的冒号同样需要转义(旧版 mpv 会将冒号解析为 key:value 分隔符) | ||
| if (!Platform.isAndroid) { | ||
| extras['audio-files'] = '"$escapedAudio"'; | ||
| } | ||
| await player.command(['change-list', 'audio-files', 'clr', '']); | ||
| await player.command(['change-list', 'audio-files', 'set', escapedAudio]); | ||
| } | ||
| if (kDebugMode || Platform.isAndroid) { | ||
| String audioNormalization = AudioNormalization.getParamFromConfig( | ||
| Pref.audioNormalization, | ||
| String audioNormalization = AudioNormalization.getParamFromConfig( | ||
| Pref.audioNormalization, | ||
| ); | ||
| if (volume != null && volume.isNotEmpty) { | ||
| audioNormalization = audioNormalization.replaceFirstMapped( | ||
| loudnormRegExp, | ||
| (i) => | ||
| 'loudnorm=${volume.format( | ||
| Map.fromEntries( | ||
| i.group(1)!.split(':').map((item) { | ||
| final parts = item.split('='); | ||
| return MapEntry(parts[0].toLowerCase(), num.parse(parts[1])); | ||
| }), | ||
| ), | ||
| )}', | ||
| ); | ||
| if (volume != null && volume.isNotEmpty) { | ||
| audioNormalization = audioNormalization.replaceFirstMapped( | ||
| loudnormRegExp, | ||
| (i) => | ||
| 'loudnorm=${volume.format( | ||
| Map.fromEntries( | ||
| i.group(1)!.split(':').map((item) { | ||
| final parts = item.split('='); | ||
| return MapEntry(parts[0].toLowerCase(), num.parse(parts[1])); | ||
| }), | ||
| ), | ||
| )}', | ||
| ); | ||
| } else { | ||
| audioNormalization = audioNormalization.replaceFirst( | ||
| loudnormRegExp, | ||
| AudioNormalization.getParamFromConfig(Pref.fallbackNormalization), | ||
| ); | ||
| } | ||
| if (audioNormalization.isNotEmpty) { | ||
| } else { | ||
| audioNormalization = audioNormalization.replaceFirst( | ||
| loudnormRegExp, | ||
| AudioNormalization.getParamFromConfig(Pref.fallbackNormalization), | ||
| ); | ||
| } | ||
| if (audioNormalization.isNotEmpty) { | ||
| // Android 上不使用 extras 传递 lavfi-complex,避免旧版 mpv 的 loadfile options 参数导致整体失败 | ||
| if (!Platform.isAndroid) { | ||
| extras['lavfi-complex'] = '"[aid1] $audioNormalization [ao]"'; | ||
| } | ||
| await player.command(['set', 'lavfi-complex', '[aid1] $audioNormalization [ao]']); | ||
| } | ||
|
Comment on lines
+962
to
968
|
||
| } | ||
|
|
||
|
|
@@ -964,17 +976,40 @@ class PlPlayerController with BlockConfigMixin { | |
| ), | ||
| play: false, | ||
| ); | ||
|
|
||
| // 旧版 libmpv(如部分 armeabi-v7a Android TV 设备)的 loadfile replace 命令会清除 audio-files 属性, | ||
| // 因此在 player.open() 之后再次通过 change-list 重新设置,确保旧版 mpv 也能正确加载独立音频流。 | ||
| // 注意:URL 中的冒号需要转义(旧版 mpv change-list 会将未转义的冒号解析为 key:value 分隔符) | ||
| if (Platform.isAndroid) { | ||
| if (dataSource.audioSource case final audio? when (audio.isNotEmpty && !onlyPlayAudio.value)) { | ||
| final ea = audio.replaceAll(':', r'\:'); | ||
| await player.command(['change-list', 'audio-files', 'clr', '']); | ||
| await player.command(['change-list', 'audio-files', 'set', ea]); | ||
| } | ||
|
Comment on lines
+983
to
+988
|
||
| } | ||
| } | ||
|
|
||
| Future<void>? refreshPlayer() { | ||
| if (dataSource is FileSource) { | ||
| return null; | ||
| } | ||
| if (_videoPlayerController?.current.isNotEmpty ?? false) { | ||
| return _videoPlayerController!.open( | ||
| final future = _videoPlayerController!.open( | ||
| _videoPlayerController!.current.last.copyWith(start: position), | ||
| play: true, | ||
| ); | ||
| // Android 上需要在 open() 后重新设置 audio-files(旧版 mpv loadfile replace 会清除该属性) | ||
| // URL 中的冒号需要转义(旧版 mpv change-list 会将未转义的冒号解析为 key:value 分隔符) | ||
| if (Platform.isAndroid) { | ||
| if (dataSource.audioSource case final audio? when (audio.isNotEmpty && !onlyPlayAudio.value)) { | ||
| final ea = audio.replaceAll(':', r'\:'); | ||
| return future.then((_) async { | ||
| await _videoPlayerController?.command(['change-list', 'audio-files', 'clr', '']); | ||
| await _videoPlayerController?.command(['change-list', 'audio-files', 'set', ea]); | ||
| }); | ||
| } | ||
| } | ||
| return future; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change-list audio-filesis executed beforeplayer.open()on all platforms (including when a previous media is still loaded/paused). This mutates the player's current state and can have side effects (e.g., applying external audio to the currently loaded item) while it’s redundant on non-Android whereextras['audio-files']is still used. Consider limiting the pre-openchange-listpath to Android only, and/or moving thechange-listcalls to post-open()where you already re-apply them for Android.