In this article, I will tell you how to play youtube video in ExoPlayer in Android. In previous tutorials, We were learned video streaming from the server using ExoPlayer. If you wish to read the previous article Go Here. I assume you have a basic understanding of ExoPlayer.
How to play Youtube video inside the ExoPlayer in Android. I will demonstrate step by step
1. Let’s create a new project in Android Studio.
Add dependency in Project build.gradle
allprojects { repositories { google() jcenter() /** * Add dependency in Project build.gradle */ maven { url "https://jitpack.io" } } }
Add dependency in app build.gradle
// For youtube Url Extractor implementation 'com.github.HaarigerHarald:android-youtubeExtractor:v1.7.0' // ExoPlayer implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
2. Prepare ExoPlayerManager Singleton
In my previous tutorials wrote ExoPlayer instance inside each activity (not a good practice). In case if we need more than one ExoPlayerView in the app, we have to write code in each activity, as per programming standard it’s not a good practice.
Therefore, I’m going to write ExoPlayerManager utility class. In other words, I creating a singleton class for managing ExoPlayer instance.
2.1 Create a CallBacks for observing ExoPlayer state
package com.androidwave.youtubeexoplayer; public interface CallBacks { void callbackObserver(Object obj); public interface playerCallBack { void onItemClickOnItem(Integer albumId); void onPlayingEnd(); } }
3. Furthermore, create a singleton class with ExoPlayerManager.java name
As per singleton design pattern constructor should be private
/** * declare some usable variable */ private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter(); private static final String TAG = "ExoPlayerManager"; private static ExoPlayerManager mInstance = null; PlayerView mPlayerView; DefaultDataSourceFactory dataSourceFactory; String uriString = ""; ArrayList<String> mPlayList = null; Integer playlistIndex = 0; CallBacks.playerCallBack listner; private SimpleExoPlayer mPlayer; /** * Return ExoPlayerManager instance * @param mContext * @return */ public static ExoPlayerManager getSharedInstance(Context mContext) { if (mInstance == null) { mInstance = new ExoPlayerManager(mContext); } return mInstance; } /** * private constructor * @param mContext */ private ExoPlayerManager(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: "); } }); }
After define some method complete code will seem like below class
package com.androidwave.youtubeexoplayer; 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; public class ExoPlayerManager { /** * declare some usable variable */ private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter(); private static final String TAG = "ExoPlayerManager"; private static ExoPlayerManager 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 ExoPlayerManager(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 ExoPlayerManager getSharedInstance(Context mContext) { if (mInstance == null) { mInstance = new ExoPlayerManager(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(mp4VideoUri, dataSourceFactory, null, null); } else { mp4VideoUri = Uri.parse(urlToPlay); videoSource = new ExtractorMediaSource(mp4VideoUri, dataSourceFactory, new DefaultExtractorsFactory(), null, null); } // Prepare the player with the source. mPlayer.prepare(videoSource); mPlayer.setPlayWhenReady(true); } public void setPlayerVolume(float vol) { mPlayer.setVolume(vol); } public void setUriString(String uri) { uriString = uri; } public void setPlaylist(ArrayList<String> uriArrayList, Integer index, CallBacks.playerCallBack callBack) { mPlayList = uriArrayList; playlistIndex = index; listner = callBack; playStream(mPlayList.get(playlistIndex)); } public void playerPlaySwitch() { if (uriString != "") { mPlayer.setPlayWhenReady(!mPlayer.getPlayWhenReady()); } } public void stopPlayer(boolean state) { mPlayer.setPlayWhenReady(!state); } public void destroyPlayer() { mPlayer.stop(); } 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; } } }
4. How to use ExoPlayerManager inside App?
Now we prepared ExoPlayer Manager, You are thinking about how will I use this manager in our app. don’t worry I’m here will explain with an example
4.1 Let’s create a activity.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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <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/exo_playback_control_view" app:player_layout_id="@layout/exo_simple_player_view" app:repeat_toggle_modes="none" app:show_timeout="45000" app:resize_mode="fixed_height" app:surface_type="texture_view" /> </android.support.constraint.ConstraintLayout>
4.2 Create a exo_playback_control_view.xml for the ExoPlayer playback controller
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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="44dp" android:layout_gravity="bottom" android:background="#A6000000" android:layoutDirection="ltr" android:orientation="vertical"> <!-- android:background="#11000000"--> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" android:orientation="horizontal" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"> <ImageButton android:id="@id/exo_play" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/transparent" app:srcCompat="@android:drawable/ic_media_play" /> <ImageButton android:id="@id/exo_pause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/transparent" app:srcCompat="@android:drawable/ic_media_pause" /> <TextView android:id="@id/exo_position" android:layout_width="45dp" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:gravity="center" android:includeFontPadding="false" android:letterSpacing="-0.02" android:textColor="#ffffff" android:textSize="12sp" tools:text="23:09" /> <com.google.android.exoplayer2.ui.DefaultTimeBar android:id="@id/exo_progress" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dp" app:bar_height="8dp" app:buffered_color="#4Dd8d8d8" app:played_color="@color/white" app:scrubber_color="@color/white" app:unplayed_color="#4Dd8d8d8" /> </LinearLayout> </LinearLayout>
5. Let’s extract video url from Youtube YouTubeExtractor
private void extractYoutubeUrl() { @SuppressLint("StaticFieldLeak") YouTubeExtractor mExtractor = new YouTubeExtractor(this) { @Override protected void onExtractionComplete(SparseArray<YtFile> sparseArray, VideoMeta videoMeta) { if (sparseArray != null) { playVideo(sparseArray.get(17).getUrl()); } } }; mExtractor.extract(mYoutubeLink, true, true); }
6. After that, set url with ExoPlayer for streaming
private void playVideo(String downloadUrl) { PlayerView mPlayerView = findViewById(R.id.mPlayerView); mPlayerView.setPlayer(ExoPlayerManager.getSharedInstance(MainActivity.this).getPlayerView().getPlayer()); ExoPlayerManager.getSharedInstance(MainActivity.this).playStream(downloadUrl); }
7. Finally, MainActivity.java code is
package com.androidwave.youtubeexoplayer; import android.annotation.SuppressLint; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.SparseArray; import com.google.android.exoplayer2.ui.PlayerView; import at.huber.youtubeExtractor.VideoMeta; import at.huber.youtubeExtractor.YouTubeExtractor; import at.huber.youtubeExtractor.YtFile; public class MainActivity extends AppCompatActivity { // Replace video id with your video Id private String YOUTUBE_VIDEO_ID = "uZnWUZW1hQo"; private String BASE_URL = "https://www.youtube.com"; private String mYoutubeLink = BASE_URL + "/watch?v=" + YOUTUBE_VIDEO_ID; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); extractYoutubeUrl(); } private void extractYoutubeUrl() { @SuppressLint("StaticFieldLeak") YouTubeExtractor mExtractor = new YouTubeExtractor(this) { @Override protected void onExtractionComplete(SparseArray<YtFile> sparseArray, VideoMeta videoMeta) { if (sparseArray != null) { playVideo(sparseArray.get(17).getUrl()); } } }; mExtractor.extract(mYoutubeLink, true, true); } private void playVideo(String downloadUrl) { PlayerView mPlayerView = findViewById(R.id.mPlayerView); mPlayerView.setPlayer(ExoPlayerManager.getSharedInstance(MainActivity.this).getPlayerView().getPlayer()); ExoPlayerManager.getSharedInstance(MainActivity.this).playStream(downloadUrl); } }
Conclusion
Now you are done, your project is ready to run, that way you can easily play youtube video in ExoPlayer
32 Comments
i need full source code
Here is full source code https://bitbucket.org/surya_kushawah/youtube-in-exoplayer
is this will work for unlisted video
only black screen shows
Thank you so much, It worked 😀 cheers
I am getting only blank screen while run this code. I tried a lot to solve this but failed everytime. I don’t know that where the error is?
Apis is deleted, You can create mock APIs using this response
same error
I dont understand I downloaded the source code, but where should I add the link?
and when I downloaded the app only black screen showed up!
I am getting same issue
exoplayer has no sound , please help
any error you are getting
Hello, I checked your code and its Good to play Youtube Video in Android. But, I am facing an issue of Playback Quality controls.
Can you give me an Idea how to change playback Quality of Youtube Video ? using Exoplayer
I had a same problem about NPE in getUrl().
I found the solution in the link below. You may be able to fix this issue.
https://github.com/HaarigerHarald/android-youtubeExtractor/issues/54
ERROR: Failed to resolve: “com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT”
how to fix it when I try to put in Gradle
sparseArray null
I use the method
Change URL youtube in library
from “https://youtube.googleapis.com/v/” become “https://youtube.googleapis.com/v3/” in file YouTubeExtractor.java
can u specify changing?
Is any one able to use this library to extract the data from youtube url
i am getting this error
java.lang.NullPointerException: Attempt to invoke virtual method ‘java.lang.String at.huber.youtubeExtractor.YtFile.getUrl()’ on a null object reference
at com.androidwave.youtubeexoplayer.MainActivity$1.onExtractionComplete(MainActivity.java:32)
some issue in this tutorial
I’m also facing the same issue in some videos. please help
I want to know why use 22 instead of 17?
I tested on another link, neither 17 nor 22 work
i have same question , why use 17 or 22 ? Can’t we make it dynamic instead of hardcoding ?
I will look into
I have the same warning, please can you give me the solution
Just chill
How can I pass this dynamically and not sticking to 17
if (sparseArray != null) {
playVideo(sparseArray.get(17).getUrl());
}
As I would like to play adaptive.
you need to add next permission in manifest file
it solved my issue
Not helped…. show error java.lang.NullPointerException: Attempt to invoke virtual method ‘java.lang.String at.huber.youtubeExtractor.YtFile.getUrl()’
Which Permission we have to add?
Thank you so much for this tutorial, but why I get an error when trying to play YouTube live streams !? is there a certain modification should be done on the code to allow it to retrieve YouTube live stream ?
this is my logcat
java.lang.NullPointerException: Attempt to invoke virtual method ‘java.lang.String at.huber.youtubeExtractor.YtFile.getUrl()’ on a null object reference
you need to add next permission in your manifest file
What the permission? to adding in manifest