Akamai Diversity

The Akamai Blog

Enhancing video streaming quality for ExoPlayer - Part 2: ExoPlayer's Buffering Strategy, how to lower startup time, and how to lower rebuffering for video on-demand

Co-Author: Mark Greve, Engineering Manager at Akamai. Part of the Media Client Team 

The first part of this blog series discussed about the quality of user experience (QoE) metrics and two strategies that heavily influence the QoE: the bitrate selection strategy and the buffering strategy. In this second part, we discuss about ExoPlayer's buffering strategy and how to configure the ExoPlayer to lower the startup time and, for video on-demand (VOD) content, how to lower rebuffering.

ExoPlayer's Buffering Strategy

ExoPlayer Version Investigated: 2.9.6

Classes implementing the LoadControl interface define the buffering strategy of ExoPlayer. The buffering strategy answers questions like:

  • do we need to load more media data?
  • do we have enough media data to start playback?

The buffering strategy is not responsible for managing the buffers or downloading media data (that's the MediaSource), or playback of media data (that's the Renderer). The buffering strategy is agnostic to the media format being played and has only a "consultative" role. It may occasionally happen that ExoPlayer will "ask" the LoadControl whether new media data should be loaded, and ignore the answer (e.g., ExoPlayer is already in the process of downloading media data).

ExoPlayer comes with a default implementation, DefaultLoadControl. This is a customizable implementation of the LoadControl interface.

DefaultLoadControl can be used to configure, e.g. :

  • How many milliseconds (ms) of media data ExoPlayer should buffer before starting playback (referred to as bufferForPlaybackMs). ExoPlayer will start playback as soon as it has at least bufferForPlaybackMs of media data, even if ExoPlayer did not fully buffer the complete segment.
  • The minimum amount of buffered media data (in ms) before ExoPlayer starts loading more data (referred to as minBufferMs).
  • The maximum amount of media data (in ms) ExoPlayer should buffer before it stops to load more (referred to as maxBufferMs).
  • How many milliseconds of media data ExoPlayer should buffer before restarting playback after a rebuffering event (referred to as bufferForPlaybackAfterRebufferMs). 

DefaultLoadControl has default values for each of these settings. In v2.9.6, these values are:

bufferForPlaybackMs

2500

minBufferMs

15000

maxBufferMs

50000

bufferForPlaybackAfterRebufferMs

5000

However, the values vary from ExoPlayer version to version. E.g., between ExoPlayer v2.7.3 and v2.8.0, the maxBufferMs for video streams has changed from 30 seconds to 50 seconds.

ExoPlayer's modular architecture also allows you to implement your own buffering strategy (that implements the LoadControl interface) and to plug it into ExoPlayer. But that's a topic for another post.

Lowering Startup Time

In ExoPlayer, it is easy to configure how much media data is needed before playback is started. In the first part of this blog series, we highlighted the importance of startup time as a QoE metric. A report Akamai published in 2016 found that "viewers will start abandoning a video if startup takes longer than two seconds to begin playing and for every additional second of delay, roughly an additional 6% of the audience leaves. [...] with a 10 second delay, nearly half the audience has left".

ExoPlayer version 2.9.6 requires by default 2.5 seconds of media data buffered before initiating playback. It's possible to lower the startup time by requiring less data to be buffered. On one hand, lowering the media data buffering value would result in lowering the video startup time, however the downside is, this might also result in increasing the rebuffering metrics at startup.

In the first part of this blog series, we have used a radar chart to visualize the impact of the different trade-offs on the 5 QoE metrics. The chart below shows the impact of lowering the minimum required amount of media data before initiating playback. 

enhancing one.pngSince the default value for bufferForPlaybackMs of 2.5 seconds is a conservative value, we believe it is a good choice to lower it.

The graph below shows the impact on startup time for a 3 Mbps stream when varying the value of this configuration option (not taking into account the round-trip time to the server, nor whether the server has to fetch the content from elsewhere). For example, on a 4 Mbps connection playing a 3 Mbps stream, configuring ExoPlayer to start playback after buffering 1.5s of media data means the theoretical startup time will be of 1.1s, instead of the 1.9s with the default configuration of buffering 2.5s of media data.

 enhancing two.pngThe next section talks about how to configure the ExoPlayer to lower the startup time and the hand waving latency. If you are not familiar with ExoPlayer, we recommend you first follow Google's Codelabs on Media streaming with ExoPlayer.

Configuring the DefaultLoadControl

Assuming that you create your ExoPlayer instance with the default LoadControl implementation using the following code (or similar):

ExoPlayer player = ExoPlayerFactory.newSimpleInstance(
new DefaultRenderersFactory(this),
new DefaultTrackSelector(),
new DefaultLoadControl())

You can configure the DefaultLoadControl using a DefaultLoadControl.Builder:

/* Instantiate a DefaultLoadControl.Builder. */
DefaultLoadControl.Builder builder = new DefaultLoadControl.Builder();

/* Milliseconds of media data buffered before playback starts or resumes. */
final long loadControlStartBufferMs = 1500;

Configure the new DefaultLoadControl to use our setting for how many Milliseconds of media data must be buffered before playback starts or resumes after a user action event.

builder.setBufferDurationMs(
DefaultLoadControl.DEFAULT MAX BUFFER MS,
loadControlStartBufferMs,

DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);

