Eagle 3D Streaming understands the iframe messages a StreamPixel page sends and the Web SDK calls an Arcware page makes, so a website built for either platform can move to Eagle 3D Streaming without being rewritten. For StreamPixel you change the iframe address; for Arcware you add one script and start the session from a token.
You already have a web page that shows an Unreal application through
StreamPixel's <iframe> or through Arcware's Web SDK
(@arcware-cloud/pixelstreaming-websdk), and you want to stream the
same application from Eagle 3D Streaming instead. Your page keeps its code: the
messages it sends, the events it listens for, and the calls it makes are
translated into Eagle 3D Streaming's own.
| Your page uses | What you change | What keeps working |
|---|---|---|
| StreamPixel's iframe | the iframe src, plus ?e3dsCompat=streampixel on it; the origin your code checks |
the messages you post, the stream-state and stream-metadata events, custom data to your app |
| Arcware's Web SDK | two script tags in place of the npm import, a <div id="playerUI">, and a session token in place of the Share ID |
emitUIInteraction, replies from your app, the ready and closed handlers, mute, disconnect |
Your Unreal application needs no change for the messages your page sends it. They reach it the way they did before, so the Blueprints that read them keep working. A few platform features have no counterpart here; they are listed in What you still change by hand.
Nothing about Eagle 3D Streaming's own messages changes. A page written for Eagle 3D Streaming sees exactly what it saw before; the StreamPixel and Arcware names are added alongside.
Here is a small StreamPixel page, written the way StreamPixel's documentation
shows. It hides a loading screen when the stream is ready, has a mute button, and
sends openDoor to the Unreal application:
<iframe id="streamIframe"
src="https://share.streampixel.io/PROJECT_ID"
allow="autoplay; fullscreen" allowfullscreen></iframe>
<script>
const STREAM_ORIGIN = "https://share.streampixel.io";
const streamIframe = document.getElementById("streamIframe");
window.addEventListener("message", (event) => {
if (event.origin !== STREAM_ORIGIN) return;
if (event.data.type === "stream-state" && event.data.value === "loadingComplete") {
hideMyLoadingScreen();
}
});
function mute() {
streamIframe.contentWindow.postMessage({ message: "muteAudio" }, STREAM_ORIGIN);
}
function openDoor() {
streamIframe.contentWindow.postMessage({ message: "openDoor" }, STREAM_ORIGIN);
}
</script>
The same page on Eagle 3D Streaming. Two lines differ:
<iframe id="streamIframe"
src="https://connector.eagle3dstreaming.com/v5/YOURNAME/YourApp/default?e3dsCompat=streampixel"
allow="autoplay; fullscreen" allowfullscreen></iframe>
<script>
const STREAM_ORIGIN = "https://connector.eagle3dstreaming.com";
const streamIframe = document.getElementById("streamIframe");
window.addEventListener("message", (event) => {
if (event.origin !== STREAM_ORIGIN) return;
if (event.data.type === "stream-state" && event.data.value === "loadingComplete") {
hideMyLoadingScreen();
}
});
function mute() {
streamIframe.contentWindow.postMessage({ message: "muteAudio" }, STREAM_ORIGIN);
}
function openDoor() {
streamIframe.contentWindow.postMessage({ message: "openDoor" }, STREAM_ORIGIN);
}
</script>
Copy the streaming URL of your application from the Control Panel, as
described in Embed with an
iframe, and use it as the src.
?e3dsCompat=streampixel to it
This turns on the two StreamPixel behaviours that could confuse a page
written for Eagle 3D Streaming, explained below. If the link already has a
?, add it as &e3dsCompat=streampixel.
The browser checks this, not Eagle 3D Streaming. A message posted with
"https://share.streampixel.io" as its target origin is never
delivered to a frame on another address, and a listener that only accepts
that origin ignores every reply. Use the origin of your Eagle 3D Streaming
link, which is the scheme and host only, with no path.
?e3dsCompat=streampixel turns on
Almost everything works without it: the messages your page posts, the
stream-state events and the stream-metadata event are
always understood and always sent. The parameter turns on StreamPixel
mode, which adds two behaviours that a page written for Eagle 3D
Streaming would not expect:
Why the parameter, and not just the first message.
StreamPixel's own sample page waits for the plain text
"loadingComplete" before it posts anything. Without the parameter
that page would wait forever: the plain text is only sent in StreamPixel mode,
and nothing has been posted yet to turn the mode on. With the parameter, the
plain text arrives once the stream is ready. If the mode turns on later, after the
stream is already ready, the plain text is sent at that moment.
Why it is not always on. A page written for Eagle 3D Streaming
may run JSON.parse on every message it receives, and a plain
loadingComplete would make it throw. And a bare web address from an
Eagle 3D Streaming app is shown as a short message on screen, not opened.
Post them exactly as StreamPixel documents them, as plain objects. The same command sent as a JSON string works too. Only messages from the page that directly contains the iframe are accepted.
| Your page posts | What Eagle 3D Streaming does | Same as Eagle's |
|---|---|---|
{ message: { type: "setResolution", value: "720p (1280x720)" } } |
Sets the stream to 1280 × 720, read from the numbers in the text. A value with no width × height in it is ignored. | freezeResolutionAt |
{ message: "muteAudio" } * |
Turns the sound off and remembers the level it was at. | setVolume 0 |
{ message: "unMuteAudio" } * |
Puts the sound back to the remembered level, or full volume if there is none. unmuteAudio is accepted too. |
setVolume |
{ message: "terminateSession" } * |
Ends the session. The disconnected event that follows carries code 1000. |
ForceEndSession |
{ message: "heartbeat" } |
Tells the player the viewer is still there, which restarts the idle timer. If the idle warning is already showing, it dismisses it, as a click would. | — |
{ message: { type: "togglehoveringmouse", value: true } } |
true: hovering mouse, cursor visible. false: locked mouse, which takes effect on the viewer's next click on the video. That click is a browser rule, and the player tells the viewer so. |
switchMouseControlSchemeTo |
{ message: "requestScreenshot" } * |
Captures the current frame and sends it to your page as a screenshot event carrying the picture. Your page saves it; see below. |
captureScreenShot |
{ command: "sendUIInteraction", data: { type: "isTouchDevice", value: true } } |
Sends data to your Unreal application. |
sendDataToUE |
Any other object, for example { message: "openDoor" } or { customField: "customValue" } |
Sends the whole object to your Unreal application, unchanged, so Get JSON String Value on message reads openDoor as it did before. |
sendDataToUE |
{ message: { type: "comms", value: { name: "Alice", roomId: "room-123" } } } |
Ignored, and not sent to your application. StreamPixel's built-in chat has no counterpart here. | — |
* These use the Eagle 3D Streaming iframe commands setVolume,
ForceEndSession and captureScreenShot, which are in
the next platform release. The current production player ignores them without
an error. See Controlling the stream.
A screenshot is not downloaded inside the frame. StreamPixel's frame downloads the picture by itself. A browser refuses a download that starts inside a frame from another website without a click in that frame, so on Eagle 3D Streaming the picture is handed to your page and your page saves it. The few lines that do that are in Controlling the stream.
Messages posted before the Eagle 3D page has loaded are lost,
as they are with StreamPixel. Wait for the first stream-state
event, authenticating, before posting anything.
Every event arrives as StreamPixel documents it,
{ type: "stream-state", value: "loadingComplete" } and so on, after
Eagle 3D Streaming's own event for the same moment. The four loading stages
always arrive in order, each exactly once, even when Eagle 3D Streaming skips a
step: an application that is already running has nothing to prepare, so
connecting is sent just before finalising.
| Your page receives | Sent when |
|---|---|
stream-state authenticating |
The Eagle 3D page has loaded and can receive your messages. |
queue-1, queue-2, … |
The session is waiting for a free streaming machine; the number is the place in line, sent each time it changes. Position 1 can appear briefly even when there is no real wait. |
connecting |
Your application is being made ready on the machine. |
finalising |
A streaming machine is assigned and the video connection is being set up. |
loadingComplete |
Your page can now send messages to your application: whichever comes first of the message channel opening and the video playing. |
showPassword |
The link is password protected and the password box is showing, so move any overlay of yours out of its way. |
hidePassword |
The session moved on after the password box, which means the password was accepted. |
afkWarning |
The idle warning appeared. |
afkAbort |
The idle warning was dismissed, by the viewer or by a heartbeat. |
reconnecting * |
The connection was lost and the player is counting down to try again. |
disconnected |
The session ended. Sent once. It carries a code where
StreamPixel's meaning matches: 1000 when your page ended it
with terminateSession, 4004 when the session
reached its time limit, 4006 when your application closed or
crashed. Otherwise there is no code. Where Eagle 3D
Streaming gives an explanation, it is in reason; the
possible texts are in Why a session
ended.
|
stream-metadata |
{ type: "stream-metadata", sessionId, streamerId }, once, when finalising is reached. sessionId identifies this session; streamerId names the streaming machine running it. |
loadingComplete, as plain text |
Once, at the same moment as loadingComplete, in StreamPixel mode only. |
* reconnecting arrives with the next platform release. Codes
4004 and 4006 follow from the same events as Eagle 3D
Streaming's own time-limit and application-closed messages, but have not yet
been reproduced in a test.
Never sent: restricted, and the codes
1001, 1005, 1006,
4000–4003, 4005 and
4007.
Your application sends these with Send Pixel Streaming Response, exactly as it did for StreamPixel.
| Your application sends | What Eagle 3D Streaming does |
|---|---|
{"message": "requestScreenshot"} |
Takes a screenshot, the same as Eagle's own {"cmd":"captureScreenShot"}; see Capture a screenshot. |
{"message": {"type": "togglehoveringmouse", "value": true}} |
Hovering mouse for true, locked mouse for false, as from your page. |
A bare web address, for example https://example.com |
In StreamPixel mode, opens it in a new tab. A browser may block that as a pop-up. Otherwise it is shown as a short message on screen, as before. See Open and redirect URLs for Eagle's own way. |
{"message": {"type": "comms", ...}} |
Ignored. |
| Anything else | Reaches your page as a message event whose event.data is the text exactly as your application sent it. |
Your listener also receives Eagle 3D Streaming's own messages.
They are objects, and when your application sends JSON, a parsed copy of it
follows the text. A StreamPixel page that checks event.data.type
is unaffected. A page that runs JSON.parse(event.data) on
everything should check typeof event.data === "string" first.
Arcware's Web SDK starts a session from a Share ID written into
the page. Eagle 3D Streaming starts one from a session token
that your server requests with your Streaming API key and passes to the page.
That is the one part of your code that has to change. A file called
arcware-compat.js provides Arcware's names on top of the Eagle 3D
Streaming Web SDK, so the rest of your code runs as it is.
A page written the way Arcware's documentation shows:
<div id="video-container"></div>
<button onclick="showCamera()">Camera 1</button>
<script type="module">
import { ArcwareInit } from "@arcware-cloud/pixelstreaming-websdk";
const { PixelStreaming, Application } = ArcwareInit(
{ shareId: "share-YOUR-SHARE-ID" },
{ initialSettings: { AutoConnect: true }, settings: {} }
);
PixelStreaming.videoInitializedHandler.add(() => hideMyLoadingScreen());
Application.getApplicationResponse((response) => console.log("from Unreal", response));
document.getElementById("video-container").appendChild(Application.rootElement);
window.showCamera = () => Application.emitUIInteraction({ camera_view: "cam_01" });
</script>
The same page on Eagle 3D Streaming:
<div id="video-container"><div id="playerUI"></div></div>
<button onclick="showCamera()">Camera 1</button>
<script src="./scripts/dist/e3dsCore.min_ns.js"></script>
<script src="./compat/arcware-compat.js"></script>
<script type="module">
// Your server requests the token with your Streaming API key.
const tokenData = await fetch("/api/stream-token", { method: "POST" })
.then((r) => r.json());
const { PixelStreaming, Application } = ArcwareInit(
{ tokenData, client: "your-username" },
{ initialSettings: { AutoConnect: true }, settings: {} }
);
PixelStreaming.videoInitializedHandler.add(() => hideMyLoadingScreen());
Application.getApplicationResponse((response) => console.log("from Unreal", response));
document.getElementById("video-container").appendChild(Application.rootElement);
window.showCamera = () => Application.emitUIInteraction({ camera_view: "cam_01" });
</script>
arcware-compat.js
In that order. e3dsCore.min_ns.js is the Web SDK itself, from
the Web SDK sample (see Set up the Web SDK);
arcware-compat.js is in the compat folder of the
same repository. Remove the import of
@arcware-cloud/pixelstreaming-websdk: ArcwareInit
is now provided by the script.
<div id="playerUI"></div>
The Eagle 3D Streaming player draws into that element, and
rootElement returns it. Your code can still append
rootElement wherever it did before; that moves it there.
ArcwareInit({ tokenData, client }): tokenData is
the token response your server got from Eagle 3D Streaming, and
client is the username the token was created for. How to get
one is in The token, and e3ds_controller.main(); keep
the request on your server, as
Keeping your API key off the browser
explains. Passing only a shareId stops with an error saying
that a token is needed.
Everything after ArcwareInit is unchanged. Each mapping below has
been checked call by call; a complete live session through this layer has not
yet been tested end to end.
| Your code uses | On Eagle 3D Streaming |
|---|---|
ArcwareInit(ids, configuration) |
Starts the session, with e3ds_controller.main(). ids must be { tokenData, client }. Returns { Config, PixelStreaming, Application }. |
emitUIInteraction(data), on PixelStreaming or Application |
Sends data to your Unreal application with sendDataToUE. A JSON string is parsed first; plain text is sent as text. |
applicationResponseHandler = fn or applicationResponseHandler.add(fn) |
Called with each reply from your application, always as a string, as Arcware does. Both forms work. |
Application.getApplicationResponse(fn), Application.onApplicationResponse(fn) |
The same. |
videoInitializedHandler.add(fn) |
Called when the message channel to your application opens, which is the moment your page can send. Arcware calls it on the first video frame; the two are close but not the same moment. |
websocketOnCloseHandler.add(fn) |
Called once when the session ends, whether Eagle 3D Streaming reports it as ending (the Web SDK's onSessionEnding) or as having reached its time limit (onSessionExpired), with { code: 1000, reason, wasClean: true }. reason is Eagle 3D Streaming's explanation; see Why a session ended. |
errorHandler.add(fn) |
Called with { message, source } when your application crashes or stops unexpectedly (the Web SDK's onStreamerDisconnected). |
onStreamingStateChange(fn) |
true when the message channel opens, false at the same moment as websocketOnCloseHandler. |
setAudioEnabled(enabled), toggleAudio(video, enabled) |
Sound off, or back on at full volume. |
disconnect(), removePlayer() |
End the session. |
rootElement, on PixelStreaming or Application |
The <div id="playerUI"> element. |
postInitSideEffectsHandler.add(fn) |
Called once, straight after ArcwareInit starts the session. |
ArcwarePixelStreaming.clearSessionId() |
Does nothing: there is no remembered session to clear. |
Accepted, but never called. These exist so your code does not
stop with an error, and the first use of each logs one notice in the browser
console: queueHandler, sessionIdHandler,
loveLetterHandler, whiteLabellingChangedHandler,
fileTransferHandler, and the afkWarningActivate,
afkWarningUpdate, afkWarningDeactivate and
afkTimedOut events. Nothing registered with
addEventListener is called.
Accepted, and do nothing apart from a one-time notice:
toggleMic, reconnect, send and
sendAnalyticsEvent.
comms) has no counterpart. The
messages are accepted and ignored.?key=value sent to Unreal as JSON at the start) are not
translated. Eagle 3D Streaming passes values on the link to your application
as command-line parameters instead; see
Command-line parameters.streamerId,
sfuHost, sfuPlayer on the link) are not translated.
Eagle 3D Streaming's equivalent is
meeting links.isTouchDevice is not sent to your application
at the start of a session. Your application asks for the viewer's device
instead; see Detect the viewer's
device.setResolution is applied as requested.
StreamPixel's rule of ignoring sizes above the project's maximum quality has
no counterpart.cmd or
Type field is read as an Eagle 3D Streaming message,
not forwarded to your application. Rename that field.StreamPixelApplication,
from streampixelsdk) is not translated; only the iframe is. Move
such a page to an iframe as above, or to the
Eagle 3D Streaming Web SDK.initialSettings and settings are
kept in Config but not applied. Buttons, mouse mode, muted start
and branding are set in the Eagle 3D Streaming Control Panel or with the Web
SDK's own calls; see
Controlling the stream.4450–4666 are
never produced. websocketOnCloseHandler always reports
1000, and reason says why.reconnect() and the
?session, ?noSession and ?reconnect
parameters — has no counterpart. A new session needs a new token,
because a token is used once.CoreSetup,
getIncomingFile(), fileDownload() and the
?wl branding parameter. Code that calls them must be changed.{ Console: "r.setres …" } when the window changes size.
This layer does not, because Eagle 3D Streaming manages the stream's
resolution itself. Whether an application that resizes only on that message
follows the window has not been tested.Next: Embed with an iframe · Embed with the Web SDK · iframe or Web SDK · Sending and receiving data
Last updated