Android SDK Documentation

Version 1509020@v330

The Octoshape SDK provides parts of the Octoshape content delivery system on the Android platform, enabling developers to create applications with instant access to on-demand HD quality video and instant seek capabilities providing an unparalleled viewing experience.

The intention of this document is to provide Android application developers with the necessary information and reference, for integrating the live and on-demand content delivery system provided by Octoshape into Android applications. The Android SDK contains the following:

Table of Contents

1. Introduction
 .  1.1. Octoshape SDK Architecture
 .  1.2. Supported Media / Streaming Formats
2. Using the SDK and Getting Started
 .  2.1. Setting-up a new project:
 .  2.2. Importing the SDK reference applications:
3. Playing Octoshape streams
 .  3.1. Creating and configuring OctoshapeSystem
 .  3.2. Prepare your streams by setting up a number of StreamPlayer instances
 .  3.3. Requesting playback and receiving URLs through callbacks
 .  3.4. Playing an URL using the Android MediaPlayer
 .  3.5. Handling errors
 .   .  3.5.1. Handling MediaPlayer Errors
 .  3.6. Supporting multiple players
 .   .  3.6.1. Selecting a player
 .   .   .  3.6.1.1. Playback using the Android MediaPlayer
 .  3.7. Seeking for on-demand streaming
 .  3.8. DVR for live streaming
4. Enabling remote playback using ChromeCast/Airplay
 .  4.1. Set up
5. Ticket Restriction
 .  5.1. Octoshape ticket system
 .   .  5.1.1. Introduction
 .   .  5.1.2. Octoshape stream configuration
 .   .  5.1.3. Issuing tickets
 .   .   .  5.1.3.1. The authID
 .   .   .  5.1.3.2. Setting options (the authInfo)
 .   .   .  5.1.3.3. Hash calculation
 .   .   .   .  5.1.3.3.1. Algo 1 - MD5
 .   .   .   .  5.1.3.3.2. Algo 2 - HMAC-MD5
 .   .   .   .  5.1.3.3.3. Algo 3 - SHA256
 .   .   .   .  5.1.3.3.4. Algo 4 - HMAC-SHA256
 .   .   .   .  5.1.3.3.5. Adding extra options
 .   .  5.1.4. Octoshape client authorization
 .  5.2. Using tickets in the Android SDK
6. Broadcasting live streams
 .  6.1. Configuring the client
 .  6.2. Creating and configuring OctoshapeSystem
 .  6.3. Prepare your broadcaster by setting up a number of StreamPushers
 .  6.4. Querying the status of StreamPushers
7. Enable Logging

1. Introduction

As Octoshape only provides a content delivery (e.g., data stream), the SDK does not contain any media player itself, but only the mechanism to utilize the Octoshape content delivery system. This makes the development using the Octoshape SDK fairly straightforward; doing so is the same approach as for any other Android video playback application.

We currently support the following architectures:

From an application developer perspective the SDK allows retrieval of media data ( e.g., rtmp://127.0.0.1:60001/ond/Octoshape ) from Octoshape streams ( e.g., octoshape://streams.octoshape.net/androidsdk/mp4/nascar_500 ), and passing these to a media player. Moreover, the SDK API also provides streaming features such as: seeking (ond-demand only), DVR (live-only), ticketing and live broadcasting.

1.1. Octoshape SDK Architecture

The SDK consists of two components the Java API, used by the application developer, and the Android Octoshape Service. The service is automatically launched and bound and only requiring adding it to the manifest file. The service installs the Octoshape Client from the assets files and launches / stops it, as needed. Once started, the Octoshape Client will then directly communicate with the application (see below figure).

Figure 1: Overview of the Octoshape SDK components

1.2. Supported Media / Streaming Formats

The SDK Octoshape supports the following streaming and media formats:

As native Android playback capabilities are version depended, we refer to the list of media formats for which Android offers built-in support ( Android Developer Pages ). For Android 4.0 and above we transmux all , except mp3 as HLS. Devices running Honeycomb (Android 3.x) or below do not support HLS reliably, and therefore require a third party player (e.g. Vitamio) for playing live video.

Moreover, there exists Android devices (e.g., STBs) with native media-frameworks supporting additional media formats and protocols (e.g, MPEG-TS over HTTP or HLS on Android 2.3) than listed in the Android Developer Pages ). When targeting hardware (e.g., STB) with special media playback capabilities, different from those listed in the link above, please contact: support@octoshape.com.

2. Using the SDK and Getting Started

It is recommended to use an Eclipse based IDE, as this allows direct import of the library and reference player projects.

