6262import static org .schabi .newpipe .util .ListHelper .getPopupResolutionIndex ;
6363import static org .schabi .newpipe .util .ListHelper .getResolutionIndex ;
6464import static org .schabi .newpipe .util .Localization .assureCorrectAppLanguage ;
65- import static org .schabi .newpipe .util .Localization .containsCaseInsensitive ;
6665import static java .util .concurrent .TimeUnit .MILLISECONDS ;
6766
6867import android .animation .Animator ;
130129import com .google .android .exoplayer2 .C ;
131130import com .google .android .exoplayer2 .DefaultRenderersFactory ;
132131import com .google .android .exoplayer2 .ExoPlayer ;
132+ import com .google .android .exoplayer2 .Format ;
133133import com .google .android .exoplayer2 .PlaybackException ;
134134import com .google .android .exoplayer2 .PlaybackParameters ;
135135import com .google .android .exoplayer2 .Player .PositionInfo ;
136136import com .google .android .exoplayer2 .RenderersFactory ;
137137import com .google .android .exoplayer2 .Timeline ;
138138import com .google .android .exoplayer2 .TracksInfo ;
139139import com .google .android .exoplayer2 .source .MediaSource ;
140- import com .google .android .exoplayer2 .source .TrackGroup ;
141- import com .google .android .exoplayer2 .source .TrackGroupArray ;
142140import com .google .android .exoplayer2 .text .Cue ;
143141import com .google .android .exoplayer2 .trackselection .DefaultTrackSelector ;
144142import com .google .android .exoplayer2 .trackselection .MappingTrackSelector ;
212210import org .schabi .newpipe .views .ExpandableSurfaceView ;
213211import org .schabi .newpipe .views .player .PlayerFastSeekOverlay ;
214212
215- import java .util .ArrayList ;
216213import java .util .Collections ;
217214import java .util .List ;
218215import java .util .Objects ;
@@ -2530,7 +2527,7 @@ public void onTracksInfoChanged(@NonNull final TracksInfo tracksInfo) {
25302527 Log .d (TAG , "ExoPlayer - onTracksChanged(), "
25312528 + "track group size = " + tracksInfo .getTrackGroupInfos ().size ());
25322529 }
2533- onTextTracksChanged ();
2530+ onTextTracksChanged (tracksInfo );
25342531 }
25352532
25362533 @ Override
@@ -3516,17 +3513,7 @@ private void buildCaptionMenu(@NonNull final List<String> availableLanguages) {
35163513 return ;
35173514 }
35183515 captionPopupMenu .getMenu ().removeGroup (POPUP_MENU_ID_CAPTION );
3519-
3520- final String userPreferredLanguage =
3521- prefs .getString (context .getString (R .string .caption_user_set_key ), null );
3522- /*
3523- * only search for autogenerated cc as fallback
3524- * if "(auto-generated)" was not already selected
3525- * we are only looking for "(" instead of "(auto-generated)" to hopefully get all
3526- * internationalized variants such as "(automatisch-erzeugt)" and so on
3527- */
3528- boolean searchForAutogenerated = userPreferredLanguage != null
3529- && !userPreferredLanguage .contains ("(" );
3516+ captionPopupMenu .setOnDismissListener (this );
35303517
35313518 // Add option for turning off caption
35323519 final MenuItem captionOffItem = captionPopupMenu .getMenu ().add (POPUP_MENU_ID_CAPTION ,
@@ -3549,30 +3536,42 @@ private void buildCaptionMenu(@NonNull final List<String> availableLanguages) {
35493536 captionItem .setOnMenuItemClickListener (menuItem -> {
35503537 final int textRendererIndex = getCaptionRendererIndex ();
35513538 if (textRendererIndex != RENDERER_UNAVAILABLE ) {
3539+ // DefaultTrackSelector will select for text tracks in the following order.
3540+ // When multiple tracks share the same rank, a random track will be chosen.
3541+ // 1. ANY track exactly matching preferred language name
3542+ // 2. ANY track exactly matching preferred language stem
3543+ // 3. ROLE_FLAG_CAPTION track matching preferred language stem
3544+ // 4. ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND track matching preferred language stem
3545+ // This means if a caption track of preferred language is not available,
3546+ // then an auto-generated track of that language will be chosen automatically.
35523547 trackSelector .setParameters (trackSelector .buildUponParameters ()
3553- .setPreferredTextLanguage (captionLanguage )
3548+ .setPreferredTextLanguages (captionLanguage ,
3549+ PlayerHelper .captionLanguageStemOf (captionLanguage ))
3550+ .setPreferredTextRoleFlags (C .ROLE_FLAG_CAPTION )
35543551 .setRendererDisabled (textRendererIndex , false ));
35553552 prefs .edit ().putString (context .getString (R .string .caption_user_set_key ),
35563553 captionLanguage ).apply ();
35573554 }
35583555 return true ;
35593556 });
3560- // apply caption language from previous user preference
3561- if (userPreferredLanguage != null
3562- && (captionLanguage .equals (userPreferredLanguage )
3563- || (searchForAutogenerated && captionLanguage .startsWith (userPreferredLanguage ))
3564- || (userPreferredLanguage .contains ("(" ) && captionLanguage .startsWith (
3565- userPreferredLanguage .substring (0 , userPreferredLanguage .indexOf ('(' )))))) {
3566- final int textRendererIndex = getCaptionRendererIndex ();
3567- if (textRendererIndex != RENDERER_UNAVAILABLE ) {
3568- trackSelector .setParameters (trackSelector .buildUponParameters ()
3569- .setPreferredTextLanguage (captionLanguage )
3570- .setRendererDisabled (textRendererIndex , false ));
3571- }
3572- searchForAutogenerated = false ;
3573- }
35743557 }
3575- captionPopupMenu .setOnDismissListener (this );
3558+
3559+ // apply caption language from previous user preference
3560+ final List <String > selectedPreferredLanguages =
3561+ trackSelector .getParameters ().preferredTextLanguages ;
3562+ final String userPreferredLanguage =
3563+ prefs .getString (context .getString (R .string .caption_user_set_key ), null );
3564+ final int textRendererIndex = getCaptionRendererIndex ();
3565+
3566+ if (userPreferredLanguage != null
3567+ && !selectedPreferredLanguages .contains (userPreferredLanguage )
3568+ && textRendererIndex != RENDERER_UNAVAILABLE ) {
3569+ trackSelector .setParameters (trackSelector .buildUponParameters ()
3570+ .setPreferredTextLanguages (userPreferredLanguage ,
3571+ PlayerHelper .captionLanguageStemOf (userPreferredLanguage ))
3572+ .setPreferredTextRoleFlags (C .ROLE_FLAG_CAPTION )
3573+ .setRendererDisabled (textRendererIndex , false ));
3574+ }
35763575 }
35773576
35783577 /**
@@ -3668,41 +3667,43 @@ private void setupSubtitleView() {
36683667 binding .subtitleView .setStyle (captionStyle );
36693668 }
36703669
3671- private void onTextTracksChanged () {
3672- final int textRenderer = getCaptionRendererIndex ();
3673-
3670+ private void onTextTracksChanged (@ NonNull final TracksInfo currentTrackInfo ) {
36743671 if (binding == null ) {
36753672 return ;
36763673 }
3674+
36773675 if (trackSelector .getCurrentMappedTrackInfo () == null
3678- || textRenderer == RENDERER_UNAVAILABLE ) {
3676+ || ! currentTrackInfo . isTypeSupportedOrEmpty ( C . TRACK_TYPE_TEXT ) ) {
36793677 binding .captionTextView .setVisibility (View .GONE );
36803678 return ;
36813679 }
36823680
3683- final TrackGroupArray textTracks = trackSelector .getCurrentMappedTrackInfo ()
3684- .getTrackGroups (textRenderer );
3685-
36863681 // Extract all loaded languages
3687- final List <String > availableLanguages = new ArrayList <>(textTracks .length );
3688- for (int i = 0 ; i < textTracks .length ; i ++) {
3689- final TrackGroup textTrack = textTracks .get (i );
3690- if (textTrack .length > 0 ) {
3691- availableLanguages .add (textTrack .getFormat (0 ).language );
3692- }
3693- }
3682+ final List <TracksInfo .TrackGroupInfo > textTracks = currentTrackInfo
3683+ .getTrackGroupInfos ()
3684+ .stream ()
3685+ .filter (trackGroupInfo -> C .TRACK_TYPE_TEXT == trackGroupInfo .getTrackType ())
3686+ .collect (Collectors .toList ());
3687+ final List <String > availableLanguages = textTracks .stream ()
3688+ .map (TracksInfo .TrackGroupInfo ::getTrackGroup )
3689+ .filter (textTrack -> textTrack .length > 0 )
3690+ .map (textTrack -> textTrack .getFormat (0 ).language )
3691+ .collect (Collectors .toList ());
3692+
3693+ // Find selected text track
3694+ final Optional <Format > selectedTracks = textTracks .stream ()
3695+ .filter (TracksInfo .TrackGroupInfo ::isSelected )
3696+ .filter (info -> info .getTrackGroup ().length >= 1 )
3697+ .map (info -> info .getTrackGroup ().getFormat (0 ))
3698+ .findFirst ();
36943699
3695- // Normalize mismatching language strings
3696- final String preferredLanguage = trackSelector .getParameters ()
3697- .preferredTextLanguages .stream ().findFirst ().orElse (null );
36983700 // Build UI
36993701 buildCaptionMenu (availableLanguages );
3700- if (trackSelector .getParameters ().getRendererDisabled (textRenderer )
3701- || preferredLanguage == null || (!availableLanguages .contains (preferredLanguage )
3702- && !containsCaseInsensitive (availableLanguages , preferredLanguage ))) {
3702+ if (trackSelector .getParameters ().getRendererDisabled (getCaptionRendererIndex ())
3703+ || !selectedTracks .isPresent ()) {
37033704 binding .captionTextView .setText (R .string .caption_none );
37043705 } else {
3705- binding .captionTextView .setText (preferredLanguage );
3706+ binding .captionTextView .setText (selectedTracks . get (). language );
37063707 }
37073708 binding .captionTextView .setVisibility (
37083709 availableLanguages .isEmpty () ? View .GONE : View .VISIBLE );
0 commit comments