From 92a545e1f18e2464c61a0031fa47709a2b61e2a0 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Wed, 11 Sep 2024 08:32:52 +0200 Subject: [PATCH 1/6] chore: move contentStartTime into source prop --- .../src/main/java/com/brentvatne/common/api/Source.kt | 6 ++++++ .../com/brentvatne/exoplayer/ReactExoplayerView.java | 9 ++------- .../brentvatne/exoplayer/ReactExoplayerViewManager.kt | 6 ------ docs/pages/component/props.mdx | 11 +++++++++++ src/specs/VideoNativeComponent.ts | 2 +- src/types/video.ts | 2 ++ 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/android/src/main/java/com/brentvatne/common/api/Source.kt b/android/src/main/java/com/brentvatne/common/api/Source.kt index b9ea8bf762..711b70a059 100644 --- a/android/src/main/java/com/brentvatne/common/api/Source.kt +++ b/android/src/main/java/com/brentvatne/common/api/Source.kt @@ -38,6 +38,9 @@ class Source { /** Will crop content end at specified position */ var cropEndMs: Int = -1 + /** Will virtually consider that content before contentStartTime is a preroll ad */ + var contentStartTime: Int = -1 + /** Allow to force stream content, necessary when uri doesn't contain content type (.mlp4, .m3u, ...) */ var extension: String? = null @@ -74,6 +77,7 @@ class Source { startPositionMs == other.startPositionMs && extension == other.extension && drmProps == other.drmProps && + contentStartTime == other.contentStartTime && cmcdProps == other.cmcdProps ) } @@ -133,6 +137,7 @@ class Source { private const val PROP_SRC_START_POSITION = "startPosition" private const val PROP_SRC_CROP_START = "cropStart" private const val PROP_SRC_CROP_END = "cropEnd" + private const val PROP_SRC_CONTENT_START_TIME = "contentStartTime" private const val PROP_SRC_TYPE = "type" private const val PROP_SRC_METADATA = "metadata" private const val PROP_SRC_HEADERS = "requestHeaders" @@ -194,6 +199,7 @@ class Source { source.startPositionMs = safeGetInt(src, PROP_SRC_START_POSITION, -1) source.cropStartMs = safeGetInt(src, PROP_SRC_CROP_START, -1) source.cropEndMs = safeGetInt(src, PROP_SRC_CROP_END, -1) + source.contentStartTime = safeGetInt(src, PROP_SRC_CONTENT_START_TIME, -1) source.extension = safeGetString(src, PROP_SRC_TYPE, null) source.drmProps = parse(safeGetMap(src, PROP_SRC_DRM)) source.cmcdProps = CMCDProps.parse(safeGetMap(src, PROP_SRC_CMCD)) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 6cab02d074..c7339f0b77 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -236,7 +236,6 @@ public class ReactExoplayerView extends FrameLayout implements private boolean disableFocus; private boolean focusable = true; private BufferingStrategy.BufferingStrategyEnum bufferingStrategy; - private long contentStartTime = -1L; private boolean disableDisconnectError; private boolean preventsDisplaySleepDuringVideoPlayback = true; private float mProgressUpdateInterval = 250.0f; @@ -1429,7 +1428,7 @@ private void videoLoaded() { ArrayList audioTracks = getAudioTrackInfo(); ArrayList textTracks = getTextTrackInfo(); - if (this.contentStartTime != -1L) { + if (source.getContentStartTime() != -1L) { ExecutorService es = Executors.newSingleThreadExecutor(); es.execute(() -> { // To prevent ANRs caused by getVideoTrackInfo we run this on a different thread and notify the player only when we're done @@ -1532,7 +1531,7 @@ private ArrayList getVideoTrackInfoFromManifest(int retryCount) { ExecutorService es = Executors.newSingleThreadExecutor(); final DataSource dataSource = this.mediaDataSourceFactory.createDataSource(); final Uri sourceUri = source.getUri(); - final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset + final long startTime = source.getContentStartTime() * 1000 - 100; // s -> ms with 100ms offset Future> result = es.submit(new Callable() { final DataSource ds = dataSource; @@ -2202,10 +2201,6 @@ public void setFocusable(boolean focusable) { exoPlayerView.setFocusable(this.focusable); } - public void setContentStartTime(int contentStartTime) { - this.contentStartTime = contentStartTime; - } - public void setShowNotificationControls(boolean showNotificationControls) { this.showNotificationControls = showNotificationControls; diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt index bb607671b1..8f8f6fca6c 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt @@ -52,7 +52,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View private const val PROP_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount" private const val PROP_MAXIMUM_BIT_RATE = "maxBitRate" private const val PROP_PLAY_IN_BACKGROUND = "playInBackground" - private const val PROP_CONTENT_START_TIME = "contentStartTime" private const val PROP_DISABLE_FOCUS = "disableFocus" private const val PROP_BUFFERING_STRATEGY = "bufferingStrategy" private const val PROP_DISABLE_DISCONNECT_ERROR = "disableDisconnectError" @@ -246,11 +245,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View videoView.setFocusable(focusable) } - @ReactProp(name = PROP_CONTENT_START_TIME, defaultInt = -1) - fun setContentStartTime(videoView: ReactExoplayerView, contentStartTime: Int) { - videoView.setContentStartTime(contentStartTime) - } - @ReactProp(name = PROP_BUFFERING_STRATEGY) fun setBufferingStrategy(videoView: ReactExoplayerView, bufferingStrategy: String) { val strategy = BufferingStrategy.parse(bufferingStrategy) diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx index 655161cde5..ae8d783cac 100644 --- a/docs/pages/component/props.mdx +++ b/docs/pages/component/props.mdx @@ -166,9 +166,13 @@ controlsStyles={{ ### `contentStartTime` +> [!WARNING] +> Deprecated, use source.contentStartTime instead + The start time in ms for SSAI content. This determines at what time to load the video info like resolutions. Use this only when you have SSAI stream where ads resolution is not the same as content resolution. +Note: This feature only works on DASH streams ### `debug` @@ -833,6 +837,13 @@ source={{ }} ``` +#### `contentStartTime` + + + +The start time in ms for SSAI content. This determines at what time to load the video info like resolutions. Use this only when you have SSAI stream where ads resolution is not the same as content resolution. +Note: This feature only works on DASH streams + #### `textTracksAllowChunklessPreparation` diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index ce722fb336..eb8a5ff1c8 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -38,6 +38,7 @@ export type VideoSrc = Readonly<{ startPosition?: Float; cropStart?: Float; cropEnd?: Float; + contentStartTime?: Int32; // Android metadata?: VideoMetadata; drm?: Drm; cmcd?: NativeCmcdConfiguration; // android @@ -344,7 +345,6 @@ export interface VideoNativeProps extends ViewProps { debug?: DebugConfig; showNotificationControls?: WithDefault; // Android, iOS bufferConfig?: BufferConfig; // Android - contentStartTime?: Int32; // Android currentPlaybackTime?: Double; // Android disableDisconnectError?: boolean; // Android focusable?: boolean; // Android diff --git a/src/types/video.ts b/src/types/video.ts index 0fbc9b72b8..0ae27c9937 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -31,6 +31,7 @@ export type ReactVideoSourceProperties = { startPosition?: number; cropStart?: number; cropEnd?: number; + contentStartTime?: number; // Android metadata?: VideoMetadata; drm?: Drm; cmcd?: Cmcd; // android @@ -264,6 +265,7 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps { bufferConfig?: BufferConfig; // Android bufferingStrategy?: BufferingStrategyType; chapters?: Chapters[]; // iOS + /** @deprecated Use source.contentStartTime */ contentStartTime?: number; // Android controls?: boolean; currentPlaybackTime?: number; // Android From f91013d32349ba898e7680649d026d4fd7b85eb6 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Sat, 14 Sep 2024 15:24:45 +0200 Subject: [PATCH 2/6] chore(android): handle code review --- .../main/java/com/brentvatne/exoplayer/ReactExoplayerView.java | 2 +- 1 file changed, 1 insertion(+), 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 c7339f0b77..368455c96c 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1428,7 +1428,7 @@ private void videoLoaded() { ArrayList audioTracks = getAudioTrackInfo(); ArrayList textTracks = getTextTrackInfo(); - if (source.getContentStartTime() != -1L) { + if (source.getContentStartTime() != -1) { ExecutorService es = Executors.newSingleThreadExecutor(); es.execute(() -> { // To prevent ANRs caused by getVideoTrackInfo we run this on a different thread and notify the player only when we're done From 9748f0ae22bb30d4428652e25c8177b5e6e9c759 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Sat, 14 Sep 2024 15:30:05 +0200 Subject: [PATCH 3/6] fix(android): add missing contentStartTime merge in source --- src/Video.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Video.tsx b/src/Video.tsx index b375b5bf72..d0fd40a2f9 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -79,6 +79,7 @@ const Video = forwardRef( poster, posterResizeMode, renderLoader, + contentStartTime, drm, textTracks, selectedVideoTrack, @@ -203,6 +204,9 @@ const Video = forwardRef( } } + const selectedContentStartTime = + source.contentStartTime || contentStartTime; + return { uri, isNetwork, @@ -215,6 +219,7 @@ const Video = forwardRef( startPosition: resolvedSource.startPosition ?? -1, cropStart: resolvedSource.cropStart || 0, cropEnd: resolvedSource.cropEnd, + contentStartTime: selectedContentStartTime, metadata: resolvedSource.metadata, drm: _drm, cmcd: _cmcd, From a63391cdd5b67ce2e48a2e93b1f54efd74f4c70b Mon Sep 17 00:00:00 2001 From: Olivier Bouillet Date: Sat, 14 Sep 2024 15:56:10 +0200 Subject: [PATCH 4/6] fix(JS): improve loader api to allow function call instead of component creation --- docs/pages/component/props.mdx | 21 +++++++++++++++++--- examples/basic/src/VideoPlayer.tsx | 4 +++- src/Video.tsx | 32 ++++++++++++++++++++++++------ src/types/video.ts | 9 ++++++++- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx index e3069d17d2..f662906de0 100644 --- a/docs/pages/component/props.mdx +++ b/docs/pages/component/props.mdx @@ -515,14 +515,29 @@ Speed at which the media should play. -Allows you to create custom components to display while the video is loading. If `renderLoader` is provided, `poster` and `posterResizeMode` will be ignored. +Allows you to create custom components to display while the video is loading. +If `renderLoader` is provided, `poster` and `posterResizeMode` will be ignored. +renderLoader is either a component or a function returning a component. +It is recommended to use the function for optimization matter. + +`renderLoader` function be called with parameters of type `ReactVideoRenderLoaderProps` to be able to adapt loader + +```typescript +interface ReactVideoRenderLoaderProps { + source?: ReactVideoSource; /// source of the video + style?: StyleProp; /// style to apply + resizeMode?: EnumValues; /// resizeMode provided to the video component +} +```` + +Sample: ```javascript ```` diff --git a/examples/basic/src/VideoPlayer.tsx b/examples/basic/src/VideoPlayer.tsx index 8a31d19839..be3da0275a 100644 --- a/examples/basic/src/VideoPlayer.tsx +++ b/examples/basic/src/VideoPlayer.tsx @@ -231,6 +231,8 @@ const VideoPlayer: FC = ({}) => { Platform.OS === 'ios' && setPaused(true); }; + const _renderLoader = showPoster ? () => : undefined + return (