/* Build the actual DefaultLoadControl instance */
DefaultLoadControl loadControl = builder.createDefaultLoadControl();

/* Instantiate ExoPlayer with our configured DefaultLoadControl */
ExoPlayer player = ExoPlayerFactory.newSimpleInstance(
new DefaultRenderersFactory(this),
new DefaultTrackSelector(), loadControl);

Lowering Rebuffering Metrics for VOD Content

Besides configuring settings that lead to lower startup time, you can configure the DefaultLoadControl to establish how much media data to buffer, which can impact rebuffering metrics for VOD content. 

ExoPlayer v2.9.6 will by default load 50 seconds of media data into its internal buffer if it can (the maxBufferMs setting). In the case of VOD content, increasing this value will give the player more room to handle fluctuations in network bandwidth and lower rebuffering metrics; the downside is that it will also increase the memory usage of ExoPlayer. Keeping memory usage low is important on low-end devices. Another downside is the wasted bandwidth usage in case playback stops prematurely.

To find the value that works best for your setup of VOD content, we recommend doing A/B testing with various values of the configuration option to find the one that gives a good tradeoff between rebuffering rates and memory usage. We recommend being conservative by increasing the value to, e.g., 60 seconds initially to find out if it provides a material difference for rebuffering metrics.

Configuring the DefaultLoadControl

We assume that you create your ExoPlayer instance with the default LoadControl implementation (as shown in the previous section on Lowering Startup Time - Configuring the DefaultLoadControl).

You can configure the DefaultLoadControl using a DefaultLoadControl.Builder:

/* Instantiate a DefaultLoadControl.Builder. */
DefaultLoadControl.Builder builder = new
DefaultLoadControl.Builder();

/* Maximum amount of media data to buffer (in milliseconds). */
final long loadControlMaxBufferMs = 60000;

/*Configure the DefaultLoadControl to use our setting for how many
Milliseconds of media data to buffer. */
builder.setBufferDurationsMs(
DefaultLoadcontrol.DEFAULT MIN BUFFER MS,
loadControlMaxBufferMs,
/* To reduce the startup time, also change the line below */
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,

DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);

/* Build the actual DefaultLoadControl instance */
DefaultLoadControl loadControl = builder.createDefaultLoadControl();

/* Instantiate ExoPlayer with our configured DefaultLoadControl */
ExoPlayer player = ExoPlayerFactory.newSimpleInstance(
new DefaultRenderersFactory(this),
new DefaultTrackSelector(),
loadControl);

In case minBufferMs and maxBufferMs have different values, ExoPlayer will have a bursty behavior refilling the buffer. ExoPlayer will buffer until it fills its buffer with maxBufferMs of media data, then wait until it decreases to minBufferMs (mainly applicable to VOD). Once the buffer level falls below minBufferMs of media data, ExoPlayer will start loading media data again until it has a buffer worth maxBufferMs of media data. Such a bursty behavior can lead to rebuffering issues. This is the default behavior in ExoPlayer v2.9.6 and prior, as these values differ. In v2.9.6, minBufferMs has a default value of 15000 and maxBufferMs is 50000.

To further reduce rebuffering chances, we recommend maintaining a large buffer in ExoPlayer at all times, by setting the minBufferMs to the same value as maxBufferMs. The ExoPlayer team has run experiments and found that setting minBufferMs to the same value as maxBufferMs (and thus changing the buffering behavior from bursty to drip-style) reduced significantly rebuffering events, while increasing only slightly battery usage. In fact, starting with v2.10, ExoPlayer has for the video use-case the default minBufferMs equal to maxBufferMs and set to 50s.

How to configure the DefaultLoadControl using a DefaultLoadControl.Builder:

/* Instantiate a DefaultLoadControl.Builder. */
DefaultLoadControl.Builder builder = new
DefaultLoadControl.Builder();

/*How many milliseconds of media data to buffer at any time. */
final long loadControlBufferMs = DefaultloadControl.MAX_BUFFER_MS; /* This is 50000 milliseconds in ExoPlayer 2.9.6 */

/* Configure the DefaultLoadControl to use the same value for */
builder.setBufferDurationMs(
loadControlBufferMs,
loadControlBufferMs,
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);

Summary

LoadControl is the entry point to ExoPlayer's buffering strategy. ExoPlayer comes with a default, yet configurable implementation of the LoadControl interface that should be sufficient for most use cases.

 Based on the discussions so far, below is a quick recap of the recommendations:

  1. To reduce the startup time, lower the value of bufferForPlaybackMs; effectively, lowering the minimum amount of media data buffered before starting playback. This has the drawback of potentially increasing the number rebuffering events.
  2. To reduce the rebuffering metrics for VOD content, increase the value of  maxBufferMs; effectively increasing the maximum amount of media data buffered. However, keep in mind that can negatively impact low-end devices.
  3. To reduce rebuffering metrics, set minBufferMs and maxBufferMs to the same value. This will change ExoPlayer's behavior from a bursty to a drip-style buffering. 

We would like to thank our colleagues Christian Worm Mortensen and Laust Brock-Nannestad for feedback in writing this post.

In the next blog post, we will look at ExoPlayer's Bitrate Selection Strategy. Stay tuned!

Leave a comment