Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix exo player slow loading of video

I am trying to write an app where i'll have a couple of video URLs which i need to play. I am using ExoPlayer for playing videos.

ExoPlayer is playing the videos but the problem i am facing is, ExoPlayer is loading some part of video (let's say 5 seconds) and then playing them and once exo player is done playing that part, it is trying to load next part of video and then playing that part of video. This problem is occurring even when the internet speed is good.

I tried reading ExoPlayer docs and other stackoverflow posts but i am not able to figure out what's wrong here.

Below is my code for exo player's singleton class:

/**
 * Singleton class that manages all video playback in the app. Ensures that only a single stream can be heard at a time
 * and reports the current playback status to any interested listeners.
 */
public class VideoWrapper {
    private static String TAG = "VideoWrapper";

    private int VideoPlaybackState = VideoPlaybackListener.STATE_VIDEO_STOPPED;

    // Player that plays the videos.
    private SimpleExoPlayer exoPlayer;

    // Singleton instance of the class
    private static VideoWrapper videoWrapper;

    // URL of Current Video Item that is getting played.
    private String mCurrentStreamingUrl;

    // Handler to take request of video playback in-case there is a playlist is being sent for play.
    private Handler handler;

    // Add the listener and notify them if required. -- For demo i am not using it but for UI updates and all it will be useful.
    private List<VideoPlaybackListener> listeners = new LinkedList<>();

    // Tells to which screen we have to go back
    private String mPreviousScreenName;

    private Context context;

    public static VideoWrapper getInstance() {
        if (videoWrapper == null) {
            videoWrapper = new VideoWrapper();
        }

        return videoWrapper;
    }

    // Default constrructor!
    private VideoWrapper() {
        // Nothing to do.
    }

    public void setCurrentPreviousScreen(String screen) {
        mPreviousScreenName = screen;
    }

    public void init(Context context) {
        try {
            this.context = context;
            handler = new Handler(context.getMainLooper());
        } catch (Exception e) {
            // TODO
        }
    }

    public void stopAndReleasePlayer() {

        // Stop the player first.
        stopVideoStreaming();

        // Now release the player and make it null.
        if (exoPlayer != null) {
            exoPlayer.release();
            exoPlayer = null;
        }
    }

    public void addListener(VideoPlaybackListener listener) {
        listeners.add(listener);
    }

    public void removeListener(VideoPlaybackListener listener) {
        listeners.remove(listener);
    }

    public int getVideoPlaybackState() {
        return VideoPlaybackState;
    }

    private SimpleExoPlayer.VideoListener gVideoEventListener = new SimpleExoPlayer.VideoListener() {
        @Override
        public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
            // TODO: Do Something!
        }

        @Override
        public void onRenderedFirstFrame() {
            // TODO: Do Something!
        }
    };

    private ExoPlayer.EventListener eventListener = new ExoPlayer.EventListener() {
        @Override
        public void onTimelineChanged(Timeline timeline, Object manifest) {
            // TODO: Do Something!
        }

        @Override
        public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
            // TODO: Do Something!
        }

        @Override
        public void onLoadingChanged(boolean isLoading) {
            // TODO: Do Something!
        }

        @Override
        public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
            switch (playbackState) {
                // playback state handling serially
                case ExoPlayer.STATE_IDLE:
                    // TODO: Do something!
                    break;

                case ExoPlayer.STATE_BUFFERING:
                    // TODO: Do something!
                    break;

                case ExoPlayer.STATE_READY:
                    // TODO: Do something!
                    break;

                case ExoPlayer.STATE_ENDED:
                    // TODO: Do something!
                    break;
            }
        }

        @Override
        public void onPlayerError(ExoPlaybackException error) {
            int errorType = error.type;
            switch (errorType) {
                case ExoPlaybackException.TYPE_RENDERER:
                    // TODO: Do something!
                    break;
                case ExoPlaybackException.TYPE_SOURCE:
                    // TODO: Do something!
                    break;
                case ExoPlaybackException.TYPE_UNEXPECTED:
                    // TODO: Do something!
                    break;
                default:
                    // TODO: Do something!
                    break;
            }
        }

        @Override
        public void onPositionDiscontinuity() {
            // TODO: Do something!
        }
    };

    private boolean canPlayCurrentItem() {
        return mCurrentStreamingUrl != null && !TextUtils.isEmpty(mCurrentStreamingUrl);
    }

    public void stopVideoStreaming() {
        if (exoPlayer != null) {
            exoPlayer.setPlayWhenReady(false);
        }
    }

    private void restartVideoStreaming() {
        if (canPlayCurrentItem()) {
            streamCurrentTrack();
        }
    }

    public void startVideoStreaming(String streamUrl) {
        // Stop any video if it is playing.
        stopVideoStreaming();
        VideoPlaybackState = VideoPlaybackListener.STATE_VIDEO_STOPPED;
        mCurrentStreamingUrl = streamUrl;

        openStreamUrl();
    }

    private void openStreamUrl() {
        Uri uri = Uri.parse(mCurrentStreamingUrl);

        TrackSelector trackSelector = new DefaultTrackSelector();

        LoadControl loadControl = new DefaultLoadControl();

        DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        DataSource.Factory dataSourceFactory = buildDataSourceFactory(bandwidthMeter);

        // This is the MediaSource representing the media to be played.
        HlsMediaSource mediaSource = new HlsMediaSource(uri, dataSourceFactory, handler, new AdaptiveMediaSourceEventListener() {
            @Override
            public void onLoadStarted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs) {
                // TODO: Do something!
            }

            @Override
            public void onLoadCompleted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
                // TODO: Do something!
            }

            @Override
            public void onLoadCanceled(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
                // TODO: Do something!
            }

            @Override
            public void onLoadError(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded, IOException error, boolean wasCanceled) {
                // TODO: Do something!
            }

            @Override
            public void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs) {
                // TODO: Do something!
            }

            @Override
            public void onDownstreamFormatChanged(int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaTimeMs) {
                // TODO: Do something!
            }
        });

        exoPlayer.prepare(mediaSource);

        streamCurrentTrack();
    }

    private DataSource.Factory buildDataSourceFactory(DefaultBandwidthMeter bandwidthMeter){
        return new DefaultDataSourceFactory(context, bandwidthMeter, buildHttpDataSourceFactory(bandwidthMeter));
    }

    private HttpDataSource.Factory buildHttpDataSourceFactory(DefaultBandwidthMeter bandwidthMeter){
        return new DefaultHttpDataSourceFactory("TiVoPhoenix", bandwidthMeter);
    }

    public void togglePlayPause() {
        if (canPlayCurrentItem()) {
            if (VideoPlaybackState == PlaybackListener.STATE_PLAYING) {
                stopVideoStreaming();
                VideoPlaybackState = PlaybackListener.STATE_PAUSED;
            } else {
                restartVideoStreaming();
            }
        }
    }

    private void streamCurrentTrack() {
        VideoPlaybackState = VideoPlaybackListener.STATE_VIDEO_PLAYING;
        exoPlayer.setPlayWhenReady(true);
    }

    public SimpleExoPlayer getPlayer() {
        TrackSelector trackSelector = new DefaultTrackSelector();
        LoadControl loadControl = new DefaultLoadControl();

        // Stop and release the video player before re-creating it.
        stopAndReleasePlayer();

        exoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector, loadControl);
        exoPlayer.addListener(eventListener);
        exoPlayer.setVideoListener(gVideoEventListener);

        return exoPlayer;
    }
}  

