Hi. I’m having a small issue with egress. When I’m streaming live, viewers can see a black border on the right side of the live stream. Do you have any idea how I could fix this issue? Thank you!
![]()
Hi. I’m having a small issue with egress. When I’m streaming live, viewers can see a black border on the right side of the live stream. Do you have any idea how I could fix this issue? Thank you!
![]()
This is almost certainly an aspect ratio or viewport mismatch in your egress configuration.
When you use a custom template (custom_base_url), the egress renderer may launch Chrome at 1279×719 instead of your intended resolution. That 1px gap on the right is exactly what happens in this case the canvas ends up slightly smaller than the output frame, which produces the visible black edge.
This has been reported before and is currently not being actively addressed upstream, so it’s unlikely to be patched on the LiveKit side.
Two reliable fixes:
EncodingOptionsPreset. Instead, define explicit encoding options and set exact width and height values. This bypasses the hardcoded 1279×719 fallback and forces correct rendering dimensions.Can you confirm whether you’re using custom_base_url in your StartRoomCompositeEgress call? That will help identify which path is causing it. RoomComposite & web egress | LiveKit Documentation
@ioan, Most common cause of a black border on one side of egress output is aspect ratio mismatch between source video and egress canvas. Default RoomComposite / Web egress output is 1920×1080 landscape [Egress API | LiveKit Documentation]. If your source is portrait (mobile capture, 9:16) or any aspect narrower than 16:9, the canvas gets filled with the source plus black bars to make up the difference.
If your source is portrait, switch to a portrait preset on the egress request:
from livekit import api
req = api.RoomCompositeEgressRequest(
room_name="my-room",
layout="speaker",
preset=api.EncodingOptionsPreset.PORTRAIT_H264_720P_30,
segment_outputs=[...],
)
res = await lkapi.egress.start_room_composite_egress(req)
That uses 720×1280 instead of the default 1920×1080 [ Egress examples | LiveKit Documentation ]. Other portrait variants exist at 1080P and 60 fps in the same enum [ Egress API | LiveKit Documentation ].
Two quick checks before changing config:
If you need fully custom canvas sizing (non-preset), EncodingOptions(width=..., height=..., framerate=..., video_bitrate=...) on the request gives explicit control. Fit/fill behavior beyond the canvas crop landed via livekit/egress#74 (closed 2023).
Hello, guys.
Thank you for your responses. I really appreciate it! I’ve tried all the suggested approaches, but I still can’t figure out what I’m doing wrong.
This is my version of the code:
const isMobile = ls.roomLayout.previewType === LSMultihostPreviewType.Mobile;
encodingOptions = new EncodingOptions({
width: isMobile ? (isMulticast ? 720 : 1080) : isMulticast ? 1280 : 1920,
height: isMobile ? (isMulticast ? 1280 : 1920) : isMulticast ? 720 : 1080,
framerate: 30,
videoBitrate: isMulticast ? 2500 : 4000, // 2.5 Mbps at 720p, 4 Mbps at 1080p
keyFrameInterval: 2,
videoCodec: VideoCodec.H264_MAIN,
audioCodec: AudioCodec.AAC,
audioBitrate: 64,
audioFrequency: 44100,
});
const info = await this.egressClient.startRoomCompositeEgress(
roomName, {
stream: streamOutput
}, {
layout: 'speaker', // "grid", "single-participant"
customBaseUrl: layoutUrl,
// encodingOptions: EncodingOptionsPreset.PORTRAIT_H264_1080P_30,
encodingOptions,
},
);
To me, it does not look like an aspect ratio mismatch.
Fastest way to confirm it’s the template and not egress: open your template URL in a normal Chrome window sized to exactly 720×1280, or run:
lk egress test-template --base-url <your-template-url> --room <room> --layout speaker --publishers 2
You’ll see the same right-side strip and can fix it live in devtools before re-running egress.
Two quick checks that tell what maybe the iisue:
customBaseUrl template to the built-in speaker layout (leave customBaseUrl unset)? If yes, it’s purely your template CSS.object-fit)?Make the root fill the compositor and clip everything else:
html, body, #root {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: #000;
}
/* your video stage */
.stage {
position: fixed;
inset: 0; /* fills the full 720x1280 */
width: 100vw;
height: 100vh;
}
.stage video {
width: 100%;
height: 100%;
object-fit: cover; /* use 'contain' if you'd rather show the whole source with intentional bars */
}
Some helpful docs: