# Player Basics

This chapter explains the steps required to create a custom player `Activity` and start playback manually or using an Intent. The chapter follows closely the examples demonstrated in the `demos` application (see Demos).

## Activity and Layout

The SDK comes with a custom view component: the `PlayerView`. You will need to add this view to your Activities layout. The Activity that hosts the view can be any Android Activity or Fragment implementation. However, if you want to support orientation changes, where the user can turn the device, consider adding `orientation` to the Activities `configChanges` setup in your Manifest. With that setting in place, you need to handle the orientation change manually. The Activity will not be recreated by the system. However, this will allow you to continue playback without any interruptions.

In addition to the `orientation` configuration, you should also consider using a custom theme for your player Activity in order to be able to properly customize the behavior of the application’s toolbar and the systems status bar.

We can start implementing a custom player Activity using the following entry in the Manifest:

    <activity
        android:name=".SimplePlaybackDemo"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:label="@string/title_activity_simple_playback_demo"
        android:theme="@style/PlayerTheme"
        android:parentActivityName=".MainActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.castlabs.sdk.demos.MainActivity"/>
    </activity>

Here, we are implementing a custom Activity `SimplePlaybackDemo` in our application package and setting the `configChanges` accordingly to support orientation changes while playback continues.

Our demo application leverages Googles Material Design theme and the extended theme of our player activity consists of only one entry:

    <style name="PlayerTheme" parent="AppTheme">
        <item name="colorPrimaryDark">@color/black</item>
    </style>

We extend the application’s theme and change the primary color to black. This ensures that the status bar is rendered with a black background in Android M.

The layout of the demo Activity contains only one component: the `PlayerView`. The layout file looks like this:

    <FrameLayout 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:background="#000000"
                 tools:context="com.castlabs.sdk.demos.SimplePlaybackDemo">

        <com.castlabs.android.player.PlayerView
            android:id="@+id/player_view"
            android:layout_gravity="center"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </com.castlabs.android.player.PlayerView>
    </FrameLayout>

We are using a simple `FrameLayout` and adding the `PlayerView` as its primary centered component.

## Starting Playback

Now that we have the Activity in place and a `PlayerView` added to its layout, we can already start playback. All interactions with the player happen through a `PlayerController` instance. The `PlayerView` exposes a controller through its `PlayerView.getPlayerController()` method. You can use the player controller for example, in the Activity’s `onCreate()` callback:

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

        // Get the view components from the layout
        playerView = (PlayerView) findViewById(R.id.player_view);
        // Get the player controller
        PlayerController controller = playerView.getPlayerController();
        // Start playback of a clear stream at position 0
        controller.open(new PlayerConfig.Builder("http://sample.url/manifest.mpd").get());
    }

Here, we first get access to the PlayerView component. Then we use the component to access the PlayerController and send a `PlayerConfig` to the controller’s `open(PlayerConfig)` method. Please note that it might be more convenient to use the Intent extra data to send a `Bundle` to the `PlayerController` to configure it and start playback (see Starting Playback from an Intent).

The example above will already be enough to start playback, but we are missing a few mandatory callbacks to ensure that the Activity behaves correctly when the user leaves it through the back button or switches applications and comes back to the activity.

## Starting Playback from a `PlayerConfig`

As demonstrated before, one way of starting playback is through a `PlayerConfig` object. An instance of this object can be attained through its `Builder` subclass.

In order to instantiate a Builder, the content url is required. This is the only mandatory setting to start playback.

You can explore the `Builder` to get a complete overview of all the possible configuration options.

The `PlayerConfig` object can be also converted to a `Bundle` and it implements the `Parcelable` interface.

At the same time, you can populate a `PlayerConfig` `Builder` with an existing `PlayerConfig` object or with a `Bundle` with the `fromConfig(PlayerConfig)` and `fromBundle(Bundle)` methods respectively.

## Starting Playback from an `Intent`

Instead of configuring the `PlayerController` programatically and passing a `PlayerConfig` to its `open` method, it can be more convenient to pack all required parameters into a `Bundle`. The bundle can then be sent the through an `Intent` and passed to the `PlayerController` `open(Bundle)` method to initialize and configure the controller and start playback. See the `API documentation` of the `open` method to get a list of all the supported bundle keys. The most important parameters are the following:

Key | Description
--- | ---
`SdkConsts.INTENT_URL` | Expects a `String` and denotes the URL to the content. This is a mandatory parameter
`SdkConsts.INTENT_START_PLAYING` | Expects a `boolean`. If `true` playback will start automatically once enough data is available.
`SdkConsts.INTENT_POSITION_TO_PLAY` | Expects a `long`. The playback start position in microseconds. (default: 0)
`SdkConsts.INTENT_DRM_CONFIGURATION` | Expects a `DrmConfiguration`. The DRM configuration for encrypted content. See DRMtoday Integration. (default: null)

