1

I am using Exoplayer in my Android app for streaming hls audio, I am trying to play adaptive stream using my master HLS playlist which contains various variant of the same audio track but in different bitrate as per suitable available network bandwidth.

But what i have seen, the Exoplayer starts playing with the initial variant in the master playlist, and then immediately moves to the highest available bitrate variant of audio, every time. It doesn't uses any of the other bitrate even if I have changed my network to 2G, 3G or any other lower bandwidth, it doesn't adapts to lower bitrate in any case and follows the above pattern. It will keep buffering for very long, but never use any lower track variant as per the bandwidth.

Here's the code of my Exoplayer implementation -

private void initialisePlayer(){
        if(exoPlayer == null) {
            AdaptiveTrackSelection.Factory videoTrackSelectionFactory =  new AdaptiveTrackSelection.Factory();
            TrackSelector trackSelector = new DefaultTrackSelector(this, videoTrackSelectionFactory);
            LoadControl loadControl = new DefaultLoadControl();
            exoPlayer = new ExoPlayer.Builder(getApplicationContext())
                    .setTrackSelector(trackSelector).setLoadControl(loadControl).build();
            exoPlayer.addListener(listener);
            exoPlayer.setAudioAttributes(audioAttributes, /* handleAudioFocus= */ true);
            exoPlayer.setHandleAudioBecomingNoisy(true);
        }
    }

   private void playAudio(){

    DefaultBandwidthMeter bandwidthMeter =  DefaultBandwidthMeter.getSingletonInstance(this);
    DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(this).setTransferListener(bandwidthMeter);

    HlsMediaSource audioSource = HlsMediaSource.Factory(dataSourceFactory)
                    .setLoadErrorHandlingPolicy(new CustomPolicy())
                    .createMediaSource(MediaItem.fromUri("my-hls-master-url"));

    exoPlayer.setMediaSource(audioSource);
    exoPlayer.prepare();
    exoPlayer.setPlayWhenReady(true);

  
   }

I am using -

implementation 'androidx.media3:media3-exoplayer:1.1.1'
implementation "androidx.media3:media3-exoplayer-hls:1.1.1"

I have my master playlist as like -

#EXT-X-VERSION:4

# Audio Renditions

#EXT-X-STREAM-INF:BANDWIDTH=54000,CODECS="mp4a.40.5"
songs54/audio.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=84000,CODECS="mp4a.40.5"
songs84/audio.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=126000,CODECS="mp4a.40.2"
songs126/audio.m3u8


#EXT-X-STREAM-INF:BANDWIDTH=160000,CODECS="mp4a.40.5"
songs160/audio.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=320000,CODECS="mp4a.40.2"
songs320/audio.m3u8

I have been trying to fix this from last 2 days, any help will be really appreciated.

MrinmoyMk
  • 551
  • 7
  • 21

1 Answers1

1

To understand more why your ExoPlayer is not adapting to the network conditions as you expect, you could start with including the bandwidth meter in the track selector. That helps ExoPlayer to make adaptive decisions.

AdaptiveTrackSelection.Factory videoTrackSelectionFactory =  new AdaptiveTrackSelection.Factory(bandwidthMeter);

Although the DefaultLoadControl should be sufficient in most cases, you might want to set custom values for buffer size, buffer duration, etc., to fine-tune the adaptive behavior.

You would need to update the initialisePlayer and playAudio methods with the bandwidth meter included in the track selection:

private void initialisePlayer(){
    TrackSelector trackSelector = new DefaultTrackSelector(this);
    LoadControl loadControl = new DefaultLoadControl();
    exoPlayer = new ExoPlayer.Builder(this)
            .setTrackSelector(trackSelector)
            .setLoadControl(loadControl)
            .build();
    exoPlayer.addListener(listener);
    exoPlayer.addAnalyticsListener(new EventLogger(trackSelector));
    exoPlayer.setAudioAttributes(audioAttributes, /* handleAudioFocus= */ true);
    exoPlayer.setHandleAudioBecomingNoisy(true);
}

private void playAudio(){
    DefaultBandwidthMeter bandwidthMeter =  DefaultBandwidthMeter.getSingletonInstance(this);
    DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(this)
            .setTransferListener(bandwidthMeter);
    
    HlsMediaSource audioSource = new HlsMediaSource.Factory(dataSourceFactory)
                .setLoadErrorHandlingPolicy(new CustomPolicy())
                .createMediaSource(MediaItem.fromUri("my-hls-master-url"));

    exoPlayer.setMediaSource(audioSource);
    exoPlayer.prepare();
    exoPlayer.setPlayWhenReady(true);
}

