Tag

exoplayer

Browsing

We demonstrate Video Streaming from the server using the ExoPlayer. ExoPlayer is used in Youtube app as well. Read the full article for details

ExoPlayer is an open-source project. This is not a part of Android SDK. In Android, Exo player is application level media player. ExoPlayer’s standard audio and video components are built on Android’s MediaCodec API, which was released in Android.

ExoPlayer is easy to use, maintainable and fully customize.

Lets Demonstrate Exo player

1. Go to file menu create a new project.

2. After that add below dependency in build.gradle.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:design:28.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    // add exo player dependency here
    implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
    implementation 'org.jsoup:jsoup:1.10.3'

    // code generator for view
    implementation "com.jakewharton:butterknife:8.8.1"
    annotationProcessor "com.jakewharton:butterknife-compiler:8.8.1"
}

In the gradle.xml file, We have 2 major dependencies. One for ExoPlayer and second as know about ButterKnife already. Basically, butterknife is injected of view in the java file.

3. Create a layout activity_main.xml and add two Button like below code.

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_design"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/buttonPlayUrlVideo"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:background="@color/colorAccent"
        android:text="@string/title_play_url"
        android:textColor="@color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/buttonPlayDefaultVideo" />

    <Button
        android:id="@+id/buttonPlayDefaultVideo"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:background="@color/colorAccent"
        android:text="@string/title_play_default_url"
        android:textColor="@color/white"
        app:layout_constraintBottom_toTopOf="@+id/buttonPlayUrlVideo"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed" />
</android.support.constraint.ConstraintLayout>

As per design, we have created two buttons one for play default video. The second user can put own URL in dialog prompt.

4.1 Now connect this layout with MainActivity.java

In the main activity, we generate view inject code by using ButterKnief injection and set on click listeners.

    @BindView(R.id.buttonPlayUrlVideo)
    Button buttonPlayUrlVideo;
    @BindView(R.id.buttonPlayDefaultVideo)
    Button buttonPlayDefaultVideo;


    @OnClick({R.id.buttonPlayUrlVideo, R.id.buttonPlayDefaultVideo})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.buttonPlayUrlVideo:
        
                break;
            case R.id.buttonPlayDefaultVideo:
           
                startActivity(mIntent);
                break;
        }
    }
4.2. Let’s bind butter knife in onCreate Activity
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }

4.3. -As per above, We are using two buttons. So If you want to play a default video then we call intent with default URL of ExoPlayer activity.
like Below

      Intent mIntent = ExoPlayerActivity.getStartIntent(this, VideoPlayerConfig.DEFAULT_VIDEO_URL);
                startActivity(mIntent);

4.3. – Button for playing own video by entering video URL. So taking user url we have created alert dialog with edit text.

  LayoutInflater li = LayoutInflater.from(this);
        View promptsView = li.inflate(R.layout.dialog_prompts, null);
        AlertDialog.Builder mBuilder = new AlertDialog.Builder( this);
        // set dialog_prompts.xml to dialog
        mBuilder.setView(promptsView);
        final EditText userInputURL = (EditText) promptsView
                .findViewById(R.id.editTextDialogUrlInput);
        // set dialog message here
        mBuilder.setCancelable(false)
                .setPositiveButton("OK",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                boolean isURL = 
                                Patterns.WEB_URL.matcher(userInputURL.getText().toString().trim()).matches();
                                if (isURL) {
                                    Intent mIntent = ExoPlayerActivity.getStartIntent(MainActivity.this, 
                                    userInputURL.getText().toString().trim());
                                    startActivity(mIntent);
                                } else {
                                    Toast.makeText(MainActivity.this, 
                                    getString(R.string.error_message_url_not_valid), Toast.LENGTH_SHORT).show();
                                }
                            }
                        })
                .setNegativeButton("Cancel",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                dialog.cancel();
                            }
                        }).create().show();

5. Now MainActivity.java full code seems like below

package com.androidwave.exoplayer;

import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;


public class MainActivity extends AppCompatActivity {

