001/* 002 * Copyright 2024 Vonage 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.vonage.client.video; 017 018import com.vonage.client.DynamicEndpoint; 019import com.vonage.client.HttpWrapper; 020import com.vonage.client.RestEndpoint; 021import com.vonage.client.VonageClient; 022import com.vonage.client.auth.JWTAuthMethod; 023import com.vonage.client.auth.ApiKeyHeaderAuthMethod; 024import com.vonage.client.common.HttpMethod; 025import com.vonage.jwt.Jwt; 026import java.time.ZonedDateTime; 027import java.util.*; 028import java.util.function.Function; 029import java.util.function.Supplier; 030 031/** 032 * A client for using the Vonage Video API. The standard way to obtain an instance of this class is to use 033 * {@link VonageClient#getVideoClient()}. 034 * 035 * @since 8.0.0-beta1 036 */ 037public class VideoClient { 038 final Supplier<? extends Jwt.Builder> newJwtSupplier; 039 040 final RestEndpoint<CreateSessionRequest, CreateSessionResponse[]> createSession; 041 final RestEndpoint<String, ListStreamsResponse> listStreams; 042 final RestEndpoint<SessionResourceRequestWrapper, GetStreamResponse> getStream; 043 final RestEndpoint<SetStreamLayoutRequest, Void> setStreamLayout; 044 final RestEndpoint<SignalRequest, Void> signal, signalAll; 045 final RestEndpoint<SessionResourceRequestWrapper, Void> forceDisconnect, muteStream; 046 final RestEndpoint<MuteSessionRequest, ProjectDetails> muteSession; 047 final RestEndpoint<SipDialRequest, SipDialResponse> sipDial; 048 final RestEndpoint<SendDtmfRequest, Void> sendDtmfToSession, sendDtmfToConnection; 049 final RestEndpoint<ListStreamCompositionsRequest, ListArchivesResponse> listArchives; 050 final RestEndpoint<String, Archive> getArchive, stopArchive; 051 final RestEndpoint<Archive, Archive> createArchive; 052 final RestEndpoint<StreamCompositionLayout, Void> updateArchiveLayout, updateBroadcastLayout; 053 final RestEndpoint<PatchComposedStreamsRequest, Void> patchArchiveStream, patchBroadcastStream; 054 final RestEndpoint<String, Void> deleteArchive, stopCaptions, stopRender; 055 final RestEndpoint<ListStreamCompositionsRequest, ListBroadcastsResponse> listBroadcasts; 056 final RestEndpoint<String, Broadcast> getBroadcast, stopBroadcast; 057 final RestEndpoint<Broadcast, Broadcast> createBroadcast; 058 final RestEndpoint<CaptionsRequest, CaptionsResponse> startCaptions; 059 final RestEndpoint<ConnectRequest, ConnectResponse> connect; 060 final RestEndpoint<RenderRequest, RenderResponse> startRender; 061 final RestEndpoint<String, RenderResponse> getRender; 062 final RestEndpoint<ListStreamCompositionsRequest, ListRendersResponse> listRenders; 063 064 /** 065 * Constructor. 066 * 067 * @param wrapper (REQUIRED) shared HTTP wrapper object used for making REST calls. 068 */ 069 public VideoClient(HttpWrapper wrapper) { 070 super(); 071 Supplier<JWTAuthMethod> jwtAuthGetter = () -> wrapper.getAuthCollection().getAuth(JWTAuthMethod.class); 072 Supplier<String> appIdGetter = () -> jwtAuthGetter.get().getApplicationId(); 073 newJwtSupplier = () -> jwtAuthGetter.get().newJwt(); 074 075 @SuppressWarnings("unchecked") 076 class Endpoint<T, R> extends DynamicEndpoint<T, R> { 077 Endpoint(Function<T, String> pathGetter, HttpMethod method, R... type) { 078 super(DynamicEndpoint.<T, R> builder(type) 079 .authMethod(JWTAuthMethod.class, ApiKeyHeaderAuthMethod.class) 080 .responseExceptionType(VideoResponseException.class) 081 .requestMethod(method).wrapper(wrapper).pathGetter((de, req) -> { 082 String base = de.getHttpWrapper().getHttpConfig().getVideoBaseUri(); 083 String end = pathGetter.apply(req); 084 String mid = end.startsWith("/") ? "" : "/v2/project/" + appIdGetter.get() + "/"; 085 return base + mid + end; 086 }) 087 ); 088 } 089 } 090 091 createSession = new Endpoint<>(req -> "/session/create", HttpMethod.POST); 092 listStreams = new Endpoint<>(req -> "session/"+req+"/stream", HttpMethod.GET); 093 setStreamLayout = new Endpoint<>(req -> "session/"+req.sessionId+"/stream", HttpMethod.PUT); 094 getStream = new Endpoint<>(req -> "session/"+req.sessionId+"/stream/"+req.resourceId, HttpMethod.GET); 095 signalAll = new Endpoint<>(req -> "session/"+req.sessionId+"/signal", HttpMethod.POST); 096 signal = new Endpoint<>(req -> "session/"+req.sessionId+"/connection/"+req.connectionId+"/signal", HttpMethod.POST); 097 forceDisconnect = new Endpoint<>(req -> "session/"+req.sessionId+"/connection/"+req.resourceId, HttpMethod.DELETE); 098 muteStream = new Endpoint<>(req -> "session/"+req.sessionId+"/stream/"+req.resourceId+"/mute", HttpMethod.POST); 099 muteSession = new Endpoint<>(req -> "session/"+req.sessionId+"/mute", HttpMethod.POST); 100 updateArchiveLayout = new Endpoint<>(req -> "archive/"+req.id+"/layout", HttpMethod.PUT); 101 deleteArchive = new Endpoint<>(req -> "archive/"+req, HttpMethod.DELETE); 102 patchArchiveStream = new Endpoint<>(req -> "archive/"+req.id+"/streams", HttpMethod.PATCH); 103 stopArchive = new Endpoint<>(req -> "archive/"+req+"/stop", HttpMethod.POST); 104 createArchive = new Endpoint<>(req -> "archive", HttpMethod.POST); 105 listArchives = new Endpoint<>(req -> "archive", HttpMethod.GET); 106 getArchive = new Endpoint<>(req -> "archive/"+req, HttpMethod.GET); 107 sendDtmfToConnection = new Endpoint<>(req -> "session/"+req.sessionId+"/connection/"+req.connectionId+"/play-dtmf", HttpMethod.POST); 108 sendDtmfToSession = new Endpoint<>(req -> "session/"+req.sessionId+"/play-dtmf", HttpMethod.POST); 109 listBroadcasts = new Endpoint<>(req -> "broadcast", HttpMethod.GET); 110 createBroadcast = new Endpoint<>(req -> "broadcast", HttpMethod.POST); 111 getBroadcast = new Endpoint<>(req -> "broadcast/"+req, HttpMethod.GET); 112 stopBroadcast = new Endpoint<>(req -> "broadcast/"+req+"/stop", HttpMethod.POST); 113 updateBroadcastLayout = new Endpoint<>(req -> "broadcast/"+req.id+"/layout", HttpMethod.PUT); 114 patchBroadcastStream = new Endpoint<>(req -> "broadcast/"+req.id+"/streams", HttpMethod.PATCH); 115 sipDial = new Endpoint<>(req -> "dial", HttpMethod.POST); 116 startCaptions = new Endpoint<>(req -> "captions", HttpMethod.POST); 117 stopCaptions = new Endpoint<>(req -> "captions/"+req+"/stop", HttpMethod.POST); 118 connect = new Endpoint<>(req -> "connect", HttpMethod.POST); 119 startRender = new Endpoint<>(req -> "render", HttpMethod.POST); 120 listRenders = new Endpoint<>(req -> "render", HttpMethod.GET); 121 getRender = new Endpoint<>(req -> "render/"+req, HttpMethod.GET); 122 stopRender = new Endpoint<>(req -> "render/"+req, HttpMethod.DELETE); 123 } 124 125 private String validateId(String param, String name, boolean uuid) { 126 if (param == null || param.isEmpty()) { 127 throw new IllegalArgumentException(name+" ID is required."); 128 } 129 if (uuid) { 130 // Validate by construction 131 Objects.requireNonNull(UUID.fromString(param)); 132 } 133 return param; 134 } 135 136 private String validateSessionId(String sessionId) { 137 return validateId(sessionId, "Session", false); 138 } 139 140 private String validateConnectionId(String connectionId) { 141 return validateId(connectionId, "Connection", true); 142 } 143 144 private String validateStreamId(String streamId) { 145 return validateId(streamId, "Stream", true); 146 } 147 148 private String validateArchiveId(String archiveId) { 149 return validateId(archiveId, "Archive", true); 150 } 151 152 private String validateBroadcastId(String broadcastId) { 153 return validateId(broadcastId, "Broadcast", true); 154 } 155 156 private String validateRenderId(String renderId) { 157 return validateId(renderId, "Render", true); 158 } 159 160 private <T> T validateRequest(T request) { 161 if (request == null) { 162 throw new IllegalArgumentException("Request properties are required."); 163 } 164 return request; 165 } 166 167 /** 168 * Generates a signed JSON Web Token which can be passed to the client. 169 * 170 * @param sessionId The session ID. 171 * @param options Configuration parameters (claims) of the token, e.g. role, expiry time etc. 172 * 173 * @return The JWT with the specified properties, as a raw string. 174 * 175 * @since 8.0.0-beta2 176 */ 177 public String generateToken(String sessionId, TokenOptions options) { 178 Jwt.Builder jwtBuilder = newJwtSupplier.get(); 179 if (options == null) { 180 options = TokenOptions.builder().build(); 181 } 182 options.addClaims(jwtBuilder); 183 return jwtBuilder 184 .addClaim("session_id", validateSessionId(sessionId)) 185 .addClaim("scope", "session.connect") 186 .issuedAt(ZonedDateTime.now()) 187 .build().generate(); 188 } 189 190 /** 191 * Generates a signed JSON Web Token which can be passed to the client, using the default token options. 192 * 193 * @param sessionId The session ID. 194 * 195 * @return The JWT with the default properties, as a raw string. 196 * 197 * @see #generateToken(String, TokenOptions) 198 * @since 8.0.0-beta2 199 */ 200 public String generateToken(String sessionId) { 201 return generateToken(sessionId, null); 202 } 203 204 /** 205 * Generate a new session, using default properties. 206 * 207 * @return Details of the created session. 208 * @see #createSession(CreateSessionRequest) 209 */ 210 public CreateSessionResponse createSession() { 211 return createSession(null); 212 } 213 214 /** 215 * Generate a new session. 216 * 217 * @param request (OPTIONAL) The session properties. 218 * @return Details of the created session. 219 */ 220 public CreateSessionResponse createSession(CreateSessionRequest request) { 221 CreateSessionResponse[] response = createSession.execute(request); 222 return response == null || response.length == 0 ? new CreateSessionResponse() : response[0]; 223 } 224 225 /** 226 * Use this method to get information on all Vonage Video streams in a session. 227 * 228 * @param sessionId The session ID. 229 * @return Details for each stream, as a List. 230 * @see #getStream(String, String) 231 */ 232 public List<GetStreamResponse> listStreams(String sessionId) { 233 return listStreams.execute(validateSessionId(sessionId)).getItems(); 234 } 235 236 /** 237 * Use this method to get information on a Vonage Video stream. For example, you can call this method to get 238 * information about layout classes used by a Vonage Video stream. The layout classes define how the stream is 239 * displayed in the layout of a broadcast stream. 240 * 241 * @param sessionId The session ID. 242 * @param streamId ID of the stream to retrieve. 243 * @return Details of the requested stream. 244 */ 245 public GetStreamResponse getStream(String sessionId, String streamId) { 246 return getStream.execute(new SessionResourceRequestWrapper( 247 validateSessionId(sessionId), 248 validateStreamId(streamId) 249 )); 250 } 251 252 /** 253 * Use this method to change layout classes for a Vonage Video stream. The layout classes define how the stream is 254 * displayed in the layout of a composed Vonage Video archive. 255 * 256 * @param sessionId The session ID. 257 * @param streams The stream layouts to change. 258 */ 259 public void setStreamLayout(String sessionId, List<SessionStream> streams) { 260 setStreamLayout.execute(new SetStreamLayoutRequest(validateSessionId(sessionId), streams)); 261 } 262 263 /** 264 * Use this method to change layout classes for a Vonage Video stream. The layout classes define how the stream is 265 * displayed in the layout of a composed Vonage Video archive. 266 * 267 * @param sessionId The session ID. 268 * @param streams The stream layouts to change. 269 * 270 * @see #setStreamLayout(String, List) 271 * @since 8.0.0-beta2 272 */ 273 public void setStreamLayout(String sessionId, SessionStream... streams) { 274 setStreamLayout(sessionId, Arrays.asList(streams)); 275 } 276 277 /** 278 * Sends signals to all participants in an active Vonage Video session. 279 * 280 * @param sessionId The session ID. 281 * @param request Signal payload. 282 */ 283 public void signalAll(String sessionId, SignalRequest request) { 284 validateRequest(request); 285 request.sessionId = validateSessionId(sessionId); 286 signalAll.execute(request); 287 } 288 289 /** 290 * Sends signal to a specific participant in an active Vonage Video session. 291 * 292 * @param sessionId The session ID. 293 * @param connectionId Specific publisher connection ID. 294 * @param request Signal payload. 295 */ 296 public void signal(String sessionId, String connectionId, SignalRequest request) { 297 validateRequest(request); 298 request.sessionId = validateSessionId(sessionId); 299 request.connectionId = validateConnectionId(connectionId); 300 signal.execute(request); 301 } 302 303 /** 304 * Force a client to disconnect from a session. 305 * 306 * @param sessionId The session ID. 307 * @param connectionId Specific publisher connection ID. 308 */ 309 public void forceDisconnect(String sessionId, String connectionId) { 310 forceDisconnect.execute(new SessionResourceRequestWrapper( 311 validateSessionId(sessionId), 312 validateConnectionId(connectionId) 313 )); 314 } 315 316 /** 317 * Force mute a specific publisher stream. 318 * 319 * @param sessionId The session ID. 320 * @param streamId ID of the stream to mute. 321 */ 322 public void muteStream(String sessionId, String streamId) { 323 muteStream.execute(new SessionResourceRequestWrapper( 324 validateSessionId(sessionId), 325 validateStreamId(streamId) 326 )); 327 } 328 329 /** 330 * Force all streams (except for an optional list of streams) in a session to mute published audio. 331 * You can also use this method to disable the force mute state of a session. 332 * 333 * @param sessionId The session ID. 334 * 335 * @param active Whether to mute streams in the session (true) and enable the mute state of the session, or to 336 * disable the mute state of the session (false). With the mute state enabled (true), all current and future 337 * streams published to the session (except streams in "excludedStreamIds") are muted. If this is 338 * set to {@code false}, future streams published to the session are not muted (but any existing muted 339 * streams will remain muted). 340 * 341 * @param excludedStreamIds (OPTIONAL) The stream IDs for streams that should not be muted. 342 * If you omit this, all streams in the session will be muted. This only applies when the "active" property is set 343 * {@code true}. When the "active" property is set to {@code false}, it is ignored. 344 * 345 * @return Details about the Vonage Video project. 346 */ 347 public ProjectDetails muteSession(String sessionId, boolean active, Collection<String> excludedStreamIds) { 348 return muteSession.execute(new MuteSessionRequest( 349 validateSessionId(sessionId), 350 active, 351 excludedStreamIds 352 )); 353 } 354 355 /** 356 * Force all streams (except for an optional list of streams) in a session to mute published audio. 357 * You can also use this method to disable the force mute state of a session. 358 * 359 * @param sessionId The session ID. 360 * 361 * @param active Whether to mute streams in the session (true) and enable the mute state of the session, or to 362 * disable the mute state of the session (false). With the mute state enabled (true), all current and future 363 * streams published to the session (except streams in "excludedStreamIds") are muted. If this is 364 * set to {@code false}, future streams published to the session are not muted (but any existing muted 365 * streams will remain muted). 366 * 367 * @param excludedStreamIds (OPTIONAL) The stream IDs for streams that should not be muted. 368 * If you omit this, all streams in the session will be muted. This only applies when the "active" property is set 369 * {@code true}. When the "active" property is set to {@code false}, it is ignored. 370 * 371 * @see #muteSession(String, boolean, Collection) 372 */ 373 public void muteSession(String sessionId, boolean active, String... excludedStreamIds) { 374 muteSession(sessionId, active, 375 excludedStreamIds != null && excludedStreamIds.length > 0 ? 376 Arrays.asList(excludedStreamIds) : null 377 ); 378 } 379 380 /** 381 * Use this method to connect your SIP platform to a Vonage video session. 382 * The audio from your end of the SIP call is added to the video session as an audio-only stream. The Vonage 383 * Media Router mixes audio from other streams in the session and sends the mixed audio to your SIP endpoint. 384 * 385 * <p> 386 * The call ends when your SIP server sends a BYE message (to terminate the call). You can also end a call 387 * using {@link #forceDisconnect(String, String)}. The Vonage Video SIP gateway automatically ends a call after 388 * 5 minutes of inactivity (5 minutes without media received). Also, as a security measure, the Vonage Video SIP 389 * gateway closes any SIP call that lasts longer than 6 hours. 390 * 391 * <p> 392 * The SIP interconnect feature requires that you use video session that uses the Vonage Media Router 393 * (a session with the media mode set to {@link MediaMode#ROUTED}). 394 * 395 * <p> 396 * For more information, including technical details and security considerations, see the 397 * Vonage SIP interconnect developer guide. 398 * 399 * @param request The outbound SIP call's properties. 400 * 401 * @return Details of the SIP connection. 402 * 403 * @since 8.0.0-beta4 404 */ 405 public SipDialResponse sipDial(SipDialRequest request) { 406 return sipDial.execute(validateRequest(request)); 407 } 408 409 /** 410 * Play DMTF tones into a specific connection. 411 * Telephony events are negotiated over SDP and transmitted as RFC4733/RFC2833 digits to the remote endpoint. 412 * 413 * @param sessionId The session ID. 414 * @param connectionId Specific publisher connection ID. 415 * @param digits The string of DTMF digits to send. This can include 0-9, '*', '#', and 'p'. 416 * A 'p' indicates a pause of 500ms (if you need to add a delay in sending the digits). 417 * 418 * @since 8.0.0-beta4 419 */ 420 public void sendDtmf(String sessionId, String connectionId, String digits) { 421 sendDtmfToConnection.execute(new SendDtmfRequest( 422 validateSessionId(sessionId), 423 validateConnectionId(connectionId), 424 String.valueOf(digits) 425 )); 426 } 427 428 /** 429 * Play DTMF tones into a SIP call. 430 * Telephony events are negotiated over SDP and transmitted as RFC4733/RFC2833 digits to the remote endpoint. 431 * 432 * @param sessionId The session ID. 433 * @param digits The string of DTMF digits to send. This can include 0-9, '*', '#', and 'p'. 434 * A 'p' indicates a pause of 500ms (if you need to add a delay in sending the digits). 435 * 436 * @since 8.0.0-beta4 437 */ 438 public void sendDtmf(String sessionId, String digits) { 439 sendDtmfToSession.execute(new SendDtmfRequest( 440 validateSessionId(sessionId), 441 null, String.valueOf(digits) 442 )); 443 } 444 445 /** 446 * List all archives in the application. Deleted archives are not included in the results. 447 * 448 * @return The list of archives up to the first 1000, in order from newest to oldest. 449 * 450 * @see #listArchives(ListStreamCompositionsRequest) 451 */ 452 public List<Archive> listArchives() { 453 return listArchives(ListStreamCompositionsRequest.maxResults()); 454 } 455 456 /** 457 * List all archives in the application. Deleted archives are not included in the results. 458 * 459 * @param request (OPTIONAL) Filter properties of the request. 460 * 461 * @return The list of archives matching the filter criteria, in order from newest to oldest. 462 */ 463 public List<Archive> listArchives(ListStreamCompositionsRequest request) { 464 return listArchives.execute(request).getItems(); 465 } 466 467 /** 468 * Retrieve information about a specific archive. 469 * 470 * @param archiveId ID of the archive to retrieve. 471 * 472 * @return The Archive corresponding to the archiveId. 473 */ 474 public Archive getArchive(String archiveId) { 475 return getArchive.execute(validateArchiveId(archiveId)); 476 } 477 478 /** 479 * Create a new archive. 480 * 481 * @param request Properties of the archive. 482 * 483 * @return The created Archive. 484 */ 485 public Archive createArchive(Archive request) { 486 return createArchive.execute(validateRequest(request)); 487 } 488 489 /** 490 * Dynamically change the layout type of a composed archive while it is being recorded. 491 * 492 * @param archiveId ID of the archive to change. 493 * @param layout Properties of the layout change request. 494 */ 495 public void updateArchiveLayout(String archiveId, StreamCompositionLayout layout) { 496 validateRequest(layout); 497 layout.id = validateArchiveId(archiveId); 498 updateArchiveLayout.execute(layout); 499 } 500 501 /** 502 * Adds a stream to a composed archive that was started with the 503 * {@code streamMode} set to {@link StreamMode#MANUAL}. 504 * 505 * @param archiveId ID of the archive. 506 * @param streamId ID of the stream to add. 507 * 508 * @see #addArchiveStream(String, String, Boolean, Boolean) 509 */ 510 public void addArchiveStream(String archiveId, String streamId) { 511 addArchiveStream(archiveId, streamId, null, null); 512 } 513 514 /** 515 * Adds a stream to a composed archive. 516 * 517 * @param archiveId ID of the archive. 518 * @param streamId ID of the stream to add. 519 * @param audio (OPTIONAL) Whether the composed archive should include the stream's audio (true by default). 520 * @param video (OPTIONAL) Whether the composed archive should include the stream's video (true by default). 521 */ 522 public void addArchiveStream(String archiveId, String streamId, Boolean audio, Boolean video) { 523 patchArchiveStream(archiveId, new PatchComposedStreamsRequest(validateStreamId(streamId), audio, video)); 524 } 525 526 /** 527 * Removes a stream from a composed archive that was started with the 528 * {@code streamMode} set to {@link StreamMode#MANUAL}. 529 * 530 * @param archiveId ID of the archive. 531 * @param streamId ID of the stream to remove. 532 */ 533 public void removeArchiveStream(String archiveId, String streamId) { 534 patchArchiveStream(archiveId, new PatchComposedStreamsRequest(validateStreamId(streamId))); 535 } 536 537 private void patchArchiveStream(String archiveId, PatchComposedStreamsRequest request) { 538 PatchComposedStreamsRequest pasr = validateRequest(request); 539 pasr.id = validateArchiveId(archiveId); 540 patchArchiveStream.execute(pasr); 541 } 542 543 /** 544 * Archives stop recording after 4 hours (14,400 seconds), or 60 seconds after the last client disconnects from 545 * the session, or 60 minutes after the last client stops publishing. However, automatic archives continue 546 * recording to multiple consecutive files of up to 4 hours in length each. 547 * <p> 548 * Calling this method for automatic archives has no effect. Automatic archives continue recording to multiple 549 * consecutive files of up to 4 hours (14,400 seconds) in length each, until 60 seconds after the last client 550 * disconnects from the session, or 60 minutes after the last client stops publishing a stream to the session. 551 * 552 * @param archiveId ID of the archive to stop. 553 * 554 * @return The Archive corresponding to the archiveId. 555 */ 556 public Archive stopArchive(String archiveId) { 557 return stopArchive.execute(validateArchiveId(archiveId)); 558 } 559 560 /** 561 * Deletes an archive. You can only delete an archive which has a status of 562 * {@linkplain ArchiveStatus#AVAILABLE} or {@linkplain ArchiveStatus#UPLOADED}. 563 * Deleting an archive removes its record from the list of archives (see {@linkplain #listArchives()}). 564 * For an "available" archive, it also removes the archive file, making it unavailable for download. 565 * 566 * @param archiveId ID of the archive to delete. 567 */ 568 public void deleteArchive(String archiveId) { 569 deleteArchive.execute(validateArchiveId(archiveId)); 570 } 571 572 /** 573 * List all broadcasts that are in progress and started in the application. 574 * Completed broadcasts are not included in the listing. 575 * 576 * @return The list of broadcasts up to the first 1000, in order from newest to oldest. 577 * 578 * @see #listBroadcasts(ListStreamCompositionsRequest) 579 * @since 8.0.0-beta4 580 */ 581 public List<Broadcast> listBroadcasts() { 582 return listBroadcasts(ListStreamCompositionsRequest.maxResults()); 583 } 584 585 /** 586 * List all broadcasts that are in progress and started in the application. 587 * Completed broadcasts are not included in the listing. 588 * 589 * @param request (OPTIONAL) Filter properties of the request. 590 * 591 * @return The list of broadcasts matching the filter criteria, in order from newest to oldest. 592 * 593 * @since 8.0.0-beta4 594 */ 595 public List<Broadcast> listBroadcasts(ListStreamCompositionsRequest request) { 596 return listBroadcasts.execute(request).getItems(); 597 } 598 599 /** 600 * Get Information about a Broadcast that is in progress. 601 * 602 * @param broadcastId ID of the broadcast to retrieve. 603 * 604 * @return The Broadcast corresponding to the broadcastId. 605 * 606 * @since 8.0.0-beta4 607 */ 608 public Broadcast getBroadcast(String broadcastId) { 609 return getBroadcast.execute(validateBroadcastId(broadcastId)); 610 } 611 612 /** 613 * Start a new live streaming broadcast. 614 * This broadcasts the session to an HLS (HTTP live streaming) or to RTMP streams. 615 * To successfully start broadcasting a session, at least one client must be connected to the session. 616 * <p> 617 * The live streaming broadcast can target one HLS endpoint and up to five RTMP servers simultaneously for 618 * a session. You can only start live streaming for sessions that use the Vonage Media Router (with the 619 * media mode set to {@linkplain MediaMode#ROUTED}); you cannot use live streaming with sessions that have the 620 * media mode set to {@linkplain MediaMode#RELAYED}. 621 * 622 * @param request Broadcast object with initial properties. 623 * 624 * @return The same Broadcast object that was passed in with additional fields populated from the server's response. 625 * 626 * @since 8.0.0-beta4 627 */ 628 public Broadcast createBroadcast(Broadcast request) { 629 return createBroadcast.execute(validateRequest(request)); 630 } 631 632 /** 633 * Dynamically change the layout type of a live streaming broadcast. 634 * 635 * @param broadcastId ID of the broadcast to change. 636 * @param layout Properties of the layout change request. 637 * 638 * @since 8.0.0-beta4 639 */ 640 public void updateBroadcastLayout(String broadcastId, StreamCompositionLayout layout) { 641 validateRequest(layout); 642 layout.id = validateBroadcastId(broadcastId); 643 updateBroadcastLayout.execute(layout); 644 } 645 646 /** 647 * Adds a stream to a live broadcast that was started with the {@code streamMode} set to {@link StreamMode#MANUAL}. 648 * 649 * @param broadcastId ID of the broadcast. 650 * @param streamId ID of the stream to add. 651 * 652 * @see #addBroadcastStream(String, String, Boolean, Boolean) 653 * @since 8.0.0-beta4 654 */ 655 public void addBroadcastStream(String broadcastId, String streamId) { 656 addBroadcastStream(broadcastId, streamId, null, null); 657 } 658 659 /** 660 * Adds a stream to a live broadcast that was started with the {@code streamMode} set to {@link StreamMode#MANUAL}. 661 * 662 * @param broadcastId ID of the broadcast. 663 * @param streamId ID of the stream to add. 664 * @param audio (OPTIONAL) Whether the broadcast should include the stream's audio (true by default). 665 * @param video (OPTIONAL) Whether the broadcast should include the stream's video (true by default). 666 * 667 * @since 8.0.0-beta4 668 */ 669 public void addBroadcastStream(String broadcastId, String streamId, Boolean audio, Boolean video) { 670 patchBroadcastStream(broadcastId, new PatchComposedStreamsRequest(validateStreamId(streamId), audio, video)); 671 } 672 673 /** 674 * Removes a stream from a live broadcast that was started with the 675 * {@code streamMode} set to {@link StreamMode#MANUAL}. 676 * 677 * @param broadcastId ID of the broadcastId. 678 * @param streamId ID of the stream to remove. 679 * 680 * @since 8.0.0-beta4 681 */ 682 public void removeBroadcastStream(String broadcastId, String streamId) { 683 patchBroadcastStream(broadcastId, new PatchComposedStreamsRequest(validateStreamId(streamId))); 684 } 685 686 private void patchBroadcastStream(String broadcastId, PatchComposedStreamsRequest request) { 687 PatchComposedStreamsRequest pasr = validateRequest(request); 688 pasr.id = validateBroadcastId(broadcastId); 689 patchBroadcastStream.execute(pasr); 690 } 691 692 /** 693 * Stop a live broadcast. 694 * Note that a broadcast stops automatically 60 seconds after the last client disconnects from the session. 695 * There is a default maximum duration of 4 hours (14400 seconds) for each HLS and RTMP stream (the live 696 * stream broadcast automatically stops when this duration is reached). You can change the maximum duration for 697 * the broadcast by setting the {@link Broadcast.Builder#maxDuration(int)} property when you start the broadcast 698 * using the {@link #createBroadcast(Broadcast)} method. 699 * 700 * @param broadcastId ID of the broadcast to stop. 701 * 702 * @return Details of the Broadcast. 703 * 704 * @since 8.0.0-beta4 705 */ 706 public Broadcast stopBroadcast(String broadcastId) { 707 return stopBroadcast.execute(validateBroadcastId(broadcastId)); 708 } 709 710 /** 711 * Start real-time Live Captions for a Vonage Video Session. 712 * <p> 713 * The maximum allowed duration is 4 hours, after which the audio captioning will stop without any 714 * effect on the ongoing Vonage Video Session. An event will be posted to your callback URL if provided 715 * when starting the captions. Each Vonage Video Session supports only one audio captioning session. 716 * 717 * @param request Properties of the live captioning. 718 * 719 * @return Live captioning metadata. 720 * 721 * @since 8.5.0 722 */ 723 public CaptionsResponse startCaptions(CaptionsRequest request) { 724 return startCaptions.execute(validateRequest(request)); 725 } 726 727 /** 728 * Stop live captions for a session. 729 * 730 * @param captionsId ID of the live captions to stop. 731 * 732 * @since 8.5.0 733 */ 734 public void stopCaptions(String captionsId) { 735 stopCaptions.execute(validateId(captionsId, "Captions", true)); 736 } 737 738 /** 739 * Send audio from a Vonage Video API session to a WebSocket. 740 * 741 * @param request Properties of the WebSocket connection. 742 * 743 * @return The connection metadata. 744 * 745 * @since 8.5.0 746 */ 747 public ConnectResponse connectToWebsocket(ConnectRequest request) { 748 return connect.execute(validateRequest(request)); 749 } 750 751 /** 752 * List all Experience Composers in the application. 753 * 754 * @return The list of Experience Composers up to the first 1000, in order from newest to oldest. 755 * 756 * @since 8.6.0 757 * @see #listRenders(ListStreamCompositionsRequest) 758 */ 759 public List<RenderResponse> listRenders() { 760 return listRenders(ListStreamCompositionsRequest.maxResults()); 761 } 762 763 /** 764 * List Experience Composers in the application, with the specified offset and number of results. 765 * 766 * @param request (OPTIONAL) Filter properties of the request. 767 * Note that only {@code offset} and {@code count} are applicable here. 768 * 769 * @return The list of broadcasts matching the filter criteria, in order from newest to oldest. 770 * 771 * @since 8.6.0 772 * @see #listRenders() 773 */ 774 public List<RenderResponse> listRenders(ListStreamCompositionsRequest request) { 775 return listRenders.execute(request).getItems(); 776 } 777 778 /** 779 * Retrieve details on an Experience Composer. 780 * 781 * @param renderId ID of the Experience Composer instance. 782 * 783 * @return The Experience Composer corresponding to the specified renderId. 784 * 785 * @since 8.6.0 786 */ 787 public RenderResponse getRender(String renderId) { 788 return getRender.execute(validateRenderId(renderId)); 789 } 790 791 /** 792 * Create an Experience Composer for a Vonage Video session. 793 * 794 * @param request Properties of the Experience Composer. 795 * 796 * @return Details of the created Experience Composer. 797 * 798 * @since 8.6.0 799 */ 800 public RenderResponse startRender(RenderRequest request) { 801 return startRender.execute(validateRequest(request)); 802 } 803 804 /** 805 * Stop an active Experience Composer. 806 * 807 * @param renderId ID of the Experience Composer instance. 808 * 809 * @since 8.6.0 810 */ 811 public void stopRender(String renderId) { 812 stopRender.execute(validateRenderId(renderId)); 813 } 814}