Skip to main content

Overview

Meeting video comes in three kinds, each with its own entry point: Local video doesn’t need a subscription; just render the track directly. Remote video must be subscribed first before it has any data.

Local video

SwiftUI

Don’t pass view when turning on the camera; afterward, pass the track directly to SRTCVideoView:
Screen sharing works the same way, using meeting.screenTrack:
SMeetingEngine is not an ObservableObject, so cameraTrack / screenTrack changing from nil to a value doesn’t trigger a SwiftUI refresh by itself. Keep a “camera is on” flag in your own @Published state and let it drive view updates.

UIKit / AppKit

Pass the preview view when turning on the camera:
The view you pass must be an object that is actually attached to the view hierarchy. Passing a temporarily created local variable that hasn’t been added to any superview leaves the rendering pipeline running with nothing to show.

Remote video (SwiftUI)

SMeetingRemoteVideoView is the recommended entry point for SwiftUI. It handles the entire subscription lifecycle: it subscribes by uid + trackDesc when the view appears and unsubscribes when the view disappears.
To view someone’s screen sharing, change trackDesc to .screen:
What the component guarantees:
  • Rendering the same (uid, trackDesc) in multiple places doesn’t make them knock out each other’s subscriptions
  • When a layout change recreates the view (the old instance disappears and a new one appears), no extra unsubscribe + resubscribe happens, so the video doesn’t flicker
  • The subscription result and the arrival of the underlying media don’t happen at the same moment; the component binds automatically once the data actually arrives, so you don’t need delays or retries
A typical usage is rendering a grid from the member list:

Remote video (UIKit / AppKit)

If you already hold an SRTCVideoRenderer attached to the view hierarchy, use this pair of convenience methods:
stopPlayRemoteVideo removes only the one renderer view you pass in; it actually unsubscribes only when the track no longer has any renderer view. So when the same video is shown in several windows, closing one of them doesn’t affect the others.

Control subscriptions yourself

When you need to separate subscription timing from rendering timing (for example, pre-subscribe and then decide the layout), use the core APIs:
Note that unsubscribeRemoteVideoTrack unsubscribes unconditionally, regardless of whether any renderer view is still using it. When rendering the same video in multiple places, use SMeetingRemoteVideoView or stopPlayRemoteVideo. To just check whether a given track of a given member exists (without subscribing), use:

When to subscribe

Remote video is subscribed on demand; the SDK doesn’t subscribe to everyone automatically for you. Base the decision on member state and events:
  • MeetingUserInfo.cameraState == .on → this member has camera video
  • MeetingUserInfo.shareState == ShareType.screen.rawValue → this member is sharing their screen
  • Refresh the layout when you receive userCameraStateDidChange or roomShareDidStart / roomShareDidStop
With SMeetingRemoteVideoView, you only need to make the views appear / disappear along with these states, and the subscriptions follow automatically.

Server-side composite video (MCU)

When the meeting has a server-side composite task running, you can pull a single composite video instead of subscribing to members one by one:
The composite video requires the server to set up the composite task first; the layout is controlled by adminUpdateLayout(_:). See Recording and composite layout.