Importing the OctoshapeLibrary project (/OctoAndroidLib) into an Eclipse workspace (File>Import) allow all Android projects to use the SDK by referencing to the library project OctoAndroidLib project.

2.1. Setting-up a new project:

2.2. Importing the SDK reference applications:

The SDK includes two different reference applications. The OctoAndroidPlayer, only implement a subset of the available SDK features. The OctoDemoPlayer is a 'full-fledged' production like implementation.

The reference applications are directly imported into the workspace, will all dependencies set, only requiring the presence of the library project and coping the asset folder from the library project to the reference application.

3. Playing Octoshape streams

This chapter documents the retrieval of a media URLs and how to pass these to the native Android media player. The media URL is received by the following number of steps.

In the following each of these steps will be more thoroughly explained. The complete examples using the native Android media-framework are found in Reference Players.

3.1. Creating and configuring OctoshapeSystem

The OctoshapeSystem class serves as an 'entry-point' for the Octoshape service/client and there must only exist one instance of OctoshapeSystem. When opened, the object launches the Octoshape service, which starts the Octoshape client. Instances are created using the OctoStatic class. When the application closes the OctoStatic.close(Runnable r) must be called. This unbinds the service, freeing memory and terminates established socket connections.

OctoshapeSystem is created with the reference to a ProblemListener and a OctoshapePortListener . The ProblemListener reports problems for Octoshape service and the Octoshape client (see section handling errors). The OctoshapePortListener receives a callback once the Octoshape client has successfully launched and bound to localhost, providing the bound port number.

Next, the available media players are specified by invoking:

public void addPlayerNameAndVersion(final String mediaPlayerId, final String mediaPlayerName, final String mediaPlayerVersion)

The different media player name are defined as constants in: OctoshapeSystem.

Additionally, a OctoshapeSystemListener should be set by OctoshapeSystem.setOctoshapeSystemListener(OctoshapeSystemListener), providing the callback when a connection to the Octoshape client is established.

As a last step OctoshapeSystem.open() is called, launching the OctoshapeSystem (starting the Octoshape service and client), which will trigger the callback for the OctoshapeSystemListener indicating that service and client has successfully been launched and streaming can start.

Having the Octoshape client bound to a known port on localhost allows for requesting media using direct HTTP GET requests. This way playback can be achieved within the app itself or using external players (e.g., a webbrowser). Please see the OctoServicePlayer, for details.

Code Listing 1: Creating and configuring OctoshapeSystem

/**
 * Creating OctoshapeSystem, adding ProblemListener and setting OctoshapePortListener triggering a
 * callback once the Octoshape service/client has started successfully.
 */
public void initOctoshapeSystem() {

  ProblemListener problemListener = new ProblemListener() {
    @Override
    public void gotProblem(Problem p) {
      Log.e(LOGTAG, p.getMessage() + " " + p.toString());
    }
  };
  // Create OctoshapeSystem
  os = OctoStatic.create(this, problemListener,
      new OctoshapePortListener() {
        // called once the Octoshape service/client has started.
        @Override
        public void onPortBound(int port) {
          boundPort=port;
        }
      });
  // Adding AndroidMediaPlayer
  os.addPlayerNameAndVersion(OctoshapeSystem.MEDIA_PLAYER_NATIVE, OctoshapeSystem.MEDIA_PLAYER_NATIVE,
      "" + Build.VERSION.SDK_INT);
  
  os.setOctoshapeSystemListener(new OctoshapeSystemListener() {
	@Override
	public void onConnect(String id) {
	  authID=id;
	  Log.d(LOGTAG, "Connection to Octoshape client established");
	}
  });
  // Launching OctoshapeSystem
  os.open();
}

When the the application is closed (e.g, onDestroy() is called), the OctoshapeSystem has to be closed by invoking

3.2. Prepare your streams by setting up a number of StreamPlayer instances

Streams are represented by the StreamPlayer class. So for every stream (octolink) an instance is to be created (see below example). As a minimum a StreamPlayerListener has to be registered for each StreamPlayer, as it provides the media URL trough a callback. It is generally recommended to initialize StreamPlayer objects prior to playback, as it reduces the playback start-up time. For details please refer to the API documentation for additional listeners for the StreamPlayer .

In the code listing below is an example function, setting up and initializing one StreamPlayer at a time. The returned and initialized StreamPlayer object, is ready for playback. When requesting playback, by invoking StreamPlayer.requestPlay(), a StreamPlayerListener.gotUrl(URL, seekOffset, mediaPlayerListener) callback is received providing the URL to the MediaPlayer (in this example playStream(Uri mediaUrl, final String playerId)).

When done playing the StreamPlayer instance should always be closed by calling StreamPlayer.close(Runnable r).

