How to turn On/OFF closed captions in HLS Streaming URL in Exoplayer?

I was able to control caption selection in ExoPlayer 2 using a DefaultTrackSelector. The code below was modified based on the ExoPlayer 2 Demo's TrackSelectionHelper class, which should be referenced for more implementation details.

To turn captions off, you need to disable the renderer for the text tracks and clear the selection overrides.

trackSelector.setRendererDisabled(TRACK_TEXT, true);
trackSelector.clearSelectionOverrides();

TRACK_TEXT is a local static variable I created representing the index of the text tracks (2), in relation to video/audio tracks. I believe that SelectionOverrides are just programmatically specified track selections.

To enable the tracks again, you need to enable the renderer for text tracks, and then set up a new SelectionOverride for your desired text track. Before you can do this, you need to get the TrackGroupArray of the currently mapped text tracks from your DefaultTrackSelector.

MappingTrackSelector.MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
TrackGroupArray textGroups = mappedTrackInfo.getTrackGroups(TRACK_TEXT); // list of captions
int groupIndex = 1; // index of desired caption track within the textGroups array

trackSelector.setRendererDisabled(TRACK_TEXT, false);
MappingTrackSelector.SelectionOverride override = 
    new MappingTrackSelector.SelectionOverride(fixedFactory, groupIndex, 0);
trackSelector.setSelectionOverride(TRACK_TEXT, textGroups, override);

For more implementation details (e.g., initializing the trackSelector and fixedFactory), check out the ExoPlayer 2 Demo.

You can use a SubtitleView to position the captions within your layout. Your class will need to implement TextRenderer.Output and override the onCues() method.

@Override
public void onCues(List<Cue> cues) {
    if (subtitleView != null) {
        subtitleView.onCues(cues);
    }
}

After updating the ExoPlayer library to

implementation 'com.google.android.exoplayer:exoplayer:2.9.5'

I found that the MappingTrackSelector.SelectionOverride method does not exist anymore.

I was unable to find a working alternative, as just replacing with DefaultTrackSelector.SelectionOverride didn't work for me, and subtitles were not playing in my case for video.

MediaSource mediaSource =  new HlsMediaSource.Factory(mediaDataSourceFactory).createMediaSource(uri);

Final set of lines which worked for me:

TrackGroupArray trackGroups = trackSelector.getCurrentMappedTrackInfo().getTrackGroups(newSubtitle.renderIndex);

DefaultTrackSelector.ParametersBuilder paramsBuilder = trackSelector.buildUponParameters();

DefaultTrackSelector.SelectionOverride sOverride = new DefaultTrackSelector.SelectionOverride(newSubtitle.groupIndex, newSubtitle.indexWithinGroup);

boolean isDisabled = trackSelector.getParameters().getRendererDisabled(newSubtitle.renderIndex);

paramsBuilder.setRendererDisabled(newSubtitle.renderIndex, isDisabled);

if (sOverride != null) {
    paramsBuilder.setSelectionOverride(newSubtitle.renderIndex, trackGroups, sOverride);
} else {
    paramsBuilder.clearSelectionOverrides(newSubtitle.renderIndex);
}

trackSelector.setParameters(paramsBuilder);

Old code was:

TrackGroupArray trackGroups = trackSelector.getCurrentMappedTrackInfo().getTrackGroups(newSubtitle.renderIndex);

MappingTrackSelector.SelectionOverride sOverride = new MappingTrackSelector.SelectionOverride(
    new FixedTrackSelection.Factory(), 
    newSubtitle.groupIndex, 
    newSubtitle.indexWithinGroup
);

trackSelector.setSelectionOverride(newSubtitle.renderIndex, trackGroups, sOverride);
trackSelector.setRendererDisabled(newSubtitle.renderIndex, false);

To close subtitles:

DefaultTrackSelector.ParametersBuilder paramsBuilder = trackSelector.buildUponParameters();

paramsBuilder.setRendererDisabled(playingSubtitle.renderIndex, true);

trackSelector.setParameters(paramsBuilder);

In my application I initialize the trackselecktor as follow

To turn off close captions from video track

trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
    trackSelector.setParameters(new DefaultTrackSelector.ParametersBuilder()
            .setRendererDisabled(C.TRACK_TYPE_VIDEO, true)
            .build()
    );

To turn on close captions from video track

trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
    trackSelector.setParameters(new DefaultTrackSelector.ParametersBuilder()
            .setRendererDisabled(C.TRACK_TYPE_VIDEO, false)
            .build()
    );

I highly recommend don't use the constant TRACK_TEXT or C.TRACK_TYPE_TEXT as this:

trackSelector.setRendererDisabled(TRACK_TEXT, true);
trackSelector.clearSelectionOverrides();

because it could vary depends on the asset which you are using, you can ensure that you are disabling the correct renderer with the following code (Kotlin version):

  private fun disableSubtitles(){
    (0 until (trackSelector.currentMappedTrackInfo?.rendererCount ?: 0)).filter {position ->
      player.getRendererType(position) == C.TRACK_TYPE_TEXT
    }.map {position ->
      trackSelector.parameters = trackSelector.buildUponParameters()
          .setRendererDisabled(position,true)
          .clearSelectionOverrides(position)
          .build()
    }
  }