Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
Tr00d committed Mar 4, 2024
2 parents 16ab0dd + a47c99c commit 26b8d46
Show file tree
Hide file tree
Showing 16 changed files with 144 additions and 18 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -220,5 +220,5 @@ pip-log.txt
#Samples/HelloWorld/App.config
#Samples/Archiving/App.config

## Jetbrains IDE
.idea/
## Jetbrains Rider files
.idea/
20 changes: 18 additions & 2 deletions OpenTok/Broadcast.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Expand Down Expand Up @@ -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;
Expand All @@ -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"))
{
Expand Down Expand Up @@ -201,6 +206,17 @@ internal void CopyBroadcast(Broadcast broadcast)
[JsonProperty("multiBroadcastTag")]
public string MultiBroadcastTag { get; set; }

/// <summary>
/// HLS status. Can be one of the following: 'connecting', 'ready', 'live', 'ended' or 'error'.
/// </summary>
public string HlsStatus { get; set; }

/// <summary>
/// Maximum BitRate allowed for the broadcast composing. The default value is 2000000, which is also the maximum value. The minimum value is 400000.
/// </summary>
[JsonConverter(typeof(BroadcastBitrateConverter))]
public BroadcastBitrate MaxBitRate { get; set; } = new BroadcastBitrate();

/// <summary>
/// Stops the live broadcasting if it is started.
/// </summary>
Expand Down
53 changes: 53 additions & 0 deletions OpenTok/BroadcastBitrate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using Newtonsoft.Json;
using OpenTokSDK.Exception;

namespace OpenTokSDK
{
/// <summary>
/// Represents a BitRate for broadcasts.
/// </summary>
public class BroadcastBitrate
{
private const int MaxBitrate = 2000000;
private const int MinBitrate = 400000;

/// <summary>
/// The maximum BitRate allowed for the broadcast composing.
/// </summary>
public long Bitrate { get; }

/// <summary>
/// Creates a BroadcastBitrate with the maximum BitRate.
/// </summary>
public BroadcastBitrate() => this.Bitrate = MaxBitrate;

/// <summary>
/// Creates a BroadcastBitrate with a specific Bitrate.
/// </summary>
/// <param name="bitrate">The Bitrate.</param>
/// <exception cref="OpenTokArgumentException">
/// When specified bitrate is lower than the minimum value, or higher than the
/// maximum value.
/// </exception>
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<BroadcastBitrate>
{
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;
}
}
}
5 changes: 5 additions & 0 deletions OpenTok/DialOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,10 @@ public class DialOptions
/// (true) or not (false, the default).
/// </summary>
public bool? ObserveForceMute { get; set; }

/// <summary>
/// The stream IDs of the participants' which will be subscribed by the SIP participant. If not provided, all streams in session will be selected.
/// </summary>
public string[] Streams { get; set; }
}
}
10 changes: 8 additions & 2 deletions OpenTok/OpenTok.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,11 @@ public OpenTok(int apiKey, string apiSecret, string apiUrl)
/// based on the first client connecting to the session.
/// </param>
/// <param name="mediaMode">
/// <para>
/// Whether the session will transmit streams using the OpenTok Media Router
/// (<see cref="MediaMode.ROUTED"/>) or not (<see cref="MediaMode.RELAYED"/>).
/// By default, the setting is <see cref="MediaMode.RELAYED"/>.
/// </para>
/// <para>
/// With the parameter set to <see cref="MediaMode.RELAYED"/>, the session will
/// attempt to transmit streams directly between clients. If clients cannot connect
Expand Down Expand Up @@ -247,9 +249,11 @@ public Session CreateSession(
/// based on the first client connecting to the session.
/// </param>
/// <param name="mediaMode">
/// <para>
/// Whether the session will transmit streams using the OpenTok Media Router
/// (<see cref="MediaMode.ROUTED"/>) or not (<see cref="MediaMode.RELAYED"/>).
/// By default, the setting is <see cref="MediaMode.RELAYED"/>.
/// </para>
/// <para>
/// With the parameter set to <see cref="MediaMode.RELAYED"/>, the session will
/// attempt to transmit streams directly between clients. If clients cannot connect
Expand Down Expand Up @@ -1905,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,
}
}
};
Expand Down Expand Up @@ -1964,7 +1969,8 @@ public async Task<Sip> 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,
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion OpenTok/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ private string BuildDataString(Role role, double expireTime, string connectionDa
dataStringBuilder.Append(string.Format("session_id={0}", this.Id));
dataStringBuilder.Append(string.Format("&create_time={0}", (long)createTime));
dataStringBuilder.Append(string.Format("&nonce={0}", nonce));
dataStringBuilder.Append(string.Format("&role={0}", role.ToString()));
dataStringBuilder.Append(string.Format("&role={0}", role.ToString().ToLowerInvariant()));

if (initialLayoutClassList != null)
{
Expand Down
30 changes: 30 additions & 0 deletions OpenTokTest/BroadcastBitrateTest.cs
Original file line number Diff line number Diff line change
@@ -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<OpenTokArgumentException>(() => new BroadcastBitrate(invalidBitrate));
Assert.Equal("Bitrate value must be between 400000 and 2000000.", exception.Message);
}
}
}
6 changes: 6 additions & 0 deletions OpenTokTest/BroadcastTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>(url => url.Equals("v2/project/" + ApiKey + "/broadcast")),
Expand Down Expand Up @@ -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<Dictionary<string, string>>(),
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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<string>(url => url.Equals(expectedUrl))),
Expand All @@ -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<string>(url => url.Equals(expectedUrl)), null),
Expand Down
3 changes: 2 additions & 1 deletion OpenTokTest/Data/BroadcastTests/GetBroadcast-response.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"resolution": "640x480",
"status": "started",
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8"
"hls": "http://server/fakepath/playlist.m3u8",
"hlsStatus" : "ready"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"resolution": "640x480",
"status": "started",
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8"
"hls": "http://server/fakepath/playlist.m3u8",
"hlsStatus" : "ready"
}
}
3 changes: 2 additions & 1 deletion OpenTokTest/Data/BroadcastTests/StartBroadcast-response.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8"
},
"settings": { "hls": { "lowLatency": false } }
"settings": { "hls": { "lowLatency": false } },
"maxBitRate": 2000000
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8"
},
"settings": { "hls": { "lowLatency": false } }
"settings": { "hls": { "lowLatency": false } },
"maxBitRate": 1000000
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"status": "started",
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8",
"hlsStatus" : "ready",
"rtmp": [
{
"status": "connecting",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"status": "started",
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8",
"hlsStatus" : "ready",
"rtmp": [
{
"status": "connecting",
Expand Down
8 changes: 6 additions & 2 deletions OpenTokTest/DialTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string> headersSent = null;
Expand Down Expand Up @@ -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]
Expand All @@ -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<string, string> headersSent = null;
Expand Down Expand Up @@ -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]
Expand Down
10 changes: 5 additions & 5 deletions OpenTokTest/TokenTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void GenerateTokenTest()
Assert.NotNull(data["sig"]);
Assert.NotNull(data["create_time"]);
Assert.NotNull(data["nonce"]);
Assert.Equal(data["role"], Role.PUBLISHER.ToString());
Assert.Equal(data["role"], "publisher");

Check warning on line 27 in OpenTokTest/TokenTests.cs

View workflow job for this annotation

GitHub Actions / build

The literal or constant value "publisher" should be passed as the 'expected' argument in the call to 'Assert.Equal(expected, actual)' in method 'GenerateTokenTest' on type 'TokenTests'. Swap the parameter values.
}

[Fact]
Expand All @@ -42,7 +42,7 @@ public void GenerateTokenWithRoleTest()
Assert.NotNull(data["sig"]);
Assert.NotNull(data["create_time"]);
Assert.NotNull(data["nonce"]);
Assert.Equal(data["role"], Role.SUBSCRIBER.ToString());
Assert.Equal(data["role"], "subscriber");

Check warning on line 45 in OpenTokTest/TokenTests.cs

View workflow job for this annotation

GitHub Actions / build

The literal or constant value "subscriber" should be passed as the 'expected' argument in the call to 'Assert.Equal(expected, actual)' in method 'GenerateTokenWithRoleTest' on type 'TokenTests'. Swap the parameter values.
}

