diff --git a/OpenTok/Archive.cs b/OpenTok/Archive.cs index 249a80c1..909b027a 100644 --- a/OpenTok/Archive.cs +++ b/OpenTok/Archive.cs @@ -109,6 +109,7 @@ internal void CopyArchive(Archive archive) OutputMode = archive.OutputMode; Resolution = archive.Resolution; StreamMode = archive.StreamMode; + MultiArchiveTag = archive.MultiArchiveTag; } /// @@ -201,6 +202,11 @@ internal void CopyArchive(Archive archive) /// public StreamMode StreamMode { get; set; } + /// + /// The unique tag for simultaneous archives (if one was set). + /// + public string MultiArchiveTag { get; set; } + /// /// Stops the OpenTok archive if it is being recorded. /// diff --git a/OpenTok/Broadcast.cs b/OpenTok/Broadcast.cs index 075623cb..6637c0d0 100644 --- a/OpenTok/Broadcast.cs +++ b/OpenTok/Broadcast.cs @@ -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; @@ -193,6 +194,12 @@ internal void CopyBroadcast(Broadcast broadcast) /// [JsonProperty("settings")] public BroadcastSettings Settings { get; set; } + + /// + /// The unique tag for simultaneous broadcasts (if one was set). + /// + [JsonProperty("multiBroadcastTag")] + public string MultiBroadcastTag { get; set; } /// /// Stops the live broadcasting if it is started. diff --git a/OpenTok/OpenTok.cs b/OpenTok/OpenTok.cs index e98dbe3e..e8f2064c 100644 --- a/OpenTok/OpenTok.cs +++ b/OpenTok/OpenTok.cs @@ -394,10 +394,18 @@ public string GenerateToken(string sessionId, Role role = Role.PUBLISHER, double /// and /// methods). /// + /// + /// 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 + /// Simultaneous archives. + /// /// /// The Archive object. This object includes properties defining the archive, including the archive ID. /// - 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)) { @@ -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()); @@ -496,10 +509,18 @@ public Archive StartArchive(string sessionId, string name = "", bool hasVideo = /// and /// methods). /// + /// + /// 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 + /// Simultaneous archives. + /// /// /// The Archive object. This object includes properties defining the archive, including the archive ID. /// - public async Task 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 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)) { @@ -540,6 +561,11 @@ public async Task StartArchiveAsync(string sessionId, string name = "", } data.Add("layout", layout); } + + if (multiArchiveTag is object) + { + data.Add("multiArchiveTag", multiArchiveTag); + } if (streamMode.HasValue) { @@ -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 DVR functionality /// 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 Low-latency HLS broadcasts + /// + /// 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 + /// Simultaneous broadcasts. + /// /// The Broadcast object. This object includes properties defining the archive, including the archive ID. public Broadcast StartBroadcast(string sessionId, bool hls = true, List 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 { { "Content-Type", "application/json" } }; string response = Client.Post(url, headers, data); return OpenTokUtils.GenerateBroadcast(response, ApiKey, ApiSecret, OpenTokServer); } - + /// /// 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. @@ -1106,11 +1137,16 @@ public Broadcast StartBroadcast(string sessionId, bool hls = true, List rt /// the HLS URL will include a ?DVR query string appended to the end. See DVR functionality /// 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 Low-latency HLS broadcasts + /// + /// 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 + /// Simultaneous broadcasts. + /// /// The Broadcast object. This object includes properties defining the archive, including the archive ID. public async Task StartBroadcastAsync(string sessionId, bool hls = true, List 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 { { "Content-Type", "application/json" } }; string response = await Client.PostAsync(url, headers, data); @@ -1118,7 +1154,7 @@ public async Task StartBroadcastAsync(string sessionId, bool hls = tr } private Dictionary PrepareStartBroadcastData(string sessionId, bool hls = true, List 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)) { @@ -1189,6 +1225,11 @@ private Dictionary PrepareStartBroadcastData(string sessionId, b data.Add("layout", layout); } } + + if (multiBroadcastTag is object) + { + data.Add("multiBroadcastTag", multiBroadcastTag); + } if (streamMode.HasValue) { diff --git a/OpenTokTest/ArchiveTests.cs b/OpenTokTest/ArchiveTests.cs index afcb98b8..c0ac0e98 100644 --- a/OpenTokTest/ArchiveTests.cs +++ b/OpenTokTest/ArchiveTests.cs @@ -730,6 +730,53 @@ public async Task StartArchiveAsyncManualStreamMode() It.IsAny>()), Times.Once()); } + [Fact] + public void StartArchiveWithMultiArchiveTag() + { + string responseJson = GetResponseJson(); + string multiArchiveTagName = "multiArchiveTag"; + string multiArchiveTag = "TestArchiveTag"; + var mockClient = new Mock(); + mockClient.Setup(httpClient => httpClient.Post(It.IsAny(), It.IsAny>(), + It.IsAny>())) + .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(url => url.Equals("v2/project/" + ApiKey + "/archive")), + It.IsAny>(), + It.Is>(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(); + mockClient.Setup(httpClient => httpClient.PostAsync(It.IsAny(), + It.IsAny>(), It.IsAny>())) + .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(url => url.Equals("v2/project/" + ApiKey + "/archive")), + It.IsAny>(), + It.Is>(dictionary => + dictionary.ContainsKey(multiArchiveTagName) && dictionary[multiArchiveTagName].ToString() == multiArchiveTag)), + Times.Once()); + } // AddStreamToArchive @@ -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, + }; } } \ No newline at end of file diff --git a/OpenTokTest/BroadcastTests.cs b/OpenTokTest/BroadcastTests.cs index a39ff3ab..487472cc 100644 --- a/OpenTokTest/BroadcastTests.cs +++ b/OpenTokTest/BroadcastTests.cs @@ -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(); + mockClient.Setup(httpClient => httpClient.Post(It.IsAny(), It.IsAny>(), It.IsAny>())).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(url => url.Equals("v2/project/" + ApiKey + "/broadcast")), + It.IsAny>(), + It.Is>(dictionary => + dictionary.ContainsKey(multiBroadcastTagName) && dictionary[multiBroadcastTagName].ToString() == multiBroadcastTag)), + Times.Once()); + } // AddStreamToBroadcast diff --git a/OpenTokTest/Data/ArchiveTests/StartArchiveWithMultiArchiveTag-response.json b/OpenTokTest/Data/ArchiveTests/StartArchiveWithMultiArchiveTag-response.json new file mode 100644 index 00000000..2c195261 --- /dev/null +++ b/OpenTokTest/Data/ArchiveTests/StartArchiveWithMultiArchiveTag-response.json @@ -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" +} \ No newline at end of file diff --git a/OpenTokTest/Data/ArchiveTests/StartArchiveWithMultiArchiveTagAsync-response.json b/OpenTokTest/Data/ArchiveTests/StartArchiveWithMultiArchiveTagAsync-response.json new file mode 100644 index 00000000..2c195261 --- /dev/null +++ b/OpenTokTest/Data/ArchiveTests/StartArchiveWithMultiArchiveTagAsync-response.json @@ -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" +} \ No newline at end of file diff --git a/OpenTokTest/Data/BroadcastTests/StartBroadcastWithMultiBroadcastTag-response.json b/OpenTokTest/Data/BroadcastTests/StartBroadcastWithMultiBroadcastTag-response.json new file mode 100644 index 00000000..4b603de3 --- /dev/null +++ b/OpenTokTest/Data/BroadcastTests/StartBroadcastWithMultiBroadcastTag-response.json @@ -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" +} \ No newline at end of file diff --git a/OpenTokTest/OpenTokTest.csproj b/OpenTokTest/OpenTokTest.csproj index 95303469..8d0efa45 100644 --- a/OpenTokTest/OpenTokTest.csproj +++ b/OpenTokTest/OpenTokTest.csproj @@ -77,6 +77,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -191,6 +197,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest