Show Menu
TOPICS×

TVSDK 1.4 to 2.5 for Android (Java)

TVSDK 2.5 offers multiple benefits over version 1.4 in terms of performance, security, better integrations, and more.
TVSDK solves the biggest challenges, on the device that matter the most. Android continues it's global dominance, with over 86% of market share. Migration to TVSDK on Android will optimize playback performance to improve user engagement and accelerate time-to-market with support for new content formats.

Benefits of migrating to TVSDK v2.5

TVSDK 2.5 offers multiple benefits over version 1.4 in terms of performance, security, better integrations, and more. Read on to quickly know the benefits of migrating to this new version.
According to a third-party benchmarking study, v2.5 provides 5x reduction in startup time and 3.8x reduction in dropped frames over the industry average.
Performance Features
Description
Instant-on for VOD and Live
Pre-load initial ts segments for Immediate playback for VOD and live-linear streams when channel switching, for a TV like experience.
Lazy ad loading
Starts playback as soon as pre-roll or content is available while resolving mid-roll ads in a parallel thread.
Persistent network connections
Increase in efficiency and decrease in latency of networking code for faster playback performance.
Improved ABR logic
The new ABR logic is based on buffer length, rate of change of buffer length, and measured bandwidth. This ensures that the ABR chooses the right bit rate when the bandwidth fluctuates and also optimizes the number of times the bitrate switch actually happens by monitoring the rate at which the buffer length changes.
Partial segment download
Starts playback as soon as enough frames from a segment are available for rendering video reliably on the client side.
Parallel downloads
TVSDK downloads audio and video segments in parallel for demuxed content to optimize playback performance.
The playback features improve consumer engagement by delivering the experience of linear broadcast on digital. Also, it helps you leverage native DRM such as Widevine for HD playback.
Playback Features
Description
MP4 playback
MP4 short clips don't have to be re-transcoded to playback within TVSDK.
DASH VOD content playback
Basic DASH VOD playback use cases are supported.
Smooth Trickplay with ABR
Support for fast forward and rewind in HLS using keyframes at low rates and I-Frames at faster speeds. ABR support for all supported frames.
The features are important to meet the studio restrictions such as HD playback over native DRM.
Features
Description
Resolution Based Output Protection
playback can be restricted to only certain resolutions that are allowed by DRM requirements. Available only through Primetime DRM.
Widevine support
Supported with DASH VOD streams to enable native DRM use cases.
Direct billing enhancement does away with the need to create manual reports for billing every month. VHL 2.0 allows for a faster time to market with pre-build integration and better accuracy in tracking.
Features
Description
Moat integration
Support for ad viewability measurement from Moat.
VHL 2.0
The latest optimized video heartbeats library integration for automatic collection of usage data for Adobe Analytics.
Failover Support
Additional strategies implemented to continue uninterrupted playback, despite failures of host servers, playlist files, and segments.
Direct Billing Integration
Sends billing metrics to Adobe Analytics backend which is certified by Adobe Primetime for streams used by customer.
All the features of TVSDK v1.4 are supported in v2.5, except Multi-CDN support.

Overview of the migration process

Migrating smoothly from TVSDK 1.4 to 2.5 entails changing to version 2.5 libraries, recompiling, then using this document to help debug any problems that occur.
TVSDK v1.4 libraries do not work with and coexist with v2.5 libraries. You must use v2.5 libraries with TVSDK 2.5 and migrate your applications and integrations to upgrade to TVSDK 2.5. This document describes how and what to change in your application code and how to address errors during re-compilation.
The psdk.jar file uses third party libraries for supporting different features. To prevent removal of the libraries, include the following in the proguard.cfg file:
# Adobe TVSDK keep classes
-keep class com.adobe.** { *; }
-keep class com.google.android.exoplayer.**
{ *; }

In the build.gradle file, you need to include the compile directive to include the TVSDK based JAR files. If your app includes Adobe Video Analytics then you need to include the compile directive for the additional jars required for Adobe Video Analytics integration in the app
# Compile Adobe TVSDK jars compile files('libs/psdk-va.jar')
compile files('libs/VideoHeartbeat.jar')


Multiple minor changes are not covered in this document. For the minor API changes, refer TVSDK 2.5 for Android Java API . The corresponding C++ API reference has detailed descriptions. For analogous C++ API documentation, see TVSDK 2.5 for Android C++ API .
Multiple examples of the API usage are covered in the reference implementation distributed with the TVSDK.

API changes in TVSDK v2.5

The new, obsolete, and modified APIs are documented below.
TVSDK v1.4
TVSDK v2.5
Description
import com.adobe.ave.drm .DRMAcquireLicenseSettings
import com.adobe.mediacore.drm .DRMAcquireLicenseSettings;
All class names in the TVSDK 2.5 API begin with the com.adobe.mediacore prefix. This is just an example.
MediaPlayerException, IllegalStateException, or IllegalArgumentException
MediaPlayerException
In 2.5, the APIs generate only MediaPlayerException.
MediaPlayer.PlayerState (MediaPlayer.Event.PLAYBACK)
MediaPlayerStatus (MediaPlayerEvent.STATUS_CHANGED)
In v2.5, MediaPlayer.PlayerState was renamed to a separate enum MediaPlayerStatus.
DefaultMediaPlayer.create (getActivity().getApplicationContext())
MediaPlayer mediaPlayer = new MediaPlayer(getActivity(). getApplicationContext());
The static methods used to create objects are replaced by public constructors.
MediaPlayer.seekToLocalTime()
MediaPlayer.seekToLocal()
The MediaPlayer.seekToLocalTime() method is now called MediaPlayer.seekToLocal().
closedCaptionsTrack.isActive()
Not available
MetadataNode
Metadata
In v2.5, the Metadata class replaces the use of the v1.4 MetadataNode class.
DefaultMetadataKeys
MetadataKeys
The DefaultMetadataKeys from v1.4 are in v2.5 enum MetadataKeys.
AdvertisingFactory
ContentFactory
The AdvertisingFactory from v1.4 is renamed to ContentFactory in v2.5
PlacementOpportunityDetector
OpportunityGenerator
Detectors are replaced with Generators.
mediaPlayer.getView().notifyClick();
mediaPlayer.notifyClick();
The notifyClick() method of MediaPlayerView was moved to the MediaPlayer class.
public ABRControlParameters(ABRPolicy abrPolicy, int nInitialBitRate, int nMinBitRate, int nMaxBitRate)
public ABRControlParameters(int nInitialBitRate, int nMinBitRate, int nMaxBitRate, ABRPolicy abrPolicy, int nMinTrickPlayBitRate, int nMaxTrickPlayBitRate, int nMaxTrickPlayBandwidthUsage, double dMaxPlayoutRate)
playbackInformation.getTimeToFirstFrame()
Not available
playbackInformation.get PerceivedBandwidth()
TVSDK v2.5 QOSProvider has a new property to determine the perceived bandwidth during a streaming session.