    @BindView(R.id.buttonPlayUrlVideo)
    Button buttonPlayUrlVideo;
    @BindView(R.id.buttonPlayDefaultVideo)
    Button buttonPlayDefaultVideo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportActionBar().hide();
        ButterKnife.bind(this);
    }

    @OnClick({R.id.buttonPlayUrlVideo, R.id.buttonPlayDefaultVideo})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.buttonPlayUrlVideo:
                showDialogPrompt();
                break;
            case R.id.buttonPlayDefaultVideo:
                Intent mIntent = ExoPlayerActivity.getStartIntent(this, VideoPlayerConfig.DEFAULT_VIDEO_URL);
                startActivity(mIntent);
                break;
        }
    }

   private void showDialogPrompt() {
        // get dialog_prompts.xml view
        LayoutInflater li = LayoutInflater.from(this);
        View promptsView = li.inflate(R.layout.dialog_prompts, null);
        AlertDialog.Builder mBuilder = new AlertDialog.Builder( this);
        // set dialog_prompts.xml to dialog
        mBuilder.setView(promptsView);
        final EditText userInputURL = (EditText) promptsView
                .findViewById(R.id.editTextDialogUrlInput);
        // set dialog message here
        mBuilder.setCancelable(false)
                .setPositiveButton("OK",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                boolean isURL = 
                               Patterns.WEB_URL.matcher(userInputURL.getText().toString().trim()).matches();
                                if (isURL) {
                                    Intent mIntent = ExoPlayerActivity.getStartIntent(MainActivity.this, 
                                    userInputURL.getText().toString().trim());
                                    startActivity(mIntent);
                                } else {
                                    Toast.makeText(MainActivity.this, 
                                    getString(R.string.error_message_url_not_valid), Toast.LENGTH_SHORT).show();
                                }
                            }
                        })
                .setNegativeButton("Cancel",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                dialog.cancel();
                            }
                        }).create().show();
    }
}

6. Configure ExoPlayer

Before going to Exo Player activity we define followings configuration of ExoPlayer.
MIN_BUFFER_DURATION – Minimum Video you want to buffer while Playing. for now, I have set 3 second
MAX_BUFFER_DURATION – You can set Max Video you want to buffer during PlayBack.
MIN_PLAYBACK_START_BUFFER – Set Min Video you want to buffer before start Playing it
MIN_PLAYBACK_RESUME_BUFFER – Set Min video You want to buffer when the user resumes video
DEFAULT_VIDEO_URL- Set Default video url for demo

package com.androidwave.exoplayer;

public class VideoPlayerConfig {
 
    public static final int MIN_BUFFER_DURATION = 3000;
    public static final int MAX_BUFFER_DURATION = 5000;
    public static final int MIN_PLAYBACK_START_BUFFER = 1500;
    public static final int MIN_PLAYBACK_RESUME_BUFFER = 5000;

    public static final String DEFAULT_VIDEO_URL = "https://androidwave.com/wp-content/uploads/2018/09/exo-player-androidwave-com-match-highlight.mp4";
}

7. Desgin an activity_exo_player.xml View with few component e.g. Progressbar(buffering indicator), ExoPlayer View

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ExoPlayerActivity">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/videoFullScreenPlayer"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#A6000000"
        app:controller_layout_id="@layout/exo_playback_control_view"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0"
        app:player_layout_id="@layout/exo_simple_player_view"
        app:repeat_toggle_modes="none"
        app:show_timeout="45000"
        app:surface_type="texture_view" />

    <ProgressBar
        android:id="@+id/spinnerVideoDetails"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:indeterminate="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/imageViewExit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:padding="@dimen/default_item_padding"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/ic_cross_circular_button_outline"
        android:layout_marginLeft="16dp" />
</android.support.constraint.ConstraintLayout>

8.Create ExoPlayerActivity.Java

Go to create an Activity and set content layout is activity_exo_player. Furthermore generates ButteKnief Injection by select set ContentView and right click and click generate. Now one popup appeared select view and set ids and click ok.


    @BindView(R.id.videoFullScreenPlayer)
    PlayerView videoFullScreenPlayer;
    @BindView(R.id.spinnerVideoDetails)
    ProgressBar spinnerVideoDetails;
    @BindView(R.id.imageViewExit)
    ImageView imageViewExit;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_exo_player);
        ButterKnife.bind(this);

   }
8.1. Define some variable and do some fullscreen configration
    String videoUri;
    SimpleExoPlayer player;
    Handler mHandler;
    Runnable mRunnable;

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
        WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_exo_player);
        ButterKnife.bind(this);
        getSupportActionBar().hide();
8.2. We are using vector icon so enable
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
8.3. Now Inilized video with configration
  private void initializePlayer() {
        if (player == null) {
            // 1. Create a default TrackSelector
            LoadControl loadControl = new DefaultLoadControl(
                    new DefaultAllocator(true, 16),
                    VideoPlayerConfig.MIN_BUFFER_DURATION,
                    VideoPlayerConfig.MAX_BUFFER_DURATION,
                    VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER,
                    VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER, -1, true);

            BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
            TrackSelection.Factory videoTrackSelectionFactory =
                    new AdaptiveTrackSelection.Factory(bandwidthMeter);
            TrackSelector trackSelector =
                    new DefaultTrackSelector(videoTrackSelectionFactory);
            // 2. Create the player
            player = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(this), trackSelector, loadControl);
            videoFullScreenPlayer.setPlayer(player);
        }


    }
8.3. After inilizaing player create a media source using below methods
 private void buildMediaSource(Uri mUri) {
        // Measures bandwidth during playback. Can be null if not required.
        DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        // Produces DataSource instances through which media data is loaded.
        DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this,
                Util.getUserAgent(this, getString(R.string.app_name)), bandwidthMeter);
        // This is the MediaSource representing the media to be played.
        MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
                .createMediaSource(mUri);
        // Prepare the player with the source.
        player.prepare(videoSource);
        player.setPlayWhenReady(true);
        player.addListener(this);
    }
