Player SDK for Android (v 0.4.x)

Introduction

The Ustream Player SDK lets you play Ustream live and recorded videos in your native applications. Using the native SDK gives you full control over the Player, including a customizable native user interface, callbacks on status changes, and many more. (Note: if you need none of the above, you may use the HTML-based Player API instead)

This document describes the basic steps to make a mobile app using the Player SDK for Android.

Before you begin

Account prerequisites

Before going into details, please note that document assumes the following:

  • you have a registered user at ustream.tv
  • your Ustream user is entitled to use the Player SDK specifically (if you have questions, please contact us)

Development prerequisites

IDE

We recommend Android Studio version 1.3.1 (or newer) for development.

Build System

The library uses the Gradle build system, and it is deployed as a Maven artifact. The sample application also uses Gradle, you can build it using the provided gradle wrapper: 'gradlew'

Android API level

The supported minimum API level is 16 (Android version 4.1)

Step 1: Explore the SDK package

The provided zip archive contains the sample Android application project for the Player Library. There is also a 'maven' folder (a local Maven repository) which contains the library. You can use Gradle (or Maven) to import the dependency from the local repository.

Step 2: Create (or open) your project

Open the project that you would like to integrate the SDK in.

Step 3: Add the SDK to the project

Import from local repo

Copy the 'maven' folder to your project. In your project's build.gradle put the Player Library dependency:

repositories {
	maven {
		url new File(project.projectDir, 'maven').toURI()
	}
}

dependencies {
	compile "tv.ustream.player:ustream-player-android:0.4.3"
}

Step 4: Register your app at Ustream

The SDK requires the use of a Ustream API key, which is validated whenever the SDK communicates with Ustream streaming servers. The sample application contains a sample API key which you can use for testing. The sample API key can only be used to play content on the test channel(s) also used in the sample app.

Note: Although there is a provided API key for the sample app's sample content, you still need to register your identifier at Ustream. This will ensure that you can build the sample project using your own certificates.

Before you can start using the Player SDK for playing content from your own channel(s), you will need to:

  • get a valid Ustream API key owned by the Ustream user that owns the content you would like to play
  • register the application identifier(s) - of every app in which you will integrate the Player SDK in - at Ustream

The application identifier uses:

  • your Android application's package name: typically com.your_company.your_project
  • and the public key of your signing certificates

To register your indentifiers you will need to:

  • Obtain your certificate's public key
  • Encode your keys using SHA1, then Base64 algorithm
  • Send the encoded string and your applcation's package name to Ustream

Obtaining your identifiers

There are two types of certificates that your application can be signed with. The debug key is used for development and testing, and the release key is used to sign your app when you release it to the Playe Store.

Generating your identifier in your Android app:

String packageName = context.getPackageName();
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName,
						PackageManager.GET_SIGNATURES);
byte[] signature = packageInfo.signatures[0].toByteArray();
MessageDigest messageDigest = MessageDigest.getInstance("SHA");
String appIdentifier = Base64.encodeToString(messageDigest.digest(signature), Base64.NO_WRAP);

Alternatively you can get the public key of your certificates with the following shell command:

keytool -exportcert -alias my_cert_alias -keystore /path/to/keystore_file > your_company-(debug/release).key

Lets call the generated file example-debug.key. Now you can encode it using the following command:

openssl dgst -sha1 -binary example-debug.key | base64


Generate the indentifier for your release certificate's public key, and every other debug certificates that your developers will use.

Every time you initialise an instance of the Player Library you have to configure it with your Ustream API key. More details on this in the next chapter.

Step 5: Create a Player

In your layout .xml

Place a PlayerView in your layout:

<tv.ustream.player.android.PlayerView
	android:id="@+id/playerview"
	android:layout_height="match_parent"
	android:layout_width="match_parent" />

In your Activity or Fragment

In your Fragment's onCreateView() / Activity's onCreate() get the player instance from the playerView:

final PlayerView playerView = (PlayerView)findViewById(R.id.playerview);
		IUstreamPlayer ustreamPlayer = playerView.getUstreamPlayer();

