From 53ef1e1d28a65127028c2e54c0dbea48b35714fa Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Wed, 6 May 2026 10:22:27 +0200 Subject: [PATCH 01/16] Adds remote track mute event tests for mic and cam --- Tests/PlayMode/TrackTests.cs | 106 ++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/Tests/PlayMode/TrackTests.cs b/Tests/PlayMode/TrackTests.cs index ba163f05..88d5416d 100644 --- a/Tests/PlayMode/TrackTests.cs +++ b/Tests/PlayMode/TrackTests.cs @@ -52,7 +52,7 @@ public IEnumerator PublishAudioTrack_UnpublishAndRepublish_Succeeds() } [UnityTest, Category("E2E")] - public IEnumerator SetMute_TrueFalse_TogglesSourceMuteState() + public IEnumerator LocalAudioTrack_SetMute_TrueFalse_TogglesSourceMuteState() { using var context = new TestRoomContext(); yield return context.ConnectAll(); @@ -221,6 +221,110 @@ public IEnumerator RemoteTrackPublication_Unsubscribe_UpdatesFlagAndTriggersEven Assert.AreSame(receivedPublication, unsubscribedPublication); } + [UnityTest, Category("E2E")] + public IEnumerator RemoteTrackPublication_PublisherMutesMic_UpdatesFlagAndTriggersEvent() + { + var (publisher, subscriber) = TwoPeers(); + using var context = new TestRoomContext(new[] { publisher, subscriber }); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + var publisherRoom = context.Rooms[0]; + var subscriberRoom = context.Rooms[1]; + + using var source = new SineWaveAudioSource(); + var localTrack = LocalAudioTrack.CreateAudioTrack(AudioTrackName, source, publisherRoom); + + var subscribedExp = new Expectation(timeoutSeconds: 10f); + subscriberRoom.TrackSubscribed += (_, _, _) => subscribedExp.Fulfill(); + + var mutedExp = new Expectation(timeoutSeconds: 5f); + TrackPublication mutedPublication = null; + Participant mutedParticipant = null; + subscriberRoom.TrackMuted += (publication, participant) => + { + mutedPublication = publication; + mutedParticipant = participant; + mutedExp.Fulfill(); + }; + + var unpublishedFired = false; + subscriberRoom.TrackUnpublished += (_, _) => unpublishedFired = true; + + var pub = publisherRoom.LocalParticipant.PublishTrack(localTrack, AudioOptions()); + yield return pub; + Assert.IsFalse(pub.IsError); + + source.Start(); + yield return subscribedExp.Wait(); + Assert.IsNull(subscribedExp.Error); + + ((ILocalTrack)localTrack).SetMute(true); + + yield return mutedExp.Wait(); + Assert.IsNull(mutedExp.Error, "expected TrackMuted to fire on remote mute"); + Assert.IsNotNull(mutedPublication); + Assert.AreEqual(publisher.Identity, mutedParticipant.Identity); + Assert.IsTrue(mutedPublication.Muted, "publication.Muted should be true after remote mute"); + Assert.IsFalse(unpublishedFired, "TrackUnpublished must not fire on remote mute"); + Assert.IsTrue( + subscriberRoom.RemoteParticipants[publisher.Identity].Tracks.ContainsKey(mutedPublication.Sid), + "publication should still be tracked after remote mute"); + } + + [UnityTest, Category("E2E")] + public IEnumerator RemoteTrackPublication_PublisherDisablesCamera_UpdatesFlagAndTriggersEvent() + { + var (publisher, subscriber) = TwoPeers(); + using var context = new TestRoomContext(new[] { publisher, subscriber }); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + var publisherRoom = context.Rooms[0]; + var subscriberRoom = context.Rooms[1]; + + var videoSource = new StubVideoSource(); + var localTrack = LocalVideoTrack.CreateVideoTrack(VideoTrackName, videoSource, publisherRoom); + + // StubVideoSource never pushes frames, so TrackSubscribed may not fire on the + // subscriber. The RemoteTrackPublication still propagates via TrackPublished. + var publishedExp = new Expectation(timeoutSeconds: 10f); + subscriberRoom.TrackPublished += (_, _) => publishedExp.Fulfill(); + + var mutedExp = new Expectation(timeoutSeconds: 5f); + TrackPublication mutedPublication = null; + Participant mutedParticipant = null; + subscriberRoom.TrackMuted += (publication, participant) => + { + mutedPublication = publication; + mutedParticipant = participant; + mutedExp.Fulfill(); + }; + + var unpublishedFired = false; + subscriberRoom.TrackUnpublished += (_, _) => unpublishedFired = true; + + var pub = publisherRoom.LocalParticipant.PublishTrack(localTrack, VideoOptions()); + yield return pub; + Assert.IsFalse(pub.IsError); + + yield return publishedExp.Wait(); + Assert.IsNull(publishedExp.Error); + + ((ILocalTrack)localTrack).SetMute(true); + + yield return mutedExp.Wait(); + Assert.IsNull(mutedExp.Error, "expected TrackMuted to fire on remote camera disable"); + Assert.IsNotNull(mutedPublication); + Assert.AreEqual(publisher.Identity, mutedParticipant.Identity); + Assert.AreEqual(TrackKind.KindVideo, mutedPublication.Kind); + Assert.IsTrue(mutedPublication.Muted, "publication.Muted should be true after remote camera disable"); + Assert.IsFalse(unpublishedFired, "TrackUnpublished must not fire on remote camera disable"); + Assert.IsTrue( + subscriberRoom.RemoteParticipants[publisher.Identity].Tracks.ContainsKey(mutedPublication.Sid), + "publication should still be tracked after remote camera disable"); + } + [UnityTest, Category("E2E")] public IEnumerator RemoteTrack_SetEnabled_FalseAndTrue_DoesNotThrow() { From 5149bcda66d10e9e9c43b5a16d81aa6825d25885 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Wed, 6 May 2026 11:29:46 +0200 Subject: [PATCH 02/16] Tile system with placeholders seem to work --- Samples~/Meet/Assets/Runtime/MeetManager.cs | 248 ++++++++++++++++---- 1 file changed, 196 insertions(+), 52 deletions(-) diff --git a/Samples~/Meet/Assets/Runtime/MeetManager.cs b/Samples~/Meet/Assets/Runtime/MeetManager.cs index 145d1e6c..a65f054a 100644 --- a/Samples~/Meet/Assets/Runtime/MeetManager.cs +++ b/Samples~/Meet/Assets/Runtime/MeetManager.cs @@ -43,7 +43,9 @@ public class MeetManager : MonoBehaviour private Transform _audioTrackParent; private List