From f3de99a4bc80ee4d3956d89d76ed8343840641c6 Mon Sep 17 00:00:00 2001
From: Guillaume Faas <59444272+Tr00d@users.noreply.github.com>
Date: Fri, 12 Jan 2024 11:00:06 +0100
Subject: [PATCH] feat: update Broadcast and SIP (#233)
* Add HlsStatus property on Broadcast
* Add MaxBitRate property
* Make Broadcast Bitrate an object with its own validation
* Update converter for Bitrate
* Add streams property to Dial feature
* Add streams property to Dial feature
---
.gitignore | 4 +-
OpenTok/Broadcast.cs | 20 ++++++-
OpenTok/BroadcastBitrate.cs | 53 +++++++++++++++++++
OpenTok/DialOptions.cs | 5 ++
OpenTok/OpenTok.cs | 6 ++-
OpenTokTest/BroadcastBitrateTest.cs | 30 +++++++++++
OpenTokTest/BroadcastTests.cs | 6 +++
.../BroadcastTests/GetBroadcast-response.json | 3 +-
.../GetBroadcastAsync-response.json | 3 +-
.../StartBroadcast-response.json | 3 +-
.../StartBroadcastAsync-response.json | 3 +-
...StartBroadcastWithRTMPandHLS-response.json | 1 +
...BroadcastWithRTMPandHLSAsync-response.json | 1 +
OpenTokTest/DialTests.cs | 8 ++-
14 files changed, 134 insertions(+), 12 deletions(-)
create mode 100644 OpenTok/BroadcastBitrate.cs
create mode 100644 OpenTokTest/BroadcastBitrateTest.cs
diff --git a/.gitignore b/.gitignore
index 7e6c5436..a9918195 100644
--- a/.gitignore
+++ b/.gitignore
@@ -220,5 +220,5 @@ pip-log.txt
#Samples/HelloWorld/App.config
#Samples/Archiving/App.config
-## Jetbrains IDE
-.idea/
+## Jetbrains Rider files
+.idea/
\ No newline at end of file
diff --git a/OpenTok/Broadcast.cs b/OpenTok/Broadcast.cs
index 6637c0d0..6f80f71f 100644
--- a/OpenTok/Broadcast.cs
+++ b/OpenTok/Broadcast.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -92,6 +91,7 @@ internal void CopyBroadcast(Broadcast broadcast)
StreamMode = broadcast.StreamMode;
Settings = broadcast.Settings;
MultiBroadcastTag = broadcast.MultiBroadcastTag;
+ MaxBitRate = broadcast.MaxBitRate;
if (BroadcastUrls == null)
return;
@@ -100,6 +100,11 @@ internal void CopyBroadcast(Broadcast broadcast)
{
Hls = BroadcastUrls["hls"].ToString();
}
+
+ if (BroadcastUrls.ContainsKey("hlsStatus"))
+ {
+ HlsStatus = BroadcastUrls["hlsStatus"].ToString();
+ }
if (BroadcastUrls.ContainsKey("rtmp"))
{
@@ -201,6 +206,17 @@ internal void CopyBroadcast(Broadcast broadcast)
[JsonProperty("multiBroadcastTag")]
public string MultiBroadcastTag { get; set; }
+ ///
+ /// HLS status. Can be one of the following: 'connecting', 'ready', 'live', 'ended' or 'error'.
+ ///
+ public string HlsStatus { get; set; }
+
+ ///
+ /// Maximum BitRate allowed for the broadcast composing. The default value is 2000000, which is also the maximum value. The minimum value is 400000.
+ ///
+ [JsonConverter(typeof(BroadcastBitrateConverter))]
+ public BroadcastBitrate MaxBitRate { get; set; } = new BroadcastBitrate();
+
///
/// Stops the live broadcasting if it is started.
///
diff --git a/OpenTok/BroadcastBitrate.cs b/OpenTok/BroadcastBitrate.cs
new file mode 100644
index 00000000..37b32765
--- /dev/null
+++ b/OpenTok/BroadcastBitrate.cs
@@ -0,0 +1,53 @@
+using System;
+using Newtonsoft.Json;
+using OpenTokSDK.Exception;
+
+namespace OpenTokSDK
+{
+ ///
+ /// Represents a BitRate for broadcasts.
+ ///
+ public class BroadcastBitrate
+ {
+ private const int MaxBitrate = 2000000;
+ private const int MinBitrate = 400000;
+
+ ///
+ /// The maximum BitRate allowed for the broadcast composing.
+ ///
+ public long Bitrate { get; }
+
+ ///
+ /// Creates a BroadcastBitrate with the maximum BitRate.
+ ///
+ public BroadcastBitrate() => this.Bitrate = MaxBitrate;
+
+ ///
+ /// Creates a BroadcastBitrate with a specific Bitrate.
+ ///
+ /// The Bitrate.
+ ///
+ /// When specified bitrate is lower than the minimum value, or higher than the
+ /// maximum value.
+ ///
+ public BroadcastBitrate(long bitrate) =>
+ this.Bitrate = ValidateBitrate(bitrate)
+ ? bitrate
+ : throw new OpenTokArgumentException($"Bitrate value must be between {MinBitrate} and {MaxBitrate}.");
+
+ private static bool ValidateBitrate(long bitrate) => bitrate <= MaxBitrate && bitrate >= MinBitrate;
+ }
+
+ internal class BroadcastBitrateConverter : JsonConverter
+ {
+ public override void WriteJson(JsonWriter writer, BroadcastBitrate value, JsonSerializer serializer) =>
+ writer.WriteValue(value?.Bitrate);
+
+ public override BroadcastBitrate ReadJson(JsonReader reader, Type objectType, BroadcastBitrate existingValue, bool hasExistingValue,
+ JsonSerializer serializer)
+ {
+ var value = (long?)reader.Value;
+ return value.HasValue ? new BroadcastBitrate(value.Value) : null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenTok/DialOptions.cs b/OpenTok/DialOptions.cs
index 923e8e2f..24916cd8 100644
--- a/OpenTok/DialOptions.cs
+++ b/OpenTok/DialOptions.cs
@@ -50,5 +50,10 @@ public class DialOptions
/// (true) or not (false, the default).
///
public bool? ObserveForceMute { get; set; }
+
+ ///
+ /// The stream IDs of the participants' which will be subscribed by the SIP participant. If not provided, all streams in session will be selected.
+ ///
+ public string[] Streams { get; set; }
}
}
diff --git a/OpenTok/OpenTok.cs b/OpenTok/OpenTok.cs
index 779cd209..edbd622e 100644
--- a/OpenTok/OpenTok.cs
+++ b/OpenTok/OpenTok.cs
@@ -1909,7 +1909,8 @@ public Sip Dial(string sessionId, string token, string sipUri, DialOptions optio
auth = options?.Auth,
secure = options?.Secure,
video = options?.Video,
- observeForceMute = options?.ObserveForceMute
+ observeForceMute = options?.ObserveForceMute,
+ streams = options?.Streams,
}
}
};
@@ -1968,7 +1969,8 @@ public async Task DialAsync(string sessionId, string token, string sipUri,
auth = options?.Auth,
secure = options?.Secure,
video = options?.Video,
- observeForceMute = options?.ObserveForceMute
+ observeForceMute = options?.ObserveForceMute,
+ streams = options?.Streams,
}
}
};
diff --git a/OpenTokTest/BroadcastBitrateTest.cs b/OpenTokTest/BroadcastBitrateTest.cs
new file mode 100644
index 00000000..d0b1de90
--- /dev/null
+++ b/OpenTokTest/BroadcastBitrateTest.cs
@@ -0,0 +1,30 @@
+using OpenTokSDK;
+using OpenTokSDK.Exception;
+using Xunit;
+
+namespace OpenTokSDKTest
+{
+ public class BroadcastBitrateTest
+ {
+ [Fact]
+ public void Bitrate_ShouldHaveDefaultValue() =>
+ Assert.Equal(2000000, new BroadcastBitrate().Bitrate);
+
+ [Fact]
+ public void Bitrate_ShouldAllowMaximumValue()=>
+ Assert.Equal(2000000, new BroadcastBitrate(2000000).Bitrate);
+
+ [Fact]
+ public void Bitrate_ShouldAllowMinimumValue()=>
+ Assert.Equal(400000, new BroadcastBitrate(400000).Bitrate);
+
+ [Theory]
+ [InlineData(399999)]
+ [InlineData(2000001)]
+ public void Bitrate_ShouldThrowException_GivenValueIsOutsideRange(int invalidBitrate)
+ {
+ var exception = Assert.Throws(() => new BroadcastBitrate(invalidBitrate));
+ Assert.Equal("Bitrate value must be between 400000 and 2000000.", exception.Message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenTokTest/BroadcastTests.cs b/OpenTokTest/BroadcastTests.cs
index 487472cc..fdefe11f 100644
--- a/OpenTokTest/BroadcastTests.cs
+++ b/OpenTokTest/BroadcastTests.cs
@@ -30,6 +30,7 @@ public void StartBroadcast()
Assert.Equal(sessionId, broadcast.SessionId);
Assert.NotNull(broadcast.Id);
Assert.Equal(Broadcast.BroadcastStatus.STARTED, broadcast.Status);
+ Assert.Equal(2000000, broadcast.MaxBitRate.Bitrate);
mockClient.Verify(
httpClient => httpClient.Post(It.Is(url => url.Equals("v2/project/" + ApiKey + "/broadcast")),
@@ -57,6 +58,7 @@ public async Task StartBroadcastAsync()
Assert.Equal(sessionId, broadcast.SessionId);
Assert.NotNull(broadcast.Id);
Assert.Equal(Broadcast.BroadcastStatus.STARTED, broadcast.Status);
+ Assert.Equal(1000000, broadcast.MaxBitRate.Bitrate);
mockClient.Verify(
httpClient => httpClient.PostAsync(expectedUrl, It.IsAny>(),
@@ -481,6 +483,7 @@ public void StartBroadcastWithRTMPandHLS()
Assert.NotNull(broadcast.RtmpList);
Assert.Equal(2, broadcast.RtmpList.Count);
Assert.NotNull(broadcast.Hls);
+ Assert.Equal("ready", broadcast.HlsStatus);
Assert.NotNull(broadcast.Id);
Assert.Equal(Broadcast.BroadcastStatus.STARTED, broadcast.Status);
@@ -530,6 +533,7 @@ public async Task StartBroadcastWithRTMPandHLSAsync()
Assert.NotNull(broadcast.RtmpList);
Assert.Equal(2, broadcast.RtmpList.Count);
Assert.NotNull(broadcast.Hls);
+ Assert.Equal("ready", broadcast.HlsStatus);
Assert.NotNull(broadcast.Id);
Assert.Equal(Broadcast.BroadcastStatus.STARTED, broadcast.Status);
@@ -1405,6 +1409,7 @@ public void GetBroadcast()
Assert.NotNull(broadcast);
Assert.Equal(broadcastId, broadcast.Id);
Assert.NotNull(broadcast.Id);
+ Assert.Equal("ready", broadcast.HlsStatus);
var expectedUrl = $"v2/project/{ApiKey}/broadcast/{broadcastId}";
mockClient.Verify(httpClient => httpClient.Get(It.Is(url => url.Equals(expectedUrl))),
@@ -1427,6 +1432,7 @@ public async Task GetBroadcastAsync()
Assert.NotNull(broadcast);
Assert.Equal(broadcastId, broadcast.Id);
Assert.NotNull(broadcast.Id);
+ Assert.Equal("ready", broadcast.HlsStatus);
var expectedUrl = $"v2/project/{ApiKey}/broadcast/{broadcastId}";
mockClient.Verify(httpClient => httpClient.GetAsync(It.Is(url => url.Equals(expectedUrl)), null),
diff --git a/OpenTokTest/Data/BroadcastTests/GetBroadcast-response.json b/OpenTokTest/Data/BroadcastTests/GetBroadcast-response.json
index 16f14cd1..b6861f1d 100644
--- a/OpenTokTest/Data/BroadcastTests/GetBroadcast-response.json
+++ b/OpenTokTest/Data/BroadcastTests/GetBroadcast-response.json
@@ -7,6 +7,7 @@
"resolution": "640x480",
"status": "started",
"broadcastUrls": {
- "hls": "http://server/fakepath/playlist.m3u8"
+ "hls": "http://server/fakepath/playlist.m3u8",
+ "hlsStatus" : "ready"
}
}
\ No newline at end of file
diff --git a/OpenTokTest/Data/BroadcastTests/GetBroadcastAsync-response.json b/OpenTokTest/Data/BroadcastTests/GetBroadcastAsync-response.json
index 16f14cd1..b6861f1d 100644
--- a/OpenTokTest/Data/BroadcastTests/GetBroadcastAsync-response.json
+++ b/OpenTokTest/Data/BroadcastTests/GetBroadcastAsync-response.json
@@ -7,6 +7,7 @@
"resolution": "640x480",
"status": "started",
"broadcastUrls": {
- "hls": "http://server/fakepath/playlist.m3u8"
+ "hls": "http://server/fakepath/playlist.m3u8",
+ "hlsStatus" : "ready"
}
}
\ No newline at end of file
diff --git a/OpenTokTest/Data/BroadcastTests/StartBroadcast-response.json b/OpenTokTest/Data/BroadcastTests/StartBroadcast-response.json
index f7a997f2..faa46eca 100644
--- a/OpenTokTest/Data/BroadcastTests/StartBroadcast-response.json
+++ b/OpenTokTest/Data/BroadcastTests/StartBroadcast-response.json
@@ -9,5 +9,6 @@
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8"
},
- "settings": { "hls": { "lowLatency": false } }
+ "settings": { "hls": { "lowLatency": false } },
+ "maxBitRate": 2000000
}
\ No newline at end of file
diff --git a/OpenTokTest/Data/BroadcastTests/StartBroadcastAsync-response.json b/OpenTokTest/Data/BroadcastTests/StartBroadcastAsync-response.json
index f7a997f2..20ba4eb0 100644
--- a/OpenTokTest/Data/BroadcastTests/StartBroadcastAsync-response.json
+++ b/OpenTokTest/Data/BroadcastTests/StartBroadcastAsync-response.json
@@ -9,5 +9,6 @@
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8"
},
- "settings": { "hls": { "lowLatency": false } }
+ "settings": { "hls": { "lowLatency": false } },
+ "maxBitRate": 1000000
}
\ No newline at end of file
diff --git a/OpenTokTest/Data/BroadcastTests/StartBroadcastWithRTMPandHLS-response.json b/OpenTokTest/Data/BroadcastTests/StartBroadcastWithRTMPandHLS-response.json
index 9b827ce3..e1c4bfb7 100644
--- a/OpenTokTest/Data/BroadcastTests/StartBroadcastWithRTMPandHLS-response.json
+++ b/OpenTokTest/Data/BroadcastTests/StartBroadcastWithRTMPandHLS-response.json
@@ -8,6 +8,7 @@
"status": "started",
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8",
+ "hlsStatus" : "ready",
"rtmp": [
{
"status": "connecting",
diff --git a/OpenTokTest/Data/BroadcastTests/StartBroadcastWithRTMPandHLSAsync-response.json b/OpenTokTest/Data/BroadcastTests/StartBroadcastWithRTMPandHLSAsync-response.json
index 9b827ce3..e1c4bfb7 100644
--- a/OpenTokTest/Data/BroadcastTests/StartBroadcastWithRTMPandHLSAsync-response.json
+++ b/OpenTokTest/Data/BroadcastTests/StartBroadcastWithRTMPandHLSAsync-response.json
@@ -8,6 +8,7 @@
"status": "started",
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8",
+ "hlsStatus" : "ready",
"rtmp": [
{
"status": "connecting",
diff --git a/OpenTokTest/DialTests.cs b/OpenTokTest/DialTests.cs
index 9b61f255..27753b03 100644
--- a/OpenTokTest/DialTests.cs
+++ b/OpenTokTest/DialTests.cs
@@ -164,7 +164,8 @@ public void DialCorrectData()
Auth = new DialAuth { Username = "Tim", Password = "Bob" },
Secure = true,
Video = true,
- ObserveForceMute = true
+ ObserveForceMute = true,
+ Streams = new []{"stream1", "stream2"},
};
Dictionary headersSent = null;
@@ -199,6 +200,7 @@ public void DialCorrectData()
Assert.Equal(dialOptions.Secure, sip.secure);
Assert.Equal(dialOptions.Video, sip.video);
Assert.Equal(dialOptions.ObserveForceMute, sip.observeForceMute);
+ Assert.Equal(dialOptions.Streams, sip.streams);
}
[Fact]
@@ -215,7 +217,8 @@ public async Task DialAsyncCorrectData()
Auth = new DialAuth { Username = "Tim", Password = "Bob" },
Secure = true,
Video = true,
- ObserveForceMute = true
+ ObserveForceMute = true,
+ Streams = new []{"stream1", "stream2"},
};
Dictionary headersSent = null;
@@ -250,6 +253,7 @@ public async Task DialAsyncCorrectData()
Assert.Equal(dialOptions.Secure, sip.secure);
Assert.Equal(dialOptions.Video, sip.video);
Assert.Equal(dialOptions.ObserveForceMute, sip.observeForceMute);
+ Assert.Equal(dialOptions.Streams, sip.streams);
}
[Fact]