Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion lib/src/core/engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1706,7 +1706,25 @@ extension EngineInternalMethods on Engine {
kind: rtc.RTCRtpMediaType.RTCRtpMediaTypeVideo,
init: transceiverInit,
);
await setPreferredCodec(transceiver, track.kind.toString().toLowerCase(), videoCodec);
try {
// The transceiver is always video (the kind above), and the plugin's
// capability lookup wants the plain media kind. `track.kind.toString()`
// yields 'tracktype.video', which the native desktop plugins refuse
// ("kind is null or empty") and the darwin plugin reads as audio.
await setPreferredCodec(transceiver, 'video', videoCodec);
} catch (_) {
// The transceiver is already on the peer connection and the server has
// not been told about it. Left behind, the next offer carries a media
// section the server has no signalled track for, and the server binds
// it to whichever video track is pending, so this track's frames can
// surface under another publication. Take it back out before failing.
try {
await publisher!.pc.removeTrack(transceiver.sender);
} catch (e) {
logger.warning('failed to remove the backup codec sender: $e');
}
rethrow;
}
return transceiver.sender;
}

Expand Down
94 changes: 58 additions & 36 deletions lib/src/participant/local.dart
Original file line number Diff line number Diff line change
Expand Up @@ -994,47 +994,69 @@ class LocalParticipant extends Participant<LocalTrackPublication> {

final layers = Utils.computeVideoLayers(dimensions, encodings, isSVCCodec(backupCodec));

simulcastTrack.sender = await room.engine.createSimulcastTransceiverSender(
track,
simulcastTrack,
encodings,
publication,
backupCodec,
);
late final lk_models.TrackInfo trackInfo;
try {
simulcastTrack.sender = await room.engine.createSimulcastTransceiverSender(
track,
simulcastTrack,
encodings,
publication,
backupCodec,
);

// the backup codec publishes over its own sender, so it needs the same
// degradation preference the primary sender resolved to.
await track.applyDegradationPreference(simulcastTrack.sender);
// the backup codec publishes over its own sender, so it needs the same
// degradation preference the primary sender resolved to.
await track.applyDegradationPreference(simulcastTrack.sender);

final cid = simulcastTrack.sender!.senderId;
final cid = simulcastTrack.sender!.senderId;

final req = lk_rtc.AddTrackRequest(
cid: cid,
name:
options.name ??
(track.source == TrackSource.screenShareVideo
? VideoPublishOptions.defaultScreenShareName
: VideoPublishOptions.defaultCameraName),
type: track.kind.toPBType(),
source: track.source.toPBType(),
muted: track.muted,
layers: layers,
sid: publication.sid,
simulcastCodecs: <lk_rtc.SimulcastCodec>[
lk_rtc.SimulcastCodec(
codec: backupCodec.toLowerCase(),
cid: cid,
),
],
);
final req = lk_rtc.AddTrackRequest(
cid: cid,
name:
options.name ??
(track.source == TrackSource.screenShareVideo
? VideoPublishOptions.defaultScreenShareName
: VideoPublishOptions.defaultCameraName),
type: track.kind.toPBType(),
source: track.source.toPBType(),
muted: track.muted,
layers: layers,
sid: publication.sid,
simulcastCodecs: <lk_rtc.SimulcastCodec>[
lk_rtc.SimulcastCodec(
codec: backupCodec.toLowerCase(),
cid: cid,
),
],
);

// video specific
if (dimensions.width > 0 && dimensions.height > 0) {
req.width = dimensions.width;
req.height = dimensions.height;
}
// video specific
if (dimensions.width > 0 && dimensions.height > 0) {
req.width = dimensions.width;
req.height = dimensions.height;
}

final trackInfo = await room.engine.addTrack(req);
trackInfo = await room.engine.addTrack(req);
} catch (_) {
// The server never learned about this sender. Drop the local record and
// the sender itself, so the peer connection carries no transceiver the
// server cannot match to a signalled track (see
// Engine.createSimulcastTransceiverSender for what happens if it does).
// The map is keyed by codec alone, so only this attempt's own entry goes:
// a republish that raced this failure owns the key by now.
if (identical(track.simulcastCodecs[backupCodec], simulcastTrack)) {
track.simulcastCodecs.remove(backupCodec);
}
final sender = simulcastTrack.sender;
if (sender != null) {
try {
await room.engine.publisher?.pc.removeTrack(sender);
} catch (e) {
logger.warning('failed to remove the backup codec sender: $e');
}
}
rethrow;
}

await room.engine.negotiate();

Expand Down
Loading