8.4.Implement Player.EventListener and We Check player state in below methods
 @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        switch (playbackState) {

            case Player.STATE_BUFFERING:
                spinnerVideoDetails.setVisibility(View.VISIBLE);
                break;
            case Player.STATE_ENDED:
                // Activate the force enable
                break;
            case Player.STATE_IDLE:

                break;
            case Player.STATE_READY:
                spinnerVideoDetails.setVisibility(View.GONE);

                break;
            default:
                // status = PlaybackStatus.IDLE;
                break;
        }
    }

9. Finally full activity code seems like this

package com.androidwave.exoplayer;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.ProgressBar;

import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class ExoPlayerActivity extends AppCompatActivity implements Player.EventListener {
    private static final String TAG = "ExoPlayerActivity";

    private static final String KEY_VIDEO_URI = "video_uri";

    @BindView(R.id.videoFullScreenPlayer)
    PlayerView videoFullScreenPlayer;
    @BindView(R.id.spinnerVideoDetails)
    ProgressBar spinnerVideoDetails;
    @BindView(R.id.imageViewExit)
    ImageView imageViewExit;

    String videoUri;
    SimpleExoPlayer player;
    Handler mHandler;
    Runnable mRunnable;

    public static Intent getStartIntent(Context context, String videoUri) {
        Intent intent = new Intent(context, ExoPlayerActivity.class);
        intent.putExtra(KEY_VIDEO_URI, videoUri);
        return intent;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_exo_player);
        ButterKnife.bind(this);
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
        getSupportActionBar().hide();

        if (getIntent().hasExtra(KEY_VIDEO_URI)) {
            videoUri = getIntent().getStringExtra(KEY_VIDEO_URI);
        }

        setUp();
    }

    private void setUp() {
        initializePlayer();
        if (videoUri == null) {
            return;
        }
        buildMediaSource(Uri.parse(videoUri));
    }

    @OnClick(R.id.imageViewExit)
    public void onViewClicked() {
        finish();
    }

    private void initializePlayer() {
        if (player == null) {
            // 1. Create a default TrackSelector
            LoadControl loadControl = new DefaultLoadControl(
                    new DefaultAllocator(true, 16),
                    VideoPlayerConfig.MIN_BUFFER_DURATION,
                    VideoPlayerConfig.MAX_BUFFER_DURATION,
                    VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER,
                    VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER, -1, true);

            BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
            TrackSelection.Factory videoTrackSelectionFactory =
                    new AdaptiveTrackSelection.Factory(bandwidthMeter);
            TrackSelector trackSelector =
                    new DefaultTrackSelector(videoTrackSelectionFactory);
            // 2. Create the player
            player = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(this), trackSelector, loadControl);
            videoFullScreenPlayer.setPlayer(player);
        }


    }

    private void buildMediaSource(Uri mUri) {
        // Measures bandwidth during playback. Can be null if not required.
        DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        // Produces DataSource instances through which media data is loaded.
        DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this,
                Util.getUserAgent(this, getString(R.string.app_name)), bandwidthMeter);
        // This is the MediaSource representing the media to be played.
        MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
                .createMediaSource(mUri);
        // Prepare the player with the source.
        player.prepare(videoSource);
        player.setPlayWhenReady(true);
        player.addListener(this);
    }

    private void releasePlayer() {
        if (player != null) {
            player.release();
            player = null;
        }
    }

    private void pausePlayer() {
        if (player != null) {
            player.setPlayWhenReady(false);
            player.getPlaybackState();
        }
    }

    private void resumePlayer() {
        if (player != null) {
            player.setPlayWhenReady(true);
            player.getPlaybackState();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        pausePlayer();
        if (mRunnable != null) {
            mHandler.removeCallbacks(mRunnable);
        }
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        resumePlayer();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releasePlayer();
    }

    @Override
    public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {

    }

    @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 Player.STATE_BUFFERING:
                spinnerVideoDetails.setVisibility(View.VISIBLE);
                break;
            case Player.STATE_ENDED:
                // Activate the force enable
                break;
            case Player.STATE_IDLE:

                break;
            case Player.STATE_READY:
                spinnerVideoDetails.setVisibility(View.GONE);

                break;
            default:
                // status = PlaybackStatus.IDLE;
                break;
        }
    }

    @Override
    public void onRepeatModeChanged(int repeatMode) {

    }

    @Override
    public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {

    }

    @Override
    public void onPlayerError(ExoPlaybackException error) {

    }

    @Override
    public void onPositionDiscontinuity(int reason) {

    }

    @Override
    public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {

    }

    @Override
    public void onSeekProcessed() {

    }
}
Download Sample Project- Video Streaming ExoPlayer in Android

 
If you have any query, feel free to connect us.