Removed classes

The following classes are removed and have no equivalents.
  • TimeRangeCollection
  • PSDKConfig
  • BaseLogger
  • DefaultLogger
  • Log
  • Logger
  • LogFactory
  • NullLogger
The last six of these classes are available as utility classes in the reference implementation.

Namespace changes

The TVSDK 2.5 API consolidates namespaces.
All class names in the TVSDK 2.5 API begin with the com.adobe.mediacore prefix. Some class names in the TVSDK 1.4 API start with com.adobe.ave. The corresponding 2.5 classes change com.adobe.ave to com.adobe.mediacore. For example., note the changes in the following lines of code for 1.4 and 2.5:
// TVSDK 1.4
import com.adobe.ave.drm.DRMAcquireLicenseSettings; import com.adobe.ave.drm.DRMLicense;
import com.adobe.ave.drm.DRMManager; import com.adobe.ave.drm.DRMMetadata


// TVSDK 2.5
import com.adobe.mediacore.drm.DRMAcquireLicenseSettings; import com.adobe.mediacore.drm.DRMLicense;
import com.adobe.mediacore.drm.DRMManager; import com.adobe.mediacore.drm.DRMMetadata;


Changes in event handling

To register events in this version, pass the handler to addEventListener . The list of events has been substantially revised from version 1.4 to version 2.5. For example, here is how to register an event handler for MediaPlayerEvent.STATUS_CHANGED:
mPlayer.addEventListener(MediaPlayerEvent.STATUS_CHANGED,
new StatusChangeEventListener() {
@Override
public void onStatusChanged(MediaPlayerStatusChangeEvent event) {
//Handle status change event
}
});


This is how the event was registered in 1.4:
mPlayer.addEventListener(MediaPlayer.Event.PLAYBACK, new MediaPlayer.PlaybackEventListener() {
@Override
public void onStateChanged(MediaPlayer.PlayerState state,
MediaPlayerNotification notification) {
//Handle status change event
}
// Additional handlers
...
});


The MediaPlayerEvent enum contains all event codes. The following event codes from 1.4 no longer exist in 2.5:
  • ADBREAK_PLACEMENT_COMPLETED
  • ADBREAK_PLACEMENT_FAILED
  • ADBREAK_REMOVAL_COMPLETED
  • AD_BREAK_MANIFEST_LOAD_COMPLETE
  • AD_MANIFEST_LOAD_COMPLETE
  • AD_MANIFEST_LOAD_FAILED
  • AUDIO_TRACK_FAILED
  • BACKGROUND_MANIFEST_FAILED
  • BUFFERING_FULL, CUSTOM_AD_EVENT
  • CONTENT_MARKER
  • CONTENT_PLACEMENT_COMPLETE
  • CONTENT_CHANGED
  • ITEM_REPLACED
  • ITEM_READY
  • VIEW_CLICKED
  • PREPARED
  • UPDATED
  • OPPORTUNITY_COMPLETED
  • OPPORTUNITY_CREATED
  • OPPORTUNITY_FAILED
  • PAUSE_AT_PERIOD_END
  • PLAYBACK_STARTED
  • PLAYBACK_PAUSED
  • PLAYBACK_COMPLETED
  • PLAY_COMPLETE
  • POST_ROLL_COMPLETE
  • RESOURCE_LOADED
  • TIMED_METADATA_SKIPPED
  • VIDEO_ERROR
  • VIDEO_STATE_CHANGED
The following event codes are new in 2.5:
  • AD_RESOLUTION_COMPLETE
  • BUFFER_PREPARED
  • CAPTIONS_UPDATED
  • MANIFEST_UPDATED
  • PLAYBACK_RANGE_UPDATED
  • RESERVATION_REACHED
  • TIME_CHANGED
  • TIMED_EVENT
  • TIMED_METADATA_ADDED_IN_BACKGROUND

Renamed Events

New Name
Old Name
SEEK_BEGIN
SEEK_STARTED
SEEK_END
SEEK_COMPLETED
SEEK_POSITION_ADJUSTED
SEEK_ADJUST_COMPLETED
BUFFERING_BEGIN
BUFFERING_STARTED
BUFFERING_END
BUFFERING_COMPLETED
AUDIO_TRACK_UPDATED
AUDIO_TRACK_CHANGED
STATUS_CHANGED
STATE_CHANGED
TIMED_METADATA_AVAILABLE
TIMED_METADATA_ADDED
SIZE_AVAILABLE
SIZE_CHANGED
LOAD_INFO
LOAD_INFORMATION_AVAILABLE

MediaPlayer changes

A new way of constructing MediaPlayer and changes to some of the methods.
Changes to the MediaPlayer class
Here are the changes to MediaPlayer class:
  • The MediaPlayerStatus enum replaces MediaPlayer.PlayerState . For example:
//TVSDK v1.4
mPlayer. addEventListener(MediaPlayer.Event.PLAYBACK, new MediaPlayer.PlaybackEventListener() {

@Override
public void onStateChanged(MediaPlayer.PlayerState state,MediaPlayerNotification notification) {
//Handle status change event
}
// Additional handlers
...
});

//TVSDK v2.5
mPlayer.addEventListener(MediaPlayerEvent.STATUS_CHANGED, new StatusChangeEventListener() {
@Override
public void onStatusChanged(MediaPlayerStatusChangeEvent event) {
//Handle status change event
}
//Additional handlers.
...
});


  • The MediaPlayer.seekToLocalTime() method is now called MediaPlayer.seekToLocal . For example:
//TVSDK v1.4
public void seekToLocal(long localPosition) { mediaPlayer.seekToLocalTime(localPosition);
}

//TVSDK v2.5
public void seekToLocal(long localPosition) { mediaPlayer.seekToLocal(localPosition);
}


  • The MediaPlayerView.notifyClick() method is now MediaPlayer.notifyClick() . For example:
//TVSDK v1.4
public void adClick() { mediaPlayer.getView().notifyClick();
}

//TVSDK v2.5
public void adClick() { mediaPlayer.notifyClick();
}


  • The former MediaPlayer.MediaPlayer.getNotificationHistory() method is now gone and not replaced.
  • The former MediaPlayer.replaceCurrentItem() is split into two methods: replaceCurrentResource() , which takes an instance of MediaResource , and replaceCurrentItem() , which takes an instance of MediaPlayerItem . For example:
//TVSDK v1.4
MediaResource playerResource = MediaResource.createFromUrl(url, getAdvertisingMetadata());

mediaPlayer.replaceCurrentItem(playerResource);

//TVSDK v2.5 - replacing a resource
MediaResource playerResource = new MediaResource(url,
MediaResource.Type.HLS, getAdvertisingMetadata());
...
try {
mMediaPlayer.replaceCurrentResource(playerResource, _mediaPlayerItemConfig);
} catch (MediaPlayerException mediaPlayerException) {
// handle exception.
}
// TVSDK 2.5 - replacing an Item
MediaPlayerItemLoader itemLoader = new MediaPlayerItemLoader(context, new MediaPlayerItemLoader.LoaderListener() {

@Override
public void onError(PSDKErrorCode psdkErrorCode, String s) {
...
}
@Override
public void onLoadComplete(MediaPlayerItem mediaPlayerItem) {
...
}
@Override
public void onBufferingBegin() {
...
}
@Override
public void onBufferPrepared() {
...
}
});
MediaResource playerResource = new MediaResource(url,
MediaResource.Type.HLS, getAdvertisingMetadata());

itemLoader.load(playerResource); itemLoader.prepareBuffer();
mediaPlayer.replaceCurrentItem(itemLoader.getItem());


You can use this to switch between pre-initialized MediaPlayer instances, as in the case of blackouts.
Constructors replace static create() methods
You can use constructors in TVSDK v2.5, instead of using create() methods of TVSDK v1.4. All the classes with names beginning with Default, such as DefaultMediaPlayer , DefaultNetworkConfig , DefaultContentFactory , are not available in v2.5.
In some cases the TVSDK v1.4 API uses the following pattern for creating classes:
  1. Define an interface (for example, MediaPlayer ).
  2. Provide a default class (for example, DefaultMediaPlayer ).
  3. Provide a create() method on the default class to supply a class that implements the interface.
In TVSDK v2.5, such interfaces are concrete classes and you create instances of these classes using the respective constructors. The following code snippets illustrate this difference:
//TVSDK v1.4
// Create a media player
// MediaPlayer is an interface
private MediaPlayer createMediaPlayer() { MediaPlayer mediaPlayer =
DefaultMediaPlayer.create(getActivity().getApplicationContext()); return mediaPlayer;

//TVSDK v2.5
// Create a media player
// MediaPlayer is a class that you instantiate private MediaPlayer createMediaPlayer() {
MediaPlayer mediaPlayer =
new MediaPlayer(getActivity().getApplicationContext()); return mediaPlayer;
}

Other classes that don't follow this pattern but use create() methods in 1.4 include:
  • MediaResource This formerly used MediaResource.createFromUrl() . Now use the constructor, which takes a URL, resource type, and metadata. For example:
//TVSDK v1.4
MediaResource playerResource = MediaResource.createFromUrl(url, getAdvertisingMetadata());
mediaPlayer.replaceCurrentItem(playerResource);

//TVSDK v2.5
MediaResource playerResource =
new MediaResource(url, MediaResource.Type.HLS, getAdvertisingMetadata());

try { mediaPlayer.replaceCurrentResource(playerResource,_mediaPlayerItemConfig);
} catch (MediaPlayerException mediaPlayerException) {
// handle exception.
}


  • Ad
  • AdAsset
  • AdBreak
Some classes (for example, ContentFactory ) are abstract classes with no publicly available default implementation (for example, DefaultContentFactory ). In these cases you can provide a default implementation via a convenience function, for example: mediaPlayerItemConfig.getDefaultContentFactory()
Changes in closed captioning
The following changes affect classes related to closed captioning:
  • When retrieving closed caption tracks, MediaPlayerItem.getClosedCaptionTracks() returns only active tracks.
  • ClosedCaptionTrack no longer has an isActive() method.
//TVSDK v1.4
public List<String> getClosedCaptionTracks(String label) {
List<String> closedCaptionsTracksAsStrings = new ArrayList<String>(); MediaPlayerItem currentItem = mediaPlayer.getCurrentItem(); List<ClosedCaptionsTrack> closedCaptionsTracks =
currentItem.getClosedCaptionsTracks(); Iterator<ClosedCaptionsTrack> iterator =
closedCaptionsTracks.iterator();
if (currentItem != null) {
while (iterator.hasNext()) {
ClosedCaptionsTrack closedCaptionsTrack = iterator.next(); String isActive = closedCaptionsTrack.isActive() ? " (" + label
+ ")" : "";
closedCaptionsTracksAsStrings.add(closedCaptionsTrack.getName()
+ " : " + closedCaptionsTrack.getLanguage() + isActive);
}
}
return closedCaptionsTracksAsStrings;
}

//TVSDK v2.5
public List<String> getClosedCaptionTracks(String label) {

MediaPlayerItem currentItem = mediaPlayer.getCurrentItem(); List<ClosedCaptionsTrack> closedCaptionsTracks =
currentItem.getClosedCaptionsTracks();
Iterator<ClosedCaptionsTrack> iterator = closedCaptionsTracks.iterator();

List<String> closedCaptionsTracksAsStrings = new ArrayList<String>(); if (currentItem != null) {
while (iterator.hasNext()) {
ClosedCaptionsTrack closedCaptionsTrack = iterator.next(); closedCaptionsTracksAsStrings.
add(closedCaptionsTrack.getName() + " : ");
}
}
return closedCaptionsTracksAsStrings;
}
// Add snippet and desc for CaptionsUpdatedEventListener mediaPlayer.addEventListener(MediaPlayerEvent.CAPTIONS_UPDATED,
captionsUpdatedEventListener);

private final CaptionsUpdatedEventListener captionsUpdatedEventListener = new CaptionsUpdatedEventListener() {

@Override
public void onCaptionsUpdated(MediaPlayerItemEvent mediaPlayerItemEvent) { getActivity().findViewById(R.id.sbPlayerControlCC).setVisibility(
View.VISIBLE/*Visible*/);
}
};

Advertising changes

There are several ad-related changes in version 2.5.
Changes to advertising behavior
The default behavior of the ad playback when a user performs a seek beyond an Ad pod changes slightly in v2.5. If the ad break is not watched then the ad is played back after a seek forward. When the app is using the default ad policy, the ad break is removed after playback is completed. Using TVSDK v2.5, if a user seeks behind an Ad pod on the timeline and the ad break is not watched, then no ad is played back.
However, in TVSDK v1.4, by default, an ad is played back in case of a seek backward. For example if you seek backward between the third and fourth ad break, the default behavior for TVSDK v1.4 is to play the third ad break.
Ad Rules change
The Ad rules are specified using a JSON file. The format of the JSON file remains the same in both versions of the TVSDK. However, in TVSDK v2.5, the Ad rules JSON file must be hosted on a location accessible via a HTTP URL. The application can use an instance of AuditudeSettings.
//TVSDK v2.5
AuditudeSettings result = new AuditudeSettings(); result.setCRSRulesJsonURL(<http url of AdobeTVSDKConfig.json>);


