From 851c2f1427b8d82bd8eb84be36f75652f68dbabc Mon Sep 17 00:00:00 2001 From: Noam Tamim Date: Sun, 2 Apr 2017 20:00:43 +0300 Subject: [PATCH] Add playSessionId to playManifest URLs (#196) * UrlDecorator allows "decorating" (changing) a URL before usage. PKMediaConfig now has a UrlDecorator field -- app can set it per media. * PlayManifestSessionIdDecorator -- UrlDecorator that adds playSessionId to playManifest URLs. * Moved sessionId value from PlayManifestSessionIdDecorator to PlayerController (Player.getSessionId()). * Moved urlDecorator instance from PKMediaConfig to Player. * Replaced UrlDecorator with RequestInfo.Decorator. RequestInfo contains url and headers. The decorator allows changing both. Added Player.Settings. Allows changing player behavior -- for now only setting contentRequestDecorator. * Rename PlayManifestSessionIdDecorator to KalturaPlaybackRequestDecorator. * PlayerLoader: set contentRequestDecorator to KalturaPlaybackRequestDecorator. * fix coach * Removed Player.Settings getter and added javadoc. * Moved KalturaPlaybackRequestDecorator to plugins.playback package. * Added a plugin that wraps KalturaPlaybackRequestDecorator. * Remove KalturaPlaybackUrlPlugin. It's not needed. * Encapsulate KalturaPlaybackRequestDecorator setup. * Made the request decorator's constructor private. --- .../kaltura/playkit/plugins/MockPlayer.java | 19 ++++++++ .../com/kaltura/playkit/PKMediaConfig.java | 1 + .../com/kaltura/playkit/PKRequestInfo.java | 38 +++++++++++++++ .../main/java/com/kaltura/playkit/Player.java | 25 ++++++++++ .../kaltura/playkit/PlayerDecoratorBase.java | 14 +++++- .../com/kaltura/playkit/PlayerLoader.java | 4 ++ .../playkit/player/ExoPlayerWrapper.java | 22 ++++----- .../playkit/player/MediaPlayerWrapper.java | 17 +++---- .../playkit/player/PKMediaSourceConfig.java | 47 +++++++++++++++++++ .../playkit/player/PlayerController.java | 44 +++++++++++++---- .../kaltura/playkit/player/PlayerEngine.java | 6 +-- .../KalturaPlaybackRequestDecorator.java | 41 ++++++++++++++++ 12 files changed, 247 insertions(+), 31 deletions(-) create mode 100644 playkit/src/main/java/com/kaltura/playkit/PKRequestInfo.java create mode 100644 playkit/src/main/java/com/kaltura/playkit/player/PKMediaSourceConfig.java create mode 100644 playkit/src/main/java/com/kaltura/playkit/plugins/playback/KalturaPlaybackRequestDecorator.java diff --git a/playkit/src/androidTest/java/com/kaltura/playkit/plugins/MockPlayer.java b/playkit/src/androidTest/java/com/kaltura/playkit/plugins/MockPlayer.java index 6af689210..9a5f9771c 100644 --- a/playkit/src/androidTest/java/com/kaltura/playkit/plugins/MockPlayer.java +++ b/playkit/src/androidTest/java/com/kaltura/playkit/plugins/MockPlayer.java @@ -5,10 +5,13 @@ import com.kaltura.playkit.PKEvent; import com.kaltura.playkit.PKMediaConfig; +import com.kaltura.playkit.PKRequestInfo; import com.kaltura.playkit.Player; import com.kaltura.playkit.ads.AdController; import com.kaltura.playkit.player.PlayerView; +import java.util.UUID; + /** * Created by zivilan on 11/12/2016. */ @@ -18,6 +21,17 @@ public class MockPlayer implements Player { private int duration = 100; private long currentPosition = 0; + @Override + public Settings getSettings() { + return new Settings() { + + @Override + public Settings setContentRequestDecorator(PKRequestInfo.Decorator contentRequestDecorator) { + return null; + } + }; + } + @Override public void prepare(@NonNull PKMediaConfig playerConfig) { @@ -128,6 +142,11 @@ public AdController getAdController() { return null; } + @Override + public UUID getSessionId() { + return null; + } + public void setDuration(int duration){ this.duration = duration; } diff --git a/playkit/src/main/java/com/kaltura/playkit/PKMediaConfig.java b/playkit/src/main/java/com/kaltura/playkit/PKMediaConfig.java index 93f9f4ed2..90169b4fd 100644 --- a/playkit/src/main/java/com/kaltura/playkit/PKMediaConfig.java +++ b/playkit/src/main/java/com/kaltura/playkit/PKMediaConfig.java @@ -38,3 +38,4 @@ public PKMediaConfig setMediaEntry(PKMediaEntry mediaEntry) { return this; } } + diff --git a/playkit/src/main/java/com/kaltura/playkit/PKRequestInfo.java b/playkit/src/main/java/com/kaltura/playkit/PKRequestInfo.java new file mode 100644 index 000000000..ba711bcd6 --- /dev/null +++ b/playkit/src/main/java/com/kaltura/playkit/PKRequestInfo.java @@ -0,0 +1,38 @@ +package com.kaltura.playkit; + +import android.net.Uri; + +import java.util.Map; + +public class PKRequestInfo { + + private Uri url; + private Map headers; + + public PKRequestInfo(Uri url, Map headers) { + this.url = url; + this.headers = headers; + } + + public Uri getUrl() { + return url; + } + + public PKRequestInfo setUrl(Uri url) { + this.url = url; + return this; + } + + public Map getHeaders() { + return headers; + } + + public PKRequestInfo setHeaders(Map headers) { + this.headers = headers; + return this; + } + + public interface Decorator { + PKRequestInfo getRequestInfo(PKRequestInfo requestInfo); + } +} diff --git a/playkit/src/main/java/com/kaltura/playkit/Player.java b/playkit/src/main/java/com/kaltura/playkit/Player.java index 8de452119..e31e80cf9 100644 --- a/playkit/src/main/java/com/kaltura/playkit/Player.java +++ b/playkit/src/main/java/com/kaltura/playkit/Player.java @@ -7,11 +7,30 @@ import com.kaltura.playkit.player.PlayerView; import com.kaltura.playkit.utils.Consts; +import java.util.UUID; + /** * Created by Noam Tamim @ Kaltura on 18/09/2016. */ public interface Player { + /** + * Interface used for setting optional Player settings. + */ + interface Settings { + /** + * Set the Player's contentRequestDecorator. + * @param contentRequestDecorator + * @return Player Settings. + */ + Settings setContentRequestDecorator(PKRequestInfo.Decorator contentRequestDecorator); + } + + /** + * Get the Player's {@link Settings} object, for setting some optional properties. + * @return Player Settings. + */ + Settings getSettings(); /** * Prepare the player for playback. @@ -134,5 +153,11 @@ public interface Player { void seekTo(long position); AdController getAdController(); + + /** + * Get the Player's SessionId. The SessionId is initialized when the player loads. + * @return Player's SessionId, as a UUID object. + */ + UUID getSessionId(); } diff --git a/playkit/src/main/java/com/kaltura/playkit/PlayerDecoratorBase.java b/playkit/src/main/java/com/kaltura/playkit/PlayerDecoratorBase.java index 255ab5411..3224e7256 100644 --- a/playkit/src/main/java/com/kaltura/playkit/PlayerDecoratorBase.java +++ b/playkit/src/main/java/com/kaltura/playkit/PlayerDecoratorBase.java @@ -6,8 +6,15 @@ import com.kaltura.playkit.ads.AdController; import com.kaltura.playkit.player.PlayerView; +import java.util.UUID; + public class PlayerDecoratorBase implements Player { - + + @Override + public Settings getSettings() { + return player.getSettings(); + } + @Override public void prepare(@NonNull PKMediaConfig mediaConfig) { player.prepare(mediaConfig); @@ -33,6 +40,11 @@ public AdController getAdController() { return player.getAdController(); } + @Override + public final UUID getSessionId() { + return player.getSessionId(); + } + @Override public void play() { player.play(); diff --git a/playkit/src/main/java/com/kaltura/playkit/PlayerLoader.java b/playkit/src/main/java/com/kaltura/playkit/PlayerLoader.java index fdf45399c..5eba29fea 100644 --- a/playkit/src/main/java/com/kaltura/playkit/PlayerLoader.java +++ b/playkit/src/main/java/com/kaltura/playkit/PlayerLoader.java @@ -5,6 +5,7 @@ import android.support.annotation.Nullable; import com.kaltura.playkit.player.PlayerController; +import com.kaltura.playkit.plugins.playback.KalturaPlaybackRequestDecorator; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -41,6 +42,9 @@ class PlayerLoader extends PlayerDecoratorBase { public void load(@NonNull PKPluginConfigs pluginsConfig) { PlayerController playerController = new PlayerController(context); + + // By default, set Kaltura decorator. + KalturaPlaybackRequestDecorator.setup(playerController); playerController.setEventListener(new PKEvent.Listener() { @Override diff --git a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java index 4edc4bfcc..eecd96b0e 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java @@ -36,7 +36,6 @@ import com.kaltura.playkit.BuildConfig; import com.kaltura.playkit.PKLog; import com.kaltura.playkit.PKMediaFormat; -import com.kaltura.playkit.PKMediaSource; import com.kaltura.playkit.PlaybackParamsInfo; import com.kaltura.playkit.PlayerEvent; import com.kaltura.playkit.PlayerState; @@ -69,7 +68,8 @@ class ExoPlayerWrapper implements PlayerEngine, ExoPlayer.EventListener { private DeferredDrmSessionManager drmSessionManager; private PlayerEvent.Type currentEvent; - private PlayerState currentState = PlayerState.IDLE, previousState; + private PlayerState currentState = PlayerState.IDLE; + private PlayerState previousState; private Factory mediaDataSourceFactory; private Handler mainHandler = new Handler(Looper.getMainLooper()); @@ -149,25 +149,25 @@ private DefaultTrackSelector initializeTrackSelector() { return trackSelector; } - private void preparePlayer(PKMediaSource pkMediaSource) { + private void preparePlayer(PKMediaSourceConfig sourceConfig) { sameErrorOccurrenceCounter = 0; - drmSessionManager.setMediaSource(pkMediaSource); + drmSessionManager.setMediaSource(sourceConfig.mediaSource); shouldGetTracksInfo = true; - this.lastPlayedSource = Uri.parse(pkMediaSource.getUrl()); - MediaSource mediaSource = buildExoMediaSource(pkMediaSource); + this.lastPlayedSource = sourceConfig.getUrl(); + MediaSource mediaSource = buildExoMediaSource(sourceConfig); player.prepare(mediaSource, shouldResetPlayerPosition, shouldResetPlayerPosition); changeState(PlayerState.LOADING); } - private MediaSource buildExoMediaSource(PKMediaSource source) { - PKMediaFormat format = source.getMediaFormat(); + private MediaSource buildExoMediaSource(PKMediaSourceConfig sourceConfig) { + PKMediaFormat format = sourceConfig.mediaSource.getMediaFormat(); if (format == null) { // TODO: error? return null; } - Uri uri = Uri.parse(source.getUrl()); + Uri uri = sourceConfig.getUrl(); switch (format) { @@ -357,13 +357,13 @@ public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray tra } @Override - public void load(PKMediaSource mediaSource) { + public void load(PKMediaSourceConfig mediaSourceConfig) { log.d("load"); if (player == null) { initializePlayer(); } - preparePlayer(mediaSource); + preparePlayer(mediaSourceConfig); } @Override diff --git a/playkit/src/main/java/com/kaltura/playkit/player/MediaPlayerWrapper.java b/playkit/src/main/java/com/kaltura/playkit/player/MediaPlayerWrapper.java index d6c03c26d..2a58cfbe1 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/MediaPlayerWrapper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/MediaPlayerWrapper.java @@ -11,7 +11,6 @@ import com.kaltura.playkit.PKDrmParams; import com.kaltura.playkit.PKLog; -import com.kaltura.playkit.PKMediaSource; import com.kaltura.playkit.PlaybackParamsInfo; import com.kaltura.playkit.PlayerEvent; import com.kaltura.playkit.PlayerState; @@ -37,12 +36,12 @@ public class MediaPlayerWrapper implements PlayerEngine, SurfaceHolder.Callback, private static final PKLog log = PKLog.get("MediaPlayerWrapper"); - private static int ILLEGAL_STATEׁ_OPERATION = -38; + private static final int ILLEGAL_STATEׁ_OPERATION = -38; private Context context; private MediaPlayer player; private MediaPlayerView mediaPlayerView; - private PKMediaSource mediaSource; + private PKMediaSourceConfig mediaSourceConfig; private String assetUri; private String licenseUri; @@ -82,14 +81,16 @@ public void onEvent(DrmEvent event) { } @Override - public void load(PKMediaSource mediaSource) { + public void load(PKMediaSourceConfig mediaSourceConfig) { log.d("load"); - if (currentState != null && this.mediaSource != null && !this.mediaSource.equals(mediaSource) && prepareState != PREPARING) { + + if (currentState != null && this.mediaSourceConfig != null && !this.mediaSourceConfig.equals(mediaSourceConfig) && prepareState != PREPARING) { player.reset(); currentState = PlayerState.IDLE; prepareState = PrepareState.NOT_PREPARED; } - this.mediaSource = mediaSource; + + this.mediaSourceConfig = mediaSourceConfig; if ((currentState == null || currentState == PlayerState.IDLE) && prepareState != PREPARING) { initializePlayer(); } @@ -103,7 +104,7 @@ private void initializePlayer() { //player.setAudioStreamType(AudioManager.STREAM_MUSIC); //player.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT); - assetUri = mediaSource.getUrl(); + assetUri = mediaSourceConfig.getUrl().toString(); String assetAcquireUri = getWidevineAssetAcquireUri(assetUri); try { mediaPlayerView.getSurfaceHolder().addCallback(this); @@ -113,7 +114,7 @@ private void initializePlayer() { log.e(e.toString()); } if (drmClient.needToAcquireRights(assetAcquireUri)) { - List drmData = mediaSource.getDrmData(); + List drmData = mediaSourceConfig.mediaSource.getDrmData(); if (drmData != null) { licenseUri = drmData.get(0).getLicenseUri(); drmClient.acquireRights(assetAcquireUri, licenseUri); diff --git a/playkit/src/main/java/com/kaltura/playkit/player/PKMediaSourceConfig.java b/playkit/src/main/java/com/kaltura/playkit/player/PKMediaSourceConfig.java new file mode 100644 index 000000000..dd22318ef --- /dev/null +++ b/playkit/src/main/java/com/kaltura/playkit/player/PKMediaSourceConfig.java @@ -0,0 +1,47 @@ +package com.kaltura.playkit.player; + +import android.net.Uri; + +import com.kaltura.playkit.PKMediaSource; +import com.kaltura.playkit.PKRequestInfo; + +/** + * Created by Noam Tamim @ Kaltura on 29/03/2017. + */ +class PKMediaSourceConfig { + PKMediaSource mediaSource; + PKRequestInfo.Decorator decorator; + + PKMediaSourceConfig(PKMediaSource mediaSource, PKRequestInfo.Decorator decorator) { + this.mediaSource = mediaSource; + this.decorator = decorator; + } + + Uri getUrl() { + Uri uri = Uri.parse(mediaSource.getUrl()); + if (decorator == null) { + return uri; + } else { + return decorator.getRequestInfo(new PKRequestInfo(uri, null)).getUrl(); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PKMediaSourceConfig that = (PKMediaSourceConfig) o; + + if (mediaSource != null ? !mediaSource.equals(that.mediaSource) : that.mediaSource != null) + return false; + return decorator != null ? decorator.equals(that.decorator) : that.decorator == null; + } + + @Override + public int hashCode() { + int result = mediaSource != null ? mediaSource.hashCode() : 0; + result = 31 * result + (decorator != null ? decorator.hashCode() : 0); + return result; + } +} diff --git a/playkit/src/main/java/com/kaltura/playkit/player/PlayerController.java b/playkit/src/main/java/com/kaltura/playkit/player/PlayerController.java index 5c77f6ffa..d4bc0c156 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/PlayerController.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/PlayerController.java @@ -13,12 +13,15 @@ import com.kaltura.playkit.PKMediaConfig; import com.kaltura.playkit.PKMediaFormat; import com.kaltura.playkit.PKMediaSource; +import com.kaltura.playkit.PKRequestInfo; import com.kaltura.playkit.Player; import com.kaltura.playkit.PlayerEvent; import com.kaltura.playkit.PlayerState; import com.kaltura.playkit.ads.AdController; import com.kaltura.playkit.utils.Consts; +import java.util.UUID; + import static com.kaltura.playkit.utils.Consts.MILLISECONDS_MULTIPLIER; /** @@ -31,21 +34,43 @@ public class PlayerController implements Player { private static final int ALLOWED_ERROR_RETRIES = 3; + private PlayerEngine player = null; private Context context; private PlayerView rootPlayerView; - private PKMediaConfig mediaConfig = null; + private PKMediaConfig mediaConfig; + private PKMediaSourceConfig sourceConfig; private PKEvent.Listener eventListener; private PlayerView playerEngineView; + private UUID sessionId = UUID.randomUUID(); + private PKRequestInfo.Decorator contentRequestDecorator; + private boolean isNewEntry = true; + private Settings settings = new Settings(); + + private class Settings implements Player.Settings { + + @Override + public Player.Settings setContentRequestDecorator(PKRequestInfo.Decorator contentRequestDecorator) { + PlayerController.this.contentRequestDecorator = contentRequestDecorator; + return this; + } + } + + public void setEventListener(PKEvent.Listener eventListener) { this.eventListener = eventListener; } + @Override + public UUID getSessionId() { + return sessionId; + } + interface EventListener { void onEvent(PlayerEvent.Type event); } @@ -153,6 +178,11 @@ private void setVideoSurfaceVisibility(boolean isVisible) { } } + @Override + public Player.Settings getSettings() { + return settings; + } + public void prepare(@NonNull PKMediaConfig mediaConfig) { isNewEntry = isNewEntry(mediaConfig); @@ -171,7 +201,10 @@ public void prepare(@NonNull PKMediaConfig mediaConfig) { switchPlayers(source.getMediaFormat()); } - player.load(source); + + this.sourceConfig = new PKMediaSourceConfig(source, contentRequestDecorator); + + player.load(sourceConfig); } private void switchPlayers(PKMediaFormat mediaFormat) { @@ -436,13 +469,8 @@ private boolean maybeHandleExceptionLocally(PlayerEvent.ExceptionInfo exceptionI ExoPlayerWrapper exoPlayerWrapper = (ExoPlayerWrapper) player; long currentPosition = player.getCurrentPosition(); exoPlayerWrapper.savePlayerPosition(); - PKMediaSource source = SourceSelector.selectSource(mediaConfig.getMediaEntry()); - if (source == null) { - log.e("No playable source found for entry"); - return false; - } - exoPlayerWrapper.load(source); + exoPlayerWrapper.load(sourceConfig); exoPlayerWrapper.startFrom(currentPosition); return true; } diff --git a/playkit/src/main/java/com/kaltura/playkit/player/PlayerEngine.java b/playkit/src/main/java/com/kaltura/playkit/player/PlayerEngine.java index 80a1cf141..bf42a60ce 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/PlayerEngine.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/PlayerEngine.java @@ -1,6 +1,5 @@ package com.kaltura.playkit.player; -import com.kaltura.playkit.PKMediaSource; import com.kaltura.playkit.PlaybackParamsInfo; import com.kaltura.playkit.PlayerEvent; import com.kaltura.playkit.utils.Consts; @@ -18,9 +17,9 @@ interface PlayerEngine { /** * Initialize player (if needed), and load the mediaSourceUri * that should be played. - * @param mediaSource - the source to be played. + * @param mediaSourceConfig - the source to be played. */ - void load(PKMediaSource mediaSource); + void load(PKMediaSourceConfig mediaSourceConfig); /** * Getter for the View to which current @@ -175,3 +174,4 @@ interface PlayerEngine { */ void stop(); } + diff --git a/playkit/src/main/java/com/kaltura/playkit/plugins/playback/KalturaPlaybackRequestDecorator.java b/playkit/src/main/java/com/kaltura/playkit/plugins/playback/KalturaPlaybackRequestDecorator.java new file mode 100644 index 000000000..fbf396d13 --- /dev/null +++ b/playkit/src/main/java/com/kaltura/playkit/plugins/playback/KalturaPlaybackRequestDecorator.java @@ -0,0 +1,41 @@ +package com.kaltura.playkit.plugins.playback; + +import android.net.Uri; + +import com.kaltura.playkit.PKRequestInfo; +import com.kaltura.playkit.Player; + +import java.util.UUID; + +import static com.kaltura.playkit.PlayKitManager.CLIENT_TAG; + +/** + * Created by Noam Tamim @ Kaltura on 28/03/2017. + */ +public class KalturaPlaybackRequestDecorator implements PKRequestInfo.Decorator { + + private UUID playSessionId; + + public static void setup(Player player) { + KalturaPlaybackRequestDecorator decorator = new KalturaPlaybackRequestDecorator(player.getSessionId()); + player.getSettings().setContentRequestDecorator(decorator); + } + + private KalturaPlaybackRequestDecorator(UUID playSessionId) { + this.playSessionId = playSessionId; + } + + @Override + public PKRequestInfo getRequestInfo(PKRequestInfo requestInfo) { + Uri url = requestInfo.getUrl(); + if (url.getPath().contains("/playManifest/")) { + Uri alt = url.buildUpon() + .appendQueryParameter("clientTag", CLIENT_TAG) + .appendQueryParameter("playSessionId", playSessionId.toString()) + .build(); + return new PKRequestInfo(alt, requestInfo.getHeaders()); + } + + return requestInfo; + } +}