Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move view type and drm in source #3867

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
d3fc4fd
perf: ensure we do not provide callback to native if no callback prov…
freeboub May 5, 2024
e1da32d
chore: rework bufferConfig to make it more generic and reduce ReactEx…
freeboub May 6, 2024
df3da43
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 8, 2024
0f33ad1
chore: improve issue template
freeboub May 8, 2024
17f2385
Merge branch 'TheWidlarzGroup:master' into master
freeboub May 10, 2024
1066898
fix(android): avoid video view flickering at playback startup
freeboub May 10, 2024
485f867
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 11, 2024
a5e10a3
Merge branch 'master' of github.com:freeboub/react-native-video into …
freeboub May 11, 2024
9ce1d95
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 15, 2024
fa27db7
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 17, 2024
be681d3
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 21, 2024
2fadb26
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 23, 2024
92df10d
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 24, 2024
aa61388
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 28, 2024
41e2577
chore(android): refactor DRM props into a dedicated class
freeboub May 28, 2024
755b834
Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerV…
freeboub May 28, 2024
dc12ff8
chore: fix linter
freeboub May 28, 2024
cdb77e5
Merge branch 'chore/refactorDrmProps' of github.com:freeboub/react-na…
freeboub May 28, 2024
25eaedb
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 30, 2024
1e155fc
fix: ensure drm prop is correctly cleaned
freeboub May 30, 2024
ceafc53
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 30, 2024
d30c5f5
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 30, 2024
5d69e28
feat(android): move viewType (secure texture) & drm inside the source
freeboub May 31, 2024
cc5aceb
chore: fix linter
freeboub May 31, 2024
df474ef
chore(ios): move drm prop in source like on android
freeboub May 31, 2024
1836031
chore: fix linter
freeboub May 31, 2024
4e3a2cb
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub May 31, 2024
3f87ee9
chore: clean log
freeboub Jun 2, 2024
22fa2a8
fix: allow to disable secure View
freeboub Jun 2, 2024
9557fc0
chore: fix viewType resolution (source value was not handled)
freeboub Jun 2, 2024
96d38cb
chore: use contentDeepEquals instead of manual checks
freeboub Jun 2, 2024
784c2ae
chore: fix linter
freeboub Jun 2, 2024
f3f8663
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 3, 2024
5f8c461
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 4, 2024
0eba6da
fix: ensure player doesn't start when view is unmounted
freeboub Jun 4, 2024
8693dbc
Fix/ensure view drop stop playback startup (#3875)
freeboub Jun 6, 2024
89c0e96
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 7, 2024
814bcd0
Merge branch 'master' of github.com:freeboub/react-native-video into …
freeboub Jun 7, 2024
239833e
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 7, 2024
80ec434
chore: revert change
freeboub Jun 7, 2024
a64366e
chore: add warning in case of invalid Surface configuration
freeboub Jun 7, 2024
8dd32db
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 9, 2024
f7cc6cd
chore: code clean
freeboub Jun 9, 2024
e0a1815
fix: simplify surface management
freeboub Jun 9, 2024
777201f
chore: restore previous code
freeboub Jun 9, 2024
0a31cbe
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 10, 2024
3d03f29
chore: fix typo
freeboub Jun 10, 2024
f352a59
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jun 27, 2024
f9fa8fd
chore: code cleanup
freeboub Jun 27, 2024
62e9e9d
feat(android): add multiDrm flag support
freeboub Jun 27, 2024
7921541
docs: update docs
freeboub Jun 27, 2024
efab48f
Merge branch 'master' of github.com:react-native-video/react-native-v…
freeboub Jul 5, 2024
e076590
chore: fix ios build
freeboub Jul 5, 2024
731d1e7
chore: fix deprecated declaration
freeboub Jul 5, 2024
b5b512a
Merge branch 'master' into feat/moveViewTypeAndDrmInSource
KrzysztofMoch Jul 10, 2024
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
18 changes: 18 additions & 0 deletions android/src/main/java/com/brentvatne/common/api/DRMProps.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.brentvatne.common.api

import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetArray
import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetBool
import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetString
import com.facebook.react.bridge.ReadableMap
import java.util.UUID
Expand Down Expand Up @@ -29,12 +30,28 @@ class DRMProps {
* DRM Http Header to access to license server
*/
var drmLicenseHeader: Array<String> = emptyArray<String>()

/**
* Flag to enable key rotation support
*/
var multiDrm: Boolean = false

/** return true if this and src are equals */
override fun equals(other: Any?): Boolean {
if (other == null || other !is DRMProps) return false
return drmType == other.drmType &&
drmLicenseServer == other.drmLicenseServer &&
multiDrm == other.multiDrm &&
drmLicenseHeader.contentDeepEquals(other.drmLicenseHeader) // drmLicenseHeader is never null
}

companion object {
private const val PROP_DRM_TYPE = "type"
private const val PROP_DRM_LICENSE_SERVER = "licenseServer"
private const val PROP_DRM_HEADERS = "headers"
private const val PROP_DRM_HEADERS_KEY = "key"
private const val PROP_DRM_HEADERS_VALUE = "value"
private const val PROP_DRM_MULTI_DRM = "multiDrm"

/** parse the source ReadableMap received from app */
@JvmStatic
Expand All @@ -44,6 +61,7 @@ class DRMProps {
drm = DRMProps()
drm.drmType = safeGetString(src, PROP_DRM_TYPE)
drm.drmLicenseServer = safeGetString(src, PROP_DRM_LICENSE_SERVER)
drm.multiDrm = safeGetBool(src, PROP_DRM_MULTI_DRM, false)
val drmHeadersArray = safeGetArray(src, PROP_DRM_HEADERS)
if (drm.drmType != null && drm.drmLicenseServer != null) {
if (drmHeadersArray != null) {
Expand Down
11 changes: 10 additions & 1 deletion android/src/main/java/com/brentvatne/common/api/Source.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.content.Context
import android.content.res.Resources
import android.net.Uri
import android.text.TextUtils
import com.brentvatne.common.api.DRMProps.Companion.parse
import com.brentvatne.common.toolbox.DebugLog
import com.brentvatne.common.toolbox.DebugLog.e
import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetArray
Expand Down Expand Up @@ -46,6 +47,11 @@ class Source {
/** http header list */
val headers: MutableMap<String, String> = HashMap()

/**
* DRM properties linked to the source
*/
var drmProps: DRMProps? = null

/** enable chunkless preparation for HLS
* see:
*/
Expand All @@ -61,7 +67,8 @@ class Source {
cropStartMs == other.cropStartMs &&
cropEndMs == other.cropEndMs &&
startPositionMs == other.startPositionMs &&
extension == other.extension
extension == other.extension &&
drmProps == other.drmProps
)
}

Expand Down Expand Up @@ -123,6 +130,7 @@ class Source {
private const val PROP_SRC_TYPE = "type"
private const val PROP_SRC_METADATA = "metadata"
private const val PROP_SRC_HEADERS = "requestHeaders"
private const val PROP_SRC_DRM = "drm"
private const val PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION = "textTracksAllowChunklessPreparation"

@SuppressLint("DiscouragedApi")
Expand Down Expand Up @@ -180,6 +188,7 @@ class Source {
source.cropStartMs = safeGetInt(src, PROP_SRC_CROP_START, -1)
source.cropEndMs = safeGetInt(src, PROP_SRC_CROP_END, -1)
source.extension = safeGetString(src, PROP_SRC_TYPE, null)
source.drmProps = parse(safeGetMap(src, PROP_SRC_DRM))
source.textTracksAllowChunklessPreparation = safeGetBool(src, PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION, true)

val propSrcHeadersArray = safeGetArray(src, PROP_SRC_HEADERS)
Expand Down
15 changes: 4 additions & 11 deletions android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.brentvatne.exoplayer;

import android.annotation.SuppressLint;
import android.content.Context;

import androidx.annotation.NonNull;
Expand All @@ -15,7 +16,6 @@
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.ui.SubtitleView;

import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.SurfaceView;
Expand All @@ -32,6 +32,7 @@

import java.util.List;

@SuppressLint("ViewConstructor")
public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
private final static String TAG = "ExoPlayerView";
private View surfaceView;
Expand All @@ -48,15 +49,7 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
private boolean hideShutterView = false;

public ExoPlayerView(Context context) {
this(context, null);
}

public ExoPlayerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public ExoPlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
super(context, null, 0);

this.context = context;

Expand Down Expand Up @@ -214,7 +207,7 @@ public void setPlayer(ExoPlayer player) {
* @param resizeMode The resize mode.
*/
public void setResizeMode(@ResizeMode.Mode int resizeMode) {
if (layout.getResizeMode() != resizeMode) {
if (layout != null && layout.getResizeMode() != resizeMode) {
layout.setResizeMode(resizeMode);
post(measureAndLayout);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public class ReactExoplayerView extends FrameLayout implements
private boolean hasDrmFailed = false;
private boolean isUsingContentResolution = false;
private boolean selectTrackWhenReady = false;
private Handler mainHandler;
private final Handler mainHandler;
private Runnable mainRunnable;
private boolean useCache = false;
private ControlsConfig controlsConfig = new ControlsConfig();
Expand All @@ -241,7 +241,6 @@ public class ReactExoplayerView extends FrameLayout implements
private float mProgressUpdateInterval = 250.0f;
private boolean playInBackground = false;
private boolean mReportBandwidth = false;
private DRMProps drmProps;
private boolean controls;
private Uri adTagUrl;

Expand Down Expand Up @@ -311,7 +310,7 @@ public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig confi
this.eventEmitter = new VideoEventEmitter();
this.config = config;
this.bandwidthMeter = config.getBandwidthMeter();

mainHandler = new Handler();
createViews();

audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Expand All @@ -334,12 +333,9 @@ private void createViews() {
LayoutParams.MATCH_PARENT);
exoPlayerView = new ExoPlayerView(getContext());
exoPlayerView.setLayoutParams(layoutParams);

addView(exoPlayerView, 0, layoutParams);

exoPlayerView.setFocusable(this.focusable);

mainHandler = new Handler();
}

// LifecycleEventListener implementation
Expand Down Expand Up @@ -781,19 +777,22 @@ private void initializePlayerCore(ReactExoplayerView self) {
}
}

private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) {
private DrmSessionManager initializePlayerDrm() {
DrmSessionManager drmSessionManager = null;
if (self.drmProps != null) {
try {
drmSessionManager = self.buildDrmSessionManager(self.drmProps.getDrmUUID(),
self.drmProps.getDrmLicenseServer(),
self.drmProps.getDrmLicenseHeader());
} catch (UnsupportedDrmException e) {
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
eventEmitter.onVideoError.invoke(getResources().getString(errorStringId), e, "3003");
return null;
DRMProps drmProps = source.getDrmProps();
// need to realign UUID in DRM Props from source
if (drmProps != null && drmProps.getDrmType() != null) {
UUID uuid = Util.getDrmUuid(drmProps.getDrmType());
if (uuid != null) {
try {
DebugLog.w(TAG, "drm buildDrmSessionManager");
drmSessionManager = buildDrmSessionManager(uuid, drmProps);
} catch (UnsupportedDrmException e) {
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
eventEmitter.onVideoError.invoke(getResources().getString(errorStringId), e, "3003");
}
}
}
return drmSessionManager;
Expand All @@ -803,14 +802,14 @@ private void initializePlayerSource() {
if (source.getUri() == null) {
return;
}
DrmSessionManager drmSessionManager = initializePlayerDrm(this);
if (drmSessionManager == null && drmProps != null && drmProps.getDrmUUID() != null) {
// Failed to intialize DRM session manager - cannot continue
/// init DRM
DrmSessionManager drmSessionManager = initializePlayerDrm();
freeboub marked this conversation as resolved.
Show resolved Hide resolved
if (drmSessionManager == null && source.getDrmProps() != null && source.getDrmProps().getDrmType() != null) {
// Failed to initialize DRM session manager - cannot continue
DebugLog.e(TAG, "Failed to initialize DRM Session Manager Framework!");
eventEmitter.onVideoError.invoke("Failed to initialize DRM Session Manager Framework!", new Exception("DRM Session Manager Framework failure!"), "3003");
return;
}

// init source to manage ads and external text tracks
ArrayList<MediaSource> mediaSourceList = buildTextSources();
MediaSource videoSource = buildMediaSource(source.getUri(), source.getExtension(), drmSessionManager, source.getCropStartMs(), source.getCropEndMs());
MediaSource mediaSourceWithAds = null;
Expand Down Expand Up @@ -945,21 +944,21 @@ private void cleanupPlaybackService() {
}
}

private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException {
return buildDrmSessionManager(uuid, licenseUrl, keyRequestPropertiesArray, 0);
private DrmSessionManager buildDrmSessionManager(UUID uuid, DRMProps drmProps) throws UnsupportedDrmException {
return buildDrmSessionManager(uuid, drmProps, 0);
}

private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, int retryCount) throws UnsupportedDrmException {
private DrmSessionManager buildDrmSessionManager(UUID uuid, DRMProps drmProps, int retryCount) throws UnsupportedDrmException {
if (Util.SDK_INT < 18) {
return null;
}
try {
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl,
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(drmProps.getDrmLicenseServer(),
buildHttpDataSourceFactory(false));
if (keyRequestPropertiesArray != null) {
for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) {
drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i], keyRequestPropertiesArray[i + 1]);
}

String[] keyRequestPropertiesArray = drmProps.getDrmLicenseHeader();
for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) {
drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i], keyRequestPropertiesArray[i + 1]);
}
FrameworkMediaDrm mediaDrm = FrameworkMediaDrm.newInstance(uuid);
if (hasDrmFailed) {
Expand All @@ -969,15 +968,15 @@ private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, S
return new DefaultDrmSessionManager.Builder()
.setUuidAndExoMediaDrmProvider(uuid, (_uuid) -> mediaDrm)
.setKeyRequestParameters(null)
.setMultiSession(false)
.setMultiSession(drmProps.getMultiDrm())
.build(drmCallback);
} catch (UnsupportedDrmException ex) {
// Unsupported DRM exceptions are handled by the calling method
throw ex;
} catch (Exception ex) {
if (retryCount < 3) {
// Attempt retry 3 times in case where the OS Media DRM Framework fails for whatever reason
return buildDrmSessionManager(uuid, licenseUrl, keyRequestPropertiesArray, ++retryCount);
return buildDrmSessionManager(uuid, drmProps, ++retryCount);
}
// Handle the unknow exception and emit to JS
eventEmitter.onVideoError.invoke(ex.toString(), ex, "3006");
Expand Down Expand Up @@ -1818,7 +1817,9 @@ private void reloadSource() {
}

public void setResizeModeModifier(@ResizeMode.Mode int resizeMode) {
exoPlayerView.setResizeMode(resizeMode);
if (exoPlayerView != null) {
exoPlayerView.setResizeMode(resizeMode);
}
}

private void applyModifiers() {
Expand Down Expand Up @@ -2261,13 +2262,6 @@ public void setBufferConfig(BufferConfig config) {
initializePlayer();
}

public void setDrm(DRMProps drmProps) {
this.drmProps = drmProps;
if (drmProps != null && drmProps.getDrmType() != null) {
this.drmProps.setDrmUUID(Util.getDrmUuid(drmProps.getDrmType()));
}
}

@Override
public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
DebugLog.d("DRM Info", "onDrmKeysLoaded");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;

import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;

import javax.annotation.Nullable;

Expand All @@ -40,8 +38,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String REACT_CLASS = "RCTVideo";
private static final String PROP_SRC = "src";
private static final String PROP_AD_TAG_URL = "adTagUrl";
private static final String PROP_DRM = "drm";
private static final String PROP_SRC_HEADERS = "requestHeaders";
private static final String PROP_RESIZE_MODE = "resizeMode";
private static final String PROP_REPEAT = "repeat";
private static final String PROP_SELECTED_AUDIO_TRACK = "selectedAudioTrack";
Expand Down Expand Up @@ -81,7 +77,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_DEBUG = "debug";
private static final String PROP_CONTROLS_STYLES = "controlsStyles";


private final ReactExoplayerConfig config;

public ReactExoplayerViewManager(ReactExoplayerConfig config) {
Expand Down Expand Up @@ -118,12 +113,6 @@ public void addEventEmitters(@NonNull ThemedReactContext reactContext, @NonNull
view.eventEmitter.addEventEmitters(reactContext, view);
}

@ReactProp(name = PROP_DRM)
public void setDRM(final ReactExoplayerView videoView, @Nullable ReadableMap drm) {
DRMProps drmProps = DRMProps.parse(drm);
videoView.setDrm(drmProps);
}

@ReactProp(name = PROP_SRC)
public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src) {
Context context = videoView.getContext().getApplicationContext();
Expand Down
7 changes: 7 additions & 0 deletions docs/pages/component/drm.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ Default: false

The URL pointing to the licenseServer that will provide the authorization to play the protected stream.

### `multiDrm`
<PlatformsList types={['Android']} />
Type: boolean\
Default: false

Indicates that drm system shall support key rotation, see: https://developer.android.google.cn/media/media3/exoplayer/drm?hl=en#key-rotation

### `type`

<PlatformsList types={['Android', 'iOS']} />
Expand Down
Loading
Loading