Customer Sign In

upLynk

Android SDK

Overview

This guide is provided to assist an integrator with the use of the upLynk Android Player Library.

Compatibility

Android Versions Supported

2.3 Gingerbread
4.0 Ice Cream Sandwich
4.1 Jelly Bean
4.2 Jelly Bean
4.3 Jelly Bean
4.4 KitKat
Support for most major brands and chipsets *
5.x Lollipop
6.x Marshmallow
7.x Nougat
8.x Oreo
General compatibility via MediaCodec API *
* Compatibility testing and support is limited to latest firmware for a given device.

Download

The latest SDK can be downloaded from the CDN. Once authenticated, in the lower left corner, click "Download Player SDK" and then select "Android".

Alternatively, please contact upLynk support for the link to download the latest version of this library.

API

The upLynk Android Player API mirrors that of android.media.MediaPlayer [API Level 14] and generally allows for a direct replacement by swapping imports.

Included here are additional interfaces/methods/objects specific to upLynk MediaPlayer features.

Interfaces
  • MediaPlayer.OnAssetBoundaryListener
    • onAssetBoundary(MediaPlayer mp, String assetID):void
  • MediaPlayer.OnCaptionEventListener
    • onCaptionEvent(MediaPlayer mp, CaptionEvent event):boolean
  • MediaPlayer.OnID3MetadataListener
    • onID3Metadata(MediaPlayer mp, UplynkID3 metadata):boolean
  • MediaPlayer.OnUplynkMetadataListener
    • onUplynkMetadata(MediaPlayer mp, UplynkMetadata metadata):boolean
  • MediaPlayer.OnUplynkSegmentListener
    upLynk Segments Events are fired when a playlist is updated / reloaded and indicate a map of assets that make up the media.
    • onUplynkSegmentList(MediaPlayer mp, Vector<UplynkSegment> segments):boolean
Support Classes
  • CaptionStyle
    • TEXT_SIZE_XSMALL
      50% of native size
    • TEXT_SIZE_SMALL
      75% of native size
    • TEXT_SIZE_NORMAL
      100% of native size
    • TEXT_SIZE_LARGE
      150% of native size
    • TEXT_SIZE_XLARGE
      200% of native size
    • EDGE_TYPE_NONE
      no special edge type
    • EDGE_TYPE_DROP_SHADOW
      drop shadow effect for caption text
    • EDGE_TYPE_RAISED
      raised effect for caption text
    • EDGE_TYPE_DEPRESSED
      depressed effect for caption text
    • EDGE_TYPE_UNIFORM
      outline effect for caption text
    setBackgroundColor(int):voidcaption text background color in Android color hexadecimal format (0xaarrggbb). Default=0xff000000 {*}
    setCaptionPreviewText(int row,
    int column, String text):void
    Set the starting row (1 -15), starting column (1-32), and the text to be displayed as a Caption Preview.
    setEdgeType(int):voidedge effect from EDGE_TYPE constants. Default=EDGE_TYPE_NONE
    setTextColor(int):voidcaption text color in Android color hexadecimal format (0xaarrggbb). Default=0xffffffff {*}
    setTextSize(int):voidcaption text size adjustment percentage as integer [50-200]. Default=TEXT_SIZE_NORMAL or 100%
    setTypeface(Typeface):voidcaption text font. Default=MONOSPACE
    setWindowColor(int):voidcaption background area color in Android color hexadecimal format. Default=0x00000000 {*}
    * color values are expressed as android.graphics.Color packed integers
  • CaptionEvent
    • mode:CaptionMode
      returns mode in range UNKNOWN,POP_ON,ROLL_UP,PAINT_ON
    • eventType:CaptionEventType
      returns event type in range TEXT,LINEBREAK,CLEAR
    • rowCount:short
      returns the number of rows that should be used for painting text when mode==ROLL_UP
    • rows:SparseArray<CaptionRow>
      returns a list of CaptionRow objects ( populated when mode==POP_ON )
    • character:CaptionCharacter
      returns a single CaptionCharacter object
  • CaptionEvent.CaptionCharacter
    • character():char
      character
    • color():int
      color as 0xrrggbb
    • isItalic():boolean
      true if character should be italicized
    • isUnderlined():boolean
      true if character should be underlined
  • CaptionEvent.CaptionRow
    • getRow():int
      row index
    • getColumn():int
      column index
    • getIndent():int
      column position offset
    • getText():String
      row text as a plain string
    • getCharacters():Vector
      list of character objects with style attributes
  • MediaPlayer.UplynkAssetInfo
    • getAssetID():String
      upLynk asset GUID
    • getOwner():String
      upLynk asset owner ID
    • getDescription:String
      content description as defined in the CMS
    • isAudioOnly():boolean
      indicates if content only contains audio
    • isAd():boolean
      indicates if content is flagged as an ad in the CMS
    • hasError():boolean
      if true, there was an error retrieving asset information
    • getTvRating():int
      tv rating as defined in the CMS
    • getMovieRating():int
      movie rating as defined in the CMS
    • getRatingFlags():int
      rating flags as defined in the CMS
    • getMaxSlice():int
      gets the total number of slices
    • getDuration():double
      length in milliseconds of content
    • getSliceDuration():double
      length in milliseconds of each slice
    • getThumbnailPrefix():String
      url thumbnail path prefix
  • MediaPlayer.UplynkID3
    • getKey():String
      ID3 tag name
    • getValue():String
      ID3 tag value
  • MediaPlayer.UplynkMetadata
    • getAssetID():String
      upLynk asset GUID
    • getRay():String
      Ray Alpha Index (A-G)
    • getSliceIndex():String
      slice number in hex
    • getSliceNumber():int
      slice number in decimal
    • getAssetInfo():UplynkAssetInfo
      UplynkAssetInfo object describing the current slice ( can be NULL )
  • MediaPlayer.UplynkSegment
    Defines a piece of a playlist. Use the offset for calculating the correct timecode for loading thumbnails.
    • getType():char
      type flag of segment { A=Ad, C=Content }
    • getDuration():double
      duration of the segment in seconds
    • getOffset():double
      index Offset from the beginning of the specified asset
    • getAssetId():String
      upLynk Asset ID
  • MediaPlayer.UplynkTrackInfo
    • getType():char
      Type Flag of Track { A=Audio, S=Subtitle }
    • getName():String
      Track Name
    • getGroup():String
      Track Group Name
    • getLang():String
      Track Language
