Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ private void FixVideoOrientation()
// For local user, we don't have a video track, so we get the video rotation angle directly from WebCamTexture
if (Participant != null && Participant.IsLocalParticipant && _video.texture is WebCamTexture sourceWebCamTexture)
{
// WebCamTexture reports width=16 until fully initialized; reading videoRotationAngle before that logs a warning every frame
if (!sourceWebCamTexture.isPlaying || sourceWebCamTexture.width <= 16)
{
return;
}

_videoRectTransform.rotation = _baseVideoRotation * Quaternion.AngleAxis(-sourceWebCamTexture.videoRotationAngle, Vector3.forward);
}
}
Expand Down
15 changes: 14 additions & 1 deletion Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2302,7 +2302,8 @@ private static bool TryParseSubscriberStreamId(string streamId, out string track
trackTypeKey = null;

var idParts = streamId.Split(':');
if (idParts.Length != 2 || string.IsNullOrEmpty(idParts[0]) || string.IsNullOrEmpty(idParts[1]))
// SFU stream IDs are `{trackLookupPrefix}:{trackTypeKey}` with an optional track suffix, e.g. `:tR`.
if (idParts.Length < 2 || string.IsNullOrEmpty(idParts[0]) || string.IsNullOrEmpty(idParts[1]))
{
return false;
}
Expand Down Expand Up @@ -2638,12 +2639,24 @@ private async Task ClosePreviousSfuWebSocketAsync(ISfuWebSocket previousSfuWebSo

private void OnPublisherAudioTrackChanged(AudioStreamTrack audioTrack)
{
if (audioTrack != null && _publisherAudioTrackIsEnabled)
{
UpdateMuteStateAsync(TrackType.Audio, true).LogIfFailed();
}

UpdateAudioRecording();
PublisherAudioTrackChanged?.Invoke();
}

private void OnPublisherVideoTrackChanged(VideoStreamTrack videoTrack)
{
// InternalExecuteSetPublisherVideoTrackEnabled runs before the track exists on first enable,
// so UpdateMuteStates must be sent once the publisher track is actually created.
if (videoTrack != null && _publisherVideoTrackIsEnabled)
{
UpdateMuteStateAsync(TrackType.Video, true).LogIfFailed();
}

PublisherVideoTrackChanged?.Invoke();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,13 @@ private void UploadLocalParticipantPublishedVideoRotationAngle()
return;
}

var angle = Client.InternalLowLevelClient.RtcSession.VideoInput.videoRotationAngle;
var videoInput = Client.InternalLowLevelClient.RtcSession.VideoInput;
if (!videoInput.isPlaying || videoInput.width <= 16)
{
return;
}

var angle = videoInput.videoRotationAngle;
var hasPrevAngle = CustomData.TryGet<int>(VideoRotationAngleKey, out var prevAngle);

if (!hasPrevAngle || Mathf.Abs(angle - prevAngle) > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ private void FixVideoOrientation()
// For local user, we don't have a video track, so we get the video rotation angle directly from WebCamTexture
if (Participant != null && Participant.IsLocalParticipant && _video.texture is WebCamTexture sourceWebCamTexture)
{
// WebCamTexture reports width=16 until fully initialized; reading videoRotationAngle before that logs a warning every frame
if (!sourceWebCamTexture.isPlaying || sourceWebCamTexture.width <= 16)
{
return;
}

_videoRectTransform.rotation = _baseVideoRotation * Quaternion.AngleAxis(-sourceWebCamTexture.videoRotationAngle, Vector3.forward);
}
}
Expand Down
16 changes: 16 additions & 0 deletions Packages/StreamVideo/Tests/Editor/PendingRemoteTrackBufferTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,22 @@ private Task When_participant_already_has_prefix_expect_immediate_bind_Async()
return Task.CompletedTask;
}

[UnityTest]
public IEnumerator When_stream_id_has_track_suffix_expect_immediate_bind()
=> When_stream_id_has_track_suffix_expect_immediate_bind_Async().RunAsIEnumerator();

private Task When_stream_id_has_track_suffix_expect_immediate_bind_Async()
{
SetupCallWithRemoteParticipant(trackLookupPrefix: TrackPrefix);
InvokeSubscriberStreamAdded(CreateMediaStream($"{TrackPrefix}:{VideoTrackTypeKey}:tR"));

Assert.That(_session.BindAttempts, Has.Count.EqualTo(1),
"Stream IDs with an optional track suffix should still bind.");
Assert.That(_session.BindAttempts[0].TrackTypeKey, Is.EqualTo(VideoTrackTypeKey));
Assert.That(GetPendingTrackCount(), Is.EqualTo(0));
return Task.CompletedTask;
}

private StreamVideoCallParticipant SetupCallWithRemoteParticipant(string trackLookupPrefix)
{
var call = CreateCallWithRemoteParticipant(RemoteSessionId, trackLookupPrefix);
Expand Down
Loading