Adaptive Video Streaming in Android

Farman
3 min readFeb 7, 2021

First, let’s understand what is Progressive Streaming?

Progressive Video Streaming: A single video file is streamed over internet and served to user. This type of video file is often .mp4, but it can be other formats as well. Here the same video file is served to different devices (likes Mobile, Desktop), hence the video quality can vary on devices having different screen size. And again the same file can take different buffering time for different internet bandwidth available on the devices.

Adaptive Video Streaming solves these two problems:

(a) Video quality issue on different devices,

(b) Buffering time issue on varying internet connection.

Adaptive Video Streaming: Adaptive video file contains set of segments of a video. Each segment is often 4 seconds long (can be longer as well). And each segment contains a video chunk of different quality, so video player can switch to a video chunk (segment) if necessary. Hence the same adaptive video file will be steamed with good quality irrespective of screen size of the device and user will not see buffering even when internet quality is low, video is adapted accordingly.

Adaptive Video Streaming Using ExoPlayer

Let’s jump to our main topic here. Google provides ExoPlayer, an application level media player, alternative to Android MediaPlayer API. ExoPlayer is easy to customise and extend. It supports many features which are not supported by Android’s MediaPlayer API, including DASH and SmoothStreaming adaptive playbacks.

We can follow below steps to setup basic ExoPlayer in our app and start streaming video in the app.

Step-1: Make sure you have the Google and JCenter repositories included in the build.gradle file in the root of your project.

repositories {
google()
jcenter()
}

Step-2: Add ExoPlayer modules in the build.gradle file of your app module.

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

The above line adds the full library. alternatively, we can add only library modules what we need. Let’s add dependencies on the Core, DASH and UI library modules as of now.

implementation 'com.google.android.exoplayer:exoplayer-core:2.13.0'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.0'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.13.0'
implementation 'com.google.android.exoplayer:exoplayer-hls:2.13.0'
  • exoplayer-core: Core functionality (required).
  • exoplayer-dash: Support for DASH content.
  • exoplayer-hls: Support for HLS content.
  • exoplayer-ui: UI components and resources for use with ExoPlayer.

Here we are adding two Adaptive streaming support- 1) Dash and 2) HLS.

Step-3: Make sure Java 8 support is enabled in build.gradle

compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
}

Step-4: Adding PlayerView in your layout file

<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="400dp"/>

Step-5: Reference the PlayerView in your activity, declared in layout file

private lateinit var playerView: PlayerView

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

playerView = findViewById(R.id.player_view)
}
}

Step-6: Initialise the Player

private fun initPlayer(context: Context) {
player = SimpleExoPlayer.Builder(context)
.setBandwidthMeter(DefaultBandwidthMeter.Builder(context).build())
.setTrackSelector(DefaultTrackSelector(context, AdaptiveTrackSelection.Factory()))
.build()
}

Step-7: Prepare MediaSource for the player

private fun getMediaSource(uri: Uri): MediaSource {
val lastPathSegment = uri.getLastPathSegment() ?: ""
val httpDataSource = DefaultHttpDataSourceFactory(Constants.USER_AGENT,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, true)
val mediaItem = MediaItem.Builder().setUri(uri).build()

if (lastPathSegment.contains(Constants.MEDIA_TYPE_MP3) || lastPathSegment.contains(Constants.MEDIA_TYPE_MP4)) {
return ProgressiveMediaSource.Factory(httpDataSource).createMediaSource(mediaItem)
} else if (lastPathSegment.contains(Constants.MEDIA_TYPE_HLS)) {
return HlsMediaSource.Factory(httpDataSource).createMediaSource(mediaItem)
} else {
val dashChunkSourceFactory = DefaultDashChunkSource.Factory(httpDataSource)
val manifestDataSourceFactory = httpDataSource
return DashMediaSource.Factory(dashChunkSourceFactory, manifestDataSourceFactory)
.createMediaSource(mediaItem)
}
}

Step-8: Prepare Player to play the video after setting media source.

private fun preparePlayer(uri: Uri) {
player.setMediaSource(getMediaSource(uri))
player.prepare()
player.playWhenReady = true
}

Here we are are done with initial setup of ExoPlayer. And it should play the video url passed to preparePlayer() method above.

Let’s cover more in the next part where we will see states of player and handling Track Selection of Adaptive video stream.

--

--

Farman

Learner, enthuisast and Android Dev. The best way to learn and stay up to date is simply to do something.