Code Listing 2: Preparing Streams and StreamPlayers.

/**
 * Adding a stream to the OctoMediaPlayer. Creates an StreamPlayer the
 * StreamPlayer instance is initiated with a UrlListener and
 * ProblemListener.
 *
 * @param stream
 *            link (e.g., octoshape://ond.octoshape.com/demo/ios/bbb.mp4)
 * @return StreamPlayer on which we can request playback.
 */
public StreamPlayer setupStream(final String stream) {

  Log.d(LOGTAG, "Setting up stream: " + stream);
  StreamPlayer sp = os.createStreamPlayer(stream);
  sp.setListener(new StreamPlayerListener() {
    private String playerId;

    /**
     * Receiving new URL from the streamplayer instance MUST always trigger
     * playback of the provided URL. 
     *
     * @param url
     *            to be passed to the media player
     * @param seekOffset
     *            offset we have seek to in milliseconds
     * @param mediaplayerListener
     *            interface used for signaling when playback has begun and is complete 
     */
    @Override
    public void gotUrl(String url, long seekOffset, MediaPlayerListener mpl) {
        playStream(Uri.parse(url), playerId, mpl);
    }

    /**
     * Resets an on-demand file duration previously reported in
     * resolvedOsaSeek(..) method
     */
    @Override
    public void gotNewOnDemandStreamDuration(long duration) {
      // TODO:
    }

    /**
     * Called if the stream is seekable using the media player's own
     * native seeking functionality (e.g., the Android MediaPlayer does
     * the seeking for us).
     */
    @Override
    public void resolvedNativeSeek(boolean isLive, String playerId) {
      this.playerId = playerId;
    }

    /**
     * Called if it is not possible to seek in the stream.
     */
    @Override
    public void resolvedNoSeek(boolean isLive, String playerId) {
      this.playerId = playerId;
    }

    @Override
    /**
     * Called when stream support OsaSeek / DVR
     */
    public void resolvedOsaSeek(boolean isLive, long duration,
        String playerId) {
      this.playerId = playerId;
    }
  });
  sp.setProblemListener(problemListener);
  sp.initialize(false);
  return sp;
}

As this example only uses one stream we can change the onConnect(String id) callback in code listing 1 to:

@Override
public void onConnect(String id) {
	authID=id;
	Log.d(LOGTAG, "Connection to Octoshape client established");
	mStreamPlayer = setupStream(OCTOLINK).requestPlay();
}

3.3. Requesting playback and receiving URLs through callbacks

Having set up (and optional initialized) a number StreamPlayer , allows requesting playback by invoking .requestPlay().

Calling the .requestPlay() triggers the gotUrl callback in the corresponding StreamPlayerListener (see code listing 2), providing the media URL together with a MediaPlayerListener instance. Receiving a new URL MUST always trigger playback of the new URL.

Remark

Important: The application should at any time be prepared to handle callbacks yielding new media URLs, and MUST play these immediately. If new URLs are not handled accordingly, playback may terminate. Moreover, before starting playback with a new URL, the media player instance has to be reset.

Remark

Important: The MediaPlayerListener instance received from the gotUrl(..) MUST be used to signal when playback has begun and completed, by invoking: onMediaPlaybackStarted() and onMediaPlaybackCompleted(). Calling these may trigger callback providing new URLs.

3.4. Playing an URL using the Android MediaPlayer

The following shows a simple function playing a media URL. Notice the onCompletionListener invoking: onMediaPlaybackCompleted(). Android provides two mechanisms for detecting when playback has has started: 1) the OnPreparedListener and 2) the MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START info event provided by an OnInfoListener. Here 2) requires JellyBean MR1 (4.2 - API level 17) and should be used if the given API level is available as it is much more accurate than 1).

Code Listing 3: Playback with the Android MediaPlayer

/**
 * Setting up and playing a received media URL 
 *
 * @param mediaUrl URL which needs to be passed to a media player
 * @param playerId the id of the media player which should be used to play the media Url
 */