Methods
getAudioTrackOptions():Vector<UplynkTrackInfo>retrieves a list of audio tracks. May return an empty list when there are no alternate audio tracks available. [Build 64]
Valid Player States { PREPARED | PLAYING | PAUSED }
getDisplayRectForMaxDimensions(maxWidth, maxHeight):Rectreturns a Rect of the content dimensions with proper aspect ratio given a set of bounds[Build 86]
getPlaybackBitrate():intreturns the current variant's reported bitrate in bps [Build 90]
selectAudioTrack(int):boolean sets the target audio track index. zero is always the default audio track. [Build 64]
Valid Player States { PREPARED | PLAYING | PAUSED }
setContext(Context):voidallows persisting bandwidth stats by writing to a file in the apps private storage
setCaptionChannel(int):voidset the preferred caption channel [1-4]
setCaptionStyle(CaptionStyle):voidset the visual properties of the captions
setMaxBitrate(int):voidlimits the variant selection by specified value in Kbps. eg 500=500Kbps streams or less
setCaptionsEnabled(boolean):voidenables/disables the display of closed captions in configured container
setCaptionLayoutContainer(RelativeLayout):voidsets the target container that closed captions will be created in. The RelativeLayout is assumed to be directly over the video SurfaceView with matching dimensions.
setOnAssetBoundaryListener(OnAssetBoundaryListener):voidRegister for Content Boundary Events that are triggered when content changes, such as entering or leaving ad break. Note: this event my be received twice, once with an Asset ID of the new asset and once with a null asset id to signify an ad marker.
setOnCaptionEventListener(OnCaptionEventListener):voidRegister for Caption Events to receive caption text
setOnID3MetadataListener(OnID3MetadataListener):voidRegister for ID3 Metadata Events to receive name/value pairs
setOnUplynkMetadataListener(OnUplynkMetadataListener):voidRegister for Metadata Events that are fired for each Slice
setOnUplynkSegmentListener(OnUplynkSegmentListener):voidRegister for Segment List Events that are fired during playlist updates
setVolume(float leftVolume, float rightVolume):voidscaler values normalized in range of 0.0 to 1.0.
Note: volume setting is local to player and does not effect device volume
Class Methods
getDisplayRectForMaxDimensionsAndVideoDimensions(maxWidth, maxHeight, videoWidth, videoHeight):Rectreturns an aspect ratio constrained Rect for a given set of Max display bounds and content dimensions [Build 86]
initSurfaceHolder(SurfaceHolder):voidallows for configuration of a SurfaceHolder to render correctly with the current device hardware. Note: this must be called on the SurfaceHolder BEFORE passing the SurfaceHolder to a MediaPlayer instance via setDisplay(SurfaceHolder)