The tv.ustream.player.api.IUstreamPlayer class is the point where you can interface with the player library. It's methods send events to the player, and it's states are observed through the listeners (see below). The IUstreamPlayer's methods are explained further in it's javadoc

Step 6: Play live or recorded content

After the ustreamPlayer is created, initialize it with a content. This will most likely be in your Activity's onCreate() (Fragment's onCreateView()). A player instance can be initialized more than once with different content, but connect() or play() has to be called in order to reconnect to the servers. The connect() method is optional (a play() or pause() call will also handle it implicitly) though the player will respond to play() calls more quickly because it is already connected to Ustream's servers.

First time initialization:

// To play videos, use ContentType.RECORDED and the video id
ContentDescriptor contentDescriptor = new ContentDescriptor(ContentType.RECORDED, 54321);
// To play live streams, use ContentType.LIVE and the channel id
ContentDescriptor contentDescriptor = new ContentDescriptor(ContentType.LIVE, 12345);
if (!ustreamPlayer.isInitialized()) {
	ustreamPlayer.initWithContent(API_KEY, contentDescriptor);
	/**
	If the password (or birthday) is known in advance (and it is known to be required)
	it can be supplied here, for example:
	ustreamPlayer.setPassword("super-secret");
	*/
	ustreamPlayer.connect();
}

SDK version 0.4.0 introduced changes in the user facing interface, see the Changelog for details.

Setting your listeners

To receive state changes and other events from the player you need to set listeners. There are mandatory and optional ones, but all of these listeners have to be set prior to calling ustreamPlayer.attach() on your player instance. This should happen in the onResume() callback of your Activity or Fragment. Calling attach() is an important step, this is where your listeners and the player view is bound to the library. Forgetting to call this will cause the player to not render video on your view, and you will not receive any callback on your listeners.

@Override
protected void onResume() {
	super.onResume();
	ustreamPlayer.setPlayerListener(playerListener);
	ustreamPlayer.setErrorListener(errorListener);
	ustreamPlayer.setProgressListener(progressListener);
	ustreamPlayer.setViewerCountListener(viewerCountListener);
	ustreamPlayer.setLogoClickListener(logoClickListener);
	ustreamPlayer.setMetaDataListener(metaDataListener);
	ustreamPlayer.setBufferingListener(bufferingListener);
	ustreamPlayer.attach();
}

You also need to call ustreamPlayer.detach() in your Activity's or Fragment's onPause() callback, so your views can be recycled properly.

@Override
protected void onPause() {
	ustreamPlayer.detach();
	super.onPause();
}

See the next section and the sample application for more details.

Changing content

Changing content on an already initialized player (Please note detach() and attach() have to be called to properly reinit views):

private void changeContent(ContentDescriptor nextContent) {
	ustreamPlayer.detach();
	ustreamPlayer.initWithContent(API_KEY, nextContent);
	ustreamPlayer.connect();
	ustreamPlayer.attach();
}

Step 7: Handle Player callbacks

State flow diagram

This diagram represents the state flow of the library, the nodes are the states reported by the player, the named edges are events that can be sent to the player. There is a decision in the flow that happens automatically based on the content selected (live or recorded).

Catching callbacks

There are seven different listeners that you can add to the Player Library instance to receive callbacks. Some of these listeners are mandatory, others are optional, but each listener represents a group of functionality of the library.
The seven listeners are:

  • PlayerListener (mandatory)
  • ErrorListener (mandatory)
  • BufferingListener
  • ProgressListener
  • ViewerCountListener
  • LogoClickListener
  • MetaDataListener

PlayerListener

The PlayerListener is the most important listener, this is also a mandatory one, you must provide it, or you will receive an exception. The library's state is observed through this interface.

package tv.ustream.player.api;

/**
* Observes the state of the content playback.
*
* All callbacks represent mutually exclusive states.
*/
public interface PlayerListener {

	/**
	 * Called when the player is initialized.
	 * This is the initial state, and this is the state the player
	 * returns to after re-initialized with a new content.
	 * The library is not connected to the internet in this state.
	 */
	void onInitialized();

