diff --git a/core/src/main/java/com/novoda/noplayer/Heart.java b/core/src/main/java/com/novoda/noplayer/Heart.java index e1b33667..b1f8c403 100644 --- a/core/src/main/java/com/novoda/noplayer/Heart.java +++ b/core/src/main/java/com/novoda/noplayer/Heart.java @@ -1,7 +1,6 @@ package com.novoda.noplayer; import android.os.Handler; -import android.os.Looper; public class Heart { @@ -14,12 +13,11 @@ public class Heart { private boolean beating; - public static Heart newInstance() { - Handler handler = new Handler(Looper.getMainLooper()); + public static Heart newInstance(Handler handler) { return new Heart(handler, HEART_BEAT_FREQUENCY_IN_MILLIS); } - Heart(Handler handler, long heartbeatFrequencyInMillis) { + private Heart(Handler handler, long heartbeatFrequencyInMillis) { this.handler = handler; this.heartbeatFrequency = heartbeatFrequencyInMillis; } diff --git a/core/src/main/java/com/novoda/noplayer/drm/DownloadDrmSessionCreator.java b/core/src/main/java/com/novoda/noplayer/drm/DownloadDrmSessionCreator.java index ba09bf9b..0d7dd6d1 100644 --- a/core/src/main/java/com/novoda/noplayer/drm/DownloadDrmSessionCreator.java +++ b/core/src/main/java/com/novoda/noplayer/drm/DownloadDrmSessionCreator.java @@ -1,5 +1,7 @@ package com.novoda.noplayer.drm; +import android.os.Handler; + import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; @@ -8,14 +10,22 @@ class DownloadDrmSessionCreator implements DrmSessionCreator { private final DownloadedModularDrm downloadedModularDrm; private final FrameworkMediaDrmCreator mediaDrmCreator; + private final Handler handler; - DownloadDrmSessionCreator(DownloadedModularDrm downloadedModularDrm, FrameworkMediaDrmCreator mediaDrmCreator) { + DownloadDrmSessionCreator(DownloadedModularDrm downloadedModularDrm, FrameworkMediaDrmCreator mediaDrmCreator, Handler handler) { this.downloadedModularDrm = downloadedModularDrm; this.mediaDrmCreator = mediaDrmCreator; + this.handler = handler; } @Override public DrmSessionManager create(DefaultDrmSessionManager.EventListener eventListener) { - return LocalDrmSessionManager.newInstance(downloadedModularDrm.getKeySetId(), mediaDrmCreator.create(WIDEVINE_MODULAR_UUID), WIDEVINE_MODULAR_UUID, eventListener); + return new LocalDrmSessionManager( + downloadedModularDrm.getKeySetId(), + mediaDrmCreator.create(WIDEVINE_MODULAR_UUID), + WIDEVINE_MODULAR_UUID, + handler, + eventListener + ); } } diff --git a/core/src/main/java/com/novoda/noplayer/drm/DrmSessionCreatorFactory.java b/core/src/main/java/com/novoda/noplayer/drm/DrmSessionCreatorFactory.java index 1ff8b167..68b76be2 100644 --- a/core/src/main/java/com/novoda/noplayer/drm/DrmSessionCreatorFactory.java +++ b/core/src/main/java/com/novoda/noplayer/drm/DrmSessionCreatorFactory.java @@ -1,10 +1,24 @@ package com.novoda.noplayer.drm; +import android.os.Build; +import android.os.Handler; + +import com.novoda.noplayer.drm.provision.HttpPoster; import com.novoda.noplayer.drm.provision.ProvisionExecutor; +import com.novoda.noplayer.drm.provision.ProvisioningCapabilities; import com.novoda.noplayer.player.PlayerFactory; +import com.novoda.utils.AndroidDeviceVersion; public class DrmSessionCreatorFactory { + private final AndroidDeviceVersion androidDeviceVersion; + private final Handler handler; + + public DrmSessionCreatorFactory(AndroidDeviceVersion androidDeviceVersion, Handler handler) { + this.androidDeviceVersion = androidDeviceVersion; + this.handler = handler; + } + public DrmSessionCreator createFor(DrmType drmType, DrmHandler drmHandler) { switch (drmType) { case NONE: @@ -12,16 +26,41 @@ public DrmSessionCreator createFor(DrmType drmType, DrmHandler drmHandler) { case WIDEVINE_CLASSIC: return new NoDrmSessionCreator(); case WIDEVINE_MODULAR_STREAM: - ProvisionExecutor provisionExecutor = ProvisionExecutor.newInstance(); - ProvisioningModularDrmCallback mediaDrmCallback = new ProvisioningModularDrmCallback( - (StreamingModularDrm) drmHandler, - provisionExecutor - ); - return StreamingDrmSessionCreator.newInstance(mediaDrmCallback, new FrameworkMediaDrmCreator()); + assertThatApiLevelIsJellyBeanEighteenOrAbove(drmType); + return createModularStream((StreamingModularDrm) drmHandler); case WIDEVINE_MODULAR_DOWNLOAD: - return new DownloadDrmSessionCreator((DownloadedModularDrm) drmHandler, new FrameworkMediaDrmCreator()); + assertThatApiLevelIsJellyBeanEighteenOrAbove(drmType); + return createModularDownload((DownloadedModularDrm) drmHandler); default: throw PlayerFactory.UnableToCreatePlayerException.noDrmHandlerFor(drmType); } } + + private void assertThatApiLevelIsJellyBeanEighteenOrAbove(DrmType drmType) { + if (androidDeviceVersion.isJellyBeanEighteenOrAbove()) { + return; + } + throw PlayerFactory.UnableToCreatePlayerException.deviceDoesNotMeetTargetApiException( + drmType, + Build.VERSION_CODES.JELLY_BEAN_MR2, + androidDeviceVersion + ); + } + + private DrmSessionCreator createModularStream(StreamingModularDrm drmHandler) { + HttpPoster httpPoster = new HttpPoster(); + ProvisioningCapabilities capabilities = ProvisioningCapabilities.newInstance(); + ProvisionExecutor provisionExecutor = new ProvisionExecutor(httpPoster, capabilities); + ProvisioningModularDrmCallback mediaDrmCallback = new ProvisioningModularDrmCallback( + drmHandler, + provisionExecutor + ); + FrameworkMediaDrmCreator mediaDrmCreator = new FrameworkMediaDrmCreator(); + return new StreamingDrmSessionCreator(mediaDrmCallback, mediaDrmCreator, handler); + } + + private DownloadDrmSessionCreator createModularDownload(DownloadedModularDrm drmHandler) { + FrameworkMediaDrmCreator mediaDrmCreator = new FrameworkMediaDrmCreator(); + return new DownloadDrmSessionCreator(drmHandler, mediaDrmCreator, handler); + } } diff --git a/core/src/main/java/com/novoda/noplayer/drm/LocalDrmSessionManager.java b/core/src/main/java/com/novoda/noplayer/drm/LocalDrmSessionManager.java index 6662d54a..5951cd78 100644 --- a/core/src/main/java/com/novoda/noplayer/drm/LocalDrmSessionManager.java +++ b/core/src/main/java/com/novoda/noplayer/drm/LocalDrmSessionManager.java @@ -25,19 +25,11 @@ class LocalDrmSessionManager implements DrmSessionManager private final UUID drmScheme; private final Handler handler; - static LocalDrmSessionManager newInstance(KeySetId keySetIdToRestore, - ExoMediaDrm mediaDrm, - UUID drmScheme, - DefaultDrmSessionManager.EventListener eventListener) { - Handler handler = new Handler(Looper.getMainLooper()); - return new LocalDrmSessionManager(keySetIdToRestore, mediaDrm, drmScheme, handler, eventListener); - } - - private LocalDrmSessionManager(KeySetId keySetIdToRestore, - ExoMediaDrm mediaDrm, - UUID drmScheme, - Handler handler, - DefaultDrmSessionManager.EventListener eventListener) { + LocalDrmSessionManager(KeySetId keySetIdToRestore, + ExoMediaDrm mediaDrm, + UUID drmScheme, + Handler handler, + DefaultDrmSessionManager.EventListener eventListener) { this.keySetIdToRestore = keySetIdToRestore; this.mediaDrm = mediaDrm; this.eventListener = eventListener; diff --git a/core/src/main/java/com/novoda/noplayer/drm/StreamingDrmSessionCreator.java b/core/src/main/java/com/novoda/noplayer/drm/StreamingDrmSessionCreator.java index a4feee4a..cbe1bc24 100644 --- a/core/src/main/java/com/novoda/noplayer/drm/StreamingDrmSessionCreator.java +++ b/core/src/main/java/com/novoda/noplayer/drm/StreamingDrmSessionCreator.java @@ -1,7 +1,6 @@ package com.novoda.noplayer.drm; import android.os.Handler; -import android.os.Looper; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager; @@ -19,12 +18,7 @@ class StreamingDrmSessionCreator implements DrmSessionCreator { private final FrameworkMediaDrmCreator frameworkMediaDrmCreator; private final Handler handler; - static StreamingDrmSessionCreator newInstance(MediaDrmCallback mediaDrmCallback, FrameworkMediaDrmCreator frameworkMediaDrmCreator) { - Handler handler = new Handler(Looper.getMainLooper()); - return new StreamingDrmSessionCreator(mediaDrmCallback, frameworkMediaDrmCreator, handler); - } - - private StreamingDrmSessionCreator(MediaDrmCallback mediaDrmCallback, FrameworkMediaDrmCreator frameworkMediaDrmCreator, Handler handler) { + StreamingDrmSessionCreator(MediaDrmCallback mediaDrmCallback, FrameworkMediaDrmCreator frameworkMediaDrmCreator, Handler handler) { this.mediaDrmCallback = mediaDrmCallback; this.frameworkMediaDrmCreator = frameworkMediaDrmCreator; this.handler = handler; diff --git a/core/src/main/java/com/novoda/noplayer/drm/provision/HttpPoster.java b/core/src/main/java/com/novoda/noplayer/drm/provision/HttpPoster.java index 06775858..07580bdb 100644 --- a/core/src/main/java/com/novoda/noplayer/drm/provision/HttpPoster.java +++ b/core/src/main/java/com/novoda/noplayer/drm/provision/HttpPoster.java @@ -6,7 +6,7 @@ import java.net.HttpURLConnection; import java.net.URL; -class HttpPoster { +public class HttpPoster { private static final String POST_REQUEST_METHOD = "POST"; private static final int RESPONSE_BUFFER_SIZE = 1024 * 4; diff --git a/core/src/main/java/com/novoda/noplayer/drm/provision/ProvisionExecutor.java b/core/src/main/java/com/novoda/noplayer/drm/provision/ProvisionExecutor.java index ddbb4d05..ecee65d8 100644 --- a/core/src/main/java/com/novoda/noplayer/drm/provision/ProvisionExecutor.java +++ b/core/src/main/java/com/novoda/noplayer/drm/provision/ProvisionExecutor.java @@ -12,13 +12,7 @@ public class ProvisionExecutor { private final HttpPoster httpPoster; private final ProvisioningCapabilities capabilities; - public static ProvisionExecutor newInstance() { - HttpPoster httpPoster = new HttpPoster(); - ProvisioningCapabilities capabilities = ProvisioningCapabilities.newInstance(); - return new ProvisionExecutor(httpPoster, capabilities); - } - - ProvisionExecutor(HttpPoster httpPoster, ProvisioningCapabilities capabilities) { + public ProvisionExecutor(HttpPoster httpPoster, ProvisioningCapabilities capabilities) { this.httpPoster = httpPoster; this.capabilities = capabilities; } diff --git a/core/src/main/java/com/novoda/noplayer/drm/provision/ProvisioningCapabilities.java b/core/src/main/java/com/novoda/noplayer/drm/provision/ProvisioningCapabilities.java index 160e5561..4cd50c46 100644 --- a/core/src/main/java/com/novoda/noplayer/drm/provision/ProvisioningCapabilities.java +++ b/core/src/main/java/com/novoda/noplayer/drm/provision/ProvisioningCapabilities.java @@ -2,7 +2,7 @@ import android.os.Build; -class ProvisioningCapabilities { +public class ProvisioningCapabilities { private final int deviceOsVersion; @@ -10,11 +10,11 @@ public static ProvisioningCapabilities newInstance() { return new ProvisioningCapabilities(Build.VERSION.SDK_INT); } - ProvisioningCapabilities(int deviceOsVersion) { + private ProvisioningCapabilities(int deviceOsVersion) { this.deviceOsVersion = deviceOsVersion; } - public boolean canProvision() { + boolean canProvision() { return deviceOsVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2; } } diff --git a/core/src/main/java/com/novoda/noplayer/exoplayer/ExoPlayerTwoImplFactory.java b/core/src/main/java/com/novoda/noplayer/exoplayer/ExoPlayerTwoImplFactory.java deleted file mode 100644 index b8d73c0c..00000000 --- a/core/src/main/java/com/novoda/noplayer/exoplayer/ExoPlayerTwoImplFactory.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.novoda.noplayer.exoplayer; - -import android.content.Context; -import android.os.Handler; -import android.os.Looper; - -import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; -import com.google.android.exoplayer2.trackselection.FixedTrackSelection; -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; -import com.novoda.noplayer.Heart; -import com.novoda.noplayer.LoadTimeout; -import com.novoda.noplayer.PlayerListenersHolder; -import com.novoda.noplayer.SystemClock; -import com.novoda.noplayer.drm.DrmSessionCreator; -import com.novoda.noplayer.exoplayer.forwarder.ExoPlayerForwarder; -import com.novoda.noplayer.exoplayer.mediasource.ExoPlayerAudioTrackSelector; -import com.novoda.noplayer.exoplayer.mediasource.ExoPlayerSubtitleTrackSelector; -import com.novoda.noplayer.exoplayer.mediasource.ExoPlayerTrackSelector; -import com.novoda.noplayer.exoplayer.mediasource.MediaSourceFactory; - -public class ExoPlayerTwoImplFactory { - - public ExoPlayerTwoImpl create(Context context, DrmSessionCreator drmSessionCreator, boolean downgradeSecureDecoder) { - DefaultDataSourceFactory defaultDataSourceFactory = new DefaultDataSourceFactory(context, "user-agent"); - Handler handler = new Handler(Looper.getMainLooper()); - MediaSourceFactory mediaSourceFactory = new MediaSourceFactory(defaultDataSourceFactory, handler); - - DefaultTrackSelector trackSelector = new DefaultTrackSelector(); - - ExoPlayerTrackSelector exoPlayerTrackSelector = ExoPlayerTrackSelector.newInstance(trackSelector); - FixedTrackSelection.Factory trackSelectionFactory = new FixedTrackSelection.Factory(); - ExoPlayerAudioTrackSelector exoPlayerAudioTrackSelector = new ExoPlayerAudioTrackSelector(exoPlayerTrackSelector, trackSelectionFactory); - ExoPlayerSubtitleTrackSelector exoPlayerSubtitleTrackSelector = new ExoPlayerSubtitleTrackSelector( - exoPlayerTrackSelector, - trackSelectionFactory - ); - - ExoPlayerCreator exoPlayerCreator = new ExoPlayerCreator(context, trackSelector); - RendererTypeRequesterCreator rendererTypeRequesterCreator = new RendererTypeRequesterCreator(); - ExoPlayerFacade exoPlayerFacade = new ExoPlayerFacade( - mediaSourceFactory, - exoPlayerAudioTrackSelector, - exoPlayerSubtitleTrackSelector, - exoPlayerCreator, - rendererTypeRequesterCreator); - - PlayerListenersHolder listenersHolder = new PlayerListenersHolder(); - ExoPlayerForwarder exoPlayerForwarder = new ExoPlayerForwarder(); - LoadTimeout loadTimeout = new LoadTimeout(new SystemClock(), new Handler(Looper.getMainLooper())); - Heart heart = Heart.newInstance(); - - return new ExoPlayerTwoImpl( - exoPlayerFacade, - listenersHolder, - exoPlayerForwarder, - loadTimeout, - heart, - drmSessionCreator, - downgradeSecureDecoder - ); - } -} diff --git a/core/src/main/java/com/novoda/noplayer/exoplayer/NoPlayerExoPlayerCreator.java b/core/src/main/java/com/novoda/noplayer/exoplayer/NoPlayerExoPlayerCreator.java new file mode 100644 index 00000000..de244421 --- /dev/null +++ b/core/src/main/java/com/novoda/noplayer/exoplayer/NoPlayerExoPlayerCreator.java @@ -0,0 +1,88 @@ +package com.novoda.noplayer.exoplayer; + +import android.content.Context; +import android.os.Handler; + +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.FixedTrackSelection; +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.novoda.noplayer.Heart; +import com.novoda.noplayer.LoadTimeout; +import com.novoda.noplayer.Player; +import com.novoda.noplayer.PlayerListenersHolder; +import com.novoda.noplayer.SystemClock; +import com.novoda.noplayer.drm.DrmSessionCreator; +import com.novoda.noplayer.exoplayer.forwarder.ExoPlayerForwarder; +import com.novoda.noplayer.exoplayer.mediasource.ExoPlayerAudioTrackSelector; +import com.novoda.noplayer.exoplayer.mediasource.ExoPlayerSubtitleTrackSelector; +import com.novoda.noplayer.exoplayer.mediasource.ExoPlayerTrackSelector; +import com.novoda.noplayer.exoplayer.mediasource.MediaSourceFactory; + +public class NoPlayerExoPlayerCreator { + + private final InternalCreator internalCreator; + + public static NoPlayerExoPlayerCreator newInstance(Handler handler) { + InternalCreator internalCreator = new InternalCreator(handler); + return new NoPlayerExoPlayerCreator(internalCreator); + } + + NoPlayerExoPlayerCreator(InternalCreator internalCreator) { + this.internalCreator = internalCreator; + } + + public Player createExoPlayer(Context context, DrmSessionCreator drmSessionCreator, boolean downgradeSecureDecoder) { + ExoPlayerTwoImpl player = internalCreator.create(context, drmSessionCreator, downgradeSecureDecoder); + player.initialise(); + return player; + } + + static class InternalCreator { + + private final Handler handler; + + InternalCreator(Handler handler) { + this.handler = handler; + } + + ExoPlayerTwoImpl create(Context context, DrmSessionCreator drmSessionCreator, boolean downgradeSecureDecoder) { + DefaultDataSourceFactory defaultDataSourceFactory = new DefaultDataSourceFactory(context, "user-agent"); + MediaSourceFactory mediaSourceFactory = new MediaSourceFactory(defaultDataSourceFactory, handler); + + DefaultTrackSelector trackSelector = new DefaultTrackSelector(); + + ExoPlayerTrackSelector exoPlayerTrackSelector = ExoPlayerTrackSelector.newInstance(trackSelector); + FixedTrackSelection.Factory trackSelectionFactory = new FixedTrackSelection.Factory(); + ExoPlayerAudioTrackSelector exoPlayerAudioTrackSelector = new ExoPlayerAudioTrackSelector(exoPlayerTrackSelector, trackSelectionFactory); + ExoPlayerSubtitleTrackSelector exoPlayerSubtitleTrackSelector = new ExoPlayerSubtitleTrackSelector( + exoPlayerTrackSelector, + trackSelectionFactory + ); + + ExoPlayerCreator exoPlayerCreator = new ExoPlayerCreator(context, trackSelector); + RendererTypeRequesterCreator rendererTypeRequesterCreator = new RendererTypeRequesterCreator(); + ExoPlayerFacade exoPlayerFacade = new ExoPlayerFacade( + mediaSourceFactory, + exoPlayerAudioTrackSelector, + exoPlayerSubtitleTrackSelector, + exoPlayerCreator, + rendererTypeRequesterCreator + ); + + PlayerListenersHolder listenersHolder = new PlayerListenersHolder(); + ExoPlayerForwarder exoPlayerForwarder = new ExoPlayerForwarder(); + LoadTimeout loadTimeout = new LoadTimeout(new SystemClock(), handler); + Heart heart = Heart.newInstance(handler); + + return new ExoPlayerTwoImpl( + exoPlayerFacade, + listenersHolder, + exoPlayerForwarder, + loadTimeout, + heart, + drmSessionCreator, + downgradeSecureDecoder + ); + } + } +} diff --git a/core/src/main/java/com/novoda/noplayer/mediaplayer/AndroidMediaPlayerImplFactory.java b/core/src/main/java/com/novoda/noplayer/mediaplayer/AndroidMediaPlayerImplFactory.java deleted file mode 100644 index b6214f78..00000000 --- a/core/src/main/java/com/novoda/noplayer/mediaplayer/AndroidMediaPlayerImplFactory.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.novoda.noplayer.mediaplayer; - -import android.content.Context; -import android.os.Handler; -import android.os.Looper; - -import com.novoda.noplayer.Heart; -import com.novoda.noplayer.LoadTimeout; -import com.novoda.noplayer.PlayerListenersHolder; -import com.novoda.noplayer.SystemClock; -import com.novoda.noplayer.mediaplayer.forwarder.MediaPlayerForwarder; - -public class AndroidMediaPlayerImplFactory { - public AndroidMediaPlayerImpl create(Context context) { - LoadTimeout loadTimeout = new LoadTimeout(new SystemClock(), new Handler(Looper.getMainLooper())); - MediaPlayerForwarder forwarder = new MediaPlayerForwarder(); - AndroidMediaPlayerFacade facade = AndroidMediaPlayerFacade.newInstance(context, forwarder); - PlayerListenersHolder listenersHolder = new PlayerListenersHolder(); - CheckBufferHeartbeatCallback bufferHeartbeatCallback = new CheckBufferHeartbeatCallback(); - Heart heart = Heart.newInstance(); - BuggyVideoDriverPreventer preventer = BuggyVideoDriverPreventer.newInstance(); - Handler handler = new Handler(Looper.getMainLooper()); - - return new AndroidMediaPlayerImpl(facade, forwarder, listenersHolder, bufferHeartbeatCallback, loadTimeout, heart, handler, preventer); - } - -} diff --git a/core/src/main/java/com/novoda/noplayer/mediaplayer/NoPlayerMediaPlayerCreator.java b/core/src/main/java/com/novoda/noplayer/mediaplayer/NoPlayerMediaPlayerCreator.java new file mode 100644 index 00000000..e03788b4 --- /dev/null +++ b/core/src/main/java/com/novoda/noplayer/mediaplayer/NoPlayerMediaPlayerCreator.java @@ -0,0 +1,52 @@ +package com.novoda.noplayer.mediaplayer; + +import android.content.Context; +import android.os.Handler; + +import com.novoda.noplayer.Heart; +import com.novoda.noplayer.LoadTimeout; +import com.novoda.noplayer.Player; +import com.novoda.noplayer.PlayerListenersHolder; +import com.novoda.noplayer.SystemClock; +import com.novoda.noplayer.mediaplayer.forwarder.MediaPlayerForwarder; + +public class NoPlayerMediaPlayerCreator { + + private final InternalCreator internalCreator; + + public static NoPlayerMediaPlayerCreator newInstance(Handler handler) { + InternalCreator internalCreator = new InternalCreator(handler); + return new NoPlayerMediaPlayerCreator(internalCreator); + } + + NoPlayerMediaPlayerCreator(InternalCreator internalCreator) { + this.internalCreator = internalCreator; + } + + public Player createMediaPlayer(Context context) { + AndroidMediaPlayerImpl player = internalCreator.create(context); + player.initialise(); + return player; + } + + static class InternalCreator { + + private final Handler handler; + + InternalCreator(Handler handler) { + this.handler = handler; + } + + public AndroidMediaPlayerImpl create(Context context) { + LoadTimeout loadTimeout = new LoadTimeout(new SystemClock(), handler); + MediaPlayerForwarder forwarder = new MediaPlayerForwarder(); + AndroidMediaPlayerFacade facade = AndroidMediaPlayerFacade.newInstance(context, forwarder); + PlayerListenersHolder listenersHolder = new PlayerListenersHolder(); + CheckBufferHeartbeatCallback bufferHeartbeatCallback = new CheckBufferHeartbeatCallback(); + Heart heart = Heart.newInstance(handler); + BuggyVideoDriverPreventer preventer = BuggyVideoDriverPreventer.newInstance(); + + return new AndroidMediaPlayerImpl(facade, forwarder, listenersHolder, bufferHeartbeatCallback, loadTimeout, heart, handler, preventer); + } + } +} diff --git a/core/src/main/java/com/novoda/noplayer/player/PlayerFactory.java b/core/src/main/java/com/novoda/noplayer/player/PlayerFactory.java index 5cd3c3e6..312c8899 100644 --- a/core/src/main/java/com/novoda/noplayer/player/PlayerFactory.java +++ b/core/src/main/java/com/novoda/noplayer/player/PlayerFactory.java @@ -1,16 +1,17 @@ package com.novoda.noplayer.player; import android.content.Context; +import android.os.Handler; +import android.os.Looper; import com.novoda.noplayer.Player; import com.novoda.noplayer.drm.DrmHandler; import com.novoda.noplayer.drm.DrmSessionCreator; import com.novoda.noplayer.drm.DrmSessionCreatorFactory; import com.novoda.noplayer.drm.DrmType; -import com.novoda.noplayer.exoplayer.ExoPlayerTwoImpl; -import com.novoda.noplayer.exoplayer.ExoPlayerTwoImplFactory; -import com.novoda.noplayer.mediaplayer.AndroidMediaPlayerImpl; -import com.novoda.noplayer.mediaplayer.AndroidMediaPlayerImplFactory; +import com.novoda.noplayer.exoplayer.NoPlayerExoPlayerCreator; +import com.novoda.noplayer.mediaplayer.NoPlayerMediaPlayerCreator; +import com.novoda.utils.AndroidDeviceVersion; public class PlayerFactory { @@ -18,23 +19,31 @@ public class PlayerFactory { private final Context context; private final PrioritizedPlayerTypes prioritizedPlayerTypes; - private final ExoPlayerCreator exoPlayerCreator; - private final MediaPlayerCreator mediaPlayerCreator; + private final NoPlayerExoPlayerCreator noPlayerExoPlayerCreator; + private final NoPlayerMediaPlayerCreator noPlayerMediaPlayerCreator; private final DrmSessionCreatorFactory drmSessionCreatorFactory; - public PlayerFactory(Context context, PrioritizedPlayerTypes prioritizedPlayerTypes) { - this(context, prioritizedPlayerTypes, ExoPlayerCreator.newInstance(), MediaPlayerCreator.newInstance(), new DrmSessionCreatorFactory()); + public static PlayerFactory newInstance(Context context, PrioritizedPlayerTypes prioritizedPlayerTypes) { + Handler handler = new Handler(Looper.getMainLooper()); + DrmSessionCreatorFactory drmSessionCreatorFactory = new DrmSessionCreatorFactory(AndroidDeviceVersion.newInstance(), handler); + return new PlayerFactory( + context, + prioritizedPlayerTypes, + NoPlayerExoPlayerCreator.newInstance(handler), + NoPlayerMediaPlayerCreator.newInstance(handler), + drmSessionCreatorFactory + ); } PlayerFactory(Context context, PrioritizedPlayerTypes prioritizedPlayerTypes, - ExoPlayerCreator exoPlayerCreator, - MediaPlayerCreator mediaPlayerCreator, + NoPlayerExoPlayerCreator noPlayerExoPlayerCreator, + NoPlayerMediaPlayerCreator noPlayerMediaPlayerCreator, DrmSessionCreatorFactory drmSessionCreatorFactory) { this.context = context; this.prioritizedPlayerTypes = prioritizedPlayerTypes; - this.exoPlayerCreator = exoPlayerCreator; - this.mediaPlayerCreator = mediaPlayerCreator; + this.noPlayerExoPlayerCreator = noPlayerExoPlayerCreator; + this.noPlayerMediaPlayerCreator = noPlayerMediaPlayerCreator; this.drmSessionCreatorFactory = drmSessionCreatorFactory; } @@ -58,10 +67,10 @@ public Player create(DrmType drmType, DrmHandler drmHandler, boolean downgradeSe private Player createPlayerForType(PlayerType playerType, DrmType drmType, DrmHandler drmHandler, boolean downgradeSecureDecoder) { switch (playerType) { case MEDIA_PLAYER: - return mediaPlayerCreator.createMediaPlayer(context); + return noPlayerMediaPlayerCreator.createMediaPlayer(context); case EXO_PLAYER: DrmSessionCreator drmSessionCreator = drmSessionCreatorFactory.createFor(drmType, drmHandler); - return exoPlayerCreator.createExoPlayer(context, drmSessionCreator, downgradeSecureDecoder); + return noPlayerExoPlayerCreator.createExoPlayer(context, drmSessionCreator, downgradeSecureDecoder); default: throw UnableToCreatePlayerException.unhandledPlayerType(playerType); } @@ -81,48 +90,21 @@ static UnableToCreatePlayerException unhandledPlayerType(PlayerType playerType) return new UnableToCreatePlayerException("Unhandled player type: " + playerType.name()); } - UnableToCreatePlayerException(String reason) { - super(reason); - } - } - - static class ExoPlayerCreator { - - private final ExoPlayerTwoImplFactory factory; - - static ExoPlayerCreator newInstance() { - ExoPlayerTwoImplFactory factory = new ExoPlayerTwoImplFactory(); - return new ExoPlayerCreator(factory); - } - - ExoPlayerCreator(ExoPlayerTwoImplFactory factory) { - this.factory = factory; - } - - Player createExoPlayer(Context context, DrmSessionCreator drmSessionCreator, boolean downgradeSecureDecoder) { - ExoPlayerTwoImpl player = factory.create(context, drmSessionCreator, downgradeSecureDecoder); - player.initialise(); - return player; - } - } - - static class MediaPlayerCreator { - - private final AndroidMediaPlayerImplFactory factory; - - static MediaPlayerCreator newInstance() { - AndroidMediaPlayerImplFactory factory = new AndroidMediaPlayerImplFactory(); - return new MediaPlayerCreator(factory); - } - - MediaPlayerCreator(AndroidMediaPlayerImplFactory factory) { - this.factory = factory; + public static UnableToCreatePlayerException deviceDoesNotMeetTargetApiException(DrmType drmType, + int targetApiLevel, + AndroidDeviceVersion actualApiLevel) { + return new UnableToCreatePlayerException( + "Device must be target: " + + targetApiLevel + + " but was: " + + actualApiLevel.sdkInt() + + " for DRM type: " + + drmType.name() + ); } - Player createMediaPlayer(Context context) { - AndroidMediaPlayerImpl player = factory.create(context); - player.initialise(); - return player; + UnableToCreatePlayerException(String reason) { + super(reason); } } } diff --git a/core/src/main/java/com/novoda/utils/AndroidDeviceVersion.java b/core/src/main/java/com/novoda/utils/AndroidDeviceVersion.java new file mode 100644 index 00000000..bdd469c7 --- /dev/null +++ b/core/src/main/java/com/novoda/utils/AndroidDeviceVersion.java @@ -0,0 +1,24 @@ +package com.novoda.utils; + +import android.os.Build; + +public class AndroidDeviceVersion { + + private final int sdkInt; + + public static AndroidDeviceVersion newInstance() { + return new AndroidDeviceVersion(Build.VERSION.SDK_INT); + } + + public AndroidDeviceVersion(int sdkInt) { + this.sdkInt = sdkInt; + } + + public boolean isJellyBeanEighteenOrAbove() { + return sdkInt >= Build.VERSION_CODES.JELLY_BEAN_MR2; + } + + public int sdkInt() { + return sdkInt; + } +} diff --git a/core/src/test/java/com/novoda/noplayer/drm/DrmSessionCreatorFactoryTest.java b/core/src/test/java/com/novoda/noplayer/drm/DrmSessionCreatorFactoryTest.java new file mode 100644 index 00000000..411db285 --- /dev/null +++ b/core/src/test/java/com/novoda/noplayer/drm/DrmSessionCreatorFactoryTest.java @@ -0,0 +1,92 @@ +package com.novoda.noplayer.drm; + +import android.os.Handler; + +import com.novoda.noplayer.player.PlayerFactory; +import com.novoda.utils.AndroidDeviceVersion; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import utils.ExceptionMatcher; + +import static org.fest.assertions.api.Assertions.assertThat; + +public class DrmSessionCreatorFactoryTest { + + private static final AndroidDeviceVersion UNSUPPORTED_MEDIA_DRM_DEVICE_VERSION = new AndroidDeviceVersion(17); + private static final DrmHandler IGNORED_DRM_HANDLER = DrmHandler.NO_DRM; + private static final AndroidDeviceVersion SUPPORTED_MEDIA_DRM_DEVICE = new AndroidDeviceVersion(18); + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Mock + private Handler handler; + @Mock + private DownloadedModularDrm downloadedModularDrm; + @Mock + private StreamingModularDrm streamingModularDrm; + + private DrmSessionCreatorFactory drmSessionCreatorFactory; + + @Before + public void setUp() { + drmSessionCreatorFactory = new DrmSessionCreatorFactory(SUPPORTED_MEDIA_DRM_DEVICE, handler); + } + + @Test + public void givenDrmTypeNone_whenCreatingDrmSessionCreator_thenReturnsNoDrmSession() { + DrmSessionCreator drmSessionCreator = drmSessionCreatorFactory.createFor(DrmType.NONE, IGNORED_DRM_HANDLER); + + assertThat(drmSessionCreator).isInstanceOf(NoDrmSessionCreator.class); + } + + @Test + public void givenDrmTypeWidevineClassic_whenCreatingDrmSessionCreator_thenReturnsNoDrmSession() { + DrmSessionCreator drmSessionCreator = drmSessionCreatorFactory.createFor(DrmType.WIDEVINE_CLASSIC, IGNORED_DRM_HANDLER); + + assertThat(drmSessionCreator).isInstanceOf(NoDrmSessionCreator.class); + } + + @Test + public void givenDrmTypeWidevineModularStream_whenCreatingDrmSessionCreator_thenReturnsStreaming() { + DrmSessionCreator drmSessionCreator = drmSessionCreatorFactory.createFor(DrmType.WIDEVINE_MODULAR_STREAM, streamingModularDrm); + + assertThat(drmSessionCreator).isInstanceOf(StreamingDrmSessionCreator.class); + } + + @Test + public void givenDrmTypeWidevineModularStream_andAndroidVersionDoesNotSupportMediaDrmApis_whenCreatingDrmSessionCreator_thenThrowsUnableToCreatePlayerException() { + drmSessionCreatorFactory = new DrmSessionCreatorFactory(UNSUPPORTED_MEDIA_DRM_DEVICE_VERSION, handler); + + String message = "Device must be target: 18 but was: 17 for DRM type: WIDEVINE_MODULAR_STREAM"; + thrown.expect(ExceptionMatcher.matches(message, PlayerFactory.UnableToCreatePlayerException.class)); + + drmSessionCreatorFactory.createFor(DrmType.WIDEVINE_MODULAR_STREAM, IGNORED_DRM_HANDLER); + } + + @Test + public void givenDrmTypeWidevineModularDownload_whenCreatingDrmSessionCreator_thenReturnsDownload() { + DrmSessionCreator drmSessionCreator = drmSessionCreatorFactory.createFor(DrmType.WIDEVINE_MODULAR_DOWNLOAD, downloadedModularDrm); + + assertThat(drmSessionCreator).isInstanceOf(DownloadDrmSessionCreator.class); + } + + @Test + public void givenDrmTypeWidevineDownloadStream_andAndroidVersionDoesNotSupportMediaDrmApis_whenCreatingDrmSessionCreator_thenThrowsUnableToCreatePlayerException() { + drmSessionCreatorFactory = new DrmSessionCreatorFactory(UNSUPPORTED_MEDIA_DRM_DEVICE_VERSION, handler); + + String message = "Device must be target: 18 but was: 17 for DRM type: WIDEVINE_MODULAR_DOWNLOAD"; + thrown.expect(ExceptionMatcher.matches(message, PlayerFactory.UnableToCreatePlayerException.class)); + + drmSessionCreatorFactory.createFor(DrmType.WIDEVINE_MODULAR_DOWNLOAD, IGNORED_DRM_HANDLER); + } +} diff --git a/core/src/test/java/com/novoda/noplayer/player/ExoPlayerTwoCreatorTest.java b/core/src/test/java/com/novoda/noplayer/exoplayer/NoPlayerExoPlayerCreatorTest.java similarity index 66% rename from core/src/test/java/com/novoda/noplayer/player/ExoPlayerTwoCreatorTest.java rename to core/src/test/java/com/novoda/noplayer/exoplayer/NoPlayerExoPlayerCreatorTest.java index a0b85b53..6639256f 100644 --- a/core/src/test/java/com/novoda/noplayer/player/ExoPlayerTwoCreatorTest.java +++ b/core/src/test/java/com/novoda/noplayer/exoplayer/NoPlayerExoPlayerCreatorTest.java @@ -1,10 +1,8 @@ -package com.novoda.noplayer.player; +package com.novoda.noplayer.exoplayer; import android.content.Context; import com.novoda.noplayer.drm.DrmSessionCreator; -import com.novoda.noplayer.exoplayer.ExoPlayerTwoImpl; -import com.novoda.noplayer.exoplayer.ExoPlayerTwoImplFactory; import org.junit.Before; import org.junit.Rule; @@ -16,28 +14,28 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.verify; -public class ExoPlayerTwoCreatorTest { +public class NoPlayerExoPlayerCreatorTest { private static final boolean USE_SECURE_CODEC = true; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); - @Mock - private ExoPlayerTwoImplFactory factory; @Mock private ExoPlayerTwoImpl player; @Mock private Context context; @Mock private DrmSessionCreator drmSessionCreator; + @Mock + private NoPlayerExoPlayerCreator.InternalCreator internalCreator; - private PlayerFactory.ExoPlayerCreator creator; + private NoPlayerExoPlayerCreator creator; @Before public void setUp() { - creator = new PlayerFactory.ExoPlayerCreator(factory); - given(factory.create(context, drmSessionCreator, USE_SECURE_CODEC)).willReturn(player); + given(internalCreator.create(context, drmSessionCreator, USE_SECURE_CODEC)).willReturn(player); + creator = new NoPlayerExoPlayerCreator(internalCreator); } @Test diff --git a/core/src/test/java/com/novoda/noplayer/mediaplayer/NoPlayerMediaPlayerCreatorTest.java b/core/src/test/java/com/novoda/noplayer/mediaplayer/NoPlayerMediaPlayerCreatorTest.java new file mode 100644 index 00000000..e7a07fa0 --- /dev/null +++ b/core/src/test/java/com/novoda/noplayer/mediaplayer/NoPlayerMediaPlayerCreatorTest.java @@ -0,0 +1,41 @@ +package com.novoda.noplayer.mediaplayer; + +import android.content.Context; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +public class NoPlayerMediaPlayerCreatorTest { + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private NoPlayerMediaPlayerCreator.InternalCreator internalCreator; + @Mock + private AndroidMediaPlayerImpl player; + @Mock + private Context context; + + private NoPlayerMediaPlayerCreator creator; + + @Before + public void setUp() { + creator = new NoPlayerMediaPlayerCreator(internalCreator); + given(internalCreator.create(context)).willReturn(player); + } + + @Test + public void whenCreatingMediaPlayer_thenInitialisesPlayer() { + creator.createMediaPlayer(context); + + verify(player).initialise(); + } +} diff --git a/core/src/test/java/com/novoda/noplayer/player/PlayerFactoryTest.java b/core/src/test/java/com/novoda/noplayer/player/PlayerFactoryTest.java index 058f0077..3bec247c 100644 --- a/core/src/test/java/com/novoda/noplayer/player/PlayerFactoryTest.java +++ b/core/src/test/java/com/novoda/noplayer/player/PlayerFactoryTest.java @@ -9,8 +9,8 @@ import com.novoda.noplayer.drm.DrmSessionCreatorFactory; import com.novoda.noplayer.drm.DrmType; import com.novoda.noplayer.drm.StreamingModularDrm; -import com.novoda.noplayer.mediaplayer.AndroidMediaPlayerImpl; -import com.novoda.noplayer.mediaplayer.AndroidMediaPlayerImplFactory; +import com.novoda.noplayer.exoplayer.NoPlayerExoPlayerCreator; +import com.novoda.noplayer.mediaplayer.NoPlayerMediaPlayerCreator; import org.junit.Before; import org.junit.Rule; @@ -27,7 +27,6 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; @RunWith(Enclosed.class) public class PlayerFactoryTest { @@ -47,9 +46,9 @@ public static abstract class Base { Context context; @Mock - PlayerFactory.ExoPlayerCreator exoPlayerCreator; + NoPlayerExoPlayerCreator noPlayerExoPlayerCreator; @Mock - PlayerFactory.MediaPlayerCreator mediaPlayerCreator; + NoPlayerMediaPlayerCreator noPlayerMediaPlayerCreator; @Mock DrmSessionCreator drmSessionCreator; @Mock @@ -60,9 +59,9 @@ public static abstract class Base { @Before public void setUp() { given(drmSessionCreatorFactory.createFor(any(DrmType.class), any(DrmHandler.class))).willReturn(drmSessionCreator); - given(exoPlayerCreator.createExoPlayer(context, drmSessionCreator, USE_SECURE_CODEC)).willReturn(EXO_PLAYER); - given(mediaPlayerCreator.createMediaPlayer(context)).willReturn(MEDIA_PLAYER); - playerFactory = new PlayerFactory(context, prioritizedPlayerTypes(), exoPlayerCreator, mediaPlayerCreator, drmSessionCreatorFactory); + given(noPlayerExoPlayerCreator.createExoPlayer(context, drmSessionCreator, USE_SECURE_CODEC)).willReturn(EXO_PLAYER); + given(noPlayerMediaPlayerCreator.createMediaPlayer(context)).willReturn(MEDIA_PLAYER); + playerFactory = new PlayerFactory(context, prioritizedPlayerTypes(), noPlayerExoPlayerCreator, noPlayerMediaPlayerCreator, drmSessionCreatorFactory); } abstract PrioritizedPlayerTypes prioritizedPlayerTypes(); @@ -153,32 +152,4 @@ public void whenCreatingPlayerWithDrmTypeWidevineModularDownload_thenReturnsExoP assertThat(player).isEqualTo(EXO_PLAYER); } } - - public static class MediaPlayerCreatorTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - AndroidMediaPlayerImplFactory factory; - @Mock - AndroidMediaPlayerImpl player; - @Mock - Context context; - - private PlayerFactory.MediaPlayerCreator creator; - - @Before - public void setUp() { - creator = new PlayerFactory.MediaPlayerCreator(factory); - given(factory.create(any(Context.class))).willReturn(player); - } - - @Test - public void whenCreatingMediaPlayer_thenInitialisesPlayer() { - creator.createMediaPlayer(context); - - verify(player).initialise(); - } - } } diff --git a/demo/src/main/java/com/novoda/demo/MainActivity.java b/demo/src/main/java/com/novoda/demo/MainActivity.java index ed9bafcb..7dbfd1a7 100644 --- a/demo/src/main/java/com/novoda/demo/MainActivity.java +++ b/demo/src/main/java/com/novoda/demo/MainActivity.java @@ -50,7 +50,10 @@ protected void onCreate(Bundle savedInstanceState) { protected void onStart() { super.onStart(); // TODO: Add switch in UI to avoid redeploy. - player = new PlayerFactory(this, PrioritizedPlayerTypes.prioritizeExoPlayer()).create(DrmType.WIDEVINE_MODULAR_STREAM, new DataPostingModularDrm(EXAMPLE_MODULAR_LICENSE_SERVER_PROXY)); + PlayerFactory playerFactory = PlayerFactory.newInstance(this, PrioritizedPlayerTypes.prioritizeExoPlayer()); + DataPostingModularDrm drmHandler = new DataPostingModularDrm(EXAMPLE_MODULAR_LICENSE_SERVER_PROXY); + + player = playerFactory.create(DrmType.WIDEVINE_MODULAR_STREAM, drmHandler); player.getListeners().addPreparedListener(new Player.PreparedListener() { @Override public void onPrepared(PlayerState playerState) {