In this tutorial, I’m going to explain how to use ExoPlayer. In this tutorial, we will demonstrate how to stream audio and video local URI and server as well. Means we are going to create an audio-video player, that player will capable to play audio and video file local or remote server as well. If you want to implement ExoPlayer in RecylerView read this article.

ExoPlayer

ExoPlayer is an open-source project. That is distributed separately from the Android SDK. It not a part of Android SDK. In Android, ExoPlayer’s standard video and audio components are built on Android’s MediaCodec API, which was released in AndroidAndroid 4.1 ( JELLY_BEAN ).

ExoPlayer, Play audio video (Demo App)

Objective

First, We will create a gallery view that fetches all video file and shows on RecyclerView in GirdView layout. Second, I will create Singleton instance of ExoPlayer, The manager names are PlayerManager. Now you are thinking about what needs to creating a singleton? Don’t worry I explain, Two reasons behind it, first are Abstraction, Activity code should be clean and second better player state management. Will explain how to user PlayerManager in Activity.

1. Create a GalleryView

1.1 Create a model class for holding data

Go to src and create a new java file with VideoModel.java name, declare simple getter setter for file path and video thumbnail.

package com.androidwave.videoplayer; /** * Created on : Jan 24, 2019 * Author : AndroidWave * Email : info@androidwave.com */ public class VideoModel { private String mFilePath, mVideoThumb; private boolean isSelected; public String getFilePath() { return mFilePath; } public void setFilePath(String mFilePath) { this.mFilePath = mFilePath; } public String getVideoThumb() { return mVideoThumb; } public void setVideoThumb(String mVideoThumb) { this.mVideoThumb = mVideoThumb; } public boolean isSelected() { return isSelected; } public void setSelected(boolean selected) { isSelected = selected; } }

1.2 Create a video row layout for RecyclerView

Go to res folder and create a layout named video_list_row.xml

<?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" android:layout_width="match_parent" android:layout_height="200dp"> <ImageView android:id="@+id/imageViewThumbnail" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:scaleType="centerCrop" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@android:drawable/ic_menu_report_image" /> <ImageView android:id="@+id/imageView2" android:layout_width="50dp" android:layout_height="50dp" android:layout_margin="@dimen/_24" android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/play_button" /> </android.support.constraint.ConstraintLayout>

1.3 Create RecyclerView Adapter

Just go to src and create a java file for VideoRecyclerAdapter and set setOnClickListener() on itemView.

package com.androidwave.videoplayer; import android.content.Intent; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import com.bumptech.glide.Glide; import java.util.List; /** * Created on : Jan 24, 2019 * Author : AndroidWave * Email : info@androidwave.com */ public class VideoRecyclerAdapter extends RecyclerView.Adapter<VideoRecyclerAdapter.VideoViewHolder> { private List<VideoModel> videoList; public VideoRecyclerAdapter(List<VideoModel> videoList) { this.videoList = videoList; } public class VideoViewHolder extends RecyclerView.ViewHolder { public ImageView imageViewThumbnail; VideoViewHolder(View view) { super(view); imageViewThumbnail = view.findViewById(R.id.imageViewThumbnail); } } @Override public VideoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.video_list_row, parent, false); return new VideoViewHolder(itemView); } @Override public void onBindViewHolder(final VideoViewHolder holder, int position) { final VideoModel mVideo = videoList.get(position); Glide.with(holder.itemView.getContext()).load(mVideo.getVideoThumb()).into(holder.imageViewThumbnail); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent mPlayerIntent = PlayerActivity.getStartIntent(holder.itemView.getContext(), mVideo.getFilePath()); holder.itemView.getContext().startActivity(mPlayerIntent); } }); } @Override public int getItemCount() { return videoList.size(); } }

2. Open Activity class and manage following task

Check permission for above marshmallow.

If storage permission is granted then fetch all video file from gallery

All files add details in VideoModel and add in the list.

Set layout manager as a GridLayoutManager over the RecyclerView.

Finally initialised adapter and set on the RecyclerView

MainActivity Code looks like below

