Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Devx 6491 - Update.NET Server SDK for Multiple Archives and Records #208

Merged
merged 9 commits into from
Jan 10, 2023
6 changes: 6 additions & 0 deletions OpenTok/Archive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ internal void CopyArchive(Archive archive)
OutputMode = archive.OutputMode;
Resolution = archive.Resolution;
StreamMode = archive.StreamMode;
MultiArchiveTag = archive.MultiArchiveTag;
}

/// <summary>
Expand Down Expand Up @@ -201,6 +202,11 @@ internal void CopyArchive(Archive archive)
/// </summary>
public StreamMode StreamMode { get; set; }

/// <summary>
/// The unique tag for simultaneous archives (if one was set).
/// </summary>
public string MultiArchiveTag { get; set; }

/// <summary>
/// Stops the OpenTok archive if it is being recorded.
/// <para>
Expand Down
7 changes: 7 additions & 0 deletions OpenTok/Broadcast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ internal void CopyBroadcast(Broadcast broadcast)
BroadcastUrls = broadcast.BroadcastUrls;
StreamMode = broadcast.StreamMode;
Settings = broadcast.Settings;
MultiBroadcastTag = broadcast.MultiBroadcastTag;

if (BroadcastUrls == null)
return;
Expand Down Expand Up @@ -193,6 +194,12 @@ internal void CopyBroadcast(Broadcast broadcast)
/// </summary>
[JsonProperty("settings")]
public BroadcastSettings Settings { get; set; }

/// <summary>
/// The unique tag for simultaneous broadcasts (if one was set).
/// </summary>
[JsonProperty("multiBroadcastTag")]
public string MultiBroadcastTag { get; set; }

