Streaming API (Native Player)
App Container provides web developers with two distinct ways to display IP camera video streams:
- Native Player (
appcontainer.streamBridge): A Flutter-native video overlay powered bylibmpv/FFmpeg. Engineered specifically for AVS-10 and AVS-15 Touch Panels, offering zero added latency, hardware video decoding, and maximum performance for room control and surveillance dashboards. - WebRTC / WHEP Proxy: Bridges RTSP to WebRTC for viewing in standard web browsers without plugins. (See WebRTC / WHEP Proxy Documentation).
🛠️ How appcontainer.streamBridge Works
The app exposes a JavaScript bridge object at window.appcontainer.streamBridge. Sending JSON messages through postMessage creates, updates, or destroys a native video surface rendered directly over the WebView at the exact location of a target HTML <div> or specific screen coordinates.
🚀 JavaScript API Reference
1. Opening a Video Stream
To open an RTSP stream and map it onto an existing DOM element on your page:
window.appcontainer.streamBridge.postMessage(JSON.stringify({
action: 'open',
id: 'cam1', // Unique stream identifier (string)
url: 'rtsp://192.168.1.10:554/stream',
divId: 'player-div' // ID of the target DOM <div> element
}));
The native player calculates the bounding rectangle of #player-div and aligns the hardware video surface precisely on top of it.
Note: The placeholder
<div>itself remains untouched by the native layer. You can style the<div>with CSS (e.g., loading spinner, placeholder background, or borders) while the stream initializes.
Absolute Screen Coordinates (rect)
Alternatively, specify overlay dimensions in fractional screen coordinates (ranging from 0.0 to 1.0):
window.appcontainer.streamBridge.postMessage(JSON.stringify({
action: 'open',
id: 'cam1',
url: 'rtsp://192.168.1.10:554/stream',
rect: { x: 0, y: 0, w: 1, h: 0.5 } // Top half of the screen
}));
2. Handling Scroll and Window Resize
When page elements shift (due to layout changes, scrolling, window resizing, or DOM mutations), update the native overlay position by posting a resize action:
// Attach a ResizeObserver to the target container <div>
const playerDiv = document.getElementById('player-div');
const observer = new ResizeObserver(() => {
window.appcontainer.streamBridge.postMessage(JSON.stringify({
action: 'resize',
id: 'cam1',
divId: 'player-div'
}));
});
observer.observe(playerDiv);
Using ResizeObserver ensures the video surface automatically tracks element layout changes smoothly.
3. Closing Video Streams
Close individual video overlays or terminate all active streams simultaneously:
// Close a specific stream by ID
window.appcontainer.streamBridge.postMessage(JSON.stringify({
action: 'close',
id: 'cam1'
}));
// Close all active streams
window.appcontainer.streamBridge.postMessage(JSON.stringify({
action: 'closeAll'
}));
4. Mute and Volume Control
Control audio playback volume and muting per stream:
// Mute audio for a stream
window.appcontainer.streamBridge.postMessage(JSON.stringify({
action: 'mute',
id: 'cam1'
}));
// Unmute audio for a stream
window.appcontainer.streamBridge.postMessage(JSON.stringify({
action: 'unmute',
id: 'cam1'
}));
// Set volume level (fractional value from 0.0 to 1.0)
window.appcontainer.streamBridge.postMessage(JSON.stringify({
action: 'volume',
id: 'cam1',
volume: 0.5
}));
Note:
streamBridgeis fire-and-forget (no return value or query endpoint for current volume/mute state). The web project should track what it last set in its own UI state.
5. Multiple Simultaneous Streams
You can render multiple active streams on the same screen by providing a unique id for each instance:
const cameras = [
{ id: 'cam1', url: 'rtsp://192.168.1.10:554/stream1', divId: 'div-cam1' },
{ id: 'cam2', url: 'rtsp://192.168.1.11:554/stream1', divId: 'div-cam2' },
{ id: 'cam3', url: 'rtsp://192.168.1.12:554/stream1', divId: 'div-cam3' },
{ id: 'cam4', url: 'rtsp://192.168.1.13:554/stream1', divId: 'div-cam4' }
];
cameras.forEach(cam => {
window.appcontainer.streamBridge.postMessage(JSON.stringify({
action: 'open',
id: cam.id,
url: cam.url,
divId: cam.divId
}));
});
Note: While there is no software limit on the number of streams, real-time decoding performance depends on device hardware acceleration and stream resolution.
🔄 Navigation & Lifecycle
All active native video overlays are automatically destroyed whenever the WebView navigates to a new page or reloads.
If your web app uses multi-page navigation, re-initialize your streams in a pageshow or DOMContentLoaded event listener:
window.addEventListener('DOMContentLoaded', () => {
if (window.appcontainer?.streamBridge) {
// Re-initialize stream player
}
});