Skip to content

Commit

Permalink
Scene player fixes and improvements (#5340)
Browse files Browse the repository at this point in the history
* Don't log context canceled error during live transcode
* Pause live transcode if still scrubbing
* Debounce loading live transcode source to avoid multiple ffmpeg instances
* Don't start from start or resume time if seeking before playing
* Play video when seeked before playing
  • Loading branch information
WithoutPants authored Oct 6, 2024
1 parent 3e4515e commit 4697271
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 22 deletions.
6 changes: 5 additions & 1 deletion pkg/ffmpeg/stream_transcode.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ffmpeg

import (
"context"
"errors"
"io"
"net/http"
Expand Down Expand Up @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions ui/v2.5/src/components/ScenePlayer/ScenePlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -451,12 +451,28 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
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
Expand All @@ -477,12 +493,14 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
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]);

Expand Down
54 changes: 33 additions & 21 deletions ui/v2.5/src/components/ScenePlayer/live.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { debounce } from "lodash-es";
import videojs, { VideoJsPlayer } from "video.js";

export interface ISource extends videojs.Tech.SourceObject {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
},
Expand Down

0 comments on commit 4697271

Please sign in to comment.