Skip to content

Commit aaa31a6

Browse files
authored
Support setting local participant metadata & attributes (#96)
1 parent b057ed4 commit aaa31a6

5 files changed

Lines changed: 196 additions & 33 deletions

File tree

Runtime/Scripts/Internal/FFIClient.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ internal sealed class FfiClient : IFFIClient
3939
public event RoomEventReceivedDelegate? RoomEventReceived;
4040
public event TrackEventReceivedDelegate? TrackEventReceived;
4141
public event RpcMethodInvocationReceivedDelegate? RpcMethodInvocationReceived;
42+
public event SetLocalMetadataReceivedDelegate? SetLocalMetadataReceived;
43+
public event SetLocalNameReceivedDelegate? SetLocalNameReceived;
44+
public event SetLocalAttributesReceivedDelegate? SetLocalAttributesReceived;
4245

4346
// participant events are not allowed in the fii protocol public event ParticipantEventReceivedDelegate ParticipantEventReceived;
4447
public event VideoStreamEventReceivedDelegate? VideoStreamEventReceived;
@@ -254,6 +257,15 @@ static unsafe void FFICallback(UIntPtr data, UIntPtr size)
254257
case FfiEvent.MessageOneofCase.RoomEvent:
255258
Instance.RoomEventReceived?.Invoke(r.RoomEvent);
256259
break;
260+
case FfiEvent.MessageOneofCase.SetLocalName:
261+
Instance.SetLocalNameReceived?.Invoke(r.SetLocalName!);
262+
break;
263+
case FfiEvent.MessageOneofCase.SetLocalMetadata:
264+
Instance.SetLocalMetadataReceived?.Invoke(r.SetLocalMetadata!);
265+
break;
266+
case FfiEvent.MessageOneofCase.SetLocalAttributes:
267+
Instance.SetLocalAttributesReceived?.Invoke(r.SetLocalAttributes!);
268+
break;
257269
case FfiEvent.MessageOneofCase.TrackEvent:
258270
Instance.TrackEventReceived?.Invoke(r.TrackEvent!);
259271
break;

Runtime/Scripts/Internal/FFIClients/FFIEvents.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ namespace LiveKit.Internal
2222

2323
internal delegate void GetSessionStatsDelegate(GetStatsCallback e);
2424

25+
internal delegate void SetLocalMetadataReceivedDelegate(SetLocalMetadataCallback e);
26+
internal delegate void SetLocalNameReceivedDelegate(SetLocalNameCallback e);
27+
internal delegate void SetLocalAttributesReceivedDelegate(SetLocalAttributesCallback e);
28+
2529
internal delegate void ByteStreamReaderReadAllReceivedDelegate(ByteStreamReaderReadAllCallback e);
2630

2731
internal delegate void ByteStreamReaderWriteToFileReceivedDelegate(ByteStreamReaderWriteToFileCallback e);

Runtime/Scripts/Internal/FFIClients/FfiRequestExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public static void Inject<T>(this FfiRequest ffiRequest, T request)
3939
case SetLocalNameRequest updateLocalNameRequest:
4040
ffiRequest.SetLocalName = updateLocalNameRequest;
4141
break;
42+
case SetLocalAttributesRequest setLocalAttributesRequest:
43+
ffiRequest.SetLocalAttributes = setLocalAttributesRequest;
44+
break;
4245
case GetSessionStatsRequest getSessionStatsRequest:
4346
ffiRequest.GetSessionStats = getSessionStatsRequest;
4447
break;

Runtime/Scripts/Participant.cs

Lines changed: 148 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Linq;
33
using System.Collections.Generic;
44
using System.Threading.Tasks;
5-
using Google.Protobuf.Collections;
65
using LiveKit.Internal;
76
using LiveKit.Proto;
87
using LiveKit.Internal.FFIClients.Requests;
@@ -16,15 +15,15 @@ public class Participant
1615
{
1716
public delegate void PublishDelegate(RemoteTrackPublication publication);
1817

19-
20-
private ParticipantInfo _info;
18+
internal ParticipantInfo _info; // Can be updated by the server through room events.
2119
internal readonly Dictionary<string, TrackPublication> _tracks = new();
2220
public FfiHandle Handle;
2321
public string Sid => _info.Sid;
2422
public string Identity => _info.Identity;
2523
public string Name => _info.Name;
2624
public string Metadata => _info.Metadata;
27-
public MapField<string, string> Attributes => _info.Attributes;
25+
public IReadOnlyDictionary<string, string> Attributes => _info.Attributes;
26+
2827
public ConnectionQuality ConnectionQuality { internal set; get; }
2928
public event PublishDelegate TrackPublished;
3029
public event PublishDelegate TrackUnpublished;
@@ -38,23 +37,14 @@ protected Participant(OwnedParticipant participant, Room room)
3837
{
3938
Room = new WeakReference<Room>(room);
4039
Handle = FfiHandle.FromOwnedHandle(participant.Handle);
41-
UpdateInfo(participant.Info);
40+
_info = participant.Info;
4241
}
4342

44-
public void SetMeta(string meta)
45-
{
46-
_info.Metadata = meta;
47-
}
43+
[Obsolete("Use SetMetadata on LocalParticipant instead; this method has no effect")]
44+
public void SetMeta(string meta) {}
4845

49-
public void SetName(string name)
50-
{
51-
_info.Name = name;
52-
}
53-
54-
internal void UpdateInfo(ParticipantInfo info)
55-
{
56-
_info = info;
57-
}
46+
[Obsolete("Use SetName on LocalParticipant instead; this method has no effect")]
47+
public void SetName(string name) {}
5848

5949
internal void OnTrackPublished(RemoteTrackPublication publication)
6050
{
@@ -65,7 +55,6 @@ internal void OnTrackUnpublished(RemoteTrackPublication publication)
6555
{
6656
TrackUnpublished?.Invoke(publication);
6757
}
68-
6958
}
7059

7160
public sealed class LocalParticipant : Participant
@@ -124,22 +113,91 @@ public void PublishData(Span<byte> data, IReadOnlyCollection<string> destination
124113
}
125114
}
126115

116+
[Obsolete("Use SetMetadata instead")]
127117
public void UpdateMetadata(string metadata)
128118
{
129-
using var request = FFIBridge.Instance.NewRequest<SetLocalMetadataRequest>();
130-
var updateReq = request.request;
131-
updateReq.Metadata = metadata;
132-
var resp = request.Send();
119+
SetMetadata(metadata);
133120
}
134121

122+
[Obsolete("Use SetName instead")]
135123
public void UpdateName(string name)
124+
{
125+
SetName(name);
126+
}
127+
128+
/// <summary>
129+
/// Set the metadata for the local participant.
130+
/// </summary>
131+
/// <remarks>
132+
/// This requires `canUpdateOwnMetadata` permission.
133+
/// </remarks>
134+
/// <param name="metadata">The new metadata.</param>
135+
public SetLocalMetadataInstruction SetMetadata(string metadata)
136+
{
137+
using var request = FFIBridge.Instance.NewRequest<SetLocalMetadataRequest>();
138+
var setReq = request.request;
139+
setReq.LocalParticipantHandle = (ulong)Handle.DangerousGetHandle();
140+
setReq.Metadata = metadata;
141+
142+
using var response = request.Send();
143+
FfiResponse res = response;
144+
return new SetLocalMetadataInstruction(res.SetLocalMetadata.AsyncId);
145+
}
146+
147+
/// <summary>
148+
/// Set the name for the local participant.
149+
/// </summary>
150+
/// <remarks>
151+
/// This requires `canUpdateOwnMetadata` permission.
152+
/// </remarks>
153+
/// <param name="name">The new name.</param>
154+
public new SetLocalNameInstruction SetName(string name)
136155
{
137156
using var request = FFIBridge.Instance.NewRequest<SetLocalNameRequest>();
138-
var updateReq = request.request;
139-
updateReq.Name = name;
140-
var resp = request.Send();
157+
var setReq = request.request;
158+
setReq.LocalParticipantHandle = (ulong)Handle.DangerousGetHandle();
159+
setReq.Name = name;
160+
161+
using var response = request.Send();
162+
FfiResponse res = response;
163+
return new SetLocalNameInstruction(res.SetLocalName.AsyncId);
141164
}
142165

166+
/// <summary>
167+
/// Set custom attributes for the local participant.
168+
/// </summary>
169+
/// <remarks>
170+
/// This requires `canUpdateOwnMetadata` permission.
171+
/// </remarks>
172+
/// <param name="attributes">The new attributes. Existing attributes that
173+
/// are not overridden will remain unchanged.</param>
174+
public SetLocalAttributesInstruction SetAttributes(IDictionary<string, string> attributes)
175+
{
176+
using var request = FFIBridge.Instance.NewRequest<SetLocalAttributesRequest>();
177+
var setReq = request.request;
178+
setReq.LocalParticipantHandle = (ulong)Handle.DangerousGetHandle();
179+
180+
var newAttributes = new Dictionary<string, string>(Attributes);
181+
foreach (var kvp in attributes)
182+
{
183+
// Override existing attributes
184+
newAttributes[kvp.Key] = kvp.Value;
185+
}
186+
187+
foreach (var kvp in newAttributes)
188+
{
189+
var entry = new AttributesEntry
190+
{
191+
Key = kvp.Key,
192+
Value = kvp.Value
193+
};
194+
setReq.Attributes.Add(entry);
195+
}
196+
197+
using var response = request.Send();
198+
FfiResponse res = response;
199+
return new SetLocalAttributesInstruction(res.SetLocalAttributes.AsyncId);
200+
}
143201

144202
/// <summary>
145203
/// Performs RPC on another participant in the room.
@@ -514,6 +572,70 @@ internal void OnPublish(PublishTrackCallback e)
514572
FfiClient.Instance.PublishTrackReceived -= OnPublish;
515573
}
516574
}
575+
576+
public sealed class SetLocalMetadataInstruction : YieldInstruction
577+
{
578+
private ulong _asyncId;
579+
580+
internal SetLocalMetadataInstruction(ulong asyncId)
581+
{
582+
_asyncId = asyncId;
583+
FfiClient.Instance.SetLocalMetadataReceived += OnSetLocalMetadata;
584+
}
585+
586+
internal void OnSetLocalMetadata(SetLocalMetadataCallback e)
587+
{
588+
if (e.AsyncId != _asyncId)
589+
return;
590+
591+
IsError = !string.IsNullOrEmpty(e.Error);
592+
IsDone = true;
593+
FfiClient.Instance.SetLocalMetadataReceived -= OnSetLocalMetadata;
594+
}
595+
}
596+
597+
public sealed class SetLocalNameInstruction : YieldInstruction
598+
{
599+
private ulong _asyncId;
600+
601+
internal SetLocalNameInstruction(ulong asyncId)
602+
{
603+
_asyncId = asyncId;
604+
FfiClient.Instance.SetLocalNameReceived += OnSetLocalName;
605+
}
606+
607+
internal void OnSetLocalName(SetLocalNameCallback e)
608+
{
609+
if (e.AsyncId != _asyncId)
610+
return;
611+
612+
IsError = !string.IsNullOrEmpty(e.Error);
613+
IsDone = true;
614+
FfiClient.Instance.SetLocalNameReceived -= OnSetLocalName;
615+
}
616+
}
617+
618+
public sealed class SetLocalAttributesInstruction : YieldInstruction
619+
{
620+
private ulong _asyncId;
621+
622+
internal SetLocalAttributesInstruction(ulong asyncId)
623+
{
624+
_asyncId = asyncId;
625+
FfiClient.Instance.SetLocalAttributesReceived += OnSetLocalAttributes;
626+
}
627+
628+
internal void OnSetLocalAttributes(SetLocalAttributesCallback e)
629+
{
630+
if (e.AsyncId != _asyncId)
631+
return;
632+
633+
IsError = !string.IsNullOrEmpty(e.Error);
634+
IsDone = true;
635+
FfiClient.Instance.SetLocalAttributesReceived -= OnSetLocalAttributes;
636+
}
637+
}
638+
517639
public sealed class UnpublishTrackInstruction : YieldInstruction
518640
{
519641
private ulong _asyncId;

Runtime/Scripts/Room.cs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ public class Room
159159
public event MetaDelegate RoomMetadataChanged;
160160
public event ParticipantDelegate ParticipantMetadataChanged;
161161
public event ParticipantDelegate ParticipantNameChanged;
162+
public event ParticipantDelegate ParticipantAttributesChanged;
162163

163164
public ConnectInstruction Connect(string url, string token, RoomOptions options)
164165
{
@@ -265,20 +266,41 @@ internal void OnEventReceived(RoomEvent e)
265266
case RoomEvent.MessageOneofCase.ParticipantMetadataChanged:
266267
{
267268
var participant = GetParticipant(e.ParticipantMetadataChanged.ParticipantIdentity);
268-
if (participant != null)
269+
if (participant == null)
269270
{
270-
participant.SetMeta(e.ParticipantMetadataChanged.Metadata);
271-
ParticipantMetadataChanged?.Invoke(participant);
271+
Utils.Debug($"Unable to find participant: {e.ParticipantMetadataChanged.ParticipantIdentity} in Meta data Change Event");
272+
return;
272273
}
273-
else Utils.Debug("Unable to find participant: " + e.ParticipantMetadataChanged.ParticipantIdentity + " in Meta data Change Event");
274+
participant._info.Metadata = e.ParticipantMetadataChanged.Metadata;
275+
ParticipantMetadataChanged?.Invoke(participant);
274276
}
275277
break;
276278
case RoomEvent.MessageOneofCase.ParticipantNameChanged:
277279
{
278280
var participant = GetParticipant(e.ParticipantNameChanged.ParticipantIdentity);
279-
participant.SetName(e.ParticipantNameChanged.Name);
280-
if (participant != null) ParticipantNameChanged?.Invoke(participant);
281-
else Utils.Debug("Unable to find participant: " + e.ParticipantNameChanged.ParticipantIdentity + " in Meta data Change Event");
281+
if (participant == null)
282+
{
283+
Utils.Debug($"Unable to find participant: {e.ParticipantNameChanged.ParticipantIdentity} in Name Change Event");
284+
return;
285+
}
286+
participant._info.Name = e.ParticipantNameChanged.Name;
287+
ParticipantNameChanged?.Invoke(participant);
288+
}
289+
break;
290+
case RoomEvent.MessageOneofCase.ParticipantAttributesChanged:
291+
{
292+
var participant = GetParticipant(e.ParticipantAttributesChanged.ParticipantIdentity);
293+
if (participant == null)
294+
{
295+
Utils.Debug($"Unable to find participant: {e.ParticipantAttributesChanged.ParticipantIdentity} in Attributes Change Event");
296+
return;
297+
}
298+
participant._info.Attributes.Clear();
299+
foreach (var entry in e.ParticipantAttributesChanged.Attributes)
300+
{
301+
participant._info.Attributes.Add(entry.Key, entry.Value);
302+
}
303+
ParticipantAttributesChanged?.Invoke(participant);
282304
}
283305
break;
284306
case RoomEvent.MessageOneofCase.ParticipantConnected:

0 commit comments

Comments
 (0)