The `API documentation` contains a complete list of supported intent parameters, including support for additional query parameters and side-loaded subtitle tracks.

With the support for bundles, you can now prepare the Intent, for example, in your main Activity and then start your player Activity already configured. Your main Activity might contain code like this to prepare the Intent:

    Intent intent = new Intent(this, PlayerActivity.class);
    intent.putExtra(SdkConsts.INTENT_URL, "...");
    intent.putExtra(SdkConsts.INTENT_POSITION_TO_PLAY, 0);
    intent.putExtra(SdkConsts.INTENT_START_PLAYING, false);
    startActivity(intent);

In the `PlayerActivity`, you could then take the intent parameters and pass them to the `PlayerController` to configure it and start playback:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the view components from the layout
        playerView = (PlayerView) findViewById(R.id.player_view);

        if (getIntent() != null) {
            try {
                playerView.getPlayerController().open(getIntent().getExtras());
            } catch (Exception e) {
                Log.e(TAG, "Error while opening player: " + e.getMessage(), e);
                ...
            }
        }
    }

Please note the `try/catch` block around the call to `open()`. This is required because the `open` method will check for mandatory parameters and their types and throw an exception if either a mandatory parameter is missing or a parameter type does not match. The player will then delegate all further data loading to a dedicated thread, so you should always setup the error handlers appropriately (see Error Handling) before the call to `open`.

### Playback Local Content

The player can be use to playback content that is stored on the device. For that, you need to specify the content URL with a file:// or content:// scheme prefix. The same applies also for side-loaded tracks.

## Mandatory Callbacks

In order to ensure that the player activity behaves correctly when it’s finished or the user leaves the application and comes back to the Activity, we need to overwrite a few callbacks to delegate the Activity’s life cycle events and shutdown or resume playback accordingly. This will also ensure that the Activity acquires and releases locks on the devices screen saver to ensure that the display stays on while a video is played.

The `PlayerView` exposes a `PlayerViewLifecycleDelegate` through its `PlayerView.getLifecycleDelegate()` method. The delegate instance can be used to ensure the correct behavior for basic Activity life cycle changes. We need to overwrite the following methods in the Activity implementation:

    @Override
    protected void onStart() {
        super.onStart();
        playerView.getLifecycleDelegate().start(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        playerView.getLifecycleDelegate().resume();
    }

    @Override
    protected void onStop() {
        super.onStop();
        playerView.getLifecycleDelegate().releasePlayer(false);
    }

Overwriting `onStart` and delegating to `start()` ensures that the required locks are acquired. The call to `resume()` ensures that playback will be resumed when the user comes back to the Activity after leaving the app. Finally, `releasePlayer()` is called in `onStop()` to save the current playback state and shutdown the player. This needs to be called to release any resources occupied.

Please note that up until now, no errors are handled. You need to setup the required error handlers appropriately (see Error Handling) to catch any runtime issues during download or playback.

## Background Playback

The SDK comes with a `PlayerService` implementation. The service can be used to send the player to the background continuing with audio-only playback and recover the player when coming back to the Activity.

Because a Bound-Service is used, the usage pattern slightly differs. Essentially, you will first need to connect to the service and postpone all usage of the `PlayerController` until the service is connected. The `demos` application contains a working example of the `PlayerService` integration. The main concepts are as follows.

The Activity layout and setup is the same. You still add the `PlayerView` to your layout and use it to access the `PlayerController`, but you have to wait until the `PlayerService` is connected and initialized.

    public class PlayServiceDemo extends Activity {
        private PlayerView playerView;
        private PlayerService.Binder playerServiceBinder;
        private final ServiceConnection playerServiceConnection = new ServiceConnection() {

            public void onServiceConnected(ComponentName name, IBinder service) {
                playerServiceBinder = (PlayerService.Binder) service;
                boolean backgrounded = playerServiceBinder.init(playerView);
                if (!backgrounded) {
                    playerView.getPlayerController().open(...);
                }
                playbackBundle = null;
            }

            public void onServiceDisconnected(ComponentName name) {
                playerServiceBinder.release(playerView, true);
                playerServiceBinder = null;
            }
        };

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_simple_playback_demo);
            playerView = (PlayerView) findViewById(R.id.player_view);
        }

        @Override
        protected void onStart() {
            super.onStart();
            playerView.getLifecycleDelegate().start(this);
        }

        @Override
        protected void onResume() {
            super.onResume();
            Intent serviceIntent = new Intent(this, PlayerService.class);
            bindService(serviceIntent, playerServiceConnection, Context.BIND_AUTO_CREATE);
        }

        @Override
        protected void onStop() {
            super.onStop();
            if (playerServiceBinder == null) {
                playerView.getLifecycleDelegate().releasePlayer(false);
            } else {
                if (!isFinishing()) {
                    playerServiceBinder.releaseToBackground(
                            playerView,
                            NOTIFICATION_ID,
                            createBackgroundNotification(),
                            true);
                } else {
                    playerServiceBinder.release(playerView, true);
                }
                unbindService(playerServiceConnection);
                playerServiceBinder = null;
            }
        }
        ...
    }

