-
Notifications
You must be signed in to change notification settings - Fork 28
ALL-3936/Assert support DRM #54
Changes from all commits
6e9623c
5f2192f
2873c73
e88977d
64acc6b
4e68a31
398cae1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,66 @@ | ||
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: | ||
// Fall-through. | ||
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); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
import java.net.HttpURLConnection; | ||
import java.net.URL; | ||
|
||
class HttpPoster { | ||
public class HttpPoster { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thy is this class now public? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rather keep this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We inlined the logic into the client and instead hide all of this logic |
||
|
||
private static final String POST_REQUEST_METHOD = "POST"; | ||
private static final int RESPONSE_BUFFER_SIZE = 1024 * 4; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,13 +12,7 @@ public class ProvisionExecutor { | |
private final HttpPoster httpPoster; | ||
private final ProvisioningCapabilities capabilities; | ||
|
||
public static ProvisionExecutor newInstance() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
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; | ||
} | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure we gain much doing this (apart from creating a whole class). You can just hold a reference to the handler and then use a static method just use a local create method. We then reduce indirection. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the internal classes are to enable testing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also if you do this, you can remove the static newInstance and simply use the normal constructor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checked the code, ok.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We did this in the #55 PR 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok 👍 |
||
|
||
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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
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 | ||
); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that we can throw unchecked exceptions, we can help the user of this method to manage expectations if we document them ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you think we should add a checked exception to the
PlayerBuilder.build()
?if so we can propagate those errors up into
PlayerCreationExceptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we recover from those exceptions or is a fatal crash?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope, they'll be thrown if the device or available players can't handle the drm types (content types should eventually be taken into account here as well)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we cannot recover this is a fatal exception, don't check them then