Skip to content
This repository has been archived by the owner on Feb 11, 2022. It is now read-only.

ALL-3936/Assert support DRM #54

Merged
merged 7 commits into from
Jun 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions core/src/main/java/com/novoda/noplayer/Heart.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.novoda.noplayer;

import android.os.Handler;
import android.os.Looper;

public class Heart {

Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<FrameworkMediaCrypto> 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
);
}
}
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) {
Copy link
Contributor

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 ;)

Copy link
Contributor

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

Copy link
Contributor

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?

Copy link
Contributor

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)

Copy link
Contributor

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

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
Expand Up @@ -25,19 +25,11 @@ class LocalDrmSessionManager implements DrmSessionManager<FrameworkMediaCrypto>
private final UUID drmScheme;
private final Handler handler;

static LocalDrmSessionManager newInstance(KeySetId keySetIdToRestore,
ExoMediaDrm<FrameworkMediaCrypto> 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<FrameworkMediaCrypto> mediaDrm,
UUID drmScheme,
Handler handler,
DefaultDrmSessionManager.EventListener eventListener) {
LocalDrmSessionManager(KeySetId keySetIdToRestore,
ExoMediaDrm<FrameworkMediaCrypto> mediaDrm,
UUID drmScheme,
Handler handler,
DefaultDrmSessionManager.EventListener eventListener) {
this.keySetIdToRestore = keySetIdToRestore;
this.mediaDrm = mediaDrm;
this.eventListener = eventListener;
Expand Down
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;
Expand All @@ -19,12 +18,7 @@ class StreamingDrmSessionCreator implements DrmSessionCreator {
private final FrameworkMediaDrmCreator frameworkMediaDrmCreator;
private final Handler handler;

static StreamingDrmSessionCreator newInstance(MediaDrmCallback mediaDrmCallback, FrameworkMediaDrmCreator frameworkMediaDrmCreator) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import java.net.HttpURLConnection;
import java.net.URL;

class HttpPoster {
public class HttpPoster {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thy is this class now public?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather keep this package and in case make a newInstance on the ProvisioningExecutor. I think that's one of the little benefits, on my opinion, of the newInstance and therefore I don't see justification from removing it from ProvisioningExecutor

Copy link
Contributor

@ouchadam ouchadam Jun 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these classes were being used in the client project, we'll replace this class with an interface and make this impl package again

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@ public class ProvisionExecutor {
private final HttpPoster httpPoster;
private final ProvisioningCapabilities capabilities;

public static ProvisionExecutor newInstance() {
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

import android.os.Build;

class ProvisioningCapabilities {
public class ProvisioningCapabilities {

private final int deviceOsVersion;

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;
}
}

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;
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

@ouchadam ouchadam Jun 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the internal classes are to enable testing

Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the code, ok.

ExoPlayerTwoImpl and AndroidMediaPlayerImpl class can be package protected ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did this in the #55 PR 😄

Copy link
Contributor

Choose a reason for hiding this comment

The 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();
Copy link
Contributor

Choose a reason for hiding this comment

The 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
);
}
}
}
Loading