Compare commits

...

444 Commits

Author SHA1 Message Date
Carlos Santos efada4c166 ov-components: (test) Add check for speaker element when local participant is muted 2025-08-14 12:54:30 +02:00
Carlos Santos 3fc0193260 Revert "ov-components: (test) Enhance element counting method to handle timeouts gracefully"
This reverts commit 61a3589dd7.
2025-08-14 12:53:31 +02:00
Carlos Santos 61a3589dd7 ov-components: (test) Enhance element counting method to handle timeouts gracefully 2025-08-14 12:43:10 +02:00
Carlos Santos 8d1c2468f5 ov-components: Refactor VideoconferenceComponent to use private properties for external structural directives, enhancing encapsulation and template setup 2025-08-14 12:16:54 +02:00
Carlos Santos 020413257f ov-components: Implement toolbar room name directive and update toolbar to display room name dynamically 2025-08-13 12:49:03 +02:00
Carlos Santos 6c78abdcc0 ov-components: Remove unnecessary mic button interaction in stream UI tests 2025-08-11 14:17:36 +02:00
Carlos Santos e31a78d153 ov-components: refactor(storage): Enhance tab management and cleanup mechanisms in StorageService 2025-08-11 13:57:23 +02:00
Carlos Santos b1d0269211 ov-components: Add participant badges directive for enhanced participant panel functionality 2025-08-05 17:39:29 +02:00
Carlos Santos 00fcb0b115 ov-components: Revamp participant panel item for improved UI/UX and accessibility; add mute/unmute functionality and translations 2025-08-05 16:35:20 +02:00
Carlos Santos 4bf351b2df ov-components: Add layout additional elements directive for customizable UI extensions 2025-07-31 13:50:47 +02:00
Carlos Santos e9ecceeb77 ov-components: Add participant panel directive for enhanced user experience 2025-07-30 19:37:40 +02:00
Carlos Santos 413dec3e0f ov-components: Close connection dialog on room disconnection event 2025-07-30 12:38:09 +02:00
Carlos Santos 414c26c31b ov-components: Enhance recording status messages with improved styling and structure for starting and stopping states 2025-07-30 12:26:19 +02:00
Carlos Santos fce026766b ov-components: Enhance recording activity component: update view recordings button visibility based on recording status and improve toolbar button text for active recording state 2025-07-29 19:10:35 +02:00
Carlos Santos fe3f90d266 ov-components: Implement error handling for recording start failures and enhance UI interactions 2025-07-29 18:19:16 +02:00
Carlos Santos 5f6b404576 ov-components: Enhance error handling UI in recording activity component with modern styling 2025-07-29 17:50:15 +02:00
Carlos Santos 9b8348bc04 ov-components: Enhance recording activity component with improved status handling and styling 2025-07-29 17:35:28 +02:00
Carlos Santos 1403d062e9 ov-components: Fixed wrong initialize value in show recordings directive 2025-07-29 15:50:31 +02:00
Carlos Santos 7bf0e0036c ov-components: optimize participant name subscription with filter and tap operators 2025-07-29 15:46:05 +02:00
Carlos Santos 76c957903f ov-components: Refactors config service to use RxJS Subjects
Updates the configuration service to use RxJS BehaviorSubjects and Observables for managing configuration values.

This change improves the reactivity and maintainability of the configuration system by providing a consistent and type-safe way to manage application settings.

Specifically, it introduces a helper method to create configuration items with BehaviorSubject and Observable, and uses distinctUntilChanged and shareReplay operators to optimize the observable streams.

ov-components: Refactor configuration management in OpenVidu components

- Updated directive methods to use centralized configuration updates for general, stream, and toolbar settings.
- Replaced individual setter methods with batch update methods for improved performance and maintainability.
- Introduced specific comparison methods for configuration objects to optimize change detection.
- Enhanced the structure of configuration interfaces for better clarity and organization.
- Removed redundant code and streamlined the configuration service for better readability.

ov-components: Enhance participant name handling in PreJoin and Videoconference components
2025-07-29 14:05:14 +02:00
Carlos Santos 68ea8001f1 ci: Update Selenium Chrome version to 138.0 and add internal directives tests workflow 2025-07-29 11:42:56 +02:00
Carlos Santos 5a249fc3e1 ov-components: Add internal directives tests and update related components for recording functionality 2025-07-29 11:38:46 +02:00
Carlos Santos 9bac0f6490 ov-components: Add directive to control visibility of recording list and update related services and components 2025-07-29 10:47:22 +02:00
Carlos Santos fa664c97f1 ov-components: Add view recordings button functionality and related directives 2025-07-29 10:26:25 +02:00
Carlos Santos 8e218ade3c ov-components: Add start/stop recording button directive and update related components 2025-07-28 18:51:20 +02:00
Carlos Santos 22af5c7df6 ov-components: Implement centralized template management for videoconference components 2025-07-22 19:24:07 +02:00
Carlos Santos 04b8b741e2 ov-components: Refactor videoconference component to use centralized state management 2025-07-22 18:17:57 +02:00
Carlos Santos 8af9f75a10 ov-components: Prevent prejoin from showing again after user initiates join process 2025-07-22 17:45:03 +02:00
Carlos Santos fabeaf1471 ov-components: Add ID to external view recording buttons for better accessibility 2025-07-22 17:05:33 +02:00
Carlos Santos 1ffd7ea9d6 ov-components: Fixed bug showing prejoin
- Rename joinSession method to join and update related calls for consistency
refactor
- Change isPrejoin method to showPrejoin in directive config service
2025-07-22 16:23:33 +02:00
cruizba 3af490522e openvidu-deployment: Change DefaultApp string literals to Meet in templates. 2025-07-22 14:08:25 +02:00
cruizba 0280b64084 openvidu-deployment: Azure HA - Replace Default App (OpenVidu Call) with OpenVidu Meet 2025-07-22 14:00:56 +02:00
cruizba 83aad06574 openvidu-deployment: Azure HA - Fix port priority rules. 2025-07-22 13:52:54 +02:00
cruizba 892c6efed2 openvidu-deployment: Azure Elastic - Fix port priority rules. 2025-07-21 21:21:04 +02:00
cruizba 5b888aafc0 openvidu-deployment: Build azure bicep of Elastic. 2025-07-21 21:02:53 +02:00
cruizba d918e8059a openvidu-deployment: Azure Elastic - Replace Default App (OpenVidu Call) with OpenVidu Meet 2025-07-21 21:00:23 +02:00
Carlos Santos 82ddca6b50 ov-components: Add IDs to action buttons in recording activity component for improved accessibility 2025-07-21 17:36:11 +02:00
Carlos Santos 6fb7d9583c ov-components: Enhance recording activity UI with additional status indicators and style adjustments 2025-07-21 17:03:22 +02:00
Carlos Santos 181c5f0789 ov-components: Improves recording activity UI
Refactors the recording activity component's template and styles
to use cards for displaying recording information.

Enhances the display of recording metadata, including duration,
size, and date, with appropriate icons.

Adds visual cues for active recordings and improves overall
responsiveness of the recording list.
2025-07-21 14:12:28 +02:00
Carlos Santos e486665efd ov-components: Updated recording activity component 2025-07-21 14:11:40 +02:00
Carlos Santos b659400c88 ov-components: implement read-only mode and customizable controls for recording activity 2025-07-21 14:11:40 +02:00
cruizba 98c7e3f751 openvidu-deployment: Build azure bicep of Single Node - PRO 2025-07-20 22:56:50 +02:00
cruizba 5e1df8b511 openvidu-deployment: Fix wrong environment variable in Azure Single Node - PRO 2025-07-20 22:42:23 +02:00
cruizba 16ec1f3920 openvidu-deployment: Replace Default App (OpenVidu Call) with OpenVidu Meet in Single Node Pro 2025-07-20 22:23:19 +02:00
cruizba b01e8f4d23 openvidu-deployment: update enabled modules to include openviduMeet in deployment script 2025-07-20 20:59:56 +02:00
cruizba 2413a0bb6d openvidu-deployment: Generate json from bicep Single Node - Community 2025-07-20 20:12:10 +02:00
cruizba bf6091e997 openvidu-deployment: Replace Default App (OpenVidu Call) with OpenVidu Meet in Single Node - Community 2025-07-20 20:10:25 +02:00
cruizba 9e0034dfac openvidu-deployment: Replace Default App (OpenVidu Call) with OpenVidu Meet 2025-07-18 21:53:32 +02:00
Carlos Santos 637142cec6 ov-components: add room initialization checks and error handling in SessionComponent 2025-07-17 17:03:18 +02:00
Carlos Santos c304c9c761 ov-components: add PreJoin directive to support custom pre-join templates in VideoconferenceComponent 2025-07-17 16:53:17 +02:00
Carlos Santos 4dd007395f ov-components: Refactor components to use takeUntil for unsubscribing from observables
- Replaced manual subscription management with takeUntil pattern
- Introduced a destroy$ Subject in each component to handle unsubscriptions on component destruction.
- Improved memory management and code readability by eliminating multiple subscription variables.
2025-07-17 15:44:37 +02:00
Carlos Santos 7573656060 ov-components: refactor VideoconferenceComponent to improve template setup and icon management 2025-07-17 14:00:51 +02:00
Carlos Santos 8407363aaf ov-components: add track subscription and manage room tracks published state 2025-07-17 13:29:17 +02:00
Carlos Santos 55fd64c254 ov-components: enhance recording functionality with track checks and UI updates 2025-07-17 13:29:17 +02:00
cruizba d151834048 openvidu-deployment: Replace OPENVIDU_CALL_SERVER_IMAGE with OPENVIDU_MEET_SERVER_IMAGE in deployment scripts 2025-07-16 12:36:15 +02:00
pabloFuente ce47224400 openvidu-testapp: make update interval for dialog optional 2025-07-14 22:18:12 +02:00
cruizba 61fbf9850b Add TCP port rules for WebRTC traffic on port 7881 and 50000-60000 across multiple deployment configurations 2025-07-11 21:33:05 +02:00
Piwccle ba1df4660c openvidu-testapp: RTCIceCandidate stats for publisher and subscriber peer connections fixed in firefox 2025-07-10 16:12:08 +02:00
pabloFuente d44e24592d openvidu-testapp: RTCIceCandidate stats for publisher and subscriber peer connections 2025-07-09 18:24:00 +02:00
Carlos Santos 91aa127dad ov-components: replace and improve recordingElapsedTime logic 2025-07-04 17:51:30 +02:00
Carlos Santos 1be876678c ov-components: add subscription for virtual background effects management 2025-07-04 15:55:22 +02:00
Carlos Santos 388981be31 ov-components: reorder imports and add ShowDisconnectionDialogDirective to ApiDirectiveModule 2025-07-04 12:51:57 +02:00
Carlos Santos 6497751375 ov-component: add showDisconnectionDialog directive and update service for disconnection dialog management 2025-07-04 12:45:34 +02:00
pabloFuente 5f0639c157 openvidu-test-e2e: adapted egress test to tolerate mediasoup limitation 2025-07-03 14:23:10 +02:00
pabloFuente e435a1a937 openvidu-test-e2e: fix signalTest 2025-07-02 21:53:45 +02:00
pabloFuente 932eda8115 openvidu-test-e2e: include event RoomEvent.ParticipantActive 2025-07-02 18:35:44 +02:00
pabloFuente 5d91f4d343 openvidu-testapp: add RoomEvent.ParticipantActive 2025-07-02 18:14:45 +02:00
pabloFuente 703698b25f openvidu-components-angular: fix comment links 2025-07-02 18:14:07 +02:00
pabloFuente b884e924b6 openvidu-test-e2e: added Egress tests 2025-06-30 14:34:37 +02:00
pabloFuente ee8847c5cb openvidu-testapp: add checkbox to force relay from browser 2025-06-30 12:11:09 +02:00
GitHub Actions 99c787c4f5 Revert "Bump version to 3.3.0"
This reverts commit 2083c078fd.
2025-06-26 20:02:58 +00:00
GitHub Actions 2083c078fd Bump version to 3.3.0 2025-06-26 20:02:55 +00:00
github-actions bc42a72836 openvidu-components-angular: Bumped version to 3.3.0 2025-06-26 18:43:30 +00:00
cruizba c3b7c6f4bb openvidu-deployment: Shutdown gracefully agents in aws and azure. 2025-06-25 19:03:10 +02:00
pabloFuente b913dbb3b8 openvidu-testapp: updated dependencies 2025-06-25 13:26:42 +02:00
pabloFuente f16eefa9df openvidu-testapp: differ between final and non-final transcription events 2025-06-25 13:26:28 +02:00
pabloFuente 96132553ae openvidu-testapp: add lk.transcription handler 2025-06-24 17:26:38 +02:00
Carlos Santos 709779b7fd ov-components: Remove debugger statement from setRecordingStarted method in RecordingService 2025-06-24 16:18:18 +02:00
cruizba 11ac3d32eb openvidu-deployment: Fix wrong image 2025-06-24 11:34:54 +02:00
cruizba c8bbcfed56 openvidu-deployment: Add OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE environment variable to installation scripts 2025-06-24 11:33:21 +02:00
Piwccle 76a6f6c301 openvidu-deployment: azure - Add support for additional install flags in all the deployments 2025-06-18 16:26:50 +02:00
Carlos Santos 2de2920acf ov-components: Add check for existing Material Icons link in VideoconferenceComponent 2025-06-18 10:12:39 +02:00
cruizba fd8be9f23f openvidu-deployment: - AWS HA - Add experimental TURN TLS 2025-06-14 02:02:29 +02:00
cruizba e66e5a23e1 openvidu-deployment: - HA - Open port 5349 in media nodes for master nodes if Turn Domain is not configured 2025-06-13 22:02:13 +02:00
cruizba 335fd8e3c3 Remove unnecessary condition for SecurityGroupIngress in CloudFormation templates 2025-06-13 21:37:39 +02:00
cruizba 237ebe1d59 openvidu-deployment: Add "Additional Installer Flag" 2025-06-13 19:20:25 +02:00
cruizba 5e5e404d7d openvidu-deployment: Remove non official Cloudformation 2025-06-13 19:20:25 +02:00
Carlos Santos 16e869c5da ov-components: update API directive documentation 2025-06-11 12:50:08 +02:00
Carlos Santos c628b2ab68 ov-components: enhance directive table generation logic and improve error handling 2025-06-11 12:49:41 +02:00
Carlos Santos b7d9f822de ov-components: apply background from storage based on background effects button status 2025-06-10 17:28:00 +02:00
Piwccle fe606cbf15 openvidu-deployment: azure - added cluster data container for caddy and observability info 2025-06-10 16:30:41 +02:00
Piwccle 7c71f41d95 openvidu-deployment: azure - removed one '}' that was making TURN HA fail 2025-06-10 12:48:12 +02:00
Piwccle 9728d966ad openvidu-deployment: azure - bugfix: added dependsOn to remove possible race condition 2025-06-10 12:17:10 +02:00
Piwccle ce610cab03 openvidu-deployment: azure - removed parameter from HA CUID 2025-06-10 11:46:42 +02:00
Piwccle d8f14c6905 openvidu-deployment: azure - changes to let TURN work in HA deployment 2025-06-10 11:34:57 +02:00
Piwccle bdf4f07a28 openvidu-deployment: azure - removed one line of the install script that was there to try the new delete_media_node 2025-06-10 11:08:55 +02:00
Piwccle 206a51baf7 openvidu-deployment: azure - added scripts to delete media node if fails 2025-06-10 10:56:50 +02:00
Carlos Santos 94b51b9971 ov-components: Update language files to replace "session" with "room" for consistency across translations 2025-06-09 11:21:35 +02:00
github-actions 8984cfdcad openvidu-components-angular: Bumped version to 3.2.1 2025-06-05 11:01:42 +00:00
Carlos Santos 022d18578e ov-components: Remove unnecessary mat-line directive from settings panel options 2025-06-05 12:49:53 +02:00
GitHub Actions 9beb8b435a Revert "Bump version to 3.2.0"
This reverts commit 26b790bfd8.
2025-06-04 15:01:06 +00:00
GitHub Actions 26b790bfd8 Bump version to 3.2.0 2025-06-04 15:01:02 +00:00
github-actions 308315dee4 openvidu-components-angular: Bumped version to 3.2.0 2025-06-04 14:50:58 +00:00
cruizba 5fac396487 Update submodule openvidu-livekit for 3.2.0 2025-06-04 16:34:41 +02:00
Piwccle 99e36178a5 openvidu-deployment: builded .bicep 2025-06-04 13:29:32 +02:00
Piwccle 8999076291 openvidu-deployment: azure - HA fixed turn 2025-06-04 13:29:32 +02:00
cruizba 220c6d7d4a openvidu-deployment: Bump Redis server image version to 7.4.4. 2025-06-02 20:41:24 +02:00
cruizba f989dc26d2 openvidu-deployment: Force minimum docker and docker-compose version in update script. 2025-06-02 17:54:33 +02:00
cruizba 3107d504ce openvidu-deployment: Force minimum docker and docker-compose version. 2025-06-02 17:47:46 +02:00
cruizba b8c0bb3283 openvidu-deployment: azure - update signal for stopping media node services to SIGQUIT 2025-06-02 17:00:49 +02:00
Piwccle 4aa8739ee8 openvidu-deployment: compiled all .bicep and fixed roles 2025-05-29 17:43:37 +02:00
Piwccle bcfcfdd704 openvidu-deployment: added roles in HA to configure blob storage 2025-05-29 17:40:53 +02:00
Piwccle a59872c9e9 openvidu-deployment: fixed common arguments for openvidu single node pro 2025-05-29 17:33:37 +02:00
Piwccle cbf4bafa36 openvidu-deployment: added more validation in CUID 2025-05-29 17:21:47 +02:00
Piwccle 95584d7437 openvidu-deployment: CUID for azure deployments modified to work better 2025-05-29 17:17:59 +02:00
cruizba 20e348ed60 Revert redis to 7.4.3-alpine 2025-05-29 17:11:18 +02:00
Piwccle fbcc460f10 openvidu-deployment: fixed ha config_blobstorage in all master nodes 2025-05-29 16:51:59 +02:00
Piwccle e7e1a39e05 openvidu-deployment: added Openvidu Single Node PRO and added v2compatibility modules in HA and Elastic deployments 2025-05-29 15:58:41 +02:00
pabloFuente 18fd65350c openvidu-integration-tests: update start-openvidu-local-deployment action 2025-05-29 14:43:41 +02:00
cruizba 1551bf8244 openvidu-deployment: Bump docker image versions for 3.2.0 2025-05-28 23:39:14 +02:00
pabloFuente 882f9405db openvidu-test-e2e: put RTSP container in network mode host 2025-05-28 12:51:37 +02:00
Piwccle af3608c81c openvidu-deployment: azure - added optional automationAccountName 2025-05-27 11:18:05 +02:00
Carlos Santos 395d0c3ade ov-components: fix name getter in ParticipantModel to return participant's name 2025-05-26 19:09:19 +02:00
Carlos Santos f0bcb3ac03 ov-components:remove unnecessary screenshot logging in API directive tests 2025-05-26 18:38:25 +02:00
Carlos Santos 3579b9dc6a ov-components: update URL handling in API directive tests and improve error logging in leaveRoom method 2025-05-26 18:31:51 +02:00
Carlos Santos dbb45eaffd ov-components: improve leaveRoom method by adding fallback navigation on error 2025-05-26 18:24:14 +02:00
Carlos Santos fc7d7fef5a ov-components: replace fit with it for consistency in API directive tests 2025-05-26 17:52:55 +02:00
Carlos Santos 3f234276ee ov-components: enhance leaveRoom method with error handling and verification steps 2025-05-26 17:46:54 +02:00
GitHub Actions b10bc5f8c6 Revert "Bump version to 3.2.0-dev1"
This reverts commit a9e727fd51.
2025-05-26 14:34:37 +00:00
GitHub Actions a9e727fd51 Bump version to 3.2.0-dev1 2025-05-26 14:34:34 +00:00
cruizba c9b350eddc openvidu-deployment: Add single node PRO. Update,sh changes 2025-05-26 13:39:18 +02:00
Carlos Santos 21c297c73b ov-components: ensure room is left after each test in attribute and event directives 2025-05-26 10:33:17 +02:00
Carlos Santos 7df7b31d97 ov-components: add screenshot logging and delay in API directives tests 2025-05-23 18:13:35 +02:00
Carlos Santos d8d4870eb7 ov-components: add screenshot logging for video presence check 2025-05-23 17:18:02 +02:00
Carlos Santos aa15e8b713 ov-components: add delay before checking session presence in audio disabled test 2025-05-23 15:48:15 +02:00
Carlos Santos 621df40a7f ci: update cleanup action references in workflows to use official OpenVidu action 2025-05-23 15:37:13 +02:00
Carlos Santos 147f7078a0 ci: update action for building and serving openvidu-components-angular Testapp to use the latest version 2025-05-23 15:19:48 +02:00
Carlos Santos 55a67f191a ci: replace custom action for building and serving openvidu-components-angular Testapp with official OpenVidu action 2025-05-23 15:17:50 +02:00
Piwccle 31587d7181 openvidu-deployment: azure - added 2 'none' values missing in HA .bicep 2025-05-23 14:32:27 +02:00
Carlos Santos d0f714a5e2 ci: replace setup action for OpenVidu Call Backend with official action 2025-05-23 13:58:20 +02:00
Carlos Santos e4ed447009 ci: remove setup action for OpenVidu local deployment 2025-05-23 13:58:20 +02:00
Piwccle 57ea002953 openvidu-deployment: azure - compiled all new .bicep 2025-05-23 13:53:24 +02:00
Carlos Santos 1b5a230132 ci: uses OpenVidu local deployment action in integration tests 2025-05-23 13:29:06 +02:00
Carlos Santos 7348402364 ci: update openvidu-local-deployment action to use the official OpenVidu action 2025-05-23 13:27:22 +02:00
Piwccle 915a4598b1 openvidu-deployment: azure - CUID for elastic and ha deployments and adjustments to those .bicep to support the new CUID 2025-05-23 13:19:59 +02:00
Carlos Santos 0b017b2abb ci: update workflow to use OpenVidu action for local deployment setup 2025-05-23 12:55:36 +02:00
Carlos Santos c12dbf247c ov-components: refactor participant left and room disconnected handling to use a common disconnect method 2025-05-23 12:02:29 +02:00
pabloFuente f087c172fc openvidu-testapp: remove bold font weight from buttons 2025-05-23 11:49:25 +02:00
pabloFuente fe3e99e4e1 Find out RTSP container IP instead of using host.docker.internal 2025-05-23 11:49:25 +02:00
Carlos Santos 442b99771a ov-components: enhance error handling and add screenshot capture during room leave process 2025-05-23 11:41:56 +02:00
Carlos Santos 5f9fb06c2a ov-components: add error logging and screenshot capture on room leave failure 2025-05-23 11:35:21 +02:00
Carlos Santos dfae82d4cf ov-components: add event handling for participant left and update config state 2025-05-23 11:24:45 +02:00
Carlos Santos 549a822491 ov-components: rename redirectOnLeaves to redirectToHomeOnLeaves for clarity and update related logic 2025-05-23 11:09:07 +02:00
Carlos Santos b6f242107e ov-components: adjust max-width and padding for prejoin card styling 2025-05-22 13:29:16 +02:00
Carlos Santos 9023f03cee ov-components: center progress spinner in loading container 2025-05-22 13:26:11 +02:00
Carlos Santos 500b19d1b9 Revert "ov-components: change runner for API Directives and Events E2E tests to ov-actions-runner"
This reverts commit c275381cfd.
2025-05-22 13:06:59 +02:00
Carlos Santos 421c5ea29c ov-components: add participant left event handling and navigation in testapp 2025-05-22 12:47:13 +02:00
Carlos Santos cea8ce0def ov-components: update test descriptions for structural directives to improve clarity and consistency 2025-05-22 12:46:45 +02:00
Carlos Santos daf1349e75 ov-components: fixed panel toggle logic for external panels. Fixes #854
refactor togglePanel method for improved state management and clarity
2025-05-22 12:44:06 +02:00
Carlos Santos ffbc1f5e5d ov-components: update toolbar additional panel button count in tests and template 2025-05-22 12:42:26 +02:00
pabloFuente a02e23763a openvidu-testapp: updated all dependencies 2025-05-22 12:11:38 +02:00
Carlos Santos c275381cfd ov-components: change runner for API Directives and Events E2E tests to ov-actions-runner 2025-05-22 10:54:56 +02:00
Carlos Santos 34eb27dfe3 ov-components: update onRoomDisconnected event to reference onParticipantLeft for deprecation notice 2025-05-21 16:02:52 +02:00
Carlos Santos d3702ab6ac ov-components: update audio path for CI mode in Selenium configuration 2025-05-21 14:23:05 +02:00
Carlos Santos dafba8cdee ov-components: update audio capture path to use dynamic variable based on launch mode 2025-05-21 13:46:12 +02:00
Carlos Santos 995ce8c00b ov-components: add autoplay policy and file access arguments for CI chrome configuration 2025-05-21 13:45:04 +02:00
Carlos Santos 1e15fea5e4 ov-components: enable Toolbar E2E tests by removing conditional check 2025-05-21 13:33:12 +02:00
Carlos Santos 3139437bfe ov-components: remove OpenviduWebComponent and related files 2025-05-21 13:31:26 +02:00
Carlos Santos 59112b79fa ov-components: add initial E2E tests for captions functionality 2025-05-21 13:31:18 +02:00
Carlos Santos fd486270b9 ov-components: update audio capture file in E2E tests and remove unnecessary entry from .gitignore 2025-05-21 13:24:08 +02:00
Carlos Santos 042f9b5a89 ov-components: add E2E tests for toolbar 2025-05-21 13:22:29 +02:00
Carlos Santos 28e96647fa ci: Added stream tests 2025-05-21 13:15:30 +02:00
Carlos Santos 35eacb45a1 ov-components: update E2E tests for stream functionality and adjust audio capture file 2025-05-21 13:13:56 +02:00
Carlos Santos c42108bd22 ov-components: enable Panels E2E test by removing conditional check 2025-05-21 12:45:48 +02:00
Carlos Santos 63588148a7 ci: update screen sharing test command 2025-05-21 12:43:13 +02:00
Carlos Santos 52d253a1db ov-components: add E2E tests for screensharing features 2025-05-21 12:41:34 +02:00
Carlos Santos bf7d83134a ov-components: add panels E2E test suite 2025-05-21 12:11:01 +02:00
Carlos Santos 77bba7e587 ov-components: uncomment screen track replacement test 2025-05-21 12:06:36 +02:00
Carlos Santos fb999df526 ov-components: comment out headless mode in chrome arguments for local testing 2025-05-21 11:55:44 +02:00
Carlos Santos 314a3d1898 ov-components: change fit to it for video disabled prejoin page test 2025-05-21 11:55:31 +02:00
Carlos Santos 35f1085870 ov-components: remove obsolete events E2E tests 2025-05-21 11:55:10 +02:00
Carlos Santos cfdaabfc0b ov-components: remove unit tests and update media devices E2E tests 2025-05-21 11:54:36 +02:00
Carlos Santos 146aeed893 ov-components: assign participant name to observable on ready to join 2025-05-21 11:07:57 +02:00
Carlos Santos 02b594e405 ov-components: Added events e2e tests 2025-05-20 18:55:03 +02:00
Piwccle bd3504b818 openvidu-deployment: azure - fixed one thing left to be able to try the new .bicep 2025-05-20 18:49:39 +02:00
Piwccle 743b05911c openvidu-deployment: azure - fixing .bicep file to work with the new CUID 2025-05-20 18:46:31 +02:00
Piwccle 1b2af77196 openvidu-deployment: azure - CUID for Single Node Deployment ready 2025-05-20 18:29:22 +02:00
Carlos Santos 1f13b11f88 ov-components: enable Chat E2E tests in workflow 2025-05-20 17:38:44 +02:00
Carlos Santos 1cba6761bb ov-components: update E2E test names and add chat feature tests 2025-05-20 17:38:06 +02:00
Carlos Santos 920bba1bf3 ov-components: update test to focus on VIDEO DISABLED scenario in prejoin page 2025-05-20 17:33:09 +02:00
Piwccle 1d3cba9517 openvidu-deployment: azure - fixing SSH key parameter in Single Node CUID 2025-05-20 17:15:47 +02:00
Carlos Santos 331a102a19 ov-components: add end-to-end tests for structural directives in OpenVidu Components 2025-05-20 16:56:19 +02:00
Carlos Santos 6fe461dc0c ov-components: refactor workflow jobs for clarity and organization 2025-05-20 16:51:23 +02:00
Carlos Santos baf3da51c2 ov-components: add end-to-end tests for attribute and structural directives 2025-05-20 16:51:23 +02:00
Carlos Santos 48084544ba ov-components: remove debug console logs for query parameters and participant name in testapp 2025-05-20 16:51:23 +02:00
Piwccle 6a02f96af2 openvidu-deployment: azure - added UI definition to Single Node 2025-05-20 13:58:37 +02:00
Carlos Santos 3c72bc6c4d ov-components: update disconnect logic to handle client-initiated events in OpenViduService 2025-05-20 13:17:00 +02:00
Carlos Santos 3027ab6c5b ov-components: update parameter name for audio detection display in testapp 2025-05-20 12:51:19 +02:00
Carlos Santos ffc6bb4c41 ov-components: streamline E2E test setup by removing redundant steps and renaming jobs 2025-05-20 12:44:02 +02:00
Carlos Santos 5df42d5344 ov-components: refactor participant name retrieval logic in VideoconferenceComponent 2025-05-20 12:40:22 +02:00
Carlos Santos 4ad8c52c38 ov-components: enhance testapp with dynamic configuration options 2025-05-20 12:40:04 +02:00
Carlos Santos 741954ac2b OV-COMPONENTS: upgrade chromedriver to 136.0.2 and selenium-webdriver to 4.32.0 2025-05-20 12:39:19 +02:00
Carlos Santos 75d6576603 ov-components: add end-to-end tests for API directives and implement leaveRoom utility function 2025-05-20 12:38:20 +02:00
Carlos Santos 8a9e9e0d27 ov-components: fixed race condition with onTokenRequested event and participantName 2025-05-19 19:10:40 +02:00
Carlos Santos 1065f32a3a ov-components: add action for building and serving openvidu-components-angular Testapp 2025-05-19 18:08:49 +02:00
Carlos Santos 223c7473e5 ov-components: add setup action for OpenVidu Call Backend in workflows 2025-05-19 18:03:26 +02:00
Carlos Santos f951eddfe5 ov-components: add cleanup action to workflows for better resource management 2025-05-19 17:55:09 +02:00
Carlos Santos 8dfb513bb0 ov-components: add setup action for local deployment and streamline workflow steps 2025-05-19 17:52:29 +02:00
Carlos Santos ab7a453507 ov-components: remove unused active button styles and clean up SCSS variables 2025-05-19 17:08:50 +02:00
Carlos Santos 5b112286d9 ov-components: add material symbols support and improve toolbar symbols handling 2025-05-19 16:59:24 +02:00
Carlos Santos c4c5fb9866 ov-components: improve logging for participant disconnection event 2025-05-14 17:50:49 +02:00
Carlos Santos bd711b57f3 ov-components: simplify disconnect logic by removing callback. The participantLeft event will be fired by LK event handler 2025-05-14 13:48:53 +02:00
Piwccle 94f2e3e573 openvidu-deployment: azure - changed one description of a param to clarify the use of the param 2025-05-13 14:36:12 +02:00
Piwccle b7a94f4b03 openvidu-deployment: azure - compiled .bicep into .json 2025-05-13 11:59:15 +02:00
Piwccle 21b94bc4fd Merge branch 'master' of https://github.com/OpenVidu/openvidu 2025-05-13 11:55:40 +02:00
Carlos Santos 745e04baa6 ov-components: enhance recording URL handling for backward compatibility 2025-05-13 11:35:04 +02:00
Carlos Santos 4b611893e9 ov-components: enhance recording URL handling for backward compatibility 2025-05-13 11:14:40 +02:00
Piwccle b8fc003a4c openvidu-deployment: azure - added existing storage account support in CE, Elastic and HA deployments 2025-05-13 11:01:22 +02:00
Carlos Santos ed579b4e72 ov-components: update recording stream URL to use '/media' segment instead of '/stream' 2025-05-12 16:00:42 +02:00
Carlos Santos d8ce736cc0 ov-components: improve disconnect handling and manage client-initiated disconnect events 2025-05-12 13:39:45 +02:00
Carlos Santos cb8f11449f ov-components: add UNKNOWN_DISCONNECT message for various languages and handle participant disconnection reason 2025-05-06 15:43:56 +02:00
Piwccle efb691f481 openvidu-deployment: added config script of azure blob storage in elastic deployment and added a rol assignment in CE needed 2025-05-06 09:03:55 +02:00
Carlos Santos a8c2459d5f ov-components: implement OnPush change detection strategy and optimize change detection calls in prejoin component 2025-05-05 14:35:51 +02:00
Carlos Santos 0074b28d3a ov-components: Fix available langs for allowing custom ones 2025-05-05 13:52:10 +02:00
Carlos Santos 760cf604eb ov-components: update package dependencies and TypeScript configurations
- Added @types/dom-mediacapture-transform to package.json for type definitions.
- Changed hoveringTimeout type in StreamComponent to ReturnType<typeof setTimeout> for better type safety.
- Updated TypeScript lib version from ES2020 to ES2021 in tsconfig files for improved features.
- Included dom-mediacapture-transform in types for TypeScript configurations across various tsconfig files.
- Removed empty types array in tsconfig.app.json to ensure proper type checking.
- Adjusted skipLibCheck settings in tsconfig files to improve compatibility with Livekit track processors.
2025-05-05 12:15:16 +02:00
Carlos Santos 8ce155df6a ov-components: enhance recording status management and elapsed time calculation 2025-04-30 19:18:29 +02:00
Carlos Santos cff617b0c3 ov-components: disable webcomponent E2E tests 2025-04-30 18:12:36 +02:00
Carlos Santos 127fbbd4e1 ov-components: update build process and module definition for improved structure 2025-04-30 17:58:11 +02:00
Carlos Santos 3adfa91c54 fix(tsconfig): add skipLibCheck comment for Livekit track processors 2025-04-30 15:22:18 +02:00
Carlos Santos 7f00318cbb ov-components: update dependencies and TypeScript configuration
- Updated @livekit/track-processors from 0.3.2 to ^0.5.6
- Updated livekit-client from 2.5.2 to 2.11.4
- Changed TypeScript lib option from "dom" to "DOM" in tsconfig.lib.json
- Added skipLibCheck option in tsconfig.lib.json and tsconfig.base.json
- Updated TypeScript lib option from "dom" to "dom" in tsconfig.base.json
2025-04-30 14:57:47 +02:00
Carlos Santos 5433f516a9 ov-components: remove redundant background processor removal logic in virtual background service 2025-04-30 14:54:26 +02:00
Carlos Santos 81fcae2d4e ov-components: enhance virtual background service updating the switching logic for improving performance 2025-04-30 14:38:43 +02:00
Carlos Santos 0c1e1a7134 ov-components: Updated to Angular 19 2025-04-30 14:37:10 +02:00
Carlos Santos 518e1511d2 ov-components: add participant left handling and update room disconnection logic 2025-04-30 12:13:03 +02:00
Carlos Santos b92630ecd8 ov-components: enhance participant disconnection handling with reasons and refactor disconnect logic 2025-04-30 12:13:03 +02:00
Carlos Santos 11137a2a8f ov-components: add disconnect and error messages for various scenarios in multiple language files 2025-04-30 12:13:03 +02:00
Piwccle 9310ff3ea1 openvidu-deployment: azure - added blob storage resources and modified CE to support config blobStorage 2025-04-23 13:26:33 +02:00
Piwccle 37428c91b6 openvidu-deployment: azure - changed links that references de runbook 2025-04-22 12:06:04 +02:00
cruizba 9e04d59b61 Add openvidu-deployment scripts 2025-04-22 11:46:24 +02:00
Micael Gallego c1f2971881
Fix NewGenVidu project logo in README.md 2025-03-24 19:49:32 +01:00
Carlos Santos 8c28228357 ov-components: update default recording stream URL in directives and config service 2025-03-23 14:25:35 +01:00
Carlos Santos 90fd0ef44e ov-components: add recordingStreamBaseUrl directive and integrate with config service for dynamic stream URL construction 2025-03-14 19:08:34 +01:00
Carlos Santos 6137bdbbbc ov-components: add null check for participant name updates in pre-join component and participant name directive 2025-03-12 19:03:14 +01:00
Carlos Santos 2acf636842 ov-components: reduce max-width of pre-join component to improve layout 2025-03-12 18:41:42 +01:00
Carlos Santos 0ad51d6c58 ov-components: remove max-height constraint from pre-join component styles 2025-03-12 18:28:53 +01:00
Carlos Santos 6f97b9d8c2 ov-components: move loading state update to ngAfterContentChecked lifecycle hook 2025-03-12 18:28:47 +01:00
Carlos Santos a64cf1d577 ov-components: enhance pre-join component layout and add participant name visibility check 2025-03-12 18:05:37 +01:00
Carlos Santos e373d23cc9 ov-components: add participant name visibility control to pre-join component 2025-03-12 17:19:55 +01:00
Carlos Santos 72888e4ea9 ov-components: add camera and microphone button visibility controls in the E2E tests 2025-03-10 10:27:44 +01:00
Carlos Santos 2bf212ccfe ov-components: add camera and microphone button visibility controls in toolbar and settings panel 2025-03-10 10:11:38 +01:00
Carlos Santos 5fba250b1d ov-components: remove ServiceConfigService and update components to use LayoutService directly 2025-03-08 17:42:43 +01:00
Carlos Santos 7315360fbc ov-components: add onParticipantLeft event and disconnect callback to notify when local participant leaves 2025-03-07 19:25:27 +01:00
Carlos Santos d965e72822 ov-components: add OpenViduComponentsUiModule with component and pipe declarations 2025-03-07 18:52:12 +01:00
Carlos Santos 288a585809 ov-components: rename participantId to participantName in ParticipantLeftEvent and update disconnect method 2025-03-07 13:52:43 +01:00
Carlos Santos 272eb9002c ov-components: rename participant created event to participant connected and update documentation 2025-03-07 13:42:50 +01:00
Carlos Santos dee470609c ov-components: clarify event documentation for local participant actions 2025-03-07 13:36:10 +01:00
Carlos Santos ad8f368a91 ov-components: enhance documentation for room and participant events 2025-03-07 13:30:26 +01:00
Carlos Santos 5d855a1338 ov-components: log room name when a room is created 2025-03-07 13:04:22 +01:00
Carlos Santos 3defad20cc ov-components: emit room created event after participant connection 2025-03-07 13:04:06 +01:00
Carlos Santos 9b4f330c4a ov-components: clarify method documentation for local participant connection 2025-03-06 13:45:02 +01:00
pabloFuente 7d10ec6097 openvidu-testapp: fix webpack builder with custom configuration 2025-02-19 12:38:57 +01:00
pabloFuente 8d443ac506 openvidu-testapp: update rest of dependencies 2025-02-19 12:13:37 +01:00
pabloFuente 1521a766e5 openvidu-testapp: update minor upgrade change 2025-02-19 12:09:43 +01:00
pabloFuente a1edded5df openvidu-testapp: update to Angular Material 19 2025-02-19 12:04:21 +01:00
pabloFuente fa507d492f openvidu-testapp: update to Angular 19 2025-02-19 12:03:43 +01:00
pabloFuente 0ae67e8a87 openvidu-testapp: update Angular Material 18 2025-02-19 12:01:54 +01:00
pabloFuente 051b14c5b2 openvidu-testapp: update to Angular 18 2025-02-19 11:48:56 +01:00
pabloFuente f610f37ac3 openvidu-testapp: update pollyfils.ts 2025-02-19 11:47:11 +01:00
pabloFuente 501881c602 openvidu-testapp: update to Angular Material 17 2025-02-19 11:42:14 +01:00
pabloFuente ea8f0e2101 Upgrade to Angular 17 2025-02-19 11:41:31 +01:00
github-actions d3520e9098 openvidu-components-angular: Bumped version to 3.1.0 2025-02-17 14:42:29 +00:00
cruizba b29b9102a9 update openvidu-livekit subproject to latest commit 2025-02-17 15:23:37 +01:00
Carlos Santos 64fbaa2a42 ov-components: Fixed nested event test 2025-02-13 12:43:22 +01:00
Carlos Santos 4c4380a87f ov-components e2e: Added participantLeft listener 2025-02-13 12:33:04 +01:00
Carlos Santos d8452c42ad ov-components: Fired room and participantLeft events 2025-02-13 12:31:50 +01:00
cruizba 09ba39642e openvidu-test-e2e: Disable flaky audio ingress tests for RTSP 2025-02-11 13:35:20 +01:00
pabloFuente 11c6f2f965 openvidu-testapp: remove unused imports 2025-02-11 12:47:02 +01:00
cruizba 9a01c0e179 openvidu-test-e2e: Fix file URL selection logic for lossless video encoding 2025-02-11 12:33:44 +01:00
cruizba 06fc88f2b0 openvidu-test-e2e: Add lossless flag to getFileUrl method 2025-02-11 12:30:56 +01:00
cruizba f19a9129ba openvidu-test-e2e: Improve CPU performance by using losless codec ffv1 and flac. Fix some tests also because of mp3 used as source instead of flac. 2025-02-11 02:28:22 +01:00
Carlos Santos f785dfd7ad ov-components: subscribe to vc directives in constructor 2025-02-07 17:21:09 +01:00
pabloFuente 8185324a0a openvidu-test-e2e: disable audio only opus RTSP test 2025-02-07 11:14:06 +01:00
pabloFuente 88214676a8 openvidu-test-e2e: add -crf flags to video encodings with ffmpeg 2025-02-06 23:16:36 +01:00
pabloFuente 9f9eda5ce4 openvidu-test-e2e: improve waiters resilience 2025-02-06 20:49:42 +01:00
pabloFuente 75bb35d00a openvidu-test-e2e: fix opus codec with "-ac 2" flag 2025-02-06 20:43:47 +01:00
pabloFuente d4006421e9 openvidu-test-e2e: add AAC audio codec RTSP tests 2025-02-06 20:27:32 +01:00
pabloFuente 0fe47c4b13 openvidu-test-e2e: test RTSP with all codecs 2025-02-06 20:07:17 +01:00
pabloFuente 5c97301c6d openvidu-testapp: allow configuring URI for Ingress URL types 2025-02-06 18:59:29 +01:00
pabloFuente de5f67dd3c openvidu-test-e2e: parameterized rtsp/srt port 2025-02-03 19:17:50 +01:00
pabloFuente da108a4031 openvidu-test-e2e: update rtstp server container to network host 2025-02-03 19:10:44 +01:00
pabloFuente 5fb3be503c openvidu-test-e2e: add RTSP and SRT tests 2025-02-03 15:36:37 +01:00
pabloFuente 6ea42e82c0 openvidu-testapp: add ingress URL type selector 2025-02-03 15:35:52 +01:00
Carlos Santos 6e1d9c7ee7 ov-components: Updated logo directive 2025-01-28 21:37:53 +01:00
Carlos Santos 4cdb761737 ov-components: Allowed Angular 19 2025-01-27 17:09:13 +01:00
Carlos Santos 51e3abd483 ov-components: Updated npm script 2025-01-27 17:04:11 +01:00
pabloFuente 77f23512a6 openvidu-test-e2e: add comment 2025-01-20 14:48:09 +01:00
pabloFuente f2cb2fb252 openvidu-test-e2e: add tags @OnlyPion and @OnlyMediasoup. Fix customIngress test 2025-01-20 14:46:16 +01:00
pabloFuente d3b8214dcb openvidu-test-e2e: add signal test 2025-01-17 11:45:24 +01:00
pabloFuente 98a4b1b2cb openvidu-test-e2e: fix array outbound exception in customIngressTest 2025-01-15 18:31:39 +01:00
github-actions 9114f4fcc6 openvidu-components-angular: Bumped version to 3.0.1-beta1 2025-01-09 12:26:03 +00:00
Carlos Santos 2ea61d05a1 ov-components: Fixed message badge color 2025-01-07 09:17:20 +01:00
pabloFuente c82d7315ef Update all Java project dependencies 2025-01-03 13:52:33 +01:00
pabloFuente 6f3475fd94 openvidu-test-e2e: fix version.surefire.plugin pom.xml version 2025-01-03 13:17:43 +01:00
pabloFuente 7de722714e Update pom.xml to remove openvidu-parent dependency 2025-01-03 13:15:11 +01:00
pabloFuente d2a59f1193 openvidu-test-e2e: disable required tests 2025-01-03 12:38:02 +01:00
pabloFuente 36df6348d6 Update pom.xml to update openvidu-java-client versions 2025-01-02 20:43:56 +01:00
pabloFuente fa0392a0a3 openvidu-test-e2e: fix testcontainer usage 2025-01-02 18:10:45 +01:00
juancarmore b829142caf openvidu-components-angular: Update links in documentation to use the latest version 2024-12-18 15:32:17 +01:00
juancarmore dead4853aa integration-tests: Refactor utility code. Exec MongoDB disconnect method after running all tests 2024-12-12 15:42:09 +01:00
cruizba d320cb2bdd ci: Remove OpenVidu V2 Test from master 2024-12-10 12:54:51 +01:00
juancarmore 07ba255beb Update integration tests workflow to use "development" branch when checking out openvidu-local-deployment and install LiveKit CLI 2024-12-09 19:04:05 +01:00
cruizba 5e4cbadff1 ci: Rename v2 test job 2024-12-09 14:06:20 +01:00
cruizba ee876987c1 Update workflow name for OpenVidu CE V2 tests 2024-12-09 13:58:53 +01:00
cruizba 0fd0b6c11c Fixing v2 e2e tests 2024-12-09 13:58:17 +01:00
pabloFuente 01d01f3d00 openvidu-test-e2e: add simple Firefox subscriber tests 2024-12-09 12:32:36 +01:00
juancarmore 363d352c79 Increase timeout for active entities fixer integration test 2024-12-06 14:08:07 +01:00
juancarmore 40f6bd790d Add GitHub Actions workflow for OpenVidu integration tests 2024-12-06 13:54:45 +01:00
juancarmore a01b1e2a2f integration-tests: Add test for openvidu active entities fixer 2024-12-05 21:04:41 +01:00
juancarmore c2d7f01310 Add integration tests setup with Jest 2024-12-05 21:02:04 +01:00
pabloFuente 5e0af77913 openvidu-test-e2e: fix race condition on client event management 2024-12-05 19:21:21 +01:00
pabloFuente 40ffd97aca openvidu-testpp: fixed versions of livekit dependencies to avoid typescript error TS1046 2024-12-05 12:13:10 +01:00
pabloFuente 09132835fe openvidu-testapp: update package-lock.json 2024-12-05 11:36:23 +01:00
pabloFuente 8c18243c88 openvidu-testapp: print string reasons 2024-12-05 11:35:16 +01:00
pabloFuente 01530fef32 openvidu-test-browsers: fix h264 codec in Selenium-managed Firefox 2024-12-04 17:21:11 +01:00
pabloFuente d1cca5efac openvidu-test-e2e: added force codec and ingress tests 2024-12-02 17:26:21 +01:00
github-actions d7f1d859a0 openvidu-components-angular: Bumped version to 3.0.0 2024-11-29 11:27:08 +00:00
cruizba 235d586d0f Update submodule openvidu for 3.0.0 2024-11-29 11:53:28 +01:00
pabloFuente 57be57d892 openvidu-testapp: fix codec selector 2024-11-27 16:47:50 +01:00
pabloFuente a4b010faba Add enableTranscoding ingress option for WHIP 2024-11-21 16:11:33 +01:00
pabloFuente 18343fa47d openvidu-testapp: add CreateIngressOptions to dialog 2024-11-21 14:54:49 +01:00
pabloFuente 738431b6bf openvidu-testapp: fix error messages 2024-11-18 17:08:47 +01:00
pabloFuente d156db6051 openvidu-testapp: refactor room service to use Node SDKs 2024-11-18 13:59:56 +01:00
Carlos Santos e1634efe39 ov-components: Refactored lang selector template 2024-11-06 12:34:04 +01:00
Carlos Santos ee79e39d4b ov-components: Updated css 2024-11-06 12:33:26 +01:00
Carlos Santos 47ccaf58c7 ov-components: Fixed recording activity panel styles 2024-11-06 11:43:34 +01:00
pabloFuente 0a3129e818 openvidu-testapp: add auto refresh of video track information 2024-11-06 11:32:19 +01:00
pabloFuente baa753ac21 openvidu-test-e2e: add simulcastDisabledDynacastEnabled test 2024-11-06 11:31:46 +01:00
Carlos Santos 6a7ab9b026 ov-components: Updated audio-wave css 2024-11-06 10:49:29 +01:00
Carlos Santos b59c56c3e8 ov-components: Updated css variable names 2024-11-06 10:06:51 +01:00
Carlos Santos 02e3fb0d4e ov-components: Updated css styles for improving custom color support 2024-11-05 17:48:10 +01:00
Carlos Santos ada5984a32 ov-components: Updated css color variables 2024-11-05 12:38:52 +01:00
pabloFuente 1d91d6fa9d openvidu-testapp: make deviceId a proper selector in VideoCaptureOptions 2024-10-28 19:12:16 +01:00
pabloFuente ed2fa45d59 openvidu-testapp: show codec in video track info window 2024-10-28 14:30:22 +01:00
pabloFuente 57d92873da openvidu-testapp: toggle video zoom 2024-10-24 11:37:04 +02:00
Carlos Santos 897404b54b ov-components: Removed unnecessary code
Removed getTokenMetadata method
2024-10-23 16:57:48 +02:00
pabloFuente c53b1b5b81 openvidu-test-e2e: improve test adaptiveStreamDisabledDynacastDisabledTest 2024-10-22 15:25:41 +02:00
pabloFuente 083adb1775 openvidu-testapp: update livekit dependencies 2024-10-22 12:13:11 +02:00
pabloFuente b74f340042 openvidu-test-browsers: move srcObject wait to JS script 2024-10-21 22:03:36 +02:00
pabloFuente 23ac1c3b00 Update to Java 17 2024-10-21 22:03:04 +02:00
pabloFuente cbc8a6b859 openvidu-tesr-browsers: wait for srcObject to be defined when checking media elements 2024-10-21 20:33:43 +02:00
pabloFuente 9f4bcc827e openvidu-test-e2e: reduce tests flakyness with active waits 2024-10-21 13:33:34 +02:00
pabloFuente 333436a90e Fix path in npm script 2024-10-15 15:56:47 +02:00
github-actions 2132d5b984 openvidu-components-angular: Bumped version to 3.0.0-beta3 2024-10-15 13:21:16 +00:00
cruizba 7ff73ac225 Update submodule openvidu for 3.0.0-beta3 2024-10-15 15:03:54 +02:00
Carlos Santos ee57064095 ci: Renamed openvidu components test file 2024-10-11 10:21:00 +02:00
Carlos Santos 28f2405cc6 ov-components: Enhanced tests timeout 2024-10-10 21:14:08 +02:00
Carlos Santos 7f2660abd9 ov-components: Removed unnecessary files 2024-10-10 21:07:23 +02:00
Carlos Santos 5a0aea9ecb ov-components: Used jasmine instead of mocha 2024-10-10 21:01:01 +02:00
Carlos Santos 5a40ce9056 ov-components: Set headless chrome for unit tests 2024-10-10 20:39:16 +02:00
Carlos Santos f309527c26 ci: Run unit tests 2024-10-10 20:32:10 +02:00
Carlos Santos 52422fb58a ov-components: Added unit tests 2024-10-10 20:30:02 +02:00
Carlos Santos 9a7a2e3adb ov-components: Fixed blocking dialog when connection issues 2024-10-10 20:28:48 +02:00
pabloFuente c273ae0109 openvidu-test-e2e: update manyToMany test 2024-10-10 13:18:58 +02:00
pabloFuente f19432af8e openvidu-test-e2e: improve test's flakiness with more checks 2024-10-09 10:47:00 +02:00
pabloFuente 9238becfe3 openvidu-testapp: update livekit dependencies 2024-10-09 10:24:23 +02:00
pabloFuente 8e12739c97 openvidu-test-e2e: fix uri resolution from protocol 2024-10-07 16:43:16 +02:00
Carlos Santos d19f0f7bb5 ov-components: Allowed custom title on admin components 2024-10-07 12:01:08 +02:00
pabloFuente 6bb5937b23 openvidu-test-browsers: remove geckodriver location 2024-10-04 13:28:18 +02:00
pabloFuente 3d60920447 openvidu-test-e2e: add enabled/disabled, Server API Mute and simulcast disabled tests 2024-10-04 13:27:20 +02:00
pabloFuente 0ce0e2acde openvidu-testapp: refactor TrackComponent 2024-10-04 13:25:44 +02:00
pabloFuente 4354561d5a openvidu-test-e2e: add new tests (speaker detection, simulcast, dynacast, adaptive stream) 2024-10-02 01:17:02 +02:00
pabloFuente 88baf99368 openvidu-testapp: new features to test simulcast, dynacast and adaptive stream 2024-10-02 01:14:42 +02:00
pabloFuente fdc3b82122 openvidu-test-e2e: add speaker detection and adaptive stream tests 2024-10-01 14:16:01 +02:00
pabloFuente e307af24e3 openvidu-test-e2e: improved e2e tests 2024-09-30 15:19:33 +02:00
pabloFuente 71598c2d7d openvidu-test-e2e: enable tests (one-to-one audio and video only, massive room) 2024-09-28 12:13:51 +02:00
Carlos Santos 28a0574d99 ov-components: Refactored translate service 2024-09-26 10:26:59 +02:00
Carlos Santos 7336f4f609 ci: Fixed start backend command 2024-09-24 14:47:25 +02:00
Carlos Santos 3d164c6422 ci: Fixed openvidu call backend path 2024-09-24 13:26:19 +02:00
Carlos Santos 72c5aa97ad ov-components: Refactored storage service 2024-09-24 12:51:43 +02:00
Carlos Santos c3b2b4663f ov-components: Exported storage service 2024-09-24 11:30:51 +02:00
Carlos Santos f7d6e81261 ov-components: Fixed selenium-webdriver types 2024-09-23 10:32:45 +02:00
Carlos Santos 8d99c376f7 ov-components: Added node types into tsconfig 2024-09-23 10:21:19 +02:00
Carlos Santos d37f094773 ov-components: Updated dependencies 2024-09-23 10:10:57 +02:00
pabloFuente 5bacf37523 openvidu-testapp: better end scenario logic 2024-09-19 13:37:46 +02:00
pabloFuente 888f0bdb7e openvidu-testapp: complete participant options. Add VideoResolution 2024-09-17 14:46:21 +02:00
pabloFuente 7e7b6d2915 openvidu-testapp: test scenarios. Full LiveKit options dialog 2024-09-17 01:32:41 +02:00
Carlos Santos bd6ba55504 ov-components: Refactored and improved config 2024-09-04 12:19:13 +02:00
Carlos Santos 724fbd9186 ov-components: Refactored config service 2024-08-26 13:20:14 +02:00
Carlos Santos 95b8d055ed ov-components: Exported toolbar model 2024-08-22 11:35:38 +02:00
Carlos Santos 0a6dd252af ov-components: Exported config service to make it public 2024-08-22 11:20:01 +02:00
Carlos Santos a82b2189bc ov-componentes: Added ovToolbarAdditionalButtonsPosition directive 2024-08-22 11:17:02 +02:00
Carlos Santos 9f0246db2f ov-components: Added cdk as peerDependencies 2024-08-22 11:13:37 +02:00
Carlos Santos 64f62dd716 ci: Fixed server start commands 2024-08-12 13:47:48 +02:00
Carlos Santos 33d71f24c3 ov-components: fixed screen sharing e2e 2024-08-02 11:44:57 +02:00
Carlos Santos 7c1066d126 ov-components: Specify screen sharing options 2024-08-02 11:12:52 +02:00
Carlos Santos 5bf5c68da4 ov-components: Updated css classes 2024-08-01 13:34:13 +02:00
Carlos Santos 33d11926a7 ci: Updated e2e test 2024-07-30 18:13:06 +02:00
Carlos Santos 6604d6aa1f ci: Updated build webcomponent step 2024-07-30 17:08:07 +02:00
Carlos Santos 1b93b1a551 ov-components: included fullscreen e2e event test 2024-07-30 16:44:07 +02:00
Carlos Santos 822ef1ad09 ov-components: Refactored screensharing e2e tests 2024-07-30 16:42:51 +02:00
Carlos Santos 2d6ab64d00 ov-components: Fixed chat e2e tests 2024-07-30 16:42:21 +02:00
Carlos Santos 4c9e949133 ci: Added webcomponent e2e tests 2024-07-30 16:34:07 +02:00
Carlos Santos 31dd06c7e8 ci: Updated e2e tests 2024-07-30 16:07:02 +02:00
Carlos Santos 17a6edbe91 ci: Added webcomponent directives e2e tests 2024-07-30 16:01:54 +02:00
Carlos Santos 9fd1c5b746 ov-components: Fixed webcomponent directives e2e tests 2024-07-30 15:48:13 +02:00
Carlos Santos 5072804e67 ov-components: Refactored and fixed device service 2024-07-30 15:45:22 +02:00
Carlos Santos 7b560b447c ov-components: Fixed fullscreen e2e test 2024-07-29 17:52:56 +02:00
Carlos Santos d52dc9b6cf ov-components: Remove old and unnecessary test and refactored code 2024-07-29 13:20:07 +02:00
Carlos Santos b2e50aa53b ci: Run E2E tests in parallel for improved performance 2024-07-29 13:08:37 +02:00
Carlos Santos 5fa86f4f4c ov-components: split ee2 tests into files 2024-07-29 12:59:25 +02:00
Carlos Santos c61bed5a75 ov-components: Fixed e2e tests events 2024-07-29 12:38:35 +02:00
Carlos Santos 27644e8fa5 ci: Commented webcomponent tests 2024-07-26 13:26:53 +02:00
Carlos Santos 9f7efd8ad5 ov-components: Added elements ids for testing 2024-07-26 13:24:02 +02:00
Carlos Santos a7e5f27eb0 ov-components: Updated generate-docs script 2024-07-26 10:30:24 +02:00
Carlos Santos 52564b5f5d ov-components: Avoid null pointer 2024-07-26 10:29:55 +02:00
Carlos Santos 3f1334b65d ci: Fixed ov-components test workflow running openvidu-call-backend 2024-07-24 18:32:12 +02:00
Carlos Santos 110cbc130f ci: Fixed start test app in e2e workflow 2024-07-24 18:07:32 +02:00
Carlos Santos 5097dc925e ov-components: fixed bundle path 2024-07-24 18:07:13 +02:00
Carlos Santos 551bc76ba1 ci: Wait for openvidu-local-deployment and testapp 2024-07-24 17:29:02 +02:00
Carlos Santos 25ef634757 ci: Updated openvidu-components e2e tests 2024-07-24 17:21:19 +02:00
Carlos Santos aa437be8cf ov-components: Updated dependencies 2024-07-24 17:02:15 +02:00
Carlos Santos d02b12141b ov-components: Refactored and cleaned code 2024-07-24 16:59:40 +02:00
Carlos Santos f28652b425 ov-components: Added missing services to providers section module
Fixed virtual background feature
2024-07-24 16:58:28 +02:00
Carlos Santos fc17b3bf9e ov-components: Fixed base href when playing and downloading recordings
Added base href to video src and downloading link for fixing the recordings features
2024-07-17 15:54:07 +02:00
github-actions d8033dcde4 openvidu-components-angular: Bumped version to 3.0.0-beta2 2024-07-16 12:56:54 +00:00
pabloFuente d6d828d3ea Update reference to openvidu-livekit 2024-07-16 12:38:21 +02:00
github-actions db28c79ee8 openvidu-components-angular: Bumped to version 3.0.0-beta2-dev.20240704 2024-07-04 16:13:56 +00:00
pabloFuente 7a7a9d6582 Add openvidu-livekit as git submodule 2024-07-02 19:23:37 +02:00
pabloFuente eccc37156a OpenVidu v3 2024-07-02 19:19:05 +02:00
Carlos Santos d9bc9b3700 ov-components: Added spinner while joining session 2024-06-28 13:32:09 +02:00
Carlos Santos db2748367c ov-webvomponent: Increased max limit bundle. 2024-06-25 14:45:11 +02:00
Carlos Santos 9fcc593fb2 openvidu-angular-v2compatibility: Bumped to 3.0.0-beta1 2024-06-25 13:14:59 +02:00
Carlos Santos 027ad510dc openvidu-java-client-v2compatibility: Bumped to 3.0.0-beta1 2024-06-25 12:51:09 +02:00
Carlos Santos 5f51f7446f openvidu-node-client-v2compatibility: Bumped to 3.0.0-beta1 2024-06-25 12:50:14 +02:00
cruizba 649a2efdab Change openvidu-java-client to 3.0.0-beta0 2024-06-25 11:56:10 +02:00
Carlos Santos 07e4ede64d ov-components: Fixed recording panel elements 2024-06-24 18:10:47 +02:00
Carlos Santos 5dd689adc6 node-client: Bump to 3.0.0-dev5 2024-06-24 16:49:34 +02:00
Carlos Santos 40c19cedca ov-components: Bump to 3.0.0-dev6 2024-06-24 16:49:15 +02:00
Carlos Santos 6ec78e5522 ov-components: Used openvidu-browser-v2compatibility dependency 2024-06-24 16:48:02 +02:00
Carlos Santos 0b89603e1b ov-components: Updated README 2024-06-24 16:19:42 +02:00
Carlos Santos 82264d436e ov-components: Bump to 3.0.0-dev5 2024-06-24 16:19:33 +02:00
Carlos Santos 81fa53b5bf Added v2compatibility suffix to java-client 2024-06-21 11:39:13 +02:00
Carlos Santos 30c705e8fc Added v2compatibility suffix to node-client 2024-06-21 11:30:34 +02:00
Carlos Santos 6698ae2314 ov-components: Added v2compatibility suffix to library and webcomponent 2024-06-21 11:20:34 +02:00
1110 changed files with 85859 additions and 161828 deletions

View File

@ -1,113 +0,0 @@
name: OpenVidu CE Tests
on:
push:
branches:
- master
paths-ignore:
- ".github/workflows/openvidu-components-angular-E2E.yml"
- "openvidu-components-angular/**"
- "openvidu-server/docker/**"
- "openvidu-server/deployments/**"
pull_request:
branches:
- master
workflow_dispatch:
inputs:
TEST_IMAGE:
description: "Docker image where to run the tests"
required: true
default: "openvidu/openvidu-test-e2e:22.04"
KURENTO_JAVA_COMMIT:
description: 'Commit to use in kurento-java dependencies. If "default" the release version declared in property "version.kurento" of openvidu-parent/pom.xml will be used'
required: true
default: "default"
KURENTO_MEDIA_SERVER_IMAGE:
description: "Docker image of kurento-media-server"
required: true
default: "kurento/kurento-media-server:7.0.1"
DOCKER_RECORDING_VERSION:
description: "Force version of openvidu/openvidu-recording container"
required: true
default: "default"
CHROME_VERSION:
description: "Version of Chrome to use. Must be a valid image tag from https://hub.docker.com/r/selenium/standalone-chrome/tags"
required: true
default: "latest"
FIREFOX_VERSION:
description: "Version of Firefox to use. Must be a valid image tag from https://hub.docker.com/r/selenium/standalone-firefox/tags"
required: true
default: "latest"
EDGE_VERSION:
description: "Version of Edge to use. Must be a valid image tag from https://hub.docker.com/r/selenium/standalone-edge/tags"
required: true
default: "latest"
jobs:
main:
runs-on: ubuntu-latest
container:
image: ${{ inputs.TEST_IMAGE || 'openvidu/openvidu-test-e2e:22.04' }}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/openvidu:/opt/openvidu
env:
TEST_IMAGE: ${{ inputs.TEST_IMAGE || 'openvidu/openvidu-test-e2e:22.04' }}
KURENTO_SNAPSHOTS_URL: ${{ secrets.KURENTO_SNAPSHOTS_URL }}
KURENTO_MEDIA_SERVER_IMAGE: ${{ inputs.KURENTO_MEDIA_SERVER_IMAGE || 'kurento/kurento-media-server:7.0.1' }}
KURENTO_JAVA_COMMIT: ${{ inputs.KURENTO_JAVA_COMMIT || 'default' }}
DOCKER_RECORDING_VERSION: ${{ inputs.DOCKER_RECORDING_VERSION || 'default' }}
CHROME_VERSION: ${{ inputs.CHROME_VERSION || 'latest' }}
FIREFOX_VERSION: ${{ inputs.FIREFOX_VERSION || 'latest' }}
EDGE_VERSION: ${{ inputs.EDGE_VERSION || 'latest' }}
steps:
- uses: actions/checkout@v3
- name: Setup scripts
run: |
curl -sOJ --output-dir /opt https://raw.githubusercontent.com/OpenVidu/openvidu/master/ci-scripts/commons/build.sh
curl -sOJ --output-dir /opt https://raw.githubusercontent.com/OpenVidu/openvidu/master/ci-scripts/commons/test-utils.sh
cp ci-scripts/openvidu-e2e-tests.sh /opt/openvidu-e2e-tests.sh
find /opt/*.sh -type f -print0 | xargs -0 chmod u+x
- name: Clean environment
run: /opt/build.sh --clean-environment
- name: Prepare test environment
run: /opt/test-utils.sh --prepare-test-environment "${TEST_IMAGE}"
- name: Check and prepare kurento snapshots
run: /opt/build.sh --check-and-prepare-kurento-snapshot
- name: Use specific kurento-java commit
if: ${{ env.KURENTO_JAVA_COMMIT != 'default'}}
run: /opt/test-utils.sh --use-specific-kurento-java-commit
- name: Build openvidu-browser
run: /opt/build.sh --build-openvidu-browser
- name: Build openvidu-node-client
run: /opt/build.sh --build-openvidu-node-client
- name: Build openvidu-java-client
run: /opt/build.sh --build-openvidu-java-client
- name: Build openvidu-parent
run: /opt/build.sh --build-openvidu-parent
- name: Build openvidu-testapp
run: /opt/build.sh --build-openvidu-testapp
- name: Build openvidu-server dashboard
run: /opt/build.sh --build-openvidu-server-dashboard true
- name: Build openvidu-server
run: /opt/build.sh --build-openvidu-server
- name: openvidu-server unit tests
run: /opt/openvidu-e2e-tests.sh --openvidu-server-unit-tests
- name: openvidu-server integration tests
run: /opt/openvidu-e2e-tests.sh --openvidu-server-integration-tests
- name: Environment launch Kurento
run: /opt/openvidu-e2e-tests.sh --environment-launch-kurento
- name: Serve openvidu-testapp
run: /opt/test-utils.sh --serve-openvidu-testapp
- name: OpenVidu E2E Tests Kurento
run: /opt/openvidu-e2e-tests.sh --openvidu-e2e-tests-kurento
- name: Test reports
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
report_paths: "**/target/surefire-reports/TEST-*.xml"
- name: Upload logs
uses: actions/upload-artifact@v3
if: always() # always run even if the previous step fails
with:
name: Logs
path: |
/opt/openvidu/*.log

View File

@ -1,158 +0,0 @@
name: openvidu-components-angular E2E
on:
push:
paths:
- 'openvidu-components-angular/**'
- 'openvidu-browser/**'
- 'openvidu-node-client/**'
- '.github/workflows/openvidu-components-angular-E2E.yml'
pull_request:
branches:
- master
workflow_dispatch:
inputs:
commit_sha:
description: 'Commit SHA'
required: false
default: ''
jobs:
test_setup:
name: Test setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Commit URL
run: echo https://github.com/OpenVidu/openvidu/commit/${{ inputs.commit_sha || github.sha }}
- name: Send repository dispatch event
env:
GITHUB_TOKEN: ${{ secrets.OPENVIDU_DISPATCH_EVENT_GA }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message || 'Manually' }}
COMMIT_URL: ${{ github.event.commits[0].url || 'Manually' }}
BRANCH_NAME: ${{ github.ref_name }}
run: |
curl \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
https://api.github.com/repos/OpenVidu/openvidu-call/dispatches \
-d '{"event_type":"openvidu-components-angular","client_payload":{"commit-message":"'"$COMMIT_MESSAGE"'","commit-ref":"'"$COMMIT_URL"'", "branch-name":"'"$BRANCH_NAME"'"}}'
- name: Build openvidu-browser
run: |
cd openvidu-browser
npm install
npm run build && \
npm pack
- uses: actions/upload-artifact@v4
with:
name: openvidu-browser
path: openvidu-browser/openvidu-browser-*.tgz
openvidu_angular_e2e:
needs: test_setup
name: OpenVidu Angular E2E tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: actions/download-artifact@v4
with:
name: openvidu-browser
path: openvidu-components-angular
- name: Run Browserless Chrome
run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run openvidu-server-kms
run: |
docker run -p 4443:4443 --rm -d \
-e OPENVIDU_SECRET=MY_SECRET \
openvidu/openvidu-dev:latest
- name: Install openvidu-browser and dependencies
run: |
cd openvidu-components-angular
npm install openvidu-browser-*.tgz
- name: Build openvidu-angular
run: npm run lib:build --prefix openvidu-components-angular
- name: Build openvidu-angular-testapp
run: npm run build --prefix openvidu-components-angular
- name: Serve openvidu-angular-testapp
run: npm run start-prod --prefix openvidu-components-angular &
- name: Run openvidu-angular E2E
run: npm run lib:e2e-ci --prefix openvidu-components-angular
webcomponent_e2e:
needs: test_setup
name: Webcomponent E2E CE tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: actions/download-artifact@v4
with:
name: openvidu-browser
path: openvidu-components-angular
- name: Run Browserless Chrome
run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run openvidu-server-kms
run: |
docker run -p 4443:4443 --rm -d \
-e OPENVIDU_SECRET=MY_SECRET \
openvidu/openvidu-dev:latest
- name: Install openvidu-browser and dependencies
run: |
cd openvidu-components-angular
npm install openvidu-browser-*.tgz
- name: Build openvidu-angular
run: npm run lib:build --prefix openvidu-components-angular
- name: Build openvidu-webcomponent
run: npm run webcomponent:build --prefix openvidu-components-angular
- name: Serve Webcomponent Testapp
run: npm run webcomponent:serve-testapp --prefix openvidu-components-angular &
- name: Run Webcomponent E2E
run: npm run webcomponent:e2e-ci --prefix openvidu-components-angular
webcomponent_e2e_pro:
if: false #Skip PRO test because infra is unstable
needs: test_setup
name: Webcomponent E2E PRO tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: actions/download-artifact@v4
with:
name: openvidu-browser
path: openvidu-components-angular
- name: Run Browserless Chrome
run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Install openvidu-browser and dependencies
run: |
cd openvidu-components-angular
npm install openvidu-browser-*.tgz
- name: Build openvidu-angular
run: npm run lib:build --prefix openvidu-components-angular
- name: Build openvidu-webcomponent
run: npm run webcomponent:build --prefix openvidu-components-angular
- name: Serve Webcomponent Testapp
run: npm run webcomponent:serve-testapp --prefix openvidu-components-angular &
- name: Run Webcomponent E2E PRO
env:
OPENVIDU_SERVER_URL: ${{ secrets.OPENVIDU_CALL_NEXT_URL }}
OPENVIDU_SECRET: ${{ secrets.OPENVIDU_CALL_NEXT_SECRET }}
run: npm run webcomponent:e2e-pro-ci --prefix openvidu-components-angular

View File

@ -0,0 +1,445 @@
name: openvidu-components-angular Tests
on:
push:
branches:
- master
paths:
- 'openvidu-components-angular/**'
- '.github/workflows/openvidu-components-angular-tests.yml'
pull_request:
branches:
- master
workflow_dispatch:
inputs:
commit_sha:
description: 'Commit SHA'
required: false
default: ''
jobs:
test_setup:
name: Test setup
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Commit URL
run: echo https://github.com/OpenVidu/openvidu/commit/${{ inputs.commit_sha || github.sha }}
- name: Send Dispatch Event
env:
GITHUB_TOKEN: ${{ secrets.OPENVIDU_DISPATCH_EVENT_GA }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message || 'Manually' }}
COMMIT_URL: ${{ github.event.commits[0].url || 'Manually' }}
BRANCH_NAME: ${{ github.ref_name }}
run: |
curl -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
https://api.github.com/repos/OpenVidu/openvidu-call/dispatches \
-d '{"event_type":"openvidu-components-angular","client_payload":{"commit-message":"'"$COMMIT_MESSAGE"'","commit-ref":"'"$COMMIT_URL"'", "branch-name":"'"$BRANCH_NAME"'"}}'
nested_events:
needs: test_setup
name: Nested events
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Install dependencies
run: |
cd openvidu-components-angular
npm install
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run nested components E2E event tests
env:
LAUNCH_MODE: CI
run: npm run e2e:nested-events --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
nested_structural_directives:
needs: test_setup
name: Nested Structural Directives
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run nested structural directives tests
env:
LAUNCH_MODE: CI
run: npm run e2e:nested-structural-directives --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
nested_attribute_directives:
needs: test_setup
name: Nested Attribute Directives
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run nested attribute directives tests
env:
LAUNCH_MODE: CI
run: npm run e2e:nested-attribute-directives --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_directives:
needs: test_setup
name: API Directives Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-directives --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_internal_directives:
needs: test_setup
name: Internal Directives Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-internal-directives --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_chat:
needs: test_setup
name: Chat E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-chat --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_events:
needs: test_setup
name: Events E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-events --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_media_devices:
needs: test_setup
name: Media devices E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-media-devices --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_panels:
needs: test_setup
name: Panels E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-panels --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_screen_sharing:
needs: test_setup
name: Screen sharing E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-screensharing --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_stream:
needs: test_setup
name: Stream E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -v $(pwd)/openvidu-components-angular/e2e/assets:/e2e-assets selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-stream --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_toolbar:
needs: test_setup
name: Toolbar E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Webcomponent E2E
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-toolbar --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main

View File

@ -0,0 +1,56 @@
name: OpenVidu integration tests
on:
push:
branches:
- master
paths:
- "openvidu-test-integration/**"
- ".github/workflows/openvidu-integration-tests.yml"
workflow_dispatch:
jobs:
integration-tests:
name: Integration tests
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- name: Configure OpenVidu Local Deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
with:
ref-openvidu-local-deployment: development
pre_startup_commands: |
sed -i 's/interval: 10s/interval: 1s/' livekit.yaml
sed -i '/interval: 1s/a \ fixer_interval: 10s' livekit.yaml
- name: Install LiveKit CLI
run: |
curl -sSL https://get.livekit.io/cli | bash
- name: Checkout current repository
uses: actions/checkout@v4
with:
path: openvidu
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
working-directory: ./openvidu/openvidu-test-integration
run: npm ci
- name: Run tests
working-directory: ./openvidu/openvidu-test-integration
run: npm run test:ci
- name: Upload report
uses: actions/upload-artifact@v4
if: always()
with:
name: openvidu-integration-tests-report
path: ./openvidu/openvidu-test-integration/test-results.json
retention-days: 7
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "openvidu-livekit"]
path = openvidu-livekit
url = https://github.com/OpenVidu/openvidu-livekit.git

View File

@ -39,7 +39,7 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com
OpenVidu has been supported under project "CPP2021-008720 NewGenVidu: An elastic, user-friendly and privacy-friendly videoconferencing platform", funded by MCIN/AEI/10.13039/501100011033 and by the European Union-NextGenerationEU/PRTR.
<img height="75px" src="https://openvidu.io/img/logos/support.jpg">
<img height="75px" src="https://docs.openvidu.io/en/stable/img/logos/support.jpg">
## Sponsors

View File

@ -1,248 +0,0 @@
#!/bin/bash -x
set -eu -o pipefail
############################################################################
# Any function offered by this file that is not path agnostic assumes that #
# the path is located where the first command of each function requires it #
############################################################################
CLEAN_ENVIRONMENT=false
BUILD_OV_BROWSER=false
BUILD_OV_NODE_CLIENT=false
BUILD_OV_JAVA_CLIENT=false
BUILD_OV_PARENT=false
BUILD_OV_TESTAPP=false
BUILD_OV_SERVER_DASHBOARD=false
BUILD_OV_SERVER=false
BUILD_OV_SERVER_DEPENDENCY=false
BUILD_OV_SERVER_PRO_INSPECTOR=false
BUILD_OV_SERVER_PRO=false
CHECK_AND_PREPARE_KURENTO_SNAPSHOT=false
if [[ -n ${1:-} ]]; then
case "${1:-}" in
--clean-environment)
CLEAN_ENVIRONMENT=true
;;
--build-openvidu-browser)
BUILD_OV_BROWSER=true
;;
--build-openvidu-node-client)
BUILD_OV_NODE_CLIENT=true
;;
--build-openvidu-java-client)
BUILD_OV_JAVA_CLIENT=true
;;
--build-openvidu-parent)
BUILD_OV_PARENT=true
;;
--build-openvidu-testapp)
BUILD_OV_TESTAPP=true
;;
--build-openvidu-server-dashboard)
if [[ -z "${2:-}" ]]; then
echo "Must provide LINK_LOCAL_DEPENDENCIES as 1st parameter"
exit 1
fi
BUILD_OV_SERVER_DASHBOARD=true
LINK_LOCAL_DEPENDENCIES="${2}"
;;
--build-openvidu-server)
BUILD_OV_SERVER=true
;;
--build-openvidu-server-dependency)
BUILD_OV_SERVER_DEPENDENCY=true
;;
--build-openvidu-server-pro-inspector)
if [[ -z "${2:-}" ]]; then
echo "Must provide LINK_LOCAL_DEPENDENCIES as 1st parameter"
exit 1
fi
BUILD_OV_SERVER_PRO_INSPECTOR=true
LINK_LOCAL_DEPENDENCIES="${2}"
;;
--build-openvidu-server-pro)
BUILD_OV_SERVER_PRO=true
;;
--check-and-prepare-kurento-snapshot)
CHECK_AND_PREPARE_KURENTO_SNAPSHOT=true
;;
*)
echo "Unrecognized method $1"
exit 1
;;
esac
else
echo "Must provide a method to execute as first parameter when calling the script"
exit 1
fi
# -------------
# Clean environment
# -------------
if [[ "${CLEAN_ENVIRONMENT}" == true ]]; then
# Remove all running containers except test container and runner container
ids=$(docker ps -a -q)
for id in $ids; do
DOCKER_IMAGE=$(docker inspect --format='{{.Config.Image}}' $id)
if [[ "${DOCKER_IMAGE}" != *"$TEST_IMAGE"* ]] &&
[[ "${DOCKER_IMAGE}" != *"runner-image"* ]]; then
echo "Removing container image '$DOCKER_IMAGE' with id '$id'"
docker stop $id && docker rm $id
fi
done
# Clean /opt/openvidu contents
rm -rf /opt/openvidu/*
fi
# -------------
# Build openvidu-browser
# -------------
if [[ "${BUILD_OV_BROWSER}" == true ]]; then
pushd openvidu-browser || exit 1
npm install
npm run build
npm link
npm pack
mv openvidu-browser-*.tgz /opt/openvidu
npm run browserify
npm run browserify-prod
popd
fi
# -------------
# Build openvidu-node-client
# -------------
if [[ "${BUILD_OV_NODE_CLIENT}" == true ]]; then
pushd openvidu-node-client
npm install
npm run build
npm link
npm pack
mv openvidu-node-client-*.tgz /opt/openvidu
popd
fi
# -------------
# Build openvidu-java-client
# -------------
if [[ "${BUILD_OV_JAVA_CLIENT}" == true ]]; then
pushd openvidu-java-client
MVN_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
mvn -B clean compile package
mvn -B install:install-file -Dfile=target/openvidu-java-client-${MVN_VERSION}.jar \
-DgroupId=io.openvidu \
-DartifactId=openvidu-java-client \
-Dversion=${MVN_VERSION} -Dpackaging=jar
popd
fi
# -------------
# Build openvidu-parent
# -------------
if [[ "${BUILD_OV_PARENT}" == true ]]; then
mvn -B -DskipTests=true -Dmaven.artifact.threads=1 clean install
fi
# -------------
# Build openvidu-testapp
# -------------
if [[ "${BUILD_OV_TESTAPP}" == true ]]; then
pushd openvidu-testapp
npm install
npm link openvidu-browser openvidu-node-client
export NG_CLI_ANALYTICS="false" && ./node_modules/@angular/cli/bin/ng.js build --configuration production --output-path=/opt/openvidu/testapp
popd
fi
# -------------
# Build openvidu-server dashboard
# -------------
if [[ "${BUILD_OV_SERVER_DASHBOARD}" == true ]]; then
pushd openvidu-server/src/dashboard
npm install
if [[ "${LINK_LOCAL_DEPENDENCIES}" == true ]]; then
npm link openvidu-browser openvidu-node-client
fi
npm run build-prod
popd
fi
# -------------
# Build openvidu-server
# -------------
if [[ "${BUILD_OV_SERVER}" == true ]]; then
pushd openvidu-server
mvn -B -DskipTests=true clean package
mv target/openvidu-server-*.jar /opt/openvidu
popd
fi
# -------------
# Build openvidu-server dependency
# -------------
if [[ "${BUILD_OV_SERVER_DEPENDENCY}" == true ]]; then
pushd openvidu-server
mvn -B -DskipTests=true -Pdependency clean install
popd
fi
# -------------
# Build Inspector
# -------------
if [[ "${BUILD_OV_SERVER_PRO_INSPECTOR}" == true ]]; then
pushd dashboard
npm install
if [[ "${LINK_LOCAL_DEPENDENCIES}" == true ]]; then
npm link openvidu-browser openvidu-node-client
fi
npm run build-server-prod
popd
fi
# -------------
# Build openvidu-server-pro
# -------------
if [[ "${BUILD_OV_SERVER_PRO}" == true ]]; then
pushd openvidu-server-pro
mvn -B -DskipTests=true clean package
mv target/openvidu-server-pro-*.jar /opt/openvidu
popd
fi
# -------------
# Check kurento version from pom.xml
# If kurento version is a snapshot, configure snapshot builds
# -------------
if [[ "${CHECK_AND_PREPARE_KURENTO_SNAPSHOT}" == true ]]; then
# Check if kurento version is a snapshot
KURENTO_VERSION=$(awk -F'[<>]' '/<version.kurento>/ {print $3}' pom.xml)
if [[ "${KURENTO_VERSION}" == *"-SNAPSHOT" ]] && [[ -n "${KURENTO_SNAPSHOTS_URL:-}" ]]; then
echo "Kurento version is a SNAPSHOT: ${KURENTO_VERSION}"
mkdir -p /etc/maven
chmod -R 777 /etc/maven
pushd /etc/maven
rm -f settings.xml
curl https://raw.githubusercontent.com/OpenVidu/openvidu/master/ci-scripts/kurento-snapshots.xml -o settings.xml
sed -i "s|KURENTO_SNAPSHOTS_URL|${KURENTO_SNAPSHOTS_URL}|g" settings.xml
popd
else
echo "Kurento version is not a SNAPSHOT: ${KURENTO_VERSION}"
fi
fi

View File

@ -1,307 +0,0 @@
#!/bin/bash -x
set -eu -o pipefail
############################################################################
# Any function offered by this file that is not path agnostic assumes that #
# the path is located where the first command of each function requires it #
############################################################################
# Bump versions
BUMP_NPM_PROJECT_VERSION=false
BUMP_NPM_DEPENDENCY_VERSION=false
BUMP_MAVEN_PROJECT_VERSION=false
BUMP_MAVEN_PROPERTY_VERSION=false
BUMP_MAVEN_DEPENDENCY_VERSION=false
BUMP_DOCKER_COMPOSE_SERVICE_VERSION=false
BUMP_DOCKER_COMPOSE_HEADER_VERSION=false
BUMP_DOCKER_IMAGE_VERSION_IN_FILES=false
BUMP_APPLICATION_PROPERTIES_VAR_VALUE=false
WAIT_FOR_NPM_DEPENDENCY=false
GENERIC_SED=false
if [[ -n ${1:-} ]]; then
case "${1:-}" in
--bump-npm-project-version)
if [[ -z "${2:-}" ]]; then
echo "Must provide VERSION as 1st parameter"
exit 1
fi
BUMP_NPM_PROJECT_VERSION=true
VERSION="${2}"
;;
--bump-npm-dependency-version)
if [[ -z "${2:-}" ]]; then
echo "Must provide DEPENDENCY as 1st parameter"
exit 1
fi
if [[ -z "${3:-}" ]]; then
echo "Must provide VERSION as 2nd parameter"
exit 1
fi
BUMP_NPM_DEPENDENCY_VERSION=true
DEPENDENCY="${2}"
VERSION="${3}"
TYPE_OF_DEPENDENCY="${4:-dependencies}" # [dependencies, devDependencies, peerDependencies, optionalDependencies]
;;
--bump-maven-project-version)
if [[ -z "${2:-}" ]]; then
echo "Must provide VERSION as 1st parameter"
exit 1
fi
BUMP_MAVEN_PROJECT_VERSION=true
VERSION="${2}"
;;
--bump-maven-property-version)
if [[ -z "${2:-}" ]]; then
echo "Must provide PROPERTY as 1st parameter"
exit 1
fi
if [[ -z "${3:-}" ]]; then
echo "Must provide VERSION as 2nd parameter"
exit 1
fi
BUMP_MAVEN_PROPERTY_VERSION=true
PROPERTY="${2}"
VERSION="${3}"
;;
--bump-maven-dependency-version)
if [[ -z "${2:-}" ]]; then
echo "Must provide DEPENDENCY as 1st parameter"
exit 1
fi
if [[ -z "${3:-}" ]]; then
echo "Must provide VERSION as 2nd parameter"
exit 1
fi
BUMP_MAVEN_DEPENDENCY_VERSION=true
DEPENDENCY="${2}"
VERSION="${3}"
;;
--bump-docker-compose-service-version)
if [[ -z "${2:-}" ]]; then
echo "Must provide DOCKER_COMPOSE_FILE as 1st parameter"
exit 1
fi
if [[ -z "${3:-}" ]]; then
echo "Must provide SERVICE_IMAGE as 2nd parameter"
exit 1
fi
if [[ -z "${4:-}" ]]; then
echo "Must provide VERSION as 3rd parameter"
exit 1
fi
BUMP_DOCKER_COMPOSE_SERVICE_VERSION=true
DOCKER_COMPOSE_FILE="${2}"
SERVICE_IMAGE="${3}"
VERSION="${4}"
;;
--bump-docker-compose-header-version)
if [[ -z "${2:-}" ]]; then
echo "Must provide DOCKER_COMPOSE_FILE as 1st parameter"
exit 1
fi
if [[ -z "${3:-}" ]]; then
echo "Must provide HEADER as 2nd parameter"
exit 1
fi
if [[ -z "${4:-}" ]]; then
echo "Must provide VERSION as 3rd parameter"
exit 1
fi
BUMP_DOCKER_COMPOSE_HEADER_VERSION=true
DOCKER_COMPOSE_FILE="${2}"
HEADER="${3}"
VERSION="${4}"
;;
--bump-docker-image-version-in-files)
if [[ -z "${3:-}" ]]; then
echo "Must provide FILE_NAME_PATTERN as 1st parameter"
exit 1
fi
if [[ -z "${4:-}" ]]; then
echo "Must provide IMAGE as 2nd parameter"
exit 1
fi
if [[ -z "${4:-}" ]]; then
echo "Must provide VERSION as 3rd parameter"
exit 1
fi
BUMP_DOCKER_IMAGE_VERSION_IN_FILES=true
FILE_NAME_PATTERN="${2}"
IMAGE="${3}"
VERSION="${4}"
;;
--bump-application-properties-var-value)
if [[ -z "${3:-}" ]]; then
echo "Must provide APPLICATION_PROPERTIES_FILE as 2nd parameter"
exit 1
fi
if [[ -z "${4:-}" ]]; then
echo "Must provide VARIABLE as 3rd parameter"
exit 1
fi
if [[ -z "${4:-}" ]]; then
echo "Must provide VALUE as 4th parameter"
exit 1
fi
BUMP_APPLICATION_PROPERTIES_VAR_VALUE=true
APPLICATION_PROPERTIES_FILE="${2}"
VARIABLE="${3}"
VALUE="${4}"
;;
--wait-for-npm-dependency)
if [[ -z "${2:-}" ]]; then
echo "Must provide DEPENDENCY as 1st parameter"
exit 1
fi
if [[ -z "${3:-}" ]]; then
echo "Must provide VERSION as 2nd parameter"
exit 1
fi
WAIT_FOR_NPM_DEPENDENCY=true
DEPENDENCY="${2}"
VERSION="${3}"
;;
--generic-sed)
if [[ -z "${2:-}" ]]; then
echo "Must provide FILE as 1st parameter"
exit 1
fi
if [[ -z "${3:-}" ]]; then
echo "Must provide SED_EXPRESSION as 2nd parameter"
exit 1
fi
GENERIC_SED=true
FILE="${2}"
SED_EXPRESSION="${3}"
;;
*)
echo "Unrecognized method $1"
exit 1
;;
esac
else
echo "Must provide a method to execute as first parameter when calling the script"
exit 1
fi
compareFiles() {
if cmp -s "$1" "$1-AUX"; then
rm -f $1-AUX
echo "Error: no changes has been made to $1"
echo "Trying to change \"$2\" to \"$3\""
exit 1
else
cp -f "$1-AUX" "$1"
rm -f "$1-AUX"
fi
}
# -------------
# Bump NPM project version
# -------------
if [[ "${BUMP_NPM_PROJECT_VERSION}" == true ]]; then
npm version ${VERSION} --git-tag-version=false --commit-hooks=false
fi
# -------------
# Bump NPM project dependency
# -------------
if [[ "${BUMP_NPM_DEPENDENCY_VERSION}" == true ]]; then
jq -j ".${TYPE_OF_DEPENDENCY}.\"${DEPENDENCY}\" = \"${VERSION}\"" package.json >package.json-AUX
compareFiles package.json version $VERSION
fi
# -------------
# Bump Maven project version
# -------------
if [[ "${BUMP_MAVEN_PROJECT_VERSION}" == true ]]; then
cp pom.xml pom.xml-AUX
mvn -DskipTests=true versions:set -DnewVersion="${VERSION}"
mv pom.xml changed-pom.xml && mv pom.xml-AUX pom.xml && mv changed-pom.xml pom.xml-AUX
compareFiles pom.xml "<version>" $VERSION
fi
# -------------
# Bump Maven project property
# -------------
if [[ "${BUMP_MAVEN_PROPERTY_VERSION}" == true ]]; then
mvn --batch-mode \
-DskipTests=true \
versions:set-property \
-Dproperty="${PROPERTY}" \
-DnewVersion="${VERSION}"
fi
# -------------
# Bump Maven dependency property
# -------------
if [[ "${BUMP_MAVEN_DEPENDENCY_VERSION}" == true ]]; then
mvn --batch-mode versions:use-dep-version -Dincludes=$DEPENDENCY -DdepVersion=$VERSION -DforceVersion=true
fi
# -------------
# Bump docker-compose.yml service version
# -------------
if [[ "${BUMP_DOCKER_COMPOSE_SERVICE_VERSION}" == true ]]; then
sed -r "s|image:\s+${SERVICE_IMAGE}:[[:alnum:]._-]+|image: ${SERVICE_IMAGE}:${VERSION}|g" ${DOCKER_COMPOSE_FILE} >${DOCKER_COMPOSE_FILE}-AUX
compareFiles $DOCKER_COMPOSE_FILE $SERVICE_IMAGE $VERSION
fi
# -------------
# Bump docker-compose.yml header version
# -------------
if [[ "${BUMP_DOCKER_COMPOSE_HEADER_VERSION}" == true ]]; then
sed -r "s|#\s+${HEADER}:\s+[[:alnum:]._-]+|# ${HEADER}: ${VERSION}|g" ${DOCKER_COMPOSE_FILE} >${DOCKER_COMPOSE_FILE}-AUX
compareFiles $DOCKER_COMPOSE_FILE $HEADER $VERSION
fi
# -------------
# Bump Docker image version in files
# -------------
if [[ "${BUMP_DOCKER_IMAGE_VERSION_IN_FILES}" == true ]]; then
find . -type f -name ${FILE_NAME_PATTERN} | xargs sed -i -r "s|${IMAGE}:[[:alnum:]._-]+|${IMAGE}:${VERSION}|g"
fi
# -------------
# Bump application.properties variable value
# -------------
if [[ "${BUMP_APPLICATION_PROPERTIES_VAR_VALUE}" == true ]]; then
sed -r "s%${VARIABLE}((:|=)\s*).*$%${VARIABLE}\1${VALUE}%g" ${APPLICATION_PROPERTIES_FILE} >${APPLICATION_PROPERTIES_FILE}-AUX
compareFiles $APPLICATION_PROPERTIES_FILE $VARIABLE $VALUE
fi
# -------------
# Wait for NPM dependency to be available
# -------------
if [[ "${WAIT_FOR_NPM_DEPENDENCY}" == true ]]; then
CHECK_VERSION_AVAILABILTY="npm show ${DEPENDENCY}@${VERSION} version || echo ''"
VERSION_AUX=$(eval "${CHECK_VERSION_AVAILABILTY}")
until [[ "${VERSION_AUX}" == "${VERSION}" ]]; do
echo "Waiting for ${DEPENDENCY}@${VERSION} to be available in NPM..."
sleep 2
VERSION_AUX=$(eval "${CHECK_VERSION_AVAILABILTY}")
done
echo "${DEPENDENCY}@${VERSION} already available in NPM"
fi
# -------------
# Generic sed replacement
# -------------
if [[ "${GENERIC_SED}" == true ]]; then
sed -r "$SED_EXPRESSION" ${FILE} >${FILE}-AUX
compareFiles $FILE "(generic sed)" "$SED_EXPRESSION"
fi

View File

@ -1,161 +0,0 @@
#!/bin/bash -x
set -eu -o pipefail
############################################################################
# Any function offered by this file that is not path agnostic assumes that #
# the path is located where the first command of each function requires it #
############################################################################
# CI flags
PREPARE_TEST_ENVIRONMENT=false
USE_SPECIFIC_KURENTO_JAVA_COMMIT=false
SERVE_OV_TESTAPP=false
CHECK_AND_PREPARE_KURENTO_SNAPSHOT=false
if [[ -n ${1:-} ]]; then
case "${1:-}" in
--prepare-test-environment)
PREPARE_TEST_ENVIRONMENT=true
;;
--check-and-prepare-kurento-snapshot)
CHECK_AND_PREPARE_KURENTO_SNAPSHOT=true
;;
--use-specific-kurento-java-commit)
USE_SPECIFIC_KURENTO_JAVA_COMMIT=true
;;
--serve-openvidu-testapp)
SERVE_OV_TESTAPP=true
;;
*)
echo "Unrecognized method $1"
exit 1
;;
esac
else
echo "Must provide a method to execute as first parameter when calling the script"
exit 1
fi
# -------------
# Prepare build
# -------------
if [[ "${PREPARE_TEST_ENVIRONMENT}" == true ]]; then
# Connect e2e test container to network bridge so it is vissible for browser and media server containers
if [[ -n "${TEST_IMAGE}" ]]; then
E2E_CONTAINER_ID="$(docker ps | grep "$TEST_IMAGE" | awk '{ print $1 }')" || echo "Docker container not found for image ${TEST_IMAGE}"
if [[ -n "${E2E_CONTAINER_ID}" ]]; then
docker network connect bridge "${E2E_CONTAINER_ID}"
else
echo "Could not connect test docker container to docker bridge, because no running container was found for image \"${TEST_IMAGE}\""
fi
else
echo "No TEST_IMAGE env var provided. Skipping network bridge connection"
fi
# Prepare directory for OpenVidu recordings
sudo mkdir -p /opt/openvidu/recordings && sudo chmod 777 /opt/openvidu/recordings
# Prepare directory for OpenVidu Android apps
sudo mkdir -p /opt/openvidu/android && sudo chmod 777 /opt/openvidu/android
# Download fake videos
FAKE_VIDEO1=/opt/openvidu/barcode.y4m
FAKE_VIDEO2=/opt/openvidu/girl.mjpeg
if [ ! -f ${FAKE_VIDEO1} ]; then
sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/barcode.y4m --create-dirs --output /opt/openvidu/barcode.y4m
else
echo "File ${FAKE_VIDEO1} already exists"
fi
if [ ! -f ${FAKE_VIDEO2} ]; then
sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/girl.mjpeg --create-dirs --output /opt/openvidu/girl.mjpeg
else
echo "File ${FAKE_VIDEO2} already exists"
fi
# Download fake audios
FAKE_AUDIO1=/opt/openvidu/fakeaudio.wav
FAKE_AUDIO2=/opt/openvidu/stt-test.wav
if [ ! -f ${FAKE_AUDIO1} ]; then
sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/fakeaudio.wav --create-dirs --output /opt/openvidu/fakeaudio.wav
else
echo "File ${FAKE_AUDIO1} already exists"
fi
if [ ! -f ${FAKE_AUDIO2} ]; then
sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/stt-test.wav --create-dirs --output /opt/openvidu/stt-test.wav
else
echo "File ${FAKE_AUDIO2} already exists"
fi
# Download recording custom layout
sudo curl --location https://raw.githubusercontent.com/OpenVidu/openvidu/master/openvidu-test-e2e/docker/my-custom-layout/index.html --create-dirs --output /opt/openvidu/test-layouts/layout1/index.html
# Open permissions for /opt/openvidu folder
sudo chmod -R 777 /opt/openvidu
# Pull browser images
# Pull chrome image if env variable CHROME_VERSION is set
if [[ -n "${CHROME_VERSION:-}" ]]; then
docker pull selenium/standalone-chrome:"${CHROME_VERSION}"
fi
# Pull firefox image if env variable FIREFOX_VERSION is set
if [[ -n "${FIREFOX_VERSION:-}" ]]; then
docker pull selenium/standalone-firefox:"${FIREFOX_VERSION}"
fi
# Pull edge image if env variable EDGE_VERSION is set
if [[ -n "${EDGE_VERSION:-}" ]]; then
docker pull selenium/standalone-edge:"${EDGE_VERSION}"
fi
# Pull Docker Android image if env variable DOCKER_ANDROID_IMAGE is set
if [[ -n "${DOCKER_ANDROID_IMAGE:-}" ]]; then
docker pull "${DOCKER_ANDROID_IMAGE}"
fi
# Pull mediasoup and kurento
if [[ -n "${MEDIASOUP_CONTROLLER_VERSION:-}" ]]; then
docker pull openvidu/mediasoup-controller:"${MEDIASOUP_CONTROLLER_VERSION}"
fi
if [[ -n "${KURENTO_MEDIA_SERVER_IMAGE:-}" ]]; then
docker pull "${KURENTO_MEDIA_SERVER_IMAGE}"
fi
fi
# -------------
# Use a specific kurento-java commit other than the configured in openvidu-parent pom.xml
# -------------
if [[ "${USE_SPECIFIC_KURENTO_JAVA_COMMIT}" == true ]]; then
git clone https://github.com/Kurento/kurento.git
pushd kurento/clients/java
git checkout -f "${KURENTO_JAVA_COMMIT}"
MVN_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
mvn -B -Dmaven.artifact.threads=1 clean install
popd
rm -rf kurento
mvn -B versions:set-property \
-Dproperty=version.kurento \
-DnewVersion="${MVN_VERSION}"
fi
# -------------
# Serve openvidu-testapp
# -------------
if [[ "${SERVE_OV_TESTAPP}" == true ]]; then
# Generate certificate
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 \
-subj "/CN=www.mydom.com/O=My Company LTD./C=US" \
-keyout /opt/openvidu/testapp/key.pem \
-out /opt/openvidu/testapp/cert.pem
# Serve TestApp
pushd /opt/openvidu/testapp
http-server -S -p 4200 &>/opt/openvidu/testapp.log &
popd
fi

View File

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<pluginGroups></pluginGroups>
<proxies></proxies>
<servers></servers>
<mirrors></mirrors>
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>kurento-github-public</id>
<name>Kurento GitHub Maven packages (public access)</name>
<url>KURENTO_SNAPSHOTS_URL</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>kurento-github-public</id>
<name>Kurento GitHub Maven packages (public access)</name>
<url>KURENTO_SNAPSHOTS_URL</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>

View File

@ -1,241 +0,0 @@
#!/bin/bash -x
set -eu -o pipefail
############################################################################
# Any function offered by this file that is not path agnostic assumes that #
# the path is located where the first command of each function requires it #
############################################################################
OV_INTEGRATION_TESTS=false
OV_UNIT_TESTS=false
OV_E2E_KURENTO=false
OV_E2E_MEDIASOUP=false
LAUNCH_OV_KURENTO=false
LAUNCH_OV_MEDIASOUP=false
function environmentLaunch {
local MEDIA_SERVER="$1"
# Get e2e container id
local E2E_CONTAINER_ID
E2E_CONTAINER_ID="$(docker ps | grep "$TEST_IMAGE" | awk '{ print $1 }')"
# Get e2e container IP so services running can be accessed by browser and media server containers
local E2E_CONTAINER_IP
E2E_CONTAINER_IP="$(docker inspect "$E2E_CONTAINER_ID" | awk '/bridge/,/IPAddress/' | grep IPAddress | cut -d'"' -f4)"
# Kurento and mediasoup needs to run as network host, so we need Docker host IP.
local DOCKER_HOST_IP
DOCKER_HOST_IP="$(docker inspect bridge --format '{{with index .IPAM.Config 0}}{{or .Gateway .Subnet}}{{end}}' | sed -r 's|\.0/[[:digit:]]+$|.1|')"
if [[ "${MEDIA_SERVER}" == "kurento" ]]; then
docker run -e KMS_UID=$(id -u) --network=host --detach=true --volume=/opt/openvidu/recordings:/opt/openvidu/recordings "${KURENTO_MEDIA_SERVER_IMAGE}"
while true; do
RC="$(curl \
--silent \
--no-buffer \
--write-out '%{http_code}' \
--header "Connection: Upgrade" \
--header "Upgrade: websocket" \
--header "Host: ${DOCKER_HOST_IP}" \
--header "Origin: ${DOCKER_HOST_IP}" \
"http://${DOCKER_HOST_IP}:8888/kurento" || echo '')"
if [[ "$RC" == "500" ]]; then
break
else
echo "Waiting for ${MEDIA_SERVER}..."
sleep 1
fi
done
elif [[ "${MEDIA_SERVER}" == "mediasoup" ]]; then
LOG_DATE=$(printf '%(%Y-%m-%d-%H-%M-%S)T')
docker run --network=host --restart=always \
--env=KMS_MIN_PORT=40000 \
--env=KMS_MAX_PORT=65535 \
--env=OPENVIDU_PRO_LICENSE="${OPENVIDU_PRO_LICENSE}" \
--env=OPENVIDU_PRO_LICENSE_API="${OPENVIDU_PRO_LICENSE_API}" \
--env=WEBRTC_LISTENIPS_0_ANNOUNCEDIP="${DOCKER_HOST_IP}" \
--env=WEBRTC_LISTENIPS_0_IP="${DOCKER_HOST_IP}" \
--volume=/opt/openvidu/recordings:/opt/openvidu/recordings \
openvidu/mediasoup-controller:"${MEDIASOUP_CONTROLLER_VERSION}" >& /opt/openvidu/mediasoup-controller-${LOG_DATE}.log &
until $(curl --insecure --output /dev/null --silent http://${DOCKER_HOST_IP}:8888/kurento); do
echo "Waiting for ${MEDIA_SERVER}..."
sleep 1
done
else
echo "Not valid media server"
exit 1
fi
if [ "${DOCKER_RECORDING_VERSION}" != "default" ]; then
echo "Using custom openvidu-recording tag: ${DOCKER_RECORDING_VERSION}"
java -jar -DKMS_URIS="[\"ws://${DOCKER_HOST_IP}:8888/kurento\"]" \
-DDOMAIN_OR_PUBLIC_IP="${E2E_CONTAINER_IP}" \
-DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true \
-DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts \
-DOPENVIDU_RECORDING_VERSION="${DOCKER_RECORDING_VERSION}" -DOPENVIDU_WEBHOOK=true \
-DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook \
/opt/openvidu/openvidu-server-*.jar &>/opt/openvidu/openvidu-server-"${MEDIA_SERVER}".log &
else
echo "Using default openvidu-recording tag"
java -jar -DKMS_URIS="[\"ws://${DOCKER_HOST_IP}:8888/kurento\"]" \
-DDOMAIN_OR_PUBLIC_IP="${E2E_CONTAINER_IP}" \
-DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true \
-DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts -DOPENVIDU_WEBHOOK=true \
-DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook \
/opt/openvidu/openvidu-server-*.jar &>/opt/openvidu/openvidu-server-"${MEDIA_SERVER}".log &
fi
until $(curl --insecure --output /dev/null --silent --head --fail https://OPENVIDUAPP:MY_SECRET@localhost:4443/); do
echo "Waiting for openvidu-server..."
sleep 2
done
}
function openviduE2ETests {
local MEDIA_SERVER="$1"
# Get e2e container id
local E2E_CONTAINER_ID
E2E_CONTAINER_ID="$(docker ps | grep "$TEST_IMAGE" | awk '{ print $1 }')"
# Get e2e container IP so services running can be accessed by browser and media server containers
local E2E_CONTAINER_IP
E2E_CONTAINER_IP="$(docker inspect "$E2E_CONTAINER_ID" | awk '/bridge/,/IPAddress/' | grep IPAddress | cut -d'"' -f4)"
# Kurento and mediasoup needs to run as network host, so we need Docker host IP.
local DOCKER_HOST_IP
DOCKER_HOST_IP="$(docker network inspect bridge | grep Subnet | cut -d'"' -f4 | cut -d'/' -f1 | sed 's/.$/1/' | grep 172)"
pushd openvidu-test-e2e
if [[ "${MEDIA_SERVER}" == "kurento" ]]; then
mvn -DMEDIA_SERVER_IMAGE="${KURENTO_MEDIA_SERVER_IMAGE}" \
-DOPENVIDU_URL="https://${E2E_CONTAINER_IP}:4443" \
-DCHROME_VERSION="${CHROME_VERSION}" \
-DFIREFOX_VERSION="${FIREFOX_VERSION}" \
-DEDGE_VERSION="${EDGE_VERSION}" \
-Dtest=OpenViduTestAppE2eTest \
-DAPP_URL="https://${E2E_CONTAINER_IP}:4200" \
-DEXTERNAL_CUSTOM_LAYOUT_URL="http://${E2E_CONTAINER_IP}:4114" \
-DREMOTE_URL_CHROME="http://${DOCKER_HOST_IP}:6666/wd/hub/" \
-DREMOTE_URL_FIREFOX="http://${DOCKER_HOST_IP}:6667/wd/hub/" \
-DREMOTE_URL_OPERA="http://${DOCKER_HOST_IP}:6668/wd/hub/" \
-DREMOTE_URL_EDGE="http://${DOCKER_HOST_IP}:6669/wd/hub/" \
-DEXTERNAL_CUSTOM_LAYOUT_PARAMS="sessionId,CUSTOM_LAYOUT_SESSION,secret,MY_SECRET" \
test
elif [[ "${MEDIA_SERVER}" == "mediasoup" ]]; then
mvn -DMEDIA_SERVER_IMAGE="openvidu/mediasoup-controller:${MEDIASOUP_CONTROLLER_VERSION}" \
-DOPENVIDU_URL="https://${E2E_CONTAINER_IP}:4443" \
-DCHROME_VERSION="${CHROME_VERSION}" \
-DFIREFOX_VERSION="${FIREFOX_VERSION}" \
-DEDGE_VERSION="${EDGE_VERSION}" \
-Dtest=OpenViduTestAppE2eTest \
-DAPP_URL="https://${E2E_CONTAINER_IP}:4200" \
-DEXTERNAL_CUSTOM_LAYOUT_URL="http://${E2E_CONTAINER_IP}:4114" \
-DREMOTE_URL_CHROME="http://${DOCKER_HOST_IP}:6666/wd/hub/" \
-DREMOTE_URL_FIREFOX="http://${DOCKER_HOST_IP}:6667/wd/hub/" \
-DREMOTE_URL_OPERA="http://${DOCKER_HOST_IP}:6668/wd/hub/" \
-DREMOTE_URL_EDGE="http://${DOCKER_HOST_IP}:6669/wd/hub/" \
-DEXTERNAL_CUSTOM_LAYOUT_PARAMS="sessionId,CUSTOM_LAYOUT_SESSION,secret,MY_SECRET" \
-DOPENVIDU_PRO_LICENSE="${OPENVIDU_PRO_LICENSE}" \
-DOPENVIDU_PRO_LICENSE_API="${OPENVIDU_PRO_LICENSE_API}" \
test
else
echo "Not valid media server"
exit 1
fi
stopMediaServer
kill -9 $(pgrep -f /opt/openvidu/openvidu-server) || true
popd
}
function stopMediaServer {
# Remove Kurento Media Server
declare -a arr=("kurento/kurento-media-server"
"openvidu/mediasoup-controller:")
for image in "${arr[@]}"; do
docker ps -a | awk '{ print $1,$2 }' | grep "${image}" | awk '{ print $1 }' | xargs -I {} docker rm -f {} || true
done
docker ps -a
}
# Environment variables
if [[ -n ${1:-} ]]; then
case "${1:-}" in
--openvidu-server-unit-tests)
OV_UNIT_TESTS=true
;;
--openvidu-server-integration-tests)
OV_INTEGRATION_TESTS=true
;;
--openvidu-e2e-tests-kurento)
OV_E2E_KURENTO=true
;;
--openvidu-e2e-tests-mediasoup)
OV_E2E_MEDIASOUP=true
;;
--environment-launch-kurento)
LAUNCH_OV_KURENTO=true
;;
--environment-launch-mediasoup)
LAUNCH_OV_MEDIASOUP=true
;;
*)
echo "Unrecognized method $1"
exit 1
;;
esac
else
echo "Must provide a method to execute as first parameter when calling the script"
exit 1
fi
# -------------
# openvidu-server unit tests
# -------------
if [[ "${OV_UNIT_TESTS}" == true ]]; then
pushd openvidu-server
mvn -B -Dtest=io.openvidu.server.test.unit.*Test test
popd
fi
# -------------
# openvidu-server integration tests
# -------------
if [[ "${OV_INTEGRATION_TESTS}" == true ]]; then
pushd openvidu-server
mvn -B -Dtest=io.openvidu.server.test.integration.*Test test
popd
fi
# -------------
# OpenVidu E2E Tests Kurento
# -------------
if [[ "${OV_E2E_KURENTO}" == true ]]; then
openviduE2ETests "kurento"
fi
# -------------
# OpenVidu E2E Tests mediasoup
# -------------
if [[ "${OV_E2E_MEDIASOUP}" == true ]]; then
openviduE2ETests "mediasoup"
fi
# -------------
# Environment launch Kurento
# -------------
if [[ "${LAUNCH_OV_KURENTO}" == true ]]; then
environmentLaunch "kurento"
fi
# -------------
# Environment launch mediasoup
# -------------
if [[ "${LAUNCH_OV_MEDIASOUP}" == true ]]; then
environmentLaunch "mediasoup"
fi

View File

@ -1,62 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
docs/
lib/
static/**

View File

@ -1 +0,0 @@
docs/

View File

@ -1,10 +0,0 @@
{
"singleQuote": true,
"printWidth": 140,
"trailingComma": "none",
"semi": true,
"bracketSpacing": true,
"useTabs": false,
"jsxSingleQuote": true,
"tabWidth": 4
}

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,7 +0,0 @@
#!/bin/bash
set -e
SEARCH_STRING_1="<K extends keyof SessionEventMap>(type: K,"
REPLACE_STRING_1="<K extends keyof SessionEventMap>(type: K | string,"
SEARCH_STRING_2='\[key: `signal:\${string}`\]: SignalEvent;'
sed -i "s~${SEARCH_STRING_1}~${REPLACE_STRING_1}~g" ts4.4/lib/OpenVidu/Session.d.ts
sed -i "/${SEARCH_STRING_2}/d" ts4.4/lib/OpenViduInternal/Events/EventMap/SessionEventMap.d.ts

View File

@ -1,20 +0,0 @@
{
"include": ["../src"],
"exclude": ["../config", "../docs", "../lib", "../node_modules", "../ts4.4"],
"typedocOptions": {
"name": "OpenVidu Browser",
"entryPoints": ["../src/index.ts"],
"out": "../docs",
"theme": "default",
"readme": "none",
"includeVersion": true,
"validation": {
"notExported": true,
"invalidLink": true
},
"excludeExternals": true,
"excludePrivate": true,
"excludeProtected": true,
"excludeInternal": true
}
}

View File

@ -1,154 +0,0 @@
{
"extends": "tslint:recommended",
"rules": {
"array-type": [
true,
"array"
],
"ban-types": {
"options": [
[
"Object",
"Avoid using the `Object` type. Did you mean `object`?"
],
[
"Function",
"Avoid using the `Function` type. Prefer a specific function type, like `() => void`, or use `ts.AnyFunction`."
],
[
"Boolean",
"Avoid using the `Boolean` type. Did you mean `boolean`?"
],
[
"Number",
"Avoid using the `Number` type. Did you mean `number`?"
],
[
"String",
"Avoid using the `String` type. Did you mean `string`?"
]
]
},
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": [
true,
"ignore-same-line"
],
"indent": [
true,
"spaces",
2
],
"interface-name": [
true,
"never-prefix"
],
"interface-over-type-literal": true,
"jsdoc-format": true,
"no-inferrable-types": true,
"no-internal-module": true,
"no-null-keyword": false,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": [
true,
"ignore-template-strings"
],
"no-var-keyword": true,
"object-literal-shorthand": true,
"one-line": [
true,
"check-open-brace",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single",
"avoid-escape",
"avoid-template"
],
"semicolon": [
true,
"always",
"ignore-bound-class-methods"
],
"space-within-parens": true,
"triple-equals": true,
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
},
{
"call-signature": "onespace",
"index-signature": "onespace",
"parameter": "onespace",
"property-declaration": "onespace",
"variable-declaration": "onespace"
}
],
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-module",
"check-separator",
"check-type"
],
"no-implicit-dependencies": [
true,
"dev"
],
"object-literal-key-quotes": [
true,
"consistent-as-needed"
],
"variable-name": [
true,
"ban-keywords",
"check-format",
"allow-leading-underscore"
],
"arrow-parens": false,
"arrow-return-shorthand": false,
"forin": false,
"member-access": false,
"no-conditional-assignment": false,
"no-console": false,
"no-debugger": false,
"no-empty-interface": false,
"no-eval": false,
"no-object-literal-type-assertion": false,
"no-shadowed-variable": false,
"no-submodule-imports": false,
"no-var-requires": false,
"ordered-imports": false,
"prefer-conditional-expression": false,
"radix": false,
"trailing-comma": false,
"align": false,
"eofline": false,
"max-line-length": false,
"no-consecutive-blank-lines": false,
"space-before-function-paren": false,
"ban-comma-operator": false,
"max-classes-per-file": false,
"member-ordering": false,
"no-angle-bracket-type-assertion": false,
"no-bitwise": false,
"no-namespace": false,
"no-reference": false,
"object-literal-sort-keys": false,
"one-variable-per-declaration": false,
"unified-signatures": false
}
}

View File

@ -1,19 +0,0 @@
#!/bin/bash
if [[ -z "$BASEHREF_VERSION" ]]; then
echo "Example of use: \"BASEHREF_VERSION=2.12.0 ${0}\"" 1>&2
exit 1
fi
# Replace version from "stable" to the specified one in all TypeDoc links
grep -rl '/en/stable/' src | xargs sed -i -e 's|/en/stable/|/en/'${BASEHREF_VERSION}'/|g'
# Generate TypeDoc
./node_modules/typedoc/bin/typedoc --tsconfig ./config/tsconfig.json
# Return links to "stable" version
grep -rl '/en/'${BASEHREF_VERSION}'/' src | xargs sed -i -e 's|/en/'${BASEHREF_VERSION}'/|/en/stable/|g'
# Clean previous docs from openvidu.io-docs repo and copy new ones
rm -rf ../../openvidu.io-docs/docs/api/openvidu-browser/*
cp -R ./docs/. ../../openvidu.io-docs/docs/api/openvidu-browser

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +0,0 @@
{
"author": "OpenVidu",
"dependencies": {
"events": "3.3.0",
"freeice": "2.2.2",
"hark": "1.2.3",
"inherits": "2.0.4",
"jsnlog": "2.30.0",
"mime": "3.0.0",
"platform": "1.3.6",
"semver": "7.6.2",
"uuid": "9.0.1",
"wolfy87-eventemitter": "5.2.9"
},
"description": "OpenVidu Browser",
"devDependencies": {
"@types/node": "18.11.9",
"@types/platform": "1.3.4",
"browserify": "17.0.0",
"terser": "5.15.1",
"tsify": "5.0.4",
"tslint": "6.1.3",
"typedoc": "0.23.21",
"typescript": "4.9.3"
},
"license": "Apache-2.0",
"main": "lib/index.js",
"name": "openvidu-browser",
"repository": {
"type": "git",
"url": "git://github.com/OpenVidu/openvidu"
},
"scripts": {
"browserify": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../static/js/openvidu-browser-$VERSION.js -v",
"browserify-prod": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js --debug Main.ts -p [ tsify ] --exclude kurento-browser-extensions | ../node_modules/terser/bin/terser --source-map content=inline --output ../static/js/openvidu-browser-$VERSION.min.js",
"build": "cd src/OpenVidu && ./../../node_modules/typescript/bin/tsc && cd ../.. && ./node_modules/typescript/bin/tsc --declaration src/index.ts --outDir ./lib --sourceMap --target es5 --lib dom,es5,es2015.promise,scripthost && rm -rf ./ts4.4 && mkdir -p ./ts4.4/lib && cp -r ./lib ./ts4.4 && find ./ts4.4/lib -type f ! -iname '*.d.ts' -delete && ./config/replace_for_ts44.sh",
"docs": "./generate-docs.sh"
},
"types": "lib/index.d.ts",
"typesVersions": {
"<4.4": {
"*": [
"ts4.4/*"
]
}
},
"version": "2.30.0"
}

View File

@ -1,9 +0,0 @@
import { OpenVidu } from './OpenVidu/OpenVidu';
import { JL } from 'jsnlog';
if (typeof globalThis !== 'undefined') {
globalThis['OpenVidu'] = OpenVidu;
}
// Disable jsnlog when library is loaded
JL.setOptions({ enabled: false });

View File

@ -1,217 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Session } from './Session';
import { Stream } from './Stream';
import { LocalConnectionOptions } from '../OpenViduInternal/Interfaces/Private/LocalConnectionOptions';
import { RemoteConnectionOptions } from '../OpenViduInternal/Interfaces/Private/RemoteConnectionOptions';
import { InboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/InboundStreamOptions';
import { StreamOptionsServer } from '../OpenViduInternal/Interfaces/Private/StreamOptionsServer';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { ExceptionEvent, ExceptionEventName } from '../OpenViduInternal/Events/ExceptionEvent';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* Represents each one of the user's connection to the session (the local one and other user's connections).
* Therefore each {@link Session} and {@link Stream} object has an attribute of type Connection
*/
export class Connection {
/**
* Unique identifier of the connection
*/
connectionId: string;
/**
* Time when this connection was created in OpenVidu Server (UTC milliseconds)
*/
creationTime: number;
/**
* Data associated to this connection (and therefore to certain user). This is an important field:
* it allows you to broadcast all the information you want for each user (a username, for example)
*/
data: string;
/**
* Role of the connection.
* - `SUBSCRIBER`: can subscribe to published Streams of other users by calling {@link Session.subscribe}
* - `PUBLISHER`: SUBSCRIBER permissions + can publish their own Streams by calling {@link Session.publish}
* - `MODERATOR`: SUBSCRIBER + PUBLISHER permissions + can force the unpublishing or disconnection over a third-party Stream or Connection by call {@link Session.forceUnpublish} and {@link Session.forceDisconnect}
*
* **Only defined for the local connection. In remote connections will be `undefined`**
*/
role: string;
/**
* Whether the streams published by this Connection will be recorded or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording/#individual-recording-selection) <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
*
* **Only defined for the local connection. In remote connections will be `undefined`**
*/
record: boolean;
/**
* @hidden
*/
stream?: Stream;
/**
* @hidden
*/
localOptions: LocalConnectionOptions | undefined;
/**
* @hidden
*/
remoteOptions: RemoteConnectionOptions | undefined;
/**
* @hidden
*/
disposed = false;
/**
* @hidden
*/
rpcSessionId: string;
/**
* @hidden
*/
constructor(private session: Session, connectionOptions: LocalConnectionOptions | RemoteConnectionOptions) {
let msg = "'Connection' created ";
if (!!(<LocalConnectionOptions>connectionOptions).role) {
// Connection is local
this.localOptions = <LocalConnectionOptions>connectionOptions;
this.connectionId = this.localOptions.id;
this.creationTime = this.localOptions.createdAt;
this.data = this.localOptions.metadata;
this.rpcSessionId = this.localOptions.sessionId;
this.role = this.localOptions.role;
this.record = this.localOptions.record;
msg += '(local)';
} else {
// Connection is remote
this.remoteOptions = <RemoteConnectionOptions>connectionOptions;
this.connectionId = this.remoteOptions.id;
this.creationTime = this.remoteOptions.createdAt;
if (this.remoteOptions.metadata) {
this.data = this.remoteOptions.metadata;
}
if (this.remoteOptions.streams) {
this.initRemoteStreams(this.remoteOptions.streams);
}
msg += "(remote) with 'connectionId' [" + this.remoteOptions.id + ']';
}
logger.info(msg);
}
/* Hidden methods */
/**
* @hidden
*/
sendIceCandidate(candidate: RTCIceCandidate): void {
if (!this.disposed) {
logger.debug((!!this.stream!.outboundStreamOpts ? 'Local' : 'Remote') + 'candidate for' + this.connectionId, candidate);
this.session.openvidu.sendRequest(
'onIceCandidate',
{
endpointName: this.connectionId,
candidate: candidate.candidate,
sdpMid: candidate.sdpMid,
sdpMLineIndex: candidate.sdpMLineIndex
},
(error, response) => {
if (error) {
logger.error('Error sending ICE candidate: ' + JSON.stringify(error));
this.session.emitEvent('exception', [
new ExceptionEvent(
this.session,
ExceptionEventName.ICE_CANDIDATE_ERROR,
this.session,
'There was an unexpected error on the server-side processing an ICE candidate generated and sent by the client-side',
error
)
]);
}
}
);
} else {
logger.warn(`Connection ${this.connectionId} disposed when trying to send an ICE candidate. ICE candidate not sent`);
}
}
/**
* @hidden
*/
initRemoteStreams(options: StreamOptionsServer[]): void {
// This is ready for supporting multiple streams per Connection object. Right now the loop will always run just once
// this.stream should also be replaced by a collection of streams to support multiple streams per Connection
options.forEach((opts) => {
const streamOptions: InboundStreamOptions = {
id: opts.id,
createdAt: opts.createdAt,
connection: this,
hasAudio: opts.hasAudio,
hasVideo: opts.hasVideo,
audioActive: opts.audioActive,
videoActive: opts.videoActive,
typeOfVideo: opts.typeOfVideo,
frameRate: opts.frameRate,
videoDimensions: !!opts.videoDimensions ? JSON.parse(opts.videoDimensions) : undefined,
filter: !!opts.filter ? opts.filter : undefined
};
const stream = new Stream(this.session, streamOptions);
this.addStream(stream);
});
logger.info(
"Remote 'Connection' with 'connectionId' [" + this.connectionId + '] is now configured for receiving Streams with options: ',
this.stream!.inboundStreamOpts
);
}
/**
* @hidden
*/
addStream(stream: Stream): void {
stream.connection = this;
this.stream = stream;
}
/**
* @hidden
*/
removeStream(): void {
delete this.stream;
}
/**
* @hidden
*/
dispose(): void {
this.disposed = true;
this.removeStream();
}
}

View File

@ -1,112 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from '../OpenViduInternal/Events/Event';
import { EventMap } from '../OpenViduInternal/Events/EventMap/EventMap';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import EventEmitter = require('wolfy87-eventemitter');
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
export abstract class EventDispatcher {
/**
* @hidden
*/
userHandlerArrowHandler: WeakMap<(event: Event) => void, (event: Event) => void> = new WeakMap();
/**
* @hidden
*/
ee = new EventEmitter();
/**
* Adds function `handler` to handle event `type`
*
* @returns The EventDispatcher object
*/
abstract on<K extends keyof EventMap>(type: K, handler: (event: EventMap[K]) => void): this;
/**
* Adds function `handler` to handle event `type` just once. The handler will be automatically removed after first execution
*
* @returns The object that dispatched the event
*/
abstract once<K extends keyof EventMap>(type: K, handler: (event: EventMap[K]) => void): this;
/**
* Removes a `handler` from event `type`. If no handler is provided, all handlers will be removed from the event
*
* @returns The object that dispatched the event
*/
abstract off<K extends keyof EventMap>(type: K, handler?: (event: EventMap[K]) => void): this;
/**
* @hidden
*/
onAux(type: string, message: string, handler: (event: Event) => void): EventDispatcher {
const arrowHandler = (event) => {
if (event) {
logger.debug(message, event);
} else {
logger.debug(message);
}
handler(event);
};
this.userHandlerArrowHandler.set(handler, arrowHandler);
this.ee.on(type, arrowHandler);
return this;
}
/**
* @hidden
*/
onceAux(type: string, message: string, handler: (event: Event) => void): EventDispatcher {
const arrowHandler = (event) => {
if (event) {
logger.debug(message, event);
} else {
logger.debug(message);
}
handler(event);
// Remove handler from map after first and only execution
this.userHandlerArrowHandler.delete(handler);
};
this.userHandlerArrowHandler.set(handler, arrowHandler);
this.ee.once(type, arrowHandler);
return this;
}
/**
* @hidden
*/
offAux(type: string, handler?: (event: Event) => void): EventDispatcher {
if (!handler) {
this.ee.removeAllListeners(type);
} else {
// Must remove internal arrow function handler paired with user handler
const arrowHandler = this.userHandlerArrowHandler.get(handler);
if (!!arrowHandler) {
this.ee.off(type, arrowHandler);
}
this.userHandlerArrowHandler.delete(handler);
}
return this;
}
}

View File

@ -1,285 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Stream } from './Stream';
import { FilterEvent } from '../OpenViduInternal/Events/FilterEvent';
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* **WARNING**: experimental option. This interface may change in the near future
*
* Video/audio filter applied to a Stream. See {@link Stream.applyFilter}
*/
export class Filter {
/**
* Type of filter applied. This is the name of the remote class identifying the filter to apply in Kurento Media Server.
* For example: `"FaceOverlayFilter"`, `"GStreamerFilter"`.
*
* You can get this property in `*.kmd.json` files defining the Kurento filters. For example, for GStreamerFilter that's
* [here](https://github.com/Kurento/kms-filters/blob/53a452fac71d61795952e3d2202156c6b00f6d65/src/server/interface/filters.GStreamerFilter.kmd.json#L4)
*/
type: string;
/**
* Parameters used to initialize the filter.
* These correspond to the constructor parameters used in the filter in Kurento Media Server (except for `mediaPipeline` parameter, which is never needed).
*
* For example: for `filter.type = "GStreamerFilter"` could be `filter.options = {"command": "videobalance saturation=0.0"}`
*
* You can get this property in `*.kmd.json` files defining the Kurento filters. For example, for GStreamerFilter that's
* [here](https://github.com/Kurento/kms-filters/blob/53a452fac71d61795952e3d2202156c6b00f6d65/src/server/interface/filters.GStreamerFilter.kmd.json#L13-L31)
*/
options: Object;
/**
* Value passed the last time {@link Filter.execMethod} was called. If `undefined` this method has not been called yet.
*
* You can use this value to know the current status of any applied filter
*/
lastExecMethod?: {
method: string;
params: Object;
};
/**
* @hidden
*/
handlers: Map<string, (event: FilterEvent) => void> = new Map();
/**
* @hidden
*/
stream: Stream;
private logger: OpenViduLogger;
/**
* @hidden
*/
constructor(type: string, options: Object) {
this.type = type;
this.options = options;
}
/**
* Executes a filter method. Available methods are specific for each filter
*
* @param method Name of the method
* @param params Parameters of the method
*/
execMethod(method: string, params: Object): Promise<void> {
return new Promise((resolve, reject) => {
logger.info('Executing filter method to stream ' + this.stream.streamId);
let finalParams;
const successExecMethod = (triggerEvent) => {
logger.info('Filter method successfully executed on Stream ' + this.stream.streamId);
const oldValue = (<any>Object).assign({}, this.stream.filter);
this.stream.filter!.lastExecMethod = { method, params: finalParams };
if (triggerEvent) {
this.stream.session.emitEvent('streamPropertyChanged', [
new StreamPropertyChangedEvent(
this.stream.session,
this.stream,
'filter',
this.stream.filter!,
oldValue,
'execFilterMethod'
)
]);
this.stream.streamManager.emitEvent('streamPropertyChanged', [
new StreamPropertyChangedEvent(
this.stream.streamManager,
this.stream,
'filter',
this.stream.filter!,
oldValue,
'execFilterMethod'
)
]);
}
return resolve();
};
if (this.type.startsWith('VB:')) {
if (typeof params === 'string') {
try {
params = JSON.parse(params);
} catch (error) {
return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'Wrong params syntax: ' + error));
}
}
finalParams = params;
if (method === 'update') {
if (!this.stream.virtualBackgroundSinkElements?.VB) {
return reject(
new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'There is no Virtual Background filter applied')
);
} else {
this.stream.virtualBackgroundSinkElements.VB.updateValues(params)
.then(() => successExecMethod(false))
.catch((error) => {
if (error.name === OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR) {
return reject(new OpenViduError(error.name, error.message));
} else {
return reject(
new OpenViduError(
OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR,
'Error updating values on Virtual Background filter: ' + error
)
);
}
});
}
} else {
return reject(
new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, `Unknown Virtual Background method "${method}"`)
);
}
} else {
let stringParams;
if (typeof params !== 'string') {
try {
stringParams = JSON.stringify(params);
} catch (error) {
const errorMsg = "'params' property must be a JSON formatted object";
logger.error(errorMsg);
return reject(errorMsg);
}
} else {
stringParams = <string>params;
}
finalParams = stringParams;
this.stream.session.openvidu.sendRequest(
'execFilterMethod',
{ streamId: this.stream.streamId, method, params: stringParams },
(error, response) => {
if (error) {
logger.error('Error executing filter method for Stream ' + this.stream.streamId, error);
if (error.code === 401) {
return reject(
new OpenViduError(
OpenViduErrorName.OPENVIDU_PERMISSION_DENIED,
"You don't have permissions to execute a filter method"
)
);
} else {
return reject(error);
}
} else {
return successExecMethod(true);
}
}
);
}
});
}
/**
* Subscribe to certain filter event. Available events are specific for each filter
*
* @param eventType Event to which subscribe to.
* @param handler Function to execute upon event dispatched. It receives as parameter a {@link FilterEvent} object
*
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully attached to the filter and rejected with an Error object if not
*/
addEventListener(eventType: string, handler: (event: FilterEvent) => void): Promise<void> {
return new Promise((resolve, reject) => {
logger.info('Adding filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId);
this.stream.session.openvidu.sendRequest(
'addFilterEventListener',
{ streamId: this.stream.streamId, eventType },
(error, response) => {
if (error) {
logger.error(
'Error adding filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId,
error
);
if (error.code === 401) {
return reject(
new OpenViduError(
OpenViduErrorName.OPENVIDU_PERMISSION_DENIED,
"You don't have permissions to add a filter event listener"
)
);
} else {
return reject(error);
}
} else {
this.handlers.set(eventType, handler);
logger.info(
'Filter event listener to event ' + eventType + ' successfully applied on Stream ' + this.stream.streamId
);
return resolve();
}
}
);
});
}
/**
* Removes certain filter event listener previously set.
*
* @param eventType Event to unsubscribe from.
*
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully removed from the filter and rejected with an Error object in other case
*/
removeEventListener(eventType: string): Promise<void> {
return new Promise((resolve, reject) => {
logger.info('Removing filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId);
this.stream.session.openvidu.sendRequest(
'removeFilterEventListener',
{ streamId: this.stream.streamId, eventType },
(error, response) => {
if (error) {
logger.error(
'Error removing filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId,
error
);
if (error.code === 401) {
return reject(
new OpenViduError(
OpenViduErrorName.OPENVIDU_PERMISSION_DENIED,
"You don't have permissions to add a filter event listener"
)
);
} else {
return reject(error);
}
} else {
this.handlers.delete(eventType);
logger.info(
'Filter event listener to event ' + eventType + ' successfully removed on Stream ' + this.stream.streamId
);
return resolve();
}
}
);
});
}
}

View File

@ -1,416 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Stream } from './Stream';
import { LocalRecorderState } from '../OpenViduInternal/Enums/LocalRecorderState';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
import Mime = require('mime/lite');
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
/**
* Easy recording of {@link Stream} objects straightaway from the browser. Initialized with {@link OpenVidu.initLocalRecorder} method
*/
export class LocalRecorder {
state: LocalRecorderState;
private connectionId: string;
private mediaRecorder: MediaRecorder;
private chunks: any[] = [];
private blob?: Blob;
private id: string;
private videoPreviewSrc: string;
private videoPreview: HTMLVideoElement;
/**
* @hidden
*/
constructor(private stream: Stream) {
platform = PlatformUtils.getInstance();
this.connectionId = !!this.stream.connection ? this.stream.connection.connectionId : 'default-connection';
this.id = this.stream.streamId + '_' + this.connectionId + '_localrecord';
this.state = LocalRecorderState.READY;
}
/**
* Starts the recording of the Stream. {@link state} property must be `READY`. After method succeeds is set to `RECORDING`
*
* @param options The [MediaRecorder.options](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#parameters) to be used to record this Stream.
* For example:
*
* ```javascript
* var OV = new OpenVidu();
* var publisher = await OV.initPublisherAsync();
* var localRecorder = OV.initLocalRecorder(publisher.stream);
* var options = {
* mimeType: 'video/webm;codecs=vp8',
* audioBitsPerSecond:128000,
* videoBitsPerSecond:2500000
* };
* localRecorder.record(options);
* ```
*
* If not specified, the default options preferred by the platform will be used.
*
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording successfully started and rejected with an Error object if not
*/
record(options?: any): Promise<void> {
return new Promise((resolve, reject) => {
try {
if (typeof options === 'string' || options instanceof String) {
return reject(
`When calling LocalRecorder.record(options) parameter 'options' cannot be a string. Must be an object like { mimeType: "${options}" }`
);
}
if (typeof MediaRecorder === 'undefined') {
logger.error(
'MediaRecorder not supported on your device. See compatibility in https://caniuse.com/#search=MediaRecorder'
);
throw Error(
'MediaRecorder not supported on your device. See compatibility in https://caniuse.com/#search=MediaRecorder'
);
}
if (this.state !== LocalRecorderState.READY) {
throw Error(
"'LocalRecord.record()' needs 'LocalRecord.state' to be 'READY' (current value: '" +
this.state +
"'). Call 'LocalRecorder.clean()' or init a new LocalRecorder before"
);
}
logger.log("Starting local recording of stream '" + this.stream.streamId + "' of connection '" + this.connectionId + "'");
if (!options) {
options = { mimeType: 'video/webm' };
} else if (!options.mimeType) {
options.mimeType = 'video/webm';
}
this.mediaRecorder = new MediaRecorder(this.stream.getMediaStream(), options);
this.mediaRecorder.start();
} catch (err) {
return reject(err);
}
this.mediaRecorder.ondataavailable = (e) => {
if (e.data.size > 0) {
this.chunks.push(e.data);
}
};
this.mediaRecorder.onerror = (e) => {
logger.error('MediaRecorder error: ', e);
};
this.mediaRecorder.onstart = () => {
logger.log('MediaRecorder started (state=' + this.mediaRecorder.state + ')');
};
this.mediaRecorder.onstop = () => {
this.onStopDefault();
};
this.mediaRecorder.onpause = () => {
logger.log('MediaRecorder paused (state=' + this.mediaRecorder.state + ')');
};
this.mediaRecorder.onresume = () => {
logger.log('MediaRecorder resumed (state=' + this.mediaRecorder.state + ')');
};
this.state = LocalRecorderState.RECORDING;
return resolve();
});
}
/**
* Ends the recording of the Stream. {@link state} property must be `RECORDING` or `PAUSED`. After method succeeds is set to `FINISHED`
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording successfully stopped and rejected with an Error object if not
*/
stop(): Promise<void> {
return new Promise((resolve, reject) => {
try {
if (this.state === LocalRecorderState.READY || this.state === LocalRecorderState.FINISHED) {
throw Error(
"'LocalRecord.stop()' needs 'LocalRecord.state' to be 'RECORDING' or 'PAUSED' (current value: '" +
this.state +
"'). Call 'LocalRecorder.start()' before"
);
}
this.mediaRecorder.onstop = () => {
this.onStopDefault();
return resolve();
};
this.mediaRecorder.stop();
} catch (e) {
return reject(e);
}
});
}
/**
* Pauses the recording of the Stream. {@link state} property must be `RECORDING`. After method succeeds is set to `PAUSED`
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording was successfully paused and rejected with an Error object if not
*/
pause(): Promise<void> {
return new Promise((resolve, reject) => {
try {
if (this.state !== LocalRecorderState.RECORDING) {
return reject(
Error(
"'LocalRecord.pause()' needs 'LocalRecord.state' to be 'RECORDING' (current value: '" +
this.state +
"'). Call 'LocalRecorder.start()' or 'LocalRecorder.resume()' before"
)
);
}
this.mediaRecorder.pause();
this.state = LocalRecorderState.PAUSED;
return resolve();
} catch (error) {
return reject(error);
}
});
}
/**
* Resumes the recording of the Stream. {@link state} property must be `PAUSED`. After method succeeds is set to `RECORDING`
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording was successfully resumed and rejected with an Error object if not
*/
resume(): Promise<void> {
return new Promise((resolve, reject) => {
try {
if (this.state !== LocalRecorderState.PAUSED) {
throw Error(
"'LocalRecord.resume()' needs 'LocalRecord.state' to be 'PAUSED' (current value: '" +
this.state +
"'). Call 'LocalRecorder.pause()' before"
);
}
this.mediaRecorder.resume();
this.state = LocalRecorderState.RECORDING;
return resolve();
} catch (error) {
return reject(error);
}
});
}
/**
* Previews the recording, appending a new HTMLVideoElement to element with id `parentId`. {@link state} property must be `FINISHED`
*/
preview(parentElement): HTMLVideoElement {
if (this.state !== LocalRecorderState.FINISHED) {
throw Error(
"'LocalRecord.preview()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" +
this.state +
"'). Call 'LocalRecorder.stop()' before"
);
}
this.videoPreview = document.createElement('video');
this.videoPreview.id = this.id;
this.videoPreview.autoplay = true;
if (platform.isSafariBrowser()) {
this.videoPreview.playsInline = true;
}
if (typeof parentElement === 'string') {
const parentElementDom = document.getElementById(parentElement);
if (parentElementDom) {
this.videoPreview = parentElementDom.appendChild(this.videoPreview);
}
} else {
this.videoPreview = parentElement.appendChild(this.videoPreview);
}
this.videoPreview.src = this.videoPreviewSrc;
return this.videoPreview;
}
/**
* Gracefully stops and cleans the current recording (WARNING: it is completely dismissed). Sets {@link state} to `READY` so the recording can start again
*/
clean(): void {
const f = () => {
delete this.blob;
this.chunks = [];
this.state = LocalRecorderState.READY;
};
if (this.state === LocalRecorderState.RECORDING || this.state === LocalRecorderState.PAUSED) {
this.stop()
.then(() => f())
.catch(() => f());
} else {
f();
}
}
/**
* Downloads the recorded video through the browser. {@link state} property must be `FINISHED`
*/
download(): void {
if (this.state !== LocalRecorderState.FINISHED) {
throw Error(
"'LocalRecord.download()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" +
this.state +
"'). Call 'LocalRecorder.stop()' before"
);
} else {
const a: HTMLAnchorElement = document.createElement('a');
a.style.display = 'none';
document.body.appendChild(a);
const url = globalThis.URL.createObjectURL(<any>this.blob);
a.href = url;
a.download = this.id + '.' + Mime.getExtension(this.blob!.type);
a.click();
globalThis.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
}
/**
* Gets the raw Blob file. Methods preview, download, uploadAsBinary and uploadAsMultipartfile use this same file to perform their specific actions. {@link state} property must be `FINISHED`
*/
getBlob(): Blob {
if (this.state !== LocalRecorderState.FINISHED) {
throw Error("Call 'LocalRecord.stop()' before getting Blob file");
} else {
return this.blob!;
}
}
/**
* Uploads the recorded video as a binary file performing an HTTP/POST operation to URL `endpoint`. {@link state} property must be `FINISHED`. Optional HTTP headers can be passed as second parameter. For example:
* ```
* var headers = {
* "Cookie": "$Version=1; Skin=new;",
* "Authorization":"Basic QWxhZGpbjpuIHNlctZQ=="
* }
* ```
* @returns A Promise (to which you can optionally subscribe to) that is resolved with the `http.responseText` from server if the operation was successful and rejected with the failed `http.status` if not
*/
uploadAsBinary(endpoint: string, headers?: any): Promise<any> {
return new Promise((resolve, reject) => {
if (this.state !== LocalRecorderState.FINISHED) {
return reject(
Error(
"'LocalRecord.uploadAsBinary()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" +
this.state +
"'). Call 'LocalRecorder.stop()' before"
)
);
} else {
const http = new XMLHttpRequest();
http.open('POST', endpoint, true);
if (typeof headers === 'object') {
for (const key of Object.keys(headers)) {
http.setRequestHeader(key, headers[key]);
}
}
http.onreadystatechange = () => {
if (http.readyState === 4) {
if (http.status.toString().charAt(0) === '2') {
// Success response from server (HTTP status standard: 2XX is success)
return resolve(http.responseText);
} else {
return reject(http.status);
}
}
};
http.send(this.blob);
}
});
}
/**
* Uploads the recorded video as a multipart file performing an HTTP/POST operation to URL `endpoint`. {@link state} property must be `FINISHED`. Optional HTTP headers can be passed as second parameter. For example:
* ```
* var headers = {
* "Cookie": "$Version=1; Skin=new;",
* "Authorization":"Basic QWxhZGpbjpuIHNlctZQ=="
* }
* ```
* @returns A Promise (to which you can optionally subscribe to) that is resolved with the `http.responseText` from server if the operation was successful and rejected with the failed `http.status` if not:
*/
uploadAsMultipartfile(endpoint: string, headers?: any): Promise<any> {
return new Promise((resolve, reject) => {
if (this.state !== LocalRecorderState.FINISHED) {
return reject(
Error(
"'LocalRecord.uploadAsMultipartfile()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" +
this.state +
"'). Call 'LocalRecorder.stop()' before"
)
);
} else {
const http = new XMLHttpRequest();
http.open('POST', endpoint, true);
if (typeof headers === 'object') {
for (const key of Object.keys(headers)) {
http.setRequestHeader(key, headers[key]);
}
}
const sendable = new FormData();
sendable.append('file', this.blob!, this.id + '.' + Mime.getExtension(this.blob!.type));
http.onreadystatechange = () => {
if (http.readyState === 4) {
if (http.status.toString().charAt(0) === '2') {
// Success response from server (HTTP status standard: 2XX is success)
return resolve(http.responseText);
} else {
return reject(http.status);
}
}
};
http.send(sendable);
}
});
}
/* Private methods */
private onStopDefault(): void {
logger.log('MediaRecorder stopped (state=' + this.mediaRecorder.state + ')');
this.blob = new Blob(this.chunks, { type: this.mediaRecorder.mimeType });
this.chunks = [];
this.videoPreviewSrc = globalThis.URL.createObjectURL(this.blob);
this.state = LocalRecorderState.FINISHED;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,875 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { OpenVidu } from './OpenVidu';
import { Session } from './Session';
import { Stream } from './Stream';
import { StreamManager } from './StreamManager';
import { PublisherProperties } from '../OpenViduInternal/Interfaces/Public/PublisherProperties';
import { PublisherEventMap } from '../OpenViduInternal/Events/EventMap/PublisherEventMap';
import { StreamEvent } from '../OpenViduInternal/Events/StreamEvent';
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
import { TypeOfVideo } from '../OpenViduInternal/Enums/TypeOfVideo';
import { StreamEventReason } from '../OpenViduInternal/Events/Types/Types';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
/**
* Packs local media streams. Participants can publish it to a session. Initialized with {@link OpenVidu.initPublisher} method.
*
* See available event listeners at {@link PublisherEventMap}.
*/
export class Publisher extends StreamManager {
/**
* Whether the Publisher has been granted access to the requested input devices or not
*/
accessAllowed = false;
/**
* Whether you have called {@link Publisher.subscribeToRemote} with value `true` or `false` (*false* by default)
*/
isSubscribedToRemote = false;
/**
* The {@link Session} to which the Publisher belongs
*/
session: Session; // Initialized by Session.publish(Publisher)
private accessDenied = false;
protected properties: PublisherProperties;
private permissionDialogTimeout: NodeJS.Timer;
/**
* @hidden
*/
openvidu: OpenVidu;
/**
* @hidden
*/
videoReference: HTMLVideoElement;
/**
* @hidden
*/
screenShareResizeInterval: NodeJS.Timer;
/**
* @hidden
*/
constructor(targEl: string | HTMLElement | undefined, properties: PublisherProperties, openvidu: OpenVidu) {
super(
new Stream(!!openvidu.session ? openvidu.session : new Session(openvidu), {
publisherProperties: properties,
mediaConstraints: {}
}),
targEl
);
platform = PlatformUtils.getInstance();
this.properties = properties;
this.openvidu = openvidu;
this.stream.ee.on('local-stream-destroyed', (reason: StreamEventReason) => {
this.stream.isLocalStreamPublished = false;
const streamEvent = new StreamEvent(true, this, 'streamDestroyed', this.stream, reason);
this.emitEvent('streamDestroyed', [streamEvent]);
streamEvent.callDefaultBehavior();
});
}
/**
* Publish or unpublish the audio stream (if available). Calling this method twice in a row passing same `enabled` value will have no effect
*
* #### Events dispatched
*
* > _Only if `Session.publish(Publisher)` has been called for this Publisher_
*
* The {@link Session} object of the local participant will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"audioActive"` and `reason` set to `"publishAudio"`
* The {@link Publisher} object of the local participant will also dispatch the exact same event
*
* The {@link Session} object of every other participant connected to the session will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"audioActive"` and `reason` set to `"publishAudio"`
* The respective {@link Subscriber} object of every other participant receiving this Publisher's stream will also dispatch the exact same event
*
* See {@link StreamPropertyChangedEvent} to learn more.
*
* @param enabled `true` to publish the audio stream, `false` to unpublish it
*/
publishAudio(enabled: boolean): void {
if (this.stream.audioActive !== enabled) {
const affectedMediaStream: MediaStream = this.stream.displayMyRemote()
? this.stream.localMediaStreamWhenSubscribedToRemote!
: this.stream.getMediaStream();
affectedMediaStream.getAudioTracks().forEach((track) => {
track.enabled = enabled;
});
if (!!this.session && !!this.stream.streamId) {
this.session.openvidu.sendRequest(
'streamPropertyChanged',
{
streamId: this.stream.streamId,
property: 'audioActive',
newValue: enabled,
reason: 'publishAudio'
},
(error, response) => {
if (error) {
logger.error("Error sending 'streamPropertyChanged' event", error);
} else {
this.session.emitEvent('streamPropertyChanged', [
new StreamPropertyChangedEvent(this.session, this.stream, 'audioActive', enabled, !enabled, 'publishAudio')
]);
this.emitEvent('streamPropertyChanged', [
new StreamPropertyChangedEvent(this, this.stream, 'audioActive', enabled, !enabled, 'publishAudio')
]);
this.session.sendVideoData(this.stream.streamManager);
}
}
);
}
this.stream.audioActive = enabled;
logger.info("'Publisher' has " + (enabled ? 'published' : 'unpublished') + ' its audio stream');
}
}
/**
* Publish or unpublish the video stream (if available). Calling this method twice in a row passing same `enabled` value will have no effect
*
* #### Events dispatched
*
* > _Only if `Session.publish(Publisher)` has been called for this Publisher_
*
* The {@link Session} object of the local participant will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"videoActive"` and `reason` set to `"publishVideo"`
* The {@link Publisher} object of the local participant will also dispatch the exact same event
*
* The {@link Session} object of every other participant connected to the session will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"videoActive"` and `reason` set to `"publishVideo"`
* The respective {@link Subscriber} object of every other participant receiving this Publisher's stream will also dispatch the exact same event
*
* See {@link StreamPropertyChangedEvent} to learn more.
*
* @param enabled `true` to publish the video stream, `false` to unpublish it
* @param resource
*
* If parameter **`enabled`** is `false`, this optional parameter is of type boolean. It can be set to `true` to forcibly free the hardware resource associated to the video track, or can be set to `false` to keep the access to the hardware resource.
* Not freeing the resource makes the operation much more efficient, but depending on the platform two side-effects can be introduced: the video device may not be accessible by other applications and the access light of
* webcams may remain on. This is platform-dependent: some browsers will not present the side-effects even when not freeing the resource.
*
* If parameter **`enabled`** is `true`, this optional parameter is of type [MediaStreamTrack](https://developer.mozilla.org/docs/Web/API/MediaStreamTrack). It can be set to force the restoration of the video track with a custom track. This may be
* useful if the Publisher was unpublished freeing the hardware resource, and openvidu-browser is not able to successfully re-create the video track as it was before unpublishing. In this way previous track settings will be ignored and this MediaStreamTrack
* will be used instead.
*/
publishVideo<T extends boolean>(enabled: T, resource?: T extends false ? boolean : MediaStreamTrack): Promise<void> {
return new Promise(async (resolve, reject) => {
if (this.stream.videoActive !== enabled) {
const affectedMediaStream: MediaStream = this.stream.displayMyRemote()
? this.stream.localMediaStreamWhenSubscribedToRemote!
: this.stream.getMediaStream();
let mustRestartMediaStream = false;
affectedMediaStream.getVideoTracks().forEach((track) => {
track.enabled = enabled;
if (!enabled && resource === true) {
track.stop();
} else if (enabled && track.readyState === 'ended') {
// Resource was freed
mustRestartMediaStream = true;
}
});
// There is a Virtual Background filter applied that must be removed in case the hardware must be freed
if (!enabled && resource === true && !!this.stream.filter && this.stream.filter.type.startsWith('VB:')) {
this.stream.lastVBFilter = this.stream.filter; // Save the filter to be re-applied in case of unmute
await this.stream.removeFilterAux(true);
}
if (mustRestartMediaStream) {
const oldVideoTrack = affectedMediaStream.getVideoTracks()[0];
affectedMediaStream.removeTrack(oldVideoTrack);
const replaceVideoTrack = async (tr: MediaStreamTrack) => {
affectedMediaStream.addTrack(tr);
if (this.stream.isLocalStreamPublished) {
await this.replaceTrackInRtcRtpSender(tr);
}
if (!!this.stream.lastVBFilter) {
setTimeout(async () => {
let options = this.stream.lastVBFilter!.options;
const lastExecMethod = this.stream.lastVBFilter!.lastExecMethod;
if (!!lastExecMethod && lastExecMethod.method === 'update') {
options = Object.assign({}, options, lastExecMethod.params);
}
await this.stream.applyFilter(this.stream.lastVBFilter!.type, options);
delete this.stream.lastVBFilter;
}, 1);
}
};
if (!!resource && resource instanceof MediaStreamTrack) {
await replaceVideoTrack(resource);
} else {
try {
const mediaStream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: this.stream.lastVideoTrackConstraints
});
await replaceVideoTrack(mediaStream.getVideoTracks()[0]);
} catch (error) {
return reject(error);
}
}
}
if (!!this.session && !!this.stream.streamId) {
this.session.openvidu.sendRequest(
'streamPropertyChanged',
{
streamId: this.stream.streamId,
property: 'videoActive',
newValue: enabled,
reason: 'publishVideo'
},
(error, response) => {
if (error) {
logger.error("Error sending 'streamPropertyChanged' event", error);
} else {
this.session.emitEvent('streamPropertyChanged', [
new StreamPropertyChangedEvent(
this.session,
this.stream,
'videoActive',
enabled,
!enabled,
'publishVideo'
)
]);
this.emitEvent('streamPropertyChanged', [
new StreamPropertyChangedEvent(this, this.stream, 'videoActive', enabled, !enabled, 'publishVideo')
]);
this.session.sendVideoData(this.stream.streamManager);
}
}
);
}
this.stream.videoActive = enabled;
logger.info("'Publisher' has " + (enabled ? 'published' : 'unpublished') + ' its video stream');
return resolve();
}
});
}
/**
* Call this method before {@link Session.publish} if you prefer to subscribe to your Publisher's remote stream instead of using the local stream, as any other user would do.
*/
subscribeToRemote(value?: boolean): void {
value = value !== undefined ? value : true;
this.isSubscribedToRemote = value;
this.stream.subscribeToMyRemote(value);
}
/**
* See {@link EventDispatcher.on}
*/
on<K extends keyof PublisherEventMap>(type: K, handler: (event: PublisherEventMap[K]) => void): this {
super.on(<any>type, handler);
if (type === 'streamCreated') {
if (!!this.stream && this.stream.isLocalStreamPublished) {
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
} else {
this.stream.ee.on('stream-created-by-publisher', () => {
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
});
}
}
if (type === 'accessAllowed') {
if (this.accessAllowed) {
this.emitEvent('accessAllowed', []);
}
}
if (type === 'accessDenied') {
if (this.accessDenied) {
this.emitEvent('accessDenied', []);
}
}
return this;
}
/**
* See {@link EventDispatcher.once}
*/
once<K extends keyof PublisherEventMap>(type: K, handler: (event: PublisherEventMap[K]) => void): this {
super.once(<any>type, handler);
if (type === 'streamCreated') {
if (!!this.stream && this.stream.isLocalStreamPublished) {
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
} else {
this.stream.ee.once('stream-created-by-publisher', () => {
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
});
}
}
if (type === 'accessAllowed') {
if (this.accessAllowed) {
this.emitEvent('accessAllowed', []);
}
}
if (type === 'accessDenied') {
if (this.accessDenied) {
this.emitEvent('accessDenied', []);
}
}
return this;
}
/**
* See {@link EventDispatcher.off}
*/
off<K extends keyof PublisherEventMap>(type: K, handler?: (event: PublisherEventMap[K]) => void): this {
super.off(<any>type, handler);
return this;
}
/**
* Replaces the current video or audio track with a different one. This allows you to replace an ongoing track with a different one
* without having to renegotiate the whole WebRTC connection (that is, initializing a new Publisher, unpublishing the previous one
* and publishing the new one).
*
* You can get this new MediaStreamTrack by using the native Web API or simply with {@link OpenVidu.getUserMedia} method.
*
* **WARNING: this method has been proven to work in the majority of cases, but there may be some combinations of published/replaced tracks that may be incompatible
* between them and break the connection in OpenVidu Server. A complete renegotiation may be the only solution in this case.
* Visit [RTCRtpSender.replaceTrack](https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/replaceTrack) documentation for further details.**
*
* @param track The [MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) object to replace the current one.
* If it is an audio track, the current audio track will be the replaced one. If it is a video track, the current video track will be the replaced one.
*
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the track was successfully replaced and rejected with an Error object in other case
*/
async replaceTrack(track: MediaStreamTrack): Promise<void> {
return this.replaceTrackAux(track, true);
}
/* Hidden methods */
/**
* @hidden
*/
initialize(): Promise<void> {
return new Promise(async (resolve, reject) => {
let constraints: MediaStreamConstraints = {};
let constraintsAux: MediaStreamConstraints = {};
const timeForDialogEvent = 2000;
let startTime;
const errorCallback = (openViduError: OpenViduError) => {
this.accessDenied = true;
this.accessAllowed = false;
logger.error(`Publisher initialization failed. ${openViduError.name}: ${openViduError.message}`);
return reject(openViduError);
};
const successCallback = (mediaStream: MediaStream) => {
this.accessAllowed = true;
this.accessDenied = false;
if (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack) {
mediaStream.removeTrack(mediaStream.getAudioTracks()[0]);
mediaStream.addTrack(<MediaStreamTrack>this.properties.audioSource);
}
if (typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack) {
mediaStream.removeTrack(mediaStream.getVideoTracks()[0]);
mediaStream.addTrack(<MediaStreamTrack>this.properties.videoSource);
}
// Apply PublisherProperties.publishAudio and PublisherProperties.publishVideo
if (!!mediaStream.getAudioTracks()[0]) {
const enabled =
this.stream.audioActive !== undefined && this.stream.audioActive !== null
? this.stream.audioActive
: !!this.stream.outboundStreamOpts.publisherProperties.publishAudio;
mediaStream.getAudioTracks()[0].enabled = enabled;
}
if (!!mediaStream.getVideoTracks()[0]) {
const enabled =
this.stream.videoActive !== undefined && this.stream.videoActive !== null
? this.stream.videoActive
: !!this.stream.outboundStreamOpts.publisherProperties.publishVideo;
mediaStream.getVideoTracks()[0].enabled = enabled;
}
// Set Content Hint on all MediaStreamTracks
for (const track of mediaStream.getAudioTracks()) {
if (!track.contentHint?.length) {
// contentHint for audio: "", "speech", "speech-recognition", "music".
// https://w3c.github.io/mst-content-hint/#audio-content-hints
track.contentHint = '';
logger.info(`Audio track Content Hint set: '${track.contentHint}'`);
}
}
for (const track of mediaStream.getVideoTracks()) {
if (!track.contentHint?.length) {
// contentHint for video: "", "motion", "detail", "text".
// https://w3c.github.io/mst-content-hint/#video-content-hints
switch (this.stream.typeOfVideo) {
case TypeOfVideo.SCREEN:
track.contentHint = 'detail';
break;
case TypeOfVideo.CUSTOM:
logger.warn('CUSTOM type video track was provided without Content Hint!');
track.contentHint = 'motion';
break;
case TypeOfVideo.CAMERA:
case TypeOfVideo.IPCAM:
default:
track.contentHint = 'motion';
break;
}
logger.info(`Video track Content Hint set: '${track.contentHint}'`);
}
}
this.initializeVideoReference(mediaStream);
if (!this.stream.displayMyRemote()) {
// When we are subscribed to our remote we don't still set the MediaStream object in the video elements to
// avoid early 'streamPlaying' event
this.stream.updateMediaStreamInVideos();
}
delete this.firstVideoElement;
if (this.stream.isSendVideo()) {
// Has video track
this.getVideoDimensions().then((dimensions) => {
this.stream.videoDimensions = {
width: dimensions.width,
height: dimensions.height
};
if (this.stream.isSendScreen()) {
if(this.stream.isSendAudio() && mediaStream.getAudioTracks().length === 0){
// If sending audio is enabled and there are no audio tracks in the mediaStream, disable audio for screen sharing.
this.stream.audioActive = false;
this.stream.hasAudio = false;
this.stream.outboundStreamOpts.publisherProperties.publishAudio = false;
this.stream.outboundStreamOpts.publisherProperties.audioSource = false;
}
// Set interval to listen for screen resize events
this.screenShareResizeInterval = setInterval(() => {
const settings: MediaTrackSettings = mediaStream.getVideoTracks()[0].getSettings();
const newWidth = settings.width;
const newHeight = settings.height;
const widthChanged = newWidth != null && newWidth !== this.stream.videoDimensions.width;
const heightChanged = newHeight != null && newHeight !== this.stream.videoDimensions.height;
if (this.stream.isLocalStreamPublished && (widthChanged || heightChanged)) {
this.openvidu.sendVideoDimensionsChangedEvent(
this,
'screenResized',
this.stream.videoDimensions.width,
this.stream.videoDimensions.height,
newWidth || 0,
newHeight || 0
);
}
}, 650);
}
this.stream.isLocalStreamReadyToPublish = true;
this.stream.ee.emitEvent('stream-ready-to-publish', []);
});
} else {
// Only audio track (no videoDimensions)
this.stream.isLocalStreamReadyToPublish = true;
this.stream.ee.emitEvent('stream-ready-to-publish', []);
}
return resolve();
};
const getMediaSuccess = async (mediaStream: MediaStream, definedAudioConstraint) => {
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
if (this.stream.isSendScreen() && this.properties.audioSource !== 'screen' && this.stream.isSendAudio()) {
// When getting desktop as user media audio constraint must be false. Now we can ask for it if required
constraintsAux.audio = definedAudioConstraint;
constraintsAux.video = false;
startTime = Date.now();
this.setPermissionDialogTimer(timeForDialogEvent);
try {
const audioOnlyStream = await navigator.mediaDevices.getUserMedia(constraintsAux);
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
mediaStream.addTrack(audioOnlyStream.getAudioTracks()[0]);
successCallback(mediaStream);
} catch (error) {
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
mediaStream.getAudioTracks().forEach((track) => {
track.stop();
});
mediaStream.getVideoTracks().forEach((track) => {
track.stop();
});
errorCallback(this.openvidu.generateAudioDeviceError(error, constraints));
return;
}
} else {
successCallback(mediaStream);
}
};
const getMediaError = async (error) => {
logger.error(`getMediaError: ${error.toString()}`);
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
if (error.name === 'Error') {
// Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError'
error.name = error.constructor.name;
}
let errorName, errorMessage;
switch (error.name.toLowerCase()) {
case 'notfounderror':
try {
const mediaStream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: constraints.video
});
mediaStream.getVideoTracks().forEach((track) => {
track.stop();
});
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
errorMessage = error.toString();
errorCallback(new OpenViduError(errorName, errorMessage));
} catch (error) {
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
errorMessage = error.toString();
errorCallback(new OpenViduError(errorName, errorMessage));
}
break;
case 'notallowederror':
errorName = this.stream.isSendScreen()
? OpenViduErrorName.SCREEN_CAPTURE_DENIED
: OpenViduErrorName.DEVICE_ACCESS_DENIED;
errorMessage = error.toString();
errorCallback(new OpenViduError(errorName, errorMessage));
break;
case 'overconstrainederror':
try {
const mediaStream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: constraints.video
});
mediaStream.getVideoTracks().forEach((track) => {
track.stop();
});
if (error.constraint.toLowerCase() === 'deviceid') {
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
errorMessage =
"Audio input device with deviceId '" +
(<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.audio).deviceId!!).exact +
"' not found";
} else {
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
errorMessage =
"Audio input device doesn't support the value passed for constraint '" + error.constraint + "'";
}
errorCallback(new OpenViduError(errorName, errorMessage));
} catch (error) {
if (error.constraint.toLowerCase() === 'deviceid') {
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
errorMessage =
"Video input device with deviceId '" +
(<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact +
"' not found";
} else {
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
errorMessage =
"Video input device doesn't support the value passed for constraint '" + error.constraint + "'";
}
errorCallback(new OpenViduError(errorName, errorMessage));
}
break;
case 'aborterror':
case 'notreadableerror':
errorName = OpenViduErrorName.DEVICE_ALREADY_IN_USE;
errorMessage = error.toString();
errorCallback(new OpenViduError(errorName, errorMessage));
break;
default:
errorName = OpenViduErrorName.GENERIC_ERROR;
errorMessage = error.toString();
errorCallback(new OpenViduError(errorName, errorMessage));
break;
}
};
try {
const myConstraints = await this.openvidu.generateMediaConstraints(this.properties);
if (
(!!myConstraints.videoTrack && !!myConstraints.audioTrack) ||
(!!myConstraints.audioTrack && myConstraints.constraints?.video === false) ||
(!!myConstraints.videoTrack && myConstraints.constraints?.audio === false)
) {
// No need to call getUserMedia at all. MediaStreamTracks already provided
successCallback(this.openvidu.addAlreadyProvidedTracks(myConstraints, new MediaStream(), this.stream));
} else {
constraints = myConstraints.constraints;
const outboundStreamOptions = {
mediaConstraints: constraints,
publisherProperties: this.properties
};
this.stream.setOutboundStreamOptions(outboundStreamOptions);
const definedAudioConstraint = constraints.audio === undefined ? true : constraints.audio;
constraintsAux.audio = this.stream.isSendScreen() ? false : definedAudioConstraint;
constraintsAux.video = constraints.video;
startTime = Date.now();
this.setPermissionDialogTimer(timeForDialogEvent);
try {
if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && !platform.isElectron()) {
const mediaStream = await navigator.mediaDevices['getDisplayMedia']({ video: true, audio: this.properties.audioSource === 'screen' });
this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream);
await getMediaSuccess(mediaStream, definedAudioConstraint);
} else {
this.stream.lastVideoTrackConstraints = constraintsAux.video;
const mediaStream = await navigator.mediaDevices.getUserMedia(constraintsAux);
this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream, this.stream);
await getMediaSuccess(mediaStream, definedAudioConstraint);
}
} catch (error) {
await getMediaError(error);
}
}
} catch (error) {
errorCallback(error);
}
});
}
/**
* @hidden
*/
async replaceTrackAux(track: MediaStreamTrack, updateLastConstraints: boolean): Promise<void> {
// Set field "enabled" of the new track to the previous value
const trackOriginalEnabledValue: boolean = track.enabled;
if (track.kind === 'video') {
track.enabled = this.stream.videoActive;
} else if (track.kind === 'audio') {
track.enabled = this.stream.audioActive;
}
try {
if (this.stream.isLocalStreamPublished) {
// Only if the Publisher has been published is necessary to call native Web API RTCRtpSender.replaceTrack
// If it has not been published yet, replacing it on the MediaStream object is enough
this.replaceTrackInMediaStream(track, updateLastConstraints);
return await this.replaceTrackInRtcRtpSender(track);
} else {
// Publisher not published. Simply replace the track on the local MediaStream
return this.replaceTrackInMediaStream(track, updateLastConstraints);
}
} catch (error) {
track.enabled = trackOriginalEnabledValue;
throw error;
}
}
/**
* @hidden
*
* To obtain the videoDimensions we wait for the video reference to have enough metadata
* and then try to use MediaStreamTrack.getSettingsMethod(). If not available, then we
* use the HTMLVideoElement properties videoWidth and videoHeight
*/
getVideoDimensions(): Promise<{ width: number; height: number }> {
return new Promise((resolve, reject) => {
// Ionic iOS and Safari iOS supposedly require the video element to actually exist inside the DOM
const requiresDomInsertion: boolean = (platform.isIonicIos() || platform.isIOSWithSafari()) && (this.videoReference.readyState < 1);
let loadedmetadataListener;
const resolveDimensions = () => {
let width: number;
let height: number;
if (typeof this.stream.getMediaStream().getVideoTracks()[0].getSettings === 'function') {
const settings = this.stream.getMediaStream().getVideoTracks()[0].getSettings();
width = settings.width || this.videoReference.videoWidth;
height = settings.height || this.videoReference.videoHeight;
} else {
logger.warn('MediaStreamTrack does not have getSettings method on ' + platform.getDescription());
width = this.videoReference.videoWidth;
height = this.videoReference.videoHeight;
}
if (loadedmetadataListener != null) {
this.videoReference.removeEventListener('loadedmetadata', loadedmetadataListener);
}
if (requiresDomInsertion) {
document.body.removeChild(this.videoReference);
}
return resolve({ width, height });
};
if (this.videoReference.readyState >= 1) {
// The video already has metadata available
// No need of loadedmetadata event
resolveDimensions();
} else {
// The video does not have metadata available yet
// Must listen to loadedmetadata event
loadedmetadataListener = () => {
if (!this.videoReference.videoWidth) {
let interval = setInterval(() => {
if (!!this.videoReference.videoWidth) {
clearInterval(interval);
resolveDimensions();
}
}, 40);
} else {
resolveDimensions();
}
};
this.videoReference.addEventListener('loadedmetadata', loadedmetadataListener);
if (requiresDomInsertion) {
document.body.appendChild(this.videoReference);
}
}
});
}
/**
* @hidden
*/
reestablishStreamPlayingEvent() {
if (this.ee.getListeners('streamPlaying').length > 0) {
this.addPlayEventToFirstVideo();
}
}
/**
* @hidden
*/
initializeVideoReference(mediaStream: MediaStream) {
this.videoReference = document.createElement('video');
this.videoReference.style.display = 'none';
this.videoReference.muted = true;
this.videoReference.autoplay = true;
this.videoReference.controls = false;
if (
platform.isSafariBrowser() ||
(platform.isIPhoneOrIPad() &&
(platform.isChromeMobileBrowser() ||
platform.isEdgeMobileBrowser() ||
platform.isOperaMobileBrowser() ||
platform.isFirefoxMobileBrowser()))
) {
this.videoReference.playsInline = true;
}
this.stream.setMediaStream(mediaStream);
if (!!this.firstVideoElement) {
this.createVideoElement(this.firstVideoElement.targetElement, <VideoInsertMode>this.properties.insertMode);
}
this.videoReference.srcObject = this.stream.getMediaStream();
}
/**
* @hidden
*/
replaceTrackInMediaStream(track: MediaStreamTrack, updateLastConstraints: boolean): void {
const mediaStream: MediaStream = this.stream.displayMyRemote()
? this.stream.localMediaStreamWhenSubscribedToRemote!
: this.stream.getMediaStream();
let removedTrack: MediaStreamTrack;
if (track.kind === 'video') {
removedTrack = mediaStream.getVideoTracks()[0];
if (updateLastConstraints) {
this.stream.lastVideoTrackConstraints = track.getConstraints();
}
} else {
removedTrack = mediaStream.getAudioTracks()[0];
}
removedTrack.enabled = false;
removedTrack.stop();
mediaStream.removeTrack(removedTrack);
mediaStream.addTrack(track);
const trackInfo = {
oldLabel: removedTrack?.label || '',
newLabel: track?.label || ''
};
if (track.kind === 'video' && updateLastConstraints) {
this.openvidu.sendNewVideoDimensionsIfRequired(this, 'trackReplaced', 50, 30);
this.openvidu.sendTrackChangedEvent(this, trackInfo.oldLabel, trackInfo.newLabel, 'videoTrack');
if (this.stream.isLocalStreamPublished) {
this.session.sendVideoData(this.stream.streamManager, 5, true, 5);
}
} else if (track.kind === 'audio' && updateLastConstraints) {
this.openvidu.sendTrackChangedEvent(this, trackInfo.oldLabel, trackInfo.newLabel, 'audioTrack');
}
if (track.kind === 'audio') {
this.stream.disableHarkSpeakingEvent(false);
this.stream.disableHarkStoppedSpeakingEvent(false);
this.stream.disableHarkVolumeChangeEvent(false);
this.stream.initHarkEvents();
}
}
/* Private methods */
private setPermissionDialogTimer(waitTime: number): void {
this.permissionDialogTimeout = setTimeout(() => {
this.emitEvent('accessDialogOpened', []);
}, waitTime);
}
private clearPermissionDialogTimer(startTime: number, waitTime: number): void {
clearTimeout(this.permissionDialogTimeout);
if (Date.now() - startTime > waitTime) {
// Permission dialog was shown and now is closed
this.emitEvent('accessDialogClosed', []);
}
}
private async replaceTrackInRtcRtpSender(track: MediaStreamTrack): Promise<void> {
const senders: RTCRtpSender[] = this.stream.getRTCPeerConnection().getSenders();
let sender: RTCRtpSender | undefined;
if (track.kind === 'video') {
sender = senders.find((s) => !!s.track && s.track.kind === 'video');
if (!sender) {
throw new Error("There's no replaceable track for that kind of MediaStreamTrack in this Publisher object");
}
} else if (track.kind === 'audio') {
sender = senders.find((s) => !!s.track && s.track.kind === 'audio');
if (!sender) {
throw new Error("There's no replaceable track for that kind of MediaStreamTrack in this Publisher object");
}
} else {
throw new Error('Unknown track kind ' + track.kind);
}
await (sender as RTCRtpSender).replaceTrack(track);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,621 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Stream } from './Stream';
import { Subscriber } from './Subscriber';
import { EventDispatcher } from './EventDispatcher';
import { StreamManagerVideo } from '../OpenViduInternal/Interfaces/Public/StreamManagerVideo';
import { StreamManagerEventMap } from '../OpenViduInternal/Events/EventMap/StreamManagerEventMap';
import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent';
import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent';
import { ExceptionEvent, ExceptionEventName } from '../OpenViduInternal/Events/ExceptionEvent';
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
/**
* Interface in charge of displaying the media streams in the HTML DOM. This wraps any {@link Publisher} and {@link Subscriber} object.
* You can insert as many video players fo the same Stream as you want by calling {@link StreamManager.addVideoElement} or
* {@link StreamManager.createVideoElement}.
* The use of StreamManager wrapper is particularly useful when you don't need to differentiate between Publisher or Subscriber streams or just
* want to directly manage your own video elements (even more than one video element per Stream). This scenario is pretty common in
* declarative, MVC frontend frameworks such as Angular, React or Vue.js
*
* See available event listeners at {@link StreamManagerEventMap}.
*/
export abstract class StreamManager extends EventDispatcher {
/**
* The Stream represented in the DOM by the Publisher/Subscriber
*/
stream: Stream;
/**
* All the videos displaying the Stream of this Publisher/Subscriber
*/
videos: StreamManagerVideo[] = [];
/**
* Whether the Stream represented in the DOM is local or remote
* - `false` for {@link Publisher}
* - `true` for {@link Subscriber}
*/
remote: boolean;
/**
* The DOM HTMLElement assigned as target element when creating the video for the Publisher/Subscriber. This property is only defined if:
* - {@link Publisher} has been initialized by calling method {@link OpenVidu.initPublisher} with a valid `targetElement` parameter
* - {@link Subscriber} has been initialized by calling method {@link Session.subscribe} with a valid `targetElement` parameter
*/
targetElement: HTMLElement;
/**
* `id` attribute of the DOM video element displaying the Publisher/Subscriber's stream. This property is only defined if:
* - {@link Publisher} has been initialized by calling method {@link OpenVidu.initPublisher} with a valid `targetElement` parameter
* - {@link Subscriber} has been initialized by calling method {@link Session.subscribe} with a valid `targetElement` parameter
*/
id: string;
/**
* @hidden
*/
protected firstVideoElement?: StreamManagerVideo;
/**
* @hidden
*/
protected element: HTMLElement;
/**
* @hidden
*/
protected canPlayListener: EventListener;
/**
* @hidden
*/
private streamPlayingEventExceptionTimeout?: NodeJS.Timeout;
/**
* @hidden
*/
private lazyLaunchVideoElementCreatedEvent = false;
/**
* @hidden
*/
constructor(stream: Stream, targetElement?: HTMLElement | string) {
super();
platform = PlatformUtils.getInstance();
this.stream = stream;
this.stream.streamManager = this;
this.remote = !this.stream.isLocal();
if (!!targetElement) {
let targEl;
if (typeof targetElement === 'string') {
targEl = document.getElementById(targetElement);
} else if (targetElement instanceof HTMLElement) {
targEl = targetElement;
}
if (!!targEl) {
this.firstVideoElement = {
targetElement: targEl,
video: document.createElement('video'),
id: '',
canplayListenerAdded: false
};
if (
platform.isSafariBrowser() ||
(platform.isIPhoneOrIPad() &&
(platform.isChromeMobileBrowser() ||
platform.isEdgeMobileBrowser() ||
platform.isOperaMobileBrowser() ||
platform.isFirefoxMobileBrowser()))
) {
this.firstVideoElement.video.playsInline = true;
}
this.targetElement = targEl;
this.element = targEl;
}
}
this.canPlayListener = () => {
this.deactivateStreamPlayingEventExceptionTimeout();
this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]);
};
}
/**
* See {@link EventDispatcher.on}
*/
on<K extends keyof StreamManagerEventMap>(type: K, handler: (event: StreamManagerEventMap[K]) => void): this {
super.onAux(type, "Event '" + type + "' triggered by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'", handler);
if (type === 'videoElementCreated') {
if (!!this.stream && this.lazyLaunchVideoElementCreatedEvent) {
this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(this.videos[0].video, this, 'videoElementCreated')]);
this.lazyLaunchVideoElementCreatedEvent = false;
}
}
if (type === 'streamPlaying') {
if (
this.videos[0] &&
this.videos[0].video &&
this.videos[0].video.currentTime > 0 &&
this.videos[0].video.paused === false &&
this.videos[0].video.ended === false &&
this.videos[0].video.readyState === 4
) {
this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]);
}
}
if (this.stream.hasAudio) {
if (type === 'publisherStartSpeaking') {
this.stream.enableHarkSpeakingEvent();
}
if (type === 'publisherStopSpeaking') {
this.stream.enableHarkStoppedSpeakingEvent();
}
if (type === 'streamAudioVolumeChange') {
this.stream.enableHarkVolumeChangeEvent(false);
}
}
return this;
}
/**
* See {@link EventDispatcher.once}
*/
once<K extends keyof StreamManagerEventMap>(type: K, handler: (event: StreamManagerEventMap[K]) => void): this {
super.onceAux(type, "Event '" + type + "' triggered once by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'", handler);
if (type === 'videoElementCreated') {
if (!!this.stream && this.lazyLaunchVideoElementCreatedEvent) {
this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(this.videos[0].video, this, 'videoElementCreated')]);
}
}
if (type === 'streamPlaying') {
if (
this.videos[0] &&
this.videos[0].video &&
this.videos[0].video.currentTime > 0 &&
this.videos[0].video.paused === false &&
this.videos[0].video.ended === false &&
this.videos[0].video.readyState === 4
) {
this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]);
}
}
if (this.stream.hasAudio) {
if (type === 'publisherStartSpeaking') {
this.stream.enableOnceHarkSpeakingEvent();
}
if (type === 'publisherStopSpeaking') {
this.stream.enableOnceHarkStoppedSpeakingEvent();
}
if (type === 'streamAudioVolumeChange') {
this.stream.enableOnceHarkVolumeChangeEvent(false);
}
}
return this;
}
/**
* See {@link EventDispatcher.off}
*/
off<K extends keyof StreamManagerEventMap>(type: K, handler?: (event: StreamManagerEventMap[K]) => void): this {
super.offAux(type, handler);
if (type === 'publisherStartSpeaking') {
// Both StreamManager and Session can have "publisherStartSpeaking" event listeners
const remainingStartSpeakingEventListeners =
this.ee.getListeners(type).length + this.stream.session.ee.getListeners(type).length;
if (remainingStartSpeakingEventListeners === 0) {
this.stream.disableHarkSpeakingEvent(false);
}
}
if (type === 'publisherStopSpeaking') {
// Both StreamManager and Session can have "publisherStopSpeaking" event listeners
const remainingStopSpeakingEventListeners =
this.ee.getListeners(type).length + this.stream.session.ee.getListeners(type).length;
if (remainingStopSpeakingEventListeners === 0) {
this.stream.disableHarkStoppedSpeakingEvent(false);
}
}
if (type === 'streamAudioVolumeChange') {
// Only StreamManager can have "streamAudioVolumeChange" event listeners
const remainingVolumeEventListeners = this.ee.getListeners(type).length;
if (remainingVolumeEventListeners === 0) {
this.stream.disableHarkVolumeChangeEvent(false);
}
}
return this;
}
/**
* Makes `video` element parameter display this {@link stream}. This is useful when you are
* [managing the video elements on your own](/en/stable/cheatsheet/manage-videos/#you-take-care-of-the-video-players)
*
* Calling this method with a video already added to other Publisher/Subscriber will cause the video element to be
* disassociated from that previous Publisher/Subscriber and to be associated to this one.
*
* @returns 1 if the video wasn't associated to any other Publisher/Subscriber and has been successfully added to this one.
* 0 if the video was already added to this Publisher/Subscriber. -1 if the video was previously associated to any other
* Publisher/Subscriber and has been successfully disassociated from that one and properly added to this one.
*/
addVideoElement(video: HTMLVideoElement): number {
this.initializeVideoProperties(video);
if (!this.remote && this.stream.displayMyRemote()) {
if (video.srcObject !== this.stream.getMediaStream()) {
video.srcObject = this.stream.getMediaStream();
}
}
// If the video element is already part of this StreamManager do nothing
for (const v of this.videos) {
if (v.video === video) {
return 0;
}
}
let returnNumber = 1;
for (const streamManager of this.stream.session.streamManagers) {
if (streamManager.disassociateVideo(video)) {
returnNumber = -1;
break;
}
}
this.stream.session.streamManagers.forEach((streamManager) => {
streamManager.disassociateVideo(video);
});
this.pushNewStreamManagerVideo({
video,
id: video.id,
canplayListenerAdded: false
});
logger.info('New video element associated to ', this);
return returnNumber;
}
/**
* Creates a new video element displaying this {@link stream}. This allows you to have multiple video elements displaying the same media stream.
*
* #### Events dispatched
*
* The Publisher/Subscriber object will dispatch a `videoElementCreated` event once the HTML video element has been added to DOM. See {@link VideoElementEvent}
*
* @param targetElement HTML DOM element (or its `id` attribute) in which the video element of the Publisher/Subscriber will be inserted
* @param insertMode How the video element will be inserted accordingly to `targetElemet`
*
* @returns The created HTMLVideoElement
*/
createVideoElement(targetElement?: string | HTMLElement, insertMode?: VideoInsertMode): HTMLVideoElement {
let targEl;
if (typeof targetElement === 'string') {
targEl = document.getElementById(targetElement);
if (!targEl) {
throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement);
}
} else if (targetElement instanceof HTMLElement) {
targEl = targetElement;
} else {
throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement);
}
const video = this.createVideo();
this.initializeVideoProperties(video);
let insMode = !!insertMode ? insertMode : VideoInsertMode.APPEND;
switch (insMode) {
case VideoInsertMode.AFTER:
targEl.parentNode!!.insertBefore(video, targEl.nextSibling);
break;
case VideoInsertMode.APPEND:
targEl.appendChild(video);
break;
case VideoInsertMode.BEFORE:
targEl.parentNode!!.insertBefore(video, targEl);
break;
case VideoInsertMode.PREPEND:
targEl.insertBefore(video, targEl.childNodes[0]);
break;
case VideoInsertMode.REPLACE:
targEl.parentNode!!.replaceChild(video, targEl);
break;
default:
insMode = VideoInsertMode.APPEND;
targEl.appendChild(video);
break;
}
const v: StreamManagerVideo = {
targetElement: targEl,
video,
insertMode: insMode,
id: video.id,
canplayListenerAdded: false
};
this.pushNewStreamManagerVideo(v);
this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(v.video, this, 'videoElementCreated')]);
this.lazyLaunchVideoElementCreatedEvent = !!this.firstVideoElement;
return video;
}
/**
* Updates the current configuration for the {@link PublisherSpeakingEvent} feature and the [StreamManagerEvent.streamAudioVolumeChange](/en/stable/api/openvidu-browser/classes/StreamManagerEvent.html) feature for this specific
* StreamManager audio stream, overriding the global options set with {@link OpenVidu.setAdvancedConfiguration}. This way you can customize the audio events options
* for each specific StreamManager and change them dynamically.
*
* @param publisherSpeakingEventsOptions New options to be applied to this StreamManager's audio stream. It is an object which includes the following optional properties:
* - `interval`: (number) how frequently the analyser polls the audio stream to check if speaking has started/stopped or audio volume has changed. Default **100** (ms)
* - `threshold`: (number) the volume at which _publisherStartSpeaking_, _publisherStopSpeaking_ events will be fired. Default **-50** (dB)
*/
updatePublisherSpeakingEventsOptions(publisherSpeakingEventsOptions: { interval?: number; threshold?: number }): void {
const currentHarkOptions = !!this.stream.harkOptions
? this.stream.harkOptions
: this.stream.session.openvidu.advancedConfiguration.publisherSpeakingEventsOptions || {};
const newInterval =
typeof publisherSpeakingEventsOptions.interval === 'number'
? publisherSpeakingEventsOptions.interval
: typeof currentHarkOptions.interval === 'number'
? currentHarkOptions.interval
: 100;
const newThreshold =
typeof publisherSpeakingEventsOptions.threshold === 'number'
? publisherSpeakingEventsOptions.threshold
: typeof currentHarkOptions.threshold === 'number'
? currentHarkOptions.threshold
: -50;
this.stream.harkOptions = {
interval: newInterval,
threshold: newThreshold
};
if (!!this.stream.speechEvent) {
this.stream.speechEvent.setInterval(newInterval);
this.stream.speechEvent.setThreshold(newThreshold);
}
}
/* Hidden methods */
/**
* @hidden
*/
initializeVideoProperties(video: HTMLVideoElement): void {
if (!(!this.remote && this.stream.displayMyRemote())) {
// Avoid setting the MediaStream into the srcObject if remote subscription before publishing
if (video.srcObject !== this.stream.getMediaStream()) {
// If srcObject already set don't do it again
video.srcObject = this.stream.getMediaStream();
}
}
video.autoplay = true;
video.controls = false;
if (
platform.isSafariBrowser() ||
(platform.isIPhoneOrIPad() &&
(platform.isChromeMobileBrowser() ||
platform.isEdgeMobileBrowser() ||
platform.isOperaMobileBrowser() ||
platform.isFirefoxMobileBrowser()))
) {
video.playsInline = true;
}
if (!video.id) {
video.id = (this.remote ? 'remote-' : 'local-') + 'video-' + this.stream.streamId;
// DEPRECATED property: assign once the property id if the user provided a valid targetElement
if (!this.id && !!this.targetElement) {
this.id = video.id;
}
}
if (this.remote && this.isMirroredVideo(video)) {
// Subscriber video associated to a previously mirrored video element
this.removeMirrorVideo(video);
} else if (!this.remote && !this.stream.displayMyRemote()) {
// Publisher video
video.muted = true;
if (this.isMirroredVideo(video) && !this.stream.outboundStreamOpts.publisherProperties.mirror) {
// If the video was already rotated and now is set to not mirror
this.removeMirrorVideo(video);
} else if (this.stream.outboundStreamOpts.publisherProperties.mirror && !this.stream.isSendScreen()) {
// If the video is now set to mirror and is not screen share
this.mirrorVideo(video);
}
}
}
/**
* @hidden
*/
removeAllVideos(): void {
for (let i = this.stream.session.streamManagers.length - 1; i >= 0; --i) {
if (this.stream.session.streamManagers[i] === this) {
this.stream.session.streamManagers.splice(i, 1);
}
}
this.videos.forEach((streamManagerVideo) => {
// Remove oncanplay event listener (only OpenVidu browser listener, not the user ones)
if (!!streamManagerVideo.video && !!streamManagerVideo.video.removeEventListener) {
streamManagerVideo.video.removeEventListener('canplay', this.canPlayListener);
}
streamManagerVideo.canplayListenerAdded = false;
if (!!streamManagerVideo.targetElement) {
// Only remove from DOM videos created by OpenVidu Browser (those generated by passing a valid targetElement in OpenVidu.initPublisher
// and Session.subscribe or those created by StreamManager.createVideoElement). All this videos triggered a videoElementCreated event
streamManagerVideo.video.parentNode!.removeChild(streamManagerVideo.video);
this.ee.emitEvent('videoElementDestroyed', [
new VideoElementEvent(streamManagerVideo.video, this, 'videoElementDestroyed')
]);
}
// Remove srcObject from the video
this.removeSrcObject(streamManagerVideo);
// Remove from collection of videos every video managed by OpenVidu Browser
this.videos = this.videos.filter((v) => !v.targetElement);
});
}
/**
* @hidden
*/
disassociateVideo(video: HTMLVideoElement): boolean {
let disassociated = false;
for (let i = 0; i < this.videos.length; i++) {
if (this.videos[i].video === video) {
this.videos[i].video.removeEventListener('canplay', this.canPlayListener);
this.videos.splice(i, 1);
disassociated = true;
logger.info('Video element disassociated from ', this);
break;
}
}
return disassociated;
}
/**
* @hidden
*/
addPlayEventToFirstVideo() {
if (!!this.videos[0] && !!this.videos[0].video && !this.videos[0].canplayListenerAdded) {
this.activateStreamPlayingEventExceptionTimeout();
this.videos[0].video.addEventListener('canplay', this.canPlayListener);
this.videos[0].canplayListenerAdded = true;
}
}
/**
* @hidden
*/
updateMediaStream(mediaStream: MediaStream) {
this.videos.forEach((streamManagerVideo) => {
streamManagerVideo.video.srcObject = mediaStream;
if (platform.isIonicIos()) {
// iOS Ionic. LIMITATION: must reinsert the video in the DOM for
// the media stream to be updated
const vParent = streamManagerVideo.video.parentElement;
const newVideo = streamManagerVideo.video;
vParent!!.replaceChild(newVideo, streamManagerVideo.video);
streamManagerVideo.video = newVideo;
}
});
}
/**
* @hidden
*/
emitEvent(type: string, eventArray: any[]): void {
this.ee.emitEvent(type, eventArray);
}
/**
* @hidden
*/
createVideo(): HTMLVideoElement {
return document.createElement('video');
}
/**
* @hidden
*/
removeSrcObject(streamManagerVideo: StreamManagerVideo) {
streamManagerVideo.video.srcObject = null;
this.deactivateStreamPlayingEventExceptionTimeout();
}
/**
* @hidden
*/
abstract replaceTrackInMediaStream(track: MediaStreamTrack, updateLastConstraints: boolean): void;
/* Private methods */
protected pushNewStreamManagerVideo(streamManagerVideo: StreamManagerVideo) {
this.videos.push(streamManagerVideo);
this.addPlayEventToFirstVideo();
if (this.stream.session.streamManagers.indexOf(this) === -1) {
this.stream.session.streamManagers.push(this);
}
}
private mirrorVideo(video: HTMLVideoElement): void {
if (!platform.isIonicIos()) {
video.style.transform = 'rotateY(180deg)';
video.style.webkitTransform = 'rotateY(180deg)';
}
}
private removeMirrorVideo(video: HTMLVideoElement): void {
video.style.transform = 'unset';
video.style.webkitTransform = 'unset';
}
private isMirroredVideo(video: HTMLVideoElement): boolean {
return video.style.transform === 'rotateY(180deg)' || video.style.webkitTransform === 'rotateY(180deg)';
}
private activateStreamPlayingEventExceptionTimeout() {
if (!this.remote) {
// ExceptionEvent NO_STREAM_PLAYING_EVENT is only for subscribers
return;
}
if (this.streamPlayingEventExceptionTimeout != null) {
// The timeout is already activated
return;
}
// Trigger ExceptionEvent NO_STREAM_PLAYING_EVENT if after timeout there is no 'canplay' event
const msTimeout = this.stream.session.openvidu.advancedConfiguration.noStreamPlayingEventExceptionTimeout || 4000;
this.streamPlayingEventExceptionTimeout = setTimeout(() => {
const msg =
'StreamManager of Stream ' +
this.stream.streamId +
' (' +
(this.remote ? 'Subscriber' : 'Publisher') +
') did not trigger "streamPlaying" event in ' +
msTimeout +
' ms';
logger.warn(msg);
this.stream.session.emitEvent('exception', [
new ExceptionEvent(this.stream.session, ExceptionEventName.NO_STREAM_PLAYING_EVENT, (<any>this) as Subscriber, msg)
]);
delete this.streamPlayingEventExceptionTimeout;
}, msTimeout);
}
private deactivateStreamPlayingEventExceptionTimeout() {
clearTimeout(this.streamPlayingEventExceptionTimeout as any);
delete this.streamPlayingEventExceptionTimeout;
}
}

View File

@ -1,101 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Stream } from './Stream';
import { StreamManager } from './StreamManager';
import { SubscriberProperties } from '../OpenViduInternal/Interfaces/Public/SubscriberProperties';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* Packs remote media streams. Participants automatically receive them when others publish their streams. Initialized with {@link Session.subscribe} method
*
* See available event listeners at {@link StreamManagerEventMap}.
*/
export class Subscriber extends StreamManager {
/**
* @hidden
*/
properties: SubscriberProperties;
/**
* @hidden
*/
constructor(stream: Stream, targEl: string | HTMLElement | undefined, properties: SubscriberProperties) {
super(stream, targEl);
this.element = this.targetElement;
this.stream = stream;
this.properties = properties;
}
/**
* Subscribe or unsubscribe from the audio stream (if available). Calling this method twice in a row passing same value will have no effect
* @param value `true` to subscribe to the audio stream, `false` to unsubscribe from it
*/
subscribeToAudio(value: boolean): Subscriber {
this.stream
.getMediaStream()
.getAudioTracks()
.forEach((track) => {
track.enabled = value;
});
this.stream.audioActive = value;
logger.info("'Subscriber' has " + (value ? 'subscribed to' : 'unsubscribed from') + ' its audio stream');
return this;
}
/**
* Subscribe or unsubscribe from the video stream (if available). Calling this method twice in a row passing same value will have no effect
* @param value `true` to subscribe to the video stream, `false` to unsubscribe from it
*/
subscribeToVideo(value: boolean): Subscriber {
this.stream
.getMediaStream()
.getVideoTracks()
.forEach((track) => {
track.enabled = value;
});
this.stream.videoActive = value;
logger.info("'Subscriber' has " + (value ? 'subscribed to' : 'unsubscribed from') + ' its video stream');
return this;
}
/* Hidden methods */
/**
* @hidden
*/
replaceTrackInMediaStream(track: MediaStreamTrack, updateLastConstraints: boolean): void {
const mediaStream: MediaStream = this.stream.getMediaStream();
let removedTrack: MediaStreamTrack;
if (track.kind === 'video') {
removedTrack = mediaStream.getVideoTracks()[0];
if (updateLastConstraints) {
this.stream.lastVideoTrackConstraints = track.getConstraints();
}
} else {
removedTrack = mediaStream.getAudioTracks()[0];
}
mediaStream.removeTrack(removedTrack);
removedTrack.stop();
mediaStream.addTrack(track);
}
}

View File

@ -1,35 +0,0 @@
{
//"allowUnusedLabels": true,
"allowUnreachableCode": false,
"buildOnSave": false,
"compileOnSave": true,
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"emitBOM": false,
"forceConsistentCasingInFileNames": true,
"lib": [
"dom",
"es2015.promise",
"es5",
"scripthost"
],
"module": "commonjs",
"noFallthroughCasesInSwitch": true,
//"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
//"noUnusedLocals": true,
//"noUnusedParameters": true,
"outDir": "../../lib",
"preserveConstEnums": true,
"removeComments": true,
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"sourceMap": true,
"strictNullChecks": true,
"suppressExcessPropertyErrors": true,
"suppressImplicitAnyIndexErrors": true,
"target": "es5"
}
}

View File

@ -1,23 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
export enum LocalRecorderState {
READY = 'READY',
RECORDING = 'RECORDING',
PAUSED = 'PAUSED',
FINISHED = 'FINISHED'
}

View File

@ -1,141 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/**
* Defines property {@link OpenViduError.name}
*/
export enum OpenViduErrorName {
/**
* Browser is not supported by OpenVidu.
* Returned upon unsuccessful {@link Session.connect}
*/
BROWSER_NOT_SUPPORTED = 'BROWSER_NOT_SUPPORTED',
/**
* The user hasn't granted permissions to the required input device when the browser asked for them.
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
DEVICE_ACCESS_DENIED = 'DEVICE_ACCESS_DENIED',
/**
* The required input device is probably being used by other process when the browser asked for it.
* This error can also be triggered when the user granted permission to use the devices but a hardware
* error occurred at the OS, browser or web page level, which prevented access to the device.
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
DEVICE_ALREADY_IN_USE = 'DEVICE_ALREADY_IN_USE',
/**
* The user hasn't granted permissions to capture some desktop screen when the browser asked for them.
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED',
/**
* Browser does not support screen sharing.
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
SCREEN_SHARING_NOT_SUPPORTED = 'SCREEN_SHARING_NOT_SUPPORTED',
/**
* Only for Chrome, there's no screen sharing extension installed
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
SCREEN_EXTENSION_NOT_INSTALLED = 'SCREEN_EXTENSION_NOT_INSTALLED',
/**
* Only for Chrome, the screen sharing extension is installed but is disabled
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
SCREEN_EXTENSION_DISABLED = 'SCREEN_EXTENSION_DISABLED',
/**
* No video input device found with the provided deviceId (property {@link PublisherProperties.videoSource})
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
INPUT_VIDEO_DEVICE_NOT_FOUND = 'INPUT_VIDEO_DEVICE_NOT_FOUND',
/**
* No audio input device found with the provided deviceId (property {@link PublisherProperties.audioSource})
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
INPUT_AUDIO_DEVICE_NOT_FOUND = 'INPUT_AUDIO_DEVICE_NOT_FOUND',
/**
* There was an unknown error when trying to access the specified audio device
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
INPUT_AUDIO_DEVICE_GENERIC_ERROR = 'INPUT_AUDIO_DEVICE_GENERIC_ERROR',
/**
* Method {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} has been called with properties `videoSource` and `audioSource` of
* {@link PublisherProperties} parameter both set to *false* or *null*
*/
NO_INPUT_SOURCE_SET = 'NO_INPUT_SOURCE_SET',
/**
* Some media property of {@link PublisherProperties} such as `frameRate` or `resolution` is not supported
* by the input devices (whenever it is possible they are automatically adjusted to the most similar value).
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
*/
PUBLISHER_PROPERTIES_ERROR = 'PUBLISHER_PROPERTIES_ERROR',
/**
* The client tried to call a method without the required permissions. This can occur for methods {@link Session.publish},
* {@link Session.forceUnpublish}, {@link Session.forceDisconnect}, {@link Stream.applyFilter}, {@link Stream.removeFilter}
*/
OPENVIDU_PERMISSION_DENIED = 'OPENVIDU_PERMISSION_DENIED',
/**
* There is no connection to the Session. This error will be thrown when any method requiring a connection to
* openvidu-server is called before successfully calling method {@link Session.connect}
*/
OPENVIDU_NOT_CONNECTED = 'OPENVIDU_NOT_CONNECTED',
/**
* Error related to [Virtual Background](/en/stable/advanced-features/virtual-background/)
*/
VIRTUAL_BACKGROUND_ERROR = 'VIRTUAL_BACKGROUND_ERROR',
/**
* Generic error
*/
GENERIC_ERROR = 'GENERIC_ERROR'
}
/**
* Simple object to identify runtime errors on the client side
*/
export class OpenViduError {
/**
* Uniquely identifying name of the error
*/
name: OpenViduErrorName;
/**
* Full description of the error
*/
message: string;
/**
* @hidden
*/
constructor(name: OpenViduErrorName, message: string) {
this.name = name;
this.message = message;
}
}

View File

@ -1,23 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
export enum TypeOfVideo {
CAMERA = 'CAMERA',
SCREEN = 'SCREEN',
CUSTOM = 'CUSTOM',
IPCAM = 'IPCAM'
}

View File

@ -1,42 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/**
* How the video will be inserted in the DOM for Publishers and Subscribers. See {@link PublisherProperties.insertMode} and {@link SubscriberProperties.insertMode}
*/
export enum VideoInsertMode {
/**
* Video inserted after the target element (as next sibling)
*/
AFTER = 'AFTER',
/**
* Video inserted as last child of the target element
*/
APPEND = 'APPEND',
/**
* Video inserted before the target element (as previous sibling)
*/
BEFORE = 'BEFORE',
/**
* Video inserted as first child of the target element
*/
PREPEND = 'PREPEND',
/**
* Video replaces target element
*/
REPLACE = 'REPLACE'
}

View File

@ -1,61 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Connection } from '../../OpenVidu/Connection';
import { Session } from '../../OpenVidu/Session';
import { ConnectionEventReason } from './Types/Types';
/**
* Triggered by:
* - {@link SessionEventMap.connectionCreated}
* - {@link SessionEventMap.connectionDestroyed}
*/
export class ConnectionEvent extends Event {
/**
* Connection object that was created or destroyed
*/
connection: Connection;
/**
* For `connectionDestroyed` event:
* - "disconnect": the remote user has called `Session.disconnect()`
* - "forceDisconnectByUser": the remote user has been evicted from the Session by other user calling `Session.forceDisconnect()`
* - "forceDisconnectByServer": the remote user has been evicted from the Session by the application
* - "sessionClosedByServer": the Session has been closed by the application
* - "networkDisconnect": the remote user network connection has dropped
* - "nodeCrashed": a node has crashed in the server side
*
* For `connectionCreated` event an empty string
*/
reason: ConnectionEventReason;
/**
* @hidden
*/
constructor(cancelable: boolean, target: Session, type: string, connection: Connection, reason: ConnectionEventReason) {
super(cancelable, target, type);
this.connection = connection;
this.reason = reason;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() { }
}

View File

@ -1,68 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Connection } from '../../OpenVidu/Connection';
import { Session } from '../../OpenVidu/Session';
import { Event } from './Event';
/**
* **This feature is part of OpenVidu
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
* and
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
* editions**
*
* Triggered by {@link SessionEventMap.connectionPropertyChanged}
*/
export class ConnectionPropertyChangedEvent extends Event {
/**
* The Connection whose property has changed
*/
connection: Connection;
/**
* The property of the stream that changed. This value is either `"role"` or `"record"`
*/
changedProperty: string;
/**
* New value of the property (after change, current value)
*/
newValue: Object;
/**
* Previous value of the property (before change)
*/
oldValue: Object;
/**
* @hidden
*/
constructor(target: Session, connection: Connection, changedProperty: string, newValue: Object, oldValue: Object) {
super(false, target, 'connectionPropertyChanged');
this.connection = connection;
this.changedProperty = changedProperty;
this.newValue = newValue;
this.oldValue = oldValue;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() {}
}

View File

@ -1,83 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Filter } from '../../OpenVidu/Filter';
import { StreamManager } from '../../OpenVidu/StreamManager';
import { Session } from '../../OpenVidu/Session';
export abstract class Event {
/**
* Whether the event has a default behavior that may be prevented by calling {@link Event.preventDefault}
*/
cancelable: boolean;
/**
* The object that dispatched the event
*/
target: Session | StreamManager | Filter;
/**
* The type of event. This is the same string you pass as first parameter when calling method `on()` of any object implementing {@link EventDispatcher} interface
*/
type: string;
/**
* @hidden
*/
hasBeenPrevented = false;
/**
* @hidden
*/
constructor(cancelable: boolean, target: Session | StreamManager | Filter, type: string) {
this.cancelable = cancelable;
this.target = target;
this.type = type;
}
/**
* Whether the default beahivour of the event has been prevented or not. Call {@link Event.preventDefault} to prevent it
*/
isDefaultPrevented(): boolean {
return this.hasBeenPrevented;
}
/**
* Prevents the default behavior of the event. The following events have a default behavior:
*
* - `sessionDisconnected`: dispatched by {@link Session} object, automatically unsubscribes the leaving participant from every Subscriber object of the session (this includes closing the RTCPeerConnection and disposing all MediaStreamTracks)
* and also deletes any HTML video element associated to each Subscriber (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement` in method {@link Session.subscribe} or
* by calling {@link Subscriber.createVideoElement}). For every video removed, each Subscriber object will also dispatch a `videoElementDestroyed` event.
*
* - `streamDestroyed`:
* - If dispatched by a {@link Publisher} (*you* have unpublished): automatically stops all media tracks and deletes any HTML video element associated to it (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement`
* in method {@link OpenVidu.initPublisher} or by calling {@link Publisher.createVideoElement}). For every video removed, the Publisher object will also dispatch a `videoElementDestroyed` event.
* - If dispatched by {@link Session} (*other user* has unpublished): automatically unsubscribes the proper Subscriber object from the session (this includes closing the RTCPeerConnection and disposing all MediaStreamTracks)
* and also deletes any HTML video element associated to that Subscriber (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement` in method {@link Session.subscribe} or
* by calling {@link Subscriber.createVideoElement}). For every video removed, the Subscriber object will also dispatch a `videoElementDestroyed` event.
*/
preventDefault() {
// tslint:disable-next-line:no-empty
this.callDefaultBehavior = () => {};
this.hasBeenPrevented = true;
}
/**
* @hidden
*/
abstract callDefaultBehavior();
}

View File

@ -1,21 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/**
* All OpenVidu Browser events inherit from this interface
*/
export interface EventMap {}

View File

@ -1,80 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { StreamEvent } from '../StreamEvent';
import { StreamManagerEventMap } from './StreamManagerEventMap';
/**
* Events dispatched by {@link Publisher} object. Manage event listeners with
* {@link Publisher.on}, {@link Publisher.once} and {@link Publisher.off} methods.
*
* Example:
*
* ```javascript
* publisher.on('accessDenied', () => {
* console.error('Camera access has been denied!');
* }
*
* publisher.off('accessDenied');
* ```
*/
export interface PublisherEventMap extends StreamManagerEventMap {
/**
* Event dispatched when the {@link Publisher} has been published to the session (see {@link Session.publish}).
*/
streamCreated: StreamEvent;
/**
* Event dispatched when the {@link Publisher} has been unpublished from the session.
*/
streamDestroyed: StreamEvent;
/**
* Event dispatched when a Publisher tries to access some media input device and has the required permissions to do so.
*
* This happens when calling {@link OpenVidu.initPublisher} or {@link OpenVidu.initPublisherAsync} and the application
* has permissions to use the devices. This usually means the user has accepted the permissions dialog that the
* browser will show when trying to access the camera/microphone/screen.
*/
accessAllowed: never;
/**
* Event dispatched when a Publisher tries to access some media input device and does NOT have the required permissions to do so.
*
* This happens when calling {@link OpenVidu.initPublisher} or {@link OpenVidu.initPublisherAsync} and the application
* lacks the required permissions to use the devices. This usually means the user has NOT accepted the permissions dialog that the
* browser will show when trying to access the camera/microphone/screen.
*/
accessDenied: never;
/**
* Event dispatched when the pop-up shown by the browser to request permissions for the input media devices is opened.
*
* You can use this event to alert the user about granting permissions for your website. Note that this event is artificially
* generated based only on time intervals when accessing media devices. A heavily overloaded client device that simply takes more
* than usual to access the media device could produce a false trigger of this event.
*/
accessDialogOpened: never;
/**
* Event dispatched after the user clicks on "Allow" or "Block" in the pop-up shown by the browser to request permissions
* for the input media devices.
*
* This event can only be triggered after an {@link accessDialogOpened} event has been previously triggered.
*/
accessDialogClosed: never;
}

View File

@ -1,225 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { EventMap } from './EventMap';
import { ConnectionEvent } from '../ConnectionEvent';
import { ConnectionPropertyChangedEvent } from '../ConnectionPropertyChangedEvent';
import { ExceptionEvent } from '../ExceptionEvent';
import { NetworkQualityLevelChangedEvent } from '../NetworkQualityLevelChangedEvent';
import { PublisherSpeakingEvent } from '../PublisherSpeakingEvent';
import { RecordingEvent } from '../RecordingEvent';
import { SessionDisconnectedEvent } from '../SessionDisconnectedEvent';
import { SignalEvent } from '../SignalEvent';
import { SpeechToTextEvent } from '../SpeechToTextEvent';
import { StreamEvent } from '../StreamEvent';
import { StreamPropertyChangedEvent } from '../StreamPropertyChangedEvent';
/**
* Events dispatched by {@link Session} object. Manage event listeners with
* {@link Session.on}, {@link Session.once} and {@link Session.off} methods.
*
* Example:
*
* ```javascript
* session.on('connectionCreated', (event) => {
* console.log('Connection ' + event.connection.connectionId + ' created');
* }
*
* session.off('connectionDestroyed');
* ```
*/
export interface SessionEventMap extends EventMap {
/**
* Event dispatched when a new user has connected to the session.
*
* It is fired for both the local user and remote users.
*/
connectionCreated: ConnectionEvent;
/**
* Event dispatched when a remote user has left the session.
*
* For the local user see {@link sessionDisconnected}.
*/
connectionDestroyed: ConnectionEvent;
/**
* **This feature is part of OpenVidu
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
* and
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
* editions**
*
* Event dispatched when a property of the local {@link Connection} object changes.
*
* It is fired only for the local user.
*
* The properties that may change are {@link Connection.role} and {@link Connection.record}.
* The only way the Connection properties may change is by updating them through:
*
* - [API REST](/en/stable/reference-docs/REST-API/#patch-connection)
* - [openvidu-java-client](/en/stable/reference-docs/openvidu-java-client/#update-a-connection)
* - [openvidu-node-client](/en/stable/reference-docs/openvidu-node-client/#update-a-connection)<br><br>
*/
connectionPropertyChanged: ConnectionPropertyChangedEvent;
/**
* Event dispatched when the local user has left the session.
*
* For remote users see {@link connectionDestroyed}.
*/
sessionDisconnected: SessionDisconnectedEvent;
/**
* Event dispatched when a user has started publishing media to the session (see {@link Session.publish}).
*
* It is fired for both the local user and remote users.
*/
streamCreated: StreamEvent;
/**
* Event dispatched when a user stops publishing media to the session.
*
* It is fired for both the local user and remote users.
*/
streamDestroyed: StreamEvent;
/**
* Event dispatched when a Stream undergoes any change in any of its mutable properties
* (see {@link StreamPropertyChangedEvent.changedProperty}).
*
* It is fired for both remote streams (owned by a {@link Subscriber}) or local streams (owned by a {@link Publisher}).
*/
streamPropertyChanged: StreamPropertyChangedEvent;
/**
* Event dispatched when a user has started speaking.
*
* It is fired for both the local user and remote users.
*
* Extra information:
* - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true).
* - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}.
*/
publisherStartSpeaking: PublisherSpeakingEvent;
/**
* Event dispatched when a user has stopped speaking.
*
* It is fired for both the local user and remote users.
*
* Extra information:
* - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true).
* - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}.
*/
publisherStopSpeaking: PublisherSpeakingEvent;
/**
* @hidden
*/
[key: `signal:${string}`]: SignalEvent;
/**
* Event dispatched when a signal is received (see [Send text messages between users](/en/stable/cheatsheet/send-messages)).
*
* If the listener is added as **`signal:TYPE`**, only signals of type **`TYPE`** will trigger the event.
*/
signal: SignalEvent;
/**
* Event dispatched when the session has started being recorded.
*
* Property **`OPENVIDU_RECORDING_NOTIFICATION`** of [the OpenVidu deployment configuration](/en/stable/reference-docs/openvidu-config/)
* defines which users should receive this events (by default, only users with role `PUBLISHER` or `MODERATOR`)
*/
recordingStarted: RecordingEvent;
/**
* Event dispatched when the session has stopped being recorded.
*
* Property **`OPENVIDU_RECORDING_NOTIFICATION`** of [the OpenVidu deployment configuration](/en/stable/reference-docs/openvidu-config/)
* defines which users should receive this events (by default, only users with role `PUBLISHER` or `MODERATOR`)
*/
recordingStopped: RecordingEvent;
/**
* **This feature is part of OpenVidu
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
* and
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
* editions**
*
* Event dispatched when the session has started being broadcasted. See [Broadcast to YouTube/Twitch](/en/stable/advanced-features/broadcast/)
*/
broadcastStarted: never;
/**
* **This feature is part of OpenVidu
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
* and
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
* editions**
*
* Event dispatched when the session has stopped being broadcasted. See [Broadcast to YouTube/Twitch](/en/stable/advanced-features/broadcast/)
*/
broadcastStopped: never;
/**
* **This feature is part of OpenVidu
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
* and
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
* editions**
*
* Event dispatched when the network quality level of a {@link Connection} changes. See [network quality](/en/stable/advanced-features/network-quality/).
*/
networkQualityLevelChanged: NetworkQualityLevelChangedEvent;
/**
* **This feature is part of OpenVidu
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
* and
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
* editions**
*
* Event dispatched when a speech-to-text message has been received for certain Stream. See [Speech To Text](/en/stable/advanced-features/speech-to-text/).
*/
speechToTextMessage: SpeechToTextEvent;
/**
* Event dispatched when the local user has lost its connection to the session, and starts the automatic reconnection process.
*
* See [Reconnection events](/en/stable/advanced-features/automatic-reconnection/#reconnection-events).
*/
reconnecting: never;
/**
* Event dispatched when the local user has successfully recovered its connection to the session after losing it.
*
* If the connection was recovered but OpenVidu Server already evicted the user due to timeout, then this event will
* not be dispatched. A {@link sessionDisconnected} event with reason `networkDisconnect` will be triggered instead.
*
* See [Reconnection events](/en/stable/advanced-features/automatic-reconnection/#reconnection-events).
*/
reconnected: never;
/**
* This event acts as a global handler for asynchronous errors that may be triggered for multiple reasons and from multiple origins.
* To see the different types of exceptions go to {@link ExceptionEventName}.
*/
exception: ExceptionEvent;
}

View File

@ -1,90 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { EventMap } from './EventMap';
import { PublisherSpeakingEvent } from '../PublisherSpeakingEvent';
import { StreamManagerEvent } from '../StreamManagerEvent';
import { StreamPropertyChangedEvent } from '../StreamPropertyChangedEvent';
import { VideoElementEvent } from '../VideoElementEvent';
/**
* Events dispatched by {@link StreamManager} object. Manage event listeners with
* {@link StreamManager.on}, {@link StreamManager.once} and {@link StreamManager.off} methods.
*
* Example:
*
* ```javascript
* streamManager.on('videoElementCreated', (event) => {
* console.log('New video element created:', event.element);
* }
*
* streamManager.off('videoElementCreated');
* ```
*/
export interface StreamManagerEventMap extends EventMap {
/**
* Event dispatched when a new HTML video element has been inserted into DOM by OpenVidu Browser library. See
* [Manage video players](/en/stable/cheatsheet/manage-videos) section.
*/
videoElementCreated: VideoElementEvent;
/**
* Event dispatched when an HTML video element has been removed from DOM by OpenVidu Browser library. See
* [Manage video players](/en/stable/cheatsheet/manage-videos) section.
*/
videoElementDestroyed: VideoElementEvent;
/**
* Event dispatched when the media stream starts playing (one of its videos has media and has begun to play).
* This event will be dispatched when these 3 conditions are met:
* 1. The StreamManager has no video associated in the DOM.
* 2. It is associated to one video.
* 3. That video starts playing. Internally the expected Web API event is [HTMLMediaElement.canplay](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event).
*/
streamPlaying: StreamManagerEvent;
/**
* Event dispatched when the volume of the media stream's audio track changes. Only applies if {@link Stream.hasAudio} is `true`.
* The frequency this event is fired with is defined by property `interval` of
* {@link OpenViduAdvancedConfiguration.publisherSpeakingEventsOptions} (default 100ms)
*/
streamAudioVolumeChange: StreamManagerEvent;
/**
* Event dispatched when a Stream undergoes any change in any of its mutable properties
* (see {@link StreamPropertyChangedEvent.changedProperty}).
*/
streamPropertyChanged: StreamPropertyChangedEvent;
/**
* Event dispatched when the user owning the stream has started speaking.
*
* Extra information:
* - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true).
* - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}.
*/
publisherStartSpeaking: PublisherSpeakingEvent;
/**
* Event dispatched when the user owning the stream has stopped speaking.
*
* Extra information:
* - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true).
* - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}.
*/
publisherStopSpeaking: PublisherSpeakingEvent;
}

View File

@ -1,136 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Session } from '../../OpenVidu/Session';
import { Stream } from '../../OpenVidu/Stream';
import { Subscriber } from '../../OpenVidu/Subscriber';
import { Event } from './Event';
/**
* Defines property {@link ExceptionEvent.name}
*/
export enum ExceptionEventName {
/**
* There was an unexpected error on the server-side processing an ICE candidate generated and sent by the client-side.
*
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Session} object.
*/
ICE_CANDIDATE_ERROR = 'ICE_CANDIDATE_ERROR',
/**
* The [ICE connection state](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState)
* of an [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) reached `failed` status.
*
* This is a terminal error that won't have any kind of possible recovery. If the client is still connected to OpenVidu Server,
* then an automatic reconnection process of the media stream is immediately performed. If the ICE connection has broken due to
* a total network drop, then no automatic reconnection process will be possible.
*
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Stream} object.
*/
ICE_CONNECTION_FAILED = 'ICE_CONNECTION_FAILED',
/**
* The [ICE connection state](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState)
* of an [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) reached `disconnected` status.
*
* This is not a terminal error, and it is possible for the ICE connection to be reconnected. If the client is still connected to
* OpenVidu Server and after certain timeout the ICE connection has not reached a success or terminal status, then an automatic
* reconnection process of the media stream is performed. If the ICE connection has broken due to a total network drop, then no
* automatic reconnection process will be possible.
*
* You can customize the timeout for the reconnection attempt with property {@link OpenViduAdvancedConfiguration.iceConnectionDisconnectedExceptionTimeout},
* which by default is 4000 milliseconds.
*
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Stream} object.
*/
ICE_CONNECTION_DISCONNECTED = 'ICE_CONNECTION_DISCONNECTED',
/**
* A {@link Subscriber} object has not fired event `streamPlaying` after certain timeout. `streamPlaying` event belongs to {@link StreamManagerEvent}
* category. It wraps Web API native event [canplay](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event).
*
* OpenVidu Browser can take care of the video players (see [here](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)),
* or you can take care of video players on your own (see [here](/en/stable/cheatsheet/manage-videos/#you-take-care-of-the-video-players)).
* Either way, whenever a {@link Subscriber} object is commanded to attach its {@link Stream} to a video element, it is supposed to fire `streamPlaying`
* event shortly after. If it does not, then we can safely assume that something wrong has happened while playing the remote video and the
* application may be notified through this specific ExceptionEvent.
*
* The timeout can be configured with property {@link OpenViduAdvancedConfiguration.noStreamPlayingEventExceptionTimeout}. By default it is 4000 milliseconds.
*
* This is just an informative exception. It only means that a remote Stream that is supposed to be playing by a video player has not done so
* in a reasonable time. But the lack of the event can be caused by multiple reasons. If a Subscriber is not playing its Stream, the origin
* of the problem could be located at the Publisher side. Or may be caused by a transient network problem. But it also could be a problem with
* autoplay permissions. Bottom line, the cause can be very varied, and depending on the application the lack of the event could even be expected.
*
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Subscriber} object.
*/
NO_STREAM_PLAYING_EVENT = 'NO_STREAM_PLAYING_EVENT',
/**
* There has been a server-side disconnection of the Speech To Text module. From the moment this exception is fired to the moment method
* {@link Session.subscribeToSpeechToText} is called again, the transcription of the audio stream will not be available and no {@link SpeechToTextEvent}
* will be fired.
*
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Session} object.
*/
SPEECH_TO_TEXT_DISCONNECTED = 'SPEECH_TO_TEXT_DISCONNECTED',
}
/**
* Triggered by {@link SessionEventMap.exception}
*/
export class ExceptionEvent extends Event {
/**
* Name of the exception
*/
name: ExceptionEventName;
/**
* Object affected by the exception. Depending on the {@link ExceptionEvent.name} property:
* - {@link Session}: `ICE_CANDIDATE_ERROR`
* - {@link Stream}: `ICE_CONNECTION_FAILED`, `ICE_CONNECTION_DISCONNECTED`
* - {@link Subscriber}: `NO_STREAM_PLAYING_EVENT`
*/
origin: Session | Stream | Subscriber;
/**
* Informative description of the exception
*/
message: string;
/**
* Any extra information associated to the exception
*/
data?: any;
/**
* @hidden
*/
constructor(session: Session, name: ExceptionEventName, origin: Session | Stream | Subscriber, message: string, data?: any) {
super(false, session, 'exception');
this.name = name;
this.origin = origin;
this.message = message;
this.data = data;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() { }
}

View File

@ -1,43 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Filter } from '../../OpenVidu/Filter';
/**
* Defines every event dispatched by audio/video stream filters. You can subscribe to filter events by calling {@link Filter.addEventListener}
*/
export class FilterEvent extends Event {
/**
* Data of the event
*/
data: Object;
/**
* @hidden
*/
constructor(target: Filter, eventType: string, data: Object) {
super(false, target, eventType);
this.data = data;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() {}
}

View File

@ -1,56 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Session } from '../../OpenVidu/Session';
import { Connection } from '../../OpenVidu/Connection';
/**
* Triggered by {@link SessionEventMap.networkQualityLevelChanged}
*/
export class NetworkQualityLevelChangedEvent extends Event {
/**
* New value of the network quality level
*/
newValue: number;
/**
* Old value of the network quality level
*/
oldValue: number;
/**
* Connection for whom the network quality level changed
*/
connection: Connection;
/**
* @hidden
*/
constructor(target: Session, newValue: number, oldValue: number, connection: Connection) {
super(false, target, 'networkQualityLevelChanged');
this.newValue = newValue;
this.oldValue = oldValue;
this.connection = connection;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() {}
}

View File

@ -1,54 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Connection } from '../../OpenVidu/Connection';
import { Session } from '../../OpenVidu/Session';
import { StreamManager } from '../../OpenVidu/StreamManager';
/**
* Triggered by:
* - `publisherStartSpeaking` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#publisherStartSpeaking) and [StreamManager](/en/stable/api/openvidu-browser/interfaces/StreamManagerEventMap.html#publisherStartSpeaking) objects)
* - `publisherStopSpeaking` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#publisherStopSpeaking) and [StreamManager](/en/stable/api/openvidu-browser/interfaces/StreamManagerEventMap.html#publisherStopSpeaking) objects)
*/
export class PublisherSpeakingEvent extends Event {
/**
* The client that started or stopped speaking
*/
connection: Connection;
/**
* The streamId of the Stream affected by the speaking event
*/
streamId: string;
/**
* @hidden
*/
constructor(target: Session | StreamManager, type: string, connection: Connection, streamId: string) {
super(false, target, type);
this.type = type;
this.connection = connection;
this.streamId = streamId;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() {}
}

View File

@ -1,71 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Session } from '../../OpenVidu/Session';
import { RecordingEventReason } from './Types/Types';
/**
* Triggered by:
* - {@link SessionEventMap.recordingStarted}
* - {@link SessionEventMap.recordingStopped}
*/
export class RecordingEvent extends Event {
/**
* The recording ID generated in openvidu-server
*/
id: string;
/**
* The recording name you supplied to openvidu-server. For example, to name your recording file MY_RECORDING:
* - With **API REST**: POST to `/api/recordings/start` passing JSON body `{"session":"sessionId","name":"MY_RECORDING"}`
* - With **openvidu-java-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, new RecordingProperties.Builder().name("MY_RECORDING").build())`
* - With **openvidu-node-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, {name: "MY_RECORDING"})`
*
* If no name is supplied, this property will be undefined and the recorded file will be named after property {@link id}
*/
name?: string;
/**
* For 'recordingStopped' event:
* - "recordingStoppedByServer": the recording has been gracefully stopped by the application
* - "sessionClosedByServer": the Session has been closed by the application
* - "automaticStop": see [Automatic stop of recordings](/en/stable/advanced-features/recording/#automatic-stop-of-recordings)
* - "nodeCrashed": a node has crashed in the server side
*
* For 'recordingStarted' empty string
*/
reason?: RecordingEventReason;
/**
* @hidden
*/
constructor(target: Session, type: string, id: string, name: string, reason?: RecordingEventReason) {
super(false, target, type);
this.id = id;
if (name !== id) {
this.name = name;
}
this.reason = reason;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() { }
}

View File

@ -1,80 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Session } from '../../OpenVidu/Session';
import { OpenViduLogger } from '../Logger/OpenViduLogger';
import { ConnectionEventReason } from './Types/Types';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* Triggered by {@link SessionEventMap.sessionDisconnected}
*/
export class SessionDisconnectedEvent extends Event {
/**
* - "disconnect": you have called `Session.disconnect()`
* - "forceDisconnectByUser": you have been evicted from the Session by other user calling `Session.forceDisconnect()`
* - "forceDisconnectByServer": you have been evicted from the Session by the application
* - "sessionClosedByServer": the Session has been closed by the application
* - "networkDisconnect": your network connection has dropped. Before a SessionDisconnectedEvent with this reason is triggered,
* Session object will always have previously dispatched a `reconnecting` event. If the reconnection process succeeds,
* Session object will dispatch a `reconnected` event. If it fails, Session object will dispatch a SessionDisconnectedEvent
* with reason "networkDisconnect"
* - "nodeCrashed": a node has crashed in the server side. You can use this reason to ask your application's backend to reconnect
* to a new session to replace the crashed one
*/
reason: ConnectionEventReason;
/**
* @hidden
*/
constructor(target: Session, reason: ConnectionEventReason) {
super(true, target, 'sessionDisconnected');
this.reason = reason;
}
/**
* @hidden
*/
callDefaultBehavior() {
logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Session'");
const session = <Session>this.target;
// Dispose and delete all remote Connections
session.remoteConnections.forEach((remoteConnection) => {
const connectionId = remoteConnection.connectionId;
if (!!session.remoteConnections.get(connectionId)?.stream) {
session.remoteConnections.get(connectionId)?.stream!.disposeWebRtcPeer();
session.remoteConnections.get(connectionId)?.stream!.disposeMediaStream();
if (session.remoteConnections.get(connectionId)?.stream!.streamManager) {
session.remoteConnections.get(connectionId)?.stream!.streamManager.removeAllVideos();
}
const streamId = session.remoteConnections.get(connectionId)?.stream?.streamId;
if (!!streamId) {
session.remoteStreamsCreated.delete(streamId);
}
session.remoteConnections.get(connectionId)?.dispose();
}
session.remoteConnections.delete(connectionId);
});
}
}

View File

@ -1,64 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Connection } from '../../OpenVidu/Connection';
import { Session } from '../../OpenVidu/Session';
/**
* Triggered by {@link SessionEventMap.signal}
*/
export class SignalEvent extends Event {
/**
* The type of signal. It is string `"signal"` for those signals sent with no {@link SignalOptions.type} property, and `"signal:type"` if was sent with a
* valid {@link SignalOptions.type} property.
*
* The client must be specifically subscribed to `Session.on('signal:type', function(signalEvent) {...})` to trigger that type of signal.
*
* Subscribing to `Session.on('signal', function(signalEvent) {...})` will trigger all signals, no matter their type.
*/
type: string;
/**
* The message of the signal (can be empty)
*/
data?: string;
/**
* The client that sent the signal. This property is undefined if the signal
* was directly generated by the application server (not by other client)
*/
from?: Connection;
/**
* @hidden
*/
constructor(target: Session, type?: string, data?: string, from?: Connection) {
super(false, target, 'signal');
if (!!type) {
this.type = 'signal:' + type;
}
this.data = data;
this.from = from;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() {}
}

View File

@ -1,72 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Connection } from '../../OpenVidu/Connection';
import { Session } from '../../OpenVidu/Session';
import { SpeechToTextEventReason } from './Types/Types';
/**
* Triggered by {@link SessionEventMap.speechToTextMessage}
*/
export class SpeechToTextEvent extends Event {
/**
* The {@link Connection} owning the Stream that produced the speech-to-text event.
* In other words, this is the participant that spoke and produced this transcription event.
*/
connection: Connection;
/**
* The text of the event. This is the transcription for this specific piece of audio stream
*/
text: string;
/**
* All speech-to-text events are generated
*/
reason: SpeechToTextEventReason;
/**
* The original event from the speech to text engine. This can vary depending on the engine
*/
raw: string;
/**
* [BCP-47](https://tools.ietf.org/html/bcp47) language tag (like "en-US" or "es-ES") of the recognized text. This will be the same as the language provided
* in method {@link Session.subscribeToSpeechToText} method
*/
lang: string;
/**
* @hidden
*/
constructor(target: Session, connection: Connection, text: string, reason: SpeechToTextEventReason, raw: string, lang: string) {
super(false, target, 'speechToTextMessage');
this.connection = connection;
this.text = text;
this.reason = reason;
this.raw = raw;
this.lang = lang;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() { }
}

View File

@ -1,113 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Publisher } from '../../OpenVidu/Publisher';
import { Session } from '../../OpenVidu/Session';
import { Stream } from '../../OpenVidu/Stream';
import { OpenViduLogger } from '../Logger/OpenViduLogger';
import { StreamEventReason } from './Types/Types';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* Triggered by:
* - `streamCreated` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#streamCreated) and [Publisher](/en/stable/api/openvidu-browser/interfaces/PublisherEventMap.html#streamCreated) objects)
* - `streamDestroyed` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#streamDestroyed) and [Publisher](/en/stable/api/openvidu-browser/interfaces/PublisherEventMap.html#streamDestroyed) objects)
*/
export class StreamEvent extends Event {
/**
* Stream object that was created or destroyed
*/
stream: Stream;
/**
* For 'streamDestroyed' event:
* - "unpublish": method `Session.unpublish()` has been called
* - "disconnect": method `Session.disconnect()` has been called
* - "forceUnpublishByUser": some user has called `Session.forceUnpublish()` over the Stream
* - "forceDisconnectByUser": some user has called `Session.forceDisconnect()` over the Stream
* - "forceUnpublishByServer": the user's stream has been unpublished from the Session by the application
* - "forceDisconnectByServer": the user has been evicted from the Session by the application
* - "sessionClosedByServer": the Session has been closed by the application
* - "networkDisconnect": the user's network connection has dropped
* - "nodeCrashed": a node has crashed in the server side
*
* For 'streamCreated' empty string
*/
reason: StreamEventReason;
/**
* @hidden
*/
constructor(cancelable: boolean, target: Session | Publisher, type: string, stream: Stream, reason: StreamEventReason) {
super(cancelable, target, type);
this.stream = stream;
this.reason = reason;
}
/**
* @hidden
*/
callDefaultBehavior() {
if (this.type === 'streamDestroyed') {
if (this.target instanceof Session) {
// Remote Stream
logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Session'");
this.stream.disposeWebRtcPeer();
} else if (this.target instanceof Publisher) {
// Local Stream
logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Publisher'");
clearInterval((<Publisher>this.target).screenShareResizeInterval);
this.stream.isLocalStreamReadyToPublish = false;
// Delete Publisher object from OpenVidu publishers array
const openviduPublishers = (<Publisher>this.target).openvidu.publishers;
for (let i = 0; i < openviduPublishers.length; i++) {
if (openviduPublishers[i] === <Publisher>this.target) {
openviduPublishers.splice(i, 1);
break;
}
}
}
// Dispose the MediaStream local object
this.stream.disposeMediaStream();
// Remove from DOM all video elements associated to this Stream, if there's a StreamManager defined
// (method Session.subscribe must have been called)
if (this.stream.streamManager) this.stream.streamManager.removeAllVideos();
// Delete stream from Session.remoteStreamsCreated map
this.stream.session.remoteStreamsCreated.delete(this.stream.streamId);
// Delete StreamOptionsServer from remote Connection
const remoteConnection = this.stream.session.remoteConnections.get(this.stream.connection.connectionId);
if (!!remoteConnection && !!remoteConnection.remoteOptions) {
const streamOptionsServer = remoteConnection.remoteOptions.streams;
for (let i = streamOptionsServer.length - 1; i >= 0; --i) {
if (streamOptionsServer[i].id === this.stream.streamId) {
streamOptionsServer.splice(i, 1);
}
}
}
}
}
}

View File

@ -1,49 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { StreamManager } from '../../OpenVidu/StreamManager';
/**
* Triggered by:
* - {@link StreamManagerEventMap.streamPlaying}
* - {@link StreamManagerEventMap.streamAudioVolumeChange}
*/
export class StreamManagerEvent extends Event {
/**
* For `streamAudioVolumeChange` event:
* - `{newValue: number, oldValue: number}`: new and old audio volume values. These values are between -100 (silence) and 0 (loudest possible volume).
* They are not exact and depend on how the browser is managing the audio track, but -100 and 0 can be taken as limit values.
*
* For `streamPlaying` event undefined
*/
value: Object | undefined;
/**
* @hidden
*/
constructor(target: StreamManager, type: string, value: Object | undefined) {
super(false, target, type);
this.value = value;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() {}
}

View File

@ -1,83 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { Session } from '../../OpenVidu/Session';
import { Stream } from '../../OpenVidu/Stream';
import { StreamManager } from '../../OpenVidu/StreamManager';
import { StreamPropertyChangedEventReason, ChangedPropertyType } from './Types/Types';
/**
* Triggered by `streamPropertyChanged` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#streamPropertyChanged) and [StreamManager](/en/stable/api/openvidu-browser/interfaces/StreamManagerEventMap.html#streamPropertyChanged) objects)
*/
export class StreamPropertyChangedEvent extends Event {
/**
* The Stream whose property has changed. You can always identify the user publishing the changed stream by consulting property {@link Stream.connection}
*/
stream: Stream;
/**
* The property of the stream that changed. This value is either `"videoActive"`, `"audioActive"`, `"videoTrack"`, `"audioTrack"`, `"videoDimensions"` or `"filter"`
*/
changedProperty: ChangedPropertyType;
/**
* Cause of the change on the stream's property:
* - For `videoActive`: `"publishVideo"`
* - For `audioActive`: `"publishAudio"`
* - For `videoTrack`: `"trackReplaced"`
* - For `audioTrack`: `"trackReplaced"`
* - For `videoDimensions`: `"deviceRotated"`, `"screenResized"` or `"trackReplaced"`
* - For `filter`: `"applyFilter"`, `"execFilterMethod"` or `"removeFilter"`
*/
reason: StreamPropertyChangedEventReason;
/**
* New value of the property (after change, current value)
*/
newValue: Object;
/**
* Previous value of the property (before change)
*/
oldValue: Object;
/**
* @hidden
*/
constructor(
target: Session | StreamManager,
stream: Stream,
changedProperty: ChangedPropertyType,
newValue: Object,
oldValue: Object,
reason: StreamPropertyChangedEventReason
) {
super(false, target, 'streamPropertyChanged');
this.stream = stream;
this.changedProperty = changedProperty;
this.newValue = newValue;
this.oldValue = oldValue;
this.reason = reason;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() { }
}

View File

@ -1,42 +0,0 @@
export type ChangedPropertyType =
'videoActive' |
'audioActive' |
'videoTrack' |
'audioTrack' |
'videoDimensions' |
'filter';
export type StreamPropertyChangedEventReason =
'publishVideo' |
'publishAudio' |
'trackReplaced' |
'deviceRotated' |
'screenResized' |
'applyFilter' |
'execFilterMethod' |
'removeFilter';
export type ConnectionEventReason =
'disconnect' |
'forceDisconnectByUser' |
'forceDisconnectByServer' |
'sessionClosedByServer' |
'networkDisconnect' |
'nodeCrashed' |
'';
export type StreamEventReason =
ConnectionEventReason |
'unpublish' |
'forceUnpublishByUser' |
'forceUnpublishByServer';
export type RecordingEventReason =
'recordingStoppedByServer' |
'sessionClosedByServer' |
'automaticStop' |
'nodeCrashed';
export type SpeechToTextEventReason =
'recognizing' |
'recognized';

View File

@ -1,45 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Event } from './Event';
import { StreamManager } from '../../OpenVidu/StreamManager';
/**
* Triggered by:
* - {@link StreamManagerEventMap.videoElementCreated}
* - {@link StreamManagerEventMap.videoElementDestroyed}
*/
export class VideoElementEvent extends Event {
/**
* Video element that was created or destroyed
*/
element: HTMLVideoElement;
/**
* @hidden
*/
constructor(element: HTMLVideoElement, target: StreamManager, type: string) {
super(false, target, type);
this.element = element;
}
/**
* @hidden
*/
// tslint:disable-next-line:no-empty
callDefaultBehavior() {}
}

View File

@ -1,22 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
export interface CustomMediaStreamConstraints {
constraints: MediaStreamConstraints;
audioTrack: MediaStreamTrack | undefined;
videoTrack: MediaStreamTrack | undefined;
}

View File

@ -1,21 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
export interface IceServerProperties {
url: string;
username?: string;
credential?: string;
}

View File

@ -1,34 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Connection } from '../../../OpenVidu/Connection';
import { Filter } from '../../../OpenVidu/Filter';
import { TypeOfVideo } from '../../Enums/TypeOfVideo';
export interface InboundStreamOptions {
id: string;
createdAt: number;
connection: Connection;
hasAudio: boolean;
hasVideo: boolean;
audioActive: boolean;
videoActive: boolean;
typeOfVideo: TypeOfVideo;
frameRate: number;
videoDimensions: { width: number; height: number };
filter?: Filter;
}

View File

@ -1,42 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { RemoteConnectionOptions } from './RemoteConnectionOptions';
import { IceServerProperties } from './IceServerProperties';
export interface LocalConnectionOptions {
id: string;
finalUserId: string;
createdAt: number;
metadata: string;
value: RemoteConnectionOptions[];
session: string; // OpenVidu Session identifier
sessionId: string; // JSON-RPC session identifier
role: string;
record: boolean;
coturnIp: string;
coturnPort: number;
turnUsername: string;
turnCredential: string;
version: string;
mediaServer: string;
videoSimulcast: boolean;
life: number;
customIceServers?: IceServerProperties[];
recordingId?: string; // Defined if the session is being recorded and the client must be notified
recordingName?: string; // Defined if the session is being recorded and the client must be notified
}

View File

@ -1,23 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { PublisherProperties } from '../Public/PublisherProperties';
export interface OutboundStreamOptions {
publisherProperties: PublisherProperties;
mediaConstraints: MediaStreamConstraints;
}

View File

@ -1,25 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { StreamOptionsServer } from './StreamOptionsServer';
export interface RemoteConnectionOptions {
id: string;
createdAt: number;
metadata: string;
streams: StreamOptionsServer[];
}

View File

@ -1,22 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
export interface SessionOptions {
sessionId: string;
participantId: string;
metadata: string;
}

View File

@ -1,24 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Connection } from '../../../OpenVidu/Connection';
export interface SignalOptions {
type?: string;
to?: Connection[];
data?: string;
}

View File

@ -1,32 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Filter } from '../../../OpenVidu/Filter';
import { TypeOfVideo } from '../../Enums/TypeOfVideo';
export interface StreamOptionsServer {
id: string;
createdAt: number;
hasAudio: boolean;
hasVideo: boolean;
audioActive: boolean;
videoActive: boolean;
typeOfVideo: TypeOfVideo;
frameRate: number;
videoDimensions: string;
filter: Filter;
}

View File

@ -1,41 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/**
* See {@link Session.capabilities}
*/
export interface Capabilities {
/**
* `true` if the client can call {@link Session.forceDisconnect}, `false` if not
*/
forceDisconnect: boolean;
/**
* `true` if the client can call {@link Session.forceUnpublish}, `false` if not
*/
forceUnpublish: boolean;
/**
* `true` if the client can call {@link Session.publish}, `false` if not
*/
publish: boolean;
/**
* `true` if the client can call {@link Session.subscribe}, `false` if not (true for every user for now)
*/
subscribe: boolean;
}

View File

@ -1,36 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/**
* See {@link OpenVidu.getDevices}
*/
export interface Device {
/**
* The kind of device
*/
kind: 'videoinput' | 'audioinput';
/**
* Unique ID for the device. Use it on `audioSource` or `videoSource` properties of {@link PublisherProperties}
*/
deviceId: string;
/**
* Description of the device. An empty string if the user hasn't granted permissions to the site to access the device
*/
label: string;
}

View File

@ -1,79 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/**
* See {@link OpenVidu.setAdvancedConfiguration}
*/
export interface OpenViduAdvancedConfiguration {
/**
* Array of [RTCIceServer](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer) to be used by OpenVidu Browser. By default OpenVidu will generate the required credentials to use the COTURN server hosted along OpenVidu Server.
* You can also set this property to string 'freeice' to force the use of free STUN servers instead (got thanks to [freeice](https://github.com/DamonOehlman/freeice) library).
*
* > **WARNING**: this value has priority over the standard `iceServers` property of {@link rtcConfiguration}. It will override any value in `OpenViduAdvancedConfiguration.rtcConfiguration.iceServers`
*/
iceServers?: RTCIceServer[] | string;
/**
* Custom configuration for all [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#parameters) objects. This object will be passed as is to all RTCPeerConnection constructor (all Publishers and Subscribers).
*/
rtcConfiguration?: RTCConfiguration;
/**
* URL to a custom screen share extension for Chrome (always based on ours: [openvidu-screen-sharing-chrome-extension](https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension)) to be used instead of the default one.
* Must be something like this: `https://chrome.google.com/webstore/detail/YOUR_WEBSTORE_EXTENSION_NAME/YOUR_EXTENSION_ID`
*/
screenShareChromeExtension?: string;
/**
* Custom configuration for the {@link PublisherSpeakingEvent} feature and the [StreamManagerEvent.streamAudioVolumeChange](/en/stable/api/openvidu-browser/classes/StreamManagerEvent.html) feature. It is an object which includes the following optional properties:
* - `interval`: (number) how frequently the analyser polls the audio stream to check if speaking has started/stopped or audio volume has changed. Default **100** (ms)
* - `threshold`: (number) the volume at which _publisherStartSpeaking_ and _publisherStopSpeaking_ events will be fired. Default **-50** (dB)
*
* This sets the global default configuration that will affect all streams, but you can later customize these values for each specific stream by calling {@link StreamManager.updatePublisherSpeakingEventsOptions}
*/
publisherSpeakingEventsOptions?: {
interval?: number;
threshold?: number;
};
/**
* Determines the automatic reconnection process policy. Whenever the client's network drops, OpenVidu Browser starts a reconnection process with OpenVidu Server. After network is recovered, OpenVidu Browser automatically
* inspects all of its media streams to see their status. For any of them that are broken, it asks OpenVidu Server for a forced and silent reconnection.
*
* This policy is technically enough to recover any broken media connection after a network drop, but in practice it has been proven that OpenVidu Browser may think a media connection has properly recovered when in fact it has not.
* This is not a common case, but it may occur. This property allows **forcing OpenVidu Browser to reconnect all of its outgoing and incoming media streams** after a network drop regardless of their supposed status.
*
* Default to `false`.
*/
forceMediaReconnectionAfterNetworkDrop?: boolean;
/**
* The milliseconds that must elapse after triggering {@link ExceptionEvent} of name [`ICE_CONNECTION_DISCONNECTED`](/en/stable/api/openvidu-browser/enums/ExceptionEventName.html#ICE_CONNECTION_DISCONNECTED) to perform an automatic reconnection process of the affected media stream.
* This automatic reconnection process can only take place if the client still has network connection to OpenVidu Server. If the ICE connection has broken because of a total network drop,
* then no reconnection process will be possible at all.
*
* Default to `4000`.
*/
iceConnectionDisconnectedExceptionTimeout?: number;
/**
* The milliseconds that must elapse for the {@link ExceptionEvent} of name [`NO_STREAM_PLAYING_EVENT`](/en/stable/api/openvidu-browser/enums/ExceptionEventName.html#NO_STREAM_PLAYING_EVENT) to be fired.
*
* Default to `4000`.
*/
noStreamPlayingEventExceptionTimeout?: number;
}

View File

@ -1,101 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Filter } from '../../../OpenVidu/Filter';
import { VideoInsertMode } from '../../Enums/VideoInsertMode';
/**
* See {@link OpenVidu.initPublisher}
*/
export interface PublisherProperties {
/**
* Which device should provide the audio source. Can be:
* - Property `deviceId` of a {@link Device}
* - `"screen"` to share the screen audio when {@link videoSource} is set to `"screen"`. If {@link videoSource} is not set to `"screen"` this will result in no audio source and a video-only publisher.
* - A MediaStreamTrack obtained from a MediaStream object with {@link OpenVidu.getUserMedia}
* - `false` or null to have a video-only publisher
* @default _Default microphone_
*/
audioSource?: string | MediaStreamTrack | boolean;
/**
* Desired framerate of the video in frames per second.
* Limiting the framerate has always effect on browsers Chrome and Opera. Firefox requires that the input device explicitly supports the desired framerate.
* @default undefined
*/
frameRate?: number;
/**
* How the video element of the publisher should be inserted in the DOM
* @default VideoInsertMode.APPEND
*/
insertMode?: VideoInsertMode | string;
/**
* Whether the publisher's video will be mirrored in the page or not. Only affects the local view of the publisher in the browser (remote streams will not be mirrored). If `videoSource` is set to "screen" this property is fixed to `false`
* @default true
*/
mirror?: boolean;
/**
* Whether to initially publish to the session with the audio unmuted or muted. Only makes sense if property `audioSource` is NOT set to *false* or *null*. You can change the audio state later during the session with {@link Publisher.publishAudio}
* @default true
*/
publishAudio?: boolean;
/**
* Whether to initially publish to the session with the video enabled or disabled. Only makes sense if property `videoSource` is NOT set to *false* or *null*. You can change the video state later during the session with {@link Publisher.publishVideo}
* @default true
*/
publishVideo?: boolean;
/**
* Resolution of the video: `"320x240"`, `"640x480"`, `"1280x720"` (low, medium and high quality respectively)
* @default "640x480"
*/
resolution?: string;
/**
* Which device should provide the video source. Can be:
* - Property `deviceId` of a {@link Device}
* - `"screen"` to screen-share. We provide a default screen-shraring extension for Chrome that can run in any domain, but you can customize it so it has your own icon, your own name, etc. Visit this
* [GitHub repository](https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension/) to learn how. Once you have uploaded your own extension to Chrome Web Store,
* simply call `OpenVidu.setAdvancedConfiguration({screenShareChromeExtension : "https://chrome.google.com/webstore/detail/YOUR_EXTENSION_NAME/YOUR_EXTENSION_ID"})` before calling `OpenVidu.initPublisher(targetElement, {videoSource: "screen"})`.
* For Firefox (<66) `"screen"` string will ask for permissions to share the entire screen. To ask for a specific window or application, use `"window"` string instead (this only applies to Firefox).
* - A MediaStreamTrack obtained from a MediaStream object with {@link OpenVidu.getUserMedia}
* - `false` or null to have an audio-only publisher
* @default _Default camera_
*/
videoSource?: string | MediaStreamTrack | boolean;
/**
* Use Simulcast video on WebRTC Publishers.
* Senders will encode duplicate video streams with different qualities,
* so the media server is able to select the most appropriate quality stream
* for each Subscriber.
* This setting is honored only if OpenVidu Server was configured to use the
* mediasoup media server. Otherwise, Simulcast will be disabled.
*/
videoSimulcast?: boolean;
/**
* **WARNING**: experimental option. This property may change in the near future
*
* Define a filter to apply in the Publisher's stream
*/
filter?: Filter;
}

View File

@ -1,40 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Connection } from '../../../OpenVidu/Connection';
/**
* See {@link Session.signal}
*/
export interface SignalOptions {
/**
* The actual message of the signal.
*/
data?: string;
/**
* The participants to whom to send the signal. They will only receive it if they are subscribed to
* event `Session.on('signal')`. If empty or undefined, the signal will be send to all participants.
*/
to?: Connection[];
/**
* The type of the signal. Participants subscribed to event `Session.on('signal:type')` will
* receive it. Participants subscribed to `Session.on('signal')` will receive all signals.
*/
type?: string;
}

View File

@ -1,57 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { VideoInsertMode } from '../../Enums/VideoInsertMode';
export interface StreamManagerVideo {
/**
* DOM video element displaying the StreamManager's stream
*/
video: HTMLVideoElement;
/**
* `id` attribute of the DOM video element displaying the StreamManager's stream
*/
id: string;
/**
* The DOM HTMLElement assigned as target element when creating a video for the StreamManager. This property is defined when:
* - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing a valid `targetElement` parameter.
* - {@link StreamManager.createVideoElement} has been called.
*
* This property is undefined when:
* - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing *null* or *undefined* as `targetElement` parameter.
* - {@link StreamManager.addVideoElement} has been called.
*/
targetElement?: HTMLElement;
/**
* How the DOM video element should be inserted with respect to `targetElement`. This property is defined when:
* - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing a valid `targetElement` parameter.
* - {@link StreamManager.createVideoElement} has been called.
*
* This property is undefined when:
* - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing *null* or *undefined* as `targetElement` parameter.
* - {@link StreamManager.addVideoElement} has been called.
*/
insertMode?: VideoInsertMode;
/**
* @hidden
*/
canplayListenerAdded: boolean;
}

View File

@ -1,41 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { VideoInsertMode } from '../../Enums/VideoInsertMode';
/**
* See {@link Session.subscribe}
*/
export interface SubscriberProperties {
/**
* How the video element of the subscriber should be inserted in the DOM
* @default VideoInsertMode.APPEND
*/
insertMode?: VideoInsertMode | string;
/**
* Whether to initially subscribe to the audio track of the stream or not. You can change the audio state later with {@link Subscriber.subscribeToAudio}
* @default true
*/
subscribeToAudio?: boolean;
/**
* Whether to initially subscribe to the video track of the stream or not. You can change the video state later with {@link Subscriber.subscribeToVideo}
* @default true
*/
subscribeToVideo?: boolean;
}

View File

@ -1,52 +0,0 @@
function Mapper() {
var sources = {};
this.forEach = function (callback) {
for (var key in sources) {
var source = sources[key];
for (var key2 in source) callback(source[key2]);
}
};
this.get = function (id, source) {
var ids = sources[source];
if (ids == undefined) return undefined;
return ids[id];
};
this.remove = function (id, source) {
var ids = sources[source];
if (ids == undefined) return;
delete ids[id];
// Check it's empty
for (var i in ids) {
return false;
}
delete sources[source];
};
this.set = function (value, id, source) {
if (value == undefined) return this.remove(id, source);
var ids = sources[source];
if (ids == undefined) sources[source] = ids = {};
ids[id] = value;
};
}
Mapper.prototype.pop = function (id, source) {
var value = this.get(id, source);
if (value == undefined) return undefined;
this.remove(id, source);
return value;
};
module.exports = Mapper;

View File

@ -1,20 +0,0 @@
/*
* (C) Copyright 2014 Kurento (http://kurento.org/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var JsonRpcClient = require('./jsonrpcclient');
exports.JsonRpcClient = JsonRpcClient;

View File

@ -1,280 +0,0 @@
/*
* (C) Copyright 2014 Kurento (http://kurento.org/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var RpcBuilder = require('../');
var WebSocketWithReconnection = require('./transports/webSocketWithReconnection');
var OpenViduLogger = require('../../../Logger/OpenViduLogger').OpenViduLogger;
Date.now =
Date.now ||
function () {
return +new Date();
};
var PING_INTERVAL = 5000;
var RECONNECTING = 'RECONNECTING';
var CONNECTED = 'CONNECTED';
var DISCONNECTED = 'DISCONNECTED';
var Logger = OpenViduLogger.getInstance();
/**
*
* heartbeat: interval in ms for each heartbeat message,
* <pre>
* ws : {
* uri : URI to conntect to,
* onconnected : callback method to invoke when connection is successful,
* ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached),
* onreconnecting : callback method to invoke when the client is reconnecting,
* onreconnected : callback method to invoke when the client successfully reconnects,
* onerror : callback method to invoke when there is an error
* },
* rpc : {
* requestTimeout : timeout for a request,
* sessionStatusChanged: callback method for changes in session status,
* mediaRenegotiation: mediaRenegotiation
* }
* </pre>
*/
function JsonRpcClient(configuration) {
var self = this;
var wsConfig = configuration.ws;
var notReconnectIfNumLessThan = -1;
var pingNextNum = 0;
var enabledPings = true;
var pingPongStarted = false;
var pingInterval;
var status = DISCONNECTED;
var onreconnecting = wsConfig.onreconnecting;
var onreconnected = wsConfig.onreconnected;
var onconnected = wsConfig.onconnected;
var onerror = wsConfig.onerror;
configuration.rpc.pull = function (params, request) {
request.reply(null, 'push');
};
wsConfig.onreconnecting = function () {
Logger.debug('--------- ONRECONNECTING -----------');
if (status === RECONNECTING) {
Logger.error('Websocket already in RECONNECTING state when receiving a new ONRECONNECTING message. Ignoring it');
return;
}
stopPing();
status = RECONNECTING;
if (onreconnecting) {
onreconnecting();
}
};
wsConfig.onreconnected = function () {
Logger.debug('--------- ONRECONNECTED -----------');
if (status === CONNECTED) {
Logger.error('Websocket already in CONNECTED state when receiving a new ONRECONNECTED message. Ignoring it');
return;
}
status = CONNECTED;
updateNotReconnectIfLessThan();
if (onreconnected) {
onreconnected();
}
};
wsConfig.onconnected = function () {
Logger.debug('--------- ONCONNECTED -----------');
if (status === CONNECTED) {
Logger.error('Websocket already in CONNECTED state when receiving a new ONCONNECTED message. Ignoring it');
return;
}
status = CONNECTED;
enabledPings = true;
usePing();
if (onconnected) {
onconnected();
}
};
wsConfig.onerror = function (error) {
Logger.debug('--------- ONERROR -----------');
status = DISCONNECTED;
stopPing();
if (onerror) {
onerror(error);
}
};
var ws = new WebSocketWithReconnection(wsConfig);
Logger.debug('Connecting websocket to URI: ' + wsConfig.uri);
var rpcBuilderOptions = {
request_timeout: configuration.rpc.requestTimeout,
ping_request_timeout: configuration.rpc.heartbeatRequestTimeout
};
var rpc = new RpcBuilder(RpcBuilder.packers.JsonRPC, rpcBuilderOptions, ws, function (request) {
Logger.debug('Received request: ' + JSON.stringify(request));
try {
var func = configuration.rpc[request.method];
if (func === undefined) {
Logger.error('Method ' + request.method + ' not registered in client');
} else {
func(request.params, request);
}
} catch (err) {
Logger.error('Exception processing request: ' + JSON.stringify(request));
Logger.error(err);
}
});
this.send = function (method, params, callback) {
var requestTime = Date.now();
rpc.encode(method, params, function (error, result) {
if (error) {
try {
Logger.error(
'ERROR:' +
error.message +
' in Request: method:' +
method +
' params:' +
JSON.stringify(params) +
' request:' +
error.request
);
if (error.data) {
Logger.error('ERROR DATA:' + JSON.stringify(error.data));
}
} catch (e) {}
error.requestTime = requestTime;
}
if (callback) {
if (result != undefined && result.value !== 'pong') {
Logger.debug('Response: ' + JSON.stringify(result));
}
callback(error, result);
}
});
};
function updateNotReconnectIfLessThan() {
Logger.debug('notReconnectIfNumLessThan = ' + pingNextNum + ' (old=' + notReconnectIfNumLessThan + ')');
notReconnectIfNumLessThan = pingNextNum;
}
function sendPing() {
if (enabledPings) {
var params = null;
if (pingNextNum == 0 || pingNextNum == notReconnectIfNumLessThan) {
params = {
interval: configuration.heartbeat || PING_INTERVAL
};
}
pingNextNum++;
self.send(
'ping',
params,
(function (pingNum) {
return function (error, result) {
if (error) {
Logger.debug('Error in ping request #' + pingNum + ' (' + error.message + ')');
if (pingNum > notReconnectIfNumLessThan) {
enabledPings = false;
updateNotReconnectIfLessThan();
Logger.debug('Server did not respond to ping message #' + pingNum + '. Reconnecting... ');
ws.reconnectWs();
}
}
};
})(pingNextNum)
);
} else {
Logger.debug('Trying to send ping, but ping is not enabled');
}
}
/*
* If configuration.hearbeat has any value, the ping-pong will work with the interval
* of configuration.hearbeat
*/
function usePing() {
if (!pingPongStarted) {
Logger.debug('Starting ping (if configured)');
pingPongStarted = true;
if (configuration.heartbeat != undefined) {
pingInterval = setInterval(sendPing, configuration.heartbeat);
sendPing();
}
}
}
function stopPing() {
clearInterval(pingInterval);
pingPongStarted = false;
enabledPings = false;
pingNextNum = -1;
rpc.cancel();
}
this.close = function (code, reason) {
Logger.debug('Closing with code: ' + code + ' because: ' + reason);
if (pingInterval != undefined) {
Logger.debug('Clearing ping interval');
clearInterval(pingInterval);
}
pingPongStarted = false;
enabledPings = false;
ws.close(code, reason);
};
this.reconnect = function () {
ws.reconnectWs();
};
this.resetPing = function () {
enabledPings = true;
pingNextNum = 0;
usePing();
};
this.getReadyState = function () {
return ws.getReadyState();
};
}
module.exports = JsonRpcClient;

View File

@ -1,20 +0,0 @@
/*
* (C) Copyright 2014 Kurento (http://kurento.org/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var WebSocketWithReconnection = require('./webSocketWithReconnection');
exports.WebSocketWithReconnection = WebSocketWithReconnection;

View File

@ -1,161 +0,0 @@
/*
* (C) Copyright 2013-2015 Kurento (http://kurento.org/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var OpenViduLogger = require('../../../../Logger/OpenViduLogger').OpenViduLogger;
var Logger = OpenViduLogger.getInstance();
var MAX_RETRIES = 2000; // Forever...
var RETRY_TIME_MS = 3000; // FIXME: Implement exponential wait times...
var CONNECTING = 0;
var OPEN = 1;
var CLOSING = 2;
var CLOSED = 3;
/*
config = {
uri : wsUri,
onconnected : callback method to invoke when connection is successful,
ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached),
onreconnecting : callback method to invoke when the client is reconnecting,
onreconnected : callback method to invoke when the client successfully reconnects,
};
*/
function WebSocketWithReconnection(config) {
var closing = false;
var registerMessageHandler;
var wsUri = config.uri;
var reconnecting = false;
var ws = new WebSocket(wsUri);
ws.onopen = () => {
Logger.debug('WebSocket connected to ' + wsUri);
if (config.onconnected) {
config.onconnected();
}
};
ws.onerror = (error) => {
Logger.error('Could not connect to ' + wsUri + ' (invoking onerror if defined)', error);
if (config.onerror) {
config.onerror(error);
}
};
var reconnectionOnClose = () => {
if (ws.readyState === CLOSED) {
if (closing) {
Logger.debug('Connection closed by user');
} else {
if (config.ismasternodecrashed()) {
Logger.error('Master Node has crashed. Stopping reconnection process');
} else {
Logger.debug('Connection closed unexpectedly. Reconnecting...');
reconnect(MAX_RETRIES, 1);
}
}
} else {
Logger.debug('Close callback from previous websocket. Ignoring it');
}
};
ws.onclose = reconnectionOnClose;
function reconnect(maxRetries, numRetries) {
Logger.debug('reconnect (attempt #' + numRetries + ', max=' + maxRetries + ')');
if (numRetries === 1) {
if (reconnecting) {
Logger.warn('Trying to reconnect when already reconnecting... Ignoring this reconnection.');
return;
} else {
reconnecting = true;
}
if (config.onreconnecting) {
config.onreconnecting();
}
}
reconnectAux(maxRetries, numRetries);
}
function addReconnectionQueryParamsIfMissing(uriString) {
var searchParams = new URLSearchParams(new URL(uriString).search);
if (!searchParams.has('reconnect')) {
uriString = Array.from(searchParams).length > 0 ? uriString + '&reconnect=true' : uriString + '?reconnect=true';
}
return uriString;
}
function reconnectAux(maxRetries, numRetries) {
Logger.debug('Reconnection attempt #' + numRetries);
ws.close(4104, 'Connection closed for reconnection');
wsUri = addReconnectionQueryParamsIfMissing(wsUri);
ws = new WebSocket(wsUri);
ws.onopen = () => {
Logger.debug('Reconnected to ' + wsUri + ' after ' + numRetries + ' attempts...');
reconnecting = false;
registerMessageHandler();
if (config.onreconnected) {
config.onreconnected();
}
ws.onclose = reconnectionOnClose;
};
ws.onerror = (error) => {
Logger.warn('Reconnection error: ', error);
if (numRetries === maxRetries) {
if (config.ondisconnect) {
config.ondisconnect();
}
} else {
setTimeout(() => {
reconnect(maxRetries, numRetries + 1);
}, RETRY_TIME_MS);
}
};
}
this.close = (code, reason) => {
closing = true;
ws.close(code, reason);
};
this.reconnectWs = () => {
Logger.debug('reconnectWs');
reconnect(MAX_RETRIES, 1);
};
this.send = (message) => {
ws.send(message);
};
this.addEventListener = (type, callback) => {
registerMessageHandler = () => {
ws.addEventListener(type, callback);
};
registerMessageHandler();
};
this.getReadyState = () => {
return ws.readyState;
};
}
module.exports = WebSocketWithReconnection;

View File

@ -1,687 +0,0 @@
/*
* (C) Copyright 2014 Kurento (http://kurento.org/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var defineProperty_IE8 = false;
if (Object.defineProperty) {
try {
Object.defineProperty({}, 'x', {});
} catch (e) {
defineProperty_IE8 = true;
}
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
var EventEmitter = require('events').EventEmitter;
var inherits = require('inherits');
var packers = require('./packers');
var Mapper = require('./Mapper');
var BASE_TIMEOUT = 5000;
function unifyResponseMethods(responseMethods) {
if (!responseMethods) return {};
for (var key in responseMethods) {
var value = responseMethods[key];
if (typeof value == 'string')
responseMethods[key] = {
response: value
};
}
return responseMethods;
}
function unifyTransport(transport) {
if (!transport) return;
// Transport as a function
if (transport instanceof Function)
return {
send: transport
};
// WebSocket & DataChannel
if (transport.send instanceof Function) return transport;
// Message API (Inter-window & WebWorker)
if (transport.postMessage instanceof Function) {
transport.send = transport.postMessage;
return transport;
}
// Stream API
if (transport.write instanceof Function) {
transport.send = transport.write;
return transport;
}
// Transports that only can receive messages, but not send
if (transport.onmessage !== undefined) return;
if (transport.pause instanceof Function) return;
throw new SyntaxError('Transport is not a function nor a valid object');
}
/**
* Representation of a RPC notification
*
* @class
*
* @constructor
*
* @param {String} method -method of the notification
* @param params - parameters of the notification
*/
function RpcNotification(method, params) {
if (defineProperty_IE8) {
this.method = method;
this.params = params;
} else {
Object.defineProperty(this, 'method', {
value: method,
enumerable: true
});
Object.defineProperty(this, 'params', {
value: params,
enumerable: true
});
}
}
/**
* @class
*
* @constructor
*
* @param {object} packer
*
* @param {object} [options]
*
* @param {object} [transport]
*
* @param {Function} [onRequest]
*/
function RpcBuilder(packer, options, transport, onRequest) {
var self = this;
if (!packer) throw new SyntaxError('Packer is not defined');
if (!packer.pack || !packer.unpack) throw new SyntaxError('Packer is invalid');
var responseMethods = unifyResponseMethods(packer.responseMethods);
if (options instanceof Function) {
if (transport != undefined) throw new SyntaxError("There can't be parameters after onRequest");
onRequest = options;
transport = undefined;
options = undefined;
}
if (options && options.send instanceof Function) {
if (transport && !(transport instanceof Function)) throw new SyntaxError('Only a function can be after transport');
onRequest = transport;
transport = options;
options = undefined;
}
if (transport instanceof Function) {
if (onRequest != undefined) throw new SyntaxError("There can't be parameters after onRequest");
onRequest = transport;
transport = undefined;
}
if (transport && transport.send instanceof Function)
if (onRequest && !(onRequest instanceof Function)) throw new SyntaxError('Only a function can be after transport');
options = options || {};
EventEmitter.call(this);
if (onRequest) this.on('request', onRequest);
if (defineProperty_IE8) this.peerID = options.peerID;
else
Object.defineProperty(this, 'peerID', {
value: options.peerID
});
var max_retries = options.max_retries || 0;
function transportMessage(event) {
self.decode(event.data || event);
}
this.getTransport = function () {
return transport;
};
this.setTransport = function (value) {
// Remove listener from old transport
if (transport) {
// W3C transports
if (transport.removeEventListener) transport.removeEventListener('message', transportMessage);
// Node.js Streams API
else if (transport.removeListener) transport.removeListener('data', transportMessage);
}
// Set listener on new transport
if (value) {
// W3C transports
if (value.addEventListener) value.addEventListener('message', transportMessage);
// Node.js Streams API
else if (value.addListener) value.addListener('data', transportMessage);
}
transport = unifyTransport(value);
};
if (!defineProperty_IE8)
Object.defineProperty(this, 'transport', {
get: this.getTransport.bind(this),
set: this.setTransport.bind(this)
});
this.setTransport(transport);
var request_timeout = options.request_timeout || BASE_TIMEOUT;
var ping_request_timeout = options.ping_request_timeout || request_timeout;
var response_timeout = options.response_timeout || BASE_TIMEOUT;
var duplicates_timeout = options.duplicates_timeout || BASE_TIMEOUT;
var requestID = 0;
var requests = new Mapper();
var responses = new Mapper();
var processedResponses = new Mapper();
var message2Key = {};
/**
* Store the response to prevent to process duplicate request later
*/
function storeResponse(message, id, dest) {
var response = {
message: message,
/** Timeout to auto-clean old responses */
timeout: setTimeout(function () {
responses.remove(id, dest);
}, response_timeout)
};
responses.set(response, id, dest);
}
/**
* Store the response to ignore duplicated messages later
*/
function storeProcessedResponse(ack, from) {
var timeout = setTimeout(function () {
processedResponses.remove(ack, from);
}, duplicates_timeout);
processedResponses.set(timeout, ack, from);
}
/**
* Representation of a RPC request
*
* @class
* @extends RpcNotification
*
* @constructor
*
* @param {String} method -method of the notification
* @param params - parameters of the notification
* @param {Integer} id - identifier of the request
* @param [from] - source of the notification
*/
function RpcRequest(method, params, id, from, transport) {
RpcNotification.call(this, method, params);
this.getTransport = function () {
return transport;
};
this.setTransport = function (value) {
transport = unifyTransport(value);
};
if (!defineProperty_IE8)
Object.defineProperty(this, 'transport', {
get: this.getTransport.bind(this),
set: this.setTransport.bind(this)
});
var response = responses.get(id, from);
/**
* @constant {Boolean} duplicated
*/
if (!(transport || self.getTransport())) {
if (defineProperty_IE8) this.duplicated = Boolean(response);
else
Object.defineProperty(this, 'duplicated', {
value: Boolean(response)
});
}
var responseMethod = responseMethods[method];
this.pack = packer.pack.bind(packer, this, id);
/**
* Generate a response to this request
*
* @param {Error} [error]
* @param {*} [result]
*
* @returns {string}
*/
this.reply = function (error, result, transport) {
// Fix optional parameters
if (error instanceof Function || (error && error.send instanceof Function)) {
if (result != undefined) throw new SyntaxError("There can't be parameters after callback");
transport = error;
result = null;
error = undefined;
} else if (result instanceof Function || (result && result.send instanceof Function)) {
if (transport != undefined) throw new SyntaxError("There can't be parameters after callback");
transport = result;
result = null;
}
transport = unifyTransport(transport);
// Duplicated request, remove old response timeout
if (response) clearTimeout(response.timeout);
if (from != undefined) {
if (error) error.dest = from;
if (result) result.dest = from;
}
var message;
// New request or overriden one, create new response with provided data
if (error || result != undefined) {
if (self.peerID != undefined) {
if (error) error.from = self.peerID;
else result.from = self.peerID;
}
// Protocol indicates that responses has own request methods
if (responseMethod) {
if (responseMethod.error == undefined && error)
message = {
error: error
};
else {
var method = error ? responseMethod.error : responseMethod.response;
message = {
method: method,
params: error || result
};
}
} else
message = {
error: error,
result: result
};
message = packer.pack(message, id);
}
// Duplicate & not-overriden request, re-send old response
else if (response) message = response.message;
// New empty reply, response null value
else
message = packer.pack(
{
result: null
},
id
);
// Store the response to prevent to process a duplicated request later
storeResponse(message, id, from);
// Return the stored response so it can be directly send back
transport = transport || this.getTransport() || self.getTransport();
if (transport) return transport.send(message);
return message;
};
}
inherits(RpcRequest, RpcNotification);
function cancel(message) {
var key = message2Key[message];
if (!key) return;
delete message2Key[message];
var request = requests.pop(key.id, key.dest);
if (!request) return;
clearTimeout(request.timeout);
// Start duplicated responses timeout
storeProcessedResponse(key.id, key.dest);
}
/**
* Allow to cancel a request and don't wait for a response
*
* If `message` is not given, cancel all the request
*/
this.cancel = function (message) {
if (message) return cancel(message);
for (var message in message2Key) cancel(message);
};
this.close = function () {
// Prevent to receive new messages
var transport = this.getTransport();
if (transport && transport.close) transport.close(4003, 'Cancel request');
// Request & processed responses
this.cancel();
processedResponses.forEach(clearTimeout);
// Responses
responses.forEach(function (response) {
clearTimeout(response.timeout);
});
};
/**
* Generates and encode a JsonRPC 2.0 message
*
* @param {String} method -method of the notification
* @param params - parameters of the notification
* @param [dest] - destination of the notification
* @param {object} [transport] - transport where to send the message
* @param [callback] - function called when a response to this request is
* received. If not defined, a notification will be send instead
*
* @returns {string} A raw JsonRPC 2.0 request or notification string
*/
this.encode = function (method, params, dest, transport, callback) {
// Fix optional parameters
if (params instanceof Function) {
if (dest != undefined) throw new SyntaxError("There can't be parameters after callback");
callback = params;
transport = undefined;
dest = undefined;
params = undefined;
} else if (dest instanceof Function) {
if (transport != undefined) throw new SyntaxError("There can't be parameters after callback");
callback = dest;
transport = undefined;
dest = undefined;
} else if (transport instanceof Function) {
if (callback != undefined) throw new SyntaxError("There can't be parameters after callback");
callback = transport;
transport = undefined;
}
if (self.peerID != undefined) {
params = params || {};
params.from = self.peerID;
}
if (dest != undefined) {
params = params || {};
params.dest = dest;
}
// Encode message
var message = {
method: method,
params: params
};
if (callback) {
var id = requestID++;
var retried = 0;
message = packer.pack(message, id);
function dispatchCallback(error, result) {
self.cancel(message);
callback(error, result);
}
var request = {
message: message,
callback: dispatchCallback,
responseMethods: responseMethods[method] || {}
};
var encode_transport = unifyTransport(transport);
function sendRequest(transport) {
var rt = method === 'ping' ? ping_request_timeout : request_timeout;
request.timeout = setTimeout(timeout, rt * Math.pow(2, retried++));
message2Key[message] = {
id: id,
dest: dest
};
requests.set(request, id, dest);
transport = transport || encode_transport || self.getTransport();
if (transport) return transport.send(message);
return message;
}
function retry(transport) {
transport = unifyTransport(transport);
console.warn(retried + ' retry for request message:', message);
var timeout = processedResponses.pop(id, dest);
clearTimeout(timeout);
return sendRequest(transport);
}
function timeout() {
if (retried < max_retries) return retry(transport);
var error = new Error('Request has timed out');
error.request = message;
error.retry = retry;
dispatchCallback(error);
}
return sendRequest(transport);
}
// Return the packed message
message = packer.pack(message);
transport = transport || this.getTransport();
if (transport) return transport.send(message);
return message;
};
/**
* Decode and process a JsonRPC 2.0 message
*
* @param {string} message - string with the content of the message
*
* @returns {RpcNotification|RpcRequest|undefined} - the representation of the
* notification or the request. If a response was processed, it will return
* `undefined` to notify that it was processed
*
* @throws {TypeError} - Message is not defined
*/
this.decode = function (message, transport) {
if (!message) throw new TypeError('Message is not defined');
try {
message = packer.unpack(message);
} catch (e) {
// Ignore invalid messages
return console.debug(e, message);
}
var id = message.id;
var ack = message.ack;
var method = message.method;
var params = message.params || {};
var from = params.from;
var dest = params.dest;
// Ignore messages send by us
if (self.peerID != undefined && from == self.peerID) return;
// Notification
if (id == undefined && ack == undefined) {
var notification = new RpcNotification(method, params);
if (self.emit('request', notification)) return;
return notification;
}
function processRequest() {
// If we have a transport and it's a duplicated request, reply inmediatly
transport = unifyTransport(transport) || self.getTransport();
if (transport) {
var response = responses.get(id, from);
if (response) return transport.send(response.message);
}
var idAck = id != undefined ? id : ack;
var request = new RpcRequest(method, params, idAck, from, transport);
if (self.emit('request', request)) return;
return request;
}
function processResponse(request, error, result) {
request.callback(error, result);
}
function duplicatedResponse(timeout) {
console.warn('Response already processed', message);
// Update duplicated responses timeout
clearTimeout(timeout);
storeProcessedResponse(ack, from);
}
// Request, or response with own method
if (method) {
// Check if it's a response with own method
if (dest == undefined || dest == self.peerID) {
var request = requests.get(ack, from);
if (request) {
var responseMethods = request.responseMethods;
if (method == responseMethods.error) return processResponse(request, params);
if (method == responseMethods.response) return processResponse(request, null, params);
return processRequest();
}
var processed = processedResponses.get(ack, from);
if (processed) return duplicatedResponse(processed);
}
// Request
return processRequest();
}
var error = message.error;
var result = message.result;
// Ignore responses not send to us
if (error && error.dest && error.dest != self.peerID) return;
if (result && result.dest && result.dest != self.peerID) return;
// Response
var request = requests.get(ack, from);
if (!request) {
var processed = processedResponses.get(ack, from);
if (processed) return duplicatedResponse(processed);
return console.warn('No callback was defined for this message', message);
}
// Process response
processResponse(request, error, result);
};
}
inherits(RpcBuilder, EventEmitter);
RpcBuilder.RpcNotification = RpcNotification;
module.exports = RpcBuilder;
var clients = require('./clients');
var transports = require('./clients/transports');
RpcBuilder.clients = clients;
RpcBuilder.clients.transports = transports;
RpcBuilder.packers = packers;

View File

@ -1,85 +0,0 @@
/**
* JsonRPC 2.0 packer
*/
/**
* Pack a JsonRPC 2.0 message
*
* @param {Object} message - object to be packaged. It requires to have all the
* fields needed by the JsonRPC 2.0 message that it's going to be generated
*
* @return {String} - the stringified JsonRPC 2.0 message
*/
function pack(message, id) {
var result = {
jsonrpc: '2.0'
};
// Request
if (message.method) {
result.method = message.method;
if (message.params) result.params = message.params;
// Request is a notification
if (id != undefined) result.id = id;
}
// Response
else if (id != undefined) {
if (message.error) {
if (message.result !== undefined) throw new TypeError('Both result and error are defined');
result.error = message.error;
} else if (message.result !== undefined) result.result = message.result;
else throw new TypeError('No result or error is defined');
result.id = id;
}
return JSON.stringify(result);
}
/**
* Unpack a JsonRPC 2.0 message
*
* @param {String} message - string with the content of the JsonRPC 2.0 message
*
* @throws {TypeError} - Invalid JsonRPC version
*
* @return {Object} - object filled with the JsonRPC 2.0 message content
*/
function unpack(message) {
var result = message;
if (typeof message === 'string' || message instanceof String) {
result = JSON.parse(message);
}
// Check if it's a valid message
var version = result.jsonrpc;
if (version !== '2.0') throw new TypeError("Invalid JsonRPC version '" + version + "': " + message);
// Response
if (result.method == undefined) {
if (result.id == undefined) throw new TypeError('Invalid message: ' + message);
var result_defined = result.result !== undefined;
var error_defined = result.error !== undefined;
// Check only result or error is defined, not both or none
if (result_defined && error_defined) throw new TypeError('Both result and error are defined: ' + message);
if (!result_defined && !error_defined) throw new TypeError('No result or error is defined: ' + message);
result.ack = result.id;
delete result.id;
}
// Return unpacked message
return result;
}
exports.pack = pack;
exports.unpack = unpack;

View File

@ -1,10 +0,0 @@
function pack(message) {
throw new TypeError('Not yet implemented');
}
function unpack(message) {
throw new TypeError('Not yet implemented');
}
exports.pack = pack;
exports.unpack = unpack;

View File

@ -1,5 +0,0 @@
var JsonRPC = require('./JsonRPC');
var XmlRPC = require('./XmlRPC');
exports.JsonRPC = JsonRPC;
exports.XmlRPC = XmlRPC;

View File

@ -1,41 +0,0 @@
type ConsoleFunction = (...data: any) => void;
export class ConsoleLogger {
/**
* @hidden
*/
logger: Console;
/**
* @hidden
*/
log: ConsoleFunction;
/**
* @hidden
*/
info: ConsoleFunction;
/**
* @hidden
*/
debug: ConsoleFunction;
/**
* @hidden
*/
warn: ConsoleFunction;
/**
* @hidden
*/
error: ConsoleFunction;
constructor(console: Console) {
this.logger = console;
(this.log = console.log),
(this.info = console.info),
(this.debug = console.debug),
(this.warn = console.warn),
(this.error = console.error);
}
}

View File

@ -1,285 +0,0 @@
import { JL } from 'jsnlog';
import { OpenVidu } from '../../OpenVidu/OpenVidu';
import { ConsoleLogger } from './ConsoleLogger';
import { OpenViduLoggerConfiguration } from './OpenViduLoggerConfiguration';
export class OpenViduLogger {
private static instance: OpenViduLogger;
private JSNLOG_URL: string = '/openvidu/elk/openvidu-browser-logs';
private MAX_JSNLOG_BATCH_LOG_MESSAGES: number = 100;
private MAX_MSECONDS_BATCH_MESSAGES: number = 5000;
private MAX_LENGTH_STRING_JSON: number = 1000;
private defaultConsoleLogger: ConsoleLogger = new ConsoleLogger(globalThis.console);
private currentAppender: any;
private isProdMode = false;
private isJSNLogSetup = false;
// This two variables are used to restart JSNLog
// on different sessions and different userIds
private loggingSessionId: string | undefined;
/**
* @hidden
*/
static configureJSNLog(openVidu: OpenVidu, token: string) {
try {
// If dev mode or...
if (
globalThis['LOG_JSNLOG_RESULTS'] ||
// If instance is created and it is OpenVidu Pro
(this.instance &&
openVidu.isAtLeastPro &&
// If logs are enabled
this.instance.isOpenViduBrowserLogsDebugActive(openVidu) &&
// Only reconfigure it if session or finalUserId has changed
this.instance.canConfigureJSNLog(openVidu, this.instance))
) {
// Check if app logs can be sent
// and replace console.log function to send
// logs of the application
if (openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug_app) {
this.instance.replaceWindowConsole();
}
// isJSNLogSetup will not be true until completed setup
this.instance.isJSNLogSetup = false;
this.instance.info('Configuring JSNLogs.');
const finalUserId = openVidu.finalUserId;
const sessionId = openVidu.session.sessionId;
const beforeSendCallback = (xhr) => {
// If 401 or 403 or 404 modify ready and status so JSNLog don't retry to send logs
// https://github.com/mperdeck/jsnlog.js/blob/v2.30.0/jsnlog.ts#L805-L818
const parentReadyStateFunction = xhr.onreadystatechange;
xhr.onreadystatechange = () => {
if (this.isInvalidResponse(xhr)) {
Object.defineProperty(xhr, 'readyState', { value: 4 });
Object.defineProperty(xhr, 'status', { value: 200 });
// Disable JSNLog too to not send periodically errors
this.instance.disableLogger();
}
parentReadyStateFunction();
};
// Headers to identify and authenticate logs
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(`${finalUserId}%/%${sessionId}` + ':' + token));
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
// Additional headers for OpenVidu
xhr.setRequestHeader('OV-Final-User-Id', finalUserId);
xhr.setRequestHeader('OV-Session-Id', sessionId);
xhr.setRequestHeader('OV-Token', token);
};
// Creation of the appender.
this.instance.currentAppender = JL.createAjaxAppender(`appender-${finalUserId}-${sessionId}`);
this.instance.currentAppender.setOptions({
beforeSend: beforeSendCallback,
maxBatchSize: 1000,
batchSize: this.instance.MAX_JSNLOG_BATCH_LOG_MESSAGES,
batchTimeout: this.instance.MAX_MSECONDS_BATCH_MESSAGES
});
// Avoid circular dependencies
const logSerializer = (obj): string => {
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value != null) {
if (seen.has(value) || (globalThis.HTMLElement && value instanceof HTMLElement)) {
return;
}
seen.add(value);
}
return value;
};
};
// Cut long messages
let stringifyJson = JSON.stringify(obj, getCircularReplacer());
if (stringifyJson.length > this.instance.MAX_LENGTH_STRING_JSON) {
stringifyJson = `${stringifyJson.substring(0, this.instance.MAX_LENGTH_STRING_JSON)}...`;
}
if (globalThis['LOG_JSNLOG_RESULTS']) {
console.log(stringifyJson);
}
return stringifyJson;
};
// Initialize JL to send logs
JL.setOptions({
defaultAjaxUrl: openVidu.httpUri + this.instance.JSNLOG_URL,
serialize: logSerializer,
enabled: true
});
JL().setOptions({
appenders: [this.instance.currentAppender]
});
this.instance.isJSNLogSetup = true;
this.instance.loggingSessionId = sessionId;
this.instance.info('JSNLog configured.');
}
} catch (e) {
// Print error
console.error('Error configuring JSNLog: ');
console.error(e);
// Restore defaults values just in case any exception happen-
this.instance.disableLogger();
}
}
/**
* @hidden
*/
static getInstance(): OpenViduLogger {
if (!OpenViduLogger.instance) {
OpenViduLogger.instance = new OpenViduLogger();
}
return OpenViduLogger.instance;
}
private static isInvalidResponse(xhr: XMLHttpRequest) {
return xhr.status == 401 || xhr.status == 403 || xhr.status == 404 || xhr.status == 0;
}
private canConfigureJSNLog(openVidu: OpenVidu, logger: OpenViduLogger): boolean {
return openVidu.session.sessionId != logger.loggingSessionId;
}
private isOpenViduBrowserLogsDebugActive(openVidu: OpenVidu) {
return (
openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug ||
openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug_app
);
}
// Return console functions with jsnlog integration
private getConsoleWithJSNLog() {
return (function (openViduLogger: OpenViduLogger) {
return {
log: function (...args) {
openViduLogger.defaultConsoleLogger.log.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
if (openViduLogger.isJSNLogSetup) {
JL().info(arguments);
}
},
info: function (...args) {
openViduLogger.defaultConsoleLogger.info.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
if (openViduLogger.isJSNLogSetup) {
JL().info(arguments);
}
},
debug: function (...args) {
openViduLogger.defaultConsoleLogger.debug.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
},
warn: function (...args) {
openViduLogger.defaultConsoleLogger.warn.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
if (openViduLogger.isJSNLogSetup) {
JL().warn(arguments);
}
},
error: function (...args) {
openViduLogger.defaultConsoleLogger.error.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
if (openViduLogger.isJSNLogSetup) {
JL().error(arguments);
}
}
};
})(this);
}
private replaceWindowConsole() {
globalThis.console = this.defaultConsoleLogger.logger;
globalThis.console.log = this.getConsoleWithJSNLog().log;
globalThis.console.info = this.getConsoleWithJSNLog().info;
globalThis.console.debug = this.getConsoleWithJSNLog().debug;
globalThis.console.warn = this.getConsoleWithJSNLog().warn;
globalThis.console.error = this.getConsoleWithJSNLog().error;
}
private disableLogger() {
JL.setOptions({ enabled: false });
this.isJSNLogSetup = false;
this.loggingSessionId = undefined;
this.currentAppender = undefined;
globalThis.console = this.defaultConsoleLogger.logger;
globalThis.console.log = this.defaultConsoleLogger.log;
globalThis.console.info = this.defaultConsoleLogger.info;
globalThis.console.debug = this.defaultConsoleLogger.debug;
globalThis.console.warn = this.defaultConsoleLogger.warn;
globalThis.console.error = this.defaultConsoleLogger.error;
}
/**
* @hidden
*/
log(...args: any[]) {
if (!this.isProdMode) {
this.defaultConsoleLogger.log.apply(this.defaultConsoleLogger.logger, arguments);
}
if (this.isJSNLogSetup) {
JL().info(arguments);
}
}
/**
* @hidden
*/
debug(...args: any[]) {
if (!this.isProdMode) {
this.defaultConsoleLogger.debug.apply(this.defaultConsoleLogger.logger, arguments);
}
}
/**
* @hidden
*/
info(...args: any[]) {
if (!this.isProdMode) {
this.defaultConsoleLogger.info.apply(this.defaultConsoleLogger.logger, arguments);
}
if (this.isJSNLogSetup) {
JL().info(arguments);
}
}
/**
* @hidden
*/
warn(...args: any[]) {
this.defaultConsoleLogger.warn.apply(this.defaultConsoleLogger.logger, arguments);
if (this.isJSNLogSetup) {
JL().warn(arguments);
}
}
/**
* @hidden
*/
error(...args: any[]) {
this.defaultConsoleLogger.error.apply(this.defaultConsoleLogger.logger, arguments);
if (this.isJSNLogSetup) {
JL().error(arguments);
}
}
/**
* @hidden
*/
flush() {
if (this.isJSNLogSetup && this.currentAppender != null) {
this.currentAppender.sendBatch();
}
}
enableProdMode() {
this.isProdMode = true;
}
}

View File

@ -1,5 +0,0 @@
export enum OpenViduLoggerConfiguration {
disabled = 'disabled',
debug = 'debug',
debug_app = 'debug_app'
}

View File

@ -1,233 +0,0 @@
// Last time updated on June 08, 2018
// Latest file can be found here: https://cdn.webrtc-experiment.com/getScreenId.js
// Muaz Khan - www.MuazKhan.com
// MIT License - www.WebRTC-Experiment.com/licence
// Documentation - https://github.com/muaz-khan/getScreenId.
// ______________
// getScreenId.js
/*
getScreenId(function (error, sourceId, screen_constraints) {
// error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome'
// sourceId == null || 'string' || 'firefox'
if(microsoftEdge) {
navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure);
}
else {
navigator.mediaDevices.getUserMedia(screen_constraints).then(onSuccess)catch(onFailure);
}
}, 'pass second parameter only if you want system audio');
*/
globalThis.getScreenId = function (firefoxString, callback, custom_parameter) {
if (navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob)) {
// microsoft edge => navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure);
callback({
video: true
});
return;
}
// for Firefox:
// sourceId == 'firefox'
// screen_constraints = {...}
if (!!navigator.mozGetUserMedia) {
callback(null, 'firefox', {
video: {
mozMediaSource: firefoxString,
mediaSource: firefoxString
}
});
return;
}
globalThis.addEventListener('message', onIFrameCallback);
function onIFrameCallback(event) {
if (!event.data) return;
if (event.data.chromeMediaSourceId) {
if (event.data.chromeMediaSourceId === 'PermissionDeniedError') {
callback('permission-denied');
} else {
callback(
null,
event.data.chromeMediaSourceId,
getScreenConstraints(null, event.data.chromeMediaSourceId, event.data.canRequestAudioTrack)
);
}
// this event listener is no more needed
globalThis.removeEventListener('message', onIFrameCallback);
}
if (event.data.chromeExtensionStatus) {
callback(event.data.chromeExtensionStatus, null, getScreenConstraints(event.data.chromeExtensionStatus));
// this event listener is no more needed
globalThis.removeEventListener('message', onIFrameCallback);
}
}
if (!custom_parameter) {
setTimeout(postGetSourceIdMessage, 100);
} else {
setTimeout(function () {
postGetSourceIdMessage(custom_parameter);
}, 100);
}
};
function getScreenConstraints(error, sourceId, canRequestAudioTrack) {
var screen_constraints = {
audio: false,
video: {
mandatory: {
chromeMediaSource: error ? 'screen' : 'desktop',
maxWidth: globalThis.screen.width > 1920 ? globalThis.screen.width : 1920,
maxHeight: globalThis.screen.height > 1080 ? globalThis.screen.height : 1080
},
optional: []
}
};
if (!!canRequestAudioTrack) {
screen_constraints.audio = {
mandatory: {
chromeMediaSource: error ? 'screen' : 'desktop'
// echoCancellation: true
},
optional: []
};
}
if (sourceId) {
screen_constraints.video.mandatory.chromeMediaSourceId = sourceId;
if (screen_constraints.audio && screen_constraints.audio.mandatory) {
screen_constraints.audio.mandatory.chromeMediaSourceId = sourceId;
}
}
return screen_constraints;
}
function postGetSourceIdMessage(custom_parameter) {
if (!iframe) {
loadIFrame(function () {
postGetSourceIdMessage(custom_parameter);
});
return;
}
if (!iframe.isLoaded) {
setTimeout(function () {
postGetSourceIdMessage(custom_parameter);
}, 100);
return;
}
if (!custom_parameter) {
iframe.contentWindow.postMessage(
{
captureSourceId: true
},
'*'
);
} else if (!!custom_parameter.forEach) {
iframe.contentWindow.postMessage(
{
captureCustomSourceId: custom_parameter
},
'*'
);
} else {
iframe.contentWindow.postMessage(
{
captureSourceIdWithAudio: true
},
'*'
);
}
}
var iframe;
// this function is used in RTCMultiConnection v3
globalThis.getScreenConstraints = function (callback) {
loadIFrame(function () {
getScreenId(function (error, sourceId, screen_constraints) {
if (!screen_constraints) {
screen_constraints = {
video: true
};
}
callback(error, screen_constraints.video);
});
});
};
function loadIFrame(loadCallback) {
if (iframe) {
loadCallback();
return;
}
iframe = document.createElement('iframe');
iframe.onload = function () {
iframe.isLoaded = true;
loadCallback();
};
iframe.src = 'https://openvidu.github.io/openvidu-screen-sharing-chrome-extension/';
iframe.style.display = 'none';
(document.body || document.documentElement).appendChild(iframe);
}
globalThis.getChromeExtensionStatus = function (callback) {
// for Firefox:
if (!!navigator.mozGetUserMedia) {
callback('installed-enabled');
return;
}
globalThis.addEventListener('message', onIFrameCallback);
function onIFrameCallback(event) {
if (!event.data) return;
if (event.data.chromeExtensionStatus) {
callback(event.data.chromeExtensionStatus);
// this event listener is no more needed
globalThis.removeEventListener('message', onIFrameCallback);
}
}
setTimeout(postGetChromeExtensionStatusMessage, 100);
};
function postGetChromeExtensionStatusMessage() {
if (!iframe) {
loadIFrame(postGetChromeExtensionStatusMessage);
return;
}
if (!iframe.isLoaded) {
setTimeout(postGetChromeExtensionStatusMessage, 100);
return;
}
iframe.contentWindow.postMessage(
{
getChromeExtensionStatus: true
},
'*'
);
}
exports.getScreenId = globalThis.getScreenId;

View File

@ -1,162 +0,0 @@
// global variables
var chromeMediaSource = 'screen';
var sourceId;
var screenCallback;
if (typeof window !== 'undefined' && typeof navigator !== 'undefined' && typeof navigator.userAgent !== 'undefined') {
var isFirefox = typeof window.InstallTrigger !== 'undefined';
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
var isChrome = !!window.chrome && !isOpera;
window.addEventListener('message', function (event) {
if (event.origin != window.location.origin) {
return;
}
onMessageCallback(event.data);
});
}
// and the function that handles received messages
function onMessageCallback(data) {
// "cancel" button is clicked
if (data == 'PermissionDeniedError') {
if (screenCallback) return screenCallback('PermissionDeniedError');
else throw new Error('PermissionDeniedError');
}
// extension notified his presence
if (data == 'rtcmulticonnection-extension-loaded') {
chromeMediaSource = 'desktop';
}
// extension shared temp sourceId
if (data.sourceId && screenCallback) {
screenCallback((sourceId = data.sourceId), data.canRequestAudioTrack === true);
}
}
// this method can be used to check if chrome extension is installed & enabled.
function isChromeExtensionAvailable(callback) {
if (!callback) return;
if (chromeMediaSource == 'desktop') return callback(true);
// ask extension if it is available
window.postMessage('are-you-there', '*');
setTimeout(function () {
if (chromeMediaSource == 'screen') {
callback(false);
} else callback(true);
}, 2000);
}
// this function can be used to get "source-id" from the extension
function getSourceId(callback) {
if (!callback) throw '"callback" parameter is mandatory.';
if (sourceId) return callback(sourceId);
screenCallback = callback;
window.postMessage('get-sourceId', '*');
}
// this function can be used to get "source-id" from the extension
function getCustomSourceId(arr, callback) {
if (!arr || !arr.forEach) throw '"arr" parameter is mandatory and it must be an array.';
if (!callback) throw '"callback" parameter is mandatory.';
if (sourceId) return callback(sourceId);
screenCallback = callback;
window.postMessage(
{
'get-custom-sourceId': arr
},
'*'
);
}
// this function can be used to get "source-id" from the extension
function getSourceIdWithAudio(callback) {
if (!callback) throw '"callback" parameter is mandatory.';
if (sourceId) return callback(sourceId);
screenCallback = callback;
window.postMessage('audio-plus-tab', '*');
}
function getChromeExtensionStatus(extensionid, callback) {
if (isFirefox) return callback('not-chrome');
if (arguments.length != 2) {
callback = extensionid;
extensionid = 'lfcgfepafnobdloecchnfaclibenjold'; // default extension-id
}
var image = document.createElement('img');
image.src = 'chrome-extension://' + extensionid + '/icon.png';
image.onload = function () {
chromeMediaSource = 'screen';
window.postMessage('are-you-there', '*');
setTimeout(function () {
if (chromeMediaSource == 'screen') {
callback('installed-disabled');
} else callback('installed-enabled');
}, 2000);
};
image.onerror = function () {
callback('not-installed');
};
}
function getScreenConstraintsWithAudio(callback) {
getScreenConstraints(callback, true);
}
// this function explains how to use above methods/objects
function getScreenConstraints(callback, captureSourceIdWithAudio) {
sourceId = '';
var firefoxScreenConstraints = {
mozMediaSource: 'window',
mediaSource: 'window'
};
if (isFirefox) return callback(null, firefoxScreenConstraints);
// this statement defines getUserMedia constraints
// that will be used to capture content of screen
var screen_constraints = {
mandatory: {
chromeMediaSource: chromeMediaSource,
maxWidth: screen.width > 1920 ? screen.width : 1920,
maxHeight: screen.height > 1080 ? screen.height : 1080
},
optional: []
};
// this statement verifies chrome extension availability
// if installed and available then it will invoke extension API
// otherwise it will fallback to command-line based screen capturing API
if (chromeMediaSource == 'desktop' && !sourceId) {
if (captureSourceIdWithAudio) {
getSourceIdWithAudio(function (sourceId, canRequestAudioTrack) {
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
if (canRequestAudioTrack) {
screen_constraints.canRequestAudioTrack = true;
}
callback(sourceId == 'PermissionDeniedError' ? sourceId : null, screen_constraints);
});
} else {
getSourceId(function (sourceId) {
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
callback(sourceId == 'PermissionDeniedError' ? sourceId : null, screen_constraints);
});
}
return;
}
// this statement sets gets 'sourceId" and sets "chromeMediaSourceId"
if (chromeMediaSource == 'desktop') {
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
}
// now invoking native getUserMedia API
callback(null, screen_constraints);
}
exports.getScreenConstraints = getScreenConstraints;
exports.getScreenConstraintsWithAudio = getScreenConstraintsWithAudio;
exports.isChromeExtensionAvailable = isChromeExtensionAvailable;
exports.getChromeExtensionStatus = getChromeExtensionStatus;
exports.getSourceId = getSourceId;

View File

@ -1,233 +0,0 @@
import platform = require('platform');
export class PlatformUtils {
protected static instance: PlatformUtils;
constructor() {}
static getInstance(): PlatformUtils {
if (!this.instance) {
this.instance = new PlatformUtils();
}
return PlatformUtils.instance;
}
public isChromeBrowser(): boolean {
return platform.name === 'Chrome';
}
/**
* @hidden
*/
public isSafariBrowser(): boolean {
return platform.name === 'Safari';
}
/**
* @hidden
*/
public isChromeMobileBrowser(): boolean {
return platform.name === 'Chrome Mobile';
}
/**
* @hidden
*/
public isFirefoxBrowser(): boolean {
return platform.name === 'Firefox';
}
/**
* @hidden
*/
public isFirefoxMobileBrowser(): boolean {
return platform.name === 'Firefox Mobile' || platform.name === 'Firefox for iOS';
}
/**
* @hidden
*/
public isOperaBrowser(): boolean {
return platform.name === 'Opera';
}
/**
* @hidden
*/
public isOperaMobileBrowser(): boolean {
return platform.name === 'Opera Mobile';
}
/**
* @hidden
*/
public isEdgeBrowser(): boolean {
const version = platform?.version ? parseFloat(platform.version) : -1;
return platform.name === 'Microsoft Edge' && version >= 80;
}
/**
* @hidden
*/
public isEdgeMobileBrowser(): boolean {
const version = platform?.version ? parseFloat(platform.version) : -1;
return platform.name === 'Microsoft Edge' && (platform.os?.family === 'Android' || platform.os?.family === 'iOS') && version > 45;
}
/**
* @hidden
*/
public isAndroidBrowser(): boolean {
return platform.name === 'Android Browser';
}
/**
* @hidden
*/
public isElectron(): boolean {
return platform.name === 'Electron';
}
/**
* @hidden
*/
public isNodeJs(): boolean {
return platform.name === 'Node.js';
}
/**
* @hidden
*/
public isSamsungBrowser(): boolean {
return platform.name === 'Samsung Internet Mobile' || platform.name === 'Samsung Internet';
}
// TODO: This method exists to overcome bug https://github.com/bestiejs/platform.js/issues/184
/**
* @hidden
*/
public isMotorolaEdgeDevice(): boolean {
return platform.product?.toLowerCase().includes('motorola edge') || false;
}
/**
* @hidden
*/
public isIPhoneOrIPad(): boolean {
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
const isTouchable = 'ontouchend' in document;
const isIPad = /\b(\w*Macintosh\w*)\b/.test(userAgent) && isTouchable;
const isIPhone = /\b(\w*iPhone\w*)\b/.test(userAgent) && /\b(\w*Mobile\w*)\b/.test(userAgent) && isTouchable;
return isIPad || isIPhone;
}
/**
* @hidden
*/
public isIOSWithSafari(): boolean {
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
return (
this.isIPhoneOrIPad() &&
/\b(\w*Apple\w*)\b/.test(navigator.vendor) &&
/\b(\w*Safari\w*)\b/.test(userAgent) &&
!/\b(\w*CriOS\w*)\b/.test(userAgent) &&
!/\b(\w*FxiOS\w*)\b/.test(userAgent)
);
}
/**
* @hidden
*/
public isIonicIos(): boolean {
return this.isIPhoneOrIPad() && platform.ua!!.indexOf('Safari') === -1;
}
/**
* @hidden
*/
public isIonicAndroid(): boolean {
return platform.os!!.family === 'Android' && platform.name == 'Android Browser';
}
/**
* @hidden
*/
public isMobileDevice(): boolean {
return platform.os!!.family === 'iOS' || platform.os!!.family === 'Android';
}
/**
* @hidden
*/
public isReactNative(): boolean {
return false;
}
/**
* @hidden
*/
public isChromium(): boolean {
return (
this.isChromeBrowser() ||
this.isChromeMobileBrowser() ||
this.isOperaBrowser() ||
this.isOperaMobileBrowser() ||
this.isEdgeBrowser() ||
this.isEdgeMobileBrowser() ||
this.isSamsungBrowser() ||
this.isIonicAndroid() ||
this.isIonicIos() ||
this.isElectron() ||
// TODO: remove when possible
this.isMotorolaEdgeDevice()
);
}
/**
* @hidden
*/
public canScreenShare(): boolean {
const version = platform?.version ? parseFloat(platform.version) : -1;
// Reject mobile devices
if (this.isMobileDevice()) {
return false;
}
return (
this.isChromeBrowser() ||
this.isFirefoxBrowser() ||
this.isOperaBrowser() ||
this.isElectron() ||
this.isEdgeBrowser() ||
(this.isSafariBrowser() && version >= 13)
);
}
/**
* @hidden
*/
public getName(): string {
return platform.name || '';
}
/**
* @hidden
*/
public getVersion(): string {
return platform.version || '';
}
/**
* @hidden
*/
public getFamily(): string {
return platform.os!!.family || '';
}
/**
* @hidden
*/
public getDescription(): string {
return platform.description || '';
}
}

View File

@ -1,600 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import freeice = require('freeice');
import { v4 as uuidv4 } from 'uuid';
import { TypeOfVideo } from '../Enums/TypeOfVideo';
import { ExceptionEventName } from '../Events/ExceptionEvent';
import { OpenViduLogger } from '../Logger/OpenViduLogger';
import { PlatformUtils } from '../Utils/Platform';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
export interface WebRtcPeerConfiguration {
mediaConstraints: {
audio: boolean;
video: boolean;
};
simulcast: boolean;
mediaServer: string;
onIceCandidate: (event: RTCIceCandidate) => void;
onIceConnectionStateException: (exceptionName: ExceptionEventName, message: string, data?: any) => void;
iceServers?: RTCIceServer[];
rtcConfiguration?: RTCConfiguration;
mediaStream?: MediaStream | null;
mode?: 'sendonly' | 'recvonly' | 'sendrecv';
id?: string;
typeOfVideo: TypeOfVideo | undefined;
}
export class WebRtcPeer {
pc: RTCPeerConnection;
remoteCandidatesQueue: RTCIceCandidate[] = [];
localCandidatesQueue: RTCIceCandidate[] = [];
// Same as WebRtcPeerConfiguration but without optional fields.
protected configuration: Required<WebRtcPeerConfiguration>;
private iceCandidateList: RTCIceCandidate[] = [];
constructor(configuration: WebRtcPeerConfiguration) {
platform = PlatformUtils.getInstance();
this.configuration = {
...configuration,
iceServers: !!configuration.iceServers && configuration.iceServers.length > 0 ? configuration.iceServers : freeice(),
rtcConfiguration: configuration.rtcConfiguration !== undefined ? configuration.rtcConfiguration : {},
mediaStream: configuration.mediaStream !== undefined ? configuration.mediaStream : null,
mode: !!configuration.mode ? configuration.mode : 'sendrecv',
id: !!configuration.id ? configuration.id : this.generateUniqueId()
};
// prettier-ignore
logger.debug(`[WebRtcPeer] configuration:\n${JSON.stringify(this.configuration, null, 2)}`);
let rtcConfiguration: RTCConfiguration = this.configuration.rtcConfiguration
? this.configuration.rtcConfiguration
: { iceServers: this.configuration.iceServers };
if (!rtcConfiguration.iceServers && this.configuration.iceServers) {
rtcConfiguration.iceServers = this.configuration.iceServers;
}
this.pc = new RTCPeerConnection(rtcConfiguration);
this.pc.addEventListener('icecandidate', (event: RTCPeerConnectionIceEvent) => {
if (event.candidate !== null) {
// `RTCPeerConnectionIceEvent.candidate` is supposed to be an RTCIceCandidate:
// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectioniceevent-candidate
//
// But in practice, it is actually an RTCIceCandidateInit that can be used to
// obtain a proper candidate, using the RTCIceCandidate constructor:
// https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-constructor
const candidateInit: RTCIceCandidateInit = event.candidate as RTCIceCandidateInit;
const iceCandidate = new RTCIceCandidate(candidateInit);
this.configuration.onIceCandidate(iceCandidate);
if (iceCandidate.candidate !== '') {
this.localCandidatesQueue.push(iceCandidate);
}
}
});
this.pc.addEventListener('signalingstatechange', async () => {
if (this.pc.signalingState === 'stable') {
// SDP Offer/Answer finished. Add stored remote candidates.
while (this.iceCandidateList.length > 0) {
let candidate = this.iceCandidateList.shift();
try {
await this.pc.addIceCandidate(<RTCIceCandidate>candidate);
} catch (error) {
logger.error('Error when calling RTCPeerConnection#addIceCandidate for RTCPeerConnection ' + this.getId(), error);
}
}
}
});
}
getId(): string {
return this.configuration.id;
}
/**
* This method frees the resources used by WebRtcPeer
*/
dispose() {
logger.debug('Disposing WebRtcPeer');
if (this.pc) {
if (this.pc.signalingState === 'closed') {
return;
}
this.pc.close();
this.remoteCandidatesQueue = [];
this.localCandidatesQueue = [];
}
}
// DEPRECATED LEGACY METHOD: Old WebRTC versions don't implement
// Transceivers, and instead depend on the deprecated
// "offerToReceiveAudio" and "offerToReceiveVideo".
private createOfferLegacy(): Promise<RTCSessionDescriptionInit> {
if (!!this.configuration.mediaStream) {
this.deprecatedPeerConnectionTrackApi();
}
const hasAudio = this.configuration.mediaConstraints.audio;
const hasVideo = this.configuration.mediaConstraints.video;
const options: RTCOfferOptions = {
offerToReceiveAudio: this.configuration.mode !== 'sendonly' && hasAudio,
offerToReceiveVideo: this.configuration.mode !== 'sendonly' && hasVideo
};
logger.debug('[createOfferLegacy] RTCPeerConnection.createOffer() options:', JSON.stringify(options));
return this.pc.createOffer(options);
}
/**
* Creates an SDP offer from the local RTCPeerConnection to send to the other peer.
* Only if the negotiation was initiated by this peer.
*/
async createOffer(): Promise<RTCSessionDescriptionInit> {
// TODO: Delete this conditional when all supported browsers are
// modern enough to implement the Transceiver methods.
if (!('addTransceiver' in this.pc)) {
logger.warn(
'[createOffer] Method RTCPeerConnection.addTransceiver() is NOT available; using LEGACY offerToReceive{Audio,Video}'
);
return this.createOfferLegacy();
} else {
logger.debug('[createOffer] Method RTCPeerConnection.addTransceiver() is available; using it');
}
// Spec doc: https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver
if (this.configuration.mode !== 'recvonly') {
// To send media, assume that all desired media tracks have been
// already added by higher level code to our MediaStream.
if (!this.configuration.mediaStream) {
throw new Error(
`[WebRtcPeer.createOffer] Direction is '${this.configuration.mode}', but no stream was configured to be sent`
);
}
for (const track of this.configuration.mediaStream.getTracks()) {
const tcInit: RTCRtpTransceiverInit = {
direction: this.configuration.mode,
streams: [this.configuration.mediaStream]
};
if (track.kind === 'video' && this.configuration.simulcast) {
// Check if the requested size is enough to ask for 3 layers.
const trackSettings = track.getSettings();
const trackConsts = track.getConstraints();
const trackWidth: number =
trackSettings.width ?? (trackConsts.width as ConstrainULongRange).ideal ?? (trackConsts.width as number) ?? 0;
const trackHeight: number =
trackSettings.height ?? (trackConsts.height as ConstrainULongRange).ideal ?? (trackConsts.height as number) ?? 0;
logger.info(`[createOffer] Video track dimensions: ${trackWidth}x${trackHeight}`);
const trackPixels = trackWidth * trackHeight;
let maxLayers = 0;
if (trackPixels >= 960 * 540) {
maxLayers = 3;
} else if (trackPixels >= 480 * 270) {
maxLayers = 2;
} else {
maxLayers = 1;
}
tcInit.sendEncodings = [];
for (let l = 0; l < maxLayers; l++) {
const layerDiv = 2 ** (maxLayers - l - 1);
const encoding: RTCRtpEncodingParameters = {
rid: 'rdiv' + layerDiv.toString(),
// @ts-ignore -- Property missing from DOM types.
scalabilityMode: 'L1T1'
};
if (['detail', 'text'].includes(track.contentHint)) {
// Prioritize best resolution, for maximum picture detail.
encoding.scaleResolutionDownBy = 1.0;
// @ts-ignore -- Property missing from DOM types.
encoding.maxFramerate = Math.floor(30 / layerDiv);
} else {
encoding.scaleResolutionDownBy = layerDiv;
}
tcInit.sendEncodings.push(encoding);
}
}
const tc = this.pc.addTransceiver(track, tcInit);
if (track.kind === 'video') {
let sendParams = tc.sender.getParameters();
let needSetParams = false;
if (!sendParams.degradationPreference?.length) {
// degradationPreference for video: "balanced", "maintain-framerate", "maintain-resolution".
// https://www.w3.org/TR/2018/CR-webrtc-20180927/#dom-rtcdegradationpreference
if (['detail', 'text'].includes(track.contentHint)) {
sendParams.degradationPreference = 'maintain-resolution';
} else {
sendParams.degradationPreference = 'balanced';
}
logger.info(`[createOffer] Video sender Degradation Preference set: ${sendParams.degradationPreference}`);
// FIXME: Firefox implements degradationPreference on each individual encoding!
// (set it on every element of the sendParams.encodings array)
needSetParams = true;
}
// FIXME: Check that the simulcast encodings were applied.
// Firefox doesn't implement `RTCRtpTransceiverInit.sendEncodings`
// so the only way to enable simulcast is with `RTCRtpSender.setParameters()`.
//
// This next block can be deleted when Firefox fixes bug #1396918:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
//
// NOTE: This is done in a way that is compatible with all browsers, to save on
// browser-conditional code. The idea comes from WebRTC Adapter.js:
// * https://github.com/webrtcHacks/adapter/issues/998
// * https://github.com/webrtcHacks/adapter/blob/v7.7.0/src/js/firefox/firefox_shim.js#L231-L255
if (this.configuration.simulcast) {
if (sendParams.encodings?.length !== tcInit.sendEncodings!.length) {
sendParams.encodings = tcInit.sendEncodings!;
needSetParams = true;
}
}
if (needSetParams) {
logger.debug(`[createOffer] Setting new RTCRtpSendParameters to video sender`);
try {
await tc.sender.setParameters(sendParams);
} catch (error) {
let message = `[WebRtcPeer.createOffer] Cannot set RTCRtpSendParameters to video sender`;
if (error instanceof Error) {
message += `: ${error.message}`;
}
throw new Error(message);
}
}
}
// DEBUG: Uncomment for details.
// if (track.kind === "video" && this.configuration.simulcast) {
// // Print browser capabilities.
// // prettier-ignore
// logger.debug(`[createOffer] Transceiver send capabilities (static):\n${JSON.stringify(RTCRtpSender.getCapabilities?.("video"), null, 2)}`);
// // prettier-ignore
// logger.debug(`[createOffer] Transceiver recv capabilities (static):\n${JSON.stringify(RTCRtpReceiver.getCapabilities?.("video"), null, 2)}`);
// // Print requested Transceiver encodings and parameters.
// // prettier-ignore
// logger.debug(`[createOffer] Transceiver send encodings (requested):\n${JSON.stringify(tcInit.sendEncodings, null, 2)}`);
// // prettier-ignore
// logger.debug(`[createOffer] Transceiver send parameters (accepted):\n${JSON.stringify(tc.sender.getParameters(), null, 2)}`);
// }
}
} else {
// To just receive media, create new recvonly transceivers.
for (const kind of ['audio', 'video']) {
// Check if the media kind should be used.
if (!this.configuration.mediaConstraints[kind]) {
continue;
}
this.configuration.mediaStream = new MediaStream();
this.pc.addTransceiver(kind, {
direction: this.configuration.mode,
streams: [this.configuration.mediaStream]
});
}
}
let sdpOffer: RTCSessionDescriptionInit;
try {
sdpOffer = await this.pc.createOffer();
} catch (error) {
let message = `[WebRtcPeer.createOffer] Browser failed creating an SDP Offer`;
if (error instanceof Error) {
message += `: ${error.message}`;
}
throw new Error(message);
}
return sdpOffer;
}
deprecatedPeerConnectionTrackApi() {
for (const track of this.configuration.mediaStream!.getTracks()) {
this.pc.addTrack(track, this.configuration.mediaStream!);
}
}
/**
* Creates an SDP answer from the local RTCPeerConnection to send to the other peer
* Only if the negotiation was initiated by the other peer
*/
createAnswer(): Promise<RTCSessionDescriptionInit> {
return new Promise((resolve, reject) => {
// TODO: Delete this conditional when all supported browsers are
// modern enough to implement the Transceiver methods.
if ('getTransceivers' in this.pc) {
logger.debug('[createAnswer] Method RTCPeerConnection.getTransceivers() is available; using it');
// Ensure that the PeerConnection already contains one Transceiver
// for each kind of media.
// The Transceivers should have been already created internally by
// the PC itself, when `pc.setRemoteDescription(sdpOffer)` was called.
for (const kind of ['audio', 'video']) {
// Check if the media kind should be used.
if (!this.configuration.mediaConstraints[kind]) {
continue;
}
let tc = this.pc.getTransceivers().find((tc) => tc.receiver.track.kind === kind);
if (tc) {
// Enforce our desired direction.
tc.direction = this.configuration.mode;
} else {
return reject(new Error(`${kind} requested, but no transceiver was created from remote description`));
}
}
this.pc
.createAnswer()
.then((sdpAnswer) => resolve(sdpAnswer))
.catch((error) => reject(error));
} else {
// TODO: Delete else branch when all supported browsers are
// modern enough to implement the Transceiver methods
let offerAudio,
offerVideo = true;
if (!!this.configuration.mediaConstraints) {
offerAudio =
typeof this.configuration.mediaConstraints.audio === 'boolean' ? this.configuration.mediaConstraints.audio : true;
offerVideo =
typeof this.configuration.mediaConstraints.video === 'boolean' ? this.configuration.mediaConstraints.video : true;
const constraints: RTCOfferOptions = {
offerToReceiveAudio: offerAudio,
offerToReceiveVideo: offerVideo
};
(this.pc as RTCPeerConnection).createAnswer(constraints)
.then((sdpAnswer) => resolve(sdpAnswer))
.catch((error) => reject(error));
}
}
// else, there is nothing to do; the legacy createAnswer() options do
// not offer any control over which tracks are included in the answer.
});
}
/**
* This peer initiated negotiation. Step 1/4 of SDP offer-answer protocol
*/
processLocalOffer(offer: RTCSessionDescriptionInit): Promise<void> {
return new Promise((resolve, reject) => {
this.pc
.setLocalDescription(offer)
.then(() => {
const localDescription = this.pc.localDescription;
if (!!localDescription) {
logger.debug('Local description set', localDescription.sdp);
return resolve();
} else {
return reject('Local description is not defined');
}
})
.catch((error) => reject(error));
});
}
/**
* Other peer initiated negotiation. Step 2/4 of SDP offer-answer protocol
*/
processRemoteOffer(sdpOffer: string): Promise<void> {
return new Promise((resolve, reject) => {
const offer: RTCSessionDescriptionInit = {
type: 'offer',
sdp: sdpOffer
};
logger.debug('SDP offer received, setting remote description', offer);
if (this.pc.signalingState === 'closed') {
return reject('RTCPeerConnection is closed when trying to set remote description');
}
this.setRemoteDescription(offer)
.then(() => resolve())
.catch((error) => reject(error));
});
}
/**
* Other peer initiated negotiation. Step 3/4 of SDP offer-answer protocol
*/
processLocalAnswer(answer: RTCSessionDescriptionInit): Promise<void> {
return new Promise((resolve, reject) => {
logger.debug('SDP answer created, setting local description');
if (this.pc.signalingState === 'closed') {
return reject('RTCPeerConnection is closed when trying to set local description');
}
this.pc
.setLocalDescription(answer)
.then(() => resolve())
.catch((error) => reject(error));
});
}
/**
* This peer initiated negotiation. Step 4/4 of SDP offer-answer protocol
*/
processRemoteAnswer(sdpAnswer: string): Promise<void> {
return new Promise((resolve, reject) => {
const answer: RTCSessionDescriptionInit = {
type: 'answer',
sdp: sdpAnswer
};
logger.debug('SDP answer received, setting remote description');
if (this.pc.signalingState === 'closed') {
return reject('RTCPeerConnection is closed when trying to set remote description');
}
this.setRemoteDescription(answer)
.then(() => {
// DEBUG: Uncomment for details.
// {
// const tc = this.pc.getTransceivers().find((tc) => tc.sender.track?.kind === "video");
// // prettier-ignore
// logger.debug(`[processRemoteAnswer] Transceiver send parameters (effective):\n${JSON.stringify(tc?.sender.getParameters(), null, 2)}`);
// }
resolve();
})
.catch((error) => reject(error));
});
}
/**
* @hidden
*/
async setRemoteDescription(sdp: RTCSessionDescriptionInit): Promise<void> {
return this.pc.setRemoteDescription(sdp);
}
/**
* Callback function invoked when an ICE candidate is received
*/
addIceCandidate(iceCandidate: RTCIceCandidate): Promise<void> {
return new Promise((resolve, reject) => {
logger.debug('Remote ICE candidate received', iceCandidate);
this.remoteCandidatesQueue.push(iceCandidate);
switch (this.pc.signalingState) {
case 'closed':
reject(new Error('PeerConnection object is closed'));
break;
case 'stable':
if (!!this.pc.remoteDescription) {
this.pc
.addIceCandidate(iceCandidate)
.then(() => resolve())
.catch((error) => reject(error));
} else {
this.iceCandidateList.push(iceCandidate);
resolve();
}
break;
default:
this.iceCandidateList.push(iceCandidate);
resolve();
}
});
}
addIceConnectionStateChangeListener(otherId: string) {
this.pc.addEventListener('iceconnectionstatechange', () => {
const iceConnectionState: RTCIceConnectionState = this.pc.iceConnectionState;
switch (iceConnectionState) {
case 'disconnected':
// Possible network disconnection
const msg1 =
'IceConnectionState of RTCPeerConnection ' +
this.configuration.id +
' (' +
otherId +
') change to "disconnected". Possible network disconnection';
logger.warn(msg1);
this.configuration.onIceConnectionStateException(ExceptionEventName.ICE_CONNECTION_DISCONNECTED, msg1);
break;
case 'failed':
const msg2 = 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') to "failed"';
logger.error(msg2);
this.configuration.onIceConnectionStateException(ExceptionEventName.ICE_CONNECTION_FAILED, msg2);
break;
case 'closed':
logger.log(
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "closed"'
);
break;
case 'new':
logger.log('IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "new"');
break;
case 'checking':
logger.log(
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "checking"'
);
break;
case 'connected':
logger.log(
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "connected"'
);
break;
case 'completed':
logger.log(
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "completed"'
);
break;
}
});
}
/**
* @hidden
*/
generateUniqueId(): string {
return uuidv4();
}
}
export class WebRtcPeerRecvonly extends WebRtcPeer {
constructor(configuration: WebRtcPeerConfiguration) {
configuration.mode = 'recvonly';
super(configuration);
}
}
export class WebRtcPeerSendonly extends WebRtcPeer {
constructor(configuration: WebRtcPeerConfiguration) {
configuration.mode = 'sendonly';
super(configuration);
}
}
export class WebRtcPeerSendrecv extends WebRtcPeer {
constructor(configuration: WebRtcPeerConfiguration) {
configuration.mode = 'sendrecv';
super(configuration);
}
}

View File

@ -1,471 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// tslint:disable:no-string-literal
import { Stream } from '../../OpenVidu/Stream';
import { OpenViduLogger } from '../Logger/OpenViduLogger';
import { PlatformUtils } from '../Utils/Platform';
/**
* @hidden
*/
const logger: OpenViduLogger = OpenViduLogger.getInstance();
/**
* @hidden
*/
let platform: PlatformUtils;
interface WebrtcStatsConfig {
interval: number;
httpEndpoint: string;
}
interface JSONStatsResponse {
'@timestamp': string;
participant_id: string;
session_id: string;
platform: string;
platform_description: string;
stream: string;
webrtc_stats: IWebrtcStats;
}
/**
* Common WebRtcSTats for latest Chromium and Firefox versions
*/
interface IWebrtcStats {
inbound?: {
audio:
| {
bytesReceived: number;
packetsReceived: number;
packetsLost: number;
jitter: number;
}
| {};
video:
| {
bytesReceived: number;
packetsReceived: number;
packetsLost: number;
jitter?: number; // Firefox
jitterBufferDelay?: number; // Chrome
framesDecoded: number;
firCount: number;
nackCount: number;
pliCount: number;
frameHeight?: number; // Chrome
frameWidth?: number; // Chrome
framesDropped?: number; // Chrome
framesReceived?: number; // Chrome
}
| {};
};
outbound?: {
audio:
| {
bytesSent: number;
packetsSent: number;
}
| {};
video:
| {
bytesSent: number;
packetsSent: number;
firCount: number;
framesEncoded: number;
nackCount: number;
pliCount: number;
qpSum: number;
frameHeight?: number; // Chrome
frameWidth?: number; // Chrome
framesSent?: number; // Chrome
}
| {};
};
candidatepair?: {
currentRoundTripTime?: number; // Chrome
availableOutgoingBitrate?: number; //Chrome
// availableIncomingBitrate?: number // No support for any browsers (https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidatePairStats/availableIncomingBitrate)
};
}
export class WebRtcStats {
private readonly STATS_ITEM_NAME = 'webrtc-stats-config';
private webRtcStatsEnabled = false;
private webRtcStatsIntervalId: NodeJS.Timer;
private statsInterval = 1;
private POST_URL: string;
constructor(private stream: Stream) {
platform = PlatformUtils.getInstance();
}
public isEnabled(): boolean {
return this.webRtcStatsEnabled;
}
public initWebRtcStats(): void {
let webrtcObj;
// When cross-site (aka third-party) cookies are blocked by the browser,
// accessing localStorage in a third-party iframe throws a DOMException.
try {
webrtcObj = localStorage.getItem(this.STATS_ITEM_NAME);
}
catch(e){}
if (!!webrtcObj) {
this.webRtcStatsEnabled = true;
const webrtcStatsConfig: WebrtcStatsConfig = JSON.parse(webrtcObj);
// webrtc object found in local storage
logger.warn(
'WebRtc stats enabled for stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId
);
logger.warn('localStorage item: ' + JSON.stringify(webrtcStatsConfig));
this.POST_URL = webrtcStatsConfig.httpEndpoint;
this.statsInterval = webrtcStatsConfig.interval; // Interval in seconds
this.webRtcStatsIntervalId = setInterval(async () => {
await this.sendStatsToHttpEndpoint();
}, this.statsInterval * 1000);
} else {
logger.debug('WebRtc stats not enabled');
}
}
// {
// "localCandidate": {
// "id": "RTCIceCandidate_/r4P1y2Q",
// "timestamp": 1616080155617,
// "type": "local-candidate",
// "transportId": "RTCTransport_0_1",
// "isRemote": false,
// "networkType": "wifi",
// "ip": "123.45.67.89",
// "port": 63340,
// "protocol": "udp",
// "candidateType": "srflx",
// "priority": 1686052607,
// "deleted": false,
// "raw": [
// "candidate:3345412921 1 udp 1686052607 123.45.67.89 63340 typ srflx raddr 192.168.1.31 rport 63340 generation 0 ufrag 0ZtT network-id 1 network-cost 10",
// "candidate:58094482 1 udp 41885695 98.76.54.32 44431 typ relay raddr 123.45.67.89 rport 63340 generation 0 ufrag 0ZtT network-id 1 network-cost 10"
// ]
// },
// "remoteCandidate": {
// "id": "RTCIceCandidate_1YO18gph",
// "timestamp": 1616080155617,
// "type": "remote-candidate",
// "transportId": "RTCTransport_0_1",
// "isRemote": true,
// "ip": "12.34.56.78",
// "port": 64989,
// "protocol": "udp",
// "candidateType": "srflx",
// "priority": 1679819263,
// "deleted": false,
// "raw": [
// "candidate:16 1 UDP 1679819263 12.34.56.78 64989 typ srflx raddr 172.19.0.1 rport 64989",
// "candidate:16 1 UDP 1679819263 12.34.56.78 64989 typ srflx raddr 172.19.0.1 rport 64989"
// ]
// }
// }
// Have been tested in:
// - Linux Desktop:
// - Chrome 89.0.4389.90
// - Opera 74.0.3911.218
// - Firefox 86
// - Microsoft Edge 91.0.825.0
// - Electron 11.3.0 (Chromium 87.0.4280.141)
// - Windows Desktop:
// - Chrome 89.0.4389.90
// - Opera 74.0.3911.232
// - Firefox 86.0.1
// - Microsoft Edge 89.0.774.54
// - Electron 11.3.0 (Chromium 87.0.4280.141)
// - MacOS Desktop:
// - Chrome 89.0.4389.90
// - Firefox 87.0
// - Opera 75.0.3969.93
// - Microsoft Edge 89.0.774.57
// - Safari 14.0 (14610.1.28.1.9)
// - Electron 11.3.0 (Chromium 87.0.4280.141)
// - Android:
// - Chrome Mobile 89.0.4389.90
// - Opera 62.3.3146.57763
// - Firefox Mobile 86.6.1
// - Microsoft Edge Mobile 46.02.4.5147
// - Ionic 5
// - React Native 0.64
// - iOS:
// - Safari Mobile
// - ¿Ionic?
// - ¿React Native?
public getSelectedIceCandidateInfo(): Promise<any> {
return new Promise(async (resolve, reject) => {
const statsReport: any = await this.stream.getRTCPeerConnection().getStats();
let transportStat;
const candidatePairs: Map<string, any> = new Map();
const localCandidates: Map<string, any> = new Map();
const remoteCandidates: Map<string, any> = new Map();
statsReport.forEach((stat: any) => {
if (stat.type === 'transport' && (platform.isChromium() || platform.isSafariBrowser() || platform.isReactNative())) {
transportStat = stat;
}
switch (stat.type) {
case 'candidate-pair':
candidatePairs.set(stat.id, stat);
break;
case 'local-candidate':
localCandidates.set(stat.id, stat);
break;
case 'remote-candidate':
remoteCandidates.set(stat.id, stat);
break;
}
});
let selectedCandidatePair;
if (transportStat != null) {
const selectedCandidatePairId = transportStat.selectedCandidatePairId;
selectedCandidatePair = candidatePairs.get(selectedCandidatePairId);
} else {
// This is basically Firefox
const length = candidatePairs.size;
const iterator = candidatePairs.values();
for (let i = 0; i < length; i++) {
const candidatePair = iterator.next().value;
if (candidatePair['selected']) {
selectedCandidatePair = candidatePair;
break;
}
}
}
const localCandidateId = selectedCandidatePair.localCandidateId;
const remoteCandidateId = selectedCandidatePair.remoteCandidateId;
let finalLocalCandidate = localCandidates.get(localCandidateId);
if (!!finalLocalCandidate) {
const candList = this.stream.getLocalIceCandidateList();
const cand = candList.filter((c: RTCIceCandidate) => {
return (
!!c.candidate &&
(c.candidate.indexOf(finalLocalCandidate.ip) >= 0 || c.candidate.indexOf(finalLocalCandidate.address) >= 0) &&
c.candidate.indexOf(finalLocalCandidate.port) >= 0
);
});
finalLocalCandidate.raw = [];
for (let c of cand) {
finalLocalCandidate.raw.push(c.candidate);
}
} else {
finalLocalCandidate = 'ERROR: No active local ICE candidate. Probably ICE-TCP is being used';
}
let finalRemoteCandidate = remoteCandidates.get(remoteCandidateId);
if (!!finalRemoteCandidate) {
const candList = this.stream.getRemoteIceCandidateList();
const cand = candList.filter((c: RTCIceCandidate) => {
return (
!!c.candidate &&
(c.candidate.indexOf(finalRemoteCandidate.ip) >= 0 || c.candidate.indexOf(finalRemoteCandidate.address) >= 0) &&
c.candidate.indexOf(finalRemoteCandidate.port) >= 0
);
});
finalRemoteCandidate.raw = [];
for (let c of cand) {
finalRemoteCandidate.raw.push(c.candidate);
}
} else {
finalRemoteCandidate = 'ERROR: No active remote ICE candidate. Probably ICE-TCP is being used';
}
return resolve({
localCandidate: finalLocalCandidate,
remoteCandidate: finalRemoteCandidate
});
});
}
public stopWebRtcStats() {
if (this.webRtcStatsEnabled) {
clearInterval(this.webRtcStatsIntervalId);
logger.warn(
'WebRtc stats stopped for disposed stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId
);
}
}
private async sendStats(url: string, response: JSONStatsResponse): Promise<void> {
try {
const configuration: RequestInit = {
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(response),
method: 'POST'
};
await fetch(url, configuration);
} catch (error) {
logger.error(`sendStats error: ${JSON.stringify(error)}`);
}
}
private async sendStatsToHttpEndpoint(): Promise<void> {
try {
const webrtcStats: IWebrtcStats = await this.getCommonStats();
const response = this.generateJSONStatsResponse(webrtcStats);
await this.sendStats(this.POST_URL, response);
} catch (error) {
logger.log(error);
}
}
// Have been tested in:
// - Linux Desktop:
// - Chrome 89.0.4389.90
// - Opera 74.0.3911.218
// - Firefox 86
// - Microsoft Edge 91.0.825.0
// - Electron 11.3.0 (Chromium 87.0.4280.141)
// - Windows Desktop:
// - Chrome 89.0.4389.90
// - Opera 74.0.3911.232
// - Firefox 86.0.1
// - Microsoft Edge 89.0.774.54
// - Electron 11.3.0 (Chromium 87.0.4280.141)
// - MacOS Desktop:
// - Chrome 89.0.4389.90
// - Opera 75.0.3969.93
// - Firefox 87.0
// - Microsoft Edge 89.0.774.57
// - Safari 14.0 (14610.1.28.1.9)
// - Electron 11.3.0 (Chromium 87.0.4280.141)
// - Android:
// - Chrome Mobile 89.0.4389.90
// - Opera 62.3.3146.57763
// - Firefox Mobile 86.6.1
// - Microsoft Edge Mobile 46.02.4.5147
// - Ionic 5
// - React Native 0.64
// - iOS:
// - Safari Mobile
// - ¿Ionic?
// - ¿React Native?
public async getCommonStats(): Promise<IWebrtcStats> {
return new Promise(async (resolve, reject) => {
try {
const statsReport: any = await this.stream.getRTCPeerConnection().getStats();
const response: IWebrtcStats = this.getWebRtcStatsResponseOutline();
const videoTrackStats = ['framesReceived', 'framesDropped', 'framesSent', 'frameHeight', 'frameWidth'];
const candidatePairStats = ['availableOutgoingBitrate', 'currentRoundTripTime'];
statsReport.forEach((stat: any) => {
let mediaType = stat.mediaType != null ? stat.mediaType : stat.kind;
const addStat = (direction: string, key: string): void => {
if (stat[key] != null && response[direction] != null) {
if (!mediaType && videoTrackStats.indexOf(key) > -1) {
mediaType = 'video';
}
if (direction != null && mediaType != null && key != null && response[direction][mediaType] != null) {
response[direction][mediaType][key] = Number(stat[key]);
} else if (direction != null && key != null && candidatePairStats.includes(key)) {
// candidate-pair-stats
response[direction][key] = Number(stat[key]);
}
}
};
switch (stat.type) {
case 'outbound-rtp':
addStat('outbound', 'bytesSent');
addStat('outbound', 'packetsSent');
addStat('outbound', 'framesEncoded');
addStat('outbound', 'nackCount');
addStat('outbound', 'firCount');
addStat('outbound', 'pliCount');
addStat('outbound', 'qpSum');
break;
case 'inbound-rtp':
addStat('inbound', 'bytesReceived');
addStat('inbound', 'packetsReceived');
addStat('inbound', 'packetsLost');
addStat('inbound', 'jitter');
addStat('inbound', 'framesDecoded');
addStat('inbound', 'nackCount');
addStat('inbound', 'firCount');
addStat('inbound', 'pliCount');
break;
case 'track':
addStat('inbound', 'jitterBufferDelay');
addStat('inbound', 'framesReceived');
addStat('outbound', 'framesDropped');
addStat('outbound', 'framesSent');
addStat(this.stream.isLocal() ? 'outbound' : 'inbound', 'frameHeight');
addStat(this.stream.isLocal() ? 'outbound' : 'inbound', 'frameWidth');
break;
case 'candidate-pair':
addStat('candidatepair', 'currentRoundTripTime');
addStat('candidatepair', 'availableOutgoingBitrate');
break;
}
});
// Delete candidatepair from response if null
if (!response?.candidatepair || Object.keys(<Object>response.candidatepair).length === 0) {
delete response.candidatepair;
}
return resolve(response);
} catch (error) {
logger.error('Error getting common stats: ', error);
return reject(error);
}
});
}
private generateJSONStatsResponse(stats: IWebrtcStats): JSONStatsResponse {
return {
'@timestamp': new Date().toISOString(),
participant_id: this.stream.connection.data,
session_id: this.stream.session.sessionId,
platform: platform.getName(),
platform_description: platform.getDescription(),
stream: 'webRTC',
webrtc_stats: stats
};
}
private getWebRtcStatsResponseOutline(): IWebrtcStats {
if (this.stream.isLocal()) {
return {
outbound: {
audio: {},
video: {}
},
candidatepair: {}
};
} else {
return {
inbound: {
audio: {},
video: {}
}
};
}
}
}

View File

@ -1,51 +0,0 @@
import { JL } from 'jsnlog';
export { OpenVidu } from './OpenVidu/OpenVidu';
export { Session } from './OpenVidu/Session';
export { Publisher } from './OpenVidu/Publisher';
export { Subscriber } from './OpenVidu/Subscriber';
export { StreamManager } from './OpenVidu/StreamManager';
export { Stream } from './OpenVidu/Stream';
export { Connection } from './OpenVidu/Connection';
export { LocalRecorder } from './OpenVidu/LocalRecorder';
export { Filter } from './OpenVidu/Filter';
export { LocalRecorderState } from './OpenViduInternal/Enums/LocalRecorderState';
export { OpenViduError, OpenViduErrorName } from './OpenViduInternal/Enums/OpenViduError';
export { TypeOfVideo } from './OpenViduInternal/Enums/TypeOfVideo';
export { VideoInsertMode } from './OpenViduInternal/Enums/VideoInsertMode';
export { Event } from './OpenViduInternal/Events/Event';
export { ConnectionEvent } from './OpenViduInternal/Events/ConnectionEvent';
export { PublisherSpeakingEvent } from './OpenViduInternal/Events/PublisherSpeakingEvent';
export { RecordingEvent } from './OpenViduInternal/Events/RecordingEvent';
export { SessionDisconnectedEvent } from './OpenViduInternal/Events/SessionDisconnectedEvent';
export { SignalEvent } from './OpenViduInternal/Events/SignalEvent';
export { StreamEvent } from './OpenViduInternal/Events/StreamEvent';
export { StreamManagerEvent } from './OpenViduInternal/Events/StreamManagerEvent';
export { VideoElementEvent } from './OpenViduInternal/Events/VideoElementEvent';
export { StreamPropertyChangedEvent } from './OpenViduInternal/Events/StreamPropertyChangedEvent';
export { ConnectionPropertyChangedEvent } from './OpenViduInternal/Events/ConnectionPropertyChangedEvent';
export { FilterEvent } from './OpenViduInternal/Events/FilterEvent';
export { NetworkQualityLevelChangedEvent } from './OpenViduInternal/Events/NetworkQualityLevelChangedEvent';
export { SpeechToTextEvent } from './OpenViduInternal/Events/SpeechToTextEvent';
export { ExceptionEvent, ExceptionEventName } from './OpenViduInternal/Events/ExceptionEvent';
export { Capabilities } from './OpenViduInternal/Interfaces/Public/Capabilities';
export { Device } from './OpenViduInternal/Interfaces/Public/Device';
export { EventDispatcher } from './OpenVidu/EventDispatcher';
export { OpenViduAdvancedConfiguration } from './OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration';
export { PublisherProperties } from './OpenViduInternal/Interfaces/Public/PublisherProperties';
export { SignalOptions } from './OpenViduInternal/Interfaces/Public/SignalOptions';
export { StreamManagerVideo } from './OpenViduInternal/Interfaces/Public/StreamManagerVideo';
export { SubscriberProperties } from './OpenViduInternal/Interfaces/Public/SubscriberProperties';
export { EventMap } from './OpenViduInternal/Events/EventMap/EventMap';
export { SessionEventMap } from './OpenViduInternal/Events/EventMap/SessionEventMap';
export { StreamManagerEventMap } from './OpenViduInternal/Events/EventMap/StreamManagerEventMap';
export { PublisherEventMap } from './OpenViduInternal/Events/EventMap/PublisherEventMap';
export * from './OpenViduInternal/Events/Types/Types';
// Disable jsnlog when library is loaded
JL.setOptions({ enabled: false });

View File

@ -1,36 +0,0 @@
{
//"allowUnusedLabels": true,
"allowUnreachableCode": false,
"buildOnSave": false,
"compileOnSave": true,
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"emitBOM": false,
"forceConsistentCasingInFileNames": true,
"lib": [
"dom",
"es2015.promise",
"es5",
"scripthost"
],
"module": "commonjs",
"noFallthroughCasesInSwitch": true,
//"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
//"noUnusedLocals": true,
//"noUnusedParameters": true,
"outDir": "../../lib",
"preserveConstEnums": true,
"removeComments": true,
"rootDir": "./src",
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"sourceMap": true,
"strictNullChecks": true,
"suppressExcessPropertyErrors": true,
"suppressImplicitAnyIndexErrors": true,
"target": "es5"
}
}

View File

@ -1 +0,0 @@
/target/

View File

@ -1,14 +0,0 @@
[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![OpenVidu Tests](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml/badge.svg)](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml)
[![Documentation Status](https://readthedocs.org/projects/openvidu/badge/?version=stable)](https://docs.openvidu.io/en/stable/?badge=stable)
[![Docker badge](https://img.shields.io/docker/pulls/fiware/orion.svg)](https://hub.docker.com/r/openvidu/)
[![Support badge](https://img.shields.io/badge/support-sof-yellowgreen.svg)](https://openvidu.discourse.group/)
[![][OpenViduLogo]](https://openvidu.io)
openvidu-client
===
Internal Java client used by [openvidu-server](https://github.com/OpenVidu/openvidu/tree/master/openvidu-server). Can be used to implement an Android client.
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120

View File

@ -1,106 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.openvidu</groupId>
<artifactId>openvidu-parent</artifactId>
<version>2.0.0</version>
</parent>
<artifactId>openvidu-client</artifactId>
<version>1.1.0</version>
<packaging>jar</packaging>
<name>OpenVidu Client</name>
<description>
OpenVidu client library for the client-side of OpenVidu Server
</description>
<url>https://github.com/OpenVidu/openvidu</url>
<licenses>
<license>
<name>Apache 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<organization>
<name>OpenVidu</name>
<url>https://github.com/OpenVidu/openvidu</url>
</organization>
<scm>
<url>${openvidu.scm.url}</url>
<connection>scm:git:${openvidu.scm.connection}</connection>
<developerConnection>scm:git:${openvidu.scm.connection}</developerConnection>
<tag>develop</tag>
</scm>
<developers>
<developer>
<id>openvidu.io</id>
<name>-openvidu.io Community</name>
<organization>OpenVidu</organization>
<organizationUrl>https://openvidu.io</organizationUrl>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>org.kurento</groupId>
<artifactId>kurento-jsonrpc-client</artifactId>
<version>${version.kurento}</version>
</dependency>
<dependency>
<groupId>org.kurento</groupId>
<artifactId>kurento-jsonrpc-client-jetty</artifactId>
<version>${version.kurento}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${version.junit}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${version.mockito.core}</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>default</id>
<activation>
<property>
<name>default</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -1,217 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.openvidu.client;
import static io.openvidu.client.internal.ProtocolElements.CUSTOMREQUEST_METHOD;
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_METHOD;
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERID_PARAM;
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERSTREAMID_PARAM;
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERSTREAMS_PARAM;
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_ROOM_PARAM;
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_USER_PARAM;
import static io.openvidu.client.internal.ProtocolElements.LEAVEROOM_METHOD;
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_CANDIDATE_PARAM;
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_EPNAME_PARAM;
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_METHOD;
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_SDPMIDPARAM;
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_SDPMLINEINDEX_PARAM;
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_DOLOOPBACK_PARAM;
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_METHOD;
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_SDPANSWER_PARAM;
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_SDPOFFER_PARAM;
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_METHOD;
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SDPANSWER_PARAM;
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SDPOFFER_PARAM;
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SENDER_PARAM;
import static io.openvidu.client.internal.ProtocolElements.SENDMESSAGE_MESSAGE_PARAM;
import static io.openvidu.client.internal.ProtocolElements.SENDMESSAGE_ROOM_METHOD;
import static io.openvidu.client.internal.ProtocolElements.UNPUBLISHVIDEO_METHOD;
import static io.openvidu.client.internal.ProtocolElements.UNSUBSCRIBEFROMVIDEO_METHOD;
import static io.openvidu.client.internal.ProtocolElements.UNSUBSCRIBEFROMVIDEO_SENDER_PARAM;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.kurento.jsonrpc.client.JsonRpcClient;
import org.kurento.jsonrpc.client.JsonRpcClientWebSocket;
import org.kurento.jsonrpc.client.JsonRpcWSConnectionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.openvidu.client.internal.JsonRoomUtils;
import io.openvidu.client.internal.Notification;
/**
* Java client for the room server.
*
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
*/
public class OpenViduClient {
private static final Logger log = LoggerFactory.getLogger(OpenViduClient.class);
private JsonRpcClient client;
private ServerJsonRpcHandler handler;
public OpenViduClient(String wsUri) {
this(new JsonRpcClientWebSocket(wsUri, new JsonRpcWSConnectionListener() {
@Override
public void reconnected(boolean sameServer) {
}
@Override
public void disconnected() {
log.warn("JsonRpcWebsocket connection: Disconnected");
}
@Override
public void connectionFailed() {
log.warn("JsonRpcWebsocket connection: Connection failed");
}
@Override
public void connected() {
}
@Override
public void reconnecting() {
log.warn("JsonRpcWebsocket connection: is reconnecting");
}
}, new SslContextFactory(true)));
}
public OpenViduClient(JsonRpcClient client) {
this.client = client;
this.handler = new ServerJsonRpcHandler();
this.client.setServerRequestHandler(this.handler);
}
public OpenViduClient(JsonRpcClient client, ServerJsonRpcHandler handler) {
this.client = client;
this.handler = handler;
this.client.setServerRequestHandler(this.handler);
}
public void close() throws IOException {
this.client.close();
}
public Map<String, List<String>> joinRoom(String roomName, String userName)
throws IOException {
JsonObject params = new JsonObject();
params.addProperty(JOINROOM_ROOM_PARAM, roomName);
params.addProperty(JOINROOM_USER_PARAM, userName);
JsonElement result = client.sendRequest(JOINROOM_METHOD, params);
Map<String, List<String>> peers = new HashMap<String, List<String>>();
JsonArray jsonPeers = JsonRoomUtils.getResponseProperty(result, "value", JsonArray.class);
if (jsonPeers.size() > 0) {
Iterator<JsonElement> peerIt = jsonPeers.iterator();
while (peerIt.hasNext()) {
JsonElement peer = peerIt.next();
String peerId = JsonRoomUtils.getResponseProperty(peer, JOINROOM_PEERID_PARAM,
String.class);
List<String> streams = new ArrayList<String>();
JsonArray jsonStreams = JsonRoomUtils.getResponseProperty(peer, JOINROOM_PEERSTREAMS_PARAM,
JsonArray.class, true);
if (jsonStreams != null) {
Iterator<JsonElement> streamIt = jsonStreams.iterator();
while (streamIt.hasNext()) {
streams.add(JsonRoomUtils.getResponseProperty(streamIt.next(),
JOINROOM_PEERSTREAMID_PARAM, String.class));
}
}
peers.put(peerId, streams);
}
}
return peers;
}
public void leaveRoom() throws IOException {
client.sendRequest(LEAVEROOM_METHOD, new JsonObject());
}
public String publishVideo(String sdpOffer, boolean doLoopback) throws IOException {
JsonObject params = new JsonObject();
params.addProperty(PUBLISHVIDEO_SDPOFFER_PARAM, sdpOffer);
params.addProperty(PUBLISHVIDEO_DOLOOPBACK_PARAM, doLoopback);
JsonElement result = client.sendRequest(PUBLISHVIDEO_METHOD, params);
return JsonRoomUtils.getResponseProperty(result, PUBLISHVIDEO_SDPANSWER_PARAM, String.class);
}
public void unpublishVideo() throws IOException {
client.sendRequest(UNPUBLISHVIDEO_METHOD, new JsonObject());
}
// sender should look like 'username_streamId'
public String receiveVideoFrom(String sender, String sdpOffer) throws IOException {
JsonObject params = new JsonObject();
params.addProperty(RECEIVEVIDEO_SENDER_PARAM, sender);
params.addProperty(RECEIVEVIDEO_SDPOFFER_PARAM, sdpOffer);
JsonElement result = client.sendRequest(RECEIVEVIDEO_METHOD, params);
return JsonRoomUtils.getResponseProperty(result, RECEIVEVIDEO_SDPANSWER_PARAM, String.class);
}
// sender should look like 'username_streamId'
public void unsubscribeFromVideo(String sender) throws IOException {
JsonObject params = new JsonObject();
params.addProperty(UNSUBSCRIBEFROMVIDEO_SENDER_PARAM, sender);
client.sendRequest(UNSUBSCRIBEFROMVIDEO_METHOD, params);
}
public void onIceCandidate(String endpointName, String candidate, String sdpMid,
int sdpMLineIndex) throws IOException {
JsonObject params = new JsonObject();
params.addProperty(ONICECANDIDATE_EPNAME_PARAM, endpointName);
params.addProperty(ONICECANDIDATE_CANDIDATE_PARAM, candidate);
params.addProperty(ONICECANDIDATE_SDPMIDPARAM, sdpMid);
params.addProperty(ONICECANDIDATE_SDPMLINEINDEX_PARAM, sdpMLineIndex);
client.sendRequest(ONICECANDIDATE_METHOD, params);
}
public void sendMessage(String userName, String roomName, String message) throws IOException {
JsonObject params = new JsonObject();
params.addProperty(SENDMESSAGE_MESSAGE_PARAM, message);
client.sendRequest(SENDMESSAGE_ROOM_METHOD, params);
}
public JsonElement customRequest(JsonObject customReqParams) throws IOException {
return client.sendRequest(CUSTOMREQUEST_METHOD, customReqParams);
}
/**
* Polls the notifications list maintained by this client to obtain new events sent by server.
* This method blocks until there is a notification to return. This is a one-time operation for
* the returned element.
*
* @return a server notification object, null when interrupted while waiting
*/
public Notification getServerNotification() {
return this.handler.getNotification();
}
}

View File

@ -1,88 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.openvidu.client;
import org.kurento.jsonrpc.JsonRpcErrorException;
public class OpenViduException extends JsonRpcErrorException {
private static final long serialVersionUID = 1L;
public static enum Code {
GENERIC_ERROR_CODE(999), WRONG_PATH_CODE(998), WRONG_OPENVIDU_EDITION_ERROR_CODE(997),
SERVICE_NOT_ENABLED_ERROR_CODE(996),
TRANSPORT_ERROR_CODE(803), TRANSPORT_RESPONSE_ERROR_CODE(802), TRANSPORT_REQUEST_ERROR_CODE(801),
MEDIA_TYPE_STREAM_INCOMPATIBLE_WITH_RECORDING_PROPERTIES_ERROR_CODE(309),
MEDIA_TYPE_RECORDING_PROPERTIES_ERROR_CODE(308), MEDIA_MUTE_ERROR_CODE(307),
MEDIA_NOT_A_WEB_ENDPOINT_ERROR_CODE(306), MEDIA_RTP_ENDPOINT_ERROR_CODE(305),
MEDIA_WEBRTC_ENDPOINT_ERROR_CODE(304), MEDIA_ENDPOINT_ERROR_CODE(303), MEDIA_SDP_ERROR_CODE(302),
MEDIA_GENERIC_ERROR_CODE(301),
ROOM_CANNOT_BE_CREATED_ERROR_CODE(204), ROOM_CLOSED_ERROR_CODE(203), ROOM_NOT_FOUND_ERROR_CODE(202),
ROOM_GENERIC_ERROR_CODE(201),
USER_ALREADY_STREAMING_ERROR_CODE(106), USER_NOT_STREAMING_ERROR_CODE(105),
EXISTING_USER_IN_ROOM_ERROR_CODE(104), USER_CLOSED_ERROR_CODE(103), USER_NOT_FOUND_ERROR_CODE(102),
USER_GENERIC_ERROR_CODE(10),
USER_UNAUTHORIZED_ERROR_CODE(401), ROLE_NOT_FOUND_ERROR_CODE(402), SESSIONID_CANNOT_BE_CREATED_ERROR_CODE(403),
TOKEN_CANNOT_BE_CREATED_ERROR_CODE(404), EXISTING_FILTER_ALREADY_APPLIED_ERROR_CODE(405),
FILTER_NOT_APPLIED_ERROR_CODE(406), FILTER_EVENT_LISTENER_NOT_FOUND_ERROR_CODE(407),
PUBLISHER_ENDPOINT_NOT_FOUND_ERROR_CODE(408),
USER_METADATA_FORMAT_INVALID_ERROR_CODE(500),
SIGNAL_FORMAT_INVALID_ERROR_CODE(600), SIGNAL_TO_INVALID_ERROR_CODE(601),
BROADCAST_CONCURRENT_ERROR_CODE(711), BROADCAST_START_ERROR_CODE(710), DOCKER_NOT_FOUND(709), RECORDING_PATH_NOT_VALID(708), RECORDING_FILE_EMPTY_ERROR(707),
RECORDING_DELETE_ERROR_CODE(706), RECORDING_LIST_ERROR_CODE(705), RECORDING_STOP_ERROR_CODE(704),
RECORDING_START_ERROR_CODE(703), RECORDING_REPORT_ERROR_CODE(702), RECORDING_COMPLETION_ERROR_CODE(701),
FORCED_CODEC_NOT_FOUND_IN_SDPOFFER(800),
MEDIA_NODE_NOT_FOUND(900), MEDIA_NODE_STATUS_WRONG(901), MEDIA_NODE_CONNECTION_ERROR_CODE(902);
private int value;
private Code(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
private Code code = Code.GENERIC_ERROR_CODE;
public OpenViduException(Code code, String message) {
super(code.getValue(), message);
this.code = code;
}
public int getCodeValue() {
return code.getValue();
}
@Override
public String toString() {
return super.toString();
}
}

View File

@ -1,222 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.openvidu.client;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.kurento.jsonrpc.DefaultJsonRpcHandler;
import org.kurento.jsonrpc.Transaction;
import org.kurento.jsonrpc.message.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.openvidu.client.internal.IceCandidate;
import io.openvidu.client.internal.IceCandidateInfo;
import io.openvidu.client.internal.JsonRoomUtils;
import io.openvidu.client.internal.MediaErrorInfo;
import io.openvidu.client.internal.Notification;
import io.openvidu.client.internal.ParticipantEvictedInfo;
import io.openvidu.client.internal.ParticipantJoinedInfo;
import io.openvidu.client.internal.ParticipantLeftInfo;
import io.openvidu.client.internal.ParticipantPublishedInfo;
import io.openvidu.client.internal.ParticipantUnpublishedInfo;
import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.client.internal.RoomClosedInfo;
import io.openvidu.client.internal.SendMessageInfo;
/**
* Service that handles server JSON-RPC events.
*
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
*/
public class ServerJsonRpcHandler extends DefaultJsonRpcHandler<JsonObject> {
private static final Logger log = LoggerFactory.getLogger(ServerJsonRpcHandler.class);
private BlockingQueue<Notification> notifications = new ArrayBlockingQueue<Notification>(100);
@Override
public void handleRequest(Transaction transaction, Request<JsonObject> request) throws Exception {
Notification notif = null;
try {
switch (request.getMethod()) {
case ProtocolElements.ICECANDIDATE_METHOD:
notif = iceCandidate(transaction, request);
break;
case ProtocolElements.MEDIAERROR_METHOD:
notif = mediaError(transaction, request);
break;
case ProtocolElements.PARTICIPANTJOINED_METHOD:
notif = participantJoined(transaction, request);
break;
case ProtocolElements.PARTICIPANTLEFT_METHOD:
notif = participantLeft(transaction, request);
break;
case ProtocolElements.PARTICIPANTEVICTED_METHOD:
notif = participantEvicted(transaction, request);
break;
case ProtocolElements.PARTICIPANTPUBLISHED_METHOD:
notif = participantPublished(transaction, request);
break;
case ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD:
notif = participantUnpublished(transaction, request);
break;
case ProtocolElements.ROOMCLOSED_METHOD:
notif = roomClosed(transaction, request);
break;
case ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD:
notif = participantSendMessage(transaction, request);
break;
default:
throw new Exception("Unrecognized request " + request.getMethod());
}
} catch (Exception e) {
log.error("Exception processing request {}", request, e);
transaction.sendError(e);
return;
}
if (notif != null) {
try {
notifications.put(notif);
log.debug("Enqueued notification {}", notif);
} catch (InterruptedException e) {
log.warn("Interrupted when enqueuing notification {}", notif, e);
}
}
}
private Notification participantSendMessage(Transaction transaction,
Request<JsonObject> request) {
String data = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTSENDMESSAGE_DATA_PARAM, String.class);
String from = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTSENDMESSAGE_FROM_PARAM, String.class);
String type = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTSENDMESSAGE_TYPE_PARAM, String.class);
SendMessageInfo eventInfo = new SendMessageInfo(data, from, type);
log.debug("Recvd send message event {}", eventInfo);
return eventInfo;
}
private Notification roomClosed(Transaction transaction, Request<JsonObject> request) {
String room = JsonRoomUtils.getRequestParam(request, ProtocolElements.ROOMCLOSED_ROOM_PARAM,
String.class);
RoomClosedInfo eventInfo = new RoomClosedInfo(room);
log.debug("Recvd room closed event {}", eventInfo);
return eventInfo;
}
private Notification participantUnpublished(Transaction transaction,
Request<JsonObject> request) {
String name = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTUNPUBLISHED_NAME_PARAM, String.class);
ParticipantUnpublishedInfo eventInfo = new ParticipantUnpublishedInfo(name);
log.debug("Recvd participant unpublished event {}", eventInfo);
return eventInfo;
}
private Notification participantPublished(Transaction transaction, Request<JsonObject> request) {
String id = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTPUBLISHED_USER_PARAM, String.class);
JsonArray jsonStreams = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTPUBLISHED_STREAMS_PARAM, JsonArray.class);
Iterator<JsonElement> streamIt = jsonStreams.iterator();
List<String> streams = new ArrayList<String>();
while (streamIt.hasNext()) {
streams.add(JsonRoomUtils.getResponseProperty(streamIt.next(),
ProtocolElements.PARTICIPANTPUBLISHED_STREAMID_PARAM, String.class));
}
ParticipantPublishedInfo eventInfo = new ParticipantPublishedInfo(id, streams);
log.debug("Recvd published event {}", eventInfo);
return eventInfo;
}
private Notification participantEvicted(Transaction transaction, Request<JsonObject> request) {
ParticipantEvictedInfo eventInfo = new ParticipantEvictedInfo();
log.debug("Recvd participant evicted event {}", eventInfo);
return eventInfo;
}
private Notification participantLeft(Transaction transaction, Request<JsonObject> request) {
String name = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTLEFT_NAME_PARAM, String.class);
ParticipantLeftInfo eventInfo = new ParticipantLeftInfo(name);
log.debug("Recvd participant left event {}", eventInfo);
return eventInfo;
}
private Notification participantJoined(Transaction transaction, Request<JsonObject> request) {
String id = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTJOINED_USER_PARAM, String.class);
ParticipantJoinedInfo eventInfo = new ParticipantJoinedInfo(id);
log.debug("Recvd participant joined event {}", eventInfo);
return eventInfo;
}
private Notification mediaError(Transaction transaction, Request<JsonObject> request) {
String description = JsonRoomUtils.getRequestParam(request,
ProtocolElements.MEDIAERROR_ERROR_PARAM, String.class);
MediaErrorInfo eventInfo = new MediaErrorInfo(description);
log.debug("Recvd media error event {}", eventInfo);
return eventInfo;
}
private Notification iceCandidate(Transaction transaction, Request<JsonObject> request) {
String candidate = JsonRoomUtils.getRequestParam(request,
ProtocolElements.ICECANDIDATE_CANDIDATE_PARAM, String.class);
String sdpMid = JsonRoomUtils.getRequestParam(request,
ProtocolElements.ICECANDIDATE_SDPMID_PARAM, String.class);
int sdpMLineIndex = JsonRoomUtils.getRequestParam(request,
ProtocolElements.ICECANDIDATE_SDPMLINEINDEX_PARAM, Integer.class);
IceCandidate iceCandidate = new IceCandidate(candidate, sdpMid, sdpMLineIndex);
String endpoint = JsonRoomUtils.getRequestParam(request,
ProtocolElements.ICECANDIDATE_EPNAME_PARAM, String.class);
IceCandidateInfo eventInfo = new IceCandidateInfo(iceCandidate, endpoint);
log.debug("Recvd ICE candidate event {}", eventInfo);
return eventInfo;
}
/**
* Blocks until an element is available and then returns it by removing it from the queue.
*
* @return a {@link Notification} from the queue, null when interrupted
* @see BlockingQueue#take()
*/
public Notification getNotification() {
try {
Notification notif = notifications.take();
log.debug("Dequeued notification {}", notif);
return notif;
} catch (InterruptedException e) {
log.info("Interrupted while polling notifications' queue");
return null;
}
}
}

View File

@ -1,33 +0,0 @@
package io.openvidu.client.internal;
public class IceCandidate {
private String candidate;
private String sdpMid;
private int sdpMLineIndex;
public IceCandidate(String candidate, String sdpMid, int sdpMLineIndex) {
super();
this.candidate = candidate;
this.sdpMid = sdpMid;
this.sdpMLineIndex = sdpMLineIndex;
}
public String getCandidate() {
return candidate;
}
public String getSdpMid() {
return sdpMid;
}
public int getSdpMLineIndex() {
return sdpMLineIndex;
}
@Override
public String toString() {
return "IceCandidate [candidate=" + candidate + ", sdpMid=" + sdpMid + ", sdpMLineIndex="
+ sdpMLineIndex + "]";
}
}

View File

@ -1,70 +0,0 @@
/*
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.openvidu.client.internal;
/**
* @see Notification
*
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
*/
public class IceCandidateInfo extends Notification {
private IceCandidate iceCandidate;
private String endpointName;
public IceCandidateInfo(IceCandidate iceCandidate, String endpointName) {
super(ProtocolElements.ICECANDIDATE_METHOD);
this.iceCandidate = iceCandidate;
this.endpointName = endpointName;
}
public IceCandidate getIceCandidate() {
return iceCandidate;
}
public void setIceCandidate(IceCandidate iceCandidate) {
this.iceCandidate = iceCandidate;
}
public String getEndpointName() {
return endpointName;
}
public void setEndpointName(String endpointName) {
this.endpointName = endpointName;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
if (getMethod() != null) {
builder.append("method=").append(getMethod()).append(", ");
}
if (endpointName != null) {
builder.append("endpointName=").append(endpointName).append(", ");
}
if (iceCandidate != null) {
builder.append("iceCandidate=[sdpMLineIndex= ").append(iceCandidate.getSdpMLineIndex())
.append(", sdpMid=").append(iceCandidate.getSdpMid()).append(", candidate=")
.append(iceCandidate.getCandidate()).append("]");
}
builder.append("]");
return builder.toString();
}
}

Some files were not shown because too many files have changed in this diff Show More