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.
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.
Although the
DefaultLoadControlshould 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
initialisePlayerandplayAudiomethods with the bandwidth meter included in the track selection:The
DefaultBandwidthMetershould 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
BandwidthMeterinto theAdaptiveTrackSelection.Factory, you can still add an event listener to it to log changes in estimated bandwidth.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.
Add the above code snippet just after preparing the player with
exoPlayer.prepare();to manually set the track.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 aPlayer.Listenercallback when the player state changes toPlayer.STATE_READY.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.If your manual track selection is successful but only results in the player using the lower bitrate track even on a high-speed network like 5G, this suggests that the track selection override is taking precedence over ExoPlayer's own adaptive mechanisms. When you set an explicit track selection override, ExoPlayer will stick with that choice and will not adapt based on network conditions.
So, remove the manual override for adaptation and try a conditional manual override: Implement logic to manually switch tracks based on certain conditions, such as if the bandwidth is below a particular threshold. In this way, you could have a mix of manual and adaptive behavior.
Again, this is for testing: while it is possible to mix manual and adaptive track selection, doing so can make the behavior of your player harder to predict and debug.
As a last resort, if ExoPlayer's adaptive algorithm is not meeting your needs and you are also not getting reliable bandwidth estimates, you may indeed have to take over complete control of track selection.
In such a case, you can write a logic that maps bandwidth estimates to specific tracks. However, make sure to apply some form of smoothing or averaging to the bandwidth estimates to avoid rapid track switching, which can be disruptive to the user.
As an alternative, you could try and use Android's network capabilities: The
ConnectivityManagerclass in Android can provide general information about the type and speed of the current network connection. This information is less granular than a bandwidth estimate but can still be useful for choosing between high, medium, and low-quality streams.That or use a third-party network measurement library, like
square/okhttp.You could also implement your own bandwidth estimation logic.
One approach is to download a small file at the beginning of the application and measure the download speed. Be cautious with this approach as it introduces latency and could vary based on server speeds, concurrent network operations, or other factors.