From d3fc4fdbce981e7aa680a4f3d16251b1957798c8 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Sun, 5 May 2024 21:06:29 +0200 Subject: [PATCH 1/9] perf: ensure we do not provide callback to native if no callback provided from app --- src/Video.tsx | 66 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/src/Video.tsx b/src/Video.tsx index cc01733890..afba2fd298 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -542,38 +542,60 @@ const Video = forwardRef( selectedAudioTrack={_selectedAudioTrack} selectedVideoTrack={_selectedVideoTrack} onGetLicense={useExternalGetLicense ? onGetLicense : undefined} - onVideoLoad={onVideoLoad as (e: NativeSyntheticEvent) => void} - onVideoLoadStart={onVideoLoadStart} - onVideoError={onVideoError} - onVideoProgress={onVideoProgress} - onVideoSeek={onVideoSeek} + onVideoLoad={ + onLoad + ? (onVideoLoad as (e: NativeSyntheticEvent) => void) + : undefined + } + onVideoLoadStart={onLoadStart ? onVideoLoadStart : undefined} + onVideoError={onError ? onVideoError : undefined} + onVideoProgress={onProgress ? onVideoProgress : undefined} + onVideoSeek={onSeek ? onVideoSeek : undefined} onVideoEnd={onEnd} - onVideoBuffer={onVideoBuffer} - onVideoPlaybackStateChanged={onVideoPlaybackStateChanged} - onVideoBandwidthUpdate={_onBandwidthUpdate} - onTimedMetadata={_onTimedMetadata} - onAudioTracks={_onAudioTracks} - onTextTracks={_onTextTracks} - onTextTrackDataChanged={_onTextTrackDataChanged} - onVideoTracks={_onVideoTracks} + onVideoBuffer={onBuffer ? onVideoBuffer : undefined} + onVideoPlaybackStateChanged={ + onPlaybackRateChange ? onVideoPlaybackStateChanged : undefined + } + onVideoBandwidthUpdate={ + onBandwidthUpdate ? _onBandwidthUpdate : undefined + } + onTimedMetadata={onTimedMetadata ? _onTimedMetadata : undefined} + onAudioTracks={onAudioTracks ? _onAudioTracks : undefined} + onTextTracks={onTextTracks ? _onTextTracks : undefined} + onTextTrackDataChanged={ + onTextTrackDataChanged ? _onTextTrackDataChanged : undefined + } + onVideoTracks={onVideoTracks ? _onVideoTracks : undefined} onVideoFullscreenPlayerDidDismiss={onFullscreenPlayerDidDismiss} onVideoFullscreenPlayerDidPresent={onFullscreenPlayerDidPresent} onVideoFullscreenPlayerWillDismiss={onFullscreenPlayerWillDismiss} onVideoFullscreenPlayerWillPresent={onFullscreenPlayerWillPresent} - onVideoExternalPlaybackChange={onVideoExternalPlaybackChange} - onVideoIdle={onVideoIdle} - onAudioFocusChanged={_onAudioFocusChanged} - onReadyForDisplay={_onReadyForDisplay} - onPlaybackRateChange={_onPlaybackRateChange} - onVolumeChange={_onVolumeChange} + onVideoExternalPlaybackChange={ + onExternalPlaybackChange ? onVideoExternalPlaybackChange : undefined + } + onVideoIdle={onIdle ? onVideoIdle : undefined} + onAudioFocusChanged={ + onAudioFocusChanged ? _onAudioFocusChanged : undefined + } + onReadyForDisplay={onReadyForDisplay ? _onReadyForDisplay : undefined} + onPlaybackRateChange={ + onPlaybackRateChange ? _onPlaybackRateChange : undefined + } + onVolumeChange={onVolumeChange ? _onVolumeChange : undefined} onVideoAudioBecomingNoisy={onAudioBecomingNoisy} - onPictureInPictureStatusChanged={_onPictureInPictureStatusChanged} + onPictureInPictureStatusChanged={ + onPictureInPictureStatusChanged + ? _onPictureInPictureStatusChanged + : undefined + } onRestoreUserInterfaceForPictureInPictureStop={ onRestoreUserInterfaceForPictureInPictureStop } - onVideoAspectRatio={_onVideoAspectRatio} + onVideoAspectRatio={onAspectRatio ? _onVideoAspectRatio : undefined} onReceiveAdEvent={ - _onReceiveAdEvent as (e: NativeSyntheticEvent) => void + onReceiveAdEvent + ? (_onReceiveAdEvent as (e: NativeSyntheticEvent) => void) + : undefined } /> {hasPoster && showPoster ? ( From e1da32d6d47fa8461020f11ad3e8bfffe59b7eae Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Mon, 6 May 2024 21:36:40 +0200 Subject: [PATCH 2/9] chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size --- .../com/brentvatne/common/api/BufferConfig.kt | 66 +++++++++++++++++ .../exoplayer/ReactExoplayerSimpleCache.kt | 2 +- .../exoplayer/ReactExoplayerView.java | 71 +++++++++---------- .../exoplayer/ReactExoplayerViewManager.java | 35 ++------- .../brentvatne/react/VideoManagerModule.kt | 4 +- 5 files changed, 105 insertions(+), 73 deletions(-) create mode 100644 android/src/main/java/com/brentvatne/common/api/BufferConfig.kt diff --git a/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt b/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt new file mode 100644 index 0000000000..297a1bf65a --- /dev/null +++ b/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt @@ -0,0 +1,66 @@ +package com.brentvatne.common.api + +import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetDouble +import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetInt +import com.facebook.react.bridge.ReadableMap + +/** + * Class representing bufferConfig for host. + * Only generic code here, no reference to the player. + * By default, if application don't provide input field, -1 is set instead + */ +class BufferConfig { + var cacheSize = BufferConfigPropUnsetInt + var minBufferMs = BufferConfigPropUnsetInt + var maxBufferMs = BufferConfigPropUnsetInt + var bufferForPlaybackMs = BufferConfigPropUnsetInt + var bufferForPlaybackAfterRebufferMs = BufferConfigPropUnsetInt + var backBufferDurationMs = BufferConfigPropUnsetInt + var maxHeapAllocationPercent = BufferConfigPropUnsetDouble + var minBackBufferMemoryReservePercent = BufferConfigPropUnsetDouble + var minBufferMemoryReservePercent = BufferConfigPropUnsetDouble + + companion object { + val BufferConfigPropUnsetInt = -1 + val BufferConfigPropUnsetDouble = -1.0 + + private val PROP_BUFFER_CONFIG_CACHE_SIZE = "cacheSizeMB" + private val PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs" + private val PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs" + private val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs" + private val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs" + private val PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent" + private val PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT = "minBackBufferMemoryReservePercent" + private val PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT = "minBufferMemoryReservePercent" + private val PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS = "backBufferDurationMs" + + @JvmStatic + fun parse(src: ReadableMap?): BufferConfig { + val bufferConfig = BufferConfig() + + if (src != null) { + bufferConfig.cacheSize = safeGetInt(src, PROP_BUFFER_CONFIG_CACHE_SIZE, BufferConfigPropUnsetInt) + bufferConfig.minBufferMs = safeGetInt(src, PROP_BUFFER_CONFIG_MIN_BUFFER_MS, BufferConfigPropUnsetInt) + bufferConfig.maxBufferMs = safeGetInt(src, PROP_BUFFER_CONFIG_MAX_BUFFER_MS, BufferConfigPropUnsetInt) + bufferConfig.bufferForPlaybackMs = safeGetInt(src, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS, BufferConfigPropUnsetInt) + bufferConfig.bufferForPlaybackAfterRebufferMs = + safeGetInt(src, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, BufferConfigPropUnsetInt) + bufferConfig.maxHeapAllocationPercent = + safeGetDouble(src, PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT, BufferConfigPropUnsetDouble) + bufferConfig.minBackBufferMemoryReservePercent = safeGetDouble( + src, + PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT, + BufferConfigPropUnsetDouble + ) + bufferConfig.minBufferMemoryReservePercent = + safeGetDouble( + src, + PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT, + BufferConfigPropUnsetDouble + ) + bufferConfig.backBufferDurationMs = safeGetInt(src, PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS, BufferConfigPropUnsetInt) + } + return bufferConfig + } + } +} diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerSimpleCache.kt b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerSimpleCache.kt index cec4b898ff..23d51f7793 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerSimpleCache.kt +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerSimpleCache.kt @@ -15,7 +15,7 @@ object RNVSimpleCache { var cacheDataSourceFactory: DataSource.Factory? = null fun setSimpleCache(context: Context, cacheSize: Int, factory: HttpDataSource.Factory) { - if (cacheDataSourceFactory != null || cacheSize == 0) return + if (cacheDataSourceFactory != null || cacheSize <= 0) return simpleCache = SimpleCache( File(context.cacheDir, "RNVCache"), LeastRecentlyUsedCacheEvictor( diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index ba4142454f..1b2623885a 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -94,6 +94,7 @@ import androidx.media3.extractor.metadata.id3.TextInformationFrame; import androidx.media3.ui.LegacyPlayerControlView; +import com.brentvatne.common.api.BufferConfig; import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.SubtitleStyle; import com.brentvatne.common.api.TimedMetadata; @@ -183,18 +184,11 @@ public class ReactExoplayerView extends FrameLayout implements private AudioOutput audioOutput = AudioOutput.SPEAKER; private float audioVolume = 1f; private int minLoadRetryCount = 3; + private BufferConfig bufferConfig = new BufferConfig(); private int maxBitRate = 0; private boolean hasDrmFailed = false; private boolean isUsingContentResolution = false; private boolean selectTrackWhenReady = false; - private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; - private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS; - private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS; - private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; - private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS; - private double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT; - private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE; - private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE; private Handler mainHandler; private Runnable mainRunnable; private DataSource.Factory cacheDataSourceFactory; @@ -495,19 +489,32 @@ private void reLayoutControls() { private class RNVLoadControl extends DefaultLoadControl { private final int availableHeapInBytes; private final Runtime runtime; - public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, int targetBufferBytes, boolean prioritizeTimeOverSizeThresholds, int backBufferDurationMs, boolean retainBackBufferFromKeyframe) { + public RNVLoadControl(DefaultAllocator allocator, BufferConfig config) { super(allocator, - minBufferMs, - maxBufferMs, - bufferForPlaybackMs, - bufferForPlaybackAfterRebufferMs, - targetBufferBytes, - prioritizeTimeOverSizeThresholds, - backBufferDurationMs, - retainBackBufferFromKeyframe); + config.getMinBufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getMinBufferMs() + : DefaultLoadControl.DEFAULT_MIN_BUFFER_MS, + config.getMaxBufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getMaxBufferMs() + : DefaultLoadControl.DEFAULT_MAX_BUFFER_MS, + config.getBufferForPlaybackMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getBufferForPlaybackMs() + : DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS , + config.getBufferForPlaybackAfterRebufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getBufferForPlaybackAfterRebufferMs() + : DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, + -1, + true, + config.getBackBufferDurationMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getBackBufferDurationMs() + : DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS, + DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME); runtime = Runtime.getRuntime(); ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(ThemedReactContext.ACTIVITY_SERVICE); - availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeapAllocationPercent * 1024 * 1024); + double maxHeap = config.getMaxHeapAllocationPercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble() + ? bufferConfig.getMaxHeapAllocationPercent() + : DEFAULT_MAX_HEAP_ALLOCATION_PERCENT; + availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeap * 1024 * 1024); } @Override @@ -522,6 +529,9 @@ public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurat } long usedMemory = runtime.totalMemory() - runtime.freeMemory(); long freeMemory = runtime.maxMemory() - usedMemory; + double minBufferMemoryReservePercent = bufferConfig.getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble() + ? bufferConfig.getMinBufferMemoryReservePercent() + : ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE; long reserveMemory = (long)minBufferMemoryReservePercent * runtime.maxMemory(); long bufferedMs = bufferedDurationUs / (long)1000; if (reserveMemory > freeMemory && bufferedMs > 2000) { @@ -602,14 +612,7 @@ private void initializePlayerCore(ReactExoplayerView self) { DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); RNVLoadControl loadControl = new RNVLoadControl( allocator, - minBufferMs, - maxBufferMs, - bufferForPlaybackMs, - bufferForPlaybackAfterRebufferMs, - -1, - true, - backBufferDurationMs, - DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME + bufferConfig ); DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext()) @@ -2025,26 +2028,18 @@ public void setHideShutterView(boolean hideShutterView) { exoPlayerView.setHideShutterView(hideShutterView); } - public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent, double newMinBackBufferMemoryReservePercent, double newMinBufferMemoryReservePercent, int newBackBufferDurationMs, int cacheSize) { - minBufferMs = newMinBufferMs; - maxBufferMs = newMaxBufferMs; - bufferForPlaybackMs = newBufferForPlaybackMs; - bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs; - maxHeapAllocationPercent = newMaxHeapAllocationPercent; - minBackBufferMemoryReservePercent = newMinBackBufferMemoryReservePercent; - minBufferMemoryReservePercent = newMinBufferMemoryReservePercent; - if (cacheSize > 0) { + public void setBufferConfig(BufferConfig config) { + bufferConfig = config; + if (bufferConfig.getCacheSize() > 0) { RNVSimpleCache.INSTANCE.setSimpleCache( this.getContext(), - cacheSize, + bufferConfig.getCacheSize(), buildHttpDataSourceFactory(false) ); cacheDataSourceFactory = RNVSimpleCache.INSTANCE.getCacheDataSourceFactory(); } else { cacheDataSourceFactory = null; } - - backBufferDurationMs = newBackBufferDurationMs; releasePlayer(); initializePlayer(); } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 1e9ffd606b..1f50faa1bd 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -11,6 +11,7 @@ import androidx.media3.datasource.RawResourceDataSource; import androidx.media3.exoplayer.DefaultLoadControl; +import com.brentvatne.common.api.BufferConfig; import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.SubtitleStyle; import com.brentvatne.common.react.VideoEventEmitter; @@ -59,15 +60,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager Unit) { UiThreadUtil.runOnUiThread { From 0f33ad1a1035400599476e5c7d13ab0a584dc750 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Wed, 8 May 2024 15:52:30 +0200 Subject: [PATCH 3/9] chore: improve issue template --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 575ecdc7a3..6fa8e0b3fa 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -7,6 +7,8 @@ body: - type: markdown attributes: value: Thanks for taking the time to fill out this bug report! + Please do not report issue on 5.2.1 version, this version is not maintained anymore. + Only issues on version > V6 will be handled. Please also ensure your issue is reproduced with the last release! - type: textarea id: version From 1066898f0af9ee624367b7ad56fe3d6af5db4ac4 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Fri, 10 May 2024 16:26:28 +0200 Subject: [PATCH 4/9] fix(android): avoid video view flickering at playback startup --- .../brentvatne/exoplayer/ExoPlayerView.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index db56f65659..9b7904c5c3 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -4,16 +4,13 @@ import androidx.core.content.ContextCompat; import androidx.media3.common.AdViewProvider; import androidx.media3.common.C; -import androidx.media3.common.PlaybackException; -import androidx.media3.common.PlaybackParameters; +import androidx.media3.common.Format; import androidx.media3.common.Player; -import androidx.media3.common.Timeline; import androidx.media3.common.Tracks; import androidx.media3.common.VideoSize; import androidx.media3.common.text.Cue; import androidx.media3.common.util.Assertions; import androidx.media3.exoplayer.ExoPlayer; -import androidx.media3.exoplayer.trackselection.TrackSelectionArray; import androidx.media3.ui.SubtitleView; import android.util.AttributeSet; @@ -27,6 +24,7 @@ import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.SubtitleStyle; +import com.google.common.collect.ImmutableList; import java.util.List; @@ -247,19 +245,22 @@ public void setHideShutterView(boolean hideShutterView) { layout(getLeft(), getTop(), getRight(), getBottom()); }; - private void updateForCurrentTrackSelections() { - if (player == null) { + private void updateForCurrentTrackSelections(Tracks tracks) { + if (tracks == null) { return; } - TrackSelectionArray selections = player.getCurrentTrackSelections(); - for (int i = 0; i < selections.length; i++) { - if (player.getRendererType(i) == C.TRACK_TYPE_VIDEO && selections.get(i) != null) { - // Video enabled so artwork must be hidden. If the shutter is closed, it will be opened in - // onRenderedFirstFrame(). + ImmutableList groups = tracks.getGroups(); + for (Tracks.Group group: groups) { + if (group.getType() == C.TRACK_TYPE_VIDEO && group.length > 0) { + // get the first track of the group to identify aspect ratio + Format format = group.getTrackFormat(0); + + // update aspect ratio ! + layout.setAspectRatio(format.height == 0 ? 1 : (format.width * format.pixelWidthHeightRatio) / format.height); return; } } - // Video disabled so the shutter must be closed. + // no video tracks, in that case refresh shutterView visibility shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE); } @@ -293,8 +294,7 @@ public void onRenderedFirstFrame() { @Override public void onTracksChanged(Tracks tracks) { - updateForCurrentTrackSelections(); + updateForCurrentTrackSelections(tracks); } } - } From 41e257742546a4b78aa220440ec7be16f60eb321 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Tue, 28 May 2024 11:26:37 +0200 Subject: [PATCH 5/9] chore(android): refactor DRM props into a dedicated class --- .../com/brentvatne/common/api/DRMProps.kt | 64 +++++++++++++++++++ .../exoplayer/ReactExoplayerView.java | 32 ++++------ .../exoplayer/ReactExoplayerViewManager.java | 29 ++------- 3 files changed, 83 insertions(+), 42 deletions(-) create mode 100644 android/src/main/java/com/brentvatne/common/api/DRMProps.kt diff --git a/android/src/main/java/com/brentvatne/common/api/DRMProps.kt b/android/src/main/java/com/brentvatne/common/api/DRMProps.kt new file mode 100644 index 0000000000..c1abadca2e --- /dev/null +++ b/android/src/main/java/com/brentvatne/common/api/DRMProps.kt @@ -0,0 +1,64 @@ +package com.brentvatne.common.api + +import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetArray +import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetString +import com.facebook.react.bridge.ReadableMap +import java.util.UUID + +/** + * Class representing DRM props for host. + * Only generic code here, no reference to the player. + */ +class DRMProps { + /** + * string version of configured UUID for drm prop + */ + var drmType: String? = null + + /** + * Configured UUID for drm prop + */ + var drmUUID: UUID? = null + /** + * DRM license server to be used + */ + var drmLicenseServer: String? = null + /** + * DRM Http Header to access to license server + */ + var drmLicenseHeader: Array = emptyArray() + 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" + + /** parse the source ReadableMap received from app */ + @JvmStatic + fun parse(src: ReadableMap?): DRMProps? { + var drm : DRMProps? = null + if (src != null && src.hasKey(PROP_DRM_TYPE)) { + drm = DRMProps() + drm.drmType = safeGetString(src, PROP_DRM_TYPE) + drm.drmLicenseServer = safeGetString(src, PROP_DRM_LICENSE_SERVER) + val drmHeadersArray = safeGetArray(src, PROP_DRM_HEADERS) + if (drm.drmType != null && drm.drmLicenseServer != null) { + if (drmHeadersArray != null) { + val drmKeyRequestPropertiesList = ArrayList() + for (i in 0 until drmHeadersArray.size()) { + val current = drmHeadersArray.getMap(i) + drmKeyRequestPropertiesList.add(safeGetString(current, PROP_DRM_HEADERS_KEY)) + drmKeyRequestPropertiesList.add(safeGetString(current, PROP_DRM_HEADERS_VALUE)) + } + val array = emptyArray() + drm.drmLicenseHeader = drmKeyRequestPropertiesList.toArray(array) + } + } else { + return null + } + } + return drm + } + } +} diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 957f7c28ae..a0fdb76d1f 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -110,6 +110,7 @@ import com.brentvatne.common.api.BufferConfig; import com.brentvatne.common.api.BufferingStrategy; import com.brentvatne.common.api.ControlsConfig; +import com.brentvatne.common.api.DRMProps; import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.SideLoadedTextTrack; import com.brentvatne.common.api.SideLoadedTextTrackList; @@ -243,9 +244,8 @@ public class ReactExoplayerView extends FrameLayout implements private boolean playInBackground = false; private Map requestHeaders; private boolean mReportBandwidth = false; - private UUID drmUUID = null; - private String drmLicenseUrl = null; - private String[] drmLicenseHeader = null; + + private DRMProps drmProps; private boolean controls; private Uri adTagUrl; @@ -659,7 +659,7 @@ private void initializePlayer() { es.execute(() -> { // DRM initialization must run on a different thread DrmSessionManager drmSessionManager = initializePlayerDrm(self); - if (drmSessionManager == null && self.drmUUID != null) { + if (drmSessionManager == null && self.drmProps != null) { // Failed to intialize DRM session manager - cannot continue DebugLog.e(TAG, "Failed to initialize DRM Session Manager Framework!"); eventEmitter.error("Failed to initialize DRM Session Manager Framework!", new Exception("DRM Session Manager Framework failure!"), "3003"); @@ -767,10 +767,11 @@ private void initializePlayerCore(ReactExoplayerView self) { private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) { DrmSessionManager drmSessionManager = null; - if (self.drmUUID != null) { + if (self.drmProps != null) { try { - drmSessionManager = self.buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, - self.drmLicenseHeader); + 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 @@ -2245,7 +2246,7 @@ public void setFullscreen(boolean fullscreen) { } public void setUseTextureView(boolean useTextureView) { - boolean finallyUseTextureView = useTextureView && this.drmUUID == null; + boolean finallyUseTextureView = useTextureView && drmProps == null; exoPlayerView.setUseTextureView(finallyUseTextureView); } @@ -2272,16 +2273,11 @@ public void setBufferConfig(BufferConfig config) { initializePlayer(); } - public void setDrmType(UUID drmType) { - this.drmUUID = drmType; - } - - public void setDrmLicenseUrl(String licenseUrl){ - this.drmLicenseUrl = licenseUrl; - } - - public void setDrmLicenseHeader(String[] header){ - this.drmLicenseHeader = header; + public void setDrm(DRMProps drmProps) { + this.drmProps = drmProps; + if (drmProps != null && drmProps.getDrmType() != null) { + this.drmProps.setDrmUUID(Util.getDrmUuid(drmProps.getDrmType())); + } } @Override diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index d92b15a8fc..fc45a9f0c7 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -16,6 +16,7 @@ import com.brentvatne.common.api.BufferConfig; import com.brentvatne.common.api.BufferingStrategy; import com.brentvatne.common.api.ControlsConfig; +import com.brentvatne.common.api.DRMProps; import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.SideLoadedTextTrackList; import com.brentvatne.common.api.SubtitleStyle; @@ -49,9 +50,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager drmKeyRequestPropertiesList = new ArrayList<>(); - for (int i = 0; i < drmHeadersArray.size(); i++) { - ReadableMap current = drmHeadersArray.getMap(i); - String key = current.hasKey("key") ? current.getString("key") : null; - String value = current.hasKey("value") ? current.getString("value") : null; - drmKeyRequestPropertiesList.add(key); - drmKeyRequestPropertiesList.add(value); - } - videoView.setDrmLicenseHeader(drmKeyRequestPropertiesList.toArray(new String[0])); - } - videoView.setUseTextureView(false); - } + DRMProps drmProps = DRMProps.parse(drm); + if (drmProps != null) { + videoView.setDrm(drmProps); + videoView.setUseTextureView(false); } } From 755b8347fdc92bbc3f0fc80c54312c6e8b067e62 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet <62574056+freeboub@users.noreply.github.com> Date: Tue, 28 May 2024 11:28:30 +0200 Subject: [PATCH 6/9] Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java --- .../main/java/com/brentvatne/exoplayer/ReactExoplayerView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index a0fdb76d1f..d9d5e2be7f 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -244,7 +244,6 @@ public class ReactExoplayerView extends FrameLayout implements private boolean playInBackground = false; private Map requestHeaders; private boolean mReportBandwidth = false; - private DRMProps drmProps; private boolean controls; private Uri adTagUrl; From dc12ff8fa6d8f38151afbfc221daf33171fbdef6 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Tue, 28 May 2024 11:29:27 +0200 Subject: [PATCH 7/9] chore: fix linter --- android/src/main/java/com/brentvatne/common/api/DRMProps.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/brentvatne/common/api/DRMProps.kt b/android/src/main/java/com/brentvatne/common/api/DRMProps.kt index c1abadca2e..a26d048ac2 100644 --- a/android/src/main/java/com/brentvatne/common/api/DRMProps.kt +++ b/android/src/main/java/com/brentvatne/common/api/DRMProps.kt @@ -19,10 +19,12 @@ class DRMProps { * Configured UUID for drm prop */ var drmUUID: UUID? = null + /** * DRM license server to be used */ var drmLicenseServer: String? = null + /** * DRM Http Header to access to license server */ @@ -37,7 +39,7 @@ class DRMProps { /** parse the source ReadableMap received from app */ @JvmStatic fun parse(src: ReadableMap?): DRMProps? { - var drm : DRMProps? = null + var drm: DRMProps? = null if (src != null && src.hasKey(PROP_DRM_TYPE)) { drm = DRMProps() drm.drmType = safeGetString(src, PROP_DRM_TYPE) From 1e155fc778d306b6f4517aabb2e16207504a443b Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Thu, 30 May 2024 10:42:07 +0200 Subject: [PATCH 8/9] fix: ensure drm prop is correctly cleaned --- .../com/brentvatne/exoplayer/ReactExoplayerViewManager.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index d97dffd737..c43349b9c6 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -117,10 +117,8 @@ public void onDropViewInstance(ReactExoplayerView view) { @ReactProp(name = PROP_DRM) public void setDRM(final ReactExoplayerView videoView, @Nullable ReadableMap drm) { DRMProps drmProps = DRMProps.parse(drm); - if (drmProps != null) { - videoView.setDrm(drmProps); - videoView.setUseTextureView(false); - } + videoView.setDrm(drmProps); + videoView.setUseTextureView(false); } @ReactProp(name = PROP_SRC) From 3f37e6dd8b90bfc2e69522fe12e6e5c5e2598ede Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Fri, 7 Jun 2024 15:09:29 +0200 Subject: [PATCH 9/9] chore: revert unnecessary change --- .../exoplayer/ReactExoplayerView.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 3c66ce8748..ecfbc8ca8b 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -869,25 +869,6 @@ private void finishPlayerInitialization() { initializePlayerControl(); setControls(controls); applyModifiers(); - refreshMetadata(); - } - - /** - * refresh custom Metadata from notification bar - */ - private void refreshMetadata() { - // refresh custom Metadata - MediaMetadata newCustomMetadata = ConfigurationUtils.buildCustomMetadata(source.getMetadata()); - // Apply custom metadata is possible - if (player != null && !Util.areEqual(newCustomMetadata, customMetadata)) { - customMetadata = newCustomMetadata; - MediaItem currentMediaItem = player.getCurrentMediaItem(); - if (currentMediaItem != null && customMetadata != null) { - MediaItem newMediaItem = currentMediaItem.buildUpon().setMediaMetadata(customMetadata).build(); - // This will cause video blink/reload but won't louse progress - player.setMediaItem(newMediaItem, false); - } - } } private void setupPlaybackService() {