protected void playStream(Uri mediaUrl, final String playerId, final MediaPlayerListener mplistener) {
 
  Log.d(LOGTAG, playerId + " now plays: " + mediaUrl);
  mMediaPlayer = new MediaPlayer();
  try {
    mMediaPlayer.setDisplay(mHolder);
    mMediaPlayer.setDataSource(this, mediaUrl);
    mMediaPlayer.setOnVideoSizeChangedListener(this);
    
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1)
      mMediaPlayer.setOnInfoListener(new OnInfoListener() {
        @Override
        public boolean onInfo(MediaPlayer mp, int what, int extra) {
          if (MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START == what) {
            Log.d("MediaPlayer", "START RENDERING");
            mpl.onMediaPlaybackStarted();
          }
          return false;
        }
      });
    
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR1)
      mMediaPlayer.setOnPreparedListener(new OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
          Log.d("MediaPlayer", "PLAYERBACK STARTED");
          mpl.onMediaPlaybackStarted();
        }
      });
    
    mMediaPlayer.setOnPreparedListener(new OnPreparedListener() {
	  @Override
	  public void onPrepared(MediaPlayer mp) {
	    mplistener.onMediaPlayerPrepared();
	    Log.d("MediaPlayer", "PLAYER READY");
	  }
	});
    
    mMediaPlayer.setOnErrorListener(new OnErrorListener() {
      @Override
      public boolean onError(MediaPlayer mp, int what, int extra) {
        error("MediaPlayer Error: " + what + ":" + extra);
        return true;
      }
    });
    mMediaPlayer.prepare();
    mMediaPlayer.start();
  } catch (Exception e) {
    Log.e(LOGTAG, "Error preparing MediaPlayer", e);
    error("Error preparing MediaPlayer: " + e.getMessage());
  }
}

This conclude the section and the reference application using the native Android MediaPlayer. For the full code listing please see OctoAndroidPlayer in the SDK package. Moreover the SDK package also contains a complete player , including most features (e.g., seeking, DVR, etc.), using the native Android media player.

3.5. Handling errors

Errors are represented by the Problem class, received by a ProblemListener reporting problems regarding the OctoshapeSystem or for a StreamPlayer instance.

