From a3465030d09bc13e2d71a63ad16a4879a9b4d257 Mon Sep 17 00:00:00 2001 From: stefpi <19478336+stefpi@users.noreply.github.com> Date: Wed, 15 Apr 2026 13:50:34 -0700 Subject: [PATCH 1/5] testing html --- video-test.html | 892 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 892 insertions(+) create mode 100644 video-test.html diff --git a/video-test.html b/video-test.html new file mode 100644 index 00000000..be913597 --- /dev/null +++ b/video-test.html @@ -0,0 +1,892 @@ + + + + + + Drive Video Seek Lab + + + + +
+
+

Drive Video Seek Lab

+

+ Paste a signed qcamera HLS URL and test seek behavior with smaller buffers, coalesced rapid scrubs, + and seek timing instrumentation. The goal is to see whether these changes reduce lag before we touch app code. +

+
+ +
+
+ + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + + + + + +
+
+ +
+ +
+ +
+
+ Scrub Test + Current 0.00s | Target 0.00s +
+ +
+ + + + + +
+
+ +
+
+

Current Stats

+
Load a route to populate stats.
+
+ +
+

Last Seek

+
No seek recorded yet.
+
+ +
+

Event Log

+
No events yet.
+
+
+
+ + + + From e55a62c44074f3ed716d5788b96ff515c20be512 Mon Sep 17 00:00:00 2001 From: stefpi <19478336+stefpi@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:41:17 -0700 Subject: [PATCH 2/5] run sync video on timeupdate --- src/components/DriveVideo/index.jsx | 42 +- video-test.html | 892 ---------------------------- 2 files changed, 33 insertions(+), 901 deletions(-) delete mode 100644 video-test.html diff --git a/src/components/DriveVideo/index.jsx b/src/components/DriveVideo/index.jsx index b35f95b7..9d4083b6 100644 --- a/src/components/DriveVideo/index.jsx +++ b/src/components/DriveVideo/index.jsx @@ -64,10 +64,11 @@ class DriveVideo extends Component { this.onHlsError = this.onHlsError.bind(this); this.onVideoError = this.onVideoError.bind(this); this.onVideoResume = this.onVideoResume.bind(this); - this.syncVideo = debounce(this.syncVideo.bind(this), 200, true); + this.syncVideo = this.syncVideo.bind(this); this.firstSeek = true; this.videoPlayer = React.createRef(); + this.internalPlayer = null; this.state = { src: null, @@ -82,7 +83,6 @@ class DriveVideo extends Component { } this.updateVideoSource({}); this.syncVideo(); - this.videoSyncIntv = setInterval(this.syncVideo, 500); } componentDidUpdate(prevProps) { @@ -91,9 +91,10 @@ class DriveVideo extends Component { } componentWillUnmount() { - if (this.videoSyncIntv) { - clearTimeout(this.videoSyncIntv); - this.videoSyncIntv = null; + if (this.internalPlayer) { + this.internalPlayer.removeEventListener('timeupdate', this.syncVideo); + this.internalPlayer.removeEventListener('seeked', this.syncVideo); + this.internalPlayer.removeEventListener('canplay', this.syncVideo); } } @@ -179,8 +180,12 @@ class DriveVideo extends Component { } onVideoResume() { + const { dispatch, isBufferingVideo } = this.props; const { videoError } = this.state; if (videoError) this.setState({ videoError: null }); + if (isBufferingVideo) { + dispatch(bufferVideo(false)); + } } updateVideoSource(prevProps) { @@ -194,6 +199,12 @@ class DriveVideo extends Component { } if (src === '' || !prevProps.currentRoute || prevProps.currentRoute?.fullname !== currentRoute.fullname) { + if (this.internalPlayer) { + this.internalPlayer.removeEventListener('timeupdate', this.syncVideo); + this.internalPlayer.removeEventListener('seeked', this.syncVideo); + this.internalPlayer.removeEventListener('canplay', this.syncVideo); + this.internalPlayer = null; + } src = Video.getQcameraStreamUrl(currentRoute.fullname, currentRoute.share_exp, currentRoute.share_sig); this.setState({ src, videoError: null }); this.syncVideo(); @@ -201,6 +212,7 @@ class DriveVideo extends Component { } syncVideo() { + console.log("sync da video") const { dispatch, isBufferingVideo, isMuted } = this.props; const videoPlayer = this.videoPlayer.current; if (!videoPlayer || !videoPlayer.getInternalPlayer() || !videoPlayer.getDuration()) { @@ -228,12 +240,12 @@ class DriveVideo extends Component { const internalPlayer = videoPlayer.getInternalPlayer(); const { hasLoaded } = getVideoState(videoPlayer); - if (isBufferingVideo && internalPlayer.readyState >= 4) { + if (internalPlayer.readyState >= 4 && isBufferingVideo) { dispatch(bufferVideo(false)); - } else if (isBufferingVideo || !hasLoaded || internalPlayer.readyState < 2) { + } else if (internalPlayer.readyState < 2) { if (!isBufferingVideo) { dispatch(bufferVideo(true)); - } + } newPlaybackRate = 0; // in some circumstances, iOS won't update readyState unless temporarily paused } @@ -275,8 +287,20 @@ class DriveVideo extends Component { const { src, videoError } = this.state; const onPlayerReady = (player) => { + const videoElement = player.getInternalPlayer(); + if (videoElement) { + if (this.internalPlayer) { + this.internalPlayer.removeEventListener('timeupdate', this.syncVideo); + this.internalPlayer.removeEventListener('seeked', this.syncVideo); + this.internalPlayer.removeEventListener('canplay', this.syncVideo); + } + this.internalPlayer = videoElement; + videoElement.addEventListener('timeupdate', this.syncVideo); + videoElement.addEventListener('seeked', this.syncVideo); + videoElement.addEventListener('canplay', this.syncVideo); + } + if (isIos()) { // ios does not support hls.js and on other browsers hls.js does not directly play the m3u8 so audioTracks are not visible - const videoElement = player.getInternalPlayer(); if (videoElement && videoElement.audioTracks && videoElement.audioTracks.length > 0) { if (onAudioStatusChange) { onAudioStatusChange(true); diff --git a/video-test.html b/video-test.html deleted file mode 100644 index be913597..00000000 --- a/video-test.html +++ /dev/null @@ -1,892 +0,0 @@ - - - - - - Drive Video Seek Lab - - - - -
-
-

Drive Video Seek Lab

-

- Paste a signed qcamera HLS URL and test seek behavior with smaller buffers, coalesced rapid scrubs, - and seek timing instrumentation. The goal is to see whether these changes reduce lag before we touch app code. -

-
- -
-
- - -
- -
- - - -
- -
- - - -
- -
- - - - - - - -
-
- -
- -
- -
-
- Scrub Test - Current 0.00s | Target 0.00s -
- -
- - - - - -
-
- -
-
-

Current Stats

-
Load a route to populate stats.
-
- -
-

Last Seek

-
No seek recorded yet.
-
- -
-

Event Log

-
No events yet.
-
-
-
- - - - From 81a92fb25e50ff304059423ed6f4b38f7adfed37 Mon Sep 17 00:00:00 2001 From: stefpi <19478336+stefpi@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:07:53 -0700 Subject: [PATCH 3/5] remove canplay eventlistener --- src/components/DriveVideo/index.jsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/DriveVideo/index.jsx b/src/components/DriveVideo/index.jsx index 9d4083b6..2d9dd975 100644 --- a/src/components/DriveVideo/index.jsx +++ b/src/components/DriveVideo/index.jsx @@ -94,7 +94,6 @@ class DriveVideo extends Component { if (this.internalPlayer) { this.internalPlayer.removeEventListener('timeupdate', this.syncVideo); this.internalPlayer.removeEventListener('seeked', this.syncVideo); - this.internalPlayer.removeEventListener('canplay', this.syncVideo); } } @@ -202,7 +201,6 @@ class DriveVideo extends Component { if (this.internalPlayer) { this.internalPlayer.removeEventListener('timeupdate', this.syncVideo); this.internalPlayer.removeEventListener('seeked', this.syncVideo); - this.internalPlayer.removeEventListener('canplay', this.syncVideo); this.internalPlayer = null; } src = Video.getQcameraStreamUrl(currentRoute.fullname, currentRoute.share_exp, currentRoute.share_sig); @@ -212,7 +210,6 @@ class DriveVideo extends Component { } syncVideo() { - console.log("sync da video") const { dispatch, isBufferingVideo, isMuted } = this.props; const videoPlayer = this.videoPlayer.current; if (!videoPlayer || !videoPlayer.getInternalPlayer() || !videoPlayer.getDuration()) { @@ -292,12 +289,10 @@ class DriveVideo extends Component { if (this.internalPlayer) { this.internalPlayer.removeEventListener('timeupdate', this.syncVideo); this.internalPlayer.removeEventListener('seeked', this.syncVideo); - this.internalPlayer.removeEventListener('canplay', this.syncVideo); } this.internalPlayer = videoElement; videoElement.addEventListener('timeupdate', this.syncVideo); videoElement.addEventListener('seeked', this.syncVideo); - videoElement.addEventListener('canplay', this.syncVideo); } if (isIos()) { // ios does not support hls.js and on other browsers hls.js does not directly play the m3u8 so audioTracks are not visible From 409a0a7924ae116cfeba86ac5a8c5fc6405985cd Mon Sep 17 00:00:00 2001 From: stefpi <19478336+stefpi@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:16:55 -0700 Subject: [PATCH 4/5] fix linter --- src/components/DriveVideo/index.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/DriveVideo/index.jsx b/src/components/DriveVideo/index.jsx index 2d9dd975..a974b195 100644 --- a/src/components/DriveVideo/index.jsx +++ b/src/components/DriveVideo/index.jsx @@ -2,7 +2,6 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { CircularProgress, Typography } from '@material-ui/core'; -import debounce from 'debounce'; import Obstruction from 'obstruction'; import ReactPlayer from 'react-player/file'; @@ -236,7 +235,6 @@ class DriveVideo extends Component { const internalPlayer = videoPlayer.getInternalPlayer(); - const { hasLoaded } = getVideoState(videoPlayer); if (internalPlayer.readyState >= 4 && isBufferingVideo) { dispatch(bufferVideo(false)); } else if (internalPlayer.readyState < 2) { From 685f6f2a3561dec1f0767e5c5f43307779f28602 Mon Sep 17 00:00:00 2001 From: stefpi <19478336+stefpi@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:42:53 -0700 Subject: [PATCH 5/5] add back canplay listener, lower ready state check --- src/components/DriveVideo/index.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/DriveVideo/index.jsx b/src/components/DriveVideo/index.jsx index a974b195..5334763e 100644 --- a/src/components/DriveVideo/index.jsx +++ b/src/components/DriveVideo/index.jsx @@ -93,6 +93,7 @@ class DriveVideo extends Component { if (this.internalPlayer) { this.internalPlayer.removeEventListener('timeupdate', this.syncVideo); this.internalPlayer.removeEventListener('seeked', this.syncVideo); + this.internalPlayer.removeEventListener('canplay', this.syncVideo); } } @@ -200,6 +201,7 @@ class DriveVideo extends Component { if (this.internalPlayer) { this.internalPlayer.removeEventListener('timeupdate', this.syncVideo); this.internalPlayer.removeEventListener('seeked', this.syncVideo); + this.internalPlayer.removeEventListener('canplay', this.syncVideo); this.internalPlayer = null; } src = Video.getQcameraStreamUrl(currentRoute.fullname, currentRoute.share_exp, currentRoute.share_sig); @@ -235,7 +237,7 @@ class DriveVideo extends Component { const internalPlayer = videoPlayer.getInternalPlayer(); - if (internalPlayer.readyState >= 4 && isBufferingVideo) { + if (internalPlayer.readyState >= 3 && isBufferingVideo) { dispatch(bufferVideo(false)); } else if (internalPlayer.readyState < 2) { if (!isBufferingVideo) { @@ -287,10 +289,12 @@ class DriveVideo extends Component { if (this.internalPlayer) { this.internalPlayer.removeEventListener('timeupdate', this.syncVideo); this.internalPlayer.removeEventListener('seeked', this.syncVideo); + this.internalPlayer.removeEventListener('canplay', this.syncVideo); } this.internalPlayer = videoElement; videoElement.addEventListener('timeupdate', this.syncVideo); videoElement.addEventListener('seeked', this.syncVideo); + videoElement.addEventListener('canplay', this.syncVideo); } if (isIos()) { // ios does not support hls.js and on other browsers hls.js does not directly play the m3u8 so audioTracks are not visible