The DefaultBandwidthMeter should provide you with bandwidth estimates. You could log these to see if they change when you switch networks. If the bandwidth estimates do not change, that might be the issue.
Even though you can't inject the BandwidthMeter into the AdaptiveTrackSelection.Factory, you can still add an event listener to it to log changes in estimated bandwidth.

bandwidthMeter.addEventListener(new Handler(Looper.getMainLooper()), new BandwidthMeter.EventListener() {
    @Override
    public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
    Log.d("BANDWIDTH_SAMPLE", "bitrate = " + bitrate);
    }
});

I have added the bandwidthMeter.addEventListener and have seen that bitrate logged is always returning a higher number like - 3200000 or 3099619 or 2929804 and even when I change to 2G network the bitrate is something between 117245 and 165935.
But exoplayer is still not switching the audio variants.

Your bandwidth estimate on a 2G network is showing values like 117245 and 165935 so... ExoPlayer thinks the bandwidth is sufficient to play the highest bitrate available in your master playlist, which has a max bitrate of 320000 according to what you posted.

Test if you can manually set the track to confirm whether the issue is with ExoPlayer's adaptive mechanism or something else.

MappingTrackSelector.MappedTrackInfo mappedTrackInfo = ((DefaultTrackSelector) trackSelector).getCurrentMappedTrackInfo();
if (mappedTrackInfo != null) {
    int rendererIndex = 0; // For audio. Change it as per your needs
    int trackIndexToBeUsed = 1; // Use 0, 1, 2... based on available tracks
    DefaultTrackSelector.SelectionOverride override = new DefaultTrackSelector.SelectionOverride(trackIndexToBeUsed, 0);
    DefaultTrackSelector.ParametersBuilder parametersBuilder = trackSelector.buildUponParameters();
    parametersBuilder.setSelectionOverride(rendererIndex, mappedTrackInfo.getTrackGroups(rendererIndex), override);
    trackSelector.setParameters(parametersBuilder);
}

Add the above code snippet just after preparing the player with exoPlayer.prepare(); to manually set the track.


I have used the codes MappingTrackSelector.MappedTrackInfo .... trackSelector.setParameters(parametersBuilder); right after exoplayer.prepare(); but still couldn't find any difference.

Exoplayer still uses the first audio variant in the initial load and then immediately move to the highest variant audio and never changes the audio variant irrespective of the bandwidth.

I can see bitrate being logged by bandwidthmeter as - 990000 in 2G network for multiple times initially, and then it changes slowly and subsequently to lower bitrates like 39000 to 189000 in the same network.

Sometimes, issues with track selection can be related to when exactly you are calling certain methods. Make sure you are calling the manual track selection code after the exoPlayer.prepare() call has completed. You might want to place the track selection code in a Player.Listener callback when the player state changes to Player.STATE_READY.

exoPlayer.addListener(new Player.Listener() {
    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        if (playbackState == Player.STATE_READY) {
            // Manual track selection code here
        }
    }
});

Although ExoPlayer should handle this itself, for debugging purposes, you could explicitly set some parameters for adaptation, such as the minimum duration of media that needs to be buffered for playback to start or continue. ExoPlayer allows you to set such thresholds through DefaultLoadControl.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Hi @VonC ```new AdaptiveTrackSelection.Factory(bandwidthMeter)``` is not available in the latest media3 exoplayer. it takes no parameter – MrinmoyMk Sep 01 '23 at 20:20
  • @MrinmoyMk Good point. I have updated the answer, using this time [Media3 ExoPlayer API](https://developer.android.com/reference/androidx/packages) – VonC Sep 02 '23 at 09:32
  • I have added the `bandwidthMeter.addEventListener` and have seen that bitrate logged is always returning a higher number like - `3200000 or 3099619 or 2929804` and even when I change to 2G network the bitrate is something between `117245 and 165935`. But exoplayer is still not switching the audio variants – MrinmoyMk Sep 02 '23 at 19:36
  • @MrinmoyMk OK. I have edited the answer to address your comment. – VonC Sep 02 '23 at 19:42
  • I have used the codes `MappingTrackSelector.MappedTrackInfo .... trackSelector.setParameters(parametersBuilder);` right after `exoplayer.prepare();` but still couldn't find any difference. exoplayer still uses the first audio variant in the initial load and then immediately move to the highest variant audio and never changes the audio variant irrespective of the bandwidth. i can see bitrate being logged by bandwidthmeter as - 990000 in 2G network for multiple times initially and then it changes slowly and subsequently to lower bitrates like 39000 to 189000 in the same network – MrinmoyMk Sep 03 '23 at 04:55