package com.androidwave.videoplayer; import android.Manifest; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.widget.Toast; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private static final int REQUEST_PERMISSIONS = 101; ArrayList<VideoModel> mVideoList; VideoRecyclerAdapter mAdapter; RecyclerView mRecyclerView; GridLayoutManager recyclerViewLayoutManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = findViewById(R.id.recyclerViewGallery); recyclerViewLayoutManager = new GridLayoutManager(getApplicationContext(), 2); mRecyclerView.setLayoutManager(recyclerViewLayoutManager); mVideoList = new ArrayList<>(); checkPermission(); } private void checkPermission() { if ((ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) { if ((ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) && (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE))) { } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSIONS); } } } else { // all permission granted getAllVideoFromGallery(); } } public void getAllVideoFromGallery() { Uri uri; Cursor mCursor; int COLUMN_INDEX_DATA, COLUMN_INDEX_NAME, COLUMN_ID, COLUMN_THUMB; String absolutePathOfFile = null; uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; String[] projection = {MediaStore.MediaColumns.DATA, MediaStore.Video.Media.BUCKET_DISPLAY_NAME, MediaStore.Video.Media._ID, MediaStore.Video.Thumbnails.DATA}; final String orderBy = MediaStore.Images.Media.DATE_TAKEN; mCursor = getApplicationContext().getContentResolver().query(uri, projection, null, null, orderBy + " DESC"); COLUMN_INDEX_DATA = mCursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA); COLUMN_INDEX_NAME = mCursor.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_DISPLAY_NAME); COLUMN_ID = mCursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID); COLUMN_THUMB = mCursor.getColumnIndexOrThrow(MediaStore.Video.Thumbnails.DATA); while (mCursor.moveToNext()) { absolutePathOfFile = mCursor.getString(COLUMN_INDEX_DATA); Log.e("Column", absolutePathOfFile); Log.e("Folder", mCursor.getString(COLUMN_INDEX_NAME)); Log.e("column_id", mCursor.getString(COLUMN_ID)); Log.e("thum", mCursor.getString(COLUMN_THUMB)); VideoModel mVideo = new VideoModel(); mVideo.setSelected(false); mVideo.setFilePath(absolutePathOfFile); mVideo.setVideoThumb(mCursor.getString(COLUMN_THUMB)); mVideoList.add(mVideo); } mAdapter = new VideoRecyclerAdapter(mVideoList); mRecyclerView.setAdapter(mAdapter); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case REQUEST_PERMISSIONS: { for (int i = 0; i < grantResults.length; i++) { if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { getAllVideoFromGallery(); } else { Toast.makeText(MainActivity.this, "The app was not allowed to read or write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show(); } } } } } }

3. Create PlayerManager Singleton

Open src folder and create a file with name PlayerManager and follow below step

Prepare SharedInstance of this class

Initialize ExoPlayer and set DataSourceFactory and MediaSource

Add Player.EventListener

Create some usable method like play, pause, resume and release

Final output looks like

package com.androidwave.videoplayer; import android.content.Context; import android.net.Uri; import android.util.Log; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayerFactory; 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.extractor.DefaultExtractorsFactory; 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.source.hls.HlsMediaSource; 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.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.util.Util; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; /** * Created on : Jan 21, 2019 * Author : AndroidWave * Email : info@androidwave.com */ public class PlayerManager { /** * declare some usable variable */ private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter(); private static final String TAG = "ExoPlayerManager"; private static PlayerManager mInstance = null; PlayerView mPlayerView; DefaultDataSourceFactory dataSourceFactory; String uriString = ""; ArrayList<String> mPlayList = null; Integer playlistIndex = 0; CallBacks.playerCallBack listner; private SimpleExoPlayer mPlayer; /** * private constructor * * @param mContext */ private PlayerManager(Context mContext) { TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER); TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); mPlayer = ExoPlayerFactory.newSimpleInstance(mContext, trackSelector); mPlayerView = new PlayerView(mContext); mPlayerView.setUseController(true); mPlayerView.requestFocus(); mPlayerView.setPlayer(mPlayer); Uri mp4VideoUri = Uri.parse(uriString); dataSourceFactory = new DefaultDataSourceFactory(mContext, Util.getUserAgent(mContext, "androidwave"), BANDWIDTH_METER); final MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory) .createMediaSource(mp4VideoUri); mPlayer.prepare(videoSource); mPlayer.addListener(new Player.EventListener() { @Override public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { Log.i(TAG, "onTimelineChanged: "); } @Override public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { Log.i(TAG, "onTracksChanged: "); } @Override public void onLoadingChanged(boolean isLoading) { Log.i(TAG, "onLoadingChanged: "); } @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { Log.i(TAG, "onPlayerStateChanged: "); if (playbackState == 4 && mPlayList != null && playlistIndex + 1 < mPlayList.size()) { Log.e(TAG, "Song Changed..."); playlistIndex++; listner.onItemClickOnItem(playlistIndex); playStream(mPlayList.get(playlistIndex)); } else if (playbackState == 4 && mPlayList != null && playlistIndex + 1 == mPlayList.size()) { mPlayer.setPlayWhenReady(false); } if (playbackState == 4 && listner != null) { listner.onPlayingEnd(); } } @Override public void onRepeatModeChanged(int repeatMode) { Log.i(TAG, "onRepeatModeChanged: "); } @Override public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { Log.i(TAG, "onShuffleModeEnabledChanged: "); } @Override public void onPlayerError(ExoPlaybackException error) { Log.i(TAG, "onPlayerError: "); } @Override public void onPositionDiscontinuity(int reason) { Log.i(TAG, "onPositionDiscontinuity: "); } @Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { Log.i(TAG, "onPlaybackParametersChanged: "); } @Override public void onSeekProcessed() { Log.i(TAG, "onSeekProcessed: "); } }); } /** * Return ExoPlayerManager instance * * @param mContext * @return */ public static PlayerManager getSharedInstance(Context mContext) { if (mInstance == null) { mInstance = new PlayerManager(mContext); } return mInstance; } public void setPlayerListener(CallBacks.playerCallBack mPlayerCallBack) { listner = mPlayerCallBack; } public PlayerView getPlayerView() { return mPlayerView; } public void playStream(String urlToPlay) { uriString = urlToPlay; Uri mp4VideoUri = Uri.parse(uriString); MediaSource videoSource; // String filenameArray[] = urlToPlay.split("\\."); if (uriString.toUpperCase().contains("M3U8")) { videoSource = new HlsMediaSource.Factory(dataSourceFactory) .setAllowChunklessPreparation(true) .createMediaSource(mp4VideoUri, null, null); } else { mp4VideoUri = Uri.parse(urlToPlay); videoSource = new ExtractorMediaSource.Factory(dataSourceFactory).setExtractorsFactory(new DefaultExtractorsFactory()).createMediaSource((mp4VideoUri)); } // Prepare the player with the source. if (mPlayer != null && videoSource != null) { mPlayer.prepare(videoSource); mPlayer.setPlayWhenReady(true); } } public void pausePlayer() { if (mPlayer != null) { mPlayer.setPlayWhenReady(false); mPlayer.getPlaybackState(); } } public void resumePlayer() { if (mPlayer != null) { mPlayer.setPlayWhenReady(true); mPlayer.getPlaybackState(); } } public Boolean isPlayerPlaying() { return mPlayer.getPlayWhenReady(); } public ArrayList<String> readURLs(String url) { if (url == null) return null; ArrayList<String> allURls = new ArrayList<String>(); try { URL urls = new URL(url); BufferedReader in = new BufferedReader(new InputStreamReader(urls .openStream())); String str; while ((str = in.readLine()) != null) { allURls.add(str); } in.close(); return allURls; } catch (Exception e) { e.printStackTrace(); return null; } } }

Create a PlayerActivity

In src folder and create a new activity, Go to File =>Activity =>choose EmptyActivity template with XML layout. Now open the activity_player.xml and below code

<?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=".PlayerActivity"> <com.google.android.exoplayer2.ui.PlayerView android:id="@+id/mPlayerView" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#A6000000" app:controller_layout_id="@layout/playback_control_view" app:player_layout_id="@layout/exo_simple_player_view" app:repeat_toggle_modes="none" app:show_timeout="45000" app:surface_type="texture_view"/> </android.support.constraint.ConstraintLayout>

Open PlayerActivity.java and bind player view with ExoPlayer using below code

package com.androidwave.videoplayer; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import com.google.android.exoplayer2.ui.PlayerView; public class PlayerActivity extends AppCompatActivity implements CallBacks.playerCallBack { String mFilePath = null; private static final String FILE_PATH = "PlayerActivity"; public static Intent getStartIntent(Context context, String mFilePath) { Intent intent = new Intent(context, PlayerActivity.class); intent.putExtra(FILE_PATH, mFilePath); return intent; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_player); if (getIntent().hasExtra(FILE_PATH)) { mFilePath = getIntent().getStringExtra(FILE_PATH); } PlayerView mPlayerView = findViewById(R.id.mPlayerView); mPlayerView.setPlayer(PlayerManager.getSharedInstance(PlayerActivity.this).getPlayerView().getPlayer()); PlayerManager.getSharedInstance(PlayerActivity.this).playStream(mFilePath); PlayerManager.getSharedInstance(this).setPlayerListener(this); } @Override public void onItemClickOnItem(Integer albumId) { } @Override public void onPlayingEnd() { PlayerManager.getSharedInstance(PlayerActivity.this).pausePlayer(); finish(); } @Override protected void onPause() { super.onPause(); PlayerManager.getSharedInstance(PlayerActivity.this).pausePlayer(); } @Override protected void onResume() { super.onResume(); PlayerManager.getSharedInstance(PlayerActivity.this).resumePlayer(); } @Override protected void onDestroy() { super.onDestroy(); PlayerManager.getSharedInstance(PlayerActivity.this).pausePlayer(); } }

After following all above just RUN the project and use app, If you have any queries, feel free to ask them in the comment section below. Happy Coding 🙂