	/**
	 * Called when the player is stopped.
	 * This is the state the player returns to after a stop() or disconnect() call.
	 * The library is not connected to the internet in this state.
	 */
	void onStopped();

	/**
	* The requested live channel is not broadcasting, or the stream is not
	* available in a playable format.
	* Note: in this state, the player is still connected to Ustream's servers,
	* waiting for the stream to become online.
	*/
	void onWaitingForContent();

	/**
	* Called when everything is ready to play the requested content.
	* Note: At this point buffering of the content did not start yet
	*/
	void onContentReady();

	/**
	* Called at playback paused or stopped.
	*/
	void onPaused();

	/**
	* Called at playback start or restart.
	*/
	void onPlaying();
}

ErrorListener

This is also a mandatory listener, the library reports all playback errors here. When an error occurs the lib will call the corresponding callback, then the library will return to the Stopped state, notifying the application using PlayerListener.onStopped().

package tv.ustream.player.api;

/**
* Observes the errors that can possibly occur in the player.
*
* The callbacks are called when playback is not possible and each callback
* represent a reason for the playback error.
*/
public interface ErrorListener {

	/**
	* The requested recorded video is not available in a playable format.
	*/
	void onContentNotPlayable();

	/**
	* The requested live channel or recorded video does not exist.
	*/
	void onNoSuchContent();

	/**
	* The requested content requires a password authentication.
	*/
	void onPasswordLock();

	/**
	* The requested content is restricted by age.
	*/
	void onAgeLock();

	/**
	* The requested content requires a HashLock authentication
	* or the provided Hash is invalid/expired.
	*/
	void onHashLock();

	/**
	* The provided api key is invalid or this api key is not authorized to
	* access this content.
	*/
	void onInvalidApiKey();

	/**
	* The broadcaster's viewer hours are spent. Receiving this callback means
	* that Your clients will not be able to watch your streams at the moment.
	*/
	void onViewerHourLimitLock();

	/**
	* Connection error.
	*/
	void onConnectionError();

	/**
	* Unknown error.
	*/
	void onUnknownError();

}

BufferingListener

This is an optional listener, notifying the application about buffering starts and stops. The player will resume it's previous operation when buffering is completed.

package tv.ustream.player.api;

/**
* Observes background network communication's state
*/
public interface BufferingListener {

	/**
	* Called when the player does not have enough video frame to continue
	* playing and it is started to download data from the server
	*/
	void onBufferingStarted();

	/**
	* Called when the player is finished receiving data from the server
	* either if there is sufficient amount of data is received and the
	* playback is continued or an error occurred.
	*/
	void onBufferingStopped();

}

ProgressListener

The ProgressListener provides information about the content's duration and progress. This is an optional listener.

package tv.ustream.player.api;

import org.joda.time.Duration;

public interface ProgressListener {

	void onPositionUpdated(Duration position);

	void onDurationUpdated(Duration duration);

	void onDurationDisabled();
}

ViewerCountListener

The ViewerCountListener provides information about the content's audience (all-time viewers and current concurrent viewers). This is an optional listener.

package tv.ustream.player.api;

/**
* Provides information about the current and total viewer numbers
*/
public interface ViewerCountListener {

	/**
	* Update of current viewer number.
	* @param viewers number of current viewers, display this on your layout
	*/
	void onCurrentViewersUpdated(long viewers);

	/**
	* Current viewers module is disabled, remove the indicator from your
	* layout.
	*/
	void onCurrentViewersDisabled();

	/**
	* Update of total viewer number.
	* @param totalViewers number of all-time combined viewers, display this on
	* your layout.
	*/
	void onTotalViewersUpdated(long totalViewers);

	/**
	* Total viewers module is disabled, remove the indicator from your
	* layout.
	*/
	void onTotalViewersDisabled();
}

LogoClickListener

The displayed logo has been clicked, you should open the URL in the callback's parameter. This is an optional listener.

package tv.ustream.player.api;

import java.net.URI;

/**
 * Observes the click of the logo of a branded channel.
 */
public interface LogoClickListener {

