We demonstrate video streaming from the server using the ExoPlayer. In fact, youtube player also used ExoPlayer for streaming video. This ExoPlayer android example I will explain how to use ExoPlayer in your own android application.
ExoPlayer is an open-source project. This is not a part of Android SDK. In Android, ExoPlayer is application level media player. Its 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.
ExoPlayer Android Tutorial Sample App
1. Create a new project.
Go to File Menu and Create a New Project, fill information like project and package name after that click Next and select target SDK and Finish
2. After that add below dependency in build.gradle.
// 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, the butterknife is injected of view in the java file.
3. Open activity_main.xml and add two Button like below code.
While creating project Android Studio is provide default template like
<?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" android:background="@drawable/bg_design" tools:context=".MainActivity" > <Button android:id="@+id/buttonPlayUrlVideo" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" 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_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="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
Open 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: showDialogPrompt(); break; case R.id.buttonPlayDefaultVideo: Intent mIntent = ExoPlayerActivity.getStartIntent(this, VideoPlayerConfig.DEFAULT_VIDEO_URL); 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); getSupportActionBar().hide(); 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
// End Activity Here Intent mIntent = ExoPlayerActivity.getStartIntent(this, VideoPlayerConfig.DEFAULT_VIDEO_URL); startActivity(mIntent);
4.3. – Button for playing its own video by entering video URL. So taking user URL we have created
Just create a layout file for dialog view. In res, folder create file named dialog_prompts.xml and add few components
<?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="wrap_content" android:padding="@dimen/default_item_padding" > <TextView android:id="@+id/textView1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:text="Type Your Url" android:textAppearance="?android:attr/textAppearanceLarge" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/editTextDialogUrlInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView1" > <requestFocus /> </EditText> </android.support.constraint.ConstraintLayout>
Open again MainActivity.java and put this code for creating dialog
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(); }
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 { //Minimum Video you want to buffer while Playing public static final int MIN_BUFFER_DURATION = 3000; //Max Video you want to buffer during PlayBack public static final int MAX_BUFFER_DURATION = 5000; //Min Video you want to buffer before start Playing it public static final int MIN_PLAYBACK_START_BUFFER = 1500; //Min video You want to buffer when user resumes video public static final int MIN_PLAYBACK_RESUME_BUFFER = 5000; public static final String DEFAULT_VIDEO_URL = "https://androidwave.com/media/androidwave-video-exo-player.mp4"; }
7. Create a layout file name with activity_exo_player.xml
Create layout file inside res-> layout-> activity_exo_player.xml. and some components Progressbar(buffering indicator) and ExoPlayerView
<?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_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="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_marginLeft="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.support.constraint.ConstraintLayout>
8. Same as Create ExoPlayerActivity.java
Go to create an Activity with ExoPlayerActivity.java and set content layout is activity_exo_player.xml. 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;
8.1. Define some variables and do some fullscreen configuration
private static final String TAG = "ExoPlayerActivity"; private static final String KEY_VIDEO_URI = "video_uri"; String videoUri; SimpleExoPlayer player; Handler mHandler; Runnable mRunnable;
8.2. We are using vector icon so enable
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
8.3. Now initialise video player configuration
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 override unimplemented methods
Just implement Player.EventListener you have to implement override methods. onPlayerStateChanged() methods will return all player state.
@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, The full activity (ExoPlayerActivity.java) code looks like this
Mainly we are doing below things in this activity
- Initialize Player with configuration .
- Prepare source files and settings on the player.
- Add Player Event listener for listening to different player state
- Manage player state with activity life cycler eg.
pausePlayer (), resumePlayer(),releasePlayer ()
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() { } }
Conclusion
Wow! Great job! You just finished the ExoPlayer android example. You learned about integrating ExoPlayer in your application.
Download Sample Project- Video Streaming ExoPlayer in Android
If you have any queries, feel free to connect us.
14 Comments
Im getting this error trying to play a video:
Source error.
com.google.android.exoplayer2.source.UnrecognizedInputFormatException: None of the available extractors (MatroskaExtractor, FragmentedMp4Extractor, Mp4Extractor, Mp3Extractor, AdtsExtractor, Ac3Extractor, TsExtractor, FlvExtractor, OggExtractor, PsExtractor, WavExtractor) could read the stream.
how to add video quality option?
i try..and its buffering all video ..
i try with video 42 secons.. take a while to start and buffer all.. and a video with 15s.. take a while too.. but buffering all
i want to play fast and will buffering..
how can i do that ?
// //Minimum Video you want to buffer while Playing
// public static final int MIN_BUFFER_DURATION = 3000;
// //Max Video you want to buffer during PlayBack
// public static final int MAX_BUFFER_DURATION = 5000;
// //Min Video you want to buffer before start Playing it
// public static final int MIN_PLAYBACK_START_BUFFER = 1500;
// //Min video You want to buffer when user resumes video
// public static final int MIN_PLAYBACK_RESUME_BUFFER = 5000;
this not work.
I am getting only the sound when i try to play the video, no video is displaying.Any help???
this is due to .ConstraintLayout in exo_player.xml use android.support.constraint.ConstraintLayout in xml
Can u pls provide the code for ic_cross_circular_button_outline ??
download full source
How to enable full screen in exo player?
Hi Sir,
How can i implement dash formate(.menifest) using exoplayer in android.
Hello sir, when i rotate screen video start playing from start, i want to play the video from it’s current state while rotating screen. How can i do? Thank You
Hi, I am beginner in using exoplayer, How to change Http method of exoplayer from GET to POST. Thank you!
the video URL should be accessable.
import com.google.android.exoplayer2.ui.PlayerView; does not exist ?
ensure you have added the dependency in build.gradle correctly