mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Implement media element state syncing for media sender
This commit is contained in:
@@ -75,7 +75,8 @@ export default class Media {
|
||||
}
|
||||
get #lastUpdateTime() {
|
||||
const lastUpdateTime = MediaLastUpdateTimes.get(this);
|
||||
if (!lastUpdateTime) throw logger.error("Missing last update time!");
|
||||
if (lastUpdateTime === undefined)
|
||||
throw logger.error("Missing last update time!");
|
||||
return lastUpdateTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Capability, ReceiverAvailability } from "../sdk/enums";
|
||||
import type Session from "../sdk/Session";
|
||||
|
||||
import cast, { ensureInit, CastPort } from "../export";
|
||||
import { Media, PlayerState } from "../sdk/media";
|
||||
|
||||
const logger = new Logger("fx_cast [media sender]");
|
||||
|
||||
@@ -31,6 +32,7 @@ export default class MediaSender {
|
||||
|
||||
// Cast API objects
|
||||
private session?: Session;
|
||||
private media?: Media;
|
||||
|
||||
constructor(opts: MediaSenderOpts) {
|
||||
this.mediaUrl = opts.mediaUrl;
|
||||
@@ -52,6 +54,16 @@ export default class MediaSender {
|
||||
logger.error("Failed to initialize cast API", err);
|
||||
}
|
||||
|
||||
window.addEventListener("beforeunload", async () => {
|
||||
if (await options.get("mediaStopOnUnload")) {
|
||||
this.port?.postMessage({
|
||||
subject: "bridge:stopMediaServer"
|
||||
});
|
||||
|
||||
this.session?.stop();
|
||||
}
|
||||
});
|
||||
|
||||
this.isLocalMedia = this.mediaUrl.startsWith("file://");
|
||||
this.isLocalMediaEnabled = await options.get("localMediaEnabled");
|
||||
|
||||
@@ -124,12 +136,14 @@ export default class MediaSender {
|
||||
const mediaInfo = new cast.media.MediaInfo(mediaUrl.href, "");
|
||||
mediaInfo.metadata = new cast.media.GenericMediaMetadata();
|
||||
mediaInfo.metadata.title = mediaTitle;
|
||||
mediaInfo.tracks = [];
|
||||
|
||||
const activeTrackIds: number[] = [];
|
||||
|
||||
mediaInfo.tracks = subtitleUrls.map((url, index) => {
|
||||
let trackIndex = 0;
|
||||
for (const url of subtitleUrls) {
|
||||
const track = new cast.media.Track(
|
||||
index,
|
||||
trackIndex++,
|
||||
cast.media.TrackType.TEXT
|
||||
);
|
||||
track.name = url.pathname;
|
||||
@@ -137,8 +151,8 @@ export default class MediaSender {
|
||||
track.trackContentType = "text/vtt";
|
||||
track.subtype = cast.media.TextTrackType.SUBTITLES;
|
||||
|
||||
return track;
|
||||
});
|
||||
mediaInfo.tracks.push(track);
|
||||
}
|
||||
|
||||
if (this.mediaElement instanceof HTMLMediaElement) {
|
||||
if (this.mediaElement instanceof HTMLVideoElement) {
|
||||
@@ -218,7 +232,68 @@ export default class MediaSender {
|
||||
loadRequest.autoplay = true;
|
||||
loadRequest.activeTrackIds = activeTrackIds;
|
||||
|
||||
this.session?.loadMedia(loadRequest);
|
||||
this.session?.loadMedia(loadRequest, async media => {
|
||||
this.media = media;
|
||||
|
||||
if (
|
||||
(await options.get("mediaSyncElement")) &&
|
||||
this.mediaElement instanceof HTMLMediaElement
|
||||
) {
|
||||
this.addMediaElementListeners(this.mediaElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private addMediaElementListeners(mediaElement: HTMLMediaElement) {
|
||||
this.session?.addUpdateListener(isAlive => {
|
||||
if (!isAlive) return;
|
||||
|
||||
// Update volume level
|
||||
const volume = this.session?.receiver.volume;
|
||||
if (!volume) return;
|
||||
|
||||
if (
|
||||
volume?.level !== null &&
|
||||
volume.level !== mediaElement.volume
|
||||
) {
|
||||
mediaElement.volume = volume.level;
|
||||
}
|
||||
// Update muted state
|
||||
if (volume?.muted !== null && volume.muted !== mediaElement.muted) {
|
||||
mediaElement.muted = volume.muted;
|
||||
}
|
||||
});
|
||||
|
||||
this.media?.addUpdateListener(isAlive => {
|
||||
if (!isAlive || !this.media) return;
|
||||
|
||||
/**
|
||||
* If media element time and estimated time are off by more
|
||||
* than two seconds, set the media element time to the
|
||||
* estimated time.
|
||||
*/
|
||||
const estimatedTime = this.media.getEstimatedTime();
|
||||
if (Math.abs(mediaElement.currentTime - estimatedTime) > 2) {
|
||||
mediaElement.currentTime = estimatedTime;
|
||||
}
|
||||
|
||||
const mediaElementPlayerState = mediaElement.paused
|
||||
? PlayerState.PAUSED
|
||||
: PlayerState.PLAYING;
|
||||
|
||||
if (mediaElementPlayerState !== this.media.playerState) {
|
||||
switch (this.media.playerState) {
|
||||
case PlayerState.PLAYING:
|
||||
mediaElement.play();
|
||||
break;
|
||||
case PlayerState.PAUSED:
|
||||
case PlayerState.BUFFERING:
|
||||
case PlayerState.IDLE:
|
||||
mediaElement.pause();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private startMediaServer(
|
||||
|
||||
Reference in New Issue
Block a user