Praween Mishra

ExoPlayer events and UI customizations

Hello folks, I am back again with the 2nd part of ExoPlayer blog. In my previous blog, I explained about the components of ExoPlayer and how to configure it in our application.
In this blog, we will learn how to detect different events of ExoPlayer and how to customize its UI components. The feature to customize the ExoPlayer UI components is available from V2.1.0 onwards.

events and UI customizations

So let’s start with ExoPlayer events first:

Events

  • ExoPlayer.EventListener: This event listener gets called when there are any changes in player state. For example, this listener will get called if we change the seek position of the player or if we change the current playing track. There are several callbacks related to this event listener:
    • onTimelineChange(): called when the duration of the source has been determined.
    • onTrackChange():  called when the source track has changed.
    • onPlayerError(): called when some error occurs in playing the source file.
    • onPlayerStateChange(): called when the player state changes from one to another. The possible player states are idle, ready, buffering, paused, playing, stopped, completed, error etc.
    • onLoadingChange(): called when the source starts or stops loading.
    • onPositionDiscontinuty(): called when the source seeks to some position.
    • onPlaybackParameterChange(): called when the current source parameter gets changed.

So if you want to perform any operation or any customization during any of these events, then you can implement this listener. To start getting callbacks for any of the events mentioned above, you can set the ExoPlayer.EventListener to your player object using the addListener() method of the SimpleExoPlayer class.

Let’s look at an example of the class that will implement the ExoPlayer.EventListener:

public class PlayerEventListener implements ExoPlayer.EventListener {
    
    @Override
    public void onTimelineChanged(Timeline timeline,Object manifest) {}
    
    @Override
    public void onTracksChanged(TrackGroupArray trackGroups,
                                TrackSelectionArray trackSelections) {}
    @Override
    public void onLoadingChanged(boolean isLoading) {}
    
    @Override
    public void onPlayerStateChanged(boolean playWhenReady,int playbackState) {}

    @Override
    public void onPlayerError(ExoPlaybackException error) {}

    @Override
    public void onPositionDiscontinuity() {}

    @Override
    public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {}
}

 

Now let’s see how we can add the ExoPlayer.EventListener to the player:

public class VideoPlayerActivity extends AppCompatActivity {
    ....
    private PlayerEventListener mPlayerEventListener;
    private SimpleExoPlayerView mAudioSEPV;
    private SimpleExoPlayer mSimpleExoPlayer;    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
	...        
	mPlayerEventListener = new PlayerEventListener();
	initializePlayer();
    }
    private void initializePlayer() {
        mSimpleExoPlayer = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(this),
new DefaultTrackSelector(), new DefaultLoadControl());
        mAudioSEPV.setPlayer(mSimpleExoPlayer);
        mSimpleExoPlayer.addListener(mPlayerEventListener);
	....
    }
}

 

  • AudioRendererEventListener: This listener can be used to listen to events related to audio rendering. For example, this listener will get called if the audio format gets changed or if the audio decoder gets initialized. Below are the available callback methods of the AudioRendererEventListener:
    • onAudioSessionId(): called when the first audio frame gets rendered and a random integer id is assigned to the audio stream.
    • onAudioDecoderInitialized(): called when the audio decoder gets initialized and the audio is ready to play.
    • onAudioTrackUnderrun(): called when the audio track gets cut down because of any reason by the ExoPlayer.
    • onAudioInputFormatChanged(): called when the audio format gets changed from one to another.
    • onAudioEnabled(): called when the audio track is enabled by the track selector.
    • onAudioDisabled(): called when the audio track is disabled by the track selector.

To start getting callback events for any of the above-mentioned methods, you can implement the AudioRendererEventListener. You can set this listener to the ExoPlayer object by using the setAudioDebugListener() method of the SimpleExoPlayer class.

Let’s look at an example of the class that will implement the AudioRendererEventListener:

public class AudioEventListener implements AudioRendererEventListener {
    @Override
    public void onAudioEnabled(DecoderCounters counters) {}

    @Override
    public void onAudioSessionId(int audioSessionId) {}

    @Override
    public void onAudioDecoderInitialized(String decoderName,long initializedTimestampMs,
                                          long initializationDurationMs) {}

    @Override
    public void onAudioInputFormatChanged(Format format) {}

    @Override
    public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs,
                                     long elapsedSinceLastFeedMs) {}

    @Override
    public void onAudioDisabled(DecoderCounters counters) {}
}

 

Now let’s see how we can set this listener to our player object:

