For media streaming such as HLS or MPEG-DASH, there is a mechanism called Adaptive Bitrate (ABR), which makes it possible to switch between multiple resolutions based on the network bandwidth. It helps reduce network usage and keep a video playing at the maximum resolution that a player is currently capable of to play. However, what resolution should a player play first? If you have enough bandwidth to play maximum resolution, you would not want to play the minimum resolution first and wait for the resolution goes up eventually.
Let’s look at how ExoPlayer decides the first resolution. Since ExoPlayer is customizable, you can set your initial bitrate to ExoPlayer, but if the initial bitrate is not provided, ExoPlayer decides the initial bitrate based on your country code and the network type. For example, if a user tries to play a video in Japan with Wifi connected, ExoPlayer assumes that you have 5.7Mbps, which would probably be about 720p or 1080p. You can check the detail implementation from here below.
You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or…
However, what about the wifi is too weak to play those resolutions or have enough bandwidth to play much higher resolution in contrast.
Then, what is the best way to decide the initial bitrate? I introduce three types of ways to decide an initial bitrate, which are Session Scope, Application Scope, Lifetime Scope.
This is the graph to show how each of these behaves.
Session scope is a simple strategy. It always gets the minimum resolution every playback session, which is unlikely to stall but blur for the first few seconds. It is suitable for a situation where you want to play as fast as possible.
The implementation is also simple and easy. You just set the minimum bitrate to
DefaultBandwidthMeter every time you create a player instance.
For Application Scope, instead of fetching the minimum resolution every time, it keeps the bitrate across an application process. Once you know the network bandwidth from the previous session, you can simply assume that you can play the same resolution that a player was previously playing. I think this strategy works pretty well in most cases.
You can archive this to hold a
DefaultBandwidthMeter as a singleton and use the same instance every time. You might be worried about the memory leak, but ExoPlayer does this as a default behavior if you do not provide your own
DefaultBandwidthMeter and it should be ok if you carefully remove all listeners you added to a player or
BandwidthMeter when releasing a player.
What if a network type has changed during playback like from Wifi to 3G? ExoPlayer provides a way to handle this situation. When you create a
DefaultBandwidthMeter, you can specify
setResetOnNetworkTypeChange(true) and it automatically clears the calculated bitrate when network type changes.
This is the third way to decide the initial bitrate. It keeps bitrate beyond an application process and always decides the initial bitrate base on the previous session. This is very helpful when you can assume network bandwidth does not change very often such as TV devices or home devices.
To archive this, you have to persist the bitrate using SharedPreferences or another way of persisting data. Then you can use it as an initial bitrate and it behaves in the same way as Application Scope while the app is alive. But you may have to clear the saved bitrate when an error happened like BehindLiveWindowException or TimeoutException for the sake of avoiding an unexpected stall.
In this post, I introduced three ways of deciding an initial bitrate. These are strategies that I have ever tried and it seems to work so far, but there is no perfect solution for everyone and I hope this post helps someone think about an initial bitrate for better user experience.