Moving from StreamPixel or Arcware

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.

Who this page is for

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 usesWhat you changeWhat 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.

StreamPixel: change the iframe address

One line of HTML and one origin, with the before and after

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>
  1. Point the iframe at your Eagle 3D Streaming link

    Copy the streaming URL of your application from the Control Panel, as described in Embed with an iframe, and use it as the src.

  2. Add ?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.

  3. Change the origin your code checks and posts to

    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.

What ?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:

?e3dsCompat=streampixel on the iframe address A StreamPixel message the first one your page posts either one turns it on StreamPixel mode on for this page load "loadingComplete" also sent as plain text, once A bare web address from your app, opens a new tab everything else on this page works with or without it
Two ways in, two things it changes. The parameter is the one to rely on, for the reason in the next paragraph.

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.

StreamPixel: messages your page posts

Each command, and what Eagle 3D Streaming does with it

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 postsWhat Eagle 3D Streaming doesSame 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.

StreamPixel: events your page receives

stream-state, stream-metadata, and when each is sent

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.

authenticating connecting finalising loadingComplete disconnected once, at the end queue-1, queue-2 only while waiting stream-metadata sent once "loadingComplete" plain text, StreamPixel mode the top row only moves forward, and never skips a box
The order a progress bar can rely on. The events that can come at other times — the password, the idle warning, reconnecting — are in the table.
Your page receivesSent 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.

StreamPixel: messages from your Unreal application

What the player itself reacts to, and what reaches your page

Your application sends these with Send Pixel Streaming Response, exactly as it did for StreamPixel.

Your application sendsWhat 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: add one script, and start from a token

The before and after, using Arcware's own calls

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>
  1. Load the Eagle 3D Streaming Web SDK, then 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.

  2. Add <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.

  3. Pass a session token instead of the Share ID

    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.

Arcware: calls and handlers

What each Arcware name does on Eagle 3D Streaming
Your code usesOn 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.

What you still change by hand

Everything the translation does not cover, for each platform

Coming from StreamPixel

  • The origin your page checks and posts to, as above.
  • Settings from StreamPixel's dashboard — password, idle timeout, session length, resolution limits — are set again in the Eagle 3D Streaming Control Panel; see Session and access.
  • Built-in chat (comms) has no counterpart. The messages are accepted and ignored.
  • Values on the link for your application (?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.
  • Meeting rooms (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.
  • Screenshots are saved by your page, not by the frame, as above.
  • setResolution is applied as requested. StreamPixel's rule of ignoring sizes above the project's maximum quality has no counterpart.
  • Custom data with a top-level cmd or Type field is read as an Eagle 3D Streaming message, not forwarded to your application. Rename that field.
  • StreamPixel's Web SDK (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.

Coming from Arcware

  • The session start: a token from your server in place of the Share ID, as above.
  • 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.
  • The queue, session id, loading messages, idle warning and file transfer events never fire, as listed above. For loading progress, use the Web SDK's own callbacks in the Web SDK reference.
  • Close codes 4450–4666 are never produced. websocketOnCloseHandler always reports 1000, and reason says why.
  • Session reuse — reconnect() and the ?session, ?noSession and ?reconnect parameters — has no counterpart. A new session needs a new token, because a token is used once.
  • Not provided at all: CoreSetup, getIncomingFile(), fileDownload() and the ?wl branding parameter. Code that calls them must be changed.
  • Resizing: Arcware's page sends your application { 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.

Last updated