Playing Video

Intro

Like many things Android, there is more than one way to go about playing video in your apps.  This lesson will introduce you to two methods.  The first involves using the MediaPlayer.  Luckily, you are already familiar with the MediaPlayer, because it's the same one you used to play audio in the previous lesson on MediaPlayer.  The second method introduces a new View type, called the VideoView, specifically designed for playing video easily.

We will learn how to play video by creating a simple app the uses both the MediaPlayer and the VideoView (but not at the same time!).  As you work through the lesson, be sure to take the time to understand what each block of code is doing before proceeding to the next section.  

The code for the final, working version of the app we will build together is available at the link below.  However, you should avoid looking at this code until you have completed the app yourself.  This is for reference only:

VideoExample App 

Sample App Setup

Before we start working with video, let's set the framework in place for our sample app.  Our app will have a Main activity with two buttons.  One button, when clicked, will launch a new Activity that uses the MediaPlayer to play a video.  Clicking the second button will launch a new Activity that uses the VideoView to play a video.

Step 1:

Create a new project in Android Studio labeled VideoExample.  Create a Blank Activity named MainActivity.  Leave all other settings as default. 

Step 2:

Open the activity_main.xml layout file and modify it so that it contains two buttons.  Notice the onClick attribute of each button.  You must ensure that you use the exact method names in your MainActivity.java file.  The rest of this lesson will use the method names that I use in the sample code below:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#000000"
    tools:context=".MainActivity">

    <Button
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:text="MediaPlayer Example"
        android:layout_weight="0.30"
        android:background="#f8f8f8"
        android:textColor="#444444"
        android:layout_margin="5dp"
        android:onClick="launchMediaPlayer"/>

    <Button
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:text="VideoView Example"
        android:layout_weight="0.30"
        android:background="#f8f8f8"
        android:textColor="#444444"
        android:layout_margin="5dp"
        android:onClick="launchVideoView"/>

</LinearLayout>

Step 3:

Now that your layout file is in order, we'll focus on the MainActivity.java file.  We only need to make a couple additions.  Specifically, we need to add the methods that we referenced in the onClick attributes of our layout.  Each of these methods is responsible for launching a new Activity, which we have yet to create.  Ignore the IDE error messages for now.  We'll fill in the blanks as we proceed.

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void launchMediaPlayer(View v){
        Intent i = new Intent(this, MediaPlayerActivity.class);
        startActivity(i);     
    }

    public void launchVideoView(View v){
        Intent i = new Intent(this, VideoViewActivity.class);
        startActivity(i);
    }
}

Step 4:

The last step is to put the proper permissions in the manifest file.  Our sample app will be streaming video from the internet, so we will need the Internet permission:

<uses-permission android:name="android.permission.INTERNET" />

That's it!  Now we're ready to start adding the video playing capability.

MediaPlayer

The first method we'll implement to play video uses the MediaPlayer.  Accomplishing this is very similar to the way we played audio, so we'll focus on the differences.  Be sure to go back and review the previous MediaPlayer lesson if you need a refresher.

Step 1:

Create a new Blank Activity called MediaPlayerActivity. Ensure the proper entry has been placed in the manifest file for this Activity:

<activity
    android:name=".MediaPlayerActivity"
    android:label="@string/title_activity_mediaplayer" >
</activity>

Step 2:

In the MediaPlayerActivity's layout file, we'll add a new View, called a SurfaceView. This powerful class handles drawing things on the screen, and has tons of other applications outside of video.  We will use it a holder for our video on the screen:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MediaPlayerActivity">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</RelativeLayout>

Step 3:

The rest of our work needs to be done in Java, so go ahead and open the MediaPlayerActivity.java file.  We will need to use a handful of class variables, so go ahead and add the following to the top of the class (above onCreate).  We'll review each variable as we need it:

private MediaPlayer mediaPlayer;
private SurfaceHolder videoHolder;
private SurfaceView videoSurface;
String videoAddress = "http://www.pepinonline.com/mcmap/green/sidechoke.mp4";

 

In the onCreate() method, we'll instantiate the SurfaceView that we defined in the layout file:

videoSurface = (SurfaceView) findViewById(R.id.surfaceView);

 

Now that we have a SurfaceView, we need to get a SurfaceHolder.  The SurfaceHolder is the object that is responsible for "holding" the video that we will play:

videoHolder = videoSurface.getHolder();

 

The SurfaceHolder object has a corresponding callback feature, which we can use to keep track of the state of the SurfaceHolder.  In order to use this feature, our Activity must implement the SurfaceHolder.Callback interface:

public class MediaPlayerActivity extends ActionBarActivity implements SurfaceHolder.Callback {

 

The Callback interface provides three methods that you must implement:

// Surface Holder Callback methods
@Override
public void surfaceCreated(SurfaceHolder holder) { }

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }

@Override
public void surfaceDestroyed(SurfaceHolder holder) { }

Step 4:

At this point you're probably asking yourself where the MediaPlayer is.  Wait no longer, it's time to add it!  Because we must wait for the SurfaceHolder to be created before we can use the MediaPlayer, it seems that the appropriate place to put out MediaPlayer creation code is in the surfaceCreated() callback method.  We will create our MediaPlayer exactly like we did previously, with one change.  Instead of calling the setAudioStreamType() method, we'll call setDisplay() method.  We'll set the MediaPlayer to use out SurfaceHolder as the display area...which is why we have to wait for it to be created:

