Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Quality selector for ExoPlayer 2

I am currently developing a live and movie player application. I chose ExoPlayer version 2 to play the movie and I do not know much about it. I want to let the user choose the quality of a movie on the player screen, for example, 720p or 1080p or etc. But I do not know how to get a list of existing qualities and show them to the user. and the below code is my implementation of SimpleExoPlayer :

private void initPlayer(String path){
    Handler handler = new Handler();
    // 1. Create a default TrackSelector
    BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    TrackSelection.Factory videoTrackSelectionFactory =
            new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
    TrackSelector trackSelector =
            new DefaultTrackSelector(videoTrackSelectionFactory);

    // 2. Create a default LoadControl
    LoadControl loadControl = new DefaultLoadControl();
    // 3. Create the player
    player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);

    SimpleExoPlayerView playerView = (SimpleExoPlayerView) findViewById(R.id.player_view);
    playerView.setPlayer(player);
    playerView.setKeepScreenOn(true);
    // Produces DataSource instances through which media data is loaded.
    DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "ExoPlayer"));

    // This is the MediaSource representing the media to be played.
    MediaSource videoSource = new HlsMediaSource(Uri.parse(path),
            dataSourceFactory,handler, null);
    // Prepare the player with the source.
    player.addListener(this);
    player.prepare(videoSource);
    playerView.requestFocus();
    player.setPlayWhenReady(true); // to play video when ready. Use false to pause a video
}

// ExoPlayer Listener Methods :
@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) {
    switch (playbackState) {
        case ExoPlayer.STATE_BUFFERING:
            //You can use progress dialog to show user that video is preparing or buffering so please wait
            progressBar.setVisibility(View.VISIBLE);
            break;
        case ExoPlayer.STATE_IDLE:
            //idle state
            break;
        case ExoPlayer.STATE_READY:
            // dismiss your dialog here because our video is ready to play now
            progressBar.setVisibility(GONE);
            //Toast.makeText(getApplicationContext(),String.valueOf(player.getCurrentTrackSelections().get(0).getSelectedFormat().bitrate),Toast.LENGTH_SHORT).show();
            break;
        case ExoPlayer.STATE_ENDED:
            // do your processing after ending of video
            break;
    }
}

@Override
public void onPlayerError(ExoPlaybackException error) {
    // show user that something went wrong. it can be a dialog
}

@Override
public void onPositionDiscontinuity() {

}

please help to solve this issue. thanks a lot.

like image 666
Ali Soleimani Avatar asked Aug 23 '17 09:08

Ali Soleimani


People also ask

How do I change video quality on ExoPlayer?

ExoPlayer provides a way of setting a max bitrate using DefaultTrackSelector#Parameters . You can use setMaxVideoBitrate or setMaxVideoSize to limit the max bitrate. In this way, it works in many cases.

What is track selector ExoPlayer?

Direct Known Subclasses: MappingTrackSelector public abstract class TrackSelector extends Object. The component of an ExoPlayer responsible for selecting tracks to be consumed by each of the player's Renderer s. The DefaultTrackSelector implementation should be suitable for most use cases.

Does ExoPlayer use MediaPlayer?

ExoPlayer is an app-level media player built on top of low-level media APIs in Android. It is an open source project used by Google apps, including YouTube and Google TV.

What is ExoPlayer v2?

ExoPlayer is an application level media player for Android. It provides an alternative to Android's MediaPlayer API for playing audio and video both locally and over the Internet. ExoPlayer supports features not currently supported by Android's MediaPlayer API, including DASH and SmoothStreaming adaptive playbacks.


2 Answers

Everything you'd like to achieve is viewable in the ExoPlayer2 demo app. More specifically the PlayerActivity class.

You can also check out this good article on the topic.

The core points you'll want to look into are around track selection (via the TrackSelector) as well as the TrackSelectionHelper. I'll include the important code samples below which will hopefully be enough to get you going. But ultimately just following something similar in the demo app will get you where you need to be.

You'll hold onto the track selector you init the player with and use that for just about everything.

Below is just a block of code to ideally cover the gist of what you're trying to do since the demo does appear to over-complicate things a hair. Also I haven't run the code, but it's close enough.

// These two could be fields OR passed around
int videoRendererIndex;
TrackGroupArray trackGroups;

// This is the body of the logic for see if there are even video tracks
// It also does some field setting
MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
for (int i = 0; i < mappedTrackInfo.length; i++) {
  TrackGroupArray trackGroups = mappedTrackInfo.getTrackGroups(i);
  if (trackGroups.length != 0) {
    switch (player.getRendererType(i)) {
      case C.TRACK_TYPE_VIDEO:
        videoRendererIndex = i;
        return true;
    }
  }
}

// This next part is actually about getting the list. It doesn't include
// some additional logic they put in for adaptive tracks (DASH/HLS/SS),
// but you can look at the sample for that (TrackSelectionHelper#buildView())
// Below you'd be building up items in a list. This just does
// views directly, but you could just have a list of track names (with indexes)
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
  TrackGroup group = trackGroups.get(groupIndex);
  for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
    if (trackIndex == 0) {
      // Beginning of a new set, the demo app adds a divider
    }
    CheckedTextView trackView = ...; // The TextView to show in the list
    // The below points to a util which extracts the quality from the TrackGroup
    trackView.setText(DemoUtil.buildTrackName(group.getFormat(trackIndex)));
}

// Assuming you tagged the view with the groupIndex and trackIndex, you
// can build your override with that info.
Pair<Integer, Integer> tag = (Pair<Integer, Integer>) view.getTag();
int groupIndex = tag.first;
int trackIndex = tag.second;
// This is the override you'd use for something that isn't adaptive.
override = new SelectionOverride(FIXED_FACTORY, groupIndex, trackIndex);
// Otherwise they call their helper for adaptives, which roughly does:
int[] tracks = getTracksAdding(override, trackIndex);
TrackSelection.Factory factory = tracks.length == 1 ? FIXED_FACTORY : adaptiveTrackSelectionFactory;
override = new SelectionOverride(factory, groupIndex, tracks);

// Then we actually set our override on the selector to switch the quality/track
selector.setSelectionOverride(rendererIndex, trackGroups, override);

As I mentioned above, this is a slight oversimplification of the process, but the core part is that you're messing around with the TrackSelector, SelectionOverride, and Track/TrackGroups to get this to work.

You could conceivably copy the demo code verbatim and it should work, but I'd highly recommend taking the time to understand what each piece is doing and tailor your solution to your use case.

If I had more time I'd get it to compile and run. But if you can get my sample going then feel free to edit my post.

Hope that helps :)

like image 153
Kyle Venn Avatar answered Oct 25 '22 09:10

Kyle Venn


I avoid the way as above posted. My way is using the DefaultTrackSelector as follows:

trackSelector.setParameters(trackSelector.getParameters()
                          .withMaxVideoBitrate(bitrate)
                          .withMaxVideoSize(width, height));

I've tested with HLS videos and it seems to perform in the right way. I get the bitrate, width and height reading from the HlsManifest.

like image 28
Billyjoker Avatar answered Oct 25 '22 10:10

Billyjoker