In the previous tutorial, we demonstrate video streaming over the Internet using ExoPlayer. Somehow you missed previous articles you must read this ExoPlayer in Android for better understanding. In a previous blog, you get an idea about ExoPlayer core functionality, DASH and UI library module.

I hope you read my previous tutorials(I can assume you have basic knowledge about ExoPlayer). In this tutorial, I will describe how to implement video playback in the RecyclerView Android. Just like some popular application eg. Magisto, Facebook, Twitter, Instagram.

This article based on ExoPlayer. In case some things miss in this article download full source and a working sample apk is there.

Before starting to think in 2 min what we needed to solve.

  • First problem is managing ExoPlayer playback
  • Second is we need to know which view on the scrolled list is currently active. So we can initialize and release ExoPlayer intense in RecylerView
  • Create Project

    Simply go to file menu and create a project and add the ExoPlayer dependency in build.gradle.

        implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
        implementation 'org.jsoup:jsoup:1.10.3'
    

    The complete project hierarchy seems like image

    ExoPlayer in RecyclerView in android tutorials
    ExoPlayer in RecyclerView in android

    Create a custom RecyclerView with ExoPlayerRecyclerView name

    Now comes in first problem is

    how to manage ExoPlayer playback?

    Create a class ExoPlayerRecyclerView.java which extend RecyclerView
    Furthermore we fetch start position and end position of view from LayoutManager after that calculate target position.
    Here now on target position, we will add ExoPlayerView and set ExoPlayer.

      //play the video in the row
        public void playVideo() {
            int startPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
            int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
    
            if (endPosition - startPosition > 1) {
                endPosition = startPosition + 1;
            }
    
            if (startPosition < 0 || endPosition < 0) {
                return;
            }
    
            int targetPosition;
            if (startPosition != endPosition) {
                int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
                int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
                targetPosition = startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
            } else {
                targetPosition = startPosition;
            }
    
            if (targetPosition < 0 || targetPosition == playPosition) {
                return;
            }
            playPosition = targetPosition;
            if (videoSurfaceView == null) {
                return;
            }
            videoSurfaceView.setVisibility(INVISIBLE);
            removeVideoView(videoSurfaceView);
    
            // get target View targetPosition in RecyclerView
            int at = targetPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
    
            View child = getChildAt(at);
            if (child == null) {
                return;
            }
    
            VideoRecyclerViewAdapter.ViewHolder holder
                    = (VideoRecyclerViewAdapter.ViewHolder) child.getTag();
            if (holder == null) {
                playPosition = -1;
                return;
            }
            mCoverImage = holder.mCover;
            FrameLayout frameLayout = holder.itemView.findViewById(R.id.video_layout);
            frameLayout.addView(videoSurfaceView);
            addedVideo = true;
            rowParent = holder.itemView;
            videoSurfaceView.requestFocus();
            // Bind the player to the view.
            videoSurfaceView.setPlayer(player);
    
            // Measures bandwidth during playback. Can be null if not required.
            DefaultBandwidthMeter defaultBandwidthMeter = new DefaultBandwidthMeter();
            // Produces DataSource instances through which media data is loaded.
    
            DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(appContext,
                    Util.getUserAgent(appContext, "android_wave_list"), defaultBandwidthMeter);
            // This is the MediaSource representing the media to be played.
            String uriString = videoInfoList.get(targetPosition).getUrl();
            if (uriString != null) {
                MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
                        .createMediaSource(Uri.parse(uriString));
                // Prepare the player with the source.
                player.prepare(videoSource);
                player.setPlayWhenReady(true);
            }
    
    
        }
    

    Now comes in the second problem get visible Video Surface scrolled list.

      private int getVisibleVideoSurfaceHeight(int playPosition) {
            int at = playPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
    
            View child = getChildAt(at);
            if (child == null) {
                return 0;
            }
    
            int[] location01 = new int[2];
            child.getLocationInWindow(location01);
    
            if (location01[1] < 0) {
                return location01[1] + videoSurfaceDefaultHeight;
            } else {
                return screenDefaultHeight - location01[1];
            }
        }
    

    The complete class code is below

    package com.androidwave.exoplayer.ui;
    
    import android.content.Context;
    import android.graphics.Point;
    import android.net.Uri;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.AttributeSet;
    import android.view.Display;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.WindowManager;
    import android.widget.AbsListView;
    import android.widget.FrameLayout;
    import android.widget.ImageView;
    
    import com.androidwave.exoplayer.R;
    import com.androidwave.exoplayer.adapter.VideoRecyclerViewAdapter;
    import com.androidwave.exoplayer.model.VideoInfo;
    import com.androidwave.exoplayer.utils.VideoPlayerConfig;
    import com.google.android.exoplayer2.DefaultLoadControl;
    import com.google.android.exoplayer2.ExoPlaybackException;
    import com.google.android.exoplayer2.ExoPlayerFactory;
    import com.google.android.exoplayer2.LoadControl;
    import com.google.android.exoplayer2.PlaybackParameters;
    import com.google.android.exoplayer2.Player;
    import com.google.android.exoplayer2.SimpleExoPlayer;
    import com.google.android.exoplayer2.Timeline;
    import com.google.android.exoplayer2.source.ExtractorMediaSource;
    import com.google.android.exoplayer2.source.MediaSource;
    import com.google.android.exoplayer2.source.TrackGroupArray;
    import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
    import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
    import com.google.android.exoplayer2.trackselection.TrackSelection;
    import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
    import com.google.android.exoplayer2.trackselection.TrackSelector;
    import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
    import com.google.android.exoplayer2.ui.PlayerView;
    import com.google.android.exoplayer2.upstream.BandwidthMeter;
    import com.google.android.exoplayer2.upstream.DataSource;
    import com.google.android.exoplayer2.upstream.DefaultAllocator;
    import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
    import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
    import com.google.android.exoplayer2.util.Util;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class ExoPlayerRecyclerView extends RecyclerView {
    
        private List<VideoInfo> videoInfoList = new ArrayList<>();
        private int videoSurfaceDefaultHeight = 0;
        private int screenDefaultHeight = 0;
        SimpleExoPlayer player;
        //surface view for playing video
        private PlayerView videoSurfaceView;
        private ImageView mCoverImage;
        private Context appContext;
    
    
        /**
         * the position of playing video
         */
        private int playPosition = -1;
    
        private boolean addedVideo = false;
        private View rowParent;
    
        /**
         * {@inheritDoc}
         *
         * @param context
         */
        public ExoPlayerRecyclerView(Context context) {
            super(context);
            initialize(context);
        }
    
        /**
         * {@inheritDoc}
         *
         * @param context
         * @param attrs
         */
        public ExoPlayerRecyclerView(Context context,
                                     AttributeSet attrs) {
            super(context, attrs);
            initialize(context);
        }
    
        /**
         * {@inheritDoc}
         *
         * @param context
         * @param attrs
         * @param defStyleAttr
         */
        public ExoPlayerRecyclerView(Context context,
                                     AttributeSet attrs,
                                     int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initialize(context);
        }
    
        public void setVideoInfoList(List<VideoInfo> videoInfoList) {
            this.videoInfoList = videoInfoList;
    
        }
    
    
        /**
         * prepare for video play
         */
        //remove the player from the row
        private void removeVideoView(PlayerView videoView) {
    
            ViewGroup parent = (ViewGroup) videoView.getParent();
            if (parent == null) {
                return;
            }
    
            int index = parent.indexOfChild(videoView);
            if (index >= 0) {
                parent.removeViewAt(index);
                addedVideo = false;
            }
    
        }
    
        //play the video in the row
        public void playVideo() {
            int startPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
            int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
    
            if (endPosition - startPosition > 1) {
                endPosition = startPosition + 1;
            }
    
            if (startPosition < 0 || endPosition < 0) {
                return;
            }
    
            int targetPosition;
            if (startPosition != endPosition) {
                int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
                int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
                targetPosition = startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
            } else {
                targetPosition = startPosition;
            }
    
            if (targetPosition < 0 || targetPosition == playPosition) {
                return;
            }
            playPosition = targetPosition;
            if (videoSurfaceView == null) {
                return;
            }
            videoSurfaceView.setVisibility(INVISIBLE);
            removeVideoView(videoSurfaceView);
    
            // get target View targetPosition in RecyclerView
            int at = targetPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
    
            View child = getChildAt(at);
            if (child == null) {
                return;
            }
    
            VideoRecyclerViewAdapter.ViewHolder holder
                    = (VideoRecyclerViewAdapter.ViewHolder) child.getTag();
            if (holder == null) {
                playPosition = -1;
                return;
            }
            mCoverImage = holder.mCover;
            FrameLayout frameLayout = holder.itemView.findViewById(R.id.video_layout);
            frameLayout.addView(videoSurfaceView);
            addedVideo = true;
            rowParent = holder.itemView;
            videoSurfaceView.requestFocus();
            // Bind the player to the view.
            videoSurfaceView.setPlayer(player);
    
            // Measures bandwidth during playback. Can be null if not required.
            DefaultBandwidthMeter defaultBandwidthMeter = new DefaultBandwidthMeter();
            // Produces DataSource instances through which media data is loaded.
    
            DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(appContext,
                    Util.getUserAgent(appContext, "android_wave_list"), defaultBandwidthMeter);
            // This is the MediaSource representing the media to be played.
            String uriString = videoInfoList.get(targetPosition).getUrl();
            if (uriString != null) {
                MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
                        .createMediaSource(Uri.parse(uriString));
                // Prepare the player with the source.
                player.prepare(videoSource);
                player.setPlayWhenReady(true);
            }
    
    
        }
    
        private int getVisibleVideoSurfaceHeight(int playPosition) {
            int at = playPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
    
            View child = getChildAt(at);
            if (child == null) {
                return 0;
            }
    
            int[] location01 = new int[2];
            child.getLocationInWindow(location01);
    
            if (location01[1] < 0) {
                return location01[1] + videoSurfaceDefaultHeight;
            } else {
                return screenDefaultHeight - location01[1];
            }
        }
    
    
        private void initialize(Context context) {
    
            appContext = context.getApplicationContext();
            Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
            Point point = new Point();
            display.getSize(point);
            videoSurfaceDefaultHeight = point.x;
    
            screenDefaultHeight = point.y;
            videoSurfaceView = new PlayerView(appContext);
            videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
    
            BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
            TrackSelection.Factory videoTrackSelectionFactory =
                    new AdaptiveTrackSelection.Factory(bandwidthMeter);
            TrackSelector trackSelector =
                    new DefaultTrackSelector(videoTrackSelectionFactory);
            LoadControl loadControl = new DefaultLoadControl(
                    new DefaultAllocator(true, 16),
                    VideoPlayerConfig.MIN_BUFFER_DURATION,
                    VideoPlayerConfig.MAX_BUFFER_DURATION,
                    VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER,
                    VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER, -1, true);
    
            // 2. Create the player
            player = ExoPlayerFactory.newSimpleInstance(appContext, trackSelector, loadControl);
            // Bind the player to the view.
            videoSurfaceView.setUseController(false);
            videoSurfaceView.setPlayer(player);
    
            addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                        playVideo();
                    }
                }
    
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                }
            });
    
            addOnChildAttachStateChangeListener(new OnChildAttachStateChangeListener() {
                @Override
                public void onChildViewAttachedToWindow(View view) {
    
                }
    
                @Override
                public void onChildViewDetachedFromWindow(View view) {
                    if (addedVideo && rowParent != null && rowParent.equals(view)) {
                        removeVideoView(videoSurfaceView);
                        playPosition = -1;
                        videoSurfaceView.setVisibility(INVISIBLE);
                    }
    
                }
            });
            player.addListener(new Player.EventListener() {
                @Override
                public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
    
                }
    
                @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 Player.STATE_BUFFERING:
                            //   videoSurfaceView.setAlpha(0.5f);
                            break;
                        case Player.STATE_ENDED:
                            player.seekTo(0);
                            break;
                        case Player.STATE_IDLE:
    
                            break;
                        case Player.STATE_READY:
                            videoSurfaceView.setVisibility(VISIBLE);
                            videoSurfaceView.setAlpha(1);
                            mCoverImage.setVisibility(GONE);
    
                            break;
                        default:
                            break;
                    }
                }
    
                @Override
                public void onRepeatModeChanged(int repeatMode) {
    
                }
    
                @Override
                public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
    
                }
    
                @Override
                public void onPlayerError(ExoPlaybackException error) {
    
                }
    
                @Override
                public void onPositionDiscontinuity(int reason) {
    
                }
    
                @Override
                public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
    
                }
    
                @Override
                public void onSeekProcessed() {
    
                }
            });
        }
    
        public void onPausePlayer() {
            if (videoSurfaceView != null) {
                removeVideoView(videoSurfaceView);
                player.release();
                videoSurfaceView = null;
            }
        }
    
        public void onRestartPlayer() {
            if (videoSurfaceView == null) {
                playPosition = -1;
                playVideo();
            }
        }
    
        /**
         * release memory
         */
        public void onRelease() {
    
            if (player != null) {
                player.release();
                player = null;
            }
    
            rowParent = null;
        }
    
    
    }
    
    

    Now ExoPlayerRecyclerView.java component is ready to use. Now I will explain how to use this component.

    Create a VideoInfo.java model class

    public class VideoInfo {
        private int mId;
        private String mTitle;
        private String mUrl;
        private String mCoverUrl;
        private String mUserHandle;
    
        public String getUserHandle() {
            return mUserHandle;
        }
    
        public void setUserHandle(String mUserHandle) {
            this.mUserHandle = mUserHandle;
        }
    
        public int getId() {
            return mId;
        }
    
        public void setId(int mId) {
            this.mId = mId;
        }
    
        public String getTitle() {
            return mTitle;
        }
    
        public void setTitle(String mTitle) {
            this.mTitle = mTitle;
        }
    
        public String getUrl() {
            return mUrl;
        }
    
        public void setUrl(String mUrl) {
            this.mUrl = mUrl;
        }
    
        public String getCoverUrl() {
            return mCoverUrl;
        }
    
        public void setCoverUrl(String mCoverUrl) {
            this.mCoverUrl = mCoverUrl;
        }
    } 
    

    Create a RecyclerView adapter and bind views with holder

    package com.androidwave.exoplayer.adapter;
    
    import android.support.annotation.NonNull;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Button;
    import android.widget.FrameLayout;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import com.androidwave.exoplayer.R;
    import com.androidwave.exoplayer.model.VideoInfo;
    import com.androidwave.exoplayer.ui.BaseViewHolder;
    import com.bumptech.glide.Glide;
    import com.bumptech.glide.request.RequestOptions;
    
    import java.util.List;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
    
    public class VideoRecyclerViewAdapter extends RecyclerView.Adapter<BaseViewHolder> {
    
        public static final int VIEW_TYPE_EMPTY = 0;
        public static final int VIEW_TYPE_NORMAL = 1;
    
        private List<VideoInfo> mInfoList;
    
        public VideoRecyclerViewAdapter(List<VideoInfo> infoList) {
            mInfoList = infoList;
        }
    
        @NonNull
        @Override
        public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            switch (viewType) {
                case VIEW_TYPE_NORMAL:
                    return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false));
                case VIEW_TYPE_EMPTY:
                    return new EmptyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_empty_view, parent, false));
                default:
                    return null;
            }
        }
    
        @Override
        public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
            holder.onBind(position);
        }
    
    
        @Override
        public int getItemViewType(int position) {
            if (mInfoList != null && mInfoList.size() > 0) {
                return VIEW_TYPE_NORMAL;
            } else {
                return VIEW_TYPE_EMPTY;
            }
        }
    
        @Override
        public int getItemCount() {
            if (mInfoList != null && mInfoList.size() > 0) {
                return mInfoList.size();
            } else {
                return 1;
            }
        }
    
    
    
        public void onRelease() {
            if (mInfoList != null) {
                mInfoList.clear();
                mInfoList = null;
            }
        }
    
        public class ViewHolder extends BaseViewHolder {
            @BindView(R.id.textViewTitle)
            TextView textViewTitle;
            @BindView(R.id.userHandle)
            TextView userHandle;
            @BindView(R.id.video_layout)
            public FrameLayout videoLayout;
            @BindView(R.id.cover)
            public ImageView mCover;
            public final View parent;
    
    
            public ViewHolder(View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
                parent = itemView;
            }
    
            protected void clear() {
    
            }
    
            public void onBind(int position) {
                super.onBind(position);
                parent.setTag(this);
                VideoInfo videoInfo = mInfoList.get(position);
                textViewTitle.setText(videoInfo.getTitle());
                userHandle.setText(videoInfo.getUserHandle());
                Glide.with(itemView.getContext())
                        .load(videoInfo.getCoverUrl()).apply(new RequestOptions().optionalCenterCrop())
                        .into(mCover);
            }
        }
    
        public class EmptyViewHolder extends BaseViewHolder {
    
            @BindView(R.id.btn_retry)
            Button retryButton;
    
            @BindView(R.id.tv_message)
            TextView messageTextView;
    
            public EmptyViewHolder(View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
                itemView.setVisibility(View.GONE);
            }
    
            @Override
            protected void clear() {
    
            }
    
            @OnClick(R.id.btn_retry)
            void onRetryClick() {
    
            }
        }
    }
    
    

    Now prepare video array list

       
      private void prepareVideoList() {
            VideoInfo videoInfo = new VideoInfo();
            videoInfo.setId(1);
            videoInfo.setUserHandle("@h.pandya");
            videoInfo.setTitle("Do you think the concept of marriage will no longer exist in the future?");
            videoInfo.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-1.png");
            videoInfo.setUrl("https://androidwave.com/media/androidwave-video-1.mp4");
    
            VideoInfo videoInfo2 = new VideoInfo();
            videoInfo2.setId(2);
            videoInfo2.setUserHandle("@hardik.patel");
            videoInfo2.setTitle("If my future husband doesn't cook food as good as my mother should I scold him?");
            videoInfo2.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-2.png");
            videoInfo2.setUrl("https://androidwave.com/media/androidwave-video-2.mp4");
    
            VideoInfo videoInfo3 = new VideoInfo();
            videoInfo3.setId(3);
            videoInfo3.setUserHandle("@arun.gandhi");
            videoInfo3.setTitle("Give your opinion about the Ayodhya temple controversy.");
            videoInfo3.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-3.png");
            videoInfo3.setUrl("https://androidwave.com/media/androidwave-video-3.mp4");
    
            VideoInfo videoInfo4 = new VideoInfo();
            videoInfo4.setId(4);
            videoInfo4.setUserHandle("@sachin.patel");
            videoInfo4.setTitle("When did kama founders find sex offensive to Indian traditions");
            videoInfo4.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-4.png");
            videoInfo4.setUrl("https://androidwave.com/media/androidwave-video-6.mp4");
    
            VideoInfo videoInfo5 = new VideoInfo();
            videoInfo5.setId(5);
            videoInfo5.setUserHandle("@monika.sharma");
            videoInfo5.setTitle("When did you last cry in front of someone?");
            videoInfo5.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-5.png");
            videoInfo5.setUrl("https://androidwave.com/media/androidwave-video-5.mp4");
    
            videoInfoList.add(videoInfo);
            videoInfoList.add(videoInfo2);
            videoInfoList.add(videoInfo3);
            videoInfoList.add(videoInfo4);
            videoInfoList.add(videoInfo5);
            videoInfoList.add(videoInfo);
            videoInfoList.add(videoInfo2);
            videoInfoList.add(videoInfo3);
            videoInfoList.add(videoInfo4);
            videoInfoList.add(videoInfo5);
    
        }
    

    Finally main actvity looks like

    package com.androidwave.exoplayer;
    
    import android.graphics.drawable.Drawable;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Looper;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.DefaultItemAnimator;
    import android.support.v7.widget.LinearLayoutManager;
    
    import com.androidwave.exoplayer.adapter.VideoRecyclerViewAdapter;
    import com.androidwave.exoplayer.model.VideoInfo;
    import com.androidwave.exoplayer.ui.ExoPlayerRecyclerView;
    import com.androidwave.exoplayer.utils.DividerItemDecoration;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    
    import static android.widget.LinearLayout.VERTICAL;
    
    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.recyclerViewFeed)
        ExoPlayerRecyclerView recyclerViewFeed;
    
        private List<VideoInfo> videoInfoList = new ArrayList<>();
        private VideoRecyclerViewAdapter mAdapter;
        private boolean firstTime = true;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            prepareVideoList();
            recyclerViewFeed.setVideoInfoList(videoInfoList);
            mAdapter = new VideoRecyclerViewAdapter(videoInfoList);
            recyclerViewFeed.setLayoutManager(new LinearLayoutManager(this, VERTICAL, false));
            Drawable dividerDrawable = ContextCompat.getDrawable(this, R.drawable.divider_drawable);
            recyclerViewFeed.addItemDecoration(new DividerItemDecoration(dividerDrawable));
            recyclerViewFeed.setItemAnimator(new DefaultItemAnimator());
            recyclerViewFeed.setAdapter(mAdapter);
    
            if (firstTime) {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        recyclerViewFeed.playVideo();
                    }
                });
                firstTime = false;
            }
    
        }
    
        private void prepareVideoList() {
            VideoInfo videoInfo = new VideoInfo();
            videoInfo.setId(1);
            videoInfo.setUserHandle("@h.pandya");
            videoInfo.setTitle("Do you think the concept of marriage will no longer exist in the future?");
            videoInfo.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-1.png");
            videoInfo.setUrl("https://androidwave.com/media/androidwave-video-1.mp4");
    
            VideoInfo videoInfo2 = new VideoInfo();
            videoInfo2.setId(2);
            videoInfo2.setUserHandle("@hardik.patel");
            videoInfo2.setTitle("If my future husband doesn't cook food as good as my mother should I scold him?");
            videoInfo2.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-2.png");
            videoInfo2.setUrl("https://androidwave.com/media/androidwave-video-2.mp4");
    
            VideoInfo videoInfo3 = new VideoInfo();
            videoInfo3.setId(3);
            videoInfo3.setUserHandle("@arun.gandhi");
            videoInfo3.setTitle("Give your opinion about the Ayodhya temple controversy.");
            videoInfo3.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-3.png");
            videoInfo3.setUrl("https://androidwave.com/media/androidwave-video-3.mp4");
    
            VideoInfo videoInfo4 = new VideoInfo();
            videoInfo4.setId(4);
            videoInfo4.setUserHandle("@sachin.patel");
            videoInfo4.setTitle("When did kama founders find sex offensive to Indian traditions");
            videoInfo4.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-4.png");
            videoInfo4.setUrl("https://androidwave.com/media/androidwave-video-6.mp4");
    
            VideoInfo videoInfo5 = new VideoInfo();
            videoInfo5.setId(5);
            videoInfo5.setUserHandle("@monika.sharma");
            videoInfo5.setTitle("When did you last cry in front of someone?");
            videoInfo5.setCoverUrl("https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-5.png");
            videoInfo5.setUrl("https://androidwave.com/media/androidwave-video-5.mp4");
    
            videoInfoList.add(videoInfo);
            videoInfoList.add(videoInfo2);
            videoInfoList.add(videoInfo3);
            videoInfoList.add(videoInfo4);
            videoInfoList.add(videoInfo5);
            videoInfoList.add(videoInfo);
            videoInfoList.add(videoInfo2);
            videoInfoList.add(videoInfo3);
            videoInfoList.add(videoInfo4);
            videoInfoList.add(videoInfo5);
    
        }
    
        @Override
        protected void onPause() {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    recyclerViewFeed.onPausePlayer();
                }
            });
            super.onPause();
        }
    
        @Override
        protected void onResume() {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    recyclerViewFeed.onRestartPlayer();
                }
            });
            super.onResume();
        }
    
        @Override
        protected void onDestroy() {
            if(recyclerViewFeed!=null)
            recyclerViewFeed.onRelease();
            super.onDestroy();
        }
    }
    
    
    Download Sample Project- ExoPlayer in RecyclerView in Android

     
    If you have any query, feel free to connect us.