[Fact]
Expand All @@ -61,7 +61,7 @@ public void GenerateTokenWithExpireTimeTest()
Assert.NotNull(data["sig"]);
Assert.NotNull(data["create_time"]);
Assert.NotNull(data["nonce"]);
Assert.Equal(data["role"], Role.PUBLISHER.ToString());
Assert.Equal(data["role"], "publisher");

Check warning on line 64 in OpenTokTest/TokenTests.cs

View workflow job for this annotation

GitHub Actions / build

The literal or constant value "publisher" should be passed as the 'expected' argument in the call to 'Assert.Equal(expected, actual)' in method 'GenerateTokenWithExpireTimeTest' on type 'TokenTests'. Swap the parameter values.
Assert.Equal(data["expire_time"], ((long) expireTime).ToString());
}

Expand All @@ -80,7 +80,7 @@ public void GenerateTokenWithConnectionDataTest()
Assert.NotNull(data["sig"]);
Assert.NotNull(data["create_time"]);
Assert.NotNull(data["nonce"]);
Assert.Equal(data["role"], Role.PUBLISHER.ToString());
Assert.Equal(data["role"], "publisher");

Check warning on line 83 in OpenTokTest/TokenTests.cs

View workflow job for this annotation

GitHub Actions / build

The literal or constant value "publisher" should be passed as the 'expected' argument in the call to 'Assert.Equal(expected, actual)' in method 'GenerateTokenWithConnectionDataTest' on type 'TokenTests'. Swap the parameter values.
Assert.Equal(data["connection_data"], connectionData);
}

Expand All @@ -101,7 +101,7 @@ public void GenerateTokenWithInitialLayoutClass()
Assert.NotNull(data["sig"]);
Assert.NotNull(data["create_time"]);
Assert.NotNull(data["nonce"]);
Assert.Equal(data["role"], Role.PUBLISHER.ToString());
Assert.Equal(data["role"],"publisher");

Check warning on line 104 in OpenTokTest/TokenTests.cs

View workflow job for this annotation

GitHub Actions / build

The literal or constant value "publisher" should be passed as the 'expected' argument in the call to 'Assert.Equal(expected, actual)' in method 'GenerateTokenWithInitialLayoutClass' on type 'TokenTests'. Swap the parameter values.
Assert.Equal("focus", data["initial_layout_class_list"]);
}

Expand Down

0 comments on commit 26b8d46

Please sign in to comment.