Track publications¶
A TrackPublication is one participant's copy of a track. When you publish a track to a participant, you get
a LocalTrackPublication for that participant, and the participant gets a RemoteTrackPublication for it on
their side. The publication carries the track (TrackPublication.track), what it is (TrackPublication.kind,
TrackPublication.source, TrackPublication.name), whether that copy is muted (TrackPublication.isMuted),
and the codec and size in use (TrackPublication.mimeType, TrackPublication.dimensions).
How to publish is on Publishing tracks; the track itself is on Tracks.
One publication per recipient¶
A track says whether its data is flowing; a publication says whether the data goes to that participant. You
publish a track to one participant at a time, so one track has as many publications as participants you
send it to: your camera sent to three participants is one LocalVideoTrack and three LocalTrackPublication
values. LocalParticipant.localVideoTracks and LocalParticipant.localAudioTracks list them, and
LocalParticipant.videoPublication(source:to:) and LocalParticipant.audioPublication(to:) find the one
that goes to a given participant.
import RevueSDK
/// Each participant you send a track to has a publication of its own, so whether your camera reaches one
/// participant is a question about their publication, not about the track.
@MainActor
func isCameraSent(to friendId: String) -> Bool {
Room.shared.localParticipant.videoPublication(source: .camera, to: friendId) != nil
}
LocalTrackPublication.mute() and LocalTrackPublication.unmute() act on that copy: muting a publication
affects that participant only, who gets the placeholder or silence while everyone else keeps receiving the
track, and the camera and microphone keep running. The track's own Track.isMuted does not change. How the
two levels combine is on the
LocalParticipant page.
The publications you receive are on the RemoteParticipant, in Participant.trackPublications,
Participant.videoTracks and Participant.audioTracks. A remote participant has at most one video and one
audio publication at a time on your side, because they send you one video and one audio track at a time.
TrackPublication.sid identifies the publication on this device only. The two sides of a call do not share
it, like Track.sid, so do not send it to the other side.
When a remote publication appears¶
When a participant publishes a track to you, they first send you a declaration of it and only start sending
the data once you have received it. The SDK creates the RemoteTrackPublication and its track together as
soon as their declaration reaches you, and calls didSubscribeTrack on your RoomDelegate and the
participant's ParticipantDelegate right then, not when the first data arrives. In didSubscribeTrack the
publication's track is already set, so you can hand it to a VideoView or read its name at once; the
first frames follow.
import RevueSDK
/// Follow the video a participant sends you. didSubscribeTrack fires as soon as their declaration
/// arrives, before any frame, and the publication's track is already set, so you can hand it to a view
/// at once. didUnsubscribeTrack fires when they stop sending or replace the track.
final class RemoteVideoObserver: RoomDelegate {
var onVideoTrack: ((RemoteParticipant, RemoteVideoTrack?) -> Void)?
init() {
Room.shared.delegates.add(delegate: self)
}
func room(_ room: Room, participant: RemoteParticipant, didSubscribeTrack publication: RemoteTrackPublication) {
guard let video = publication.track as? RemoteVideoTrack else { return }
onVideoTrack?(participant, video)
}
func room(_ room: Room, participant: RemoteParticipant, didUnsubscribeTrack publication: RemoteTrackPublication) {
guard publication.kind == .video else { return }
onVideoTrack?(participant, nil)
}
}
When the participant unpublishes the track, the publication is removed and you get didUnsubscribeTrack.
When they replace the track with another one of the same kind, say they unpublish their screen share and
publish their camera, you get didUnsubscribeTrack for the old publication and didSubscribeTrack for the
new one. The data you receive is only ever fed into a track that already exists: nothing is created from the
data itself.
isSubscribed¶
TrackPublication.isSubscribed is true whenever the publication has a track. There is no server to allow
or refuse a subscription: a local publication has its track from the moment you publish, and a remote
publication has its track from the moment the declaration arrives, so isSubscribed is true for every
publication you can reach. Use it the way you would in code written for a server, and it holds.
mimeType¶
TrackPublication.mimeType names the codec in use, as video/VP8, video/H264 or audio/opus. On a
local publication it is filled when the publish succeeds and again after setCodec(_:). On a remote
publication it is nil until the first frame arrives, because the codec is read from the data, and it is
updated when the sender changes codec.
| Situation | mimeType |
|---|---|
| You published video with VP8 | video/VP8 |
You switched that publication to H.264 with setCodec(_:) |
video/H264 |
| A remote video publication, before the first frame | nil |
| The first frame arrived, encoded with VP8 | video/VP8 |
| The sender switched to H.264 | video/H264 |
| Any audio publication | audio/opus |
import RevueSDK
/// Name the codec a participant's video reaches you in: "video/VP8" or "video/H264". mimeType is nil
/// until their first frame arrives, so treat nil as "not known yet", not as an error. Audio is always
/// "audio/opus".
func incomingVideoCodec(from friendId: String) -> String {
let publication = Room.shared.remoteParticipants[friendId]?.videoTracks.first
return publication?.mimeType ?? "not known yet"
}
Stop handling what a participant sends¶
RemoteTrackPublication.set(enabled:) with false stops your side from handling that publication: the video
is no longer decoded and the audio is no longer played or passed to anything that listens to the track, so a
recording of that track breaks off. The data is still being sent and still arrives; only your handling of it
stops. RemoteTrackPublication.isEnabled reports the current setting, and set(enabled: true) resumes.
import RevueSDK
/// Stop handling the video one participant sends you, for example while their tile is off screen.
/// Their data keeps arriving; your side only stops decoding and delivering it. Pass false to resume.
@MainActor
func stopHandlingVideo(from friendId: String, stop: Bool) {
guard let participant = Room.shared.remoteParticipants[friendId],
let publication = participant.videoTracks.first as? RemoteTrackPublication else { return }
publication.set(enabled: !stop)
}
This is not a mute. The publication's isMuted does not change, the sender is not told, and nothing happens
on their side. To hear a participant at a lower level, or not at all, while their audio keeps flowing to
recordings, set RemoteAudioTrack.volume instead (see Tracks).
To stop a participant from sending to you, there is no call on your side: they decide what they publish.
Dimensions¶
TrackPublication.dimensions is the width and height of a video publication, and nil on an audio publication.
On a remote publication it follows Track.dimensions of the track you receive, so it is the size the sender
is sending you. On a local publication it is meant to be the size you send to that participant, which can
differ from one recipient to the next; it is not filled yet:
尚未支援
本地視訊軌道的 dimensions 是 nil,目前只有遠端視訊軌道有值。