13.1: Simple media playback

Contents:

A media playback app is an app that plays audio or video. If you want to play media in your app you have several options:

  • Send an implicit intent to another app, such as the YouTube app or Google Play app.
  • Use a simple video player view to embed a video sample in your activity layout.
  • Use a media player, such as the open source ExoPlayer library or the Android platform's MediaPlayer class, to embed audio or video playback in your app.
  • Implement a complete audio or video media app that plays media and also interacts with other system functions.

This chapter gives you an overview of each of these ways to play media in your app.

Media formats and sources

To play audio or video in your app with any media player, you need a source of playable content, and that content needs to be in a format that the player can read.

The media source is where your app gets its media. Media files may be embedded directly in your app, or the files may be in storage local to the device, such as an SD card. Your app may play (stream) media files from a location on the internet such as YouTube or Google Music, or from some other web-server location you choose.

The media format is important if your app is playing embedded audio or video that you provide. The format can mean different things:

  • The format can mean the format or encoding of the individual media samples. This is often called the sample format or codec , and it determines how the media sample has been encoded or compressed. Common audio formats include MP3 or AAC. Video formats include H.264 and MPEG-4. Note that a video file contains media in at least two formats: one for the video and one for the audio track.
  • The format can mean the format of the container that holds the media content plus associated metadata. These are called container formats . An encoded media file has a single container format, for example, MPEG-4. The container format is commonly indicated by the file extension, for example .mp4. For some audio-only formats such as MP3, the sample format and container format are the same.

Finally, some media may also be available through adaptive streaming technology such as DASH, Smooth Streaming or HLS. Media may be protected by digital rights management (DRM) technologies such as Widevine or PlayReady. These are very advanced topics for media playback, and they are not covered in this course.

Supported Media Formats describes the supported codecs and containers for the core Android platform. The ExoPlayer library supports all of these formats as well as software-based decoder extensions for other formats. See Supported formats for more details on ExoPlayer format support.

Playing media with intents

The easiest way to play audio or video in your app is to send an implicit intent to the Android system. In the intent, you include the URI of the sample to play. As with all implicit intents, if an app that can handle that media type exists on the device, the Android system launches that app and plays the URI you requested. The URI can be a media file located on the device (in internal or external storage) or the URL or a media file stored on a service such as YouTube.

For example, this code launches the YouTube app (or some other device app that can play YouTube videos) to play the video specified by the URL https://www.youtube.com/watch?v=LBBqTd6uOd4

