From 9c02fa2e723a7d7ee0d1b7720f9ccca6ad5696e0 Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Sat, 7 Dec 2024 23:04:21 +1100 Subject: [PATCH 1/2] Refactored perfomance change for IOS --- modules/vlc-player/ios/VlcPlayerModule.swift | 4 +-- modules/vlc-player/ios/VlcPlayerView.swift | 35 ++++++++++---------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/modules/vlc-player/ios/VlcPlayerModule.swift b/modules/vlc-player/ios/VlcPlayerModule.swift index 131f4ebf..64d6cad5 100644 --- a/modules/vlc-player/ios/VlcPlayerModule.swift +++ b/modules/vlc-player/ios/VlcPlayerModule.swift @@ -5,9 +5,7 @@ public class VlcPlayerModule: Module { Name("VlcPlayer") View(VlcPlayerView.self) { Prop("source") { (view: VlcPlayerView, source: [String: Any]) in - if !view.hasSource { - view.setSource(source) - } + view.setSource(source) } Prop("paused") { (view: VlcPlayerView, paused: Bool) in diff --git a/modules/vlc-player/ios/VlcPlayerView.swift b/modules/vlc-player/ios/VlcPlayerView.swift index 8feec050..a5a0835d 100644 --- a/modules/vlc-player/ios/VlcPlayerView.swift +++ b/modules/vlc-player/ios/VlcPlayerView.swift @@ -16,6 +16,7 @@ class VlcPlayerView: ExpoView { private var externalTrack: [String: String]? private var progressTimer: DispatchSourceTimer? private var isStopping: Bool = false // Define isStopping here + private var lastProgressCall = Date().timeIntervalSince1970 var hasSource = false // MARK: - Initialization @@ -24,7 +25,6 @@ class VlcPlayerView: ExpoView { super.init(appContext: appContext) setupView() setupNotifications() - setupProgressTimer() } // MARK: - Setup @@ -56,15 +56,6 @@ class VlcPlayerView: ExpoView { name: UIApplication.didBecomeActiveNotification, object: nil) } - private func setupProgressTimer() { - progressTimer = DispatchSource.makeTimerSource(queue: DispatchQueue.main) - progressTimer?.schedule(deadline: .now(), repeating: progressUpdateInterval) - progressTimer?.setEventHandler { [weak self] in - self?.updateVideoProgress() - } - progressTimer?.resume() - } - // MARK: - Public Methods @objc func play() { @@ -88,7 +79,7 @@ class VlcPlayerView: ExpoView { let wasPlaying = player.isPlaying if wasPlaying { - player.pause() + self.pause() } if let duration = player.media?.length.intValue { @@ -101,7 +92,7 @@ class VlcPlayerView: ExpoView { // Wait for a short moment to ensure the seek has been processed DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { if wasPlaying { - player.play() + self.play() } self.updatePlayerState() } @@ -114,6 +105,9 @@ class VlcPlayerView: ExpoView { @objc func setSource(_ source: [String: Any]) { DispatchQueue.main.async { [weak self] in guard let self = self else { return } + if self.hasSource { + return + } let mediaOptions = source["mediaOptions"] as? [String: Any] ?? [:] self.externalTrack = source["externalTrack"] as? [String: String] @@ -158,7 +152,7 @@ class VlcPlayerView: ExpoView { self.setSubtitleTrack(subtitleTrackIndex) self.mediaPlayer?.media = media - hasSource = true + self.hasSource = true if autoplay { print("Playing...") @@ -314,6 +308,7 @@ class VlcPlayerView: ExpoView { let currentTimeMs = player.time.intValue let durationMs = player.media?.length.intValue ?? 0 + print("Debug: Current time: \(currentTimeMs)") if currentTimeMs >= 0 && currentTimeMs < durationMs { if player.isPlaying && !self.isMediaReady { self.isMediaReady = true @@ -345,11 +340,19 @@ class VlcPlayerView: ExpoView { deinit { performStop() - progressTimer?.cancel() } } extension VlcPlayerView: VLCMediaPlayerDelegate { + func mediaPlayerTimeChanged(_ aNotification: Notification) { + // self?.updateVideoProgress() + let timeNow = Date().timeIntervalSince1970 + if timeNow - lastProgressCall >= 1 { + lastProgressCall = timeNow + updateVideoProgress() + } + } + func mediaPlayerStateChanged(_ aNotification: Notification) { self.updatePlayerState() } @@ -395,10 +398,6 @@ extension VlcPlayerView: VLCMediaPlayerDelegate { } } - - func mediaPlayerTimeChanged(_ aNotification: Notification) { - // No need to call updateVideoProgress here as it's handled by the timer - } } extension VlcPlayerView: VLCMediaDelegate { From 26305c2983582ba2d053a6427c0c43b0100946af Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Sun, 8 Dec 2024 02:48:23 +1100 Subject: [PATCH 2/2] Used debouncing for trick play and stop rendering trickplay, while not sliding --- components/video-player/controls/Controls.tsx | 177 +++++++++--------- modules/vlc-player/ios/VlcPlayerView.swift | 96 ++++------ 2 files changed, 131 insertions(+), 142 deletions(-) diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index ec4e68e8..c74c9c09 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -52,6 +52,7 @@ import DropdownViewDirect from "./dropdown/DropdownViewDirect"; import DropdownViewTranscoding from "./dropdown/DropdownViewTranscoding"; import BrightnessSlider from "./BrightnessSlider"; import SkipButton from "./SkipButton"; +import { debounce } from "lodash"; interface Props { item: BaseItemDto; @@ -245,13 +246,25 @@ export const Controls: React.FC = ({ useEffect(() => { prefetchAllTrickplayImages(); }, []); - const toggleControls = () => setShowControls(!showControls); + const handleSliderStart = useCallback(() => { + if (showControls === false) return; + + setIsSliding(true); + wasPlayingRef.current = isPlaying; + lastProgressRef.current = progress.value; + + pause(); + isSeeking.value = true; + }, [showControls, isPlaying]); + + const [isSliding, setIsSliding] = useState(false); const handleSliderComplete = useCallback( async (value: number) => { isSeeking.value = false; progress.value = value; + setIsSliding(false); await seek( Math.max(0, Math.floor(isVlc ? value : ticksToSeconds(value))) @@ -262,27 +275,20 @@ export const Controls: React.FC = ({ ); const [time, setTime] = useState({ hours: 0, minutes: 0, seconds: 0 }); + const handleSliderChange = useCallback( + debounce((value: number) => { + const progressInTicks = msToTicks(value); + console.log("Progress in ticks", progressInTicks); + calculateTrickplayUrl(progressInTicks); - const handleSliderChange = (value: number) => { - const progressInTicks = isVlc ? msToTicks(value) : value; - calculateTrickplayUrl(progressInTicks); - - const progressInSeconds = Math.floor(ticksToSeconds(progressInTicks)); - const hours = Math.floor(progressInSeconds / 3600); - const minutes = Math.floor((progressInSeconds % 3600) / 60); - const seconds = progressInSeconds % 60; - setTime({ hours, minutes, seconds }); - }; - - const handleSliderStart = useCallback(() => { - if (showControls === false) return; - - wasPlayingRef.current = isPlaying; - lastProgressRef.current = progress.value; - - pause(); - isSeeking.value = true; - }, [showControls, isPlaying]); + const progressInSeconds = Math.floor(ticksToSeconds(progressInTicks)); + const hours = Math.floor(progressInSeconds / 3600); + const minutes = Math.floor((progressInSeconds % 3600) / 60); + const seconds = progressInSeconds % 60; + setTime({ hours, minutes, seconds }); + }, 20), // 100ms debounce delay + [] + ); const handleSkipBackward = useCallback(async () => { if (!settings?.rewindSkipTime) return; @@ -326,6 +332,71 @@ export const Controls: React.FC = ({ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); }, []); + const memoizedRenderBubble = useCallback(() => { + if (!trickPlayUrl || !trickplayInfo) { + return null; + } + const { x, y, url } = trickPlayUrl; + const tileWidth = 150; + const tileHeight = 150 / trickplayInfo.aspectRatio!; + + console.log("time, ", time); + + return ( + + + + + + {`${time.hours > 0 ? `${time.hours}:` : ""}${ + time.minutes < 10 ? `0${time.minutes}` : time.minutes + }:${time.seconds < 10 ? `0${time.seconds}` : time.seconds}`} + + + ); + }, [trickPlayUrl, trickplayInfo, time]); + return ( = ({ containerStyle={{ borderRadius: 100, }} - renderBubble={() => { - if (!trickPlayUrl || !trickplayInfo) { - return null; - } - const { x, y, url } = trickPlayUrl; - const tileWidth = 150; - const tileHeight = 150 / trickplayInfo.aspectRatio!; - return ( - - - - - - {`${time.hours > 0 ? `${time.hours}:` : ""}${ - time.minutes < 10 ? `0${time.minutes}` : time.minutes - }:${ - time.seconds < 10 ? `0${time.seconds}` : time.seconds - }`} - - - ); - }} + renderBubble={() => isSliding && memoizedRenderBubble()} sliderHeight={10} thumbWidth={0} progress={progress} diff --git a/modules/vlc-player/ios/VlcPlayerView.swift b/modules/vlc-player/ios/VlcPlayerView.swift index a5a0835d..f63aff44 100644 --- a/modules/vlc-player/ios/VlcPlayerView.swift +++ b/modules/vlc-player/ios/VlcPlayerView.swift @@ -59,46 +59,37 @@ class VlcPlayerView: ExpoView { // MARK: - Public Methods @objc func play() { - DispatchQueue.main.async { [weak self] in - self?.mediaPlayer?.play() - self?.isPaused = false - print("Play") - } + self.mediaPlayer?.play() + self.isPaused = false + print("Play") } @objc func pause() { - DispatchQueue.main.async { [weak self] in - self?.mediaPlayer?.pause() - self?.isPaused = true - } + self.mediaPlayer?.pause() + self.isPaused = true } @objc func seekTo(_ time: Int32) { - DispatchQueue.main.async { [weak self] in - guard let self = self, let player = self.mediaPlayer else { return } + guard let player = self.mediaPlayer else { return } + + let wasPlaying = player.isPlaying + if wasPlaying { + self.pause() + } + + if let duration = player.media?.length.intValue { + print("Seeking to time: \(time) Video Duration \(duration)") + + // If the specified time is greater than the duration, seek to the end + let seekTime = time > duration ? duration - 1000 : time + player.time = VLCTime(int: seekTime) - let wasPlaying = player.isPlaying if wasPlaying { - self.pause() - } - - if let duration = player.media?.length.intValue { - print("Seeking to time: \(time) Video Duration \(duration)") - - // If the specified time is greater than the duration, seek to the end - let seekTime = time > duration ? duration - 1000 : time - player.time = VLCTime(int: seekTime) - - // Wait for a short moment to ensure the seek has been processed - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - if wasPlaying { - self.play() - } - self.updatePlayerState() - } - } else { - print("Error: Unable to retrieve video duration") + self.play() } + self.updatePlayerState() + } else { + print("Error: Unable to retrieve video duration") } } @@ -146,11 +137,6 @@ class VlcPlayerView: ExpoView { print("Debug: Media options: \(mediaOptions)") media.addOptions(mediaOptions) - // Apply subtitle options - let subtitleTrackIndex = source["subtitleTrackIndex"] as? Int ?? -1 - print("Debug: Subtitle track index from source: \(subtitleTrackIndex)") - self.setSubtitleTrack(subtitleTrackIndex) - self.mediaPlayer?.media = media self.hasSource = true @@ -162,9 +148,7 @@ class VlcPlayerView: ExpoView { } @objc func setAudioTrack(_ trackIndex: Int) { - DispatchQueue.main.async { - self.mediaPlayer?.currentAudioTrackIndex = Int32(trackIndex) - } + self.mediaPlayer?.currentAudioTrackIndex = Int32(trackIndex) } @objc func getAudioTracks() -> [[String: Any]]? { @@ -181,29 +165,25 @@ class VlcPlayerView: ExpoView { @objc func setSubtitleTrack(_ trackIndex: Int) { print("Debug: Attempting to set subtitle track to index: \(trackIndex)") - DispatchQueue.main.async { - self.mediaPlayer?.currentVideoSubTitleIndex = Int32(trackIndex) - print( - "Debug: Current subtitle track index after setting: \(self.mediaPlayer?.currentVideoSubTitleIndex ?? -1)" - ) - } + self.mediaPlayer?.currentVideoSubTitleIndex = Int32(trackIndex) + print( + "Debug: Current subtitle track index after setting: \(self.mediaPlayer?.currentVideoSubTitleIndex ?? -1)" + ) } @objc func setSubtitleURL(_ subtitleURL: String, name: String) { - DispatchQueue.main.async { [weak self] in - guard let self = self, let url = URL(string: subtitleURL) else { - print("Error: Invalid subtitle URL") - return - } + guard let url = URL(string: subtitleURL) else { + print("Error: Invalid subtitle URL") + return + } - let result = self.mediaPlayer?.addPlaybackSlave(url, type: .subtitle, enforce: true) - if let result = result { - let internalName = "Track \(self.customSubtitles.count + 1)" - print("Subtitle added with result: \(result) \(internalName)") - self.customSubtitles.append((internalName: internalName, originalName: name)) - } else { - print("Failed to add subtitle") - } + let result = self.mediaPlayer?.addPlaybackSlave(url, type: .subtitle, enforce: true) + if let result = result { + let internalName = "Track \(self.customSubtitles.count + 1)" + print("Subtitle added with result: \(result) \(internalName)") + self.customSubtitles.append((internalName: internalName, originalName: name)) + } else { + print("Failed to add subtitle") } }