Streaming videos securely on Android can be a bit challenging, but it can be easily done with Exoplayer!
When it comes to streaming videos on Android, Exoplayer can be your go-to media player. It is even used by Google apps such as YouTube and Google TV. Exoplayer allows a lot of customization which enables its adoption for various use cases. Its support of media formats is also very wide, including adaptive streaming formats such as HLS, Dash, and Smooth Streaming. With its support for widevine, you can ensure that your content remains safe
What is an ExoPlayer?
ExoPlayer is an open-source media player for Android maintained by Google. It is not part of the Android framework and is distributed separately from the Android SDK. With ExoPlayer, you can easily take advantage of new features as they become available by updating your app.
ExoPlayer is the best alternative to android’s built-in MediaPlayer API which is used to control the playback of audio/video files and streams. It supports various features such as Dynamic Adaptive Streaming over HTTP(DASH), HTTP Live Streaming(HLS), Smooth Streaming, and Common Encryption. It can be used to play audio as well as video streaming online directly from the server and/or offline(locally) by downloading. It can be easily customized and extended. We can add features like captions, speed control, forward, rewind, etc. to the player. Exoplayer also provides a feature for encrypting the media playback (both online and offline) for secure streaming. It does not work on the device below API level 16.
Let’s see what Exoplayer has to offer and why or when we should use it over the built-in MediaPlayer API.
What are the Advantages of Using Exoplayer?
ExoPlayer has a number of advantages over Android’s built-in MediaPlayer:
- There are fewer device-specific issues and less variation in behavior across different Android versions and devices with ExoPlayer.
- You can update the player along with your application. Since ExoPlayer is a library included in your application, you can choose which version to use and easily update to a newer version. To ensure your application are developed and run smoothly, use the Docker platform.
- You can customize and extend it to meet your needs. A lot of ExoPlayer components can be replaced with custom implementations since it was designed specifically with this in mind.
- ExoPlayer has in-built support for playlists.
- The Exoplayer supports a variety of formats in addition to DASH and SmoothStreaming. Additionally, it supports advanced HLS features such as handling #EXT-X-DISCONTINUITY tags and the ability to seamlessly merge, concatenate, and loop media streams.
- On Android 4.4 (API level 19) and higher, it supports Widevine common encryption. Although the actual widevine support varies from device and is usually only available starting from Android 5. Sometimes, older devices with Android 5 and 6 can also get revoked due to security updates.
- It is possible to integrate with a number of additional libraries quickly by using official extensions. For example, by using the Interactive Media Ads SDK, you can easily monetize your content with the IMA extension.
Explore More ✅
Stream Your Content Securely On Android With VdoCipher
VdoCipher helps provide end-to-end solutions for video, right from hosting, encoding, and encryption to the player. On top of it, you get APIs to manage videos, players, and more.
How To Implement Exoplayer in Android with examples?
We will create a simple exoplayer application to play a video using MediaItem.
The steps to implement Exoplayer are as follows:
- Add exoplayer dependencies in your app level build.gradle
implementation 'com.google.android.exoplayer:exoplayer-core:2.18.0'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.18.0'
implementation 'com.google.android.exoplayer:exoplayer-hls:2.18.0'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.18.0'
- Add SimpleExoPlayerView in layout file A
SimpleExoPlayerView
can be included in the layout for an Activity belonging to a video application as follows:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.exoplayer2.ui.SimpleExoPlayerView
android:id="@+id/exoPlayerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
- Create and load the player In your Activity, create an instance of
Exoplayer
and add it to theSimpleExoPlayerView
MediaItem
represents a media which can be added to the player at the time of preparation. After the player is prepared you can set setsetPlayWhenReady
to start the playback when media is ready.
SimpleExoPlayerView playerView = findViewById(R.id.exoPlayerView);
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Bind the player to the view.
playerView.setPlayer(player);
// Create and add media item
MediaItem mediaItem = MediaItem.fromUri(video_url);
player.addMediaItem(mediaItem);
// Prepare exoplayer
player.prepare();
// Play media when it is ready
player.setPlayWhenReady(true);
- Handling the player controls Methods on the player can be called to control the player. Below are some of the methods:
play
andpause
: Used to play and pause the videoseekTo
: Seeks to a position specified in milliseconds in the current MediaItemplayWhenReady
: Whether playback should proceed when readyhasNextMediaItem
,hasPreviousMediaItem
,seekToPreviousMediaItem
,seekToNextMediaItem
: Allows navigating through the playlistsetPlaybackParameters
: Attempts to set the playback parameters.Playback parameters changes may cause the player to buffer.Player.Listener.onPlaybackParametersChanged(PlaybackParameters
) will be called whenever the currently active playback parameters change
- Release the player Use
ExoPlayer.release
method to release the player after when it is no longer required.
if (exoPlayer != null) {
exoPlayer.release();
}
How to Customize Exoplayer?
ExoPlayer comes with many customizations available such as UI adjusting to match your app, deciding caching mechanism for data loaded from the network, customizing server interaction to intercept HTTP requests and responses, customizing error handling policy, enabling asynchronous buffer queueing, and many more. In this section, we will look at how we can customize UI with ExoPlayer.
Customizing ExoPlayer’s UI components
ExoPlayer V2 includes several out-of-the-box UI components for customization, most notably:
- SimpleExoPlayerView is a high level view for SimpleExoPlayer media playbacks. It displays video, subtitles and album art during playback, and displays playback controls using a PlaybackControlView.
- PlaybackControlView is a view for controlling ExoPlayer instances. It displays standard playback controls including a play/pause button, fast-forward and rewind buttons, and a seek bar.
Use of this view is optional. You are free to implement your own UI components by yourself at the cost of some extra work. The SimpleExoPlayerView displays video, subtitles, and album art during playback, and displays playback controls using a PlaybackControlView.
A SimpleExoPlayerView can be customized by setting attributes (or calling corresponding methods), overriding the view’s layout file or by specifying a custom view layout file, as mentioned below.
Setting attributes for SimpleExoPlayerView
A SimpleExoPlayerView can be included in the layout for an Activity belonging to a video application as follows
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.exoplayer2.ui.SimpleExoPlayerView
android:id="@+id/player"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
Now you can use following attributes on SimpleExoPlayerView to customize it when used in a layout XML file.
- use_artwork – Whether artwork is used if available in audio streams.
- default_artwork – Default artwork to use if no artwork available in audio streams.
- use_controller – Whether the playback controls can be shown.
- hide_on_touch – Whether the playback controls are hidden by touch events.
- auto_show – Whether the playback controls are automatically shown when playback starts, pauses, ends, or fails. If set to false, the playback controls can also be manually operated with showController() and hideController().
- resize_mode – Controls how video and album art is resized within the view. Valid values are fit, fixed_width, fixed_height and fill.
- surface_type – The type of surface view used for video playbacks. Valid values are surface_view, texture_view and none. Using none is recommended for audio only applications, since creating the surface can be expensive. Using surface_view is recommended for video applications.
- shutter_background_color – The background color of the exo_shutter view.
- player_layout_id – Specifies the id of the layout to be inflated.
- controller_layout_id – Specifies the id of the layout resource to be inflated by the child PlaybackControlView.
Overriding the view’s layout file
To customize the layout of SimpleExoPlayerView throughout your app, or just for certain configurations, you can define exo_simple_player_view.xml layout files in your application res/layout* directories. These layouts will override the one provided by the ExoPlayer library, and will be inflated for use by SimpleExoPlayerView. The view identifies and binds its children by looking for the following ids:
- exo_content_frame – A frame whose aspect ratio is resized based on the video or album art of the media being played, and the configured resize_mode. The video surface view is inflated into this frame as its first child. Type AspectRatioFrameLayout
- exo_shutter – A view that’s made visible when video should be hidden. This view is typically an opaque view that covers the video surface view, thereby obscuring it when visible. Type: View
- exo_subtitles – Displays subtitles. Type: SubtitleView
- exo_artwork – Displays album art. Type: ImageView
- exo_controller_placeholder – A placeholder that’s replaced with the inflated PlaybackControlView. Ignored if an exo_controller view exists. Type: View
- exo_controller – An already inflated PlaybackControlView. Allows use of a custom extension of PlaybackControlView. Note that attributes such as rewind_increment will not be automatically propagated through to this instance. If a view exists with this id, any exo_controller_placeholder view will be ignored. Type: PlaybackControlView
- exo_overlay – A FrameLayout positioned on top of the player which the app can access via getOverlayFrameLayout(), provided for convenience. Type: FrameLayout
Any child views are optional, but where defined they must be of the expected type.
Specifying a custom layout file
Defining your own exo_simple_player_view.xml is useful to customize the layout of SimpleExoPlayerView throughout your application. It’s also possible to customize the layout for a single instance in a layout file. This can be achieved by setting the player_layout_id attribute on a SimpleExoPlayerView. This make specified layout inflated instead of exo_simple_player_view.xml for only the instance on which the attribute is set.
For more customization option check official document on customizing ExoPlayer.
Changing Video Quality in ExoPlayer on Android
To change the video quality in ExoPlayer, developers can utilize TrackSelector and DefaultTrackSelector. They can create a DefaultTrackSelector, configure it with desired parameters like bitrate, and then pass it to the ExoPlayer instance during initialization.
Changing ExoPlayer Aspect Ratio
Developers can set the aspect ratio of the ExoPlayer by creating a custom AspectRatioFrameLayout and wrapping it around the PlayerView. They can then use the setAspectRatio method on the AspectRatioFrameLayout to change the aspect ratio.
Implementing ExoPlayer Cache
Caching can be implemented in ExoPlayer by using the CacheDataSourceFactory which wraps around another DataSource.Factory instance. A SimpleCache instance can be used to manage the cache, and the LeastRecentlyUsedCacheEvictor can be used to evict old data from the cache to ensure it doesn’t grow too large.
ExoPlayer Play Audio from URL
To play audio from a URL, developers can initialize a DefaultDataSourceFactory and a ProgressiveMediaSource (or other appropriate MediaSource depending on the audio format), and prepare the ExoPlayer instance with the MediaSource. The uri of the audio file needs to be passed to the MediaSource to start streaming and playing the audio.
ExoPlayer Play Local File
Playing a local file can be achieved by creating a MediaItem or a RawResourceDataSource with the URI of the local file, and then preparing the ExoPlayer instance with a MediaSource created with that URI. Developers can use the res/raw folder to store and access local files, or use the assets directory if the file is stored there. They can use methods like RawResourceDataSource.buildRawResourceUri or MediaItem.fromUri to create a URI from the local file path
How To Play DRM Content On Exoplayer
So far we have gone through the advantages of using ExoPlayer and how to customize it to suit our needs, in this section, we will see how to use ExoPlayer to play DRM-protected content which is also mentioned as its advantage over the in-built MediaPlayer API.
Before we start lets understand what is Digital rights management (DRM). Digital rights management (DRM) is way to protect copyrights for digital media. It has been developed to protect all kinds of digital materials prepared for computers and other technological devices, including movies, tv series, games, music and software. Putting these restrictions on DRM-protected content is creating security issues that prevent copying and distribution over the internet.
ExoPlayer uses Android’s Media Drm API to support DRM protected playbacks.
The minimum Android versions required for different supported DRM schemes, along with the streaming formats for which they’re supported are the following. In addition to the below table, playback is limited on older devices due to security updates. Android version 7 and above are more reliable to widevine playback.
While building a media source for ExoPlayer, you should specify the UUID of the DRM system and the license server URI. Using these properties we will build an instance of DefaultDrmSessionManager needed for handling DRM related key and provisioning request to enable media playback.
Frist we need to create a instance of DrmSessionManager
private DefaultDrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String userAgent) {
HttpDataSource.Factory licenseDataSourceFactory = new DefaultHttpDataSource.Factory().setUserAgent(userAgent);
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, true,
licenseDataSourceFactory);
return new DefaultDrmSessionManager.Builder()
.setUuidAndExoMediaDrmProvider(uuid, FrameworkMediaDrm.DEFAULT_PROVIDER)
.build(drmCallback);
}
Now we have to build a media source with license url
DRM License Url : https://proxy.uat.widevine.com/proxy?provider=widevine_test
private DashMediaSource buildDashMediaSource(Uri uri) {
String drmLicenseUrl = "https://proxy.uat.widevine.com/proxy?provider=widevine_test";
String userAgent = Util.getUserAgent(context, context.getApplicationContext().getPackageName());
UUID drmSchemeUuid = Util.getDrmUuid(C.WIDEVINE_UUID.toString());
DrmSessionManager drmSessionManager = buildDrmSessionManager(drmSchemeUuid, drmLicenseUrl, userAgent);
DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(context, new DefaultHttpDataSource.Factory().setUserAgent(userAgent));
return new DashMediaSource.Factory(dataSourceFactory)
.setDrmSessionManagerProvider(unusedMediaItem -> drmSessionManager)
.createMediaSource(
new MediaItem.Builder()
.setUri(uri)
.setMimeType(MimeTypes.APPLICATION_MPD)
.build()
);
}
Now add url and its ready to be played.
DRM Url: https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd
private void initializePlayer() {
String url = "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd";
MediaSource mediaSource = buildDashMediaSource(Uri.parse(url));
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
TrackSelector trackSelector = new DefaultTrackSelector(context, videoTrackSelectionFactory);
trackSelector.setParameters(trackSelector.getParameters().buildUpon()
.setPreferredTextLanguage("en")
.build());
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context)
.forceEnableMediaCodecAsynchronousQueueing()
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
DefaultLoadControl loadControl = new DefaultLoadControl.Builder()
.setBufferDurationsMs(DefaultLoadControl.DEFAULT_MIN_BUFFER_MS,
maxBufferMs,
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS)
.build();
ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory)
.setTrackSelector(trackSelector)
.setLoadControl(loadControl)
.build();
exoPlayer.setMediaSource(mediaSource);
exoPlayer.prepare();
}
In next section we will see how we at VdoCipher use ExoPlayer to stream DRM protected videos.
Adaptive Bitrate Streaming in Exoplayer
Exoplayer can also be used for adaptive bitrate streaming to set video quality automatically based on available network bandwidth. Adaptive bitrate streaming (also known as adaptive streaming) is a technology designed to deliver video in the most efficient way possible and in the highest usable quality for each specific user and device.
For slow connections, the video will be played in low quality, and for fast connections, the video will be played in the best quality with less buffer time. These qualities(bit rates and resolutions) are known as tracks. The same media content is split into multiple tracks, each for a given quality based on bit rate and resolution. Each track is split into chunks of a given duration, typically between 2 and 10 seconds. This makes it easier to switch between tracks with changing network speeds and signals.
Implementing Adaptive Track Selection
To implement Adaptive streaming, add TrackSelector
while initializing the player. The TrackSelector is used to switch between multiple tracks.
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
TrackSelector trackSelector = new DefaultTrackSelector(context, videoTrackSelectionFactory);
trackSelector.setParameters(trackSelector.getParameters().buildUpon()
.setMaxVideoSizeSd()
.setPreferredTextLanguage("en")
.build());
ExoPlayer exoPlayer = new ExoPlayer.Builder(context)
.setTrackSelector(trackSelector)
.build();
Create an adaptive track selection factory with default parameters and pass it to DefaultTrackSelector
which is responsible for choosing tracks in the media item.
Then pass the trackSelector
to ExoPlayer builder.
Build an Adaptive Media Source
DASH, HLS, and SmoothStreaming are all media formats ExoPlayer supports that are capable of adaptive streaming, but we’ll focus on DASH for now and use the DashMediaSource
. To stream DASH content, you need to create a MediaItem.
Uri manifestUri = Uri.parse(dashUrl);
DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(context, new DefaultHttpDataSource.Factory().setUserAgent(userAgent));
mediaSource = new DashMediaSource.Factory(dataSourceFactory)
.createMediaSource(
new MediaItem.Builder()
.setUri(manifestUri)
.setMimeType(MimeTypes.APPLICATION_MPD)
.build()
);
How VdoCipher Streams Video on Android Using ExoPlayer
The components of our video streaming can be broken down into four main parts:
- Client attempting to play content
- VdoCipher license server that generates decryption keys based on client requests
- Provisioning server if unique credentials are required for devices
- Content server that serves encrypted content
At client side we try to play protected content from the content server via a DashMediaSource with a provided DrmSessionManager, this DRMSessionManager contains the implementation of MediaDRMCallback wrapping a HttpMediaDrmCallback that extends its functionality by wrapping/unwrapping license request/response and throwing custom exceptions to help identify the cause. In the meantime, if the device needs provisioning, a request to the provisioning server is done via callback. After the MediaDRM client receives the license, and it is passed to ExoPlayer via the media source and media playback will begin. This procedure is repeated with every media playback request for non-persistent licenses. Our application saves persistent licenses and reuses them until they expire. In addition, persistent license requests are fetched before the secure video playback starts with OfflineLicenseHelper, allowing for video initialization to happen regardless of whether or not the license fetch operation succeeded. Now lets see how these classes are utilized.
How Exoplayer can play video from URL in Android video player?
After creating the instance of Exoplayer you can pass the video_url as, MediaItem mediaItem = MediaItem.fromUri(video_url);
You can also stop playback on cloned apps, emulators and rooted devices in Android with our Play Integrity integration in Android SDK. Check out our play integrity api documentation to know more. Learn more about android video SDK to stream your videos on Android with VdoCipher.
Stream Your Videos Securely on Android With VdoCipher
VdoCipher helps ver 2000+ customers over 40+ countries to host their videos securely, helping them to boost their video revenues. We provide end-to-end solutions for video, right from hosting, encoding, and encryption to the player. On top of it, you get Apis to manage videos, players, and more.
Mansi and Nafis both are Android Developers at VdoCipher. This article was jointly written by both of them