From 18a0d234ddff22e6c1387f78f044dc44ad10e867 Mon Sep 17 00:00:00 2001 From: Kat Huang Date: Mon, 5 Aug 2024 14:23:46 -0600 Subject: [PATCH 1/8] Add onSeekComplete callaback to ios video player --- ios/Video/RCTVideo.swift | 55 +++++++++++++++++++------------------ ios/Video/RCTVideoManager.m | 1 + 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 325a06f1..3961b259 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -114,6 +114,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH @objc var onVideoProgress: RCTDirectEventBlock? @objc var onVideoBandwidthUpdate: RCTDirectEventBlock? @objc var onVideoSeek: RCTDirectEventBlock? + @objc var onVideoSeekComplete: RCTDirectEventBlock? @objc var onVideoEnd: RCTDirectEventBlock? @objc var onTimedMetadata: RCTDirectEventBlock? @objc var onVideoAudioBecomingNoisy: RCTDirectEventBlock? @@ -762,34 +763,36 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } @objc - func setSeek(_ time: NSNumber, _ tolerance: NSNumber) { - let item: AVPlayerItem? = _player?.currentItem - +func setSeek(_ info: NSDictionary!) { + let seekTime: NSNumber! = info["time"] as! NSNumber + let seekTolerance: NSNumber! = info["tolerance"] as! NSNumber + let item: AVPlayerItem? = _player?.currentItem + guard let player = _player, let item = item, item.status == AVPlayerItem.Status.readyToPlay else { _pendingSeek = true - - guard item != nil, let player = _player, let item, item.status == AVPlayerItem.Status.readyToPlay else { - _pendingSeekTime = time.floatValue - return - } - - RCTPlayerOperations.seek( - player: player, - playerItem: item, - paused: _paused, - seekTime: time.floatValue, - seekTolerance: tolerance.floatValue - ) { [weak self] (_: Bool) in - guard let self else { return } - - self._playerObserver.addTimeObserverIfNotSet() - self.setPaused(self._paused) - self.onVideoSeek?(["currentTime": NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))), - "seekTime": time, - "target": self.reactTag]) - } - - _pendingSeek = false + _pendingSeekTime = seekTime.floatValue + return } + let wasPaused = _paused + + let cmSeekTime = CMTimeMakeWithSeconds(Float64(seekTime.floatValue), preferredTimescale: Int32(NSEC_PER_SEC)) + let tolerance = CMTimeMakeWithSeconds(Float64(seekTolerance.floatValue), preferredTimescale: Int32(NSEC_PER_SEC)) + + player.seek(to: cmSeekTime, toleranceBefore: tolerance, toleranceAfter: tolerance) { [weak self] (finished) in + guard let self = self, finished else { return } + + self._playerObserver.addTimeObserverIfNotSet() + if !wasPaused { + self.setPaused(false) + } + + let currentTime = NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))) + self.onVideoSeekComplete?(["currentTime": currentTime, + "seekTime": seekTime, + "target": self.reactTag]) + } + + _pendingSeek = false +} @objc func setRate(_ rate: Float) { diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index 3807f955..49a54933 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -46,6 +46,7 @@ RCT_EXPORT_VIEW_PROPERTY(onVideoError, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoBandwidthUpdate, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoSeek, RCTDirectEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onVideoSeekComplete, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoEnd, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onTimedMetadata, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoAudioBecomingNoisy, RCTDirectEventBlock); -- 2.46.1 From eca4ba96f078711df2281fc651527d3ed32f89cf Mon Sep 17 00:00:00 2001 From: Kat Huang Date: Mon, 5 Aug 2024 14:24:38 -0600 Subject: [PATCH 2/8] Expose onVideoSeekComplete event in React Native interface --- src/Video.tsx | 10 ++++++++++ src/specs/VideoNativeComponent.ts | 6 ++++++ src/types/events.ts | 1 + 3 files changed, 17 insertions(+) diff --git a/src/Video.tsx b/src/Video.tsx index 98b96b64..3a798741 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -30,6 +30,7 @@ import type { OnPlaybackStateChangedData, OnProgressData, OnSeekData, + OnSeekCompleteData, OnTextTrackDataChangedData, OnTimedMetadataData, OnVideoAspectRatioData, @@ -90,6 +91,7 @@ const Video = forwardRef( onError, onProgress, onSeek, + onSeekComplete, onEnd, onBuffer, onBandwidthUpdate, @@ -385,6 +387,13 @@ const Video = forwardRef( [onSeek], ); + const onVideoSeekComplete = useCallback( + (e: NativeSyntheticEvent) => { + onSeekComplete?.(e.nativeEvent); + }, + [onSeekComplete] + ); + const onVideoPlaybackStateChanged = useCallback( (e: NativeSyntheticEvent) => { onPlaybackStateChanged?.(e.nativeEvent); @@ -716,6 +725,7 @@ const Video = forwardRef( onVideoError={onError ? onVideoError : undefined} onVideoProgress={onProgress ? onVideoProgress : undefined} onVideoSeek={onSeek ? onVideoSeek : undefined} + onVideoSeekComplete={onSeekComplete ? onVideoSeekComplete : undefined} onVideoEnd={onEnd} onVideoBuffer={onBuffer ? onVideoBuffer : undefined} onVideoPlaybackStateChanged={ diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index 546bf97d..7e929262 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -182,6 +182,12 @@ export type OnSeekData = Readonly<{ seekTime: Float; }>; +export type OnVideoSeekCompleteData = Readonly<{ + currentTime: number; + seekTime: number; + target: number; +}>; + export type OnPlaybackStateChangedData = Readonly<{ isPlaying: boolean; isSeeking: boolean; diff --git a/src/types/events.ts b/src/types/events.ts index 6d3e0d9e..3f26f40a 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -258,6 +258,7 @@ export interface ReactVideoEvents { onReceiveAdEvent?: (e: OnReceiveAdEventData) => void; //Android, iOS onRestoreUserInterfaceForPictureInPictureStop?: () => void; //iOS onSeek?: (e: OnSeekData) => void; //Android, iOS, Windows UWP + onSeekComplete?: (e: OnSeekCompleteData) -> void; // iOS onPlaybackStateChanged?: (e: OnPlaybackStateChangedData) => void; // Android, iOS onTimedMetadata?: (e: OnTimedMetadataData) => void; //Android, iOS onAudioTracks?: (e: OnAudioTracksData) => void; // Android -- 2.46.1 From f4198476ce8664be0d863062dd0e4c672dfe2196 Mon Sep 17 00:00:00 2001 From: Kat Huang Date: Mon, 5 Aug 2024 14:51:00 -0600 Subject: [PATCH 3/8] Import onSeekData --- src/types/events.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/events.ts b/src/types/events.ts index 3f26f40a..179d77fc 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -12,6 +12,7 @@ import type { OnPlaybackStateChangedData, OnProgressData, OnSeekData, + OnSeekCompleteData, OnTextTrackDataChangedData, OnTimedMetadataData, OnVideoAspectRatioData, -- 2.46.1 From 75eb2658bf320db01544bd83c2afd6fcbc37f439 Mon Sep 17 00:00:00 2001 From: Kat Huang Date: Mon, 5 Aug 2024 14:56:39 -0600 Subject: [PATCH 4/8] Fix syntax in seek --- src/types/events.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/events.ts b/src/types/events.ts index 179d77fc..fd4f2b6d 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -259,7 +259,7 @@ export interface ReactVideoEvents { onReceiveAdEvent?: (e: OnReceiveAdEventData) => void; //Android, iOS onRestoreUserInterfaceForPictureInPictureStop?: () => void; //iOS onSeek?: (e: OnSeekData) => void; //Android, iOS, Windows UWP - onSeekComplete?: (e: OnSeekCompleteData) -> void; // iOS + onSeekComplete?: (e: OnSeekCompleteData) => void; // iOS onPlaybackStateChanged?: (e: OnPlaybackStateChangedData) => void; // Android, iOS onTimedMetadata?: (e: OnTimedMetadataData) => void; //Android, iOS onAudioTracks?: (e: OnAudioTracksData) => void; // Android -- 2.46.1 From efe2739437df474c4a62680c4270bd008eed8c63 Mon Sep 17 00:00:00 2001 From: Kat Huang Date: Mon, 5 Aug 2024 14:59:17 -0600 Subject: [PATCH 5/8] Thread through onSeekCompleteData type --- src/specs/VideoNativeComponent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index 7e929262..20d3682b 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -182,7 +182,7 @@ export type OnSeekData = Readonly<{ seekTime: Float; }>; -export type OnVideoSeekCompleteData = Readonly<{ +export type OnSeekCompleteData = Readonly<{ currentTime: number; seekTime: number; target: number; -- 2.46.1 From 338148a95c40590c70d568fa45f147d2ea21a1ca Mon Sep 17 00:00:00 2001 From: Kat Huang Date: Mon, 5 Aug 2024 15:06:46 -0600 Subject: [PATCH 6/8] Add onSeekComplete to VideoNativeProps --- src/specs/VideoNativeComponent.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index 20d3682b..72813c6c 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -355,6 +355,7 @@ export interface VideoNativeProps extends ViewProps { onVideoProgress?: DirectEventHandler; onVideoBandwidthUpdate?: DirectEventHandler; onVideoSeek?: DirectEventHandler; + onVideoSeekComplete?: DirectEventHandler; onVideoEnd?: DirectEventHandler<{}>; // all onVideoAudioBecomingNoisy?: DirectEventHandler<{}>; onVideoFullscreenPlayerWillPresent?: DirectEventHandler<{}>; // ios, android -- 2.46.1 From 7d975dfd1326ed3e20ef8578470517cb067695eb Mon Sep 17 00:00:00 2001 From: Kat Huang Date: Mon, 5 Aug 2024 16:08:07 -0600 Subject: [PATCH 7/8] Keep startSeek function signature consistent --- ios/Video/RCTVideo.swift | 52 +++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 3961b259..fc285143 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -761,39 +761,41 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _paused = paused } + + @objc + func setSeek(_ time: NSNumber, _ tolerance: NSNumber) { + let item: AVPlayerItem? = _player?.currentItem + guard item != nil, let player = _player, let item, item.status == AVPlayerItem.Status.readyToPlay else { + _pendingSeek = true + _pendingSeekTime = time.floatValue + return + } + let wasPaused = _paused - @objc -func setSeek(_ info: NSDictionary!) { - let seekTime: NSNumber! = info["time"] as! NSNumber - let seekTolerance: NSNumber! = info["tolerance"] as! NSNumber - let item: AVPlayerItem? = _player?.currentItem - guard let player = _player, let item = item, item.status == AVPlayerItem.Status.readyToPlay else { - _pendingSeek = true - _pendingSeekTime = seekTime.floatValue - return - } - let wasPaused = _paused + let seekTime = CMTimeMakeWithSeconds(Float64(time.floatValue), preferredTimescale: Int32(NSEC_PER_SEC)) + let toleranceTime = CMTimeMakeWithSeconds(Float64(tolerance.floatValue), preferredTimescale: Int32(NSEC_PER_SEC)) - let cmSeekTime = CMTimeMakeWithSeconds(Float64(seekTime.floatValue), preferredTimescale: Int32(NSEC_PER_SEC)) - let tolerance = CMTimeMakeWithSeconds(Float64(seekTolerance.floatValue), preferredTimescale: Int32(NSEC_PER_SEC)) + player.seek(to: seekTime, toleranceBefore: toleranceTime, toleranceAfter: toleranceTime) { [weak self] (finished) in + guard let self = self, finished else { return } - player.seek(to: cmSeekTime, toleranceBefore: tolerance, toleranceAfter: tolerance) { [weak self] (finished) in - guard let self = self, finished else { return } + self._playerObserver.addTimeObserverIfNotSet() + if !wasPaused { + self.setPaused(false) + } - self._playerObserver.addTimeObserverIfNotSet() - if !wasPaused { - self.setPaused(false) + let currentTime = NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))) + self.onVideoSeek?(["currentTime": currentTime, + "seekTime": time, + "target": self.reactTag]) + + self.onVideoSeekComplete?(["currentTime": currentTime, + "seekTime": time, + "target": self.reactTag]) } - let currentTime = NSNumber(value: Float(CMTimeGetSeconds(item.currentTime()))) - self.onVideoSeekComplete?(["currentTime": currentTime, - "seekTime": seekTime, - "target": self.reactTag]) + _pendingSeek = false } - _pendingSeek = false -} - @objc func setRate(_ rate: Float) { if _rate != 1 { -- 2.46.1 From 38ccdd1df6080817b60b3fc105bbd6e3b47a1174 Mon Sep 17 00:00:00 2001 From: Kat Huang Date: Mon, 5 Aug 2024 16:39:48 -0600 Subject: [PATCH 8/8] WIP --- ios/Video/RCTVideo.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index fc285143..4d16679a 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -761,8 +761,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _paused = paused } - - @objc + + @objc func setSeek(_ time: NSNumber, _ tolerance: NSNumber) { let item: AVPlayerItem? = _player?.currentItem guard item != nil, let player = _player, let item, item.status == AVPlayerItem.Status.readyToPlay else { -- 2.46.1