Skip to content

Commit 01c4609

Browse files
committed
Apply PR jellyfin#5644: Fix audio and subtitle auto selection
1 parent 2965ca8 commit 01c4609

3 files changed

Lines changed: 118 additions & 19 deletions

File tree

app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ public void play(long position) {
426426

427427
protected void play(long position, @Nullable Integer forcedSubtitleIndex) {
428428
String forcedAudioLanguage = videoQueueManager.getValue().getLastPlayedAudioLanguageIsoCode();
429+
String forcedAudioCodec = videoQueueManager.getValue().getLastPlayedAudioCodec();
429430
Timber.i("Play called from state: %s with pos: %d, sub index: %d and forced audio: %s", mPlaybackState, position, forcedSubtitleIndex, forcedAudioLanguage);
430431

431432
if (mFragment == null) {
@@ -515,7 +516,7 @@ public void onClick(DialogInterface dialog, int which) {
515516
// undo setting mSeekPosition for liveTV
516517
if (isLiveTv) mSeekPosition = -1;
517518

518-
VideoOptions internalOptions = buildExoPlayerOptions(forcedSubtitleIndex, forcedAudioLanguage, item);
519+
VideoOptions internalOptions = buildExoPlayerOptions(forcedSubtitleIndex, forcedAudioLanguage, forcedAudioCodec, item);
519520

520521
playInternal(getCurrentlyPlayingItem(), position, internalOptions);
521522
mPlaybackState = PlaybackState.BUFFERING;
@@ -534,6 +535,7 @@ public void onClick(DialogInterface dialog, int which) {
534535
private VideoOptions buildExoPlayerOptions(
535536
@Nullable Integer forcedSubtitleIndex,
536537
@Nullable String forcedAudioLanguage,
538+
@Nullable String forcedAudioCodec,
537539
BaseItemDto item) {
538540
VideoOptions internalOptions = new VideoOptions();
539541
internalOptions.setItemId(item.getId());
@@ -557,14 +559,27 @@ private VideoOptions buildExoPlayerOptions(
557559
internalOptions.setSubtitleStreamIndex(forcedSubtitleIndex);
558560
}
559561
MediaSourceInfo currentMediaSource = getCurrentMediaSource();
560-
if (forcedAudioLanguage != null) {
561-
// find the first audio stream with the requested language
562-
for (MediaStream stream : currentMediaSource.getMediaStreams()) {
563-
if (stream.getType() == MediaStreamType.AUDIO && forcedAudioLanguage.equals(stream.getLanguage())) {
564-
internalOptions.setAudioStreamIndex(stream.getIndex());
565-
break;
562+
if (forcedAudioLanguage != null){
563+
Integer matchingIndex = null;
564+
if (forcedAudioCodec != null) {
565+
// find the first audio stream with the requested language and codec
566+
for (MediaStream stream : currentMediaSource.getMediaStreams()) {
567+
if (stream.getType() == MediaStreamType.AUDIO && forcedAudioLanguage.equals(stream.getLanguage()) && forcedAudioCodec.equals(stream.getCodec())) {
568+
matchingIndex = stream.getIndex();
569+
break;
570+
}
571+
}
572+
}
573+
if (matchingIndex == null) {
574+
// find the first audio stream with the requested language (fallback to language only)
575+
for (MediaStream stream : currentMediaSource.getMediaStreams()) {
576+
if (stream.getType() == MediaStreamType.AUDIO && forcedAudioLanguage.equals(stream.getLanguage())) {
577+
matchingIndex = stream.getIndex();
578+
break;
579+
}
566580
}
567581
}
582+
internalOptions.setAudioStreamIndex(matchingIndex);
568583
}
569584
if (storedAudioIndex != null) {
570585
internalOptions.setAudioStreamIndex(storedAudioIndex);
@@ -676,17 +691,41 @@ private void startItem(BaseItemDto item, long position, StreamInfo response) {
676691
if (!TrackSelectionManager.INSTANCE.hasSelectedSubtitleTrack(item.getId())) {
677692
// get subtitle info - prefer saved language preference over server default
678693
String lastSubtitleLanguage = videoQueueManager.getValue().getLastPlayedSubtitleLanguageIsoCode();
694+
Boolean lastSubtitleForcedState = videoQueueManager.getValue().getLastPlayedSubtitleForcedState();
695+
String lastSubtitleCodec = videoQueueManager.getValue().getLastPlayedSubtitleCodec();
696+
String lastSubtitleTitle = videoQueueManager.getValue().getLastPlayedSubtitleTitle();
679697
if (lastSubtitleLanguage != null) {
680698
if (lastSubtitleLanguage.isEmpty()) {
681699
// User explicitly disabled subtitles
682700
mCurrentOptions.setSubtitleStreamIndex(null);
683701
} else if (response.getMediaSource().getMediaStreams() != null) {
684702
// Find subtitle stream matching saved language
685703
Integer matchingIndex = null;
686-
for (MediaStream stream : response.getMediaSource().getMediaStreams()) {
687-
if (stream.getType() == MediaStreamType.SUBTITLE && lastSubtitleLanguage.equals(stream.getLanguage())) {
688-
matchingIndex = stream.getIndex();
689-
break;
704+
// Try with all previous subtitle info (title, codec, forced state, language)
705+
if (lastSubtitleCodec != null && lastSubtitleTitle != null) {
706+
for (MediaStream stream : response.getMediaSource().getMediaStreams()) {
707+
if (stream.getType() == MediaStreamType.SUBTITLE && lastSubtitleLanguage.equals(stream.getLanguage()) && lastSubtitleForcedState.equals(stream.isForced()) && lastSubtitleCodec.equals(stream.getCodec()) && lastSubtitleTitle.equals(stream.getTitle())) {
708+
matchingIndex = stream.getIndex();
709+
break;
710+
}
711+
}
712+
}
713+
// Fallback without title (codec, forced state, language)
714+
if (matchingIndex == null && lastSubtitleCodec != null) {
715+
for (MediaStream stream : response.getMediaSource().getMediaStreams()) {
716+
if (stream.getType() == MediaStreamType.SUBTITLE && lastSubtitleLanguage.equals(stream.getLanguage()) && lastSubtitleForcedState.equals(stream.isForced()) && lastSubtitleCodec.equals(stream.getCodec())) {
717+
matchingIndex = stream.getIndex();
718+
break;
719+
}
720+
}
721+
}
722+
// Fallback without codec (forced state, language)
723+
if (matchingIndex == null) {
724+
for (MediaStream stream : response.getMediaSource().getMediaStreams()) {
725+
if (stream.getType() == MediaStreamType.SUBTITLE && lastSubtitleLanguage.equals(stream.getLanguage()) && lastSubtitleForcedState.equals(stream.isForced())) {
726+
matchingIndex = stream.getIndex();
727+
break;
728+
}
690729
}
691730
}
692731
mCurrentOptions.setSubtitleStreamIndex(matchingIndex);
@@ -788,6 +827,22 @@ private Integer lastChosenLanguageAudioTrack(MediaSourceInfo info) {
788827
return null;
789828

790829
boolean videoFound = false;
830+
for (MediaStream track : info.getMediaStreams()) {
831+
if (track.getType() == MediaStreamType.VIDEO) {
832+
videoFound = true;
833+
} else {
834+
if (videoFound
835+
&& track.getType() == MediaStreamType.AUDIO
836+
&& (track.getLanguage() != null
837+
&& track.getLanguage().equals(videoQueueManager.getValue().getLastPlayedAudioLanguageIsoCode())
838+
&& (track.getCodec() != null
839+
&& track.getCodec().equals(videoQueueManager.getValue().getLastPlayedAudioCodec())))
840+
)
841+
return track.getIndex();
842+
}
843+
}
844+
845+
videoFound = false;
791846
for (MediaStream track : info.getMediaStreams()) {
792847
if (track.getType() == MediaStreamType.VIDEO) {
793848
videoFound = true;
@@ -830,14 +885,13 @@ public void switchAudioStream(int index) {
830885
return;
831886
}
832887

833-
String lastAudioIsoCode = videoQueueManager.getValue().getLastPlayedAudioLanguageIsoCode();
834888
String currentAudioIsoCode = currentMediaSource.getMediaStreams().get(index).getLanguage();
835-
836-
if (currentAudioIsoCode != null
837-
&& (lastAudioIsoCode == null || !lastAudioIsoCode.equals(currentAudioIsoCode))) {
838-
videoQueueManager.getValue().setLastPlayedAudioLanguageIsoCode(
839-
currentAudioIsoCode
840-
);
889+
if (currentAudioIsoCode != null) {
890+
videoQueueManager.getValue().setLastPlayedAudioLanguageIsoCode(currentAudioIsoCode);
891+
String currentAudioCodec = currentMediaSource.getMediaStreams().get(index).getCodec();
892+
if (currentAudioCodec != null) {
893+
videoQueueManager.getValue().setLastPlayedAudioCodec(currentAudioCodec);
894+
}
841895
}
842896

843897
int currAudioIndex = getAudioStreamIndex();

app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackControllerHelper.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ fun PlaybackController.disableDefaultSubtitles() {
5555
@JvmOverloads
5656
fun PlaybackController.setSubtitleIndex(index: Int, force: Boolean = false) {
5757
Timber.i("Switching subtitles from index ${mCurrentOptions.subtitleStreamIndex} to $index")
58-
5958
// Already using this subtitle index
6059
if (mCurrentOptions.subtitleStreamIndex == index && !force) return
6160

@@ -64,9 +63,15 @@ fun PlaybackController.setSubtitleIndex(index: Int, force: Boolean = false) {
6463
if (index == -1) {
6564
// Use empty string to indicate "subtitles explicitly disabled" vs null meaning "no preference"
6665
videoQueueManager.setLastPlayedSubtitleLanguageIsoCode("")
66+
videoQueueManager.setLastPlayedSubtitleForcedState(false)
67+
videoQueueManager.setLastPlayedSubtitleCodec(null)
68+
videoQueueManager.setLastPlayedSubtitleTitle(null)
6769
} else {
6870
val stream = currentMediaSource.mediaStreams?.firstOrNull { it.type == MediaStreamType.SUBTITLE && it.index == index }
6971
videoQueueManager.setLastPlayedSubtitleLanguageIsoCode(stream?.language)
72+
videoQueueManager.setLastPlayedSubtitleForcedState(stream?.isForced ?: false)
73+
videoQueueManager.setLastPlayedSubtitleCodec(stream?.codec)
74+
videoQueueManager.setLastPlayedSubtitleTitle(stream?.title)
7075
}
7176

7277
// Disable subtitles

app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoQueueManager.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ class VideoQueueManager {
66
private var _currentVideoQueue: List<BaseItemDto> = emptyList()
77
private var _currentMediaPosition = -1
88
private var _lastPlayedAudioLanguageIsoCode: String? = null
9+
private var _lastPlayedAudioCodec: String? = null
910
private var _lastPlayedSubtitleLanguageIsoCode: String? = null
11+
private var _lastPlayedSubtitleForcedState: Boolean = false
12+
private var _lastPlayedSubtitleCodec: String? = null
13+
private var _lastPlayedSubtitleTitle: String? = null
1014

1115
fun setCurrentVideoQueue(items: List<BaseItemDto>?) {
1216
if (items.isNullOrEmpty()) return clearVideoQueue()
@@ -33,6 +37,14 @@ class VideoQueueManager {
3337
_lastPlayedAudioLanguageIsoCode = isoCode
3438
}
3539

40+
fun getLastPlayedAudioCodec(): String? {
41+
return _lastPlayedAudioCodec
42+
}
43+
44+
fun setLastPlayedAudioCodec(codec: String) {
45+
_lastPlayedAudioCodec = codec
46+
}
47+
3648
fun getLastPlayedSubtitleLanguageIsoCode(): String? {
3749
return _lastPlayedSubtitleLanguageIsoCode
3850
}
@@ -41,10 +53,38 @@ class VideoQueueManager {
4153
_lastPlayedSubtitleLanguageIsoCode = isoCode
4254
}
4355

56+
fun getLastPlayedSubtitleForcedState(): Boolean {
57+
return _lastPlayedSubtitleForcedState
58+
}
59+
60+
fun setLastPlayedSubtitleForcedState(state: Boolean) {
61+
_lastPlayedSubtitleForcedState = state
62+
}
63+
64+
fun getLastPlayedSubtitleCodec(): String? {
65+
return _lastPlayedSubtitleCodec
66+
}
67+
68+
fun setLastPlayedSubtitleCodec(codecTag: String?) {
69+
_lastPlayedSubtitleCodec = codecTag
70+
}
71+
72+
fun getLastPlayedSubtitleTitle(): String? {
73+
return _lastPlayedSubtitleTitle
74+
}
75+
76+
fun setLastPlayedSubtitleTitle(title: String?) {
77+
_lastPlayedSubtitleTitle = title
78+
}
79+
4480
fun clearVideoQueue() {
4581
_currentVideoQueue = emptyList()
4682
_currentMediaPosition = -1
4783
_lastPlayedAudioLanguageIsoCode = null
84+
_lastPlayedAudioCodec = null
4885
_lastPlayedSubtitleLanguageIsoCode = null
86+
_lastPlayedSubtitleForcedState = false
87+
_lastPlayedSubtitleCodec = null
88+
_lastPlayedSubtitleTitle = null
4989
}
5090
}

0 commit comments

Comments
 (0)