Received Problem instances should always trigger a UI message to the user if such a message is available (see Problem.html#getMessage()). Below a simple example implememtation of a ProblemListeners. Here all Problems are logged to console and those containing a user message are displayed in a small popup via a Toast.

ProblemListener problemListener = new ProblemListener() {
  @Override
  public void gotProblem(Problem p) {
    Log.e(LOGTAG, "Problem: "+p.toString());
    if(p.getMessage()!=null){
      runOnUiThread(new Runnable() {
        @Override
        public void run() {
          Toast toast = Toast.makeText(MiniAndroidPlayer.this, error,Toast.LENGTH_LONG);
          toast.show();
        }
      });
    }
  }
};				

The implementation of .getMessage() is documented in detail in java docs. If an alternate language that English is required, please implement a customized variant of getMessage().

3.5.1. Handling MediaPlayer Errors

When errors are reported by the media framework, we propose the creation of Problem instances, passing information received by the OnErrorListener.onError(MediaPlayer mp, int what, int extra) callback. Existing Problem instances can then be dispatched trough the previously defined ProblemListener.

mMediaPlayer.setOnErrorListener(new OnErrorListener() {
				
  @Override
  public boolean onError(MediaPlayer mp, int what, int extra) {
    problemListener.gotProblem(OctoStatic.generateMediaPlayerProblem(what, extra));
	return false;
  }
});				

3.6. Supporting multiple players

This SDK currently supports native Android MediaPlayer, as well as some third party mediaplayer frameworks. When setting up the SDK by preparing the OctoshapeSystem, available media players are added to the invoking:

public void addPlayerNameAndVersion(final String mediaPlayerId, final String mediaPlayerName, 
									final String mediaPlayerVersion) 

Code Listing 5: Defining media players

// Adding Android MediaPlayer
os.addPlayerNameAndVersion(OctoshapeSystem.MEDIA_PLAYER_NATIVE, "Android Media Player",
    "" + Build.VERSION.SDK_INT);
// Launching OctoshapeSystem
os.open();

For multiple available mediaplayers, the above methods must be invoked once for each player.

3.6.1. Selecting a player

Prior to receiving a media URL ( by gotUrl(...) ) one of the resolved... callback in the StreamPlayerListener (see code listing 2) is triggered, providing information about seek method and suitable media player. If no suitable player was found, a corresponding Problem is received.

The resolved... callback are received prior to the gotUrl callback, after playback was requested Receiving the resolved... callback earlier, requires initializing the stream by invoking StreamPlayer.initialize(true).

3.6.1.1. Playback using the Android MediaPlayer

This is illustrated in code listing 3. For a full code listing please refer to the OctoAndroidPlayer in the SDK package.

3.7. Seeking for on-demand streaming

Prior to receiving a media URL ( by gotUrl(...) ) one of the resolved... callbacks in the StreamPlayerListener (see code listing 2) is triggered, providing information about the available seek method. There exist three types of seeking, each explained in the following:

No seek

resolvedNoSeek(boolean isLive, String playerId) was called.

Seeking is not available.

Native seek

resolvedNativeSeek(boolean isLive, String playerId) was called.

Seeking is initiated through a native player (e.g., Android MediaPlayer). The duration and current position information is directly retrieved from the media player (required for setup seeking UI).

Octoshape seek.

resolvedOsaSeek(boolean isLive, long duration, String playerId) was called.

Seeking is initiated by invoking StreamPlayer.requestPlayOndemandPermilPosition(int) This triggers a gotUrl(...) callback, providing the a new URL and a seekOffset. The duration is provided through the resolvedOsaSeek(..) callback.

Remark

Computation of the current position is depended on the media player and android version.

Native Android 4.4.2 and below:

computed by MediaPlauer.getPosition() + seekOffset.

Native Android 4.4.3 and 4.4.4:

uses the PTS (Presentation Time Stamp) values in the transport stream to determine playback in the stream, so the position soley determined by MediaPlauer.getPosition().

Native Android 5 and above:

computed by MediaPlauer.getPosition() + seekOffset.

For further details please refer to the javadoc of StreamPlayerListener.

3.8. DVR for live streaming

DVR is configured by a maximum latency defined in milliseconds. If a stream is DVR enabled, a StreamPlayerListener.resolvedOsaSeek(isLive, duration, playerId) callback is received with isLive being true and duration corresponding to the maximum configured latency (see below example).

 ...
 @Override
 /**
  * Called when stream support OsaSeek / DVR
  */
 public void resolvedOsaSeek(boolean isLive, long duration,
     String playerId) {
   this.playerId = playerId;
   if(isLive && duration > 0 )
     // DVR  for live available
   else if(isLive)
   	 // DVR is not available
   else
     // Use OSA seek for on-demand
 }
 ...

DVR is initiated invoking the StreamPlayer.html#requestPlayLiveWithLatency(latency) method

sp.requestPlayLiveWithLatency(latency);

Remark

It is required to stop the MediaPlayer playback prior to invoking StreamPlayer.html#requestPlayLiveWithLatency(latency)

4. Enabling remote playback using ChromeCast/Airplay

Enabling ChromeCast/AirPlay capability requires setting an explicit option on the StreamPlayer instance prior to invoking initialize() or any play() method by invoking:

mStreamPlayer.setOctolinkOption("airplay", "true");

When using ChromeCast and the application is terminated, playback is stopped as the application no longer can serve media data to the used Chromecast device.

The SDK contains a sample out-of-the-box Android application (the OctoAndroidCastPlayer) that can cast to Google Cast devices (e.g., ChromeCast). The OctoAndroidCastPlayer is capable of playing streams both on the local device, and on Google Cast devices. When playing on the local device, the app supports also seeking (both native and OSA seeking). In case the stream does not support seeking, or when streaming on the Google Cast device, the app shows an information that this is a LIVE stream.

The reference player is working with a custom receiver application for the ChromeCast, which implements the basic functionality required for the secure playback on Google Cast devices. Each Google Cast receiver application has a unique ID issued by Google. For our custom receiver, the id is FCC6402A, and it is hardcoded in the application. The Google's developer guide contains more information on this issue.

The reference player can also work with the default receiver application. To disable the custom receiver: open the res/values/strings.xml file and insert the default receiver app ID number on line 5 in the string app_id CC1AD845 (This is the app id for the default receiver see Google's developer guide and this reference). Then make sure that in the octoshape.android.stream.StreamPlayerWrapper#configureStreamPlayer method, to comment out line 108 or set streamPlayer.setOctolinkOption("ccs", "false").

This app is based on the CastVideos-android reference android sender, which you can find here. However, the OctoAndroidCastPlayer app bring many changes to the reference sender. The unmodified or partially modified from the CastVideos-android project and used in this player are listed in the NOTICE file in the project directory.

4.1. Set up

The app has the following dependencies:

In contrast to the other reference player, this is an Android Studio project (download from: http://developer.android.com/sdk/index.html). The following setup instructions assume Android Studio v1.2.

  1. Save OctoAndroidCastPlayer to $DIR, where $DIR will be the project directory.

  2. Download Google CastCompanionLibrary v2.3.2, rename the folder to CastCompanionLibrary.

  3. Save CastCompanionLibrary to $DIR.

  4. Save OctoAndroidLib to DIR.

  5. Open $DIR/OctoAndroidCastPlayer/settings.gradle and delete the contents of the file.

  6. From the menu, navigate to File -> New -> Import Module. For the source directory, nagivate to $DIR and select CastCompanionLibrary. Set module name to ":CastCompanionLibrary". Finish.

  7. Same as in the previous step, import $DIR/OctoAndroidLib. Set module name to ":OctoAndroidLib". Finish.

  8. Open $DIR/OctoAndroidCastPlayer/build.gradle file and:

    1. update line #46: replace "..:CastCompanionLibrary" with ":CastCompanionLibrary"

    2. update line #49: replace "..:CastCompanionLibrary" with ":CastCompanionLibrary"

    3. update line #51: replace ":..:OctoAndroidLib" with ":OctoAndroidLib"

5. Ticket Restriction

5.1. Octoshape ticket system

5.1.1. Introduction

Using the Octoshape ticket system, broadcasters can restrict access to their Octoshape streams. The most common use case is to allow only registered or paying users to watch the stream.

The ticket system works by enabling a password/shared secret for the stream in the Octoshape streaming servers. The password is known by both the broadcaster and Octoshape. A player/application attempting to play the protected stream, must present a valid ticket. Such a ticket can only be generated by knowing the password of the stream.

There are 4 components involved in the ticket system:

The player application

The player or app - implemented by the broadcaster - which is using the Octoshape client or SDK for stream playback.

The Octoshape client/SDK

Provides APIs to the player application, to pass stream play requests, and tickets for authentication.

The ticket issuer

A broadcaster-controlled component that "knows" the password and can issue valid tickets for the stream. Usually the ticket issuer will be a special web-application that validates user information (login, payment etc.) against some database before issuing tickets.

The Octoshape streaming servers

The Octoshape back-end streaming servers which serve stream data and validates tickets to allow/disallow access.

5.1.2. Octoshape stream configuration

Contact Octoshape support to have ticket-authentication enabled. Octoshape support will provide the password used to protect the streams, so the ticket issuer can be properly configured.

5.1.3. Issuing tickets

To play a ticket restricted stream the player application must provide a valid ticket to the Octoshape client (which in turn validates it with the Octoshape servers). A valid ticket consists of two parts, which are passed to the Octoshape client as strings:

  1. authInfo: A comma-separated list of options that restricts the ticket in various ways

  2. authHash: A hash of the combined string of options, as well as the stream password and optionally a unique Octoshape client session ID

A correct authhash value must matches the options set in authinfo

5.1.3.1. The authID

For additional security, all Octoshape clients generate an "authID" upon every launch. This ID is changed every time the Octoshape client re-launches and is considered to be unique for every client. The player application can obtain this id through the same API used for playing streams.

Per default, the authid is used to validate the authhash string. This ensures that one Octoshape user, cannot share the ticket with another Octoshape user, as they will have different authids and thus require different authhashes to play the stream The ticket issuer may choose to disable this feature by setting a special option. See the useauthid option.

The authID is a string of ASCII characters from the closed interval 32 to 126. Thus it could be e.g. "-1754182638" or "147966193".

5.1.3.2. Setting options (the authInfo)

Various options can be set by the ticket issuer to affect the security level and scope of a ticket.

algo

This option specifies which hash algorithm was used to compute the authhash. It takes an integer value that corresponds with one of the supported algorithms:

  1. MD5

  2. HMAC-MD5

  3. SHA256

  4. HMAC-SHA256

Note: Plain MD5 is assumed if this value is not set, however it is recommended to explicitly set the correct algo-value e.g. algo=4

hardexpire

This option specifies a time by which the ticket should expire and streaming should be discontinued. The time is given as a number of UT1 milliseconds since 00:00 UTC on January 1st 1970. This is commonly referred to as a UNIX timestamp.

E.g. to set hardexpire for Jan 1st 2020 (00:00) set hardexpire=1577836800000

useauthid

This option defaults to true so there is no need to include it if setting it to true. But if set to "false" the authid and the trailing "," (comma) will not be used to generate the hash. This offers a simpler but less secure ticketing system as the tickets can be (re)used by any Octoshape client and will be restricted only by any additional options that may be set (such as hardexpire or unique )

filter

This option is used to limit the ticket to a specific stream or stream prefix. If the value of the filter option ends with a '/' the ticket will be valid for all streams that has this string as a prefix. If it does not end in a '/', it is interpreted as a single specific stream name.

The stream name is the unique string identifying the stream in the Octoshape system, without the leading protocol and complex of the octolink, i.e. the stream name of the octolink octoshape://streams.octoshape.net/MyCustomerId/live/mystream is MyCustomerId/live/mystream.

unique

This option allows the ticket issuer to specify a unique string that can be used by the Octoshape servers to allow/disallow access in a number of ways. The most common use is to allow only one user to watch at a time with a unique string. This prevents e.g. multiple persons from logging in and accessing content using the same account.

user

This can be set to some id identifying the user. Is currently only used by the Octoshape servers and only in log files

A set of options are given as a comma separated list of key-values; Some Octoshape APIs call this list the "authInfo" sting. It is used both as a component of the authHash string, but is also the other, un-hashed, part of a ticket.

5.1.3.3. Hash calculation

To authenticate against a stream, the ticket issuing component must compute and present a correct authhash. This is done by hashing the password (that Octoshape has configured the protected streams with) with the optional authid and the authinfo options.

The Octoshape token system supports both MD5 and SHA256 in regular or HMAC mode.

Most programming languages/libraries provide built-in methods for one or more of these algorithms.

Below we provide some examples to illustrate the proper generation of authhash using different algorithms. The fifth example illustrates a ticket that has multiple options set. For each of these assume that the password is Skonmerk, the authid is 845406748, and authinfo only contains the algo option. These values can be used to test if an implementation generates the correct output.

5.1.3.3.1. Algo 1 - MD5

For plain MD5 the concatenated string [password],[authid],[algo] is hashed, e.g. in pseudo-code:

authhash = md5('Skonmerk,845406748,algo=1')

Which then gives:

authinfo:

algo=1

authhash:

afc7c1f3d2584bc79caa9ce19a4e9f2f
5.1.3.3.2. Algo 2 - HMAC-MD5

For HMAC-MD5, use the password as key and the full concatenated string (including the password) as the message. E.g. in pseudo-code:

authhash = hmac-md5('Skonmerk', 'Skonmerk,845406748,algo=2')

which gives:

authinfo:

algo=2

authhash:

8e3bfbf870151b4c1b37d293572542ae
5.1.3.3.3. Algo 3 - SHA256

For plain SHA256 the concatenated string [password],[authid],[algo] is hashed, e.g. in pseudo-code:

authhash = sha256('Skonmerk,845406748,algo=3')

Which then gives:

authinfo:

algo=3

authhash:

94b513cadd3216675920a1eb944f68b2b656c73fa683f2821ad0a6260853c08c
5.1.3.3.4. Algo 4 - HMAC-SHA256

For HMAC-SHA256, use the password as key and the full concatenated string (including the password) as the message. E.g. in pseudo-code:

authhash = hmac-sha256('Skonmerk', 'Skonmerk,845406748,algo=4')

which gives:

authinfo:

algo=4

authhash:

94b6265c5d12d5e02a0059769952e040a7b5933d8851ca704ac1ea048fa4456c
5.1.3.3.5. Adding extra options

This example (using HMAC-SHA256) illustrates how to add additional options to the authinfo string:

authhash = hmac-sha256('Skonmerk', 'Skonmerk,845406748,algo=4,hardexpire=1577836800000,unique=SomeUserName,filter=customer/live/channel1/')

which gives:

authinfo:

algo=4,hardexpire=1577836800000,unique=SomeUserName,filter=customer/live/channel1/

authhash:

858ca315ac036c1303de001290de78357846827a689b377da362fb7fa653a554

5.1.4. Octoshape client authorization

When the Octoshape client receives a ticket for some stream, that ticket is forwarded to the Octoshape streaming servers.

The Octoshape streaming server, who knows the ticket restriction password, will use the information in the ticket to calculate the same hash that the ticket issuer generated. If these hashes match, the client will be allowed to obtain the stream. If someone has attempted to manipulate the options, or if the the client is trying to play a stream using a ticket that was generated for a different authID, the hashes will not match and the streaming servers will deny access.

5.2. Using tickets in the Android SDK

If a stream is ticket restricted in the Octoshape back-end system, the setAuthorization() method must be called for the corresponding StreamPlayer, providing a ticket consisting of an authInfo and an authHash.

/** This is a reference implementation for the implementation token authentication
 *  see https://support.octoshape.com/entries/21368651-Implementing-Token-Authentication.
 *
 * @param streamPlayer stream to be authenticated
 * @param authId ID provided by the @ OctoshapeSystem on start-up.
 */
private boolean authenticateStreamPlayer(StreamPlayer streamPlayer, String authId) throws IOException {

    final String ERROR = "ERROR_NO_AUTHID";
    URL url;

    // Simple Ticket
    if (authId == null)
        url = new URL("http://octoshape.com/broadcasters/webintegration/ticket/simple.php");

    // Stronger Security using Auth-ID
    else
        url = new URL(" http://octoshape.com/broadcasters/webintegration/ticket/authid.php?OctoshapeAuthid=" + authId);

    // Read server response
    BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
    String inputLine;
    ArrayList<String> response = new ArrayList<String>();
    while ((inputLine = in.readLine()) != null)
        response.add(inputLine);
    in.close();

    // Verifying server response and authenticate
    if (response.size() >= 2 && ERROR.equals(response.get(0))) {
        streamPlayer.setAuthorization(response.get(1), response.get(0));
        return true;
    } else {
        Log.e(LOGTAG, "Error stream authentication. Invalid server response");
        return false;
    }
}

The computation of authHash may be require the authId, which is obtained through the OctoshapeSystemListener, which is received once Octoshape client has launched, by calling OctoshapeSystem.open(). This mechanism should in general be used to verify that the Octoshape client has started.

os.setOctoshapeSystemListener(new OctoshapeSystemListener() {
	
  @Override
  void onConnect(String id) {
    authID=id;
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        // prepare streamplayer 
      }
    });
  }
});		        