Intent mediaIntent = new Intent(Intent.ACTION_VIEW);
mediaIntent.setData(Uri.parse(
   "https://www.youtube.com/watch?v=LBBqTd6uOd4");
if (mediaIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(mediaIntent);
}

See Play a media file on the Common Intents page for more information about audio and video intents.

Note: To embed a YouTube video in your activity layout, you can use the YouTube API and player library. See YouTube Player API for more details.

Choosing a media player

If you play media inside your activity rather than launching another app, generally your media playback activity has two parts:

  • A media player that takes digital media in and renders it as video, audio, or both. The media player may or may not include a view to actually display video in your app.
  • A user interface (UI) with transport controls (play, pause, rewind, fast-forward, etc.) to run the player and optionally display the player's state.  Media apps include a UI and a player

One of the first decisions to make as you develop a media app is to choose a media player. You have several options:

  • For very simple video playback, you can use a VideoView. The VideoView class combines a media player (MediaPlayer) with a surface (SurfaceView) that you can place in your layout like any other View. See Using VideoView for details on how to use VideoView in your app.
  • MediaPlayer, part of the Android media APIs, provides the functionality for a bare-bones player that supports the most common audio/video formats and data sources. VideoView is a wrapper for the MediaPlayer class.
  • The YouTube Player APIs provide a media player and a view specifically for playing YouTube videos. See YouTube Player API for more details.
  • ExoPlayer is an open source library that builds on lower-level Android media components and APIs. ExoPlayer supports advanced features, such as adaptive streaming, that are not available in the core MediaPlayer APIs. ExoPlayer is the best solution for developers and most media apps, although you can only use ExoPlayer with Android version 4.1 and higher. See ExoPlayer API for more details.
  • You can also build a custom media player from the low-level media APIs such as MediaCodec, AudioTrack, and MediaDrm. Building a custom media player is an advanced topic that this course doesn't explore. For most developers, ExoPlayer provides a better option, even for complex media-playback apps.

The customizability of ExoPlayer makes it the preferred choice for most media apps. ExoPlayer supports many formats, and it has extensible, modular features, which makes it useable in almost every case.

Using VideoView

The fastest and simplest way to embed video inside your app is to use a VideoView, part of the Android platform. The VideoView class combines a basic media-player implementation (the MediaPlayer class) with a SurfaceView to display the video in a layout. The VideoView class implements a lot of the basic behavior you need to play a video in your app. You need to handle the UI for the video playback, and add behavior relating to the activity lifecycle.

To control the video playback, Android provides the MediaController view class in the android.widget package. MediaController provides a set of common media control buttons with the ability to control a media player (such as the VideoView object.)

You can use these two classes to build a very simple video-playback app.

Adding a VideoView to your layout

Add a VideoView to the layout for your activity just like any other view:

<VideoView
   android:id="@+id/videoview"
   android:layout_width="0dp"
   android:layout_height="0dp"
   android:layout_margin="8dp"
   app:layout_constraintDimensionRatio="4:3"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintEnd_toEndOf="parent"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintTop_toTopOf="parent"/>

Android shrinks or stretches the VideoView to fit the videos's width and height constraints. The layout_constraintDimensionRatio in this example can help ensure that the VideoView expands to fit the available space but also retains the original video dimensions without distorting the video horizontally or vertically.

Setting the media source

The media source for the VideoView is the location of the video content to be played. This location is specified by a Uri object representing a Uniform Resource Identifier. The URI can be:

  • The location of a video file embedded in your app as an Android resource.
  • The location of a file available on external storage such as an SD card.
  • The URL of a media file located on a web server on the internet.
  • A URI contained in a database, and supplied by a content provider you create.
  • Any other location that can be specified by a URI.

Use the VideoView.setVideoURI() method to indicate the source of the video to play:

String mediaName = "videofile";
Uri videoUri = Uri.parse("android.resource://" + getPackageName() +
       "/raw/" + mediaName);

mVideoView.setVideoUri(videoUri);

In this example, the video file is embedded in your app's resources, in the res/raw folder. Although the filename in your app may have an extension (videofile.mp4), the resource name does not include that extension.

To use a video file stored on the device's external storage (such as an SD card), use the Environment class (to get the path to the external storage) and a File object. Then convert that path specifier into a URI object:

String mediaName = "videofile.mp4"; 
String fullPath = Environment.getExternalStorageDirectory() + "/" + mediaName;
File file = new File(fullPath); 
Uri videoUri = Uri.fromFile(file);

mVideoView.setVideoUri(videoUri);

To access external storage, you need to include the following permission in your Android manifest:

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

To download a video file located on the internet, use the full URL as the location:

String mediaName = "http://myserver.com/videofile.mp4";
Uri videoUri = Uri.parse(mediaName);

mVideoView.setVideoUri(videoUri);

To access the internet, you need the internet permission in your Android manifest:

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

For video files streamed from the internet, the VideoView class handles the network connections and background threads for you, and buffers the video as it downloads.

Adding a media controller view

UI elements to control media are familiar to most users. They include buttons for play/pause, rewind, and fast-forward, and often include a slider to enable skipping ("scrubbing") to specific times within the media playback. These controls are usually referred to as transport controls .

You can implement your own transport controls with Android buttons, and connect the on-click events to methods in VideoView or some other media player. However, the Android platform provides the MediaController class in the android.widget package. Using MediaController is an easy way to implement the common media-player buttons, and to control an underlying media player such as a VideoView.  Video player app with transport controls (<code>MediaController</code>)

To use the media controller, you do not define it in your layout as you would other View elements. Instead, instantiate it programmatically in your app and then attach it to a media player (such as a VideoView).

This code shows how to initialize a media controller in your activity's onCreate() method, and connect it to a VideoView:

MediaController controller = new MediaController(this);
controller.setMediaPlayer(mVideoView);

mVideoView.setMediaController(controller);

You must connect the controller to the media player, and the media player to the controller. You do this so that the controller can control the media in response to user interaction, and the media player can update the state of the controller as the media downloads or plays.

By default, the media controller and its buttons are hidden from the user. Tapping the video view makes the controller appear and enables the user to control the media. After several seconds (three is the default), the controller disappears again.

The media controller by default attaches to the bottom of the screen and floats above the existing layout. To tell the media controller which specific layout element in your app to attach to, use the MediaController.setAnchorView() method.

Controlling playback

The media controller enables the user to control media playback. In addition, you can use these methods on the VideoView object to programmatically control media playback in response to other events in your app (such as lifecycle events):

Method What it does
start() Starts playback.
pause() Pauses playback.
suspend() Releases the media player and other system resources.
stopPlayback() Shortcut for pause() and suspend().
seekTo() Skips forward or backward in the playback to the location indicated by the number of milliseconds.
getCurrentPosition() Returns the number of milliseconds that have elapsed in the playback.
isPlaying() Boolean indicating whether the media is currently playing.

Releasing resources and retaining media player state

Media playback apps use a large number of device resources compared to other apps. For this reason, it is a best practice for your app to completely release its media-player object and other media resources as soon as possible in the app's lifecycle. That includes when your app is destroyed (such as in a configuration change), but also when the onStop() method has been called and the app is paused in the background. Every time your app is not visible on the screen, you should release your media resources. Every time your app comes to the foreground, you should set up and load your media as if the app were running for the first time.

Note: If your media app explicitly plays audio in the background (such as with a music player), you must implement media playback as a service and interact with other apps that play audio.

With VideoView, you can use the stopPlayback() method to stop video playback and release the media resources.

@Override
protected void onStop() {
   super.onStop();

   mVideoView.stopPlayback();
}

When you release all player objects and other resources for your media, make sure to retain the status of the media playback, such as the title currently being played, or the current playback position. Then when the app returns to the foreground you can reload your media and skip ahead to where the user left off.

You can retain the playback information to the instance state bundle as you would any other instance state, by implementing onSaveInstanceState(). Use the getCurrentPosition() method to get the current playback position for the media, in milliseconds:

@Override
protected void onSaveInstanceState(Bundle outState) {
   super.onSaveInstanceState(outState);

   outState.putInt(PLAYTIME_TAG, mVideoView.getCurrentPosition());
}

Then in your onCreate() method or onRestoreInstanceState() method, you can restore that value from the instance bundle:

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

   //... 

   if (savedInstanceState != null) {
       mCurrentPosition = savedInstanceState.getInt(PLAYTIME_TAG);
   }

  // ...
}