@Override
public void surfaceCreated(SurfaceHolder holder) {
    try {
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setDisplay(videoHolder);
        mediaPlayer.setDataSource(videoAddress);
        mediaPlayer.prepare();
        mediaPlayer.setOnPreparedListener(this);
    }
    catch(Exception e){
        e.printStackTrace();
    }
}

Notice that aside from that one line change, the MediaPlayer works just like before.  The videoAddress String passed in setDataSource() is the URL we defined as a class variable earlier.

We'll have our video begin playing as soon as it is prepared, so you'll need to implement the MediaPlayer.OnPreparedListener.  In the onPrepared method, we will tell the MediaPlayer to start playing:

public class MediaPlayerActivity extends ActionBarActivity 
               implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener {
// MediaPlayer OnPreparedListener method
@Override
public void onPrepared(MediaPlayer mp) {
    mediaPlayer.start();
}

The last thing we need to do is ensure the resources used by the MediaPlayer are deallocated properly.  For this sample app, we'll do so in the onDestroy method:

@Override
public void onDestroy(){
    super.onDestroy();
    
    if(mediaPlayer != null){
        if(mediaPlayer.isPlaying()){
            mediaPlayer.stop();
        }
        mediaPlayer.release();
        mediaPlayer = null;
    }
}

That does it.  Your MediaPlayerActivity will now play the video automatically when the Activity is launched and the SurfaceHolder is created.  All the other methods of MediaPlayer are still applicable, like stop() and pause().  You would need to add the proper buttons to your layout so the user has those controls.  Because we've already done that in the audio lesson, I'll leave that as an exercise for you.

VideoView

The VideoView class provides a simple way for you to add video playback to your app without having to go through the process of creating a SurfaceView, SurfaceHolder or MediaPlayer.  Another great thing about the VideoView class is that you can also add standard media controls (stop, start, pause) by using a MediaController which handles much of the proper lifecycle transitions for you.  It's also important to note that the VideoView class is subclass of SurfaceView that serves as a wrapper for MediaPlayer, so when you use the VideoView, you're doing the same thing we did above (from a technical standpoint) with a lot less code! 

Step 1:

Create a new Blank Activity called VideoViewActivity. Ensure the proper entry has been placed in the manifest file for this Activity:

<activity
    android:name=".VideoViewActivity"
    android:label="@string/title_activity_video_view" >
</activity>

Step 2:

You create a VideoView as you would any other View.  Our sample app will also also use an indeterminate progress bar so the use knows when the video is loading.  Add the VideoView and ProgressBar elements to the VideoViewActivity's layout file:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#000000"
    android:layout_gravity="center"
    tools:context=".VideoViewerActivity" >

    <VideoView
        android:id="@+id/video_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:gravity="center" />
    
    <ProgressBar 
        android:id="@+id/progressBar"
        android:indeterminate="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:maxWidth="60dp"
        android:maxHeight="60dp" />
    
</RelativeLayout>

Step 3:

Once your layout file is set up, we'll need to make changes to the VideoViewActivity.java file.  First, we'll need to define a few class variables:

VideoView vv;
String path="http://www.pepinonline.com/mcmap/tan/rearchoke.mp4";
int position;

ProgressBar progBar;

The position integer variable will be used later to jump to a different point in the video.

Inside the onCreate() method, we'll instantiate the VideoView

vv = (VideoView) findViewById(R.id.video_content);

Uri uri=Uri.parse(path);
vv.setVideoURI(uri);

vv.setMediaController(new MediaController(this));

vv.requestFocus();
vv.start();

Notice that we have passed it a Uri created from the URL String defined as a class variable.  We have also told the VideoViewer to use a MediaController.  This will add all the standard media playback buttons to our Activity.  Finally, we told the VideoView to begin playing the video.

Step 4:

In order to be more user friendly, our sample app will show an indeterminate progress bas (spinning wheel) until the video had loaded.  To do this, we will allow the ProgressBar that we defined in our layout to display until video playback starts, and then we will hide it by setting the visibility to View.GONE.  

To detect whenthe VideoView is ready to play video, and thus, when we should hide the ProgressBar, we use an MediaPlayer.OnPreparedListener:

vv.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

    public void onPrepared(MediaPlayer mp) {
        // hide the spinning wheel
        progBar.setVisibility(View.GONE);
    }

});

 Another feature we'll add for user friendliness is the proper handling of errors during playback.  We can use the MediaPlayer.OnErrorListener for this.  Our sample app will simply call finish() if a playback error occurs:

vv.setOnErrorListener(new MediaPlayer.OnErrorListener() {

    @Override
    public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
        Toast.makeText(getBaseContext(),
                "An Error Occurred.  Please Try Again.",
                Toast.LENGTH_SHORT).show();
        finish();
        return false;
    }

});

Step 5:

The last thing we need to do is properly handle Activity lifecycle changes.  We want our video to pause when the Activity is not in the foreground, and resume at the same spot when the Activity resumes.  We'll use the corresponding Activity lifecycle methods to take care of that:

@Override
protected void onPause() {
    super.onPause();
    if(vv.isPlaying()){
        vv.pause();
    }
    position = vv.getCurrentPosition();
}

@Override
protected void onResume() {
    super.onResume();
    if(position > 0) {
        vv.seekTo(position);
        progBar.setVisibility(View.VISIBLE);
    }
}

Notice that when we pause the VideoView, we save the current position.  When the Activity resume's, we tell the VideoView to move to that point in the video.  Once the VideoView has moved the playback cursor to the indicated position, the OnPreparedListener is triggered, and the ProgressBar is hidden again.

 

Task: Create a new Android project and incorporate the above code into a working app.