Closed Captions

Captions can be accessed either by allowing the MediaPlayer to display the captions or by registering an event listener to receive notifications (or both)

  1. Configure MediaPlayer to display the captions
    1. Pass a RelativeLayout to the MediaPlayer via MediaPlayer.setCaptionLayoutContainer(RelativeLayout layout). The Container should be positioned over the MediaPlayer's SurfaceView and not allow touches
    2. Enable the display by calling MediaPlayer.setCaptionsEnabled(boolean enabled)
  2. Register for Caption Events
    1. Attach an event listener to the MediaPlayer via MediaPlayer.setOnCaptionEventListener(MediaPlayer.OnCaptionEventListener listener)
    2. Implement MediaPlayer.OnCaptionEventListener on your listener class (Activity)
    3. Override onCaptionEventListener(MediaPlayer mp, CaptionEvent cc) on your listener and implement logic to display the text supplied by the event

Android Integration

Activity Setup
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import com.uplynk.media.CaptionEvent;
import com.uplynk.media.CaptionEvent.CaptionEventType;
import com.uplynk.media.CaptionEvent.CaptionMode;
import com.uplynk.media.CaptionEvent.CaptionRow;
import com.uplynk.media.MediaPlayer;
import com.uplynk.media.MediaPlayer.UplynkAssetInfo;
import com.uplynk.media.MediaPlayer.UplynkID3;
import com.uplynk.media.MediaPlayer.UplynkMetadata;

public class MyVideoPlayerActivity extends Activity
	implements
		SurfaceHolder.Callback,
		MediaController.MediaPlayerControl,
		MediaPlayer.OnCompletionListener,
		MediaPlayer.OnErrorListener,
		MediaPlayer.OnSeekCompleteListener,
		MediaPlayer.OnPreparedListener,
		MediaPlayer.OnAssetBoundaryListener,
		MediaPlayer.OnCaptionEventListener,
		MediaPlayer.OnID3MetadataListener,
		MediaPlayer.OnUplynkMetadataListener,
		MediaPlayer.OnUplynkSegmentListener
{

    private MediaPlayer      _mp;
    private MediaController  _mc;
    private SurfaceView      _sv;
    private SurfaceHolder    _sh;
    ...

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        ...
        _sv = (SurfaceView)this.findViewById(R.id.surfaceView);
        _sh = _sv.getHolder();
        // configure Surface Holder (required for certain devices)
        MediaPlayer.initSurfaceHolder(_sh);
        _sh.addCallback(this);
        ...
    }
}
		
Player Creation
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
private void playContent(final String url)
{
    if(_mp !=null)
    {
        _mp.release();
        _mp = null;
    }

    // Create a new media player
    _mp = new MediaPlayer();

    _mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
    _mp.setContext(this);     // allow persisting bandwidth stats (optional)
    //_mp.setMaxBitrate(1536);  // bandwidth limit in Kps (optional)

    // Attach Listeners
    _mp.setOnErrorListener(this);
    _mp.setOnCompletionListener(this);
    _mp.setOnSeekCompleteListener(this);
    _mp.setOnPreparedListener(this);
    _mp.setOnVideoSizeChangedListener(this);
    _mp.setOnID3MetadataListener(this);

    _mp.setOnUplynkMetadataListener(this);
    _mp.setOnCaptionEventListener(this);
    _mp.setOnAssetBoundaryListener(this);

    // Attach Caption container (optional)
    _mp.setCaptionsEnabled(true);
    _mp.setCaptionLayoutContainer((RelativeLayout)this.findViewById(R.id.ccContainer));

    // Set Caption Styles (optional)
    CaptionStyle style = new CaptionStyle();
    style.setBackgroundColor(0x3300ff00);
    style.setTextColor(0xffdddddd);
    style.setTextSize(CaptionStyle.TEXT_SIZE_SMALL);
    style.setEdgeType(CaptionStyle.EDGE_TYPE_DROP_SHADOW);
    style.setTypeface(Typeface.SANS_SERIF);
    _mp.setCaptionStyle(style);

    // Attach a MediaController (optional)
    _mc = new MediaController(this);
    _mc.setAnchorView(_sv);

    // Set the surface for the video output
    _mp.setDisplay(_sh); // must already be created
    _mp.setScreenOnWhilePlaying(true);

    try {
        _mp.setDataSource(url);
        _mp.prepareAsync(); // start asynchronous player preparation
    } catch (IllegalArgumentException e) {
        /* handle error */
    } catch (IllegalStateException e) {
        /* handle error */
    } catch (IOException e) {
        /* handle error */
    }
}
    