Use the seekTo() method to move the playback to the saved position when the media is loaded and ready to play:

if (mCurrentPosition > 0) {
   mVideoView.seekTo(mCurrentPosition);
}

mVideoView.start();

Using listener callbacks

The VideoView class (and the MediaPlayer it contains) lets you define listeners for several media playback-related events. For example:

  • The media has been prepared (that is, downloaded, buffered, uncompressed) and is ready to play.
  • An error has occurred during media playback.
  • The media source has reported information such as a buffering delay during playback.
  • The media has completed playing.

Listeners for the preparation and completion events are the most common listeners to implement in a media app.

The onPrepared() callback

The preparation step in your media player is the interval between when you set the media source, and when that media is ready to play. Preparing local media files may only take a second or two. For downloaded or streamed media it may be much longer, as the data has to be downloaded and buffered over a potentially slow network.

A preparation listener and the onPrepared() callback enable you to provide information to the user (such as a "Loading..." message) while preparation takes place. When the callback is invoked to indicate that the media is ready to play, you can remove that message and play the media.

You can define your media listeners in onCreate() or any time you set up your media. For VideoView, use the setOnPreparedListener() method with a MediaPlayer.OnPreparedListener object to define the onPrepared() callback. Implement this callback to add code to be executed once the media has been prepared:

mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
   @Override
   public void onPrepared(MediaPlayer mediaPlayer) {
       mLoadingTextView.setVisibility(View.INVISIBLE);

       if (mCurrentPosition > 0) {
           mVideoView.seekTo(mCurrentPosition);
       }

       mVideoView.start();
   }
});

In this example, once the media has been prepared, the following occurs:

  • A text view that contains a "Buffering..." message is hidden.
  • If the current position has been retained (and is thus greater than 0), move the playback position to that location.
  • Start playing the video.

The onCompletion() callback

When the media playback is finished, the completion event occurs, and the onCompletion() callback is called. You might use the completion listener to provide a link to the next media file to play, or to reset the playback position to the beginning.

Use the VideoView.setOnCompletionListener() method with a MediaPlayer.OnCompletionListener object to define the onCompletion() callback. Add the code to be executed once the media has completed to this method:

mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
   @Override
   public void onCompletion(MediaPlayer mediaPlayer) {
       Toast.makeText(MainActivity.this, "Playback completed",
               Toast.LENGTH_SHORT).show();
       mVideoView.seekTo(0);
   }
});

In this example, once the media has finished playing:

  • A toast appears with the message "Playback completed."
  • The playback position is returned to the beginning, 0 milliseconds.

YouTube Player API

The YouTube Android Player API enables you to incorporate video-playback functionality into your Android apps, versus relying on the external YouTube app. The API defines methods for loading and playing YouTube videos and playlists, and for customizing and controlling the video-playback experience.

Using the API, you can load or cue videos into a YouTubePlayerView object embedded in your app's UI. You can then control playback programmatically. For example, you can play, pause, or seek to a specific point in the currently loaded video.

You can also register event listeners to get callbacks for certain events, such as the player loading a video or the player state changing. Finally, the API has helper functionality to support orientation changes as well as transitions to fullscreen playback.

See the YouTube Android Player API for more information on the player and sample apps.

ExoPlayer API

ExoPlayer is an open source media player for Android. It provides an alternative to the Android MediaPlayer API for playing audio and video both locally and over the internet. ExoPlayer supports many media formats and features not currently available in the Android platform media APIs, including adaptive streaming playback with DASH and SmoothStreaming, as well as various forms of media digital rights management (DRM). ExoPlayer is also straightforward to customize and extend, which makes it the preferred choice for most media apps.

If you want to create a media-player app of any complexity on Android, ExoPlayer is the recommended starting place. See the ExoPlayer developer guide or ExoPlayer project for more information.

Architecture for complex media apps

For more complex media player apps, including those whose main purpose is to play media, it is a best practice to ensure that your app plays well with the overall Android platform ecosystem. This practice is especially important for music-player apps, because users often expect music-player apps to play even when the app is not visible on screen, and users expect to control music-player apps from hardware devices such as headset buttons, or through Android Auto.

To integrate with the Android platform, the Android media APIs include a number of classes to abstract various aspects of media playback between your app and the system, including media sessions, media controllers, and media browsers. You implement these classes in addition to and alongside of your app's transport controls and media player.

Note: The Android v4 support library includes "Compat" versions of the classes described in this section, in the Media-Compat support library. As with all Android apps, it is a best practice to use "Compat" classes in your app whenever possible. Throughout this section, assume that all class references use the compat version. For example, a media session is defined by the MediaSession class, and the compat version of the class is MediaSessionCompat.