In TVSDK version 1.4, this file is placed below the assets folder in the application and TVSDK loads the file.
Ad factory renaming
AdvertisingFactory is now named ContentFactory . With ContentFactory you can create customized advertising workflows by overriding some of its methods. Use return null to keep the default behaviors, as in the following:
//TVSDK v2.5
new ContentFactory() {
@Override
public AdPolicySelector retrieveAdPolicySelector(MediaPlayerItem item) { return new CustomAdPolicySelector();
}
@Override
public List<OpportunityGenerator> retrieveGenerators(MediaPlayerItem item) { return null;
}
@Override
public List<ContentResolver> retrieveResolvers(MediaPlayerItem item) { return null;
}
@Override
public List<CustomAdHandler> retrieveCustomAdPlaybackHandlers(MediaPlayerItem item) { return null;
}
};

Zero length Ad breaks
TVSDK 2.5 inserts zero length ad breaks as place holders when advertising server does not return any ads.
The zero length ad breaks can be determined by detecting the count of ads to be zero in the ad break using the onAdBreakStarted event and application has to handle these ad breaks accordingly.
Metadata changes
The Metadata class provides a more capable replacement for the former MetadataNode class.
  • The Metadata class can store strings, byte arrays, and other Metadata objects:
TVSDK v1.4
public AuditudeSettings createAdvertisementSettings() { Metadata advertisingParams = new MetadataNode();
advertisingParams.setValue("platform", Build.VERSION.RELEASE); advertisingParams.setValue("model", Build.MODEL);
AuditudeSettings adSettings = new AuditudeSettings();
// Set ad domain, zone id and media_id
...

// Set the target paramaters adSettings.setTargetingParameters(advertisingParams);

return adSettings;
}
//TVSDK v2.5
public AuditudeSettings createAdvertisementSettings() { Metadata advertisingParams = new Metadata();
advertisingParams.setValue("platform", Build.VERSION.RELEASE); advertisingParams.setValue("model", Build.MODEL);
AuditudeSettings adSettings = new AuditudeSettings();
// Set ad domain, zone id and media_id
...

// Set the target paramaters adSettings.setTargetingInfo(advertisingParams);

return adSettings;
}

  • The MetadataKeys enum replaces DefaultMetadataKeys . Not all of the keys in DefaultMetadataKeys are present in the new version.
//TVSDK v1.4
if (this.mediaPlayer != null && this.mediaPlayer.getCurrentItem() != null) { MetadataNode resourceMetadata =
(MetadataNode) this.mediaPlayer.getCurrentItem().getResource().getMetadata();
VideoAnalyticsMetadata vaMetadata = (VideoAnalyticsMetadata)
resourceMetadata.getNode(DefaultMetadataKeys.
VIDEO_ANALYTICS_METADATA_KEY.getValue());
}

//TVSDK v2.5
if (resourceMetadata.containsKey(VideoAnalyticsMetadata.
VIDEO_ANALYTICS_METADATA_KEY)) {
JSONObject vaMetadataObj =
new JSONObject(resourceMetadata.
getValue(VideoAnalyticsMetadata. VIDEO_ANALYTICS_METADATA_KEY));
vaMetadata = parseVideoAnalyticsMetadata(vaMetadataObj);
}

} catch (JSONException e) { e.printStackTrace();
}

  • Advertising metadata is now represented by the AdvertisingMetadata class, a subclass of Metadata, and is now stored in MediaPlayerItemConfig rather than MediaResource .
//TVSDK v1.4
MetadataNode mediaMetadata = new MetadataNode();

if (stream.live) { mediaMetadata.setNode(DefaultMetadataKeys.AUDITUDE_METADATA_KEY.getValue(),
getAdSettingsForLive());
} else {
mediaMetadata.setNode(DefaultMetadataKeys.AUDITUDE_METADATA_KEY.getValue(), getAdSettingsForVod());
}
MediaResource playerResource = MediaResource.createFromUrl(url, mediaMetadata);

//TVSDK v2.5
MediaPlayerItemConfig mediaItemConfig = new MediaPlayerItemConfig(context);

if (stream.live) {
AuditudeSettings adSettings = getAdSettingsForLive(); adSettings.setLivePreroll(true); mediaItemConfig.setAdvertisingMetadata(adSettings);
} else {
// Set the advertising parameters for VOD. mediaItemConfig.setAdvertisingMetadata(getAuditudeSettingsForVod());
}

// Create advertising metadata node Metadata mediaMetadata = new Metadata();

// Asset Url
MediaResource playerResource =
new MediaResource(url, MediaResource.Type.HLS, mediaMetadata);
try {
mediaPlayer.replaceCurrentResource(playerResource, mItemConfig);
} catch (MediaPlayerException mediaPlayerException) {
//handle exception.
}

Network configuration metadata, formerly a member of the MediaResource class, is now represented by the NetworkConfiguration class, and is now stored in MediaPlayerItemConfig rather than MediaResource .
//TVSDK v1.4
MediaResource playerResource = MediaResource.createFromUrl(url, mediaMetadata);
...

//TVSDK v2.5
MediaPlayerItemConfig mediaItemConfig = new MediaPlayerItemConfig(context);

NetworkConfiguration mediaNetworkConfiguration = mediaItemConfig.getNetworkConfiguration();

Changes to TimedMetadata parsing
The parsing of TimedMetadata has changed in 2.5 with respect to the datatypes for parsing ID3 tag.
//TVSDK v1.4
public void onTimedMetadata(TimedMetadata timedMetadata) { TimedMetadata.Type type = timedMetadata.getType();
if (type.equals(TimedMetadata.Type.ID3)){ PMPDemoApp.logger.i(LOG_TAG + "#onTimedMetadata",
"New ID3 tag detected at time = " + timedMetadata.getTime());
Metadata metadata = timedMetadata.getMetadata(); Set<String> keys = metadata.keySet();
for (String key : keys) {
String value = metadata.getValue(key); PMPDemoApp.logger.i(LOG_TAG + "#onTimedMetadata",
"Key = " + key + " Value = " + value);
}
} else if (_mediaPlayer.getPlaybackRange() != null &&
_mediaPlayer.getPlaybackRange().getDuration() > 0) { displayRanges();
PMPDemoApp.logger.i(LOG_TAG +
"#onTimedMetadata",
"New timed metadata. Name " + timedMetadata.getName() +
", time " + timedMetadata.getTime() + ", id " + timedMetadata.getId() + ", metadata " +
timedMetadata.getMetadata() + ".");
/*if (!_timedMetadataList.contains(timedMetadata)) {
_timedMetadataList.add(timedMetadata);
}*/
/* scte35 */
if (timedMetadata.getName().equalsIgnoreCase("#EXT-OATCLS-SCTE35")) { parseSCTE35Tag(timedMetadata);
}
}
}

//TVSDK v2.5
public void onTimedMetadata(TimedMetadataEvent timedMetadataEvent) { PMPDemoApp.logger.i(LOG_TAG + " inside"," ontimedmetadata"); TimedMetadata timedMetadata = timedMetadataEvent.getTimedMetadata();

TimedMetadata.Type type = timedMetadata.getType(); if (type.equals(TimedMetadata.Type.ID3)) {
PMPDemoApp.logger.i(LOG_TAG +
"#onTimedMetadata",
"New ID3 tag detected at time = " + timedMetadata.getTime());
Metadata metadata = timedMetadata.getMetadata();
if (metadata != null) { PMPDemoApp.logger.i(LOG_TAG +
"::expandID3Metadata", "isEmpty " + metadata.isEmpty());
Set<String> keys = metadata.keySet(); PMPDemoApp.logger.i(LOG_TAG + "::expandID3Metadata",
"No of keys=" + keys.size());
String s;
for (String key : keys) {
byte[] value = metadata.getByteArray(key); s = new String(value);
if (key.equalsIgnoreCase("APIC"))
s = "SUPPRESSING BINARY DATA FOR ATTACHED PICTURE.";
PMPDemoApp.logger.i(LOG_TAG + "::expandID3Metadata",
"Key=" + key + ", Length=" + value.length + ", Value=" + s.trim());
}
}
} else if (_mediaPlayer.getPlaybackRange() != null &&
_mediaPlayer.getPlaybackRange().getDuration() > 0) { displayRanges();
PMPDemoApp.logger.i(LOG_TAG +
"#onTimedMetadata",
"New timed metadata. Name " + timedMetadata.getName() +
", time " + timedMetadata.getTime() + ", id " + timedMetadata.getId() + ", metadata " +
timedMetadata.getMetadata() + ".");
/*if (!_timedMetadataList.contains(timedMetadata)) {
_timedMetadataList.add(timedMetadata);
}*/
/* scte35 */
if (timedMetadata.getName().equalsIgnoreCase("#EXT-OATCLS-SCTE35")) { PMPDemoApp.logger.i(LOG_TAG +
" inside ontimedmetadata"," ext"); parseSCTE35Tag(timedMetadata);
}
}
}

Other changes
The following additional changes are available in version 2.5:
  • The notifyClick() method has moved from MediaPlayerView to MediaPlayer .
  • AdPolicySelector is an interface, not a class. Implement all its methods.
  • AdPolicyInfo now contains a list of AdBreakTimelineItem , not AdBreakPlacement .
  • API name of ContentResolver abstract class is changed.
  • PlacementOpportunityDetector is no longer available. Instead, extend the OpportunityGenerator abstract class. The reference implementation provides an example of this.
  • The parameters of the AdBreakPlacement constructor are the same, but in a different order. For a sample implementation, see the Reference Player implementation bundled with the product.

Changes in DRM

The majority of changes in this version are in the DRM layer. The following table shows additional changes between versions 1.4 and 2.5:
DRMManager Method
Success Callback in 1.4
Error Callback in 1.4
Listener in 2.5
acquireLicense
DRMLicenseAcquiredCallback
DRMOperationErrorCallback
DRMAcquireLicenseListener
acquirePreviewLicense
DRMLicenseAcquiredCallback
DRMOperationErrorCallback
DRMAcquireLicenseListener
authenticate
DRMAuthenticationCompleteCallback
DRMOperationErrorCallback
DRMAuthenticateListener
createMetadataFromBytes
NA
DRMOperationErrorCallback
DRMErrorListener
initialize
DRMOperationCompleteCallback
DRMOperationErrorCallback
DRMOperationCompleteListener
joinLicenseDomain
DRMOperationCompleteCallback
DRMOperationErrorCallback
DRMOperationCompleteListener
leaveLicenseDomain
DRMOperationCompleteCallback
DRMOperationErrorCallback
DRMOperationCompleteListener
resetDRM
DRMOperationCompleteCallback
DRMOperationErrorCallback
DRMOperationCompleteListener
returnLicense
DRMLicenseReturnCompleteCallback
DRMOperationErrorCallback
DRMReturnLicenseListener
setAuthenticationToken
DRMOperationCompleteCallback
DRMOperationErrorCallback
DRMOperationCompleteListener
storeLicenseBytes
DRMOperationCompleteCallback
DRMOperationErrorCallback
DRMOperationCompleteListener
//TVSDK 1.4
private final MediaPlayer.DRMEventListener _drmEventListener = new MediaPlayer.DRMEventListener() {

@Override
public void onDRMMetadata(final DRMMetadataInfo drmMetadataInfo) { PMPDemoApp.logger.i(LOG_TAG + "::MediaPlayer.DRMEventListener#onDRMMetadata",
"DRM metadata available: " + drmMetadataInfo + ".");
if (getSuppressAutoPlayPreference() && drmMetadataInfo != null) {
// if suppress play setting, then the user is a tester who
// wishes to manually intervene between the metadata available
// event and the play event, so launch the metadata interface
// and give it the loaded metadata bytes, but only once
_drmMetadata = drmMetadataInfo.getDRMMetadata();
}
if (drmMetadataInfo == null ||
!DRMHelper.isAuthNeeded(drmMetadataInfo.getDRMMetadata())) { PMPDemoApp.logger.i(LOG_TAG + "#onDRMMetadata", "DRM auth is not needed."); return;
}
// Perform DRM auth.
// Possible logic might take into consideration a threshold
// between the current player time and the DRM metadata start
// time. For the time being, we resolve it as soon as we receive
// the DRM metadata.

DRMManager drmManager = _mediaPlayer.getDRMManager(); if (drmManager == null) {
PMPDemoApp.logger.e(LOG_TAG + "#onDRMMetadata", "DRMManager is null."); return;
}
SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(getActivity()); String authUser =
sharedPreferences.getString(PMPDemoApp.SETTINGS_DRM_USERNAME,
getResources().getString(R.string.drmUsername));
String authPass = sharedPreferences.getString(PMPDemoApp.SETTINGS_DRM_PASSWORD,
getResources().getString(R.string.drmPassword));
DRMHelper.performDrmAuthentication(drmManager,
drmMetadataInfo.getDRMMetadata(), authUser,
authPass,
new DRMAuthenticationListener() {
@Override
public void onAuthenticationStart() { PMPDemoApp.logger.i(LOG_TAG + "#onAuthenticationStart",
"DRM authentication started for DRM metadata at [" + drmMetadataInfo.getPrefetchTimestamp() + "].");
}
@Override
public void onAuthenticationError(long majorCode,
long minorCode, Exception e) {
if (getActivity() == null) { return;
}
PMPDemoApp.logger.e(LOG_TAG +
"#onAuthenticationError",
"DRM authentication failed. " + majorCode + " 0x" + Long.toHexString(minorCode));
_handler.post(new Runnable() {
@Override
public void run() { showToast(getString(R.string.drmAuthenticationError)); getActivity().finish();
}
});
}
@Override
public void onAuthenticationComplete(byte[] authenticationToken) { PMPDemoApp.logger.i(LOG_TAG +
"#onAuthenticationComplete",
"Auth successful for DRM metadata at [" + drmMetadataInfo.getPrefetchTimestamp() + "].");
}
});
}
};

