From 24d90e9ec8f07777dc2924950632921b45a31269 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet <62574056+freeboub@users.noreply.github.com> Date: Sat, 14 Sep 2024 19:53:54 +0200 Subject: [PATCH] chore(android): move contentStartTime into source prop (#4160) --- .../main/java/com/brentvatne/common/api/Source.kt | 6 ++++++ .../brentvatne/exoplayer/ReactExoplayerView.java | 9 ++------- .../exoplayer/ReactExoplayerViewManager.kt | 6 ------ docs/pages/component/props.mdx | 11 +++++++++++ examples/basic/src/VideoPlayer.tsx | 14 +++++++++++--- examples/basic/src/constants/general.ts | 2 +- src/Video.tsx | 7 ++++++- src/specs/VideoNativeComponent.ts | 2 +- src/types/video.ts | 2 ++ 9 files changed, 40 insertions(+), 19 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 92fd937321..1e0240224b 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 @@ -79,6 +82,7 @@ class Source { startPositionMs == other.startPositionMs && extension == other.extension && drmProps == other.drmProps && + contentStartTime == other.contentStartTime && cmcdProps == other.cmcdProps && sideLoadedTextTracks == other.sideLoadedTextTracks ) @@ -139,6 +143,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" @@ -201,6 +206,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 86e6e3d210..d9e94f4a0c 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -235,7 +235,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; @@ -1436,7 +1435,7 @@ private void videoLoaded() { ArrayList audioTracks = getAudioTrackInfo(); ArrayList textTracks = getTextTrackInfo(); - if (this.contentStartTime != -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 @@ -1539,7 +1538,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; @@ -2207,10 +2206,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 290269e915..a21b29a325 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt @@ -49,7 +49,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" @@ -237,11 +236,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 d9bbb3f5b3..e3069d17d2 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/examples/basic/src/VideoPlayer.tsx b/examples/basic/src/VideoPlayer.tsx index 8a31d19839..71aa654e3d 100644 --- a/examples/basic/src/VideoPlayer.tsx +++ b/examples/basic/src/VideoPlayer.tsx @@ -34,7 +34,12 @@ import Video, { } from 'react-native-video'; import styles from './styles'; import {type AdditionalSourceInfo} from './types'; -import {bufferConfig, isAndroid, srcList, textTracksSelectionBy} from './constants'; +import { + bufferConfig, + isAndroid, + srcList, + textTracksSelectionBy, +} from './constants'; import {Overlay, toast, VideoLoader} from './components'; import * as NavigationBar from 'expo-navigation-bar'; @@ -224,7 +229,7 @@ const VideoPlayer: FC = ({}) => { const onVideoBandwidthUpdate = (data: OnBandwidthUpdateData) => { console.log('onVideoBandwidthUpdate', data); - } + }; const onFullScreenExit = () => { // iOS pauses video on exit from full screen @@ -284,7 +289,10 @@ const VideoPlayer: FC = ({}) => { bufferingStrategy={BufferingStrategyType.DEFAULT} debug={{enable: true, thread: true}} subtitleStyle={{subtitlesFollowVideo: true}} - controlsStyles={{hideNavigationBarOnFullScreenMode: true, hideNotificationBarOnFullScreenMode: true}} + controlsStyles={{ + hideNavigationBarOnFullScreenMode: true, + hideNotificationBarOnFullScreenMode: true, + }} /> )} diff --git a/examples/basic/src/constants/general.ts b/examples/basic/src/constants/general.ts index 6325877d23..576d9260b7 100644 --- a/examples/basic/src/constants/general.ts +++ b/examples/basic/src/constants/general.ts @@ -12,7 +12,7 @@ import {Platform} from 'react-native'; // This constant allows to change how the sample behaves regarding to texts selection. // You can change it to change how selector will use tracks information. // by default, index will be displayed and index will be applied to selected tracks. -// You can also use LANGUAGE or TITLE +// You can also use LANGUAGE or TITLE export const textTracksSelectionBy = SelectedTrackType.INDEX; export const isIos = Platform.OS === 'ios'; diff --git a/src/Video.tsx b/src/Video.tsx index cff745abb9..66f11179a3 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -79,6 +79,7 @@ const Video = forwardRef( poster, posterResizeMode, renderLoader, + contentStartTime, drm, textTracks, selectedVideoTrack, @@ -204,6 +205,9 @@ const Video = forwardRef( } } + const selectedContentStartTime = + source.contentStartTime || contentStartTime; + return { uri, isNetwork, @@ -216,6 +220,7 @@ const Video = forwardRef( startPosition: resolvedSource.startPosition ?? -1, cropStart: resolvedSource.cropStart || 0, cropEnd: resolvedSource.cropEnd, + contentStartTime: selectedContentStartTime, metadata: resolvedSource.metadata, drm: _drm, cmcd: _cmcd, @@ -223,7 +228,7 @@ const Video = forwardRef( textTracksAllowChunklessPreparation: resolvedSource.textTracksAllowChunklessPreparation, }; - }, [drm, source, textTracks]); + }, [drm, source, textTracks, contentStartTime]); const _selectedTextTrack = useMemo(() => { if (!selectedTextTrack) { diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index 7b98c0e6e8..f210cfccfb 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 444e2f8359..8aa9b8d846 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 @@ -265,6 +266,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