Media session

A media session is a layer between your media player, the rest of your app, and the Android media ecosystem. The media session is defined by the MediaSession class, and it maintains a representation of the media player's state and information about what is playing. That information can be queried by your app or by other apps that can play audio, such as Android Auto.

You create a single media session for your app usually at the same time you create the media player. The player is only called from the media session that controls it.  Media session

Media controller

The media controller you learned about earlier in this chapter is a view with transport controls that you attach directly to a media player, usually a VideoView. That MediaController class is part of the android.widget package.

For more complex media apps there is another kind of MediaController in the android.media.session package. This kind of MediaController has no visible interface, and it serves as the layer between the media session and your activity's UI.  Media controller

The media controller isolates your UI from other parts of your app, including the player. Your UI code communicates only with the media controller. The media controller translates UI control actions (play, pause, fast-forward, rewind, skip back or ahead) into callbacks to the media session. It also receives callbacks from the media session whenever the session state changes, to enable your app to update the state of the UI.

Often, more than one media controller controls the media session. In addition to your app's media controller, other media controllers could be cars (Android Auto), watches (Android Wear), the media buttons on your headphones, notifications on your screen, or even other apps, which might fade out your audio to deliver notifications to the user.

Media browser client and service

In addition to the media session and controller, the Android media platform defines a client/server architecture for discovering and playing media from outside your app. This architecture is mostly useful for audio or music playback apps. For example, you might use it to enable apps such as Android Auto or Wear OS to browse and control music that your app provides.  Media browser client and service

The server part of this architecture is defined by the MediaBrowserService. It provides two main features:

  • When you use a MediaBrowserService, other components and apps can discover your service, create their own media controller, connect to your media session, and control the player. This is how Wear OS and Android Auto apps gain access to your media app.
  • MediaBrowserService also provides an optional browsing API. Apps don't have to use this feature. The browsing API lets clients query the service and build out a representation of the service's content hierarchy. The content hierarchy might represent playlists, a media library, or some other kind of collection.

The MediaBrowserService contains the media session and the media player and manages connections from client. It is also, as the name implies, an Android Service, which can run in the background independently of your app's Activity.

The media browser client is defined by the MediaBrowser class. In your playback activity you create the media browser object and use it to connect to the media browser service. The media browser also provides callbacks to manage the connection with the service and to keep the UI in sync when the playback state changes.

Implementing video apps

A video app needs a window for viewing content. Usually, the Activity in which the user plays video remains visible for the entire time the user is watching (either as a full-screen app or in multi-window or picture-in-picture mode).

For this reason, you usually implement the components of the media architecture in a video app as a single Android Activity. That activity contains a media controller, a media session, and a media player. The app's UI interacts with the controller, which sends callbacks to the media session, which controls the player. Changes to the player state are communicated back through the session to the controller, and from the controller to the UI. The screen on which the video appears is part of the activity.  A typical video app structure

Video apps do not generally use the media browser client or server, because all of the playback takes place in a single activity, and the user generally watches video with the app visible on the screen.

See Building a Video App for more information on this architecture.

Implementing audio apps

An audio player does not always need to have its UI visible. Once it begins to play audio, the player can run as a background task or service. The user can switch to another app while listening to the audio in the background. To implement this design in Android, build your audio app in two parts:

  • A media browser service (MediaBrowserService) that holds the media session and the player. The media browser can also provide media lists for browsing.
  • An activity for the app's UI. The activity contains the media controller and uses a MediaBrowser to be a client of the media browser service.

If you factor the two parts of an audio app into separate components in this way, then the service and the audio playback can run in the background when the user switches to another app.  A typical audio app structure

See Building an Audio App for more information on this architecture and implementation.

The related practical documentation is Playing video with VideoView.

Learn more

Android developer documentation:

Android API reference:

Other:

results matching ""

    No results matching ""