diff --git a/pkg/ffmpeg/stream_transcode.go b/pkg/ffmpeg/stream_transcode.go index e0a30cdd9..bb701664f 100644 --- a/pkg/ffmpeg/stream_transcode.go +++ b/pkg/ffmpeg/stream_transcode.go @@ -1,6 +1,7 @@ package ffmpeg import ( + "context" "errors" "io" "net/http" @@ -230,7 +231,10 @@ func (sm *StreamManager) ServeTranscode(w http.ResponseWriter, r *http.Request, handler, err := sm.getTranscodeStream(lockCtx, options) if err != nil { - logger.Errorf("[transcode] error transcoding video file: %v", err) + // don't log context canceled errors + if !errors.Is(err, context.Canceled) { + logger.Errorf("[transcode] error transcoding video file: %v", err) + } w.WriteHeader(http.StatusBadRequest) if _, err := w.Write([]byte(err.Error())); err != nil { logger.Warnf("[transcode] error writing response: %v", err) diff --git a/ui/v2.5/src/components/ScenePlayer/ScenePlayer.tsx b/ui/v2.5/src/components/ScenePlayer/ScenePlayer.tsx index 897ac870b..bd3f00522 100644 --- a/ui/v2.5/src/components/ScenePlayer/ScenePlayer.tsx +++ b/ui/v2.5/src/components/ScenePlayer/ScenePlayer.tsx @@ -451,12 +451,28 @@ export const ScenePlayer: React.FC = ({ if (!player) return; function canplay(this: VideoJsPlayer) { + // if we're seeking before starting, don't set the initial timestamp + // when starting from the beginning, there is a small delay before the event + // is triggered, so we can't just check if the time is 0 + if (this.currentTime() >= 0.1) { + return; + } + if (initialTimestamp.current !== -1) { this.currentTime(initialTimestamp.current); initialTimestamp.current = -1; } } + function timeupdate(this: VideoJsPlayer) { + // fired when seeking + // check if we haven't started playing yet + // if so, start playing + if (!started.current) { + this.play(); + } + } + function playing(this: VideoJsPlayer) { // This still runs even if autoplay failed on Safari, // only set flag if actually playing @@ -477,12 +493,14 @@ export const ScenePlayer: React.FC = ({ player.on("playing", playing); player.on("loadstart", loadstart); player.on("fullscreenchange", fullscreenchange); + player.on("timeupdate", timeupdate); return () => { player.off("canplay", canplay); player.off("playing", playing); player.off("loadstart", loadstart); player.off("fullscreenchange", fullscreenchange); + player.off("timeupdate", timeupdate); }; }, [getPlayer]); diff --git a/ui/v2.5/src/components/ScenePlayer/live.ts b/ui/v2.5/src/components/ScenePlayer/live.ts index e55456cb9..2ab51f763 100644 --- a/ui/v2.5/src/components/ScenePlayer/live.ts +++ b/ui/v2.5/src/components/ScenePlayer/live.ts @@ -1,3 +1,4 @@ +import { debounce } from "lodash-es"; import videojs, { VideoJsPlayer } from "video.js"; export interface ISource extends videojs.Tech.SourceObject { @@ -10,6 +11,9 @@ interface ICue extends TextTrackCue { _endTime?: number; } +// delay before loading new source after setting currentTime +const loadDelay = 200; + function offsetMiddleware(player: VideoJsPlayer) { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- allow access to private tech methods let tech: any; @@ -50,6 +54,34 @@ function offsetMiddleware(player: VideoJsPlayer) { } } + const loadSource = debounce( + (seconds: number) => { + const srcUrl = new URL(source.src); + srcUrl.searchParams.set("start", seconds.toString()); + source.src = srcUrl.toString(); + + const poster = player.poster(); + const playbackRate = tech.playbackRate(); + seeking = tech.paused() ? 1 : 2; + player.poster(""); + tech.setSource(source); + tech.setPlaybackRate(playbackRate); + tech.one("canplay", () => { + player.poster(poster); + if (seeking === 1 || tech.scrubbing()) { + tech.pause(); + } + seeking = 0; + }); + tech.trigger("timeupdate"); + tech.trigger("pause"); + tech.trigger("seeking"); + tech.play(); + }, + loadDelay, + { leading: true } + ); + return { setTech(newTech: videojs.Tech) { tech = newTech; @@ -144,27 +176,7 @@ function offsetMiddleware(player: VideoJsPlayer) { updateOffsetStart(seconds); - const srcUrl = new URL(source.src); - srcUrl.searchParams.set("start", seconds.toString()); - source.src = srcUrl.toString(); - - const poster = player.poster(); - const playbackRate = tech.playbackRate(); - seeking = tech.paused() ? 1 : 2; - player.poster(""); - tech.setSource(source); - tech.setPlaybackRate(playbackRate); - tech.one("canplay", () => { - player.poster(poster); - if (seeking === 1) { - tech.pause(); - } - seeking = 0; - }); - tech.trigger("timeupdate"); - tech.trigger("pause"); - tech.trigger("seeking"); - tech.play(); + loadSource(seconds); return 0; },