Basic layouts
In this guide, you’ll learn how layouts in Smelter works and how to position and resize components. It includes examples and explanation how component size is calculated.
-
Init Smelter and connect inputs and outputs
import { View } from "@swmansion/smelter"import Smelter from "@swmansion/smelter-node"function App() {return (<View style={{ backgroundColor: "#4d4d4d" }} />)}async function start() {const smelter = new Smelter();await smelter.init();await smelter.registerInput("input_1", {2 collapsed linestype: "mp4",serverPath: "input1.mp4"});await smelter.registerInput("input_2", {2 collapsed linestype: "mp4",serverPath: "input2.mp4"})await smelter.registerOutput("output", <App />, {11 collapsed linestype: "rtp_stream",transportProtocol: "udp",port: 9004,ip: "127.0.0.1",video: {resolution: { width: 1280, height: 720 },encoder: { type: "ffmpeg_h264" },},audio: {encoder: { type: "opus", channels: "stereo" },}});await smelter.start();}Starts the Smelter server with 2 input streams (
input_1
andinput_2
) and output stream that generates a blank output. -
Add
input_1
to the scene.import { View, InputStream } from "@swmansion/smelter"import Smelter from "@swmansion/smelter-node"function App() {return (<View style={{ backgroundColor: "#4d4d4d" }}><InputStream inputId="input_1" /></View>)}30 collapsed linesasync function start() {const smelter = new Smelter();await smelter.init();await smelter.registerInput("input_1", {type: "mp4",serverPath: "input1.mp4"});await smelter.registerInput("input_2", {type: "mp4",serverPath: "input2.mp4"});await smelter.registerOutput("output", <App />, {type: "rtp_stream",transportProtocol: "udp",port: 9004,ip: "127.0.0.1",video: {resolution: { width: 1280, height: 720 },encoder: { type: "ffmpeg_h264" },},audio: {encoder: { type: "opus", channels: "stereo" },}});await smelter.start();}The input stream in the example has a resolution
1920x1080
, but it is rendered on the1270x720
output. as a result only part of the stream is visible. -
Resize
input_1
to fill the output.import { View, Rescaler, InputStream } from "@swmansion/smelter"import Smelter from "@swmansion/smelter-node"function App() {return (<View style={{ backgroundColor: "#4d4d4d" }}><Rescaler><InputStream inputId="input_1" /></Rescaler></View>)}30 collapsed linesasync function start() {const smelter = new smelter();await smelter.init();await smelter.registerInput("input_1", {type: "mp4",serverPath: "input1.mp4"});await smelter.registerInput("input_2", {type: "mp4",serverPath: "input2.mp4"})await smelter.registerOutput("output", <App />, {type: "rtp_stream",transportProtocol: "udp",port: 9004,ip: "127.0.0.1",video: {resolution: { width: 1280, height: 720 },encoder: { type: "ffmpeg_h264" },},audio: {encoder: { type: "opus", channels: "stereo" },}});await smelter.start();}Input stream now fully fits inside the output.
Explanation:
- Root
View
component in the example takes it size from the output itself (1280x720
). Rescaler
is the only child without width/height specified, so it takes its size from theView
component.InputStream
is resized byRescaler
to fit inside it.
- Root
-
Show both inputs side-by-side.
import { View, Rescaler, InputStream } from "@swmansion/smelter"import Smelter from "@swmansion/smelter-node"function App() {return (<View style={{ backgroundColor: "#4d4d4d" }}><Rescaler><InputStream inputId="input_1" /></Rescaler><Rescaler><InputStream inputId="input_2" /></Rescaler></View>)}30 collapsed linesasync function start() {const smelter = new smelter();await smelter.init();await smelter.registerInput("input_1", {type: "mp4",serverPath: "input1.mp4"});await smelter.registerInput("input_2", {type: "mp4",serverPath: "input2.mp4"})await smelter.registerOutput("output", <App />, {type: "rtp_stream",transportProtocol: "udp",port: 9004,ip: "127.0.0.1",video: {resolution: { width: 1280, height: 720 },encoder: { type: "ffmpeg_h264" },},audio: {encoder: { type: "opus", channels: "stereo" },}});await smelter.start();}Both input streams are now visible.
Explanation:
- Root
View
component in the example takes it size from the output itself (1280x720
). - Root
View
has 2 child components, each without any dimensions specified, so both child components (Rescaler
) will have size640x720
. InputStream
is resized byRescaler
to fit inside it. Aspect ratio does not match, so it is centered vertically.
- Root
-
Put one of the inputs in the corner
import { View, Rescaler, InputStream } from "@swmansion/smelter"import Smelter from "@swmansion/smelter-node"function App() {return (<View style={{ backgroundColor: "#4d4d4d" }}><Rescaler><InputStream inputId="input_1" /></Rescaler><Rescaler style={{ width: 320, height: 180, top: 20, right: 20 }}><InputStream inputId="input_2" /></Rescaler></View>)}30 collapsed linesasync function start() {const smelter = new Smelter();await smelter.init();await smelter.registerInput("input_1", {type: "mp4",serverPath: "input1.mp4"});await smelter.registerInput("input_2", {type: "mp4",serverPath: "input2.mp4"})await smelter.registerOutput("output", <App />, {type: "rtp_stream",transportProtocol: "udp",port: 9004,ip: "127.0.0.1",video: {resolution: { width: 1280, height: 720 },encoder: { type: "ffmpeg_h264" },},audio: {encoder: { type: "opus", channels: "stereo" },}});await smelter.start();}Both input streams are now visible.
Explanation:
- Root
View
component in the example takes it size from the output itself (1280x720
) - Root
View
has 2 child components, but only one is positioned statically. (See absolute positioning)- First
Rescaler
is the only static child of the View, so it has the same size as its parent - Second
Rescaler
has fieldstop
andright
defined so it is positioned absolutely. It is rendered on top of static content of the View and it does not affect layout of the other sibling components.
- First
InputStream
components are resized byRescaler
components to fit inside them.
- Root