Skip to content

Commit

Permalink
Merge pull request #4135 from mozilla/lazy-loading-media
Browse files Browse the repository at this point in the history
Lazy load media on scene entry
  • Loading branch information
keianhzo authored Oct 13, 2021
2 parents f0ea4b9 + 2b58d1d commit 8461d01
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 25 deletions.
Binary file modified src/assets/models/LoadingObject_Atom.glb
Binary file not shown.
1 change: 1 addition & 0 deletions src/components/media-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ AFRAME.registerComponent("media-video", {
texture = linkedVideoTexture;
audioSourceEl = linkedAudioSource;
} else {
this.el.emit("video-loading");
({ texture, audioSourceEl } = await this.createVideoTextureAudioSourceEl());
if (getCurrentMirroredMedia() === this.el) {
await refreshMediaMirror();
Expand Down
5 changes: 4 additions & 1 deletion src/hub.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import "./phoenix-adapter";
import nextTick from "./utils/next-tick";
import { addAnimationComponents } from "./utils/animation";
import Cookies from "js-cookie";
import { DialogAdapter, DIALOG_CONNECTION_ERROR_FATAL } from "./naf-dialog-adapter";
import { DialogAdapter, DIALOG_CONNECTION_ERROR_FATAL, DIALOG_CONNECTION_CONNECTED } from "./naf-dialog-adapter";
import "./change-hub";

import "./components/scene-components";
Expand Down Expand Up @@ -762,6 +762,9 @@ document.addEventListener("DOMContentLoaded", async () => {
const entryManager = new SceneEntryManager(hubChannel, authChannel, history);
window.APP.entryManager = entryManager;

APP.dialog.on(DIALOG_CONNECTION_CONNECTED, () => {
scene.emit("didConnectToDialog");
});
APP.dialog.on(DIALOG_CONNECTION_ERROR_FATAL, () => {
// TODO: Change the wording of the connect error to match dialog connection error
// TODO: Tell the user that dialog is broken, but don't completely end the experience
Expand Down
3 changes: 3 additions & 0 deletions src/naf-dialog-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const WEBCAM_SIMULCAST_ENCODINGS = [
// Used for simulcast screen sharing.
const SCREEN_SHARING_SIMULCAST_ENCODINGS = [{ dtx: true, maxBitrate: 1500000 }, { dtx: true, maxBitrate: 6000000 }];

export const DIALOG_CONNECTION_CONNECTED = "dialog-connection-connected";
export const DIALOG_CONNECTION_ERROR_FATAL = "dialog-connection-error-fatal";

export class DialogAdapter extends EventEmitter {
Expand Down Expand Up @@ -440,9 +441,11 @@ export class DialogAdapter extends EventEmitter {
try {
await this._joinRoom();
resolve();
this.emit(DIALOG_CONNECTION_CONNECTED);
} catch (err) {
this.emitRTCEvent("warn", "Adapter", () => `Error during connect: ${error}`);
reject(err);
this.emit(DIALOG_CONNECTION_ERROR_FATAL);
}
});
});
Expand Down
5 changes: 5 additions & 0 deletions src/react-components/preferences-screen.js
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,10 @@ const preferenceLabels = defineMessages({
fastRoomSwitching: {
id: "preferences-screen.preference.fast-room-switching",
defaultMessage: "Enable Fast Room Switching"
},
lazyLoadSceneMedia: {
id: "preferences-screen.preference.lazy-load-scene-media",
defaultMessage: "Enable Scene Media Lazy Loading"
}
});

Expand Down Expand Up @@ -1089,6 +1093,7 @@ class PreferencesScreen extends Component {
{ key: "allowMultipleHubsInstances", prefType: PREFERENCE_LIST_ITEM_TYPE.CHECK_BOX, defaultBool: false },
{ key: "disableIdleDetection", prefType: PREFERENCE_LIST_ITEM_TYPE.CHECK_BOX, defaultBool: false },
{ key: "fastRoomSwitching", prefType: PREFERENCE_LIST_ITEM_TYPE.CHECK_BOX, defaultBool: false },
{ key: "lazyLoadSceneMedia", prefType: PREFERENCE_LIST_ITEM_TYPE.CHECK_BOX, defaultBool: false },
{ key: "preferMobileObjectInfoPanel", prefType: PREFERENCE_LIST_ITEM_TYPE.CHECK_BOX, defaultBool: false },
{ key: "animateWaypointTransitions", prefType: PREFERENCE_LIST_ITEM_TYPE.CHECK_BOX, defaultBool: true },
{ key: "showFPSCounter", prefType: PREFERENCE_LIST_ITEM_TYPE.CHECK_BOX, defaultBool: false },
Expand Down
103 changes: 79 additions & 24 deletions src/react-components/room/useRoomLoadingState.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,38 @@ function reducer(state, action) {
loading: !(state.environmentLoaded && state.networkConnected),
messageKey: state.environmentLoaded ? "enteringRoom" : "loadingObjects"
};
case "environment-loaded":
case "environment-loaded": {
const loaded = state.lazyLoadMedia
? state.networkConnected && state.dialogConnected
: state.allObjectsLoaded && state.networkConnected && state.dialogConnected;
return {
...state,
environmentLoaded: true,
loading: !(state.allObjectsLoaded && state.networkConnected),
messageKey: state.allObjectsLoaded ? "enteringRoom" : "loadingObjects"
loading: !loaded,
messageKey: state.lazyLoadMedia
? loaded
? "enteringRoom"
: "connectingScene"
: loaded
? "enteringRoom"
: "loadingObjects"
};
}
case "network-connected":
return {
...state,
networkConnected: true,
loading: !(state.environmentLoaded && state.allObjectsLoaded)
loading: state.lazyLoadMedia
? !(state.environmentLoaded && state.dialogConnected)
: !(state.environmentLoaded && state.allObjectsLoaded && state.dialogConnected)
};
case "dialog-connected":
return {
...state,
dialogConnected: true,
loading: state.lazyLoadMedia
? !(state.environmentLoaded && state.networkConnected)
: !(state.environmentLoaded && state.allObjectsLoaded && state.networkConnected)
};
}
}
Expand All @@ -42,6 +62,11 @@ const messages = defineMessages({
description: "The loading progress. How many objects have finished loading?",
defaultMessage: "Loading objects {loadedCount}/{objectCount}"
},
connectingScene: {
id: "loading-screen.connecting",
description: "The scene is loaded, we are waiting for the networked scene to be connected to enter.",
defaultMessage: "Connecting to the scene..."
},
enteringRoom: {
id: "loading-screen.entering-room",
description:
Expand All @@ -53,6 +78,7 @@ const messages = defineMessages({
export function useRoomLoadingState(sceneEl) {
// Holds the id of the current
const loadingTimeoutRef = useRef();
const lazyLoadMedia = APP.store.state.preferences.lazyLoadSceneMedia;

const [{ loading, messageKey, objectCount, loadedCount }, dispatch] = useReducer(reducer, {
loading: !sceneEl.is("loaded"),
Expand All @@ -61,7 +87,9 @@ export function useRoomLoadingState(sceneEl) {
loadedCount: 0,
allObjectsLoaded: false,
environmentLoaded: false,
networkConnected: false
networkConnected: false,
dialogConnected: false,
lazyLoadMedia
});

const onObjectLoading = useCallback(
Expand All @@ -83,10 +111,13 @@ export function useRoomLoadingState(sceneEl) {
// TODO: Determine a better way to ensure the object dependency chain has resolved, or switch to a
// progressive loading model where all objects don't have to be loaded to enter the room.
loadingTimeoutRef.current = setTimeout(() => {
dispatch({ type: "all-objects-loaded" });
// Send the objects loaded event if there are no new objects loading after 1.5secs
if (objectCount === loadedCount + 1) {
dispatch({ type: "all-objects-loaded" });
}
}, 1500);
},
[dispatch]
[dispatch, objectCount, loadedCount]
);

const onEnvironmentLoaded = useCallback(
Expand All @@ -103,37 +134,61 @@ export function useRoomLoadingState(sceneEl) {
[dispatch]
);

const onDialogConnected = useCallback(
() => {
dispatch({ type: "dialog-connected" });
},
[dispatch]
);

useEffect(
() => {
// Once the scene has loaded the dependencies to this hook will change,
// the event listeners will be removed, and we can prevent adding them again.
if (loading) {
console.log("Attaching room state event listeners");
sceneEl.addEventListener("model-loading", onObjectLoading);
sceneEl.addEventListener("image-loading", onObjectLoading);
sceneEl.addEventListener("pdf-loading", onObjectLoading);
sceneEl.addEventListener("model-loaded", onObjectLoaded);
sceneEl.addEventListener("image-loaded", onObjectLoaded);
sceneEl.addEventListener("pdf-loaded", onObjectLoaded);
sceneEl.addEventListener("model-error", onObjectLoaded);
if (!lazyLoadMedia) {
sceneEl.addEventListener("model-loading", onObjectLoading);
sceneEl.addEventListener("image-loading", onObjectLoading);
sceneEl.addEventListener("pdf-loading", onObjectLoading);
sceneEl.addEventListener("video-loading", onObjectLoading);
sceneEl.addEventListener("model-loaded", onObjectLoaded);
sceneEl.addEventListener("image-loaded", onObjectLoaded);
sceneEl.addEventListener("pdf-loaded", onObjectLoaded);
sceneEl.addEventListener("video-loaded", onObjectLoaded);
sceneEl.addEventListener("model-error", onObjectLoaded);
}
sceneEl.addEventListener("environment-scene-loaded", onEnvironmentLoaded);
sceneEl.addEventListener("didConnectToNetworkedScene", onNetworkConnected);
sceneEl.addEventListener("didConnectToDialog", onDialogConnected);
}

return () => {
console.log("Removing room state event listeners");
sceneEl.removeEventListener("model-loading", onObjectLoading);
sceneEl.removeEventListener("image-loading", onObjectLoading);
sceneEl.removeEventListener("pdf-loading", onObjectLoading);
sceneEl.removeEventListener("model-loaded", onObjectLoaded);
sceneEl.removeEventListener("image-loaded", onObjectLoaded);
sceneEl.removeEventListener("pdf-loaded", onObjectLoaded);
sceneEl.removeEventListener("model-error", onObjectLoaded);
if (!lazyLoadMedia) {
sceneEl.removeEventListener("model-loading", onObjectLoading);
sceneEl.removeEventListener("image-loading", onObjectLoading);
sceneEl.removeEventListener("pdf-loading", onObjectLoading);
sceneEl.removeEventListener("video-loading", onObjectLoading);
sceneEl.removeEventListener("model-loaded", onObjectLoaded);
sceneEl.removeEventListener("image-loaded", onObjectLoaded);
sceneEl.removeEventListener("pdf-loaded", onObjectLoaded);
sceneEl.removeEventListener("video-loaded", onObjectLoaded);
sceneEl.removeEventListener("model-error", onObjectLoaded);
}
sceneEl.removeEventListener("environment-scene-loaded", onEnvironmentLoaded);
sceneEl.removeEventListener("didConnectToNetworkedScene", onNetworkConnected);
sceneEl.removeEventListener("didConnectToDialog", onDialogConnected);
};
},
[sceneEl, loading, onObjectLoaded, onObjectLoading, onEnvironmentLoaded, onNetworkConnected]
[
sceneEl,
loading,
onObjectLoaded,
onObjectLoading,
onEnvironmentLoaded,
onNetworkConnected,
onDialogConnected,
lazyLoadMedia
]
);

const intl = useIntl();
Expand Down
1 change: 1 addition & 0 deletions src/storage/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export const SCHEMA = {
allowMultipleHubsInstances: { type: "bool" },
disableIdleDetection: { type: "bool" },
fastRoomSwitching: { type: "bool" },
lazyLoadSceneMedia: { type: "bool" },
preferMobileObjectInfoPanel: { type: "bool" },
maxResolutionWidth: { type: "number" },
maxResolutionHeight: { type: "number" },
Expand Down

0 comments on commit 8461d01

Please sign in to comment.