- Sports multi-camera views
- Security camera dashboards
- Picture-in-picture (PIP) layouts
- Side-by-side video comparison
Prerequisites
Set up your app to use the W3C Media Player. For more information, see Media Player Setup.Surface and player
Video playback on Vega uses two parts of the W3C Media API, both exported from@amazon-devices/react-native-w3cmedia:
KeplerVideoSurfaceView: A React Native component that creates a rendering surface. It doesn’t manage playback.VideoPlayer: An imperative class that handles initialization, source loading, and playback control. You connect it to the surface with a handle.
Architecture
The player and the rendering surface are independent objects. You create both separately, then connect them with a surface handle after the surface is ready.Add the dependency
Open the package.json file in your app. In thedependencies section, add the following entry.
Configure the manifest
Video playback requires declaring media service dependencies and privileges in your manifest.toml file. Without these, the media pipeline doesn’t initialize, and surfaces render black.Layout examples
Render a single video stream
The following example defines aVideoPane component that renders one video stream.
Side-by-side layout
The following example renders two streams of equal width.Grid layout with four streams
The following example renders four concurrent streams in a two-by-two grid.Picture-in-picture layout
The following example renders a smaller stream over a full-screen stream.Playback controls
The following example adds play, pause, and seek controls to a single pane.Complete side-by-side example
The following example is a complete App.tsx file that renders two video streams side by side.Limitations and best practices
Supported content
- Clear content without digital rights management (DRM): up to four concurrent streams
- DRM content: one stream at a time, because Fire TV devices based on Vega provide a single secure video decoder instance
Performance considerations
- Each stream consumes GPU memory and decode resources
- Vega Virtual Device supports four concurrent non-DRM streams using software decoding
Best practices
- Stagger stream initialization to prevent resource contention.
- Handle errors for each stream separately, so that one failed stream doesn’t crash the entire layout.
- Use a distinct URL for each stream, rather than decoding the same source four times.
- Call
clearSurfaceHandle('')andpause()in theuseEffect()cleanup function to release resources when the component unmounts. - Wait for
onSurfaceViewCreated()before you callsetSurfaceHandle().
Stagger stream initialization
Callinginitialize() on all four players at the same time causes resource contention. The following example adds an initDelay prop to VideoPane and staggers initialization by 500 milliseconds.
Troubleshooting
The following table describes common multi-stream playback problems and their solutions.Test on Vega Virtual Device
You can validate multi-stream playback on Vega Virtual Device. Vega Virtual Device supports four concurrent non-DRM streams withVideoPlayer and KeplerVideoSurfaceView.
Build and run from the command line
-
At the command prompt, build the app.
-
Run the app on the connected Vega Virtual Device. The following example targets a Mac M-series machine. Replace the architecture in the path with the one that matches your development machine, and replace the app ID with the one from your manifest.toml file.
For more information about build output paths and architecture-specific commands, see Run Your App.
Build and run from Vega Studio
- To build and run your app, choose Run in Vega Studio.
Related topics
- Media Player Setup
- General Requirements for Media Apps
- Selecting the Playback Mode
- Media Player API
- VideoPlayer
- KeplerVideoSurfaceView
Proposed sample changes for review
The examples in the body of this page are unchanged from the versions supplied by the media player team. The following items are proposed changes and open questions raised during the documentation review. Each one needs testing and confirmation before it goes into the page.Question 1 — Is clearSurfaceHandle('') correct?
Every cleanup function in this page calls clearSurfaceHandle('') with an empty
string. The API reference for
clearSurfaceHandle
documents the parameter as “The surface handle returned by
onSurfaceViewCreated event emitted by KeplerVideoSurfaceView component” —
not an empty string.
The client-side ad insertion documentation passes the cached handle instead. For
details, see Implement client-side ad insertion with the JS approach.
- Does
clearSurfaceHandle('')release the surface, or is it a no-op? - If it’s a no-op, does the surface leak until the
VideoPlayerinstance is garbage collected? - Could this explain the “Only two streams render, and the others are black” symptom in the Troubleshooting table?
onSurfaceViewCreated so the cleanup function can pass it. The following
example shows the proposed change to VideoPane.
Question 2 — Should the examples call deinitialize()?
The cleanup functions call pause() but never deinitialize().
General requirements for media apps states
that apps must release media resources, and that releasing happens by
deinitializing VideoPlayer. The appstore certification requirements depend on
this.
Questions for the team:
- Should each pane call
await player.deinitialize()on unmount? - Because
deinitialize()returns aPromise<void>and auseEffect()cleanup function can’t await, isdeinitializeSync(timeoutInMs)the correct call in this position? What timeout value do you recommend? - With four concurrent players, does deinitializing them at the same time cause the same resource contention that initialization does?
Question 3 — The playback controls example exposes nothing
In the Playback controls example,play, pause, and
seek are declared but never returned or called. As written, the component
renders a surface and the three controls are unreachable from any parent
component. TypeScript also reports them as unused locals.
The following example is a proposed replacement that exposes the controls with
forwardRef() and useImperativeHandle(). This pattern already appears in
Vega Carousel.
- Is
useImperativeHandle()the pattern you want to recommend, or would you rather the example show on-screen controls with D-pad focus handling? - Is
currentTime += secondsthe correct way to seek, or is there a seek API that handles clamping at the start and end of the stream?
Question 4 — Is KeplerVideoSurfaceView the right component?
The API reference describes
KeplerVideoSurfaceView
and KeplerShareableSurfaceView with identical text: “Use this component only
when operating in pre-buffering mode using the VideoPlayer component, where
more than one player can be pre-buffered simultaneously with different
contents.”
Questions for the team:
- What’s the difference between
KeplerVideoSurfaceViewandKeplerShareableSurfaceView, and which one should multi-stream apps use? - The reference says to use these components “only when operating in pre-buffering mode.” Do the examples in this page operate in pre-buffering mode? If pre-buffering is a precondition, the page needs to say so and show how to enable it.
Question 5 — What are the real concurrent stream limits?
The page states four concurrent streams in three places. Every source found for that number describes Vega Virtual Device with software decoding. Questions for the team:- How many concurrent clear streams do Fire TV Stick devices based on Vega support? Is it four, or is four specific to the virtual device?
- General requirements for media apps states that the first Fire TV based on Vega supports only one secure video decoder instance. The DRM warning at the top of this page is based on that. Is that still accurate for the current hardware?
- Does the four-stream limit vary by resolution? For example, is four streams at 1080p equivalent to one at 4K?
Question 6 — Confirm the staggered initialization delay
The Stagger stream initialization example uses 500 millisecond increments, for a total of 1.5 seconds before the fourth stream starts initializing. Questions for the team:- Is 500 milliseconds a measured value or a placeholder?
- Is there a deterministic alternative, such as awaiting each
initialize()in sequence, that avoids a fixed delay?
Question 7 — Build and run commands need validation
The changes below are a best guess from the surrounding documentation, and the team needs to confirm or correct them against the actual toolchain. The content as supplied told developers to runnpx react-native build-kepler --build-type Release --target aarch64 in the
Troubleshooting table and
vega run-app build/aarch64-release/<your-app-name>_aarch64.vpkg in the
Test on Vega Virtual Device steps.
What was changed, and why:
build-keplerchanged tobuild-vega.build-kepleris the legacy command name. Vega CLI tools documentsnpx react-native build-vegafor React Native Vega apps, and thebuild-kepleroccurrences elsewhere in the 0.24 documentation describe the name rather than instruct it, such as Vega Studio console output and a troubleshooting entry titled “Unrecognized command build-kepler.”--target aarch64was dropped. The flag appears withbuild-veganowhere in the 0.24 documentation. The documented flags are--build-type,--build-version, and--build-number. Build Your App shows a single build producingaarch64-release,x86_64-release, andarmv7-releaseoutput folders, so a target flag doesn’t appear to be needed.- The run command gained an app ID and
-d VirtualDevice. Vega CLI tools gives the signature asvega run-app <Vpkg path> <App ID> -d <device>, and every other 0.24 example supplies both. As supplied, the command omitted both.
- Vega CLI tools says to use
npx react-native build-vegadirectly, and notes thatvega buildexists but isn’t for React Native apps. - Build Your App — the primary build page — gives
npm run build:appin its Vega CLI path and never mentionsbuild-vega. About Vega Headless Tasks and Services showsbuild:appresolving tobuild-vega --build-type Releaseand--build-type Debug, so the npm script is presumably a wrapper. That indirection is never stated on the build page. - Step 1: Dependency Updates says
@amazon-devices/kepler-cli-platformregisters thevega buildcommand, and that removing@react-native-community/climakesnpm run build:releasefail with “Unrecognized command vega build” — which implies the React Native npm scripts invokevega build, contradicting the Vega CLI tools page.
- What is the current, correct way to build a React Native for Vega app from
the command line:
npx react-native build-vega,npm run build:app, orvega build? Arebuild-vegaandvega buildthe same command under two names, or two different commands? - Should this page instruct a raw
build-vegainvocation at all, or should it match Build Your App and usenpm run build:app, which is what a developer following the getting-started path will already have? - Does
build-vegaaccept a--targetflag? If it does, is a target needed here, or does one build produce every architecture? - Confirm the
vega run-appinvocation, including whether the app ID patterncom.amazondeveloper.<your-app-name>.mainmatches the manifest.toml file that ships with the sample. - Does the “App crashes on launch” entry describe a real, reproducible failure?
The original wording attributed
vega buildto native C++ and Rust development. That claim was removed because no native development path is documented for Vega, and Rust appears nowhere else in this documentation set. The entry now says only thatvega buildisn’t supported for React Native and produces an incomplete package. Confirm that this is accurate and that an incomplete package is what causes the crash. Note that this entry’s premise depends on the first question in this list: if the npm scripts invokevega buildunder the hood, then “you usedvega build” can’t be the cause of the crash.
build-kepler. Whichever command is correct, they need a rebuild against it
before this page publishes.