	/**
	 * Called when the logo of the branded channel has been clicked.
	 *
	 * @param url The url which should be opened on a logo click.
	 */
	void onLogoClick(URI url);
}

MetaDataListener

MetaDataListener provides updates of the content's meta data, the callback is called when the meta becomes available. This is an optional listener.

package tv.ustream.player.api;

/**
* Observes the metaData of the content
*/
public interface MetaDataListener {

	/**
	* Called when the content metadata becomes available
	* (title, category, etc...).
	*/
	void onMetaData(MetaData data);


	/**
     * Called when the content's conversation settings become available.
     * @param data Holder for the IRC chat and SocialStream settings.
     */
    void onChatAndSocialStreamData(ChatAndSocialStreamData data);
}

Interactive callbacks

There are certain callbacks in PlayerListener that indicate a disconnect from Ustream's servers. These callback methods represent the reason for the disconnect. Some of these errors can be resolved by you or the user of your application.

These callbacks are found in tv.ustream.player.api.ErrorListener:

/**
* The requested recorded video is not available in a playable format.
*/
void onContentNotPlayable();

/**
* The requested live channel or recorded video does not exist.
*/
void onNoSuchContent();

/**
* The requested content requires a password authentication.
*/
void onPasswordLock();

/**
* The requested content is restricted by age.
*/
void onAgeLock();

/**
* The requested content requires a HashLock authentication
* or the provided Hash is invalid/expired.
*/
void onHashLock();

/**
* The requested content can not be accessed due to restrictions.
*/
void onRestricted();

/**
* The provided api key is invalid or this api key is not authorized to access this content.
*/
void onInvalidApiKey();

/**
* The broadcaster's viewer hours are spent. Receiving this callback means that Your clients will not be able to
* watch your streams at the moment.
*/
void onViewerHourLimitLock();

/**
* Connection error.
*/
void onConnectionError();

/**
* Unknown error.
*/
void onUnknownError();

Errors that can be resolved by user (or developer) interaction:

  • onPasswordLock(): call ustreamPlayer.setPassword(password); then restart the playback by ustreamPlayer.play();
  • onAgeLock(): call ustreamPlayer.setBirthDate(date); then restart the playback by ustreamPlayer.play();
  • onHashLock(): call ustreamPlayer.setHash(hash); then restart the playback by ustreamPlayer.play();

Transient errors:

  • onConnectionError(): There is a problem with the internet connection, (most likely temporary) try again later by calling: ustreamPlayer.play();
  • onUnknownError(): This indicates a problem that the library can't determine, (most likely temporary) try again by calling: ustreamPlayer.play();
  • onViewerHourLimitLock(): The broadcaster's viewer hours are spent. This user can not watch the stream at the moment, try again by calling: ustreamPlayer.play();

Other errors:

  • onInvalidApiKey(): The provided API key is not authorized to play this content. You need to create a new Player library instance to provide the correct API key. The key needs to be set upon init: ustreamPlayer.initWithContent(API_KEY, contentDescriptor);
  • onNoSuchContent(): The requested live channel or recorded video does not exist.
  • onContentNotPlayable(): The requested recorded video is not available in a playable format.
  • onRestricted(): There are security restrictions set for the channel, that does not enable the library to play this content. Or your channel has reached it's maximum concurrent viewer number. The latter can be resolved by retrying later, to resolve the former the channel's settings have to be modified.

Step 8: Refine the Player UI

Appearance

The player's appearance is fully customizable. The provided sample app contains an example appearance with resources.

See: src/main/res/layout/layout_video.xml in the sample app.

Localization

Localization is totally up to you, but there is an example in the sample app's strings.xml file containing the most likely needed strings.

Changelog

Changes in version 0.4.x compared to version 0.3.x:

  • Added IUstreamPlayer.isInitialized() method to check whether the player is already initialized. This is most useful on a configuration change event. The information representing the init state of the player does not need to be manually saved anymore.
  • Tweaks around IUstreamPlayer.destroy(). This is not bound to Android lifecycle anymore, can be called anywhere after initialization.
  • Bugfixes and stability improvements