public class VideoPlayerActivity extends AppCompatActivity {
    ....
    private AudioEventListener mAudioEventListener;
    private SimpleExoPlayerView mAudioSEPV;
    private SimpleExoPlayer mSimpleExoPlayer;    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
	...        
        mAudioEventListener = new AudioEventListener();
	initializePlayer();
    }
    private void initializePlayer() {
        mSimpleExoPlayer = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(this),
                new DefaultTrackSelector(), new DefaultLoadControl());
        mAudioSEPV.setPlayer(mSimpleExoPlayer);
        mSimpleExoPlayer.setAudioDebugListener(mAudioEventListener);
	....
    }
}

 

  • VideoRendererEventListener: This listener can be used to listen to events related to video rendering. For example, this listener gets called when the first video frame is rendered on the screen or if a video frame gets dropped because of any reason. Below is the list of available callbacks of VideoRendererEventListener:
    • onDroppedFrame(): called when any video frame gets dropped by a player because of any reason. It will also give you the count and duration in milliseconds for the dropped frame.
    • onVideoSizeChanged(): called when the size (resolution) of the video gets changed.
    • onVideoInputFormatChnaged(): called when the source file format changes from one format to another.
    • onVideoEnabled: called when the video track is enabled using a track selector.
    • onRenderedFirstFrame(): called when the first frame of the video track gets rendered.
    • onVideoDecoderInitialized(): called when the video decoder gets initialized and the video is ready to play.
    • onVideoDisabled(): called when video track is disabled using a track selector.

To start getting callbacks for the above-mentioned events, you can implement the VideoRendererEventListener. You can set this listener to the ExoPlayer object using the setVideoDebugListener() method of the SimpleExoPlayer class.

Let’s look at an example of the class that implements the VideoRendererEventListener:

public class VideoEventListener implements VideoRendererEventListener {

    @Override
    public void onVideoEnabled(DecoderCounters counters) {}

    @Override
    public void onVideoDecoderInitialized(String decoderName,long initializedTimestampMs,
                                          long initializationDurationMs) {}

    @Override
    public void onVideoInputFormatChanged(Format format) {}

    @Override
    public void onDroppedFrames(int count, long elapsedMs) {}

    @Override
    public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
                                   float pixelWidthHeightRatio) {}

    @Override
    public void onRenderedFirstFrame(Surface surface) {}

    @Override
    public void onVideoDisabled(DecoderCounters counters) {}
}

 

Now let’s see how we can set this listener to our player object:

public class VideoPlayerActivity extends AppCompatActivity {
    ....
    private VideoEventListener mVideoEventListener;
    private SimpleExoPlayerView mAudioSEPV;
    private SimpleExoPlayer mSimpleExoPlayer;    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
	...        
	mVideoEventListener = new VideoEventListener();
	initializePlayer();
    }
    private void initializePlayer() {
        mSimpleExoPlayer = ExoPlayerFactory.newSimpleInstance(   
                new DefaultRenderersFactory(this),
                new DefaultTrackSelector(), new DefaultLoadControl());
        mAudioSEPV.setPlayer(mSimpleExoPlayer);
        mSimpleExoPlayer.setVideoDebugListener(mVideoEventListener);
	....
    }
}

 

Use of these events is optional. You are free to set the listeners if you need the callbacks. For example, if you want to perform any action when the video size gets changed then you can set the VideoRendererEventListener on SimpleExoPlayer via setVideoDebugListener() or if you want to perform any action when the audio decoder is initialized, you can set the AudioRendererEventListener on SimpleExoPlayer via setAudioDebugListener().

Now, let’s look at the ExoPlayer UI components.

UI Components

  • PlaybackControlView: It is a view for controlling and managing different actions on the ExoPlayer instance. PlaybackControlView displays standard controls like play/pause, seekbar (for displaying the total duration and current duration), fast-forward and rewind.
  • SimpleExoPlayerView: It is a high-level view for ExoPlayer to display video, subtitle, and other standard player controllers.

Use of these UI Components is also optional. Developers are free to customize the complete view by implementing their UI components from scratch as well if they want. ExoPlayer has provided the privilege for full customization of UI components. These UI components can be customized in three different ways.

  • By setting attributes (or calling corresponding methods) on the views.
  • By globally overriding the view layout files.
  • By specifying a custom layout file for a single view instance.

Full documentation of these three different ways to customize the attributes is available on the official website of ExoPlayer JavaDoc of PlaybackControllView & SimpleExoPlayerView.

Let’s have a look at how we can customize certain attributes of the ExoPlayer:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    ...>

    <com.google.android.exoplayer2.ui.SimpleExoPlayerView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:use_controller="false"
        app:show_timeout="10000"
        app:fastforward_increment="30000"
        app:rewind_increment="30000"
        app:controller_layout_id="@layout/exo_playback_control_view"/>

</FrameLayout>

 

As shown above, 5 attributes are used to customize the ExoPlayer UI.

  1. use_controller: If you want to display playback controller, set it to true else false.
  2. show_timeout: Input can be any long value which accepts the value in milliseconds. This is the controller view timeout.
  3. fastforward_increment: Input can be any long value which accepts the value in milliseconds. This is used to skip the video playback position forward with the input value. The default value is 15 sec.
  4. rewind_increment: Input can be any long value which accepts the value in milliseconds. This is used for moving playback position backward with the input value. The default value is 5 sec.
  5. controller_layout_id: If a developer wants to make the design of controller by themselves then they can design any layout XML file and can pass it to controller_layout_id.

That’s all for this blog. This blog was all about the different types of events and how to customize some basic UI components of ExoPlayer.

Hope you enjoyed reading this blog. In case you have any queries, I would be happy to answer them. Drop them in the comments section below.


References

https://google.github.io/ExoPlayer/

https://codelabs.developers.google.com/codelabs/exoplayer-intro/#0