/// <summary>
/// Stops the live broadcasting if it is started.
Expand Down
57 changes: 49 additions & 8 deletions OpenTok/OpenTok.cs
Original file line number Diff line number Diff line change
Expand Up @@ -394,10 +394,18 @@ public string GenerateToken(string sessionId, Role role = Role.PUBLISHER, double
/// <see cref="OpenTok.AddStreamToArchiveAsync"/> and
/// <see cref="OpenTok.RemoveStreamFromArchiveAsync"/> methods).
/// </param>
/// <param name="multiArchiveTag">
/// Set this to support recording multiple archives for the same session simultaneously.
/// Set this to a unique string for each simultaneous archive of an ongoing session.
/// You must also set this option when manually starting an archive in a session that
/// is automatically archived. If you do not specify a unique multiArchiveTag, you can
/// only record one archive at a time for a given session. See
/// <a href="https://tokbox.com/developer/guides/archiving/#simultaneous-archives">Simultaneous archives</a>.
/// </param>
/// <returns>
/// The Archive object. This object includes properties defining the archive, including the archive ID.
/// </returns>
public Archive StartArchive(string sessionId, string name = "", bool hasVideo = true, bool hasAudio = true, OutputMode outputMode = OutputMode.COMPOSED, string resolution = null, ArchiveLayout layout = null, StreamMode? streamMode = null)
public Archive StartArchive(string sessionId, string name = "", bool hasVideo = true, bool hasAudio = true, OutputMode outputMode = OutputMode.COMPOSED, string resolution = null, ArchiveLayout layout = null, StreamMode? streamMode = null, string multiArchiveTag = null)
{
if (string.IsNullOrEmpty(sessionId))
{
Expand Down Expand Up @@ -432,6 +440,11 @@ public Archive StartArchive(string sessionId, string name = "", bool hasVideo =
data.Add("layout", layout);
}

if (multiArchiveTag is object)
{
data.Add("multiArchiveTag", multiArchiveTag);
}

if (streamMode.HasValue)
{
data.Add("streamMode", streamMode.Value.ToString().ToLower());
Expand Down Expand Up @@ -496,10 +509,18 @@ public Archive StartArchive(string sessionId, string name = "", bool hasVideo =
/// <see cref="OpenTok.AddStreamToArchiveAsync"/> and
/// <see cref="OpenTok.RemoveStreamFromArchiveAsync"/> methods).
/// </param>
/// <param name="multiArchiveTag">
/// Set this to support recording multiple archives for the same session simultaneously.
/// Set this to a unique string for each simultaneous archive of an ongoing session.
/// You must also set this option when manually starting an archive in a session that
/// is automatically archived. If you do not specify a unique multiArchiveTag, you can
/// only record one archive at a time for a given session. See
/// <a href="https://tokbox.com/developer/guides/archiving/#simultaneous-archives">Simultaneous archives</a>.
/// </param>
/// <returns>
/// The Archive object. This object includes properties defining the archive, including the archive ID.
/// </returns>
public async Task<Archive> StartArchiveAsync(string sessionId, string name = "", bool hasVideo = true, bool hasAudio = true, OutputMode outputMode = OutputMode.COMPOSED, string resolution = null, ArchiveLayout layout = null, StreamMode? streamMode = null)
public async Task<Archive> StartArchiveAsync(string sessionId, string name = "", bool hasVideo = true, bool hasAudio = true, OutputMode outputMode = OutputMode.COMPOSED, string resolution = null, ArchiveLayout layout = null, StreamMode? streamMode = null, string multiArchiveTag = null)
{
if (string.IsNullOrEmpty(sessionId))
{
Expand Down Expand Up @@ -540,6 +561,11 @@ public async Task<Archive> StartArchiveAsync(string sessionId, string name = "",
}
data.Add("layout", layout);
}

if (multiArchiveTag is object)
{
data.Add("multiArchiveTag", multiArchiveTag);
}

if (streamMode.HasValue)
{
Expand Down Expand Up @@ -1044,17 +1070,22 @@ public async Task ForceDisconnectAsync(string sessionId, string connectionId)
/// the HLS URL will include a ?DVR query string appended to the end. See <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#dvr">DVR functionality</a></param>
/// <param name="lowLatency">Whether to enable low-latency mode for the HLSstream. Some HLS players do not support low-latency mode.
/// This feature is incompatible with DVR mode HLS broadcasts. See <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#low-latency-hls-broadcasts">Low-latency HLS broadcasts</a></param>
/// <param name="multiBroadcastTag">
/// Set this to support multiple broadcasts for the same session simultaneously.
/// Set this to a unique string for each simultaneous broadcast of an ongoing session. See
/// <a href="https://tokbox.com/developer/guides/broadcast/live-streaming#simultaneous-broadcasts">Simultaneous broadcasts</a>.
/// </param>
/// <returns>The Broadcast object. This object includes properties defining the archive, including the archive ID.</returns>
public Broadcast StartBroadcast(string sessionId, bool hls = true, List<Rtmp> rtmpList = null, string resolution = null,
int maxDuration = 7200, BroadcastLayout layout = null, StreamMode? streamMode = null, bool dvr = false, bool? lowLatency = null)
int maxDuration = 7200, BroadcastLayout layout = null, StreamMode? streamMode = null, bool dvr = false, bool? lowLatency = null, string multiBroadcastTag = null)
{
var data = PrepareStartBroadcastData(sessionId, hls, rtmpList, resolution, maxDuration, layout, streamMode, dvr, lowLatency);
var data = PrepareStartBroadcastData(sessionId, hls, rtmpList, resolution, maxDuration, layout, streamMode, dvr, lowLatency, multiBroadcastTag);
string url = $"v2/project/{ApiKey}/broadcast";
var headers = new Dictionary<string, string> { { "Content-Type", "application/json" } };
string response = Client.Post(url, headers, data);
return OpenTokUtils.GenerateBroadcast(response, ApiKey, ApiSecret, OpenTokServer);
}

/// <summary>
/// Use this method to start a live streaming for an OpenTok session.
/// This broadcasts the session to an HLS (HTTP live streaming) or to RTMP streams.
Expand Down Expand Up @@ -1106,19 +1137,24 @@ public Broadcast StartBroadcast(string sessionId, bool hls = true, List<Rtmp> rt
/// the HLS URL will include a ?DVR query string appended to the end. See <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#dvr">DVR functionality</a></param>
/// <param name="lowLatency">Whether to enable low-latency mode for the HLSstream. Some HLS players do not support low-latency mode.
/// This feature is incompatible with DVR mode HLS broadcasts. See <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#low-latency-hls-broadcasts">Low-latency HLS broadcasts</a></param>
/// <param name="multiBroadcastTag">
/// Set this to support multiple broadcasts for the same session simultaneously.
/// Set this to a unique string for each simultaneous broadcast of an ongoing session. See
/// <a href="https://tokbox.com/developer/guides/broadcast/live-streaming#simultaneous-broadcasts">Simultaneous broadcasts</a>.
/// </param>
/// <returns>The Broadcast object. This object includes properties defining the archive, including the archive ID.</returns>
public async Task<Broadcast> StartBroadcastAsync(string sessionId, bool hls = true, List<Rtmp> rtmpList = null, string resolution = null,
int maxDuration = 7200, BroadcastLayout layout = null, StreamMode? streamMode = null, bool dvr = false, bool? lowLatency = null)
int maxDuration = 7200, BroadcastLayout layout = null, StreamMode? streamMode = null, bool dvr = false, bool? lowLatency = null, string multiBroadcastTag = null)
{
var data = PrepareStartBroadcastData(sessionId, hls, rtmpList, resolution, maxDuration, layout, streamMode, dvr, lowLatency);
var data = PrepareStartBroadcastData(sessionId, hls, rtmpList, resolution, maxDuration, layout, streamMode, dvr, lowLatency, multiBroadcastTag);
string url = $"v2/project/{ApiKey}/broadcast";
var headers = new Dictionary<string, string> { { "Content-Type", "application/json" } };
string response = await Client.PostAsync(url, headers, data);
return OpenTokUtils.GenerateBroadcast(response, ApiKey, ApiSecret, OpenTokServer);
}

private Dictionary<string, object> PrepareStartBroadcastData(string sessionId, bool hls = true, List<Rtmp> rtmpList = null, string resolution = null,
int maxDuration = 7200, BroadcastLayout layout = null, StreamMode? streamMode = null, bool dvr = false, bool? lowLatency = null)
int maxDuration = 7200, BroadcastLayout layout = null, StreamMode? streamMode = null, bool dvr = false, bool? lowLatency = null, string multiBroadcastTag = null)
{
if (string.IsNullOrEmpty(sessionId))
{
Expand Down Expand Up @@ -1189,6 +1225,11 @@ private Dictionary<string, object> PrepareStartBroadcastData(string sessionId, b
data.Add("layout", layout);
}
}

if (multiBroadcastTag is object)
{
data.Add("multiBroadcastTag", multiBroadcastTag);
}

if (streamMode.HasValue)
{
Expand Down
53 changes: 53 additions & 0 deletions OpenTokTest/ArchiveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,53 @@ public async Task StartArchiveAsyncManualStreamMode()
It.IsAny<Dictionary<string, object>>()), Times.Once());
}

[Fact]
public void StartArchiveWithMultiArchiveTag()
{
string responseJson = GetResponseJson();
string multiArchiveTagName = "multiArchiveTag";
string multiArchiveTag = "TestArchiveTag";
var mockClient = new Mock<HttpClient>();
mockClient.Setup(httpClient => httpClient.Post(It.IsAny<string>(), It.IsAny<Dictionary<string, string>>(),
It.IsAny<Dictionary<string, object>>()))
.Returns(responseJson);
OpenTok opentok = this.BuildOpenTok(mockClient.Object);
Archive archive = opentok.StartArchive(SessionId, multiArchiveTag: multiArchiveTag);
Assert.NotNull(archive);
Assert.Equal(multiArchiveTag, archive.MultiArchiveTag);
Assert.NotEqual(Guid.Empty, archive.Id);
mockClient.Verify(
httpClient => httpClient.Post(
It.Is<string>(url => url.Equals("v2/project/" + ApiKey + "/archive")),
It.IsAny<Dictionary<string, string>>(),
It.Is<Dictionary<string, object>>(dictionary =>
dictionary.ContainsKey(multiArchiveTagName) && dictionary[multiArchiveTagName].ToString() == multiArchiveTag)),
Times.Once());
}

[Fact]
public async Task StartArchiveWithMultiArchiveTagAsync()
{
string responseJson = GetResponseJson();
string multiArchiveTagName = "multiArchiveTag";
string multiArchiveTag = "TestArchiveTag";
var mockClient = new Mock<HttpClient>();
mockClient.Setup(httpClient => httpClient.PostAsync(It.IsAny<string>(),
It.IsAny<Dictionary<string, string>>(), It.IsAny<Dictionary<string, object>>()))
.ReturnsAsync(responseJson);
OpenTok opentok = this.BuildOpenTok(mockClient.Object);
Archive archive = await opentok.StartArchiveAsync(SessionId, multiArchiveTag: multiArchiveTag);
Assert.NotNull(archive);
Assert.Equal(multiArchiveTag, archive.MultiArchiveTag);
Assert.NotEqual(Guid.Empty, archive.Id);
mockClient.Verify(
httpClient => httpClient.PostAsync(
It.Is<string>(url => url.Equals("v2/project/" + ApiKey + "/archive")),
It.IsAny<Dictionary<string, string>>(),
It.Is<Dictionary<string, object>>(dictionary =>
dictionary.ContainsKey(multiArchiveTagName) && dictionary[multiArchiveTagName].ToString() == multiArchiveTag)),
Times.Once());
}

// AddStreamToArchive

Expand Down Expand Up @@ -1724,5 +1771,11 @@ public async Task ListArchivesAsyncBadSessionId()

Assert.Equal("Session Id is not valid", exception.Message);
}

private OpenTok BuildOpenTok(HttpClient client) =>
new OpenTok(this.ApiKey, this.ApiSecret)
{
Client = client,
};
}
}
24 changes: 24 additions & 0 deletions OpenTokTest/BroadcastTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,30 @@ public async Task StartBroadcastWithLowLatencyAsync()
Assert.True(hls["lowLatency"]);
Assert.False(hls["dvr"]);
}

