Skip to content

Commit 3d660c9

Browse files
fix bug on safari where a live stream will play at the time=0 rather than next to the live edge when using startAt:fromLastPosition
1 parent f71ac51 commit 3d660c9

2 files changed

Lines changed: 44 additions & 15 deletions

File tree

src/main_thread/init/directfile_content_initializer.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export default class DirectFileContentInitializer extends ContentInitializer {
254254
function getDirectFileInitialTime(
255255
mediaElement: IMediaElement,
256256
startAt?: IInitialTimeOptions,
257-
): number {
257+
): number | undefined {
258258
if (isNullOrUndefined(startAt)) {
259259
return 0;
260260
}
@@ -268,28 +268,38 @@ function getDirectFileInitialTime(
268268
}
269269

270270
const duration = mediaElement.duration;
271-
272271
if (typeof startAt.fromLastPosition === "number") {
273-
if (isNullOrUndefined(duration) || !isFinite(duration)) {
274-
log.warn(
275-
"startAt.fromLastPosition set but no known duration, " + "beginning at 0.",
276-
);
277-
return 0;
272+
if (!isNullOrUndefined(duration) && isFinite(duration)) {
273+
return Math.max(0, duration + startAt.fromLastPosition);
278274
}
279-
return Math.max(0, duration + startAt.fromLastPosition);
275+
276+
if (mediaElement.seekable.length > 0) {
277+
const lastSegmentEnd = mediaElement.seekable.end(mediaElement.seekable.length - 1);
278+
if (isFinite(lastSegmentEnd)) {
279+
return Math.max(0, lastSegmentEnd + startAt.fromLastPosition);
280+
}
281+
}
282+
log.warn(
283+
"Init: startAt.fromLastPosition set but no known duration, " +
284+
"it may be too soon to seek",
285+
);
286+
return undefined;
280287
} else if (typeof startAt.fromLivePosition === "number") {
281288
const livePosition =
282289
mediaElement.seekable.length > 0 ? mediaElement.seekable.end(0) : duration;
283290
if (isNullOrUndefined(livePosition)) {
284291
log.warn(
285-
"startAt.fromLivePosition set but no known live position, " + "beginning at 0.",
292+
"Init: startAt.fromLivePosition set but no known live position, " +
293+
"beginning at 0.",
286294
);
287295
return 0;
288296
}
289297
return Math.max(0, livePosition + startAt.fromLivePosition);
290298
} else if (!isNullOrUndefined(startAt.percentage)) {
291299
if (isNullOrUndefined(duration) || !isFinite(duration)) {
292-
log.warn("startAt.percentage set but no known duration, " + "beginning at 0.");
300+
log.warn(
301+
"Init: startAt.percentage set but no known duration, " + "beginning at 0.",
302+
);
293303
return 0;
294304
}
295305
const { percentage } = startAt;

src/main_thread/init/utils/initial_seek_and_play.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export default function performInitialSeekAndPlay(
7171
}: {
7272
mediaElement: IMediaElement;
7373
playbackObserver: IMediaElementPlaybackObserver;
74-
startTime: number | (() => number);
74+
startTime: number | (() => number | undefined);
7575
mustAutoPlay: boolean;
7676
isDirectfile: boolean;
7777
onWarning: (err: IPlayerError) => void;
@@ -107,18 +107,37 @@ export default function performInitialSeekAndPlay(
107107
if (!isDirectfile || typeof startTime === "number") {
108108
const initiallySeekedTime =
109109
typeof startTime === "number" ? startTime : startTime();
110-
if (initiallySeekedTime !== 0) {
110+
if (initiallySeekedTime !== 0 && initiallySeekedTime !== undefined) {
111111
performInitialSeek(initiallySeekedTime);
112112
}
113113
waitForSeekable();
114114
} else {
115115
playbackObserver.listen(
116116
(obs, stopListening) => {
117+
const initiallySeekedTime =
118+
typeof startTime === "number" ? startTime : startTime();
119+
if (
120+
initiallySeekedTime === undefined &&
121+
obs.readyState < HTMLMediaElement.HAVE_CURRENT_DATA
122+
) {
123+
/**
124+
* On browser, such as Safari, the HTMLMediaElement.duration
125+
* and HTMLMediaElement.buffered may not be initialized at readyState 1, leading
126+
* to cases where it can be equal to `Infinity`.
127+
* If so, the range in which it is possible to seek is not yet known.
128+
* To solve this, the seek should be done after readyState HAVE_CURRENT_DATA (2),
129+
* at that time the previously mentioned attributes are correctly initialized and
130+
* the range in which it is possible to seek is correctly known.
131+
* If the initiallySeekedTime is still `undefined` when the readyState is >= 2,
132+
* let assume that the initiallySeekedTime will never be known and continue
133+
* the logic without seeking.
134+
*/
135+
return;
136+
}
117137
if (obs.readyState >= 1) {
118138
stopListening();
119-
const initiallySeekedTime =
120-
typeof startTime === "number" ? startTime : startTime();
121-
if (initiallySeekedTime !== 0) {
139+
140+
if (initiallySeekedTime !== 0 && initiallySeekedTime !== undefined) {
122141
if (canSeekDirectlyAfterLoadedMetadata) {
123142
performInitialSeek(initiallySeekedTime);
124143
} else {

0 commit comments

Comments
 (0)