//TVSDK 2.5
private final DRMMetadataInfoEventListener drmMetadataInfoEventListener = new DRMMetadataInfoEventListener() {
@Override
public void onDRMMetadataInfo(DRMMetadataInfoEvent drmMetadataInfoEvent) { final DRMMetadataInfo drmMetadataInfo =
drmMetadataInfoEvent.getDRMMetadataInfo();
PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.DRMEventListener#onDRMMetadata",
"DRM metadata available: " + drmMetadataInfo + ".");
if (getSuppressAutoPlayPreference() && drmMetadataInfo != null) {
// if suppress play setting, then the user is a tester who
// wishes to manually intervene between the metadata
// available event and the play event, so launch the
// metadata interface and give it the loaded metadata
// bytes, but only once
drmMetadata = drmMetadataInfo.getDRMMetadata();
}
if (drmMetadataInfo ==
null || !DRMHelper.isAuthNeeded(drmMetadataInfo.getDRMMetadata())) { PMPDemoApp.logger.i(LOG_TAG +
"#onDRMMetadata",
"DRM auth is not needed.");
return;
}
// Perform DRM auth.
// Possible logic might take into consideration a threshold between
// the current player time and the DRM metadata start time. For the
// time being, we resolve it as soon as we receive the DRM metadata.

DRMManager drmManager = _mediaPlayer.getDRMManager(); if (drmManager == null) {
PMPDemoApp.logger.e(LOG_TAG +
"#onDRMMetadata", "DRMManager is null.");
return;
}

SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
String authUser = sharedPreferences.getString(PMPDemoApp.SETTINGS_DRM_USERNAME,
getResources().getString(R.string.drmUsername));
String authPass = sharedPreferences.getString(PMPDemoApp.SETTINGS_DRM_PASSWORD,
getResources().getString(R.string.drmPassword));
DRMHelper.performDrmAuthentication(drmManager,
drmMetadataInfo.getDRMMetadata(), authUser,
authPass,
new DRMAuthenticationListener() {
@Override
public void onAuthenticationStart() { PMPDemoApp.logger.i(LOG_TAG +
"#onAuthenticationStart",
"DRM authentication started for DRM metadata at [" + drmMetadataInfo.getPrefetchTimestamp() + "].");
}
@Override
public void onAuthenticationError(int major,
int minor,
String erroString, String serverErrorURL) {
if (getActivity() == null) { return;
}
PMPDemoApp.logger.e(LOG_TAG +
"#onAuthenticationError",
"DRM authentication failed. " + major +
" 0x" +
Long.toHexString(minor));
_handler.post(new Runnable() {
@Override
public void run() { showToast(getString(R.string.drmAuthenticationError)); getActivity().finish();
}
});
}
@Override
public void onAuthenticationComplete(byte[] authenticationToken) { PMPDemoApp.logger.i(LOG_TAG +
"#onAuthenticationComplete",
"Auth successful for DRM metadata at [" + drmMetadataInfo.getPrefetchTimestamp() + "].");
}
});
}
};

The static DRMManager instance that was available in 1.4 after mediaplayer is created is now available after the onDRMMetadataInfo event listener is triggered.

Error events and handling

Changes in error handling
The MediaError class has been replaced with the Notification class. The only difference between the classes MediaError and Notification is that the latter does not contain a description attribute. The TVSDK 1.4 error codes with values 101xxx, 102xxx,104xxx,106xxx,107xxx,109xxx do not exist in TVSDK 2.5. For the playback codes in TVSDK 2.5, see Native Error- video playback values .
The following are examples of error handling in TVSDK 1.4 and 2.5:
//TVSDK v1.4
@Override
public void onStateChanged(MediaPlayer.PlayerState state, MediaPlayerNotification notification) {
PMPDemoApp.logger.i(LOG_TAG + "::MediaPlayer.PlayerStateEventListener#onStateChanged()", "Player state changed to [" + state
+ "].");

switch (state) {
// Non recoverable state - either reset player and reinitialize
// or release the player and create a new one.
case ERROR: PMPDemoApp.logger.e(LOG_TAG +
"::MediaPlayer.PlayerStateEventListener#onStateChanged()", "Error: " + notification + "."); getActivity().finish();
break;
default:
// do nothing
}
}

//TVSDK v2.5
private final StatusChangeEventListener statusChangeEventListener = new StatusChangeEventListener() {
@Override
public void onStatusChanged(MediaPlayerStatusChangeEvent mediaPlayerStatusChangeEvent)
{
MediaPlayerStatus state = mediaPlayerStatusChangeEvent.getStatus(); PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.PlayerStateEventListener#onStateChanged()", "Player state changed to [" + state
+ "].");
switch (state) {
// Non recoverable state - either reset player and reinitialize
// or release the player and create a new one.
case ERROR:
PMPDemoApp.logger.e(LOG_TAG + "::MediaPlayer.PlayerStateEventListener#onStateChanged()", "Error: ");
printErrorMetadata(mediaPlayerStatusChangeEvent.getMetadata()); showToast("MediaPlayer status ERROR: Look at the logs for details"); getActivity().finish();
break;
default:
// do nothing
}
}
};

