Skip to content

Commit

Permalink
Add subscriber active/inactive events
Browse files Browse the repository at this point in the history
  • Loading branch information
Yousif-CS committed May 28, 2024
1 parent e2d17f3 commit 37b51f9
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 195 deletions.
3 changes: 2 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
## [v1.2.0](https://github.com/millicast/millicast-unity-sdk/compare/v1.1.0...v1.2.0-alpha.1) (19-Jan-2024)
## [v1.2.0](https://github.com/millicast/millicast-unity-sdk/compare/v1.1.0...v1.2.0-beta.0) (29-May-2024)

> Description
### New Features
* Subscriber now reports events when publishers become active/inactive via {{OnActive}} and {{OnInactive}} delegates.
* Introduce Frame Transformer API For `McPublisher` & `McSubscriber`. See [FrameMetadataExample.cs](Samples~/Scripts/FrameMetadataExample.cs) for an example script that embeds text metadata into each encoded frame and extracts it when received in the subscriber.

* Simple example to show `WebCamTexture` publishing: [WebCamPublisherExample.cs](Samples~/Scripts/WebCamPublisherExample.cs)
Expand Down
156 changes: 103 additions & 53 deletions Runtime/DataContainer.cs
Original file line number Diff line number Diff line change
@@ -1,65 +1,115 @@
namespace Dolby.Millicast
{
using System;
using System.Collections.Generic;

using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;

public partial class SimulcastInfo
{
[JsonProperty("active")]
public SimulcastData[] Active { get; set; }

[JsonProperty("inactive")]
public SimulcastData[] Inactive { get; set; }

[JsonProperty("layers")]
public Layer[] Layers { get; set; }
}
namespace Dolby.Millicast {
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using UnityEngine;

public partial class SimulcastData
{
[JsonProperty("id")]
public string Id { get; set; }
/// <summary>
/// Holds track information for multisource
/// </summary>
public partial class Track {
[JsonProperty("trackId")]
public string TrackId { get; set; }
[JsonProperty("media")]
public string Media { get; set; }
}

[JsonProperty("simulcastIdx")]
public long SimulcastIdx { get; set; }
public partial class PublisherActiveEvent {
[JsonProperty("streamId")]
public string StreamId { get; set; }
[JsonProperty("sourceId")]
public string SourceId { get; set; }
[JsonProperty("tracks")]
public Track[] Tracks;
}

[JsonProperty("bitrate")]
public long Bitrate { get; set; }
public partial class PublisherInctiveEvent {
[JsonProperty("streamId")]
public string StreamId { get; set; }
[JsonProperty("sourceId")]
public string SourceId { get; set; }
}

[JsonProperty("layers")]
public Layer[] Layers { get; set; }
}
public partial class SimulcastInfo {
[JsonProperty("active")]
public SimulcastData[] Active { get; set; }

[JsonProperty("inactive")]
public SimulcastData[] Inactive { get; set; }

[JsonProperty("layers")]
public Layer[] Layers { get; set; }
}

public partial class SimulcastData {
[JsonProperty("id")]
public string Id { get; set; }

[JsonProperty("simulcastIdx")]
public long SimulcastIdx { get; set; }

[JsonProperty("bitrate")]
public long Bitrate { get; set; }

[JsonProperty("layers")]
public Layer[] Layers { get; set; }
}

public partial class Layer {
[JsonProperty("simulcastIdx")]
public long SimulcastIdx { get; set; }

public partial class Layer
{
[JsonProperty("simulcastIdx")]
public long SimulcastIdx { get; set; }
[JsonProperty("spatialLayerId")]
public long SpatialLayerId { get; set; }

[JsonProperty("spatialLayerId")]
public long SpatialLayerId { get; set; }
[JsonProperty("temporalLayerId")]
public long TemporalLayerId { get; set; }

[JsonProperty("temporalLayerId")]
public long TemporalLayerId { get; set; }
[JsonProperty("bitrate")]
public long Bitrate { get; set; }

[JsonProperty("bitrate")]
public long Bitrate { get; set; }
[JsonProperty("encodingId", NullValueHandling = NullValueHandling.Ignore)]
public string EncodingId { get; set; }
}

[JsonProperty("encodingId", NullValueHandling = NullValueHandling.Ignore)]
public string EncodingId { get; set; }
internal class ServiceResponse {
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
[JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
public JToken Data { get; set; }
[JsonProperty("transId", NullValueHandling = NullValueHandling.Ignore)]
public string TransactionId { get; set; }
}

internal class LayerEvent {
[JsonProperty("medias")]
public Dictionary<string, SimulcastInfo> Medias;
}

internal class DataContainer {
public static SimulcastInfo ParseSimulcastLayers(JToken payload) {
var simulcastData = payload.ToObject<LayerEvent>();
if (!simulcastData.Medias.ContainsKey("0")) {
Debug.LogError("Invalid payload for simulcast layers event received");
return null;
}
return simulcastData.Medias["0"];
}
public class DataContainer
{
public static SimulcastInfo ParseSimulcastLayers(object mediaData)
{
var jsonObject = mediaData as JObject;
var simulcastData = jsonObject.SelectToken("0").ToString();
SimulcastInfo info = JsonConvert.DeserializeObject<SimulcastInfo>(simulcastData);
return info;
}

public static int ParseViewerCount(JToken payload) {
return payload["viewercount"].ToObject<int>();
}

public static PublisherActiveEvent ParsePublisherActiveEvent(JToken payload) {
return payload.ToObject<PublisherActiveEvent>();
}

public static PublisherInctiveEvent ParsePublisherInactiveEvent(JToken payload) {
return payload.ToObject<PublisherInctiveEvent>();
}


}
}
7 changes: 1 addition & 6 deletions Runtime/FrameMetadata.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Playables;
using Unity.WebRTC;
using System.Text;
using System.Linq;
using UnityEngine.UIElements;


namespace Dolby.Millicast {
public class TransformableFrameInfo {
Expand Down
12 changes: 4 additions & 8 deletions Runtime/Internal/ISignaling.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace Dolby.Millicast
{
Expand All @@ -20,11 +21,6 @@ enum Event
UNPROJECT,
SELECT,

/// <summary>
/// Response events
/// </summary>
RESPONSE, // In response to a one of the sent events

/// <summary>
/// Received events
/// </summary>
Expand Down Expand Up @@ -68,16 +64,16 @@ enum Event
/// <param name="e"> The Event name </param>
/// <param name="data"> Additional data </param>
/// <returns> An awaitable task</returns>
public Task Send(Event e, Dictionary<string, object> data);
public Task<JToken> Send(Event e, Dictionary<string, object> data);

/// <summary>
/// An overload of Send which does not require data.
/// </summary>
/// <param name="e"> The event name </param>
/// <returns> An awaitable task</returns>
public Task Send(Event e);
public Task<JToken> Send(Event e);

delegate void DelegateOnEvent(Event e, ServiceResponseData data);
delegate void DelegateOnEvent(Event e, JToken data);
event DelegateOnEvent OnEvent;

public delegate void DelegateOnOpen();
Expand Down
37 changes: 8 additions & 29 deletions Runtime/Internal/PeerConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using UnityEngine;

using Unity.WebRTC;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Dolby.Millicast
{
Expand Down Expand Up @@ -272,7 +272,7 @@ private IEnumerator OnCreateAnswerSuccess(RTCSessionDescription desc)
}
}

private IEnumerator OnRemoteAnswer(RTCSessionDescription desc)
public IEnumerator OnRemoteAnswer(RTCSessionDescription desc)
{
Debug.Log($"Remote answer received: {desc.sdp}");

Expand Down Expand Up @@ -360,47 +360,26 @@ private void OnNegotiationNeeded()
OnCoroutineRunRequested?.Invoke(PeerNegotiationNeeded());
}

private RTCSessionDescription ParseAnswer(ServiceResponseData payload)
public RTCSessionDescription ParseAnswer(JToken payload)
{
RTCSessionDescription answer;
answer.type = RTCSdpType.Answer;
answer.sdp = payload.sdp;
answer.sdp = payload["sdp"].ToString();
return answer;
}

/// <summary>
/// Establish the peer connection with the peer sitting behind
/// Signaling.
/// </summary>
private void EstablishEvents()
{
if (OnCoroutineRunRequested == null)
{
throw new Exception("You must implement the OnCoroutineRunRequested event");
}

// Set the callbacks
_signaling.OnEvent += (e, data) =>
{
switch (e)
{
case ISignaling.Event.RESPONSE:
if(data != null)
OnCoroutineRunRequested.Invoke(OnRemoteAnswer(ParseAnswer(data)));
break;
}
};
}

/// <summary>
/// This method should be called before anything else.
/// </summary>
/// <param name="signaling"></param>
/// <param name="configuration"></param>
public void SetUp(ISignaling signaling, RTCConfiguration configuration, bool addTransceiver = false)
{
if (OnCoroutineRunRequested == null) {
throw new Exception("You must implement the OnCoroutineRunRequested event");
}

_signaling = signaling;
EstablishEvents();
_pc = new RTCPeerConnection(ref configuration);
_pc.OnNegotiationNeeded += OnNegotiationNeeded;
_pc.OnTrack += OnTrackEvent;
Expand Down
Loading

0 comments on commit 37b51f9

Please sign in to comment.