6. Broadcasting live streams

The following section will give a brief overview and show examples of how to use this SDK for broadcasting.

6.1. Configuring the client

Using the broadcasting feature requires the adjustment to the octoshape client configuration. The configuration is a XML file located in the assets file named setup.xml.

Then the following attribute needs to be added to the root node: TrustXML="true".

<config LocalWebServerIpp="127.0.0.1:65535" TrustXml="true"/> 

Changing the setup.xml the project requires a re-building of the project.

6.2. Creating and configuring OctoshapeSystem

Creating and configuring OctoshapeSystem is identical to media play-back. Please refer to corresponding section.

6.3. Prepare your broadcaster by setting up a number of StreamPushers

Broadcasting streams are represented by the StreamPusher class. When instantiating a StreamPusher the media source, the Octoshape link and a password have to be provided. Before initiating the broadcasting by invoking StreamPusher.initialize(), options can be set both on the octolink and the media source URL. The options should in general not be required. For general information (including using these options) please refer to the Broadcaster Documentation in the Octoshape Support Forum.

The below example code shows a generic function for setting up and initializing a number of StreamPushers.

private StreamPusher pushStream(String octoStreamLink, String MediaSourceUrl,
    String passwd, HashMap linkOptions, HashMap mediaOptions) {

  StreamPusher pushingStream = os.createStreamPusher(octoStreamLink,
      MediaSourceUrl, passwd);

  // setting link options
  MMapIterator it = linkOptions.iterator();
  while (it.hasNext())
    pushingStream.setOctolinkOption((String) it.nextKey(),
        (String) it.getValue());

  // setting media options
  it = mediaOptions.iterator();
  while (it.hasNext())
    pushingStream.setMediaSourceUrlOption((String) it.nextKey(),
        (String) it.getValue());

  pushingStream.initialize();
  return pushingStream;
}

