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

ALL-3936/Player Builder #57

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.novoda.noplayer.exoplayer.drm.DrmSessionCreator;

Expand All @@ -26,14 +27,14 @@ class ExoPlayerCreator {
this.trackSelector = trackSelector;
}

public SimpleExoPlayer create(DrmSessionCreator drmSessionCreator, DefaultDrmSessionManager.EventListener drmSessionEventListener, boolean downgradeSecureDecoder) {
public SimpleExoPlayer create(DrmSessionCreator drmSessionCreator, DefaultDrmSessionManager.EventListener drmSessionEventListener, MediaCodecSelector mediaCodecSelector) {
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = drmSessionCreator.create(drmSessionEventListener);
RenderersFactory renderersFactory = new SimpleRenderersFactory(
context,
drmSessionManager,
EXTENSION_RENDERER_MODE_OFF,
DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS,
SecurityDowngradingCodecSelector.newInstance(downgradeSecureDecoder)
mediaCodecSelector
);

DefaultLoadControl loadControl = new DefaultLoadControl();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.view.SurfaceHolder;

import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.source.MediaSource;
import com.novoda.noplayer.ContentType;
import com.novoda.noplayer.model.PlayerAudioTrack;
Expand Down Expand Up @@ -100,8 +101,8 @@ void stop() {
exoPlayer.stop();
}

void loadVideo(DrmSessionCreator drmSessionCreator, Uri uri, ContentType contentType, ExoPlayerForwarder forwarder, boolean downgradeSecureDecoder) {
exoPlayer = exoPlayerCreator.create(drmSessionCreator, forwarder.drmSessionEventListener(), downgradeSecureDecoder);
void loadVideo(DrmSessionCreator drmSessionCreator, Uri uri, ContentType contentType, ExoPlayerForwarder forwarder, MediaCodecSelector mediaCodecSelector) {
exoPlayer = exoPlayerCreator.create(drmSessionCreator, forwarder.drmSessionEventListener(), mediaCodecSelector);
rendererTypeRequester = rendererTypeRequesterCreator.createfrom(exoPlayer);
exoPlayer.addListener(forwarder.exoPlayerEventListener());
exoPlayer.setVideoDebugListener(forwarder.videoRendererEventListener());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.support.annotation.Nullable;
import android.view.SurfaceHolder;

import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.novoda.noplayer.ContentType;
import com.novoda.noplayer.Heart;
import com.novoda.noplayer.Player;
Expand All @@ -30,7 +31,7 @@ class ExoPlayerTwoImpl implements Player {
private final ExoPlayerForwarder forwarder;
private final Heart heart;
private final DrmSessionCreator drmSessionCreator;
private final boolean downgradeSecureDecoder;
private final MediaCodecSelector mediaCodecSelector;
private final LoadTimeout loadTimeout;

private SurfaceHolderRequester surfaceHolderRequester;
Expand All @@ -47,14 +48,14 @@ class ExoPlayerTwoImpl implements Player {
LoadTimeout loadTimeoutParam,
Heart heart,
DrmSessionCreator drmSessionCreator,
boolean downgradeSecureDecoder) {
MediaCodecSelector mediaCodecSelector) {
this.exoPlayer = exoPlayer;
this.listenersHolder = listenersHolder;
this.loadTimeout = loadTimeoutParam;
this.forwarder = exoPlayerForwarder;
this.heart = heart;
this.drmSessionCreator = drmSessionCreator;
this.downgradeSecureDecoder = downgradeSecureDecoder;
this.mediaCodecSelector = mediaCodecSelector;
}

void initialise() {
Expand Down Expand Up @@ -174,7 +175,7 @@ public void loadVideo(Uri uri, ContentType contentType) {
reset();
}
listenersHolder.getPreparedListeners().resetPreparedState();
exoPlayer.loadVideo(drmSessionCreator, uri, contentType, forwarder, downgradeSecureDecoder);
exoPlayer.loadVideo(drmSessionCreator, uri, contentType, forwarder, mediaCodecSelector);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.content.Context;
import android.os.Handler;

import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
Expand Down Expand Up @@ -51,6 +52,8 @@ ExoPlayerTwoImpl create(Context context, DrmSessionCreator drmSessionCreator, bo

DefaultTrackSelector trackSelector = new DefaultTrackSelector();

MediaCodecSelector mediaCodecSelector = downgradeSecureDecoder ? SecurityDowngradingCodecSelector.newInstance() : MediaCodecSelector.DEFAULT;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

boolean downgradeSecureDecoder is transformed into the associated MediaCodecSelector.

Copy link
Contributor

Choose a reason for hiding this comment

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

@joetimmins the boolean now lives on PlayerBuilder -> NoPlayerCreator and finally here where it gets converted to the codec selector


ExoPlayerTrackSelector exoPlayerTrackSelector = ExoPlayerTrackSelector.newInstance(trackSelector);
FixedTrackSelection.Factory trackSelectionFactory = new FixedTrackSelection.Factory();
ExoPlayerAudioTrackSelector exoPlayerAudioTrackSelector = new ExoPlayerAudioTrackSelector(exoPlayerTrackSelector, trackSelectionFactory);
Expand Down Expand Up @@ -81,7 +84,7 @@ ExoPlayerTwoImpl create(Context context, DrmSessionCreator drmSessionCreator, bo
loadTimeout,
heart,
drmSessionCreator,
downgradeSecureDecoder
mediaCodecSelector
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,20 @@
class SecurityDowngradingCodecSelector implements MediaCodecSelector {

private final InternalMediaCodecUtil internalMediaCodecUtil;
private final boolean downgradeSecureDecoder;

public static SecurityDowngradingCodecSelector newInstance(boolean downgradeSecureDecoder) {
public static SecurityDowngradingCodecSelector newInstance() {
InternalMediaCodecUtil internalMediaCodecUtil = new InternalMediaCodecUtil();
return new SecurityDowngradingCodecSelector(internalMediaCodecUtil, downgradeSecureDecoder);
return new SecurityDowngradingCodecSelector(internalMediaCodecUtil);
}

SecurityDowngradingCodecSelector(InternalMediaCodecUtil internalMediaCodecUtil, boolean downgradeSecureDecoder) {
SecurityDowngradingCodecSelector(InternalMediaCodecUtil internalMediaCodecUtil) {
this.internalMediaCodecUtil = internalMediaCodecUtil;
this.downgradeSecureDecoder = downgradeSecureDecoder;
}

@Override
public MediaCodecInfo getDecoderInfo(String mimeType, boolean contentRequiresSecureDecoder)
Copy link
Contributor

Choose a reason for hiding this comment

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

is contentRequiresSecureDecoder not used anymore? if so... kill it? (if we can, which I don't know)

Copy link
Contributor

Choose a reason for hiding this comment

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

it's from ExoPlayer MediaCodecSelector, we can't remove it, unless you mean rename it?

Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't notice it was from the exoplayer one, ok then 👍

throws MediaCodecUtil.DecoderQueryException {
return internalMediaCodecUtil.getDecoderInfo(mimeType, contentRequiresSecureDecoder && !downgradeSecureDecoder);
return internalMediaCodecUtil.getDecoderInfo(mimeType, false);
Copy link
Contributor

Choose a reason for hiding this comment

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

can you extract that boolean? I have no idea what's false there :P

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
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.noplayer.player.UnableToCreatePlayerException;
import com.novoda.utils.AndroidDeviceVersion;

public class DrmSessionCreatorFactory {
Expand All @@ -36,15 +36,15 @@ public DrmSessionCreator createFor(DrmType drmType, DrmHandler drmHandler) {
assertThatApiLevelIsJellyBeanEighteenOrAbove(drmType);
return createModularDownload((DownloadedModularDrm) drmHandler);
default:
throw PlayerFactory.UnableToCreatePlayerException.noDrmHandlerFor(drmType);
throw UnableToCreatePlayerException.noDrmHandlerFor(drmType);
}
}

private void assertThatApiLevelIsJellyBeanEighteenOrAbove(DrmType drmType) {
if (androidDeviceVersion.isJellyBeanEighteenOrAbove()) {
return;
}
throw PlayerFactory.UnableToCreatePlayerException.deviceDoesNotMeetTargetApiException(
throw UnableToCreatePlayerException.deviceDoesNotMeetTargetApiException(
drmType,
Build.VERSION_CODES.JELLY_BEAN_MR2,
androidDeviceVersion
Expand Down
53 changes: 53 additions & 0 deletions core/src/main/java/com/novoda/noplayer/player/NoPlayerCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.novoda.noplayer.player;

import android.content.Context;

import com.novoda.noplayer.Player;
import com.novoda.noplayer.drm.DrmHandler;
import com.novoda.noplayer.drm.DrmType;
import com.novoda.noplayer.exoplayer.NoPlayerExoPlayerCreator;
import com.novoda.noplayer.exoplayer.drm.DrmSessionCreator;
import com.novoda.noplayer.exoplayer.drm.DrmSessionCreatorFactory;
import com.novoda.noplayer.mediaplayer.NoPlayerMediaPlayerCreator;

class NoPlayerCreator {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the old PlayerFactory minus a few wrapper methods, we only expose the one method create and it is used only in the PlayerBuilder.


private final Context context;
private final PrioritizedPlayerTypes prioritizedPlayerTypes;
private final NoPlayerExoPlayerCreator noPlayerExoPlayerCreator;
private final NoPlayerMediaPlayerCreator noPlayerMediaPlayerCreator;
private final DrmSessionCreatorFactory drmSessionCreatorFactory;

NoPlayerCreator(Context context,
PrioritizedPlayerTypes prioritizedPlayerTypes,
NoPlayerExoPlayerCreator noPlayerExoPlayerCreator,
NoPlayerMediaPlayerCreator noPlayerMediaPlayerCreator,
DrmSessionCreatorFactory drmSessionCreatorFactory) {
this.context = context;
this.prioritizedPlayerTypes = prioritizedPlayerTypes;
this.noPlayerExoPlayerCreator = noPlayerExoPlayerCreator;
this.noPlayerMediaPlayerCreator = noPlayerMediaPlayerCreator;
this.drmSessionCreatorFactory = drmSessionCreatorFactory;
}

Player create(DrmType drmType, DrmHandler drmHandler, boolean downgradeSecureDecoder) {
for (PlayerType player : prioritizedPlayerTypes) {
if (player.supports(drmType)) {
return createPlayerForType(player, drmType, drmHandler, downgradeSecureDecoder);
}
}
throw UnableToCreatePlayerException.unhandledDrmType(drmType);
}

private Player createPlayerForType(PlayerType playerType, DrmType drmType, DrmHandler drmHandler, boolean downgradeSecureDecoder) {
switch (playerType) {
case MEDIA_PLAYER:
return noPlayerMediaPlayerCreator.createMediaPlayer(context);
case EXO_PLAYER:
DrmSessionCreator drmSessionCreator = drmSessionCreatorFactory.createFor(drmType, drmHandler);
return noPlayerExoPlayerCreator.createExoPlayer(context, drmSessionCreator, downgradeSecureDecoder);
default:
throw UnableToCreatePlayerException.unhandledPlayerType(playerType);
}
}
}
71 changes: 71 additions & 0 deletions core/src/main/java/com/novoda/noplayer/player/PlayerBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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.DownloadedModularDrm;
import com.novoda.noplayer.drm.DrmHandler;
import com.novoda.noplayer.drm.DrmType;
import com.novoda.noplayer.drm.StreamingModularDrm;
import com.novoda.noplayer.exoplayer.NoPlayerExoPlayerCreator;
import com.novoda.noplayer.exoplayer.drm.DrmSessionCreatorFactory;
import com.novoda.noplayer.mediaplayer.NoPlayerMediaPlayerCreator;
import com.novoda.utils.AndroidDeviceVersion;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PlayerBuilder {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Builder party 🎉


private DrmType drmType = DrmType.NONE;
private DrmHandler drmHandler = DrmHandler.NO_DRM;
private PrioritizedPlayerTypes prioritizedPlayerTypes = PrioritizedPlayerTypes.prioritizeExoPlayer();
private boolean downgradeSecureDecoder = false;

public PlayerBuilder withWidevineClassicDrm() {
return withDrm(DrmType.WIDEVINE_CLASSIC, DrmHandler.NO_DRM);
}

public PlayerBuilder withWidevineModularStreamingDrm(StreamingModularDrm streamingModularDrm) {
return withDrm(DrmType.WIDEVINE_MODULAR_STREAM, streamingModularDrm);
}

public PlayerBuilder withWidevineModularDownloadDrm(DownloadedModularDrm downloadedModularDrm) {
return withDrm(DrmType.WIDEVINE_MODULAR_DOWNLOAD, downloadedModularDrm);
}

public PlayerBuilder withDrm(DrmType drmType, DrmHandler drmHandler) {
this.drmType = drmType;
this.drmHandler = drmHandler;
return this;
}

public PlayerBuilder withPriority(PlayerType playerType, PlayerType... playerTypes) {
List<PlayerType> types = new ArrayList<>();
types.add(playerType);
types.addAll(Arrays.asList(playerTypes));
prioritizedPlayerTypes = new PrioritizedPlayerTypes(types);
return this;
}

public PlayerBuilder withDowngradedSecureDecoder() {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

downgradeSecureDecoder = true;
return this;
}

public Player build(Context context) {
Copy link
Contributor

Choose a reason for hiding this comment

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

as a user of the library I can still use NoPlayerExoPlayerCreator directly and create it without the builder.

Is this intentional?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see how to avoid this without flattening all the packages 😢

Copy link
Contributor

Choose a reason for hiding this comment

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

😢

I have taken a look and I don't see either

Copy link
Contributor

Choose a reason for hiding this comment

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

Can NoPlayerCreator be properly used by the client? if it doesn't make sense for the client to do so, we might have to flatten the packages... java, libraries and packages are not friends, it really sucks :S

Copy link
Contributor

@ouchadam ouchadam Jun 28, 2017

Choose a reason for hiding this comment

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

NoPlayerCreator is not public, it's the individual player creators which are, like NoPlayerExoPlayerCreator

I don't think it's problem or enough of a reason to flatten the entire library.

There's no issue or side effects of a client creating a single player directly via the public player creators, if anything it's an architectural feature. The PlayerBuilder is syntactic sugar around selecting a player for given types, which isn't needed if a client decides to manually use a specific creator

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's the NoPlayerMediaPlayerCreator and NoPlayerExoPlayerCreator that are exposed. They are the sole entry points into the exo-player and media-player packages. Flattening would solve this problem but make maintaining no-player extremely painful.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed offline, we'll expose these creators as part of the public API

Handler handler = new Handler(Looper.getMainLooper());
DrmSessionCreatorFactory drmSessionCreatorFactory = new DrmSessionCreatorFactory(AndroidDeviceVersion.newInstance(), handler);
NoPlayerCreator noPlayerCreator = new NoPlayerCreator(
context,
prioritizedPlayerTypes,
NoPlayerExoPlayerCreator.newInstance(handler),
NoPlayerMediaPlayerCreator.newInstance(handler),
drmSessionCreatorFactory
);
return noPlayerCreator.create(drmType, drmHandler, downgradeSecureDecoder);
}
}