Can someone help me with this?

like image 222
Alien Geography Avatar asked Apr 20 '19 13:04

Alien Geography


1 Answers

Try this settings for your load control:

public class VideoPlayerConfig {
    //Minimum Video you want to buffer while Playing
    public static final int MIN_BUFFER_DURATION = 2000;
    //Max Video you want to buffer during PlayBack
    public static final int MAX_BUFFER_DURATION = 5000;
   //Min Video you want to buffer before start Playing it
    public static final int MIN_PLAYBACK_START_BUFFER = 1500;
    //Min video You want to buffer when user resumes video
    public static final int MIN_PLAYBACK_RESUME_BUFFER = 2000;
}

LoadControl loadControl = new DefaultLoadControl.Builder()
            .setAllocator(new DefaultAllocator(true, 16))
            .setBufferDurationsMs(VideoPlayerConfig.MIN_BUFFER_DURATION,
                    VideoPlayerConfig.MAX_BUFFER_DURATION,
                    VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER,
                    VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER)
            .setTargetBufferBytes(-1)
            .setPrioritizeTimeOverSizeThresholds(true).createDefaultLoadControl();

The idea is to keep the minimum buffer low so the video loads faster.

Off-topic: And I don't know why are you using singleton pattern, probably to get control everywhere in the app. As you are using singleton pattern you can go with a weak-reference of application context.

like image 169
Sp4Rx Avatar answered Nov 03 '22 02:11

Sp4Rx