[Fact]
public void StartBroadcastWithMultiBroadcastTag()
{
string multiBroadcastTagName = "multiBroadcastTag";
string multiBroadcastTag = "TestBroadcastTag";
string sessionId = "SESSIONID";
string returnString = GetResponseJson();
var mockClient = new Mock<HttpClient>();
mockClient.Setup(httpClient => httpClient.Post(It.IsAny<string>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<Dictionary<string, object>>())).Returns(returnString);
OpenTok opentok = new OpenTok(ApiKey, ApiSecret);
opentok.Client = mockClient.Object;
Broadcast broadcast = opentok.StartBroadcast(sessionId, multiBroadcastTag: multiBroadcastTag);
Assert.NotNull(broadcast);
Assert.Equal(multiBroadcastTag, broadcast.MultiBroadcastTag);
Assert.NotNull(broadcast.Id);
Assert.Equal(Broadcast.BroadcastStatus.STARTED, broadcast.Status);
mockClient.Verify(httpClient => httpClient.Post(
It.Is<string>(url => url.Equals("v2/project/" + ApiKey + "/broadcast")),
It.IsAny<Dictionary<string, string>>(),
It.Is<Dictionary<string, object>>(dictionary =>
dictionary.ContainsKey(multiBroadcastTagName) && dictionary[multiBroadcastTagName].ToString() == multiBroadcastTag)),
Times.Once());
}

