diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/SubtitleDecoderFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/SubtitleDecoderFactory.java index 9c93a01f3b..1f78e39390 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/SubtitleDecoderFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/SubtitleDecoderFactory.java @@ -74,6 +74,11 @@ public boolean supportsFormat(Format format) { @Override public SubtitleDecoder createDecoder(Format format) { + if (delegate.supportsFormat(format)) { + SubtitleParser subtitleParser = delegate.create(format); + return new DelegatingSubtitleDecoder( + subtitleParser.getClass().getSimpleName() + "Decoder", subtitleParser); + } @Nullable String mimeType = format.sampleMimeType; if (mimeType != null) { switch (mimeType) { @@ -89,11 +94,6 @@ public SubtitleDecoder createDecoder(Format format) { break; } } - if (delegate.supportsFormat(format)) { - SubtitleParser subtitleParser = delegate.create(format); - return new DelegatingSubtitleDecoder( - subtitleParser.getClass().getSimpleName() + "Decoder", subtitleParser); - } throw new IllegalArgumentException( "Attempted to create decoder for unsupported MIME type: " + mimeType); } diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java index 5cf8455122..fa18137609 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java @@ -358,9 +358,11 @@ public interface OnFullScreenModeChangedListener { // LINT.IfChange(playback_speeds) private static final float[] PLAYBACK_SPEEDS = new float[] {0.25f, 0.5f, 0.75f, 1f, 1.25f, 1.5f, 2f}; + // LINT.ThenChange("../../../../res/values/strings.xml:playback_speeds") private static final int SETTINGS_PLAYBACK_SPEED_POSITION = 0; private static final int SETTINGS_AUDIO_TRACK_SELECTION_POSITION = 1; + private static final int SETTINGS_VIDEO_TRACK_SELECTION_POSITION = 2; private final PlayerControlViewLayoutManager controlViewLayoutManager; private final Resources resources; @@ -373,6 +375,7 @@ public interface OnFullScreenModeChangedListener { private final SettingsAdapter settingsAdapter; private final PlaybackSpeedAdapter playbackSpeedAdapter; private final TextTrackSelectionAdapter textTrackSelectionAdapter; + private final VideoTrackSelectionAdapter videoTrackSelectionAdapter; private final AudioTrackSelectionAdapter audioTrackSelectionAdapter; // TODO(insun): Add setTrackNameProvider to use customized track name provider. private final TrackNameProvider trackNameProvider; @@ -395,6 +398,7 @@ public interface OnFullScreenModeChangedListener { @Nullable private final View settingsButton; @Nullable private final View playbackSpeedButton; @Nullable private final View audioTrackButton; + @Nullable private final View videoTrackButton; @Nullable private final TextView durationView; @Nullable private final TextView positionView; @Nullable private final TimeBar timeBar; @@ -625,6 +629,11 @@ public PlayerControlView( audioTrackButton.setOnClickListener(componentListener); } + videoTrackButton = findViewById(R.id.exo_video_track); + if (videoTrackButton != null) { + videoTrackButton.setOnClickListener(componentListener); + } + TimeBar customTimeBar = findViewById(R.id.exo_progress); View timeBarPlaceholder = findViewById(R.id.exo_progress_placeholder); if (customTimeBar != null) { @@ -725,16 +734,23 @@ public PlayerControlView( controlViewLayoutManager = new PlayerControlViewLayoutManager(this); controlViewLayoutManager.setAnimationEnabled(animationEnabled); - String[] settingTexts = new String[2]; - Drawable[] settingIcons = new Drawable[2]; + String[] settingTexts = new String[3]; + Drawable[] settingIcons = new Drawable[3]; settingTexts[SETTINGS_PLAYBACK_SPEED_POSITION] = resources.getString(R.string.exo_controls_playback_speed); settingIcons[SETTINGS_PLAYBACK_SPEED_POSITION] = getDrawable(context, resources, R.drawable.exo_styled_controls_speed); + settingTexts[SETTINGS_AUDIO_TRACK_SELECTION_POSITION] = resources.getString(R.string.exo_track_selection_title_audio); settingIcons[SETTINGS_AUDIO_TRACK_SELECTION_POSITION] = getDrawable(context, resources, R.drawable.exo_styled_controls_audiotrack); + + settingTexts[SETTINGS_VIDEO_TRACK_SELECTION_POSITION] = + resources.getString(R.string.exo_track_selection_title_video); + settingIcons[SETTINGS_VIDEO_TRACK_SELECTION_POSITION] = + getDrawable(context, resources, R.drawable.exo_styled_controls_videotrack); + settingsAdapter = new SettingsAdapter(settingTexts, settingIcons); settingsWindowMargin = resources.getDimensionPixelSize(R.dimen.exo_settings_offset); settingsView = @@ -762,6 +778,7 @@ public PlayerControlView( resources.getString(R.string.exo_controls_cc_disabled_description); textTrackSelectionAdapter = new TextTrackSelectionAdapter(); audioTrackSelectionAdapter = new AudioTrackSelectionAdapter(); + videoTrackSelectionAdapter = new VideoTrackSelectionAdapter(); playbackSpeedAdapter = new PlaybackSpeedAdapter( resources.getStringArray(R.array.exo_controls_playback_speeds), PLAYBACK_SPEEDS); @@ -1320,13 +1337,18 @@ private void updateTrackLists() { private void initTrackSelectionAdapter() { textTrackSelectionAdapter.clear(); audioTrackSelectionAdapter.clear(); + videoTrackSelectionAdapter.clear(); + if (player == null || !player.isCommandAvailable(COMMAND_GET_TRACKS) || !player.isCommandAvailable(COMMAND_SET_TRACK_SELECTION_PARAMETERS)) { return; } + Tracks tracks = player.getCurrentTracks(); audioTrackSelectionAdapter.init(gatherSupportedTrackInfosOfType(tracks, C.TRACK_TYPE_AUDIO)); + videoTrackSelectionAdapter.init(gatherSupportedTrackInfosOfType(tracks, C.TRACK_TYPE_VIDEO)); + if (controlViewLayoutManager.getShowButton(subtitleButton)) { textTrackSelectionAdapter.init(gatherSupportedTrackInfosOfType(tracks, C.TRACK_TYPE_TEXT)); } else { @@ -1621,6 +1643,8 @@ private void onSettingViewClicked(int position) { displaySettingsWindow(playbackSpeedAdapter, checkNotNull(settingsButton)); } else if (position == SETTINGS_AUDIO_TRACK_SELECTION_POSITION) { displaySettingsWindow(audioTrackSelectionAdapter, checkNotNull(settingsButton)); + } else if (position == SETTINGS_VIDEO_TRACK_SELECTION_POSITION) { + displaySettingsWindow(videoTrackSelectionAdapter, checkNotNull(settingsButton)); } else { settingsWindow.dismiss(); } @@ -1927,6 +1951,9 @@ public void onClick(View view) { } else if (audioTrackButton == view) { controlViewLayoutManager.removeHideCallbacks(); displaySettingsWindow(audioTrackSelectionAdapter, audioTrackButton); + } else if (videoTrackButton == view) { + controlViewLayoutManager.removeHideCallbacks(); + displaySettingsWindow(videoTrackSelectionAdapter, videoTrackButton); } else if (subtitleButton == view) { controlViewLayoutManager.removeHideCallbacks(); displaySettingsWindow(textTrackSelectionAdapter, subtitleButton); @@ -2256,6 +2283,83 @@ public void init(List trackInformations) { } } + private final class VideoTrackSelectionAdapter extends TrackSelectionAdapter { + + @Override + public void onBindViewHolderAtZeroPosition(SubSettingViewHolder holder) { + // Video track selection option includes "Auto" at the top. + holder.textView.setText(R.string.exo_track_selection_auto); + // hasSelectionOverride is true means there is an explicit track selection, not "Auto". + TrackSelectionParameters parameters = checkNotNull(player).getTrackSelectionParameters(); + boolean hasSelectionOverride = hasSelectionOverride(parameters); + holder.checkView.setVisibility(hasSelectionOverride ? INVISIBLE : VISIBLE); + holder.itemView.setOnClickListener( + v -> { + if (player == null + || !player.isCommandAvailable(COMMAND_SET_TRACK_SELECTION_PARAMETERS)) { + return; + } + + TrackSelectionParameters trackSelectionParameters = + player.getTrackSelectionParameters(); + + castNonNull(player) + .setTrackSelectionParameters( + trackSelectionParameters + .buildUpon() + .clearOverridesOfType(C.TRACK_TYPE_VIDEO) + .setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, /* disabled= */ false) + .build()); + + settingsAdapter.setSubTextAtPosition( + SETTINGS_VIDEO_TRACK_SELECTION_POSITION, + getResources().getString(R.string.exo_track_selection_auto)); + + settingsWindow.dismiss(); + }); + } + + private boolean hasSelectionOverride(TrackSelectionParameters trackSelectionParameters) { + for (int i = 0; i < tracks.size(); i++) { + TrackGroup trackGroup = tracks.get(i).trackGroup.getMediaTrackGroup(); + if (trackSelectionParameters.overrides.containsKey(trackGroup)) { + return true; + } + } + return false; + } + + @Override + public void onTrackSelection(String subtext) { + settingsAdapter.setSubTextAtPosition(SETTINGS_VIDEO_TRACK_SELECTION_POSITION, subtext); + } + + @Override + public void init(List trackInformations) { + this.tracks = trackInformations; + // Update subtext in settings menu with current video track selection. + TrackSelectionParameters params = checkNotNull(player).getTrackSelectionParameters(); + if (trackInformations.isEmpty()) { + settingsAdapter.setSubTextAtPosition( + SETTINGS_VIDEO_TRACK_SELECTION_POSITION, + getResources().getString(R.string.exo_track_selection_none)); + } else if (!hasSelectionOverride(params)) { + settingsAdapter.setSubTextAtPosition( + SETTINGS_VIDEO_TRACK_SELECTION_POSITION, + getResources().getString(R.string.exo_track_selection_auto)); + } else { + for (int i = 0; i < trackInformations.size(); i++) { + TrackInformation track = trackInformations.get(i); + if (track.isSelected()) { + settingsAdapter.setSubTextAtPosition( + SETTINGS_VIDEO_TRACK_SELECTION_POSITION, track.trackName); + break; + } + } + } + } + } + private abstract class TrackSelectionAdapter extends RecyclerView.Adapter { protected List tracks; diff --git a/libraries/ui/src/main/res/drawable-anydpi-v21/exo_ic_videotrack.xml b/libraries/ui/src/main/res/drawable-anydpi-v21/exo_ic_videotrack.xml new file mode 100644 index 0000000000..93a07dabf2 --- /dev/null +++ b/libraries/ui/src/main/res/drawable-anydpi-v21/exo_ic_videotrack.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/ui/src/main/res/drawable/exo_ic_videotrack.xml b/libraries/ui/src/main/res/drawable/exo_ic_videotrack.xml new file mode 100644 index 0000000000..ee3eccecde --- /dev/null +++ b/libraries/ui/src/main/res/drawable/exo_ic_videotrack.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/ui/src/main/res/values/drawables.xml b/libraries/ui/src/main/res/values/drawables.xml index a515d86b74..13e291ab6c 100644 --- a/libraries/ui/src/main/res/values/drawables.xml +++ b/libraries/ui/src/main/res/values/drawables.xml @@ -61,5 +61,6 @@ @drawable/exo_ic_settings @drawable/exo_ic_check @drawable/exo_ic_audiotrack + @drawable/exo_ic_videotrack @drawable/exo_ic_speed diff --git a/libraries/ui/src/main/res/values/ids.xml b/libraries/ui/src/main/res/values/ids.xml index c36331e587..bedda8be0d 100644 --- a/libraries/ui/src/main/res/values/ids.xml +++ b/libraries/ui/src/main/res/values/ids.xml @@ -46,6 +46,7 @@ +