diff --git a/CHANGELOG.md b/CHANGELOG.md index 219f097de..4d68aa824 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). # [8.14.0] - 2024-11-?? - Close HTTP responses to prevent resource leaks - Added `RedactResponseException` and deprecated `VonageBadRequestException` +- Added `maxBitrate` to `com.vonage.client.video.Archive` # [8.13.0] - 2024-10-28 - Added support for Verify custom templates diff --git a/src/main/java/com/vonage/client/VonageClient.java b/src/main/java/com/vonage/client/VonageClient.java index dcb16c46f..6cb9e9c3b 100644 --- a/src/main/java/com/vonage/client/VonageClient.java +++ b/src/main/java/com/vonage/client/VonageClient.java @@ -39,10 +39,7 @@ import com.vonage.client.voice.VoiceClient; import org.apache.http.client.HttpClient; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Objects; +import java.nio.file.*; import java.util.UUID; /** diff --git a/src/main/java/com/vonage/client/video/Archive.java b/src/main/java/com/vonage/client/video/Archive.java index 38cdee5b3..99321e28a 100644 --- a/src/main/java/com/vonage/client/video/Archive.java +++ b/src/main/java/com/vonage/client/video/Archive.java @@ -49,6 +49,9 @@ protected Archive(Builder builder) { hasVideo = builder.hasVideo; resolution = builder.resolution; streamMode = builder.streamMode; + if ((maxBitrate = builder.maxBitrate) != null && (maxBitrate < 100000 || maxBitrate > 6000000)) { + throw new IllegalArgumentException("Maximum bitrate must be between 100000 and 6000000."); + } } /** @@ -300,6 +303,20 @@ public Builder multiArchiveTag(String multiArchiveTag) { return this; } + /** + * (OPTIONAL) The maximum bitrate for the archive, in bits per second. + * This must be between 100000 and 6000000. + * + * @param maxBitrate The maximum bitrate as an int. + * + * @return This builder. + * @since 8.14.0 + */ + public Builder maxBitrate(int maxBitrate) { + this.maxBitrate = maxBitrate; + return this; + } + /** * Builds the {@linkplain Archive} object with this builder's settings. * diff --git a/src/main/java/com/vonage/client/video/Broadcast.java b/src/main/java/com/vonage/client/video/Broadcast.java index 69e38004e..49737e288 100644 --- a/src/main/java/com/vonage/client/video/Broadcast.java +++ b/src/main/java/com/vonage/client/video/Broadcast.java @@ -33,7 +33,6 @@ public class Broadcast extends StreamComposition { private String multiBroadcastTag; @JsonProperty("updatedAt") private Long updatedAt; @JsonProperty("maxDuration") private Integer maxDuration; - private Integer maxBitrate; private BroadcastStatus status; private BroadcastUrls broadcastUrls; private Settings settings; @@ -52,7 +51,6 @@ protected Broadcast(Builder builder) { if ((maxDuration = builder.maxDuration) != null && (maxDuration < 60 || maxDuration > 36000)) { throw new IllegalArgumentException("maxDuration must be between 60 seconds and 10 hours."); } - maxBitrate = builder.maxBitrate; multiBroadcastTag = builder.multiBroadcastTag; } @@ -125,16 +123,6 @@ public Duration getMaxDuration() { return maxDuration != null ? Duration.ofSeconds(maxDuration) : null; } - /** - * The maximum bitrate of the broadcast in bits per second (if one was set). - * - * @return The maximum bits per second as an integer, or {@code null} if unset. - */ - @JsonProperty("maxBitrate") - public Integer getMaxBitrate() { - return maxBitrate; - } - /** * The status of the broadcast at the time this object was created. * @@ -191,7 +179,7 @@ public static Builder builder(String sessionId) { public static class Builder extends StreamComposition.Builder { private final List rtmps = new ArrayList<>(); private Hls hls; - private Integer maxDuration, maxBitrate; + private Integer maxDuration; private String multiBroadcastTag; Builder(String sessionId) { diff --git a/src/main/java/com/vonage/client/video/StreamComposition.java b/src/main/java/com/vonage/client/video/StreamComposition.java index b2ac83240..8f04158d5 100644 --- a/src/main/java/com/vonage/client/video/StreamComposition.java +++ b/src/main/java/com/vonage/client/video/StreamComposition.java @@ -38,6 +38,7 @@ public abstract class StreamComposition extends JsonableBaseObject { @JsonProperty("hasAudio") protected Boolean hasAudio; @JsonProperty("createdAt") protected Long createdAt; @JsonProperty("streams") protected List streams; + @JsonProperty("maxBitrate") protected Integer maxBitrate; protected StreamComposition() { } @@ -49,6 +50,7 @@ protected StreamComposition(Builder builder) { hasAudio = builder.hasAudio; hasVideo = builder.hasVideo; layout = builder.layout; + maxBitrate = builder.maxBitrate; } /** @@ -134,6 +136,15 @@ protected StreamCompositionLayout getLayout() { return layout; } + /** + * The maximum bitrate for the stream in bits per second (if one was set). + * + * @return The maximum bits per second as an integer, or {@code null} if unset. + */ + public Integer getMaxBitrate() { + return maxBitrate; + } + /** * The time at which this composition was started or created, in milliseconds since the Unix epoch. * @@ -156,6 +167,7 @@ public Instant getCreatedAt() { protected static abstract class Builder { protected String sessionId; + protected Integer maxBitrate; protected Boolean hasAudio, hasVideo; protected Resolution resolution; protected StreamMode streamMode; diff --git a/src/test/java/com/vonage/client/video/ArchiveTest.java b/src/test/java/com/vonage/client/video/ArchiveTest.java index 4056b0a9e..748b99ed8 100644 --- a/src/test/java/com/vonage/client/video/ArchiveTest.java +++ b/src/test/java/com/vonage/client/video/ArchiveTest.java @@ -28,9 +28,11 @@ public void testSerializeAllParams() { String sessionId = "flR1ZSBPY3QgMjkgMTI6MTM6MjMgUERUIDIwMTN"; String name = "Test archive", multiArchiveTag = "DemoArchive_TagName"; StreamCompositionLayout layout = StreamCompositionLayout.builder(ScreenLayoutType.VERTICAL).build(); + int maxBitrate = 321400; Archive request = Archive.builder(sessionId) - .name(name).hasAudio(true).hasVideo(true) + .name(name).maxBitrate(maxBitrate) + .hasAudio(true).hasVideo(true) .resolution(Resolution.HD_LANDSCAPE) .outputMode(OutputMode.COMPOSED) .streamMode(StreamMode.AUTO).layout(layout) @@ -47,6 +49,7 @@ public void testSerializeAllParams() { assertTrue(json.contains("\"layout\":{\"type\":\"verticalPresentation\"}")); assertTrue(json.contains("\"hasVideo\":true")); assertTrue(json.contains("\"hasAudio\":true")); + assertTrue(json.contains("\"maxBitrate\":"+maxBitrate)); } @Test @@ -94,6 +97,16 @@ public void testConstructEmptySessionId() { assertThrows(IllegalArgumentException.class, () -> Archive.builder("").build()); } + @Test + public void testMaxBitrateBounds() { + var builder = Archive.builder("SESSION_ID"); + int min = 100000, max = 6000000; + assertThrows(IllegalArgumentException.class, () -> builder.maxBitrate(min-1).build()); + assertEquals(min, builder.maxBitrate(min).build().getMaxBitrate()); + assertThrows(IllegalArgumentException.class, () -> builder.maxBitrate(max+1).build()); + assertEquals(max, builder.maxBitrate(max).build().getMaxBitrate()); + } + @Test public void testFromJsonInvalid() { assertThrows(VonageResponseParseException.class, () -> Archive.fromJson("{malformed]")); diff --git a/src/test/java/com/vonage/client/video/BroadcastTest.java b/src/test/java/com/vonage/client/video/BroadcastTest.java index f6e5da853..2f07747f0 100644 --- a/src/test/java/com/vonage/client/video/BroadcastTest.java +++ b/src/test/java/com/vonage/client/video/BroadcastTest.java @@ -61,8 +61,8 @@ public void testSerializeAndParseAllFields() { String expectedRequestedJson = "{\"sessionId\":\""+sessionId+"\",\"streamMode\":\""+streamMode + "\",\"resolution\":\""+resolution+"\",\"layout\":{\"type\":\"custom\",\"stylesheet\":\"" + - stylesheet+"\"},\"multiBroadcastTag\":\""+multiBroadcastTag + "\",\"maxDuration\":" + - maxDuration+",\"maxBitrate\":"+maxBitrate+",\"outputs\":{" + "\"rtmp\":[{\"id\":\"" + + stylesheet+"\"},\"maxBitrate\":"+maxBitrate+",\"multiBroadcastTag\":\""+multiBroadcastTag + + "\",\"maxDuration\":" + maxDuration+",\"outputs\":{" + "\"rtmp\":[{\"id\":\"" + rtmp1.getId()+"\",\"streamName\":\""+rtmp1.getStreamName()+"\",\"serverUrl\":\"" + rtmp1.getServerUrl()+"\"}],\"hls\":{\"dvr\":"+hls.dvr()+",\"lowLatency\":" + hls.lowLatency()+"}}}", requestJson = request.toJson(); @@ -133,9 +133,10 @@ public void testSerializeAndParseAllFields() { "\"layout\":{" + "\"type\":\"custom\"," + "\"stylesheet\":\"stream.instructor {position: absolute; width: 100%; height:50%;}\"" + - "},\"multiBroadcastTag\":\"Tag for multiple broadcasts\"," + - "\"maxDuration\":14500," + + "}," + "\"maxBitrate\":300000," + + "\"multiBroadcastTag\":\"Tag for multiple broadcasts\"," + + "\"maxDuration\":14500," + "\"outputs\":{" + "\"rtmp\":[{" + "\"id\":\"bar\"," + @@ -160,10 +161,10 @@ public void testSerializeAndParseAllFields() { "\"hasAudio\":"+hasAudio+"," + "\"createdAt\":"+createdAt+"," + "\"streams\":[{}]," + + "\"maxBitrate\":"+maxBitrate+"," + "\"multiBroadcastTag\":\""+multiBroadcastTag+"\"," + "\"updatedAt\":"+updatedAt+"," + "\"maxDuration\":"+maxDuration+"," + - "\"maxBitrate\":"+maxBitrate+"," + "\"status\":\""+status+"\"," + "\"broadcastUrls\":{\"hls\":\""+hlsUrl+"\"," + "\"rtmp\":[{\"id\":\""+rtmp1.getId()+"\",\"streamName\":\""+rtmp1.getStreamName()+"\"," + diff --git a/src/test/java/com/vonage/client/video/VideoClientTest.java b/src/test/java/com/vonage/client/video/VideoClientTest.java index 589d6eb16..98337dbb4 100644 --- a/src/test/java/com/vonage/client/video/VideoClientTest.java +++ b/src/test/java/com/vonage/client/video/VideoClientTest.java @@ -49,6 +49,7 @@ public class VideoClientTest extends AbstractClientTest { archiveJson = "{\n" + " \"createdAt\": 1384221730000,\n" + " \"duration\": 5049,\n" + + " \"maxBitrate\": 1234567,\n" + " \"hasAudio\": true,\n" + " \"hasVideo\": true,\n" + " \"id\": \""+archiveId+"\",\n" + @@ -191,6 +192,7 @@ static void assertArchiveEqualsExpectedJson(Archive response) { assertEquals(Instant.ofEpochSecond(1384221730L), response.getCreatedAt()); assertEquals(Integer.valueOf(5049), response.getDurationSeconds()); assertEquals(Duration.ofSeconds(5049), response.getDuration()); + assertEquals(Integer.valueOf(1234567), response.getMaxBitrate()); assertTrue(response.hasAudio()); assertTrue(response.hasVideo()); assertEquals(archiveId, response.getId().toString());