// AddStreamToBroadcast

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"createdAt": 1395183243556,
"duration": 0,
"id": "30b3ebf1-ba36-4f5b-8def-6f70d9986fe9",
"name": "",
"partnerId": 123456,
"reason": "",
"sessionId": "1_MX4xMjM0NTZ-flNhdCBNYXIgMTUgMTQ6NDI6MjMgUERUIDIwMTR-MC40OTAxMzAyNX4",
"size": 0,
"status": "started",
"url": null,
"multiArchiveTag": "TestArchiveTag"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"createdAt": 1395183243556,
"duration": 0,
"id": "30b3ebf1-ba36-4f5b-8def-6f70d9986fe9",
"name": "",
"partnerId": 123456,
"reason": "",
"sessionId": "1_MX4xMjM0NTZ-flNhdCBNYXIgMTUgMTQ6NDI6MjMgUERUIDIwMTR-MC40OTAxMzAyNX4",
"size": 0,
"status": "started",
"url": null,
"multiArchiveTag": "TestArchiveTag"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"id": "30b3ebf1-ba36-4f5b-8def-6f70d9986fe9",
"sessionId": "SESSIONID",
"projectId": 123456,
"createdAt": 1395183243556,
"updatedAt": 1395183243556,
"resolution": "640x480",
"status": "started",
"broadcastUrls": {
"hls": "http://server/fakepath/playlist.m3u8"
},
"settings": { "hls": { "lowLatency": false } },
"multiBroadcastTag": "TestBroadcastTag"
}
9 changes: 9 additions & 0 deletions OpenTokTest/OpenTokTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@
<Content Include="Data\ArchiveTests\ListArchives-response.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Data\ArchiveTests\StartArchiveWithMultiArchiveTag-response.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Data\ArchiveTests\StartArchiveWithMultiArchiveTagAsync-response.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Data\ArchiveTests\StopArchiveAsyncFromArchiveObject-response.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand Down Expand Up @@ -191,6 +197,9 @@
<Content Include="Data\BroadcastTests\GetBroadcast-response.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Data\BroadcastTests\StartBroadcastWithMultiBroadcastTag-response.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Data\BroadcastTests\StartBroadcastAsync-response.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand Down