All the recoverable errors are treated as warnings and are handled using the NotificationEventListener in TVSDK 2.5. The warnings appear as notifications with the onOperationFailed Listener in the QOS handler in TVSDK 1.4 where as in TVSDK 2.5 the Notification is separate event. The warning handled in 1.4 and 2.5 is as follows:
//TVSDK v1.4
private final MediaPlayer.QOSEventListener _qosEventListener = new MediaPlayer.QOSEventListener()
{
@Override
public void onBufferStart() {
PMPDemoApp.logger.i(LOG_TAG + "::MediaPlayer.QOSEventListener#onBufferStart()", "Buffering started.");
}
@Override
public void onBufferComplete() {
PMPDemoApp.logger.i(LOG_TAG + "::MediaPlayer.QOSEventListener#onBufferComplete()", "Buffering complete.");
}
@Override
public void onSeekStart() {
PMPDemoApp.logger.i(LOG_TAG + "::MediaPlayer.QOSEventListener#onSeekStart()", "Seek starting.");
}
@Override
public void onSeekComplete(long adjustedTime) {
PMPDemoApp.logger.i(LOG_TAG + "::MediaPlayer.QOSEventListener#onSeekComplete()", "Seek complete at position: " + adjustedTime + ".");
}
@Override
public void onLoadInfo(LoadInfo loadInfo) {
PMPDemoApp.logger.i(LOG_TAG + "::MediaPlayer.QOSEventListener#onLoadInfo()", "Url: " + loadInfo.getUrl() + ", size: "
+ loadInfo.getSize() + " bytes, media duration: " + loadInfo.getMediaDuration() + "ms, download duration: " + loadInfo.getDownloadDuration() + "ms");
}
@Override
public void onOperationFailed(MediaPlayerNotification.Warning warning) {

StringBuffer sb = new StringBuffer("Player operation failed: "); sb.append(warning.getCode()).append(" - ").append(warning.getDescription()); if (warning.getMetadata() != null) {
sb.append("Warning metadata: ").append(warning.getMetadata().toString());
}

MediaPlayerNotification innerNotification = warning.getInnerNotification(); registerFailedAudioTrackIndex(innerNotification); handleFailedSeekEvent(innerNotification);

while (innerNotification != null) { sb.append("Inner notification: ");
sb.append(innerNotification.getCode()).append(" - ").append(innerNotification.getDescription());
Metadata metadata = innerNotification.getMetadata(); if (metadata != null) {
for (String key : metadata.keySet()) { sb.append(" - ").append

//TVSDK v2.5
private final NotificationEventListener notificationEventListener = new NotificationEventListener() {
/**
* An example of dumping the information within a Notification. Here, we don't handle the event in
* any way except to print a log message.
* @param event The NotificationEvent
*/
@Override
public void onNotification(NotificationEvent event) { Notification notification = event.getNotification(); Notification.Type type = notification.getType(); StringBuilder sb = new StringBuilder(); sb.append("[").append(type).append("]");
sb.append(" Player operation failed (").append(notification.getCode()).append(")"); Metadata metadata = notification.getMetadata();
if (metadata != null)
{
for (String key : metadata.keySet())
{
sb.append(", ").append(key).append(" = ").append(metadata.getValue(key));
}
}
Notification inner = notification.getInnerNotification(); if (inner != null) {
sb.append(" :: Inner Notification [").append(inner.getType()).append("](").append(inner.getCode()).append(")");
Metadata innerMetadata = inner.getMetadata(); if (innerMetadata != null) {
for (String iKey : innerMetadata.keySet()) { sb.append(", ").append(iKey).append(" =
").append(innerMetadata.getValue(iKey));
}
}
}
switch(type)
{
case ERROR:
PMPDemoApp.logger.e(LOG_TAG, sb.toString()); break;
case WARNING:
PMPDemoApp.logger.w(LOG_TAG, sb.toString()); break;
default:
PMPDemoApp.logger.i(LOG_TAG, sb.toString()); break;
}
showToast(sb.toString()); PMPDemoApp.logger.d(LOG_TAG, sb.toString());
}
};

New exceptions
While TVSDK v1.4 used a combination of nulls, error returns, and a variety of exceptions ( MediaPlayerException , IllegalStateException , and IllegalArgumentException ), TVSDK v2.5 generatesMediaPlayerException for all errors.
To get details on a MediaPlayerException, you can use getErrorCode() .
An example of the change is below:
//TVSDK v1.4
public void setBufferControlParams() { try {
mediaPlayer.setBufferControlParameters(getBufferParamsFromSettings());
} catch (IllegalArgumentException e) { PrimetimeReference.logger.w(LOG_TAG +
"#setBufferControlParams",
"Unable to apply buffering params: " + e.getMessage() + ".");
}
}

//TVSDK v2.5
public void setBufferControlParams() { try {
mediaPlayer.setBufferControlParameters(getBufferParamsFromSettings());
} catch (MediaPlayerException e) { PrimetimeReference.logger.w(LOG_TAG + "#setBufferControlParams", "Unable to apply buffering params: " + e.getMessage() + ".");
}
}

Changes to QOS parameters

There are minor changes to QOSProvider object properties:
  • The TimeToFirstFrame is not available in TVSDK 2.5.
  • TVSDK 2.5 QOSProvider has a new property to determine the perceived bandwidth during a streaming session.
//TVSDK v1.4
public void updateQosInformation(PlaybackInformation playbackInformation) { if (playbackInformation == null) {
return;
}
setQosItem("Frame rate", (int) playbackInformation.getFrameRate() +
" (" + (int) playbackInformation.getDroppedFrameCount() + " dropped)"); setQosItem("Bitrate", (int) playbackInformation.getBitrate()); setQosItem("Buffering time", (long) playbackInformation.getBufferingTime()); setQosItem("Buffer length", (long) playbackInformation.getBufferLength()); setQosItem("Buffer time", (long) playbackInformation.getBufferTime()); setQosItem("Empty buffer count", (int) playbackInformation.getEmptyBufferCount()); setQosItem("Time to load", (int) playbackInformation.getTimeToLoad()); setQosItem("Time to start", (int) playbackInformation.getTimeToStart()); setQosItem("Time to first frame", (int) playbackInformation.getTimeToFirstFrame()); setQosItem("Time to prepare", (int) playbackInformation.getTimeToPrepare()); setQosItem("Last seek time", (int) playbackInformation.getLastSeekTime());
}

//TVSDK v2.5
public void updateQosInformation(PlaybackInformation playbackInformation) { if (playbackInformation == null) {
return;
}
setQosItem("Frame rate", (int) playbackInformation.getFrameRate() +
" (" + (int) playbackInformation.getDroppedFrameCount() + " dropped)"); setQosItem("Bitrate", (int) playbackInformation.getBitRate()); setQosItem("Buffering time", (long) playbackInformation.getBufferingTime()); setQosItem("Buffer length", (long) playbackInformation.getBufferLength()); setQosItem("Buffer time", (long) playbackInformation.getBufferTime()); setQosItem("Empty buffer count", (int) playbackInformation.getEmptyBufferCount()); setQosItem("Time to load", (int) playbackInformation.getTimeToLoad()); setQosItem("Time to start", (int) playbackInformation.getTimeToStart());
setQosItem("Time to prepare", (int) playbackInformation.getTimeToPrepare());
setQosItem("Perceived Bandwidth", (int) playbackInformation.getPerceivedBandwidth());

  • The QOSEventListener::onOperationFailed() no longer exists in TVSDK 2.5. The warnings that used to appear in this event listener will now appear in the NotificationEventListener::onNotification() event listener.
  • The QOSProvider event listeners onBufferStart() , onBufferComplete() , onSeekStart() , onSeekComplete() , and onLoadInfo() are individual event listeners that are bound with a mediaPlayer instance.
//TVSDK v1.4
private final MediaPlayer.QOSEventListener _qosEventListener = new MediaPlayer.QOSEventListener() {

@Override
public void onBufferStart() { PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.QOSEventListener#onBufferStart()", "Buffering started.");
showBufferingSpinner();
}
@Override
public void onBufferComplete() { PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.QOSEventListener#onBufferComplete()", "Buffering complete.");
hideBufferingSpinner();
}
@Override
public void onSeekStart() { PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.QOSEventListener#onSeekStart()", "Seek starting.");
inTrickPlayMode = false;
}
@Override
public void onSeekComplete(long adjustedTime) { PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.QOSEventListener#onSeekComplete()", "Seek complete at position: " +
adjustedTime + ".");
_controlBar.setPosition(adjustedTime);
if (adjustedTime == _mediaPlayer.getPlaybackRange().getEnd() &&
_mediaPlayer.getStatus() == MediaPlayer.PlayerState.COMPLETE) {
// Show the replay button. showReplayButton();
}
if (_mediaPlayer.getStatus() == MediaPlayer.PlayerState.PAUSED) {
// Show the pause icon.
_controlBar.pause();
}
}
@Override
public void onLoadInfo(LoadInfo loadInfo) { PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.QOSEventListener#onLoadInfo()", "Url: " +
loadInfo.getUrl() + ", size: " + loadInfo.getSize() +
" bytes, media duration: " +
loadInfo.getMediaDuration() + "ms, download duration: " +
loadInfo.getDownloadDuration() + "ms");
 
public void onOperationFailed(MediaPlayerNotification.Warning warning) {

StringBuffer sb = new StringBuffer("Player operation failed: "); sb.append(warning.getCode()).append(" - ").append(warning.getDescription()); if (warning.getMetadata() != null) {
sb.append("Warning metadata: ").append(warning.getMetadata().toString());
}

MediaPlayerNotification innerNotification = warning.getInnerNotification(); registerFailedAudioTrackIndex(innerNotification); handleFailedSeekEvent(innerNotification);

while (innerNotification != null) { sb.append("Inner notification: ");
sb.append(innerNotification.getCode()).append(" - "). append(innerNotification.getDescription());
Metadata metadata = innerNotification.getMetadata(); if (metadata != null) {
for (String key : metadata.keySet()) { sb.append(" - ").append(key).append(": ").
append(metadata.getValue(key));
}
}
innerNotification = innerNotification.getInnerNotification();
}
String errMsg = sb.toString(); PMPDemoApp.logger.w(LOG_TAG +
"::MediaPlayer.QOSEventListener#onOperationFailed()", errMsg);
showToast(errMsg);
}
};

//TVSDK v2.5
mediaPlayer.addEventListener(MediaPlayerEvent.LOAD_INFORMATION_AVAILABLE,
loadInfoEventListener); mediaPlayer.addEventListener(MediaPlayerEvent.BUFFERING_BEGIN,
bufferingBeginEventListener); mediaPlayer.addEventListener(MediaPlayerEvent.BUFFERING_END,
bufferingEndEventListener); mediaPlayer.addEventListener(MediaPlayerEvent.SEEK_BEGIN,
seekBeginEventListener); mediaPlayer.addEventListener(MediaPlayerEvent.SEEK_END,
seekEndEventListener);
// Buffering events.
BufferingBeginEventListener bufferingBeginEventListener = new BufferingBeginEventListener() {
@Override
public void onBufferingBegin(BufferEvent bufferEvent) { PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.QOSEventListener#onBufferStart()", "Buffering started.");
showBufferingSpinner();
}
};

BufferingEndEventListener bufferingEndEventListener = new BufferingEndEventListener() {
@Override
public void onBufferingEnd(BufferEvent bufferEvent) { PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.QOSEventListener#onBufferComplete()",
"Buffering complete."); hideBufferingSpinner();
}
};

BufferPreparedEventListener bufferPreparedEventListener = new BufferPreparedEventListener() {
@Override
public void onBufferPrepared() {
}
};
// seek events.
SeekBeginEventListener seekBeginEventListener = new SeekBeginEventListener() {

@Override
public void onSeekBegin(SeekEvent seekEvent) { PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.QOSEventListener#onSeekStart()", "Seek starting.");
inTrickPlayMode = false;
}
};
SeekEndEventListener seekEndEventListener = new SeekEndEventListener() {
@Override
public void onSeekEnd(SeekEvent seekEvent) {
double adjustedTime = seekEvent.getActualPosition();
PMPDemoApp.logger.i(LOG_TAG +
"::MediaPlayer.QOSEventListener#onSeekComplete()", "Seek complete at position: " + adjustedTime + ".");
_controlBar.setPosition((long) adjustedTime);
if (adjustedTime == _mediaPlayer.getPlaybackRange().getEnd() &&
_mediaPlayer.getStatus() == MediaPlayerStatus.COMPLETE) {
// Show the replay button. showReplayButton();
}
if (_mediaPlayer.getStatus() == MediaPlayerStatus.PAUSED) {
// Show the pause icon.
_controlBar.pause();
}
}
};
/**
* LoadInformationEventListener that intercepts PSDK load info events
*/
private final LoadInformationEventListener loadInfoEventListener = new LoadInformationEventListener() {

@Override
public void onLoadInformation(LoadInformationEvent event) { LoadInformation loadInfo = event.getLoadInfomation(); PMPDemoApp.logger.i(
LOG_TAG + "::LoadInformationEventListener#onLoadInfomation()", "Url: " + loadInfo.getUrl() + ", size: "
+ loadInfo.getSize()
+ " bytes, download duration: "
+ loadInfo.getDownloadDuration() + "ms");
}
};

Helpful resources