OnPrepared Handler
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Override
public void onPrepared(MediaPlayer mp)
{
    if(mp == _mp)
    {
        _mc.setMediaPlayer(this); // attach media controller (optional)
        //_mp.seekTo(20000); // set starting position in milliseconds if > 0 (optional)

        _mp.start(); // tell player to start playback
    }
}
OnUplynkMetadata Handler
1
2
3
4
5
6
@Override
public boolean onUplynkMetadata(MediaPlayer mp, UplynkMetadata metadata)
{
    Log.d(TAG, "MediaPlayer::onUplynkMetadata called (" + metadata.toString() + ")");
    return true;
}
OnID3Metadata Handler Build 63
1
2
3
4
5
6
@Override
public boolean onID3Metadata(MediaPlayer mp, UplynkID3 metadata)
{
    Log.d(TAG,String.format("MediaPlayer::onID3Metadata: [%s] %s",metadata.getKey(),metadata.getValue()));
    return true;
}
OnCaptionEvent Handler
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Override
public boolean onCaptionEvent(MediaPlayer mp, CaptionEvent event)
{
    Log.d(TAG, "MediaPlayer::onCaptionEvent called");

    // if POP_ON, render 'rows' data
    if( event.mode == CaptionMode.POP_ON)
    {
        // loop through all characters of all rows to render
        SparseArray<CaptionRow> rows = event.rows;
        for(int i=0;i<rows.size();++i)
        {
            CaptionEvent.CaptionRow row = rows.valueAt(i);
            // Log.i(TAG,String.format("CC [%d] {%d} %s",row.getRow(),(row.getColumn()+row.getIndent()),row.getText()));

            Vector<CaptionEvent.CaptionCharacter> characters = row.getCharacters();
            for(int x=0;x<characters.size();++x)
            {
                CaptionEvent.CaptionCharacter cc = characters.get(x);
                // Log.w(TAG,String.format("CC CHAR [%c]  color:%d  italic:%b  underlined:%b ",cc.character(), cc.color(), cc.isItalic(), cc.isUnderlined()));
                // handle character
            }
        }
    }
    // if ROLL_UP, render single character or handle other event type
    else if( event.mode == CaptionMode.ROLL_UP)
    {
        if(event.eventType == CaptionEventType.TEXT)
        {
            CaptionEvent.CaptionCharacter cc = event.character;
            // Log.d(TAG,String.format("CC %c",cc.character()));
            // render individual character
        }
        else if(event.eventType == CaptionEventType.LINEBREAK)
        {
            // line break so push content up one line and start new line
        }
        else if(event.eventType == CaptionEventType.CLEAR)
        {
            // clear all content
        }
    }
    return true;
}
OnAssetBoundary Handler
1
2
3
4
5
@Override
public void onAssetBoundary(MediaPlayer mp, String assetID)
{
    Log.i(TAG,"onAssetBoundaryListener: " + assetID);
}
SurfaceCreated CallbackBuild 61
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// As of build 61 (2014.03.20), a surfaceDestroyed(...) callback will unbind the
// surface from the player to prevent memory leaks
// make sure to rebind the surface when it is recreated via it's surfaceCreated(...) callback
@Override
public void surfaceCreated(SurfaceHolder sh)
{
    Log.d(TAG, "surfaceCreated called");

    // need to re-attach surface
    if(_mp != null && sh == _sh)
    {
        _mp.setDisplay(_sh);
        _mp.setScreenOnWhilePlaying(true);
    }
}