The differences in contrast to the non-service implementation are in the work we can do in `onCreate()` and how the life cycle callbacks are implemented.

In `onCreate()` we are not allowed to use the `PlayerController` or start playback. We need to wait for a service connection before we can get access to a valid and initialized `PlayerController`.

Because we are using a service, we are not delegating directly to the player view’s life cycle event in `onResume()`. Instead, we make sure that a connection to the player service is established. The binder implementation will then take care of initializing playback or to recovering from a background playback session.

The call to `playerServiceBinder.init(playerView)` in the service connection is mandatory to initialize the player view and its controller. After that call, the controller is ready to be used and we can start playback if we are not already recovering from a background session. This is also the place where you can add listeners to the `PlayerController`.

With the player service, we have to cover a few use cases on the `onStop()` callback implementation:

> 1. If we have no service connection, we delegate the view to release the player and any locks acquired when the activity started.

> 2. If `isFinishing()` returns `true`, we assume the user wants to actually leave the Activity and not start background playback. In that case we release the player through the binder implementation.

> 3. If the activity is not finishing, we send the player to the background using the service binder implementation.

In both cases, we unbind from the service and reset the binder in the `onStop()` callback.

## Audio Focus

By default, the PRESTOplay SDK for Android complies to the Audio Focus guidelines. This implies that the player will request audio focus on playback start, and thus other compliant apps playing media in the background will be paused.

This mechanism is bidirectional, so if the PRESTOplay SDK for Android is playing content and another app requests audio focus, the player will be paused.

This behaviour is encouraged by Google and it’s strongly recommended. Nevertheless, if you consider that this does not apply to your app’s use case, you can either disable this behaviour completely, or provide a callback to perform custom operations whenever the audio focus is lost.

### Disabling Audio Focus Management

In order to disable the audio focus management, you can set the `SdkConsts.INTENT_AUDIO_ATTRIBUTES` key to `SdkConsts.AUDIO_ATTRIBUTES_FOCUS_DISABLED`:

    Bundle bundle = new Bundle();
    bundle.putExtra(SdkConsts.INTENT_URL, "http://sample.url/manifest.mpd");
    // Disable Audio Focus logic
    intent.putExtra(SdkConsts.INTENT_AUDIO_ATTRIBUTES, SdkConsts.AUDIO_ATTRIBUTES_FOCUS_DISABLED);
    playerController.open(bundle);

Or set the `audioAttributes` in the `PlayerConfig` to the same value:

    PlayerConfig config = new PlayerConfig.Builder("http://sample.url/manifest.mpd")
        .audioAttributes(SdkConsts.AUDIO_ATTRIBUTES_FOCUS_DISABLED)
        .get();
    playerController.open(config);

Note that you can also customize the `AudioAttributes` object.

    PlayerConfig config = new PlayerConfig.Builder("http://sample.url/manifest.mpd")
        .audioAttributes(new AudioAttributes.Builder()
                .setContentType(C.CONTENT_TYPE_MUSIC)
                .setFlags(C.FLAG_AUDIBILITY_ENFORCED)
                .setUsage(C.USAGE_MEDIA)
            .get())
        .get();
    playerController.open(config);

### Callback for Audio Focus loss

The PRESTOplay SDK for Android also offers the possibility to fine-tune the app’s behaviour whenever audio focus is lost through a callback set in the `PlayerController`.

Note that setting this callback will override the default behaviour of the Player, meaning that it won’t automatically pause anymore on its own.

    playerController.setAudioFocusCallback(new AudioFocusCallback() {
        @Override
        public void onAudioFocusLost() {
            // Custom Audio Focus loss logic
            playerController.pause(); // default behaviour
        }
    });

    // ...

    playerController.open(config);

Previous topic: Project Setup
Next topic: Advanced Setup