6.4. Querying the status of StreamPushers

The status of a StreamPusher can be obtained providing a PushStatusListener to StreamPusher.requestPushStatus(PushStatusListener) You might want to periodically invoke this method. For more information on the returned status, please refer to the Broadcaster Documentation in the Octoshape Support Forum.

Please also refer to the documentation of StreamPusherand PushStatusListener.

7. Enable Logging

The Octoshape client can generate obfuscated log files for debugging purpose. Logging is enabled by setting an attribute in the Octoshape configuration file (setup.xml). Enabling and retrieving these logs requires the use of the adb program (part of the android development tools), capable of pulling and pushing file to the device.

The configuration file is a XML file located in ./files/octoshape/setup.xml where . refers to the path of the application (e.g., /data/data/com.octoshape.android.octodemoplayer/files/octoshape/setup.xml).

As a first step the setup.xml file needs to be pulled from the device.

adb pull /data/data/com.octoshape.android.octodemoplayer/files/octoshape/setup.xml 

As a second step the following node is added the root node, and the logpath is modified:

<log type='Normal'>
    <server enabled='true' useold='true'/>
    <console enabled='false'/>
    <file enabled='true' logpath='/mnt/sdcard/octoshape'/>
 </log>

As a third and final step, the setup.xml is pushed back on to the device.

adb push setup.xml /data/data/com.octoshape.android.octodemoplayer/files/octoshape/setup.xml 

If the above is not possible (due to filesystem permissions), the setup.xml is edited directly on the device. First login via ADB and navigate to the relevant folder.

flpi@cmpc045 ~
$ adb shell
shell@jflte:/ $ cd /data/data/com.octoshape.android.octoandroidplayer/files/octoshape/

Since the android does not come with a terminal editor, it is easiest to copy the edited setup.xml to the clipboard and paste it into an echo command in the terminal. The below example will write the output from the echo command to the setup.xml. Here please remember to replace any double quotes in the xml with single quotes.

shell@jflte:/data/data/com.octoshape.android.octoandroidplayer/files/octoshape $ echo -e " paste here " > setup.xml

Now the Android application needs to closed completely (force stop in the android settings) and restarted. Upon restart the application will write log files. Logging produces a significant amount of output and should only be enabled when instructed by Octoshape Support.