Compare commits

..

No commits in common. "master" and "v1.1.2" have entirely different histories.

761 changed files with 198996 additions and 115037 deletions

View File

@ -1,55 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
<!--
IMPORTANT!!! IMPORTANT!!! IMPORTANT!!! IMPORTANT!!!
YOU SHOULD NEVER DELETE THE CONTENT OF THIS TEMPLATE WHEN OPENING AN ISSUE. IF YOUR QUESTION DOES NOT FIT THE TEMPLATE THEN IT MOST PROBABLY BELONGS TO OPENVIDU FORUM (https://openvidu.discourse.group/)
Hi! First of all, welcome to OpenVidu issue tracker. Please, carefully read the two points below before opening a new issue:
1. Is your question really a bug? In other words: did you actually get an unexpected behavior from OpenVidu platform? If you are not sure about the answer or you just want support for a particular use case, you can post a new question in our official Discourse Forum (https://openvidu.discourse.group/). OpenVidu community or a team's member will reply ASAP.
2. If your question is undoubtedly a bug, check that there's no other issue (opened or closed) talking about it. Your question may have already been answered! If you cannot find anything useful, please fill the report below.
-->
**Describe the bug**
A few words describing what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Wrong current behavior**
A clear and concise description of what is actually happening instead of the expected behavior.
**OpenVidu tutorial where to replicate the error**
This is an EXTREMELY IMPORTANT STEP. If we are able to replicate the error in any of the official [OpenVidu Tutorials](https://github.com/OpenVidu/openvidu-tutorials) or [OpenVidu Demos](https://github.com/OpenVidu), then we will be able to quickly fix it. If you are getting the error in your own application, please try to add the necessary changes to the most similar tutorial so it fails with the same error (try to keep those changes as contained as possible, so that the original tutorial maintains its integrity). Once you have an application to replicate the error, explain in detail the steps to get it like this:
1. Clone repository [...]
2. Compile the application like this [...]
3. Run OpenVidu Server like this [...]
4. Run the application like this [...]
5. Join 1 user... Publish a video stream [...]
6. See error
**OpenVidu deployment info**
How is your OpenVidu Server instance deployed when you get the bug. A couple of possible examples are listed below:
- Docker container as explained in OpenVidu tutorials, run with command `docker run ...` on macOS Catalina 10.15.1
- AWS deployment as explained in OpenVidu Docs (https://docs.openvidu.io/en/stable/deployment/deploying-aws/)
> **IMPORTANT NOTE**: please, if you think the bug might be related to OpenVidu Server side, specify here if you are also getting the error by using OpenVidu Server Demos instance. This instance is publicly available (use it only for this test, because it is not secure!!!): **URL**: `https://demos.openvidu.io:4443`, **SECRET**: `MY_SECRET`
**Client device info (if applicable)**
Describe the client device(s) or platform(s) where you are able to replicate the error. For example:
- Chrome 78.0.3904.97 (Official Build) (64-bit) on Windows 10 (1903).
- Firefox Mobile 68.2.0 running on OnePlus 6 with Android 9.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here. For example, attach any useful logs related to the issue.

View File

@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: OpenVidu Discourse forum
url: https://openvidu.discourse.group/
about: If your question is about a certain feature or getting support for a specific use case, please use OpenVidu Discourse Group forum

View File

@ -1,478 +0,0 @@
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
e2e_virtual_backgrounds:
needs: test_setup
name: Virtual Backgrounds 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-virtual-backgrounds --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main

View File

@ -1,56 +0,0 @@
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

11
.gitignore vendored
View File

@ -4,10 +4,8 @@
/target
.classpath
.idea
.project
.settings
*.iml
*orig
.springBeans
*tmp/
@ -18,12 +16,3 @@ nbactions.xml
*bower_components/
.externalToolBuilders
*bin/
*/.vscode/*
.vscode/*
*/.sts4-cache/*
*/.project
*/.classpath
*/.settings/*
*/.tscache/*
.factorypath

3
.gitmodules vendored
View File

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

View File

@ -1,67 +0,0 @@
# Contribute
## Introduction
First, thank you for considering contributing to openvidu! It's people like you that make the open source community such a great community! 😊
We welcome any type of contribution, not only code. You can help with
- **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
- **Marketing**: writing blog posts, howto's, printing stickers, ...
- **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
- **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them.
- **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/openvidu).
## Your First Contribution
Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
## Submitting code
Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests.
## Code review process
The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge.
It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you?
## Financial contributions
We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/openvidu).
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
## Questions
If you have any questions, create an [issue](issue) (protip: do a quick search first to see if someone else didn't ask the same question before!).
You can also reach us at hello@openvidu.opencollective.com.
## Credits
### Contributors
Thank you to all the people who have already contributed to openvidu!
<a href="graphs/contributors"><img src="https://opencollective.com/openvidu/contributors.svg?width=890" /></a>
### Backers
Thank you to all our backers! [[Become a backer](https://opencollective.com/openvidu#backer)]
<a href="https://opencollective.com/openvidu#backers" target="_blank"><img src="https://opencollective.com/openvidu/backers.svg?width=890"></a>
### Sponsors
Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/openvidu#sponsor))
<a href="https://opencollective.com/openvidu/sponsor/0/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/1/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/2/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/3/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/4/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/5/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/6/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/7/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/8/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/9/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/9/avatar.svg"></a>
<!-- This `CONTRIBUTING.md` is based on @nayafia's template https://github.com/nayafia/contributing-template -->

2
NOTICE
View File

@ -1,4 +1,4 @@
(C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
(C) Copyright 2017 OpenVidu (http://openvidu.io)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,59 +1,13 @@
[![Backers on Open Collective](https://opencollective.com/openvidu/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/openvidu/sponsors/badge.svg)](#sponsors)
[![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)
[![Npm version](https://img.shields.io/npm/v/openvidu-browser?label=npm-version)](https://npmjs.org/package/openvidu-browser)
[![Npm downloads](https://img.shields.io/npm/dw/openvidu-browser?label=npm2-downloads)](https://npmjs.org/package/openvidu-browser)
[![Documentation badge](https://readthedocs.org/projects/fiware-orion/badge/?version=latest)](http://openvidu.io/docs/home/)
[![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://groups.google.com/forum/#!forum/openvidu)
[![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/openvidu/openvidu-server-kms.svg)](https://hub.docker.com/r/openvidu/openvidu-server-kms)
[![Support badge](https://img.shields.io/badge/support-sof-yellowgreen.svg)](https://openvidu.discourse.group/)
[![Twitter Follow](https://img.shields.io/twitter/follow/openvidu.svg?style=social)](https://twitter.com/openvidu)
[![][OpenViduLogo]](https://openvidu.io)
[![][OpenViduLogo]](http://openvidu.io)
openvidu
===
Visit [openvidu.io](https://openvidu.io)
## Community Forum
Visit [OpenVidu Community Forum](https://openvidu.discourse.group/)
Visit [openvidu.io](http://openvidu.io)
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120
## Contributors
This project exists thanks to all the people who contribute.
<a href="https://github.com/OpenVidu/openvidu/contributors"><img src="https://opencollective.com/openvidu/contributors.svg?width=890&button=false" /></a>
## Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/openvidu#backer)]
<a href="https://opencollective.com/openvidu#backers" target="_blank"><img src="https://opencollective.com/openvidu/backers.svg?width=890"></a>
## Acknowledgments
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://docs.openvidu.io/en/stable/img/logos/support.jpg">
## Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/openvidu#sponsor)]
<a href="https://opencollective.com/openvidu/sponsor/0/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/1/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/2/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/3/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/4/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/5/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/6/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/7/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/8/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/openvidu/sponsor/9/website" target="_blank"><img src="https://opencollective.com/openvidu/sponsor/9/avatar.svg"></a>

4
openvidu-browser/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/node_modules/
/yarn.lock
/npm-debug.log
/lib/

View File

@ -0,0 +1,38 @@
{
"name": "openvidu-browser",
"version": "1.1.2",
"description": "OpenVidu Browser",
"author": "OpenVidu",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git://github.com/OpenVidu/openvidu"
},
"main": "lib/OpenVidu/index.js",
"types": "lib/OpenVidu/index.d.ts",
"scripts": {
"browserify": "VERSION=${VERSION:-}; cd ts/OpenVidu && browserify Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../../static/js/openvidu-browser-$VERSION.js -v",
"browserify-prod": "VERSION=${VERSION:-}; cd ts/OpenVidu && browserify --debug Main.ts -p [ tsify ] --exclude kurento-browser-extensions | uglifyjs --source-map content=inline --output ../../static/js/openvidu-browser-$VERSION.min.js",
"updatetsc": "cd ts/OpenViduInternal && tsc && cd ../OpenVidu && tsc && cd ../.. && tsc --declaration ts/OpenVidu/index.ts --outDir lib --sourceMap && tsc --declaration ts/OpenVidu/Main.ts --outDir lib --sourceMap",
"test": "echo \"Error: no test specified\" && exit 1",
"prepublish": "cd ts/OpenViduInternal && tsc && cd ../OpenVidu && tsc && cd ../.. && tsc --declaration ts/OpenVidu/index.ts --outDir lib --sourceMap && tsc --declaration ts/OpenVidu/Main.ts --outDir lib --sourceMap"
},
"dependencies": {
"wolfy87-eventemitter": "4.2.9",
"@types/wolfy87-eventemitter": "4.2.31",
"webrtc-adapter": "5.0.4",
"uuid": "~2.0.1",
"freeice": "2.2.0",
"inherits": "^2.0.3",
"merge": "^1.2.0",
"ua-parser-js": "^0.7.7",
"hark": "1.1.6",
"sdp-translator": "0.1.24"
},
"devDependencies": {
"typescript": "2.6.1",
"browserify": "14.5.0",
"tsify": "3.0.3",
"uglify-js": "3.1.9"
}
}

View File

@ -0,0 +1,11 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"command": "tsc",
"isShellCommand": true,
"args": ["-w", "-p", "."],
"showOutput": "silent",
"isWatching": true,
"problemMatcher": "$tsc-watch"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
console.error("Error: Cannot find module 'kurento-browser-extensions' from '/home/mica/Data/Kurento/OpenVidu/git2/openvidu/openvidu-browser/src/main/resources/node_modules/kurento-utils/lib'");

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,66 @@
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

@ -0,0 +1,21 @@
/*
* (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

@ -0,0 +1,276 @@
/*
* (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');
Date.now = Date.now || function() {
return +new Date;
};
var PING_INTERVAL = 5000;
var RECONNECTING = 'RECONNECTING';
var CONNECTED = 'CONNECTED';
var DISCONNECTED = 'DISCONNECTED';
var Logger = console;
/**
*
* heartbeat: interval in ms for each heartbeat message,
* sendCloseMessage : true / false, before closing the connection, it sends a closeSession message
* <pre>
* ws : {
* uri : URI to conntect to,
* useSockJS : true (use SockJS) / false (use WebSocket) by default,
* onconnected : callback method to invoke when connection is successful,
* ondisconnect : callback method to invoke when the connection is lost,
* onreconnecting : callback method to invoke when the client is reconnecting,
* onreconnected : callback method to invoke when the client succesfully 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;
}
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;
enabledPings = true;
updateNotReconnectIfLessThan();
usePing();
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;
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) {
if (method !== 'ping') {
Logger.debug('Request: method:' + method + " params:" + JSON.stringify(params));
}
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();
}
}
}
this.close = function() {
Logger.debug("Closing jsonRpcClient explicitly by client");
if (pingInterval != undefined) {
Logger.debug("Clearing ping interval");
clearInterval(pingInterval);
}
pingPongStarted = false;
enabledPings = false;
if (configuration.sendCloseMessage) {
Logger.debug("Sending close message")
this.send('closeSession', null, function(error, result) {
if (error) {
Logger.error("Error sending close message: " + JSON.stringify(error));
}
ws.close();
});
} else {
ws.close();
}
}
// This method is only for testing
this.forceClose = function(millis) {
ws.forceClose(millis);
}
this.reconnect = function() {
ws.reconnectWs();
}
}
module.exports = JsonRpcClient;

View File

@ -0,0 +1,21 @@
/*
* (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

@ -0,0 +1,242 @@
/*
* (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 BrowserWebSocket = global.WebSocket || global.MozWebSocket;
var Logger = console;
/**
* Get either the `WebSocket` or `MozWebSocket` globals
* in the browser or try to resolve WebSocket-compatible
* interface exposed by `ws` for Node-like environment.
*/
/*var WebSocket = BrowserWebSocket;
if (!WebSocket && typeof window === 'undefined') {
try {
WebSocket = require('ws');
} catch (e) { }
}*/
//var SockJS = require('sockjs-client');
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,
useSockJS : true (use SockJS) / false (use WebSocket) by default,
onconnected : callback method to invoke when connection is successful,
ondisconnect : callback method to invoke when the connection is lost,
onreconnecting : callback method to invoke when the client is reconnecting,
onreconnected : callback method to invoke when the client succesfully reconnects,
};
*/
function WebSocketWithReconnection(config) {
var closing = false;
var registerMessageHandler;
var wsUri = config.uri;
var useSockJS = config.useSockJS;
var reconnecting = false;
var forcingDisconnection = false;
var ws;
if (useSockJS) {
ws = new SockJS(wsUri);
} else {
ws = new WebSocket(wsUri);
}
ws.onopen = function() {
logConnected(ws, wsUri);
if (config.onconnected) {
config.onconnected();
}
};
ws.onerror = function(error) {
Logger.error("Could not connect to " + wsUri + " (invoking onerror if defined)", error);
if (config.onerror) {
config.onerror(error);
}
};
function logConnected(ws, wsUri) {
try {
Logger.debug("WebSocket connected to " + wsUri);
} catch (e) {
Logger.error(e);
}
}
var reconnectionOnClose = function() {
if (ws.readyState === CLOSED) {
if (closing) {
Logger.debug("Connection closed by user");
} else {
Logger.debug("Connection closed unexpectecly. Reconnecting...");
reconnectToSameUri(MAX_RETRIES, 1);
}
} else {
Logger.debug("Close callback from previous websocket. Ignoring it");
}
};
ws.onclose = reconnectionOnClose;
function reconnectToSameUri(maxRetries, numRetries) {
Logger.debug("reconnectToSameUri (attempt #" + numRetries + ", max=" + maxRetries + ")");
if (numRetries === 1) {
if (reconnecting) {
Logger.warn("Trying to reconnectToNewUri when reconnecting... Ignoring this reconnection.")
return;
} else {
reconnecting = true;
}
if (config.onreconnecting) {
config.onreconnecting();
}
}
if (forcingDisconnection) {
reconnectToNewUri(maxRetries, numRetries, wsUri);
} else {
if (config.newWsUriOnReconnection) {
config.newWsUriOnReconnection(function(error, newWsUri) {
if (error) {
Logger.debug(error);
setTimeout(function() {
reconnectToSameUri(maxRetries, numRetries + 1);
}, RETRY_TIME_MS);
} else {
reconnectToNewUri(maxRetries, numRetries, newWsUri);
}
})
} else {
reconnectToNewUri(maxRetries, numRetries, wsUri);
}
}
}
// TODO Test retries. How to force not connection?
function reconnectToNewUri(maxRetries, numRetries, reconnectWsUri) {
Logger.debug("Reconnection attempt #" + numRetries);
ws.close();
wsUri = reconnectWsUri || wsUri;
var newWs;
if (useSockJS) {
newWs = new SockJS(wsUri);
} else {
newWs = new WebSocket(wsUri);
}
newWs.onopen = function() {
Logger.debug("Reconnected after " + numRetries + " attempts...");
logConnected(newWs, wsUri);
reconnecting = false;
registerMessageHandler();
if (config.onreconnected()) {
config.onreconnected();
}
newWs.onclose = reconnectionOnClose;
};
var onErrorOrClose = function(error) {
Logger.warn("Reconnection error: ", error);
if (numRetries === maxRetries) {
if (config.ondisconnect) {
config.ondisconnect();
}
} else {
setTimeout(function() {
reconnectToSameUri(maxRetries, numRetries + 1);
}, RETRY_TIME_MS);
}
};
newWs.onerror = onErrorOrClose;
ws = newWs;
}
this.close = function() {
closing = true;
ws.close();
};
// This method is only for testing
this.forceClose = function(millis) {
Logger.debug("Testing: Force WebSocket close");
if (millis) {
Logger.debug("Testing: Change wsUri for " + millis + " millis to simulate net failure");
var goodWsUri = wsUri;
wsUri = "wss://21.234.12.34.4:443/";
forcingDisconnection = true;
setTimeout(function() {
Logger.debug("Testing: Recover good wsUri " + goodWsUri);
wsUri = goodWsUri;
forcingDisconnection = false;
}, millis);
}
ws.close();
};
this.reconnectWs = function() {
Logger.debug("reconnectWs");
reconnectToSameUri(MAX_RETRIES, 1, wsUri);
};
this.send = function(message) {
ws.send(message);
};
this.addEventListener = function(type, callback) {
registerMessageHandler = function() {
ws.addEventListener(type, callback);
};
registerMessageHandler();
};
}
module.exports = WebSocketWithReconnection;

View File

@ -0,0 +1,822 @@
/*
* (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();
// 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

@ -0,0 +1,103 @@
/**
* 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

@ -0,0 +1,13 @@
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

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

View File

@ -0,0 +1,777 @@
/*
* (C) Copyright 2014-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.
*/
var freeice = require('freeice')
var inherits = require('inherits')
var UAParser = require('ua-parser-js')
var uuid = require('uuid')
var hark = require('hark')
var EventEmitter = require('events').EventEmitter
var recursive = require('merge').recursive.bind(undefined, true)
var sdpTranslator = require('sdp-translator')
var logger = window.Logger || console
// var gUM = navigator.mediaDevices.getUserMedia || function (constraints) {
// return new Promise(navigator.getUserMedia(constraints, function (stream) {
// videoStream = stream
// start()
// }).eror(callback));
// }
/*try {
require('kurento-browser-extensions')
} catch (error) {
if (typeof getScreenConstraints === 'undefined') {
logger.warn('screen sharing is not available')
getScreenConstraints = function getScreenConstraints(sendSource, callback) {
callback(new Error("This library is not enabled for screen sharing"))
}
}
}*/
var MEDIA_CONSTRAINTS = {
audio: true,
video: {
width: 640,
framerate: 15
}
}
// Somehow, the UAParser constructor gets an empty window object.
// We need to pass the user agent string in order to get information
var ua = (window && window.navigator) ? window.navigator.userAgent : ''
var parser = new UAParser(ua)
var browser = parser.getBrowser()
var usePlanB = false
if (browser.name === 'Chrome' || browser.name === 'Chromium') {
logger.debug(browser.name + ": using SDP PlanB")
usePlanB = true
}
function noop(error) {
if (error) logger.error(error)
}
function trackStop(track) {
track.stop && track.stop()
}
function streamStop(stream) {
stream.getTracks().forEach(trackStop)
}
/**
* Returns a string representation of a SessionDescription object.
*/
var dumpSDP = function (description) {
if (typeof description === 'undefined' || description === null) {
return ''
}
return 'type: ' + description.type + '\r\n' + description.sdp
}
function bufferizeCandidates(pc, onerror) {
var candidatesQueue = []
pc.addEventListener('signalingstatechange', function () {
if (this.signalingState === 'stable') {
while (candidatesQueue.length) {
var entry = candidatesQueue.shift()
this.addIceCandidate(entry.candidate, entry.callback, entry.callback)
}
}
})
return function (candidate, callback) {
callback = callback || onerror
switch (pc.signalingState) {
case 'closed':
callback(new Error('PeerConnection object is closed'));
break;
case 'stable':
if (pc.remoteDescription) {
pc.addIceCandidate(candidate, callback, callback);
}
break;
default:
candidatesQueue.push({
candidate: candidate,
callback: callback
})
}
}
}
/* Simulcast utilities */
function removeFIDFromOffer(sdp) {
var n = sdp.indexOf("a=ssrc-group:FID");
if (n > 0) {
return sdp.slice(0, n);
} else {
return sdp;
}
}
function getSimulcastInfo(videoStream) {
var videoTracks = videoStream.getVideoTracks();
if (!videoTracks.length) {
logger.warn('No video tracks available in the video stream')
return ''
}
var lines = [
'a=x-google-flag:conference',
'a=ssrc-group:SIM 1 2 3',
'a=ssrc:1 cname:localVideo',
'a=ssrc:1 msid:' + videoStream.id + ' ' + videoTracks[0].id,
'a=ssrc:1 mslabel:' + videoStream.id,
'a=ssrc:1 label:' + videoTracks[0].id,
'a=ssrc:2 cname:localVideo',
'a=ssrc:2 msid:' + videoStream.id + ' ' + videoTracks[0].id,
'a=ssrc:2 mslabel:' + videoStream.id,
'a=ssrc:2 label:' + videoTracks[0].id,
'a=ssrc:3 cname:localVideo',
'a=ssrc:3 msid:' + videoStream.id + ' ' + videoTracks[0].id,
'a=ssrc:3 mslabel:' + videoStream.id,
'a=ssrc:3 label:' + videoTracks[0].id
];
lines.push('');
return lines.join('\n');
}
/**
* Wrapper object of an RTCPeerConnection. This object is aimed to simplify the
* development of WebRTC-based applications.
*
* @constructor module:kurentoUtils.WebRtcPeer
*
* @param {String} mode Mode in which the PeerConnection will be configured.
* Valid values are: 'recv', 'send', and 'sendRecv'
* @param localVideo Video tag for the local stream
* @param remoteVideo Video tag for the remote stream
* @param {MediaStream} videoStream Stream to be used as primary source
* (typically video and audio, or only video if combined with audioStream) for
* localVideo and to be added as stream to the RTCPeerConnection
* @param {MediaStream} audioStream Stream to be used as second source
* (typically for audio) for localVideo and to be added as stream to the
* RTCPeerConnection
*/
function WebRtcPeer(mode, options, callback) {
if (!(this instanceof WebRtcPeer)) {
return new WebRtcPeer(mode, options, callback)
}
WebRtcPeer.super_.call(this)
if (options instanceof Function) {
callback = options
options = undefined
}
options = options || {}
callback = (callback || noop).bind(this)
var self = this
var localVideo = options.localVideo
var remoteVideo = options.remoteVideo
var videoStream = options.videoStream
var audioStream = options.audioStream
var mediaConstraints = options.mediaConstraints
var connectionConstraints = options.connectionConstraints
var pc = options.peerConnection
var sendSource = options.sendSource || 'webcam'
var dataChannelConfig = options.dataChannelConfig
var useDataChannels = options.dataChannels || false
var dataChannel
var guid = uuid.v4()
var configuration = recursive({
iceServers: freeice()
},
options.configuration)
var onicecandidate = options.onicecandidate
if (onicecandidate) this.on('icecandidate', onicecandidate)
var oncandidategatheringdone = options.oncandidategatheringdone
if (oncandidategatheringdone) {
this.on('candidategatheringdone', oncandidategatheringdone)
}
var simulcast = options.simulcast
var multistream = options.multistream
var interop = new sdpTranslator.Interop()
var candidatesQueueOut = []
var candidategatheringdone = false
Object.defineProperties(this, {
'peerConnection': {
get: function () {
return pc
}
},
'id': {
value: options.id || guid,
writable: false
},
'remoteVideo': {
get: function () {
return remoteVideo
}
},
'localVideo': {
get: function () {
return localVideo
}
},
'dataChannel': {
get: function () {
return dataChannel
}
},
/**
* @member {(external:ImageData|undefined)} currentFrame
*/
'currentFrame': {
get: function () {
// [ToDo] Find solution when we have a remote stream but we didn't set
// a remoteVideo tag
if (!remoteVideo) return;
if (remoteVideo.readyState < remoteVideo.HAVE_CURRENT_DATA)
throw new Error('No video stream data available')
var canvas = document.createElement('canvas')
canvas.width = remoteVideo.videoWidth
canvas.height = remoteVideo.videoHeight
canvas.getContext('2d').drawImage(remoteVideo, 0, 0)
return canvas
}
}
})
// Init PeerConnection
if (!pc) {
pc = new RTCPeerConnection(configuration);
if (useDataChannels && !dataChannel) {
var dcId = 'WebRtcPeer-' + self.id
var dcOptions = undefined
if (dataChannelConfig) {
dcId = dataChannelConfig.id || dcId
dcOptions = dataChannelConfig.options
}
dataChannel = pc.createDataChannel(dcId, dcOptions);
if (dataChannelConfig) {
dataChannel.onopen = dataChannelConfig.onopen;
dataChannel.onclose = dataChannelConfig.onclose;
dataChannel.onmessage = dataChannelConfig.onmessage;
dataChannel.onbufferedamountlow = dataChannelConfig.onbufferedamountlow;
dataChannel.onerror = dataChannelConfig.onerror || noop;
}
}
}
pc.addEventListener('icecandidate', function (event) {
var candidate = event.candidate
if (EventEmitter.listenerCount(self, 'icecandidate') ||
EventEmitter.listenerCount(
self, 'candidategatheringdone')) {
if (candidate) {
var cand
if (multistream && usePlanB) {
cand = interop.candidateToUnifiedPlan(candidate)
} else {
cand = candidate
}
self.emit('icecandidate', cand)
candidategatheringdone = false
} else if (!candidategatheringdone) {
self.emit('candidategatheringdone')
candidategatheringdone = true
}
} else if (!candidategatheringdone) {
// Not listening to 'icecandidate' or 'candidategatheringdone' events, queue
// the candidate until one of them is listened
candidatesQueueOut.push(candidate)
if (!candidate) candidategatheringdone = true
}
})
pc.ontrack = options.onaddstream
pc.onnegotiationneeded = options.onnegotiationneeded
this.on('newListener', function (event, listener) {
if (event === 'icecandidate' || event === 'candidategatheringdone') {
while (candidatesQueueOut.length) {
var candidate = candidatesQueueOut.shift()
if (!candidate === (event === 'candidategatheringdone')) {
listener(candidate)
}
}
}
})
var addIceCandidate = bufferizeCandidates(pc)
/**
* Callback function invoked when an ICE candidate is received. Developers are
* expected to invoke this function in order to complete the SDP negotiation.
*
* @function module:kurentoUtils.WebRtcPeer.prototype.addIceCandidate
*
* @param iceCandidate - Literal object with the ICE candidate description
* @param callback - Called when the ICE candidate has been added.
*/
this.addIceCandidate = function (iceCandidate, callback) {
var candidate
if (multistream && usePlanB) {
candidate = interop.candidateToPlanB(iceCandidate)
} else {
candidate = new RTCIceCandidate(iceCandidate)
}
logger.debug('Remote ICE candidate received', iceCandidate)
callback = (callback || noop).bind(this)
addIceCandidate(candidate, callback)
}
this.generateOffer = function (callback) {
callback = callback.bind(this)
var offerAudio = true
var offerVideo = true
// Constraints must have both blocks
if (mediaConstraints) {
offerAudio = (typeof mediaConstraints.audio === 'boolean') ?
mediaConstraints.audio : true
offerVideo = (typeof mediaConstraints.video === 'boolean') ?
mediaConstraints.video : true
}
var browserDependantConstraints = {
offerToReceiveAudio: (mode !== 'sendonly' && offerAudio),
offerToReceiveVideo: (mode !== 'sendonly' && offerVideo)
}
//FIXME: clarify possible constraints passed to createOffer()
/*var constraints = recursive(browserDependantConstraints,
connectionConstraints)*/
var constraints = browserDependantConstraints;
logger.debug('constraints: ' + JSON.stringify(constraints))
pc.createOffer(constraints).then(function (offer) {
logger.debug('Created SDP offer')
offer = mangleSdpToAddSimulcast(offer)
return pc.setLocalDescription(offer)
}).then(function () {
var localDescription = pc.localDescription
logger.debug('Local description set', localDescription.sdp)
if (multistream && usePlanB) {
localDescription = interop.toUnifiedPlan(localDescription)
logger.debug('offer::origPlanB->UnifiedPlan', dumpSDP(
localDescription))
}
callback(null, localDescription.sdp, self.processAnswer.bind(
self))
}).catch(callback)
}
this.getLocalSessionDescriptor = function () {
return pc.localDescription
}
this.getRemoteSessionDescriptor = function () {
return pc.remoteDescription
}
function setRemoteVideo() {
if (remoteVideo) {
var stream = pc.getRemoteStreams()[0]
var url = stream ? URL.createObjectURL(stream) : ''
remoteVideo.pause()
remoteVideo.src = url
remoteVideo.load()
logger.debug('Remote URL:', url)
}
}
this.showLocalVideo = function () {
localVideo.src = URL.createObjectURL(videoStream)
localVideo.muted = true
}
this.send = function (data) {
if (dataChannel && dataChannel.readyState === 'open') {
dataChannel.send(data)
} else {
logger.warn(
'Trying to send data over a non-existing or closed data channel')
}
}
/**
* Callback function invoked when a SDP answer is received. Developers are
* expected to invoke this function in order to complete the SDP negotiation.
*
* @function module:kurentoUtils.WebRtcPeer.prototype.processAnswer
*
* @param sdpAnswer - Description of sdpAnswer
* @param callback -
* Invoked after the SDP answer is processed, or there is an error.
*/
this.processAnswer = function (sdpAnswer, callback) {
callback = (callback || noop).bind(this)
var answer = new RTCSessionDescription({
type: 'answer',
sdp: sdpAnswer
})
if (multistream && usePlanB) {
var planBAnswer = interop.toPlanB(answer)
logger.debug('asnwer::planB', dumpSDP(planBAnswer))
answer = planBAnswer
}
logger.debug('SDP answer received, setting remote description')
if (pc.signalingState === 'closed') {
return callback('PeerConnection is closed')
}
pc.setRemoteDescription(answer, function () {
setRemoteVideo()
callback()
},
callback)
}
/**
* Callback function invoked when a SDP offer is received. Developers are
* expected to invoke this function in order to complete the SDP negotiation.
*
* @function module:kurentoUtils.WebRtcPeer.prototype.processOffer
*
* @param sdpOffer - Description of sdpOffer
* @param callback - Called when the remote description has been set
* successfully.
*/
this.processOffer = function (sdpOffer, callback) {
callback = callback.bind(this)
var offer = new RTCSessionDescription({
type: 'offer',
sdp: sdpOffer
})
if (multistream && usePlanB) {
var planBOffer = interop.toPlanB(offer)
logger.debug('offer::planB', dumpSDP(planBOffer))
offer = planBOffer
}
logger.debug('SDP offer received, setting remote description')
if (pc.signalingState === 'closed') {
return callback('PeerConnection is closed')
}
pc.setRemoteDescription(offer).then(function () {
return setRemoteVideo()
}).then(function () {
return pc.createAnswer()
}).then(function (answer) {
answer = mangleSdpToAddSimulcast(answer)
logger.debug('Created SDP answer')
return pc.setLocalDescription(answer)
}).then(function () {
var localDescription = pc.localDescription
if (multistream && usePlanB) {
localDescription = interop.toUnifiedPlan(localDescription)
logger.debug('answer::origPlanB->UnifiedPlan', dumpSDP(
localDescription))
}
logger.debug('Local description set', localDescription.sdp)
callback(null, localDescription.sdp)
}).catch(callback)
}
function mangleSdpToAddSimulcast(answer) {
if (simulcast) {
if (browser.name === 'Chrome' || browser.name === 'Chromium') {
logger.debug('Adding multicast info')
answer = new RTCSessionDescription({
'type': answer.type,
'sdp': removeFIDFromOffer(answer.sdp) + getSimulcastInfo(
videoStream)
})
} else {
logger.warn('Simulcast is only available in Chrome browser.')
}
}
return answer
}
/**
* This function creates the RTCPeerConnection object taking into account the
* properties received in the constructor. It starts the SDP negotiation
* process: generates the SDP offer and invokes the onsdpoffer callback. This
* callback is expected to send the SDP offer, in order to obtain an SDP
* answer from another peer.
*/
function start() {
if (pc.signalingState === 'closed') {
callback(
'The peer connection object is in "closed" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue'
)
}
if (videoStream && localVideo) {
self.showLocalVideo()
}
if (videoStream) {
pc.addStream(videoStream)
}
if (audioStream) {
pc.addStream(audioStream)
}
// [Hack] https://code.google.com/p/chromium/issues/detail?id=443558
var browser = parser.getBrowser()
if (mode === 'sendonly' &&
(browser.name === 'Chrome' || browser.name === 'Chromium') &&
browser.major === 39) {
mode = 'sendrecv'
}
callback()
}
if (mode !== 'recvonly' && !videoStream && !audioStream) {
function getMedia(constraints) {
if (constraints === undefined) {
constraints = MEDIA_CONSTRAINTS
}
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
videoStream = stream
start()
}).catch(callback);
}
if (sendSource === 'webcam') {
getMedia(mediaConstraints)
} else {
getScreenConstraints(sendSource, function (error, constraints_) {
if (error)
return callback(error)
constraints = [mediaConstraints]
constraints.unshift(constraints_)
getMedia(recursive.apply(undefined, constraints))
}, guid)
}
} else {
setTimeout(start, 0)
}
this.on('_dispose', function () {
if (localVideo) {
localVideo.pause()
localVideo.src = ''
localVideo.load()
//Unmute local video in case the video tag is later used for remote video
localVideo.muted = false
}
if (remoteVideo) {
remoteVideo.pause()
remoteVideo.src = ''
remoteVideo.load()
}
self.removeAllListeners()
if (window.cancelChooseDesktopMedia !== undefined) {
window.cancelChooseDesktopMedia(guid)
}
})
}
inherits(WebRtcPeer, EventEmitter)
function createEnableDescriptor(type) {
var method = 'get' + type + 'Tracks'
return {
enumerable: true,
get: function () {
// [ToDo] Should return undefined if not all tracks have the same value?
if (!this.peerConnection) return
var streams = this.peerConnection.getLocalStreams()
if (!streams.length) return
for (var i = 0, stream; stream = streams[i]; i++) {
var tracks = stream[method]()
for (var j = 0, track; track = tracks[j]; j++)
if (!track.enabled) return false
}
return true
},
set: function (value) {
function trackSetEnable(track) {
track.enabled = value
}
this.peerConnection.getLocalStreams().forEach(function (stream) {
stream[method]().forEach(trackSetEnable)
})
}
}
}
Object.defineProperties(WebRtcPeer.prototype, {
'enabled': {
enumerable: true,
get: function () {
return this.audioEnabled && this.videoEnabled
},
set: function (value) {
this.audioEnabled = this.videoEnabled = value
}
},
'audioEnabled': createEnableDescriptor('Audio'),
'videoEnabled': createEnableDescriptor('Video')
})
WebRtcPeer.prototype.getLocalStream = function (index) {
if (this.peerConnection) {
return this.peerConnection.getLocalStreams()[index || 0]
}
}
WebRtcPeer.prototype.getRemoteStream = function (index) {
if (this.peerConnection) {
return this.peerConnection.getRemoteStreams()[index || 0]
}
}
/**
* @description This method frees the resources used by WebRtcPeer.
*
* @function module:kurentoUtils.WebRtcPeer.prototype.dispose
*/
WebRtcPeer.prototype.dispose = function () {
logger.debug('Disposing WebRtcPeer')
var pc = this.peerConnection
var dc = this.dataChannel
try {
if (dc) {
if (dc.signalingState === 'closed') return
dc.close()
}
if (pc) {
if (pc.signalingState === 'closed') return
pc.getLocalStreams().forEach(streamStop)
// FIXME This is not yet implemented in firefox
// if(videoStream) pc.removeStream(videoStream);
// if(audioStream) pc.removeStream(audioStream);
pc.close()
}
} catch (err) {
logger.warn('Exception disposing webrtc peer ' + err)
}
this.emit('_dispose')
}
//
// Specialized child classes
//
function WebRtcPeerRecvonly(options, callback) {
if (!(this instanceof WebRtcPeerRecvonly)) {
return new WebRtcPeerRecvonly(options, callback)
}
WebRtcPeerRecvonly.super_.call(this, 'recvonly', options, callback)
}
inherits(WebRtcPeerRecvonly, WebRtcPeer)
function WebRtcPeerSendonly(options, callback) {
if (!(this instanceof WebRtcPeerSendonly)) {
return new WebRtcPeerSendonly(options, callback)
}
WebRtcPeerSendonly.super_.call(this, 'sendonly', options, callback)
}
inherits(WebRtcPeerSendonly, WebRtcPeer)
function WebRtcPeerSendrecv(options, callback) {
if (!(this instanceof WebRtcPeerSendrecv)) {
return new WebRtcPeerSendrecv(options, callback)
}
WebRtcPeerSendrecv.super_.call(this, 'sendrecv', options, callback)
}
inherits(WebRtcPeerSendrecv, WebRtcPeer)
function harkUtils(stream, options) {
return hark(stream, options);
}
exports.bufferizeCandidates = bufferizeCandidates
exports.WebRtcPeerRecvonly = WebRtcPeerRecvonly
exports.WebRtcPeerSendonly = WebRtcPeerSendonly
exports.WebRtcPeerSendrecv = WebRtcPeerSendrecv
exports.hark = harkUtils

View File

@ -0,0 +1,18 @@
/*
* (C) Copyright 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.
*/
// Don't run on Internet Explorer 8, so exit inmediatly
if (window.addEventListener) module.exports = require('./index');

View File

@ -0,0 +1,30 @@
/*
* (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.
*
*/
/**
* This module contains a set of reusable components that have been found useful
* during the development of the WebRTC applications with Kurento.
*
* @module kurentoUtils
*
* @copyright 2014 Kurento (http://kurento.org/)
* @license ALv2
*/
var WebRtcPeer = require('./WebRtcPeer');
exports.WebRtcPeer = WebRtcPeer;

View File

@ -0,0 +1,12 @@
import { OpenVidu } from './OpenVidu';
//This export with --standalone option allows using OpenVidu from bowser with namespace
//export { OpenVidu } from './OpenVidu';
//This "hack" allows to use OpenVidu from the global space window
if(window){
window["OpenVidu"] = OpenVidu;
}
//Command to generate bundle.js without namespace
//watchify Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../static/js/OpenVidu.js -v

View File

@ -0,0 +1,218 @@
/*
* (C) Copyright 2017 OpenVidu (http://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 { OpenViduInternal } from '../OpenViduInternal/OpenViduInternal';
import { Session } from './Session';
import { Publisher } from './Publisher';
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/OpenViduError';
import * as adapter from 'webrtc-adapter';
import * as screenSharing from '../ScreenSharing/Screen-Capturing.js';
import * as screenSharingAuto from '../ScreenSharing/Screen-Capturing-Auto.js';
if (window) {
window["adapter"] = adapter;
}
export class OpenVidu {
openVidu: OpenViduInternal;
constructor() {
this.openVidu = new OpenViduInternal();
console.info("'OpenVidu' initialized");
};
initSession(apiKey: string, sessionId: string): Session;
initSession(sessionId: string): Session;
initSession(param1, param2?): any {
if (this.checkSystemRequirements()) {
if (typeof param2 == "string") {
return new Session(this.openVidu.initSession(param2), this);
} else {
return new Session(this.openVidu.initSession(param1), this);
}
} else {
alert("Browser not supported");
}
}
initPublisher(parentId: string): Publisher;
initPublisher(parentId: string, cameraOptions: any): Publisher;
initPublisher(parentId: string, cameraOptions: any, callback: any): Publisher;
initPublisher(parentId: string, cameraOptions?: any, callback?: Function): any {
if (this.checkSystemRequirements()) {
let publisher: Publisher;
if (cameraOptions != null) {
cameraOptions.audio = cameraOptions.audio != null ? cameraOptions.audio : true;
cameraOptions.video = cameraOptions.video != null ? cameraOptions.video : true;
if (!cameraOptions.screen) {
// Webcam and/or microphone is being requested
let cameraOptionsAux = {
sendAudio: cameraOptions.audio != null ? cameraOptions.audio : true,
sendVideo: cameraOptions.video != null ? cameraOptions.video : true,
activeAudio: cameraOptions.audioActive != null ? cameraOptions.audioActive : true,
activeVideo: cameraOptions.videoActive != null ? cameraOptions.videoActive : true,
data: true,
mediaConstraints: this.openVidu.generateMediaConstraints(cameraOptions)
};
cameraOptions = cameraOptionsAux;
publisher = new Publisher(this.openVidu.initPublisherTagged(parentId, cameraOptions, callback), parentId, false);
console.info("'Publisher' initialized");
return publisher;
} else {
if (adapter.browserDetails.browser === 'firefox' && adapter.browserDetails.version >= 52) {
publisher = new Publisher(this.openVidu.initPublisherScreen(parentId, callback), parentId, true);
screenSharingAuto.getScreenId((error, sourceId, screenConstraints) => {
cameraOptions = {
sendAudio: cameraOptions.audio,
sendVideo: cameraOptions.video,
activeAudio: cameraOptions.audioActive != null ? cameraOptions.audioActive : true,
activeVideo: cameraOptions.videoActive != null ? cameraOptions.videoActive : true,
data: true,
mediaConstraints: {
video: screenConstraints.video,
audio: false
}
}
publisher.stream.configureScreenOptions(cameraOptions);
console.info("'Publisher' initialized");
});
return publisher;
} else if (adapter.browserDetails.browser === 'chrome') {
// Screen is being requested
/*screenSharing.isChromeExtensionAvailable((availability) => {
switch (availability) {
case 'available':
console.warn('EXTENSION AVAILABLE!!!');
screenSharing.getScreenConstraints((error, screenConstraints) => {
if (!error) {
console.warn(screenConstraints);
}
});
break;
case 'unavailable':
console.warn('EXTENSION NOT AVAILABLE!!!');
break;
case 'isFirefox':
console.warn('IT IS FIREFOX!!!');
screenSharing.getScreenConstraints((error, screenConstraints) => {
if (!error) {
console.warn(screenConstraints);
}
});
break;
}
});*/
screenSharingAuto.getScreenId((error, sourceId, screenConstraints) => {
if (error === 'not-installed') {
let error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, 'https://chrome.google.com/webstore/detail/screen-capturing/ajhifddimkapgcifgcodmmfdlknahffk');
console.error(error);
if (callback) callback(error);
return;
} else if (error === 'permission-denied') {
let error = new OpenViduError(OpenViduErrorName.SCREEN_CAPTURE_DENIED, 'You must allow access to one window of your desktop');
console.error(error);
if (callback) callback(error);
return;
}
cameraOptions = {
sendAudio: cameraOptions.audio != null ? cameraOptions.audio : true,
sendVideo: cameraOptions.video != null ? cameraOptions.video : true,
activeAudio: cameraOptions.audioActive != null ? cameraOptions.audioActive : true,
activeVideo: cameraOptions.videoActive != null ? cameraOptions.videoActive : true,
data: true,
mediaConstraints: {
video: screenConstraints.video,
audio: false
}
}
publisher.stream.configureScreenOptions(cameraOptions);
}, (error) => {
console.error('getScreenId error', error);
return;
});
publisher = new Publisher(this.openVidu.initPublisherScreen(parentId, callback), parentId, true);
console.info("'Publisher' initialized");
return publisher;
} else {
console.error('Screen sharing not supported on ' + adapter.browserDetails.browser);
}
}
} else {
cameraOptions = {
sendAudio: true,
sendVideo: true,
activeAudio: true,
activeVideo: true,
data: true,
mediaConstraints: {
audio: true,
video: { width: { ideal: 1280 } }
}
}
publisher = new Publisher(this.openVidu.initPublisherTagged(parentId, cameraOptions, callback), parentId, false);
console.info("'Publisher' initialized");
return publisher;
}
} else {
alert("Browser not supported");
}
}
checkSystemRequirements(): number {
let browser = adapter.browserDetails.browser;
let version = adapter.browserDetails.version;
//Bug fix: 'navigator.userAgent' in Firefox for Ubuntu 14.04 does not return "Firefox/[version]" in the string, so version returned is null
if ((browser == 'firefox') && (version == null)) {
return 1;
}
if (((browser == 'chrome') && (version >= 28)) || ((browser == 'edge') && (version >= 12)) || ((browser == 'firefox') && (version >= 22))) {
return 1;
} else {
return 0;
}
}
getDevices(callback) {
navigator.mediaDevices.enumerateDevices().then((deviceInfos) => {
callback(null, deviceInfos);
}).catch((error) => {
console.error("Error getting devices", error);
callback(error, null);
});
}
enableProdMode() {
console.log = function () { };
console.debug = function () { };
console.info = function () { };
console.warn = function () { };
}
}

View File

@ -0,0 +1,149 @@
/*
* options: name: XXX data: true (Maybe this is based on webrtc) audio: true,
* video: true, url: "file:///..." > Player screen: true > Desktop (implicit
* video:true, audio:false) audio: true, video: true > Webcam
*
* stream.hasAudio(); stream.hasVideo(); stream.hasData();
*/
import { Stream, StreamOptions, VideoOptions } from '../OpenViduInternal/Stream';
import { Session } from './Session';
import EventEmitter = require('wolfy87-eventemitter');
export class Publisher {
private ee = new EventEmitter();
accessAllowed = false;
element: Element;
id: string;
stream: Stream;
session: Session; //Initialized by Session.publish(Publisher)
isScreenRequested: boolean = false;
constructor(stream: Stream, parentId: string, isScreenRequested: boolean) {
this.stream = stream;
this.isScreenRequested = isScreenRequested;
this.stream.addEventListener('camera-access-changed', (event) => {
this.accessAllowed = event.accessAllowed;
if (this.accessAllowed) {
this.ee.emitEvent('accessAllowed');
} else {
this.ee.emitEvent('accessDenied');
}
});
if (document.getElementById(parentId) != null) {
this.element = document.getElementById(parentId)!!;
}
}
publishAudio(value: boolean) {
this.stream.getWebRtcPeer().audioEnabled = value;
}
publishVideo(value: boolean) {
this.stream.getWebRtcPeer().videoEnabled = value;
}
destroy() {
this.session.unpublish(this);
this.stream.dispose();
this.stream.removeVideo(this.element);
return this;
}
subscribeToRemote() {
this.stream.subscribeToMyRemote();
}
on(eventName: string, callback) {
this.ee.addListener(eventName, event => {
if (event) {
console.info("Event '" + eventName + "' triggered by 'Publisher'", event);
} else {
console.info("Event '" + eventName + "' triggered by 'Publisher'");
}
callback(event);
});
if (eventName == 'streamCreated') {
if (this.stream.isPublisherPublished) {
this.ee.emitEvent('streamCreated', [{ stream: this.stream }]);
} else {
this.stream.addEventListener('stream-created-by-publisher', () => {
console.warn('Publisher emitting streamCreated');
this.ee.emitEvent('streamCreated', [{ stream: this.stream }]);
});
}
}
if (eventName == 'videoElementCreated') {
if (this.stream.isVideoELementCreated) {
this.ee.emitEvent('videoElementCreated', [{
element: this.stream.getVideoElement()
}]);
} else {
this.stream.addOnceEventListener('video-element-created-by-stream', (element) => {
this.id = element.id;
this.ee.emitEvent('videoElementCreated', [{
element: element.element
}]);
});
}
}
if (eventName == 'videoPlaying') {
var video = this.stream.getVideoElement();
if (!this.stream.displayMyRemote() && video &&
video.currentTime > 0 &&
video.paused == false &&
video.ended == false &&
video.readyState == 4) {
this.ee.emitEvent('videoPlaying', [{
element: this.stream.getVideoElement()
}]);
} else {
this.stream.addOnceEventListener('video-is-playing', (element) => {
this.ee.emitEvent('videoPlaying', [{
element: element.element
}]);
});
}
}
if (eventName == 'remoteVideoPlaying') {
var video = this.stream.getVideoElement();
if (this.stream.displayMyRemote() && video &&
video.currentTime > 0 &&
video.paused == false &&
video.ended == false &&
video.readyState == 4) {
this.ee.emitEvent('remoteVideoPlaying', [{
element: this.stream.getVideoElement()
}]);
} else {
this.stream.addOnceEventListener('remote-video-is-playing', (element) => {
this.ee.emitEvent('remoteVideoPlaying', [{
element: element.element
}]);
});
}
}
if (eventName == 'accessAllowed') {
if (this.stream.accessIsAllowed) {
this.ee.emitEvent('accessAllowed');
} else {
this.stream.addEventListener('access-allowed-by-publisher', () => {
this.ee.emitEvent('accessAllowed');
});
}
}
if (eventName == 'accessDenied') {
if (this.stream.accessIsDenied) {
this.ee.emitEvent('accessDenied');
} else {
this.stream.addEventListener('access-denied-by-publisher', () => {
this.ee.emitEvent('accessDenied');
});
}
}
}
}

View File

@ -0,0 +1,131 @@
import { SessionInternal, SessionOptions } from '../OpenViduInternal/SessionInternal';
import { Stream } from '../OpenViduInternal/Stream';
import { Connection } from "../OpenViduInternal/Connection";
import { OpenVidu } from './OpenVidu';
import { Publisher } from './Publisher';
import { Subscriber } from './Subscriber';
import EventEmitter = require('wolfy87-eventemitter');
export class Session {
sessionId: String;
//capabilities: Capabilities
connection: Connection;
private ee = new EventEmitter();
constructor(private session: SessionInternal, private openVidu: OpenVidu) {
this.sessionId = session.getSessionId();
// Listens to the deactivation of the default behaviour upon the deletion of a Stream object
this.session.addEventListener('stream-destroyed-default', event => {
event.stream.removeVideo();
});
// Listens to the deactivation of the default behaviour upon the disconnection of a Session
this.session.addEventListener('session-disconnected-default', () => {
let s: Stream;
for (s of this.openVidu.openVidu.getRemoteStreams()) {
s.removeVideo();
}
if (this.connection) {
for (let streamId in this.connection.getStreams()) {
this.connection.getStreams()[streamId].removeVideo();
}
}
});
// Sets or updates the value of 'connection' property. Triggered by SessionInternal when succesful connection
this.session.addEventListener('update-connection-object', event => {
this.connection = event.connection;
});
}
connect(token: string, callback: any);
connect(token: string, metadata: any, callback: any);
connect(param1, param2, param3?) {
// Early configuration to deactivate automatic subscription to streams
if (param3) {
this.session.configure({
sessionId: this.session.getSessionId(),
participantId: param1,
metadata: this.session.stringClientMetadata(param2),
subscribeToStreams: false
});
this.session.connect(param1, param3);
} else {
this.session.configure({
sessionId: this.session.getSessionId(),
participantId: param1,
metadata: '',
subscribeToStreams: false
});
this.session.connect(param1, param2);
}
}
disconnect() {
this.openVidu.openVidu.close(false);
this.session.emitEvent('sessionDisconnected', [{
preventDefault: () => { this.session.removeEvent('session-disconnected-default'); }
}]);
this.session.emitEvent('session-disconnected-default', [{}]);
}
publish(publisher: Publisher) {
if (publisher.isScreenRequested && !publisher.stream.isScreenRequestedReady) {
publisher.stream.addOnceEventListener('screen-ready', () => {
publisher.session = this;
publisher.stream.publish();
});
} else {
publisher.session = this;
publisher.stream.publish();
}
}
unpublish(publisher: Publisher) {
this.session.unpublish(publisher.stream);
}
on(eventName: string, callback) {
this.session.addEventListener(eventName, event => {
if (event) {
console.info("Event '" + eventName + "' triggered by 'Session'", event);
} else {
console.info("Event '" + eventName + "' triggered by 'Session'");
}
callback(event);
});
}
once(eventName: string, callback) {
this.session.addOnceEventListener(eventName, event => {
callback(event);
});
}
off(eventName: string, eventHandler) {
this.session.removeListener(eventName, eventHandler);
}
subscribe(stream: Stream, htmlId: string, videoOptions: any): Subscriber;
subscribe(stream: Stream, htmlId: string): Subscriber;
subscribe(param1, param2, param3?): Subscriber {
// Subscription
this.session.subscribe(param1);
let subscriber = new Subscriber(param1, param2);
param1.playOnlyVideo(param2, null);
return subscriber;
}
unsubscribe(subscriber: Subscriber) {
this.session.unsuscribe(subscriber.stream);
subscriber.stream.removeVideo();
}
}

View File

@ -0,0 +1,63 @@
import { Stream, StreamOptions, VideoOptions } from '../OpenViduInternal/Stream';
import EventEmitter = require('wolfy87-eventemitter');
export class Subscriber {
private ee = new EventEmitter();
element: Element;
id: string;
stream: Stream;
constructor(stream: Stream, parentId: string) {
this.stream = stream;
if (document.getElementById(parentId) != null) {
this.element = document.getElementById(parentId)!!;
}
}
on(eventName: string, callback) {
this.ee.addListener(eventName, event => {
if (event) {
console.info("Event '" + eventName + "' triggered by 'Subscriber'", event);
} else {
console.info("Event '" + eventName + "' triggered by 'Subscriber'");
}
callback(event);
});
if (eventName == 'videoElementCreated') {
if (this.stream.isVideoELementCreated) {
this.ee.emitEvent('videoElementCreated', [{
element: this.stream.getVideoElement()
}]);
} else {
this.stream.addOnceEventListener('video-element-created-by-stream', element => {
console.warn("Subscriber emitting videoElementCreated");
this.id = element.id;
this.ee.emitEvent('videoElementCreated', [{
element: element
}]);
});
}
}
if (eventName == 'videoPlaying') {
var video = this.stream.getVideoElement();
if (!this.stream.displayMyRemote() && video &&
video.currentTime > 0 &&
video.paused == false &&
video.ended == false &&
video.readyState == 4) {
this.ee.emitEvent('videoPlaying', [{
element: this.stream.getVideoElement()
}]);
} else {
this.stream.addOnceEventListener('video-is-playing', (element) => {
this.ee.emitEvent('videoPlaying', [{
element: element.element
}]);
});
}
}
}
}

View File

@ -0,0 +1,3 @@
declare module "kurento-jsonrpc";
declare module "webrtc-adapter";
declare module "kurento-utils";

View File

@ -0,0 +1,6 @@
export * from './OpenVidu';
export * from './Session';
export * from './Publisher';
export * from './Subscriber';
export * from '../OpenViduInternal/Stream';
export * from '../OpenViduInternal/Connection';

View File

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

View File

@ -0,0 +1,95 @@
import { Stream, StreamOptions } from './Stream';
import { OpenViduInternal } from './OpenViduInternal';
import { SessionInternal } from './SessionInternal';
type ObjMap<T> = { [s: string]: T; }
export interface ConnectionOptions {
id: string;
metadata: string;
streams?: StreamOptions[];
audioActive: boolean;
videoActive: boolean;
}
export class Connection {
public connectionId: string;
public data: string;
public creationTime: number;
private streams: ObjMap<Stream> = {};
private streamsOpts: StreamOptions[] = [];
constructor( private openVidu: OpenViduInternal, private local: boolean, private room: SessionInternal, private options?: ConnectionOptions ) {
console.info( "'Connection' created (" + ( local ? "local" : "remote" ) + ")" + ( local ? "" : ", with 'connectionId' [" + (options ? options.id : '') + "] " ));
if ( options ) {
this.connectionId = options.id;
this.data = options.metadata;
if ( options.streams ) {
this.initStreams(options);
}
}
}
addStream( stream: Stream ) {
this.streams[stream.getIdInParticipant()] = stream;
this.room.getStreams()[stream.getIdInParticipant()] = stream;
}
getStreams() {
return this.streams;
}
dispose() {
for ( let key in this.streams ) {
this.streams[key].dispose();
}
}
sendIceCandidate( candidate ) {
console.debug(( this.local ? "Local" : "Remote" ), "candidate for",
this.connectionId, JSON.stringify( candidate ) );
this.openVidu.sendRequest( "onIceCandidate", {
endpointName: this.connectionId,
candidate: candidate.candidate,
sdpMid: candidate.sdpMid,
sdpMLineIndex: candidate.sdpMLineIndex
}, function( error, response ) {
if ( error ) {
console.error( "Error sending ICE candidate: "
+ JSON.stringify( error ) );
}
});
}
initStreams(options) {
for ( let streamOptions of options.streams ) {
let streamOpts = {
id: streamOptions.id,
connection: this,
sendAudio: streamOptions.sendAudio,
sendVideo: streamOptions.sendVideo,
recvAudio: ( streamOptions.audioActive == undefined ? true : streamOptions.audioActive ),
recvVideo: ( streamOptions.videoActive == undefined ? true : streamOptions.videoActive ),
activeAudio: streamOptions.activeAudio,
activeVideo: streamOptions.activeVideo,
data: streamOptions.data,
mediaConstraints: streamOptions.mediaConstraints
}
let stream = new Stream(this.openVidu, false, this.room, streamOpts );
this.addStream( stream );
this.streamsOpts.push( streamOpts );
}
console.info("Remote 'Connection' with 'connectionId' [" + this.connectionId + "] is now configured for receiving Streams with options: ", this.streamsOpts );
}
}

View File

@ -0,0 +1,12 @@
import { OpenViduInternal } from './OpenViduInternal';
//This export with --standalone option allows using OpenVidu from bowser with namespace
//export { OpenVidu } from './OpenVidu';
//This "hack" allows to use OpenVidu from the global space window
if(window){
window["OpenViduInternal"] = OpenViduInternal;
}
//Command to generate bundle.js without namespace
//watchify Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../static/js/OpenVidu.js -v

View File

@ -0,0 +1,21 @@
export enum OpenViduErrorName {
CAMERA_ACCESS_DENIED = 'CAMERA_ACCESS_DENIED',
MICROPHONE_ACCESS_DENIED = 'MICROPHONE_ACCESS_DENIED',
SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED',
NO_VIDEO_DEVICE = 'NO_VIDEO_DEVICE',
NO_INPUT_DEVICE = 'NO_INPUT_DEVICE',
SCREEN_EXTENSION_NOT_INSTALLED = 'SCREEN_EXTENSION_NOT_INSTALLED',
GENERIC_ERROR = 'GENERIC_ERROR'
}
export class OpenViduError {
name: OpenViduErrorName;
message: string;
constructor(name: OpenViduErrorName, message: string) {
this.name = name;
this.message = message;
}
}

View File

@ -0,0 +1,431 @@
/*
* (C) Copyright 2017 OpenVidu (http://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 { SessionInternal, SessionOptions } from './SessionInternal';
import { OpenViduError, OpenViduErrorName } from './OpenViduError';
import { Stream } from './Stream';
import * as RpcBuilder from '../KurentoUtils/kurento-jsonrpc';
export type Callback<T> = (error?: any, openVidu?: T) => void;
export class OpenViduInternal {
private wsUri;
private session: SessionInternal;
private jsonRpcClient: any;
private rpcParams: any;
private callback: Callback<OpenViduInternal>;
private camera: Stream;
private remoteStreams: Stream[] = [];
private secret: string;
constructor() { };
/* NEW METHODS */
initSession(sessionId) {
console.info("'Session' initialized with 'sessionId' [" + sessionId + "]");
this.session = new SessionInternal(this, sessionId);
return this.session;
}
initPublisherTagged(parentId: string, cameraOptions: any, callback?: Function): Stream {
this.getCamera(cameraOptions);
this.camera.requestCameraAccess((error, camera) => {
if (error) {
// Neither camera or microphone device is allowed/able to capture media
console.error(error);
if (callback) {
callback(error);
}
this.camera.ee.emitEvent('access-denied-by-publisher');
} else {
this.camera.setVideoElement(this.cameraReady(camera!, parentId));
if (callback) {
callback(undefined);
}
}
});
return this.camera;
}
initPublisherScreen(parentId: string, callback?): Stream {
this.camera = new Stream(this, true, this.session, 'screen-options');
this.camera.addOnceEventListener('can-request-screen', () => {
this.camera.requestCameraAccess((error, camera) => {
if (error) {
this.camera.ee.emitEvent('access-denied-by-publisher');
let errorName: OpenViduErrorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED;
let errorMessage = 'You must allow access to one window of your desktop';
let e = new OpenViduError(errorName, errorMessage);
console.error(e);
if (callback) {
callback(e);
}
}
else {
this.camera.setVideoElement(this.cameraReady(camera!, parentId));
if (this.camera.getSendAudio()) {
// If the user wants to send audio with the screen capturing
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(userStream => {
this.camera.getWrStream().addTrack(userStream.getAudioTracks()[0]);
this.camera.isScreenRequestedReady = true;
this.camera.ee.emitEvent('screen-ready');
if (callback) {
callback(undefined);
}
})
.catch(error => {
this.camera.ee.emitEvent('access-denied-by-publisher');
console.error("Error accessing the microphone", error);
if (callback) {
let errorName: OpenViduErrorName = OpenViduErrorName.MICROPHONE_ACCESS_DENIED;
let errorMessage = error.toString();
callback(new OpenViduError(errorName, errorMessage));
}
});
} else {
this.camera.isScreenRequestedReady = true;
this.camera.ee.emitEvent('screen-ready');
if (callback) {
callback(undefined);
}
}
}
});
});
return this.camera;
}
cameraReady(camera: Stream, parentId: string) {
this.camera = camera;
let videoElement = this.camera.playOnlyVideo(parentId, null);
this.camera.emitStreamReadyEvent();
return videoElement;
}
initPublisher(cameraOptions: any, callback) {
this.getCamera(cameraOptions);
this.camera.requestCameraAccess((error, camera) => {
if (error) callback(error);
else callback(undefined);
});
}
getLocalStream() {
return this.camera;
}
getRemoteStreams() {
return this.remoteStreams;
}
/* NEW METHODS */
getWsUri() {
return this.wsUri;
}
setWsUri(wsUri: string) {
this.wsUri = wsUri;
}
getSecret() {
return this.secret;
}
setSecret(secret: string) {
this.secret = secret;
}
getOpenViduServerURL() {
return 'https://' + this.wsUri.split("wss://")[1].split("/room")[0];
}
getRoom() {
return this.session;
}
connect(callback: Callback<OpenViduInternal>): void {
this.callback = callback;
this.initJsonRpcClient(this.wsUri);
}
private initJsonRpcClient(wsUri: string): void {
let config = {
heartbeat: 3000,
sendCloseMessage: false,
ws: {
uri: wsUri,
useSockJS: false,
onconnected: this.connectCallback.bind(this),
ondisconnect: this.disconnectCallback.bind(this),
onreconnecting: this.reconnectingCallback.bind(this),
onreconnected: this.reconnectedCallback.bind(this)
},
rpc: {
requestTimeout: 15000,
//notifications
participantJoined: this.onParticipantJoined.bind(this),
participantPublished: this.onParticipantPublished.bind(this),
participantUnpublished: this.onParticipantLeft.bind(this),
participantLeft: this.onParticipantLeft.bind(this),
participantEvicted: this.onParticipantEvicted.bind(this),
sendMessage: this.onNewMessage.bind(this),
iceCandidate: this.iceCandidateEvent.bind(this),
mediaError: this.onMediaError.bind(this),
custonNotification: this.customNotification.bind(this)
}
};
this.jsonRpcClient = new RpcBuilder.clients.JsonRpcClient(config);
}
private customNotification(params) {
if (this.isRoomAvailable()) {
this.session.emitEvent("custom-message-received", [{ params: params }]);
}
}
private connectCallback(error) {
if (error) {
this.callback(error);
} else {
this.callback(null);
}
}
private isRoomAvailable() {
if (this.session !== undefined && this.session instanceof SessionInternal) {
return true;
} else {
console.warn('Room instance not found');
return false;
}
}
private disconnectCallback() {
console.warn('Websocket connection lost');
if (this.isRoomAvailable()) {
this.session.onLostConnection();
} else {
alert('Connection error. Please reload page.');
}
}
private reconnectingCallback() {
console.warn('Websocket connection lost (reconnecting)');
if (this.isRoomAvailable()) {
this.session.onLostConnection();
} else {
alert('Connection error. Please reload page.');
}
}
private reconnectedCallback() {
console.warn('Websocket reconnected');
}
private onParticipantJoined(params) {
if (this.isRoomAvailable()) {
this.session.onParticipantJoined(params);
}
}
private onParticipantPublished(params) {
if (this.isRoomAvailable()) {
this.session.onParticipantPublished(params);
}
}
private onParticipantLeft(params) {
if (this.isRoomAvailable()) {
this.session.onParticipantLeft(params);
}
}
private onParticipantEvicted(params) {
if (this.isRoomAvailable()) {
this.session.onParticipantEvicted(params);
}
}
private onNewMessage(params) {
if (this.isRoomAvailable()) {
this.session.onNewMessage(params);
}
}
private iceCandidateEvent(params) {
if (this.isRoomAvailable()) {
this.session.recvIceCandidate(params);
}
}
private onRoomClosed(params) {
if (this.isRoomAvailable()) {
this.session.onRoomClosed(params);
}
}
private onMediaError(params) {
if (this.isRoomAvailable()) {
this.session.onMediaError(params);
}
}
setRpcParams(params: any) {
this.rpcParams = params;
}
sendRequest(method, params, callback?) {
if (params && params instanceof Function) {
callback = params;
params = undefined;
}
params = params || {};
if (this.rpcParams && this.rpcParams !== null && this.rpcParams !== undefined) {
for (let index in this.rpcParams) {
if (this.rpcParams.hasOwnProperty(index)) {
params[index] = this.rpcParams[index];
console.debug('RPC param added to request {' + index + ': ' + this.rpcParams[index] + '}');
}
}
}
console.debug('Sending request: {method:"' + method + '", params: ' + JSON.stringify(params) + '}');
this.jsonRpcClient.send(method, params, callback);
}
close(forced) {
if (this.isRoomAvailable()) {
this.session.leave(forced, this.jsonRpcClient);
}
};
disconnectParticipant(stream) {
if (this.isRoomAvailable()) {
this.session.disconnect(stream);
}
}
getCamera(options?) {
if (this.camera) {
return this.camera;
}
options = options || {
sendAudio: true,
sendVideo: true,
activeAudio: true,
activeVideo: true,
data: true,
mediaConstraints: {
audio: true,
video: { width: { ideal: 1280 } }
}
}
options.connection = this.session.getLocalParticipant();
this.camera = new Stream(this, true, this.session, options);
return this.camera;
};
//CHAT
sendMessage(room, user, message) {
this.sendRequest('sendMessage', {
message: message,
userMessage: user,
roomMessage: room
}, function (error, response) {
if (error) {
console.error(error);
}
});
};
sendCustomRequest(params, callback) {
this.sendRequest('customRequest', params, callback);
};
toggleLocalVideoTrack(activate: boolean) {
this.getCamera().getWebRtcPeer().videoEnabled = activate;
}
toggleLocalAudioTrack(activate: boolean) {
this.getCamera().getWebRtcPeer().audioEnabled = activate;
}
publishLocalVideoAudio() {
this.toggleLocalVideoTrack(true);
this.toggleLocalAudioTrack(true);
}
unpublishLocalVideoAudio() {
this.toggleLocalVideoTrack(false);
this.toggleLocalAudioTrack(false);
}
generateMediaConstraints(cameraOptions: any) {
let mediaConstraints = {
audio: cameraOptions.audio,
video: {}
}
if (!cameraOptions.video) {
mediaConstraints.video = false
} else {
let w, h;
switch (cameraOptions.quality) {
case 'LOW':
w = 320;
h = 240;
break;
case 'MEDIUM':
w = 640;
h = 480;
break;
case 'HIGH':
w = 1280;
h = 720;
break;
default:
w = 640;
h = 480;
}
mediaConstraints.video['width'] = { exact: w };
mediaConstraints.video['height'] = { exact: h };
//mediaConstraints.video['frameRate'] = { ideal: Number((<HTMLInputElement>document.getElementById('frameRate')).value) };
}
return mediaConstraints;
}
}

View File

@ -0,0 +1,571 @@
import { Stream } from './Stream';
import { OpenViduInternal } from './OpenViduInternal';
import { Connection, ConnectionOptions } from './Connection';
import EventEmitter = require('wolfy87-eventemitter');
const SECRET_PARAM = '?secret=';
export interface SessionOptions {
sessionId: string;
participantId: string;
metadata: string;
subscribeToStreams?: boolean;
updateSpeakerInterval?: number;
thresholdSpeaker?: number;
}
export class SessionInternal {
private id: string;
private sessionId: string;
private ee = new EventEmitter();
private streams = {};
private participants = {};
private participantsSpeaking: Connection[] = [];
private connected = false;
public localParticipant: Connection;
private subscribeToStreams: boolean;
private updateSpeakerInterval: number;
public thresholdSpeaker: number;
private options: SessionOptions;
constructor(private openVidu: OpenViduInternal, sessionId: string) {
this.sessionId = this.getUrlWithoutSecret(sessionId);
this.localParticipant = new Connection(this.openVidu, true, this);
if (!this.openVidu.getWsUri()) {
this.processOpenViduUrl(sessionId);
}
}
private processOpenViduUrl(url: string) {
this.openVidu.setSecret(this.getSecretFromUrl(url));
this.openVidu.setWsUri(this.getFinalUrl(url));
}
private getSecretFromUrl(url: string): string {
let secret = '';
if (url.indexOf(SECRET_PARAM) !== -1) {
secret = url.substring(url.lastIndexOf(SECRET_PARAM) + SECRET_PARAM.length, url.length);
}
return secret;
}
private getUrlWithoutSecret(url: string): string {
if (!url) {
console.error('sessionId is not defined');
}
if (url.indexOf(SECRET_PARAM) !== -1) {
url = url.substring(0, url.lastIndexOf(SECRET_PARAM));
}
return url;
}
private getFinalUrl(url: string): string {
url = this.getUrlWithoutSecret(url).substring(0, url.lastIndexOf('/')) + '/room';
if (url.indexOf(".ngrok.io") !== -1) {
// OpenVidu server URL referes to a ngrok IP: secure wss protocol and delete port of URL
url = url.replace("ws://", "wss://");
let regex = /\.ngrok\.io:\d+/;
url = url.replace(regex, ".ngrok.io");
} else if ((url.indexOf("localhost") !== -1) || (url.indexOf("127.0.0.1") != -1)) {
// OpenVidu server URL referes to localhost IP
}
return url;
}
/* NEW METHODS */
connect(token, callback) {
this.openVidu.connect((error) => {
if (error) {
callback('ERROR CONNECTING TO OPENVIDU');
}
else {
if (!token) {
token = this.randomToken();
}
let joinParams = {
token: token,
session: this.sessionId,
metadata: this.options.metadata,
secret: this.openVidu.getSecret(),
dataChannels: false
}
if (this.localParticipant) {
if (Object.keys(this.localParticipant.getStreams()).some(streamId =>
this.streams[streamId].isDataChannelEnabled())) {
joinParams.dataChannels = true;
}
}
this.openVidu.sendRequest('joinRoom', joinParams, (error, response) => {
if (error) {
callback(error);
} else {
this.connected = true;
let exParticipants = response.value;
// IMPORTANT: Update connectionId with value send by server
this.localParticipant.connectionId = response.id;
this.participants[response.id] = this.localParticipant;
let roomEvent = {
participants: new Array<Connection>(),
streams: new Array<Stream>()
}
let length = exParticipants.length;
for (let i = 0; i < length; i++) {
let connection = new Connection(this.openVidu, false, this,
exParticipants[i]);
connection.creationTime = new Date().getTime();
this.participants[connection.connectionId] = connection;
roomEvent.participants.push(connection);
let streams = connection.getStreams();
for (let key in streams) {
roomEvent.streams.push(streams[key]);
if (this.subscribeToStreams) {
streams[key].subscribe();
}
}
}
// Update local Connection object properties with values returned by server
this.localParticipant.data = response.metadata;
this.localParticipant.creationTime = new Date().getTime();
// Updates the value of property 'connection' in Session object
this.ee.emitEvent('update-connection-object', [{ connection: this.localParticipant }]);
// Own connection created event
this.ee.emitEvent('connectionCreated', [{ connection: this.localParticipant }]);
// One connection created event for each existing connection in the session
for (let part of roomEvent.participants) {
this.ee.emitEvent('connectionCreated', [{ connection: part }]);
}
//if (this.subscribeToStreams) {
for (let stream of roomEvent.streams) {
this.ee.emitEvent('streamCreated', [{ stream }]);
// Adding the remote stream to the OpenVidu object
this.openVidu.getRemoteStreams().push(stream);
}
callback(undefined);
}
});
}
});
}
publish() {
this.openVidu.getCamera().publish();
}
/* NEW METHODS */
configure(options: SessionOptions) {
this.options = options;
this.id = options.sessionId;
this.subscribeToStreams = options.subscribeToStreams == null ? true : options.subscribeToStreams;
this.updateSpeakerInterval = options.updateSpeakerInterval || 1500;
this.thresholdSpeaker = options.thresholdSpeaker || -50;
this.activateUpdateMainSpeaker();
}
getId() {
return this.id;
}
getSessionId() {
return this.sessionId;
}
private activateUpdateMainSpeaker() {
setInterval(() => {
if (this.participantsSpeaking.length > 0) {
this.ee.emitEvent('update-main-speaker', [{
participantId: this.participantsSpeaking[this.participantsSpeaking.length - 1]
}]);
}
}, this.updateSpeakerInterval);
}
getLocalParticipant() {
return this.localParticipant;
}
addEventListener(eventName, listener) {
this.ee.on(eventName, listener);
}
addOnceEventListener(eventName, listener) {
this.ee.once(eventName, listener);
}
removeListener(eventName, listener) {
this.ee.off(eventName, listener);
}
removeEvent(eventName) {
this.ee.removeEvent(eventName);
}
emitEvent(eventName, eventsArray) {
this.ee.emitEvent(eventName, eventsArray);
}
subscribe(stream: Stream) {
stream.subscribe();
}
unsuscribe(stream) {
console.info("Unsubscribing from " + stream.getId());
this.openVidu.sendRequest('unsubscribeFromVideo', {
sender: stream.getId()
},
function (error, response) {
if (error) {
console.error("Error unsubscribing from Subscriber", error);
} else {
console.info("Unsubscribed correctly from " + stream.getId());
}
});
}
onParticipantPublished(options) {
// Get the existing Connection created on 'onParticipantJoined' for
// existing participants or create a new one for new participants
let connection = this.participants[options.id];
if (connection) {
// Update existing Connection
options.metadata = connection.data;
connection.options = options;
connection.initStreams(options);
} else {
// Create new Connection
connection = new Connection(this.openVidu, false, this, options);
}
let pid = connection.connectionId;
if (!(pid in this.participants)) {
console.debug("Remote Connection not found in connections list by its id [" + pid + "]");
} else {
console.debug("Remote Connection found in connections list by its id [" + pid + "]");
}
this.participants[pid] = connection;
this.ee.emitEvent('participant-published', [{ connection }]);
let streams = connection.getStreams();
for (let key in streams) {
let stream = streams[key];
if (this.subscribeToStreams) {
stream.subscribe();
}
this.ee.emitEvent('streamCreated', [{ stream }]);
// Adding the remote stream to the OpenVidu object
this.openVidu.getRemoteStreams().push(stream);
}
}
onParticipantJoined(msg) {
let connection = new Connection(this.openVidu, false, this, msg);
connection.creationTime = new Date().getTime();
let pid = connection.connectionId;
if (!(pid in this.participants)) {
this.participants[pid] = connection;
} else {
//use existing so that we don't lose streams info
console.warn("Connection already exists in connections list with " +
"the same connectionId, old:", this.participants[pid], ", joined now:", connection);
connection = this.participants[pid];
}
this.ee.emitEvent('participant-joined', [{
connection: connection
}]);
this.ee.emitEvent('connectionCreated', [{
connection: connection
}]);
}
onParticipantLeft(msg) {
let connection = this.participants[msg.name];
if (connection !== undefined) {
delete this.participants[msg.name];
this.ee.emitEvent('participant-left', [{
connection: connection
}]);
let streams = connection.getStreams();
for (let key in streams) {
this.ee.emitEvent('streamDestroyed', [{
stream: streams[key],
preventDefault: () => { this.ee.removeEvent('stream-destroyed-default'); }
}]);
this.ee.emitEvent('stream-destroyed-default', [{
stream: streams[key]
}]);
// Deleting the removed stream from the OpenVidu object
let index = this.openVidu.getRemoteStreams().indexOf(streams[key]);
this.openVidu.getRemoteStreams().splice(index, 1);
}
connection.dispose();
this.ee.emitEvent('connectionDestroyed', [{
connection: connection
}]);
} else {
console.warn("Participant " + msg.name
+ " unknown. Participants: "
+ JSON.stringify(this.participants));
}
};
onParticipantEvicted(msg) {
this.ee.emitEvent('participant-evicted', [{
localParticipant: this.localParticipant
}]);
};
onNewMessage(msg) {
console.info("New message: " + JSON.stringify(msg));
let room = msg.room;
let user = msg.user;
let message = msg.message;
if (user !== undefined) {
this.ee.emitEvent('newMessage', [{
room: room,
user: user,
message: message
}]);
} else {
console.warn("User undefined in new message:", msg);
}
}
recvIceCandidate(msg) {
let candidate = {
candidate: msg.candidate,
sdpMid: msg.sdpMid,
sdpMLineIndex: msg.sdpMLineIndex
}
let connection = this.participants[msg.endpointName];
if (!connection) {
console.error("Participant not found for endpoint " +
msg.endpointName + ". Ice candidate will be ignored.",
candidate);
return;
}
let streams = connection.getStreams();
for (let key in streams) {
let stream = streams[key];
stream.getWebRtcPeer().addIceCandidate(candidate, function (error) {
if (error) {
console.error("Error adding candidate for " + key
+ " stream of endpoint " + msg.endpointName
+ ": " + error);
}
});
}
}
onRoomClosed(msg) {
console.info("Room closed: " + JSON.stringify(msg));
let room = msg.room;
if (room !== undefined) {
this.ee.emitEvent('room-closed', [{
room: room
}]);
} else {
console.warn("Room undefined in on room closed", msg);
}
}
onLostConnection() {
if (!this.connected) {
console.warn('Not connected to room: if you are not debugging, this is probably a certificate error');
if (window.confirm('If you are not debugging, this is probably a certificate error at \"' + this.openVidu.getOpenViduServerURL() + '\"\n\nClick OK to navigate and accept it')) {
location.assign(this.openVidu.getOpenViduServerURL() + '/accept-certificate');
};
return;
}
console.warn('Lost connection in Session ' + this.id);
let room = this.id;
if (room !== undefined) {
this.ee.emitEvent('lost-connection', [{ room }]);
} else {
console.warn('Room undefined when lost connection');
}
}
onMediaError(params) {
console.error("Media error: " + JSON.stringify(params));
let error = params.error;
if (error) {
this.ee.emitEvent('error-media', [{
error: error
}]);
} else {
console.warn("Received undefined media error. Params:", params);
}
}
/*
* forced means the user was evicted, no need to send the 'leaveRoom' request
*/
leave(forced, jsonRpcClient) {
forced = !!forced;
console.info("Leaving Session (forced=" + forced + ")");
if (this.connected && !forced) {
this.openVidu.sendRequest('leaveRoom', function (error, response) {
if (error) {
console.error(error);
}
jsonRpcClient.close();
});
} else {
jsonRpcClient.close();
}
this.connected = false;
if (this.participants) {
for (let pid in this.participants) {
this.participants[pid].dispose();
delete this.participants[pid];
}
}
}
disconnect(stream: Stream) {
let connection = stream.getParticipant();
if (!connection) {
console.error("Stream to disconnect has no participant", stream);
return;
}
delete this.participants[connection.connectionId];
connection.dispose();
if (connection === this.localParticipant) {
console.info("Unpublishing my media (I'm " + connection.connectionId + ")");
delete this.localParticipant;
this.openVidu.sendRequest('unpublishVideo', function (error, response) {
if (error) {
console.error(error);
} else {
console.info("Media unpublished correctly");
}
});
} else {
this.unsuscribe(stream);
}
}
unpublish(stream: Stream) {
let connection = stream.getParticipant();
if (!connection) {
console.error("Stream to disconnect has no participant", stream);
return;
}
if (connection === this.localParticipant) {
delete this.participants[connection.connectionId];
connection.dispose();
console.info("Unpublishing my media (I'm " + connection.connectionId + ")");
delete this.localParticipant;
this.openVidu.sendRequest('unpublishVideo', function (error, response) {
if (error) {
console.error(error);
} else {
console.info("Media unpublished correctly");
}
});
}
}
getStreams() {
return this.streams;
}
addParticipantSpeaking(participantId) {
this.participantsSpeaking.push(participantId);
}
removeParticipantSpeaking(participantId) {
let pos = -1;
for (let i = 0; i < this.participantsSpeaking.length; i++) {
if (this.participantsSpeaking[i] == participantId) {
pos = i;
break;
}
}
if (pos != -1) {
this.participantsSpeaking.splice(pos, 1);
}
}
stringClientMetadata(metadata): string {
if (!(typeof metadata === 'string')) {
return JSON.stringify(metadata);
} else {
return metadata;
}
}
private randomToken(): string {
return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
}
}

View File

@ -0,0 +1,727 @@
/*
* options: name: XXX data: true (Maybe this is based on webrtc) audio: true,
* video: true, url: "file:///..." > Player screen: true > Desktop (implicit
* video:true, audio:false) audio: true, video: true > Webcam
*
* stream.hasAudio(); stream.hasVideo(); stream.hasData();
*/
import { Connection } from './Connection';
import { SessionInternal } from './SessionInternal';
import { OpenViduInternal, Callback } from './OpenViduInternal';
import { OpenViduError, OpenViduErrorName } from './OpenViduError';
import EventEmitter = require('wolfy87-eventemitter');
import * as kurentoUtils from '../KurentoUtils/kurento-utils-js';
import * as adapter from 'webrtc-adapter';
declare var navigator: any;
declare var RTCSessionDescription: any;
if (window) {
window["adapter"] = adapter;
}
function jq(id: string): string {
return id.replace(/(@|:|\.|\[|\]|,)/g, "\\$1");
}
function show(id: string) {
document.getElementById(jq(id))!.style.display = 'block';
}
function hide(id: string) {
document.getElementById(jq(id))!.style.display = 'none';
}
export interface StreamOptions {
id: string;
connection: Connection;
recvVideo: boolean;
recvAudio: boolean;
sendVideo: boolean;
sendAudio: boolean;
activeAudio: boolean;
activeVideo: boolean;
data: boolean;
mediaConstraints: any;
}
export interface VideoOptions {
thumb: string;
video: HTMLVideoElement;
}
export class Stream {
public connection: Connection;
ee = new EventEmitter();
private wrStream: MediaStream;
private wp: any;
private id: string;
private video: HTMLVideoElement;
private videoElements: VideoOptions[] = [];
private elements: HTMLDivElement[] = [];
private speechEvent: any;
private recvVideo: boolean;
private recvAudio: boolean;
private sendVideo: boolean;
private sendAudio: boolean;
private mediaConstraints: any;
private showMyRemote = false;
private localMirrored = false;
private chanId = 0;
private dataChannel: boolean;
private dataChannelOpened = false;
private activeAudio = true;
private activeVideo = true;
private videoSrcObject: MediaStream | null;
private parentId: string;
public isReadyToPublish: boolean = false;
public isPublisherPublished: boolean = false;
public isVideoELementCreated: boolean = false;
public accessIsAllowed: boolean = false;
public accessIsDenied: boolean = false;
public isScreenRequestedReady: boolean = false;
private isScreenRequested = false;
constructor(private openVidu: OpenViduInternal, private local: boolean, private room: SessionInternal, options: any) {
if (options !== 'screen-options') {
this.configureOptions(options);
} else {
this.isScreenRequested = true;
this.connection = this.room.getLocalParticipant();
}
this.addEventListener('src-added', (srcEvent) => {
this.videoSrcObject = srcEvent.srcObject;
if (this.video) this.video.srcObject = srcEvent.srcObject;
console.debug("Video srcObject [" + srcEvent.srcObject + "] added to stream [" + this.getId() + "]");
});
}
emitSrcEvent(wrstream) {
this.ee.emitEvent('src-added', [{
srcObject: wrstream
}]);
}
emitStreamReadyEvent() {
this.ee.emitEvent('stream-ready'), [{}];
}
getVideoSrcObject() {
return this.videoSrcObject;
}
removeVideo(parentElement: string);
removeVideo(parentElement: Element);
removeVideo();
removeVideo(parentElement?) {
if (typeof parentElement === "string") {
document.getElementById(parentElement)!.removeChild(this.video);
} else if (parentElement instanceof Element) {
parentElement.removeChild(this.video);
}
else if (!parentElement) {
if (document.getElementById(this.parentId)) {
document.getElementById(this.parentId)!.removeChild(this.video);
}
}
}
getVideoElement(): HTMLVideoElement {
return this.video;
}
setVideoElement(video: HTMLVideoElement) {
this.video = video;
}
getRecvVideo() {
return this.recvVideo;
}
getRecvAudio() {
return this.recvAudio;
}
getSendVideo() {
return this.sendVideo;
}
getSendAudio() {
return this.sendAudio;
}
subscribeToMyRemote() {
this.showMyRemote = true;
}
displayMyRemote() {
return this.showMyRemote;
}
mirrorLocalStream(wr) {
this.showMyRemote = true;
this.localMirrored = true;
if (wr) {
this.wrStream = wr;
this.emitSrcEvent(this.wrStream);
}
}
isLocalMirrored() {
return this.localMirrored;
}
getChannelName() {
return this.getId() + '_' + this.chanId++;
}
isDataChannelEnabled() {
return this.dataChannel;
}
isDataChannelOpened() {
return this.dataChannelOpened;
}
onDataChannelOpen(event) {
console.debug('Data channel is opened');
this.dataChannelOpened = true;
}
onDataChannelClosed(event) {
console.debug('Data channel is closed');
this.dataChannelOpened = false;
}
sendData(data) {
if (this.wp === undefined) {
throw new Error('WebRTC peer has not been created yet');
}
if (!this.dataChannelOpened) {
throw new Error('Data channel is not opened');
}
console.info("Sending through data channel: " + data);
this.wp.send(data);
}
getWrStream() {
return this.wrStream;
}
getWebRtcPeer() {
return this.wp;
}
addEventListener(eventName: string, listener: any) {
this.ee.addListener(eventName, listener);
}
addOnceEventListener(eventName: string, listener: any) {
this.ee.addOnceListener(eventName, listener);
}
removeListener(eventName) {
this.ee.removeAllListeners(eventName);
}
showSpinner(spinnerParentId: string) {
let progress = document.createElement('div');
progress.id = 'progress-' + this.getId();
progress.style.background = "center transparent url('img/spinner.gif') no-repeat";
let spinnerParent = document.getElementById(spinnerParentId);
if (spinnerParent) {
spinnerParent.appendChild(progress);
}
}
hideSpinner(spinnerId?: string) {
spinnerId = (spinnerId === undefined) ? this.getId() : spinnerId;
hide('progress-' + spinnerId);
}
playOnlyVideo(parentElement, thumbnailId) {
this.video = document.createElement('video');
this.video.id = (this.local ? 'local-' : 'remote-') + 'video-' + this.getId();
this.video.autoplay = true;
this.video.controls = false;
this.video.srcObject = this.videoSrcObject;
this.videoElements.push({
thumb: thumbnailId,
video: this.video
});
if (this.local && !this.displayMyRemote()) {
this.video.muted = true;
this.video.oncanplay = () => {
console.info("Local 'Stream' with id [" + this.getId() + "] video is now playing");
this.ee.emitEvent('video-is-playing', [{
element: this.video
}]);
};
} else {
this.video.title = this.getId();
}
if (typeof parentElement === "string") {
this.parentId = parentElement;
let parentElementDom = document.getElementById(parentElement);
if (parentElementDom) {
this.video = parentElementDom.appendChild(this.video);
this.ee.emitEvent('video-element-created-by-stream', [{
element: this.video
}]);
this.isVideoELementCreated = true;
}
} else {
this.parentId = parentElement.id;
this.video = parentElement.appendChild(this.video);
}
this.isReadyToPublish = true;
return this.video;
}
playThumbnail(thumbnailId) {
let container = document.createElement('div');
container.className = "participant";
container.id = this.getId();
let thumbnail = document.getElementById(thumbnailId);
if (thumbnail) {
thumbnail.appendChild(container);
}
this.elements.push(container);
let name = document.createElement('div');
container.appendChild(name);
let userName = this.getId().replace('_webcam', '');
if (userName.length >= 16) {
userName = userName.substring(0, 16) + "...";
}
name.appendChild(document.createTextNode(userName));
name.id = "name-" + this.getId();
name.className = "name";
name.title = this.getId();
this.showSpinner(thumbnailId);
return this.playOnlyVideo(container, thumbnailId);
}
getIdInParticipant() {
return this.id;
}
getParticipant() {
return this.connection;
}
getId() {
return this.connection.connectionId + "_" + this.id;
}
getRTCPeerConnection() {
return this.getWebRtcPeer().peerConnection;
}
requestCameraAccess(callback: Callback<Stream>) {
this.connection.addStream(this);
let constraints = this.mediaConstraints;
/*let constraints2 = {
audio: true,
video: {
width: {
ideal: 1280
},
frameRate: {
ideal: 15
}
}
};*/
this.userMediaHasVideo((hasVideo) => {
if (!hasVideo) {
if (this.sendVideo) {
callback(new OpenViduError(OpenViduErrorName.NO_VIDEO_DEVICE, 'You have requested camera access but there is no video input device available. Trying to connect with an audio input device only'), this);
}
if (!this.sendAudio) {
callback(new OpenViduError(OpenViduErrorName.NO_INPUT_DEVICE, 'You must init Publisher object with audio or video streams enabled'), undefined);
} else {
constraints.video = false;
this.sendVideo = false;
this.requestCameraAccesAux(constraints, callback);
}
} else {
this.requestCameraAccesAux(constraints, callback);
}
});
}
private requestCameraAccesAux(constraints, callback) {
navigator.mediaDevices.getUserMedia(constraints)
.then(userStream => {
this.cameraAccessSuccess(userStream, callback);
})
.catch(error => {
this.accessIsDenied = true;
this.accessIsAllowed = false;
let errorName: OpenViduErrorName;
let errorMessage = error.toString();;
if (!this.isScreenRequested) {
errorName = this.sendVideo ? OpenViduErrorName.CAMERA_ACCESS_DENIED : OpenViduErrorName.MICROPHONE_ACCESS_DENIED;
} else {
errorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED; // This code is only reachable for Firefox
}
callback(new OpenViduError(errorName, errorMessage), undefined);
});
}
private cameraAccessSuccess(userStream: MediaStream, callback: Function) {
this.accessIsAllowed = true;
this.accessIsDenied = false;
this.ee.emitEvent('access-allowed-by-publisher');
if (userStream.getAudioTracks()[0] != null) {
userStream.getAudioTracks()[0].enabled = this.activeAudio;
}
if (userStream.getVideoTracks()[0] != null) {
userStream.getVideoTracks()[0].enabled = this.activeVideo;
}
this.wrStream = userStream;
this.emitSrcEvent(this.wrStream);
callback(undefined, this);
}
private userMediaHasVideo(callback) {
// If the user is going to publish its screen there's a video source
if (this.isScreenRequested) {
callback(true);
return;
} else {
// List all input devices and serach for a video kind
navigator.mediaDevices.enumerateDevices().then(function (mediaDevices) {
var videoInput = mediaDevices.filter(function (deviceInfo) {
return deviceInfo.kind === 'videoinput';
})[0];
callback(videoInput != null);
});
}
}
publishVideoCallback(error, sdpOfferParam, wp) {
if (error) {
return console.error("(publish) SDP offer error: "
+ JSON.stringify(error));
}
console.debug("Sending SDP offer to publish as "
+ this.getId(), sdpOfferParam);
this.openVidu.sendRequest("publishVideo", {
sdpOffer: sdpOfferParam,
doLoopback: this.displayMyRemote() || false,
audioActive: this.sendAudio,
videoActive: this.sendVideo
}, (error, response) => {
if (error) {
console.error("Error on publishVideo: " + JSON.stringify(error));
} else {
this.processSdpAnswer(response.sdpAnswer);
console.info("'Publisher' succesfully published to session");
}
});
}
startVideoCallback(error, sdpOfferParam, wp) {
if (error) {
return console.error("(subscribe) SDP offer error: "
+ JSON.stringify(error));
}
console.debug("Sending SDP offer to subscribe to "
+ this.getId(), sdpOfferParam);
this.openVidu.sendRequest("receiveVideoFrom", {
sender: this.getId(),
sdpOffer: sdpOfferParam
}, (error, response) => {
if (error) {
console.error("Error on recvVideoFrom: " + JSON.stringify(error));
} else {
this.processSdpAnswer(response.sdpAnswer);
}
});
}
private initWebRtcPeer(sdpOfferCallback) {
if (this.local) {
let userMediaConstraints = {
audio: this.sendAudio,
video: this.sendVideo
}
let options: any = {
videoStream: this.wrStream,
mediaConstraints: userMediaConstraints,
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
}
if (this.dataChannel) {
options.dataChannelConfig = {
id: this.getChannelName(),
onopen: this.onDataChannelOpen,
onclose: this.onDataChannelClosed
};
options.dataChannels = true;
}
if (this.displayMyRemote()) {
this.wp = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, error => {
if (error) {
return console.error(error);
}
this.wp.generateOffer(sdpOfferCallback.bind(this));
});
} else {
this.wp = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, error => {
if (error) {
return console.error(error);
}
this.wp.generateOffer(sdpOfferCallback.bind(this));
});
}
this.isPublisherPublished = true;
this.ee.emitEvent('stream-created-by-publisher');
} else {
let offerConstraints = {
audio: this.recvAudio,
video: this.recvVideo
};
console.debug("'Session.subscribe(Stream)' called. Constraints of generate SDP offer",
offerConstraints);
let options = {
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
mediaConstraints: offerConstraints
}
this.wp = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, error => {
if (error) {
return console.error(error);
}
this.wp.generateOffer(sdpOfferCallback.bind(this));
});
}
console.debug("Waiting for SDP offer to be generated ("
+ (this.local ? "local" : "remote") + " 'Stream': " + this.getId() + ")");
}
publish() {
// FIXME: Throw error when stream is not local
if (this.isReadyToPublish) {
this.initWebRtcPeer(this.publishVideoCallback);
} else {
this.ee.once('stream-ready', streamEvent => {
this.publish();
});
}
// FIXME: Now we have coupled connecting to a room and adding a
// stream to this room. But in the new API, there are two steps.
// This is the second step. For now, it do nothing.
}
subscribe() {
// FIXME: In the current implementation all participants are subscribed
// automatically to all other participants. We use this method only to
// negotiate SDP
this.initWebRtcPeer(this.startVideoCallback);
}
processSdpAnswer(sdpAnswer) {
let answer = new RTCSessionDescription({
type: 'answer',
sdp: sdpAnswer,
});
console.debug(this.getId() + ": set peer connection with recvd SDP answer",
sdpAnswer);
let participantId = this.getId();
let pc = this.wp.peerConnection;
pc.setRemoteDescription(answer, () => {
// Avoids to subscribe to your own stream remotely
// except when showMyRemote is true
if (!this.local || this.displayMyRemote()) {
this.wrStream = pc.getRemoteStreams()[0];
console.debug("Peer remote stream", this.wrStream);
if (this.wrStream != undefined) {
this.emitSrcEvent(this.wrStream);
if (this.wrStream.getAudioTracks()[0] != null) {
this.speechEvent = kurentoUtils.WebRtcPeer.hark(this.wrStream, { threshold: this.room.thresholdSpeaker });
this.speechEvent.on('speaking', () => {
this.room.addParticipantSpeaking(participantId);
this.room.emitEvent('stream-speaking', [{
participantId: participantId
}]);
});
this.speechEvent.on('stopped_speaking', () => {
this.room.removeParticipantSpeaking(participantId);
this.room.emitEvent('stream-stopped-speaking', [{
participantId: participantId
}]);
});
}
}
for (let videoElement of this.videoElements) {
let thumbnailId = videoElement.thumb;
let video = videoElement.video;
video.srcObject = this.wrStream;
video.oncanplay = () => {
if (this.local && this.displayMyRemote()) {
console.info("Your own remote 'Stream' with id [" + this.getId() + "] video is now playing");
this.ee.emitEvent('remote-video-is-playing', [{
element: video
}]);
} else if (!this.local && !this.displayMyRemote()) {
console.info("Remote 'Stream' with id [" + this.getId() + "] video is now playing");
this.ee.emitEvent('video-is-playing', [{
element: video
}]);
}
//show(thumbnailId);
//this.hideSpinner(this.getId());
};
}
this.room.emitEvent('stream-subscribed', [{
stream: this
}]);
}
}, error => {
console.error(this.getId() + ": Error setting SDP to the peer connection: "
+ JSON.stringify(error));
});
}
unpublish() {
if (this.wp) {
this.wp.dispose();
} else {
if (this.wrStream) {
this.wrStream.getAudioTracks().forEach(function (track) {
track.stop && track.stop()
})
this.wrStream.getVideoTracks().forEach(function (track) {
track.stop && track.stop()
})
}
}
if (this.speechEvent) {
this.speechEvent.stop();
}
console.info(this.getId() + ": Stream '" + this.id + "' unpublished");
}
dispose() {
function disposeElement(element) {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
}
this.elements.forEach(e => disposeElement(e));
//this.videoElements.forEach(ve => disposeElement(ve.video));
disposeElement("progress-" + this.getId());
if (this.wp) {
this.wp.dispose();
} else {
if (this.wrStream) {
this.wrStream.getAudioTracks().forEach(function (track) {
track.stop && track.stop()
})
this.wrStream.getVideoTracks().forEach(function (track) {
track.stop && track.stop()
})
}
}
if (this.speechEvent) {
this.speechEvent.stop();
}
console.info((this.local ? "Local " : "Remote ") + "'Stream' with id [" + this.getId() + "]' has been succesfully disposed");
}
private configureOptions(options) {
if (options.id) {
this.id = options.id;
} else {
this.id = "webcam";
}
this.connection = options.connection;
this.recvVideo = options.recvVideo || false;
this.recvAudio = options.recvAudio || false;
this.sendVideo = options.sendVideo;
this.sendAudio = options.sendAudio;
this.activeAudio = options.activeAudio;
this.activeVideo = options.activeVideo;
this.dataChannel = options.data || false;
this.mediaConstraints = options.mediaConstraints;
}
configureScreenOptions(options) {
if (options.id) {
this.id = options.id;
} else {
this.id = "screen";
}
this.recvVideo = options.recvVideo || false;
this.recvAudio = options.recvAudio || false;
this.sendVideo = options.sendVideo;
this.sendAudio = options.sendAudio;
this.activeAudio = options.activeAudio;
this.activeVideo = options.activeVideo;
this.dataChannel = options.data || false;
this.mediaConstraints = options.mediaConstraints;
this.ee.emitEvent('can-request-screen');
}
}

View File

@ -0,0 +1,3 @@
declare module "kurento-jsonrpc";
declare module "webrtc-adapter";
declare module "kurento-utils";

View File

@ -0,0 +1,5 @@
export * from './OpenViduInternal';
export * from './Connection';
export * from './SessionInternal';
export * from './Stream';
export * from './OpenViduError';

View File

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

View File

@ -0,0 +1,164 @@
// Last time updated at Feb 16, 2017, 08:32:23
// 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(sourceId == 'firefox') {
navigator.mozGetUserMedia(screen_constraints, onSuccess, onFailure);
}
else navigator.webkitGetUserMedia(screen_constraints, onSuccess, onFailure);
});
*/
window.getScreenId = function (callback) {
// for Firefox:
// sourceId == 'firefox'
// screen_constraints = {...}
if (!!navigator.mozGetUserMedia) {
callback(null, 'firefox', {
video: {
mozMediaSource: 'window',
mediaSource: 'window'
}
});
return;
}
window.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));
}
if (event.data.chromeExtensionStatus) {
callback(event.data.chromeExtensionStatus, null, getScreenConstraints(event.data.chromeExtensionStatus));
}
// this event listener is no more needed
window.removeEventListener('message', onIFrameCallback);
}
setTimeout(postGetSourceIdMessage, 100);
};
function getScreenConstraints(error, sourceId) {
var screen_constraints = {
audio: false,
video: {
mandatory: {
chromeMediaSource: error ? 'screen' : 'desktop',
maxWidth: window.screen.width > 1920 ? window.screen.width : 1920,
maxHeight: window.screen.height > 1080 ? window.screen.height : 1080
},
optional: []
}
};
if (sourceId) {
screen_constraints.video.mandatory.chromeMediaSourceId = sourceId;
}
return screen_constraints;
}
function postGetSourceIdMessage() {
if (!iframe) {
loadIFrame(postGetSourceIdMessage);
return;
}
if (!iframe.isLoaded) {
setTimeout(postGetSourceIdMessage, 100);
return;
}
iframe.contentWindow.postMessage({
captureSourceId: true
}, '*');
}
var iframe;
// this function is used in RTCMultiConnection v3
window.getScreenConstraints = function (callback) {
loadIFrame(function () {
getScreenId(function (error, sourceId, screen_constraints) {
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://www.webrtc-experiment.com/getSourceId/'; // https://wwww.yourdomain.com/getScreenId.html
iframe.style.display = 'none';
(document.body || document.documentElement).appendChild(iframe);
}
window.getChromeExtensionStatus = function (callback) {
// for Firefox:
if (!!navigator.mozGetUserMedia) {
callback('installed-enabled');
return;
}
window.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
window.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 = getScreenId;
exports.getChromeExtensionStatus = getChromeExtensionStatus;

View File

@ -0,0 +1,127 @@
// global variables
var chromeMediaSource = 'screen';
var sourceId;
var screenCallback;
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') {
chromeMediaSource = '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);
}
}
// this method can be used to check if chrome extension is installed & enabled.
function isChromeExtensionAvailable(callback) {
if (isFirefox)
return callback(false);
if (chromeMediaSource == 'desktop')
return callback('isFirefox');
// ask extension if it is available
window.postMessage('are-you-there', '*');
setTimeout(function () {
if (chromeMediaSource == 'screen') {
callback('unavailable');
}
else
callback('available');
}, 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', '*');
}
function getChromeExtensionStatus(extensionid, callback) {
if (isFirefox)
return callback('not-chrome');
if (arguments.length != 2) {
callback = extensionid;
extensionid = 'ajhifddimkapgcifgcodmmfdlknahffk'; // 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(extensionid == extensionid ? 'installed-enabled' : 'installed-disabled');
}
else
callback('installed-enabled');
}, 2000);
};
image.onerror = function () {
callback('not-installed');
};
}
// this function explains how to use above methods/objects
function getScreenConstraints(callback) {
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) {
getSourceId(function () {
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.isChromeExtensionAvailable = isChromeExtensionAvailable;
exports.getChromeExtensionStatus = getChromeExtensionStatus;
exports.getSourceId = getSourceId;

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

1
openvidu-client/.gitignore vendored Normal file
View File

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

23
openvidu-client/.project Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>kurento-room-client-openvic</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,4 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/test/java=UTF-8
encoding/<project>=UTF-8

View File

@ -0,0 +1,5 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

13
openvidu-client/README.md Normal file
View File

@ -0,0 +1,13 @@
[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![Documentation badge](https://readthedocs.org/projects/fiware-orion/badge/?version=latest)](http://openvidu.io/docs/home/)
[![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://groups.google.com/forum/#!forum/openvidu)
[![][OpenViduLogo]](http://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

105
openvidu-client/pom.xml Normal file
View File

@ -0,0 +1,105 @@
<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>1.1.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>http://openvidu.io</organizationUrl>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>org.kurento</groupId>
<artifactId>kurento-jsonrpc-client</artifactId>
</dependency>
<!-- <dependency>
<groupId>io.openvidu</groupId>
<artifactId>openvidu-server</artifactId>
</dependency>-->
<dependency>
<groupId>org.kurento</groupId>
<artifactId>kurento-jsonrpc-client-jetty</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<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

@ -0,0 +1,223 @@
/*
* (C) Copyright 2017 OpenVidu (http://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_DATACHANNELS_PARAM;
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.SENDMESSAGE_ROOM_PARAM;
import static io.openvidu.client.internal.ProtocolElements.SENDMESSAGE_USER_PARAM;
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, Boolean dataChannels)
throws IOException {
JsonObject params = new JsonObject();
params.addProperty(JOINROOM_ROOM_PARAM, roomName);
params.addProperty(JOINROOM_USER_PARAM, userName);
if (dataChannels != null) {
params.addProperty(JOINROOM_DATACHANNELS_PARAM, dataChannels);
}
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_USER_PARAM, userName);
params.addProperty(SENDMESSAGE_ROOM_PARAM, roomName);
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

@ -0,0 +1,75 @@
/*
* (C) Copyright 2017 OpenVidu (http://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),
TRANSPORT_ERROR_CODE(803), TRANSPORT_RESPONSE_ERROR_CODE(802), TRANSPORT_REQUEST_ERROR_CODE(
801),
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_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(101),
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),
USER_METADATA_FORMAT_INVALID_ERROR_CODE(500);
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 "CODE: " + getCodeValue() + ". EXCEPTION: " + super.toString();
}
}

View File

@ -0,0 +1,222 @@
/*
* (C) Copyright 2017 OpenVidu (http://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 room = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTSENDMESSAGE_ROOM_PARAM, String.class);
String user = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTSENDMESSAGE_USER_PARAM, String.class);
String message = JsonRoomUtils.getRequestParam(request,
ProtocolElements.PARTICIPANTSENDMESSAGE_MESSAGE_PARAM, String.class);
SendMessageInfo eventInfo = new SendMessageInfo(room, user, message);
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

@ -0,0 +1,33 @@
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

@ -0,0 +1,70 @@
/*
* (C) Copyright 2017 OpenVidu (http://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();
}
}

View File

@ -0,0 +1,107 @@
/*
* (C) Copyright 2017 OpenVidu (http://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;
import org.kurento.jsonrpc.message.Request;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code;
/**
* JSON tools for extracting info from request or response elements.
*
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
*/
public class JsonRoomUtils {
public static <T> T getRequestParam(Request<JsonObject> request, String paramName, Class<T> type) {
return getRequestParam(request, paramName, type, false);
}
public static <T> T getRequestParam(Request<JsonObject> request, String paramName, Class<T> type,
boolean allowNull) {
JsonObject params = request.getParams();
if (params == null) {
if (!allowNull) {
throw new OpenViduException(Code.TRANSPORT_REQUEST_ERROR_CODE,
"Invalid request lacking parameter '" + paramName + "'");
} else {
return null;
}
}
return getConverted(params.get(paramName), paramName, type, allowNull);
}
public static <T> T getResponseProperty(JsonElement result, String property, Class<T> type) {
return getResponseProperty(result, property, type, false);
}
public static <T> T getResponseProperty(JsonElement result, String property, Class<T> type,
boolean allowNull) {
if (!(result instanceof JsonObject)) {
throw new OpenViduException(Code.TRANSPORT_RESPONSE_ERROR_CODE,
"Invalid response format. The response '" + result + "' should be a Json object");
}
return getConverted(result.getAsJsonObject().get(property), property, type, allowNull);
}
public static JsonArray getResponseArray(JsonElement result) {
if (!result.isJsonArray()) {
throw new OpenViduException(Code.TRANSPORT_RESPONSE_ERROR_CODE,
"Invalid response format. The response '" + result + "' should be a Json array");
}
return result.getAsJsonArray();
}
@SuppressWarnings("unchecked")
private static <T> T getConverted(JsonElement paramValue, String property, Class<T> type,
boolean allowNull) {
if (paramValue == null) {
if (allowNull) {
return null;
} else {
throw new OpenViduException(Code.TRANSPORT_ERROR_CODE, "Invalid method lacking parameter '"
+ property + "'");
}
}
if (type == String.class) {
if (paramValue.isJsonPrimitive()) {
return (T) paramValue.getAsString();
}
}
if (type == Integer.class) {
if (paramValue.isJsonPrimitive()) {
return (T) Integer.valueOf(paramValue.getAsInt());
}
}
if (type == JsonArray.class) {
if (paramValue.isJsonArray()) {
return (T) paramValue.getAsJsonArray();
}
}
throw new OpenViduException(Code.TRANSPORT_ERROR_CODE, "Param '" + property + "' with value '"
+ paramValue + "' is not a " + type.getName());
}
}

View File

@ -0,0 +1,55 @@
/*
* (C) Copyright 2017 OpenVidu (http://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;
/**
*
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
*
* @see Notification
*/
public class MediaErrorInfo extends Notification {
private String description;
public MediaErrorInfo(String description) {
super(ProtocolElements.MEDIAERROR_METHOD);
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
if (getMethod() != null) {
builder.append("method=").append(getMethod()).append(", ");
}
if (description != null) {
builder.append("description=").append(description);
}
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,90 @@
/*
* (C) Copyright 2017 OpenVidu (http://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;
/**
* Wrapper for server events.
*
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
*/
public abstract class Notification {
public enum Method {
ICECANDIDATE_METHOD(ProtocolElements.ICECANDIDATE_METHOD), MEDIAERROR_METHOD(
ProtocolElements.MEDIAERROR_METHOD), PARTICIPANTJOINED_METHOD(
ProtocolElements.PARTICIPANTJOINED_METHOD), PARTICIPANTLEFT_METHOD(
ProtocolElements.PARTICIPANTLEFT_METHOD), PARTICIPANTEVICTED_METHOD(
ProtocolElements.PARTICIPANTEVICTED_METHOD), PARTICIPANTPUBLISHED_METHOD(
ProtocolElements.PARTICIPANTPUBLISHED_METHOD), PARTICIPANTUNPUBLISHED_METHOD(
ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD), ROOMCLOSED_METHOD(
ProtocolElements.ROOMCLOSED_METHOD), PARTICIPANTSENDMESSAGE_METHOD(
ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD);
private String methodValue;
private Method(String val) {
this.methodValue = val;
}
public String getMethodValue() {
return methodValue;
}
public static Method getFromValue(String val) {
for (Method m : Method.values()) {
if (m.methodValue.equals(val)) {
return m;
}
}
return null;
}
@Override
public String toString() {
return getMethodValue().toString();
}
}
private Method method;
public Notification(Method method) {
this.setMethod(method);
}
public Notification(String methodValue) {
this(Method.getFromValue(methodValue));
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
if (method != null) {
builder.append("method=").append(method);
}
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,30 @@
/*
* (C) Copyright 2017 OpenVidu (http://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 ParticipantEvictedInfo extends Notification {
public ParticipantEvictedInfo() {
super(ProtocolElements.PARTICIPANTEVICTED_METHOD);
}
}

View File

@ -0,0 +1,54 @@
/*
* (C) Copyright 2017 OpenVidu (http://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 ParticipantJoinedInfo extends Notification {
private String id;
public ParticipantJoinedInfo(String id) {
super(ProtocolElements.PARTICIPANTJOINED_METHOD);
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
if (getMethod() != null) {
builder.append("method=").append(getMethod()).append(", ");
}
if (id != null) {
builder.append("id=").append(id);
}
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,54 @@
/*
* (C) Copyright 2017 OpenVidu (http://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 ParticipantLeftInfo extends Notification {
private String name;
public ParticipantLeftInfo(String name) {
super(ProtocolElements.PARTICIPANTLEFT_METHOD);
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
if (getMethod() != null) {
builder.append("method=").append(getMethod()).append(", ");
}
if (name != null) {
builder.append("name=").append(name);
}
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,69 @@
/*
* (C) Copyright 2017 OpenVidu (http://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;
import java.util.List;
/**
* @see Notification
*
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
*/
public class ParticipantPublishedInfo extends Notification {
private String id;
private List<String> streams;
public ParticipantPublishedInfo(String id, List<String> streams) {
super(ProtocolElements.PARTICIPANTPUBLISHED_METHOD);
this.id = id;
this.streams = streams;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<String> getStreams() {
return streams;
}
public void setStreams(List<String> streams) {
this.streams = streams;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
if (getMethod() != null) {
builder.append("method=").append(getMethod()).append(", ");
}
if (id != null) {
builder.append("id=").append(id).append(", ");
}
if (streams != null) {
builder.append("streams=").append(streams);
}
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,54 @@
/*
* (C) Copyright 2017 OpenVidu (http://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 ParticipantUnpublishedInfo extends Notification {
private String name;
public ParticipantUnpublishedInfo(String name) {
super(ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD);
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
if (getMethod() != null) {
builder.append("method=").append(getMethod()).append(", ");
}
if (name != null) {
builder.append("name=").append(name);
}
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,113 @@
/*
* (C) Copyright 2017 OpenVidu (http://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;
/**
* This class defines constant values of client-server messages and their parameters.
*
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
*/
public class ProtocolElements {
// ---------------------------- CLIENT REQUESTS -----------------------
public static final String SENDMESSAGE_ROOM_METHOD = "sendMessage";
public static final String SENDMESSAGE_USER_PARAM = "userMessage";
public static final String SENDMESSAGE_ROOM_PARAM = "roomMessage";
public static final String SENDMESSAGE_MESSAGE_PARAM = "message";
public static final String LEAVEROOM_METHOD = "leaveRoom";
public static final String JOINROOM_METHOD = "joinRoom";
public static final String JOINROOM_USER_PARAM = "user";
public static final String JOINROOM_TOKEN_PARAM = "token";
public static final String JOINROOM_ROOM_PARAM = "session";
public static final String JOINROOM_METADATA_PARAM = "metadata";
public static final String JOINROOM_SECRET_PARAM = "secret";
public static final String JOINROOM_DATACHANNELS_PARAM = "dataChannels";
public static final String JOINROOM_PEERID_PARAM = "id";
public static final String JOINROOM_PEERSTREAMS_PARAM = "streams";
public static final String JOINROOM_PEERSTREAMID_PARAM = "id";
public static final String JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM = "audioActive";
public static final String JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM = "videoActive";
public static final String PUBLISHVIDEO_METHOD = "publishVideo";
public static final String PUBLISHVIDEO_SDPOFFER_PARAM = "sdpOffer";
public static final String PUBLISHVIDEO_DOLOOPBACK_PARAM = "doLoopback";
public static final String PUBLISHVIDEO_SDPANSWER_PARAM = "sdpAnswer";
public static final String PUBLISHVIDEO_AUDIOACTIVE_PARAM = "audioActive";
public static final String PUBLISHVIDEO_VIDEOACTIVE_PARAM = "videoActive";
public static final String UNPUBLISHVIDEO_METHOD = "unpublishVideo";
public static final String RECEIVEVIDEO_METHOD = "receiveVideoFrom";
public static final String RECEIVEVIDEO_SDPOFFER_PARAM = "sdpOffer";
public static final String RECEIVEVIDEO_SENDER_PARAM = "sender";
public static final String RECEIVEVIDEO_SDPANSWER_PARAM = "sdpAnswer";
public static final String UNSUBSCRIBEFROMVIDEO_METHOD = "unsubscribeFromVideo";
public static final String UNSUBSCRIBEFROMVIDEO_SENDER_PARAM = "sender";
public static final String ONICECANDIDATE_METHOD = "onIceCandidate";
public static final String ONICECANDIDATE_EPNAME_PARAM = "endpointName";
public static final String ONICECANDIDATE_CANDIDATE_PARAM = "candidate";
public static final String ONICECANDIDATE_SDPMIDPARAM = "sdpMid";
public static final String ONICECANDIDATE_SDPMLINEINDEX_PARAM = "sdpMLineIndex";
public static final String CUSTOMREQUEST_METHOD = "customRequest";
// ---------------------------- SERVER RESPONSES & EVENTS -----------------
public static final String PARTICIPANTJOINED_METHOD = "participantJoined";
public static final String PARTICIPANTJOINED_USER_PARAM = "id";
public static final String PARTICIPANTJOINED_METADATA_PARAM = "metadata";
public static final String PARTICIPANTLEFT_METHOD = "participantLeft";
public static final String PARTICIPANTLEFT_NAME_PARAM = "name";
public static final String PARTICIPANTEVICTED_METHOD = "participantEvicted";
public static final String PARTICIPANTPUBLISHED_METHOD = "participantPublished";
public static final String PARTICIPANTPUBLISHED_USER_PARAM = "id";
public static final String PARTICIPANTPUBLISHED_STREAMS_PARAM = "streams";
public static final String PARTICIPANTPUBLISHED_STREAMID_PARAM = "id";
public static final String PARTICIPANTPUBLISHED_AUDIOACTIVE_PARAM = "audioActive";
public static final String PARTICIPANTPUBLISHED_VIDEOACTIVE_PARAM = "videoActive";
public static final String PARTICIPANTUNPUBLISHED_METHOD = "participantUnpublished";
public static final String PARTICIPANTUNPUBLISHED_NAME_PARAM = "name";
public static final String PARTICIPANTSENDMESSAGE_METHOD = "sendMessage";
public static final String PARTICIPANTSENDMESSAGE_USER_PARAM = "user";
public static final String PARTICIPANTSENDMESSAGE_ROOM_PARAM = "room";
public static final String PARTICIPANTSENDMESSAGE_MESSAGE_PARAM = "message";
public static final String ROOMCLOSED_METHOD = "roomClosed";
public static final String ROOMCLOSED_ROOM_PARAM = "room";
public static final String MEDIAERROR_METHOD = "mediaError";
public static final String MEDIAERROR_ERROR_PARAM = "error";
public static final String ICECANDIDATE_METHOD = "iceCandidate";
public static final String ICECANDIDATE_EPNAME_PARAM = "endpointName";
public static final String ICECANDIDATE_CANDIDATE_PARAM = "candidate";
public static final String ICECANDIDATE_SDPMID_PARAM = "sdpMid";
public static final String ICECANDIDATE_SDPMLINEINDEX_PARAM = "sdpMLineIndex";
public static final String CUSTOM_NOTIFICATION = "custonNotification";
}

View File

@ -0,0 +1,54 @@
/*
* (C) Copyright 2017 OpenVidu (http://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 RoomClosedInfo extends Notification {
private String room;
public RoomClosedInfo(String room) {
super(ProtocolElements.ROOMCLOSED_METHOD);
this.room = room;
}
public String getRoom() {
return room;
}
public void setRoom(String room) {
this.room = room;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
if (getMethod() != null) {
builder.append("method=").append(getMethod()).append(", ");
}
if (room != null) {
builder.append("room=").append(room);
}
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,80 @@
/*
* (C) Copyright 2017 OpenVidu (http://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 SendMessageInfo extends Notification {
private String room;
private String user;
private String message;
public SendMessageInfo(String room, String user, String message) {
super(ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD);
this.room = room;
this.user = user;
this.message = message;
}
public String getRoom() {
return room;
}
public void setRoom(String room) {
this.room = room;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
if (getMethod() != null) {
builder.append("method=").append(getMethod()).append(", ");
}
if (room != null) {
builder.append("room=").append(room).append(", ");
}
if (user != null) {
builder.append("user=").append(user).append(", ");
}
if (message != null) {
builder.append("message=").append(message);
}
builder.append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,74 @@
/*
* (C) Copyright 2017 OpenVidu (http://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.test;
import static io.openvidu.client.internal.ProtocolElements.*;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.kurento.jsonrpc.client.JsonRpcClient;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.openvidu.client.OpenViduClient;
import io.openvidu.client.ServerJsonRpcHandler;
/**
* Unit tests for the room client protocol.
*
* @author Radu Tom Vlad (rvlad@naevatec.com)
* @since 6.3.1
*/
public class OpenViduClientTest {
private OpenViduClient client;
private ServerJsonRpcHandler serverHandler;
private JsonRpcClient jsonRpcClient;
@Before
public void setup() {
jsonRpcClient = mock(JsonRpcClient.class);
serverHandler = new ServerJsonRpcHandler();
client = new OpenViduClient(jsonRpcClient, serverHandler);
}
@Test
public void testRoomJoin() throws IOException {
JsonObject params = new JsonObject();
params.addProperty(JOINROOM_ROOM_PARAM, "room");
params.addProperty(JOINROOM_USER_PARAM, "user");
JsonObject result = new JsonObject();
JsonArray value = new JsonArray();
result.add("value", value);
Map<String, List<String>> joinResult = new HashMap<String, List<String>>();
when(jsonRpcClient.sendRequest(JOINROOM_METHOD, params)).thenReturn(result);
assertThat(client.joinRoom("room", "user", null), is(joinResult));
}
}

View File

@ -1,13 +0,0 @@
# Output of 'npm pack'
*.tgz
**/node_modules/
**/.angular/
**/.vscode
node_modules
dist/
docs/
coverage/**

View File

@ -1,5 +0,0 @@
build
node_modules
.github
dist
docs

View File

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

View File

@ -1,43 +0,0 @@
# Openvidu Angular TestAPP
## Architechture
```
openvidu-components-angular
└─── src (openvidu-components-testapp)
└───projects
└─── openvidu-components-angular
```
## How to develop with ease:
Run `ng serve` for a dev server.
Run, in a new terminal, `npm run lib:serve` for serving the openvidu-components-angular library with live reload for listening changes
## Code scaffolding
For generate new components in openvidu-components-angular:
```bash
ng g component components/component-name --project=openvidu-components-angular
```
## Build library
```bash
npm run lib:build
```
## Publishing
After the library is built, tun the following command:
```bash
cd dist/ && npm publish
```

View File

@ -1,157 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"openvidu-components-testapp": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": {
"base": "dist/openvidu-components-testapp",
"browser": ""
},
"index": "src/index.html",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.scss"],
"scripts": [],
"browser": "src/main.ts"
},
"configurations": {
"development": {
"optimization": false,
"outputHashing": "all",
"sourceMap": true,
"namedChunks": false,
"extractLicenses": true
},
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"proxyConfig": "src/proxy.conf.json",
"buildTarget": "openvidu-components-testapp:build"
},
"configurations": {
"development": {
"buildTarget": "openvidu-components-testapp:build:development"
},
"production": {
"buildTarget": "openvidu-components-testapp:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "openvidu-components-testapp:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.scss"],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"],
"exclude": ["**/node_modules/**"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "openvidu-components-testapp:serve"
},
"configurations": {
"production": {
"devServerTarget": "openvidu-components-testapp:serve:production"
}
}
}
}
},
"openvidu-components-angular": {
"projectType": "library",
"root": "projects/openvidu-components-angular",
"sourceRoot": "projects/openvidu-components-angular/src",
"prefix": "ov",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "projects/openvidu-components-angular/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "projects/openvidu-components-angular/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "projects/openvidu-components-angular/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/openvidu-components-angular/src/test.ts",
"tsConfig": "projects/openvidu-components-angular/tsconfig.spec.json",
"karmaConfig": "projects/openvidu-components-angular/karma.conf.js"
}
}
}
}
},
"cli": {
"analytics": false
}
}

View File

@ -1,621 +0,0 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
let url = '';
describe('Testing API Directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
url = `${TestAppConfig.appUrl}&roomName=API_DIRECTIVES_${Math.floor(Math.random() * 1000)}`;
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
if (await utils.isPresent('#session-container')) {
await utils.leaveRoom();
}
} catch (error) {}
await browser.sleep(500);
await browser.quit();
});
it('should set the MINIMAL UI', async () => {
await browser.get(`${url}&minimal=true`);
// Checking if prejoin page exist
await utils.checkPrejoinIsPresent();
// Checking if audio detection is not displayed
expect(await utils.isPresent('#audio-wave-container')).toBeFalse();
const joinButton = await utils.waitForElement('#join-button');
await joinButton.click();
// Checking if session container is present
await utils.checkSessionIsPresent();
// Checking if layout is present
await utils.checkLayoutPresent();
// Checking if stream component is present
utils.checkStreamIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if screenshare button is not present
expect(await utils.isPresent('#screenshare-btn')).toBeFalse();
// Checking if more options button is not present
expect(await utils.isPresent('#more-options-btn')).toBeFalse();
// Checking if participants panel button is not present
expect(await utils.isPresent('#participants-panel-btn')).toBeFalse();
// Checking if activities panel button is not present
expect(await utils.isPresent('#activities-panel-btn')).toBeFalse();
// Checking if logo is not displayed
expect(await utils.isPresent('#branding-logo')).toBeFalse();
// Checking if session name is not displayed
expect(await utils.isPresent('#session-name')).toBeFalse();
// Checking if nickname is not displayed
expect(await utils.getNumberOfElements('#participant-name-container')).toEqual(0);
// Checking if audio detection is not displayed
expect(await utils.isPresent('#audio-wave-container')).toBeFalse();
// Checking if settings button is not displayed
expect(await utils.isPresent('#settings-container')).toBeFalse();
});
it('should change the UI LANG in prejoin page', async () => {
await browser.get(`${url}&lang=es`);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('.language-selector');
const element = await utils.waitForElement('#join-button');
expect(await element.getText()).toEqual('Unirme ahora');
});
it('should change the UI LANG in room page', async () => {
await browser.get(`${url}&prejoin=false&lang=es`);
await utils.checkLayoutPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('#default-settings-panel')).toBeTrue();
const panelTitle = await utils.waitForElement('.panel-title');
expect(await panelTitle.getText()).toEqual('Configuración');
const element = await utils.waitForElement('.lang-name');
expect(await element.getAttribute('innerText')).toEqual('Español expand_more');
});
it('should override the LANG OPTIONS', async () => {
await browser.get(`${url}&prejoin=true&langOptions=true`);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('.language-selector');
await utils.clickOn('.language-selector');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.language-option')).toEqual(2);
await utils.clickOn('.language-option');
await browser.sleep(500);
await utils.clickOn('#join-button');
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('#settings-container');
await utils.waitForElement('.full-lang-button');
await utils.clickOn('.full-lang-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.language-option')).toEqual(2);
});
it('should show the PREJOIN page', async () => {
await browser.get(`${url}&prejoin=true`);
await utils.checkPrejoinIsPresent();
});
it('should not show the PREJOIN page', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
});
it('should join to Room', async () => {
await browser.get(`${url}`);
// Checking if prejoin page exist
await utils.checkPrejoinIsPresent();
const joinButton = await utils.waitForElement('#join-button');
await joinButton.click();
// Checking if session container is present
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Checking if screenshare button is not present
expect(await utils.isPresent('#screenshare-btn')).toBeTrue();
});
it('should show the token error WITH prejoin page', async () => {
const fixedUrl = `${TestAppConfig.appUrl}&roomName=TEST_TOKEN&participantName=PNAME`;
await browser.get(`${fixedUrl}`);
// Checking if prejoin page exist
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#join-button');
await utils.clickOn('#join-button');
// Checking if session container is present
await utils.checkSessionIsPresent();
// Starting new browser for adding a new participant
const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript);
// Go to first tab
const tabs = await browser.getAllWindowHandles();
browser.switchTo().window(tabs[1]);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#join-button');
await utils.clickOn('#join-button');
// Checking if token error is displayed
await utils.waitForElement('#token-error');
expect(await utils.isPresent('#token-error')).toBeTrue();
});
it('should show the token error WITHOUT prejoin page', async () => {
const fixedUrl = `${TestAppConfig.appUrl}&roomName=TOKEN_ERROR&prejoin=false&participantName=PNAME`;
await browser.get(`${fixedUrl}`);
// Checking if session container is present
await utils.checkSessionIsPresent();
// Starting new browser for adding a new participant
const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript);
// Go to first tab
const tabs = await browser.getAllWindowHandles();
browser.switchTo().window(tabs[1]);
// Checking if token error is displayed
await utils.waitForElement('#openvidu-dialog');
expect(await utils.isPresent('#openvidu-dialog')).toBeTrue();
});
it('should run the app with VIDEO DISABLED in prejoin page', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=false`);
await utils.checkPrejoinIsPresent();
// Checking if video is displayed
await utils.waitForElement('#video-poster');
expect(await utils.getNumberOfElements('video')).toEqual(0);
await utils.waitForElement('#videocam_off');
await utils.clickOn('#join-button');
await utils.checkSessionIsPresent();
await utils.waitForElement('#videocam_off');
expect(await utils.isPresent('#videocam_off')).toBeTrue();
await utils.waitForElement('#video-poster');
expect(await utils.getNumberOfElements('video')).toEqual(0);
});
it('should run the app with VIDEO DISABLED and WITHOUT PREJOIN page', async () => {
await browser.get(`${url}&prejoin=false&videoEnabled=false`);
await utils.checkSessionIsPresent();
await utils.checkLayoutPresent();
// Checking if video is displayed
await utils.waitForElement('#video-poster');
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('#video-poster')).toEqual(1);
await utils.waitForElement('#videocam_off');
expect(await utils.isPresent('#videocam_off')).toBeTrue();
});
it('should run the app with AUDIO DISABLED in prejoin page', async () => {
await browser.get(`${url}&audioEnabled=false`);
await utils.checkPrejoinIsPresent();
// Checking if video is displayed
await utils.checkVideoElementIsPresent();
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await utils.waitForElement('#mic_off');
expect(await utils.isPresent('#mic_off')).toBeTrue();
await utils.clickOn('#join-button');
await utils.checkSessionIsPresent();
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await utils.waitForElement('#mic_off');
expect(await utils.isPresent('#mic_off')).toBeTrue();
});
it('should run the app with AUDIO DISABLED and WITHOUT PREJOIN page', async () => {
await browser.get(`${url}&prejoin=false&audioEnabled=false`);
await browser.sleep(1000);
await utils.checkSessionIsPresent();
// Checking if video is displayed
await utils.checkVideoElementIsPresent();
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await utils.waitForElement('#mic_off');
expect(await utils.isPresent('#mic_off')).toBeTrue();
});
it('should run the app without camera button', async () => {
await browser.get(`${url}&prejoin=false&cameraBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if camera button is not present
expect(await utils.isPresent('#camera-btn')).toBeFalse();
});
it('should run the app without microphone button', async () => {
await browser.get(`${url}&prejoin=false&microphoneBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if microphone button is not present
expect(await utils.isPresent('#microphone-btn')).toBeFalse();
});
it('should HIDE the SCREENSHARE button', async () => {
await browser.get(`${url}&prejoin=false&screenshareBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if screenshare button is not present
expect(await utils.isPresent('#screenshare-btn')).toBeFalse();
});
it('should HIDE the FULLSCREEN button', async () => {
await browser.get(`${url}&prejoin=false&fullscreenBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
await utils.toggleToolbarMoreOptions();
expect(await utils.getNumberOfElements('#fullscreen-btn')).toEqual(0);
});
xit('should HIDE the CAPTIONS button', async () => {
await browser.get(`${url}&prejoin=false&toolbarCaptionsBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
await utils.toggleToolbarMoreOptions();
// Checking if captions button is not present
expect(await utils.isPresent('#captions-btn')).toBeFalse();
await utils.clickOn('#toolbar-settings-btn');
await browser.sleep(500);
await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).toBeTrue();
expect(await utils.isPresent('#captions-opt')).toBeFalse();
});
it('should HIDE the TOOLBAR RECORDING button', async () => {
await browser.get(`${url}&prejoin=false&toolbarRecordingButton=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
await utils.toggleToolbarMoreOptions();
// Checking if recording button is not present
expect(await utils.isPresent('#recording-btn')).toBeFalse();
});
it('should HIDE the TOOLBAR BROADCASTING button', async () => {
await browser.get(`${url}&prejoin=false&toolbarBroadcastingButton=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
await utils.toggleToolbarMoreOptions();
// Checking if broadcasting button is not present
expect(await utils.isPresent('#broadcasting-btn')).toBeFalse();
});
it('should HIDE the TOOLBAR SETTINGS button', async () => {
await browser.get(`${url}&prejoin=false&toolbarSettingsBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.toggleToolbarMoreOptions();
expect(await utils.isPresent('#toolbar-settings-btn')).toBeFalse();
});
it('should HIDE the LEAVE button', async () => {
await browser.get(`${url}&prejoin=false&leaveBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if leave button is not present
expect(await utils.getNumberOfElements('#leave-btn')).toEqual(0);
});
it('should HIDE the ACTIVITIES PANEL button', async () => {
await browser.get(`${url}&prejoin=false&activitiesPanelBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if activities panel button is not present
expect(await utils.isPresent('#activities-panel-btn')).toBeFalse();
});
it('should HIDE the CHAT PANEL button', async () => {
await browser.get(`${url}&prejoin=false&chatPanelBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if chat panel button is not present
expect(await utils.isPresent('#chat-panel-btn')).toBeFalse();
});
it('should HIDE the PARTICIPANTS PANEL button', async () => {
await browser.get(`${url}&prejoin=false&participantsPanelBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if participants panel button is not present
expect(await utils.isPresent('#participants-panel-btn')).toBeFalse();
});
it('should HIDE the LOGO', async () => {
await browser.get(`${url}&prejoin=false&displayLogo=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if toolbar is present
await utils.waitForElement('#info-container');
expect(await utils.isPresent('#info-container')).toBeTrue();
// Checking if logo is not displayed
expect(await utils.isPresent('#branding-logo')).toBeFalse();
});
it('should HIDE the ROOM NAME', async () => {
await browser.get(`${url}&prejoin=false&displayRoomName=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if toolbar is present
await utils.waitForElement('#info-container');
expect(await utils.isPresent('#info-container')).toBeTrue();
// Checking if session name is not displayed
expect(await utils.isPresent('#session-name')).toBeFalse();
});
it('should HIDE the PARTICIPANT NAME', async () => {
await browser.get(`${url}&prejoin=false&displayParticipantName=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if stream component is present
await utils.checkStreamIsPresent();
// Checking if nickname is not present
expect(await utils.isPresent('#participant-name-container')).toBeFalse();
});
it('should HIDE the AUDIO DETECTION element', async () => {
await browser.get(`${url}&prejoin=false&displayAudioDetection=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if stream component is present
await utils.checkStreamIsPresent();
// Checking if audio detection is not present
expect(await utils.isPresent('#audio-wave-container')).toBeFalse();
});
it('should HIDE the STREAM VIDEO CONTROLS button', async () => {
await browser.get(`${url}&prejoin=false&videoControls=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if stream component is present
await utils.checkStreamIsPresent();
// Checking if settings button is not present
expect(await utils.isPresent('.stream-video-controls')).toBeFalse();
});
it('should HIDE the MUTE button in participants panel', async () => {
const roomName = 'e2etest';
const fixedUrl = `${TestAppConfig.appUrl}&prejoin=false&participantMuteBtn=false&roomName=${roomName}`;
await browser.get(fixedUrl);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
const participantsButton = await utils.waitForElement('#participants-panel-btn');
await participantsButton.click();
// Checking if participatns panel is displayed
await utils.waitForElement('#participants-container');
expect(await utils.isPresent('#participants-container')).toBeTrue();
// Checking remote participants item
expect(await utils.isPresent('#remote-participant-item')).toBeFalse();
// Starting new browser for adding a new participant
const newTabScript = `window.open("${fixedUrl}&participantName=SecondParticipant")`;
await browser.executeScript(newTabScript);
await browser.sleep(10000);
// Go to first tab
const tabs = await browser.getAllWindowHandles();
browser.switchTo().window(tabs[0]);
// Checking if mute button is not displayed in participant item
await utils.waitForElement('#remote-participant-item');
expect(await utils.isPresent('#remote-participant-item')).toBeTrue();
expect(await utils.isPresent('#mute-btn')).toBeFalse();
});
it('should HIDE the RECORDING ACTIVITY in activities panel', async () => {
let element;
const fixedUrl = `${url}&prejoin=false&activitiesPanelRecordingActivity=false`;
await browser.get(fixedUrl);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
element = await utils.waitForElement('#activities-panel-btn');
await element.click();
// Checking if participatns panel is displayed
await utils.waitForElement('#default-activities-panel');
expect(await utils.isPresent('#default-activities-panel')).toBeTrue();
// await browser.sleep(1000);
// Checking if recording activity exists
await utils.waitForElement('.activities-body-container');
expect(await utils.isPresent('ov-recording-activity')).toBeFalse();
});
it('should HIDE the BROADCASTING ACTIVITY in activities panel', async () => {
await browser.get(`${url}&prejoin=false&activitiesPanelBroadcastingActivity=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
await utils.waitForElement('#activities-panel-btn');
await utils.clickOn('#activities-panel-btn');
// Checking if participatns panel is displayed
await utils.waitForElement('#default-activities-panel');
expect(await utils.isPresent('#default-activities-panel')).toBeTrue();
// await browser.sleep(1000);
// Checking if recording activity exists
await utils.waitForElement('.activities-body-container');
expect(await utils.isPresent('ov-broadcasting-activity')).toBeFalse();
});
});

View File

@ -1,179 +0,0 @@
import { Builder, Key, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
//TODO: Uncomment when captions are implemented
// describe('Testing captions features', () => {
// let browser: WebDriver;
// let utils: OpenViduComponentsPO;
// async function createChromeBrowser(): Promise<WebDriver> {
// return await new Builder()
// .forBrowser(TestAppConfig.browserName)
// .withCapabilities(TestAppConfig.browserCapabilities)
// .setChromeOptions(TestAppConfig.browserOptions)
// .usingServer(TestAppConfig.seleniumAddress)
// .build();
// }
// beforeEach(async () => {
// browser = await createChromeBrowser();
// utils = new OpenViduComponentsPO(browser);
// });
// afterEach(async () => {
// await browser.quit();
// });
// it('should OPEN the CAPTIONS container', async () => {
// await browser.get(`${url}&prejoin=false`);
// await utils.checkSessionIsPresent();
// // Checking if toolbar is present
// await utils.checkToolbarIsPresent();
// // Open more options menu
// await utils.clickOn('#more-options-btn');
// await browser.sleep(500);
// // Checking if button panel is present
// await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).toBeTrue();
// // Checking if captions button is present
// await utils.waitForElement('#captions-btn');
// expect(await utils.isPresent('#captions-btn')).toBeTrue();
// await utils.clickOn('#captions-btn');
// await utils.waitForElement('.captions-container');
// });
// it('should OPEN the SETTINGS panel from captions button', async () => {
// await browser.get(`${url}&prejoin=false`);
// await utils.checkSessionIsPresent();
// // Checking if toolbar is present
// await utils.checkToolbarIsPresent();
// // Open more options menu
// await utils.clickOn('#more-options-btn');
// await browser.sleep(500);
// // Checking if button panel is present
// await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).toBeTrue();
// // Checking if captions button is present
// await utils.waitForElement('#captions-btn');
// expect(await utils.isPresent('#captions-btn')).toBeTrue();
// await utils.clickOn('#captions-btn');
// await utils.waitForElement('.captions-container');
// await utils.waitForElement('#caption-settings-btn');
// await utils.clickOn('#caption-settings-btn');
// await browser.sleep(500);
// await utils.waitForElement('.settings-container');
// expect(await utils.isPresent('.settings-container')).toBeTrue();
// await utils.waitForElement('ov-captions-settings');
// // Expect caption button is not present
// expect(await utils.isPresent('#caption-settings-btn')).toBeFalse();
// });
// it('should TOGGLE the CAPTIONS container from settings panel', async () => {
// await browser.get(`${url}&prejoin=false`);
// await utils.checkSessionIsPresent();
// // Checking if toolbar is present
// await utils.checkToolbarIsPresent();
// // Open more options menu
// await utils.clickOn('#more-options-btn');
// await browser.sleep(500);
// // Checking if button panel is present
// await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).toBeTrue();
// // Checking if captions button is present
// await utils.waitForElement('#captions-btn');
// expect(await utils.isPresent('#captions-btn')).toBeTrue();
// await utils.clickOn('#captions-btn');
// await utils.waitForElement('.captions-container');
// await utils.waitForElement('#caption-settings-btn');
// await utils.clickOn('#caption-settings-btn');
// await browser.sleep(500);
// await utils.waitForElement('.settings-container');
// expect(await utils.isPresent('.settings-container')).toBeTrue();
// await utils.waitForElement('ov-captions-settings');
// expect(await utils.isPresent('.captions-container')).toBeTrue();
// await utils.clickOn('#captions-toggle-slide');
// expect(await utils.isPresent('.captions-container')).toBeFalse();
// await browser.sleep(200);
// await utils.clickOn('#captions-toggle-slide');
// expect(await utils.isPresent('.captions-container')).toBeTrue();
// });
// it('should change the CAPTIONS language', async () => {
// await browser.get(`${url}&prejoin=false`);
// await utils.checkSessionIsPresent();
// // Checking if toolbar is present
// await utils.checkToolbarIsPresent();
// // Open more options menu
// await utils.clickOn('#more-options-btn');
// await browser.sleep(500);
// // Checking if button panel is present
// await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).toBeTrue();
// // Checking if captions button is present
// await utils.waitForElement('#captions-btn');
// expect(await utils.isPresent('#captions-btn')).toBeTrue();
// await utils.clickOn('#captions-btn');
// await utils.waitForElement('.captions-container');
// await utils.waitForElement('#caption-settings-btn');
// await utils.clickOn('#caption-settings-btn');
// await browser.sleep(500);
// await utils.waitForElement('.settings-container');
// expect(await utils.isPresent('.settings-container')).toBeTrue();
// await utils.waitForElement('ov-captions-settings');
// expect(await utils.isPresent('.captions-container')).toBeTrue();
// await utils.clickOn('.lang-button');
// await browser.sleep(500);
// await utils.clickOn('#es-ES');
// await utils.clickOn('.panel-close-button');
// const button = await utils.waitForElement('#caption-settings-btn');
// expect(await button.getText()).toEqual('settingsEspañol');
// });
// });

View File

@ -1,120 +0,0 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('Testing CHAT features', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
// leaving room if connected
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should send messages', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
await utils.togglePanel('chat');
await browser.sleep(500);
await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue();
const input = await utils.waitForElement('#chat-input');
await input.sendKeys('Test message');
await utils.clickOn('#send-btn');
await utils.waitForElement('.message');
await utils.getNumberOfElements('.message');
expect(await utils.isPresent('.message')).toBeTrue();
expect(await utils.getNumberOfElements('.message')).toEqual(1);
await input.sendKeys('Test message');
await utils.clickOn('#send-btn');
expect(await utils.getNumberOfElements('.message')).toEqual(2);
});
it('should receive a message', async () => {
const roomName = 'chattingE2E';
let pName = `participant${Math.floor(Math.random() * 1000)}`;
const fixedUrl = `${url}&prejoin=false&roomName=${roomName}`;
await browser.get(fixedUrl);
await browser.sleep(1000);
await utils.checkLayoutPresent();
// Starting new browser for adding a new participant
const newTabScript = `window.open("${fixedUrl}&participantName=${pName}")`;
await browser.executeScript(newTabScript);
const tabs = await browser.getAllWindowHandles();
browser.switchTo().window(tabs[1]);
await utils.checkLayoutPresent();
await utils.togglePanel('chat');
await browser.sleep(1000);
await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue();
const input = await utils.waitForElement('#chat-input');
await input.sendKeys('test message');
await utils.clickOn('#send-btn');
// Go to first tab
browser.switchTo().window(tabs[0]);
await utils.waitForElement('.snackbarNotification');
await utils.togglePanel('chat');
await browser.sleep(1000);
await utils.waitForElement('.message');
const participantName = await utils.waitForElement('.participant-name-container>p');
expect(await utils.getNumberOfElements('.message')).toEqual(1);
expect(await participantName.getText()).toEqual(pName);
});
it('should send an url message and converts in a link', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
await utils.togglePanel('chat');
await browser.sleep(500);
await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue();
const input = await utils.waitForElement('#chat-input');
await input.sendKeys('demos.openvidu.io');
await utils.clickOn('#send-btn');
await utils.waitForElement('.chat-message a');
expect(await utils.isPresent('.chat-message a')).toBeTrue();
});
});

View File

@ -1,3 +0,0 @@
export const LAUNCH_MODE = process.env.LAUNCH_MODE || 'DEV';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;

View File

@ -1,604 +0,0 @@
import { Builder, Key, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('Testing videoconference EVENTS', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
const isHeadless: boolean = (TestAppConfig.browserOptions as any).options_.args.includes('--headless');
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
// leaving room if connected
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should receive the onReadyToJoin event', async () => {
await browser.get(`${url}`);
await utils.waitForElement('#prejoin-container');
expect(await utils.isPresent('#prejoin-container')).toBeTrue();
// Clicking to join button
await utils.waitForElement('#join-button');
await utils.clickOn('#join-button');
// Checking if onReadyToJoin has been received
await utils.waitForElement('#onReadyToJoin');
expect(await utils.isPresent('#onReadyToJoin')).toBeTrue();
});
it('should receive the onTokenRequested event', async () => {
await browser.get(`${url}`);
await utils.waitForElement('#prejoin-container');
expect(await utils.isPresent('#prejoin-container')).toBeTrue();
// Clicking to join button
await utils.waitForElement('#join-button');
await utils.clickOn('#join-button');
// Checking if onTokenRequested has been received
await utils.waitForElement('#onTokenRequested');
expect(await utils.isPresent('#onTokenRequested')).toBeTrue();
});
it('should receive the onVideoEnabledChanged event when clicking on the prejoin', async () => {
await browser.get(url);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#camera-button');
await utils.clickOn('#camera-button');
// Checking if onVideoEnabledChanged has been received
await utils.waitForElement('#onVideoEnabledChanged-false');
expect(await utils.isPresent('#onVideoEnabledChanged-false')).toBeTrue();
});
it('should receive the onVideoEnabledChanged event when clicking on the toolbar', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to leave button
await utils.waitForElement('#camera-btn');
await utils.clickOn('#camera-btn');
// Checking if onVideoEnabledChanged has been received
await utils.waitForElement('#onVideoEnabledChanged-false');
expect(await utils.isPresent('#onVideoEnabledChanged-false')).toBeTrue();
await utils.clickOn('#camera-btn');
await utils.waitForElement('#onVideoEnabledChanged-true');
expect(await utils.isPresent('#onVideoEnabledChanged-true')).toBeTrue();
});
it('should receive the onVideoDeviceChanged event on prejoin', async () => {
await browser.get(`${url}&fakeDevices=true`);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#video-dropdown');
await utils.clickOn('#video-dropdown');
await utils.waitForElement('#option-custom_fake_video_1');
await utils.clickOn('#option-custom_fake_video_1');
await utils.waitForElement('#onVideoDeviceChanged');
expect(await utils.isPresent('#onVideoDeviceChanged')).toBeTrue();
});
it('should receive the onVideoDeviceChanged event on settings panel', async () => {
await browser.get(`${url}&prejoin=false&fakeDevices=true`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('#settings-container');
await utils.clickOn('#video-opt');
await utils.waitForElement('ov-video-devices-select');
await utils.waitForElement('#video-dropdown');
await utils.clickOn('#video-dropdown');
await utils.waitForElement('#option-custom_fake_video_1');
await utils.clickOn('#option-custom_fake_video_1');
await utils.waitForElement('#onVideoDeviceChanged');
expect(await utils.isPresent('#onVideoDeviceChanged')).toBeTrue();
});
it('should receive the onAudioEnabledChanged event when clicking on the prejoin', async () => {
await browser.get(url);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#microphone-button');
await utils.clickOn('#microphone-button');
// Checking if onAudioEnabledChanged has been received
await utils.waitForElement('#onAudioEnabledChanged-false');
expect(await utils.isPresent('#onAudioEnabledChanged-false')).toBeTrue();
});
it('should receive the onAudioEnabledChanged event when clicking on the toolbar', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to leave button
await utils.waitForElement('#mic-btn');
await utils.clickOn('#mic-btn');
// Checking if onAudioEnabledChanged has been received
await utils.waitForElement('#onAudioEnabledChanged-false');
expect(await utils.isPresent('#onAudioEnabledChanged-false')).toBeTrue();
await utils.clickOn('#mic-btn');
await utils.waitForElement('#onAudioEnabledChanged-true');
expect(await utils.isPresent('#onAudioEnabledChanged-true')).toBeTrue();
});
it('should receive the onAudioDeviceChanged event on prejoin', async () => {
await browser.get(`${url}&fakeDevices=true`);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#audio-dropdown');
await utils.clickOn('#audio-dropdown');
await utils.waitForElement('#option-custom_fake_audio_1');
await utils.clickOn('#option-custom_fake_audio_1');
await utils.waitForElement('#onAudioDeviceChanged');
expect(await utils.isPresent('#onAudioDeviceChanged')).toBeTrue();
});
it('should receive the onAudioDeviceChanged event on settings panel', async () => {
await browser.get(`${url}&prejoin=false&fakeDevices=true`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('#settings-container');
await utils.clickOn('#audio-opt');
await utils.waitForElement('ov-audio-devices-select');
await utils.waitForElement('#audio-dropdown');
await utils.clickOn('#audio-dropdown');
await utils.waitForElement('#option-custom_fake_audio_1');
await utils.clickOn('#option-custom_fake_audio_1');
await utils.waitForElement('#onAudioDeviceChanged');
expect(await utils.isPresent('#onAudioDeviceChanged')).toBeTrue();
});
it('should receive the onLangChanged event on prejoin', async () => {
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('.language-selector');
await utils.clickOn('.language-selector');
await browser.sleep(500);
await utils.clickOn('#lang-opt-es');
await browser.sleep(500);
await utils.waitForElement('#onLangChanged-es');
expect(await utils.isPresent('#onLangChanged-es')).toBeTrue();
});
it('should receive the onLangChanged event on settings panel', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('#settings-container');
await utils.waitForElement('.full-lang-button');
await utils.clickOn('.full-lang-button');
await browser.sleep(500);
await utils.clickOn('#lang-opt-es');
await browser.sleep(500);
await utils.waitForElement('#onLangChanged-es');
expect(await utils.isPresent('#onLangChanged-es')).toBeTrue();
});
it('should receive the onScreenShareEnabledChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to leave button
const screenshareButton = await utils.waitForElement('#screenshare-btn');
expect(await utils.isPresent('#screenshare-btn')).toBeTrue();
await screenshareButton.click();
// Checking if onScreenShareEnabledChanged has been received
await utils.waitForElement('#onScreenShareEnabledChanged');
expect(await utils.isPresent('#onScreenShareEnabledChanged')).toBeTrue();
});
// With headless mode, the Fullscreen API doesn't work
it('should receive the onFullscreenEnabledChanged event', async () => {
let element;
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.toggleFullscreenFromToolbar();
await browser.sleep(500);
// Checking if onFullscreenEnabledChanged has been received
await utils.waitForElement('#onFullscreenEnabledChanged-true');
expect(await utils.isPresent('#onFullscreenEnabledChanged-true')).toBeTrue();
await (await utils.waitForElement('html')).sendKeys(Key.F11);
await browser.sleep(500);
await utils.waitForElement('#onFullscreenEnabledChanged-false');
expect(await utils.isPresent('#onFullscreenEnabledChanged-false')).toBeTrue();
});
it('should receive the onChatPanelStatusChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('chat');
// Checking if onChatPanelStatusChanged has been received
await utils.waitForElement('#onChatPanelStatusChanged-true');
expect(await utils.isPresent('#onChatPanelStatusChanged-true')).toBeTrue();
await utils.togglePanel('chat');
// Checking if onChatPanelStatusChanged has been received
await utils.waitForElement('#onChatPanelStatusChanged-false');
expect(await utils.isPresent('#onChatPanelStatusChanged-false')).toBeTrue();
});
it('should receive the onParticipantsPanelStatusChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('participants');
// Checking if onParticipantsPanelStatusChanged has been received
await utils.waitForElement('#onParticipantsPanelStatusChanged-true');
expect(await utils.isPresent('#onParticipantsPanelStatusChanged-true')).toBeTrue();
await utils.togglePanel('participants');
// Checking if onParticipantsPanelStatusChanged has been received
await utils.waitForElement('#onParticipantsPanelStatusChanged-false');
expect(await utils.isPresent('#onParticipantsPanelStatusChanged-false')).toBeTrue();
});
it('should receive the onActivitiesPanelStatusChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('activities');
// Checking if onActivitiesPanelStatusChanged has been received
await utils.waitForElement('#onActivitiesPanelStatusChanged-true');
expect(await utils.isPresent('#onActivitiesPanelStatusChanged-true')).toBeTrue();
await utils.togglePanel('activities');
// Checking if onActivitiesPanelStatusChanged has been received
await utils.waitForElement('#onActivitiesPanelStatusChanged-false');
expect(await utils.isPresent('#onActivitiesPanelStatusChanged-false')).toBeTrue();
});
it('should receive the onSettingsPanelStatusChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
// Checking if onSettingsPanelStatusChanged has been received
await utils.waitForElement('#onSettingsPanelStatusChanged-true');
expect(await utils.isPresent('#onSettingsPanelStatusChanged-true')).toBeTrue();
await utils.togglePanel('settings');
// Checking if onSettingsPanelStatusChanged has been received
await utils.waitForElement('#onSettingsPanelStatusChanged-false');
expect(await utils.isPresent('#onSettingsPanelStatusChanged-false')).toBeTrue();
});
it('should receive the onRecordingStartRequested and onRecordingStopRequested event when clicking toolbar button', async () => {
const roomName = 'recordingToolbarEvent';
await browser.get(`${url}&prejoin=false&roomName=${roomName}`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.toggleRecordingFromToolbar();
// Checking if onRecordingStartRequested has been received
await utils.waitForElement(`#onRecordingStartRequested-${roomName}`);
expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).toBeTrue();
await utils.waitForElement('.activity-status.started');
await utils.toggleRecordingFromToolbar();
// Checking if onRecordingStopRequested has been received
await utils.waitForElement(`#onRecordingStopRequested-${roomName}`);
expect(await utils.isPresent(`#onRecordingStopRequested-${roomName}`)).toBeTrue();
});
xit('should receive the onBroadcastingStopRequested event when clicking toolbar button', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.toggleToolbarMoreOptions();
await utils.waitForElement('#broadcasting-btn');
await utils.clickOn('#broadcasting-btn');
await browser.sleep(500);
await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('#activities-container');
await utils.waitForElement('#broadcasting-url-input');
const input = await utils.waitForElement('#broadcast-url-input');
await input.sendKeys('BroadcastUrl');
await utils.clickOn('#broadcasting-btn');
// Open more options menu
await utils.toggleToolbarMoreOptions();
await utils.waitForElement('#broadcasting-btn');
await utils.clickOn('#broadcasting-btn');
// Checking if onBroadcastingStopRequested has been received
await utils.waitForElement('#onBroadcastingStopRequested');
expect(await utils.isPresent('#onBroadcastingStopRequested')).toBeTrue();
});
it('should receive the onRecordingStartRequested and onRecordingStopRequested when clicking from activities panel', async () => {
const roomName = 'recordingActivitiesEvent';
await browser.get(`${url}&prejoin=false&roomName=${roomName}`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('activities');
await browser.sleep(1000);
// Open recording
await utils.waitForElement('ov-recording-activity');
await utils.clickOn('ov-recording-activity');
await browser.sleep(1000);
// Clicking to recording button
await utils.waitForElement('#start-recording-btn');
await utils.clickOn('#start-recording-btn');
// Checking if onRecordingStartRequested has been received
await utils.waitForElement(`#onRecordingStartRequested-${roomName}`);
expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).toBeTrue();
});
xit('should receive the onRecordingDeleteRequested event', async () => {
let element;
const roomName = 'deleteRecordingEvent';
await browser.get(`${url}&prejoin=false&roomName=${roomName}&fakeRecordings=true`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to activities button
const activitiesButton = await utils.waitForElement('#activities-panel-btn');
expect(await utils.isPresent('#activities-panel-btn')).toBeTrue();
await activitiesButton.click();
await browser.sleep(1500);
// Open recording
element = await utils.waitForElement('ov-recording-activity');
await element.click();
await browser.sleep(1500);
// Delete event
element = await utils.waitForElement('#delete-recording-btn');
expect(await utils.isPresent('#delete-recording-btn')).toBeTrue();
await element.click();
element = await utils.waitForElement('#delete-recording-confirm-btn');
expect(await utils.isPresent('#delete-recording-confirm-btn')).toBeTrue();
await element.click();
await utils.waitForElement(`#onRecordingDeleteRequested-${roomName}-fakeRecording`);
expect(await utils.isPresent(`#onRecordingDeleteRequested-${roomName}-fakeRecording`)).toBeTrue();
});
it('should receive the onBroadcastingStartRequested event when clicking from panel', async () => {
const roomName = 'broadcastingStartEvent';
const broadcastUrl = 'BroadcastUrl';
await browser.get(`${url}&prejoin=false&roomName=${roomName}`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('activities');
await browser.sleep(1000);
await utils.waitForElement('#broadcasting-activity');
await utils.clickOn('#broadcasting-activity');
await browser.sleep(1000);
const button = await utils.waitForElement('#broadcasting-btn');
expect(await button.isEnabled()).toBeFalse();
const input = await utils.waitForElement('#broadcast-url-input');
await input.sendKeys(broadcastUrl);
await utils.clickOn('#broadcasting-btn');
// Checking if onBroadcastingStartRequested has been received
await utils.waitForElement(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`);
expect(await utils.isPresent(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`)).toBeTrue();
});
xit('should receive the onBroadcastingStopRequested event when clicking from panel', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Open activities panel
await utils.togglePanel('activities');
await utils.waitForElement('#broadcasting-activity');
await utils.clickOn('#broadcasting-activity');
const button = await utils.waitForElement('#broadcasting-btn');
expect(await button.isEnabled()).toBeFalse();
const input = await utils.waitForElement('#broadcast-url-input');
await input.sendKeys('BroadcastUrl');
await utils.clickOn('#broadcasting-btn');
expect(await utils.isPresent('#broadcasting-tag')).toBeTrue();
await utils.clickOn('#stop-broadcasting-btn');
// Checking if onBroadcastingStopRequested has been received
await utils.waitForElement('#onBroadcastingStopRequested');
expect(await utils.isPresent('#onBroadcastingStopRequested')).toBeTrue();
expect(await utils.isPresent('#broadcasting-tag')).toBeFalse();
});
xit('should receive the onBroadcastingStopRequested event when clicking from toolbar', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.toggleToolbarMoreOptions();
await utils.waitForElement('#broadcasting-btn');
await utils.clickOn('#broadcasting-btn');
await browser.sleep(500);
// Checking if onBroadcastingStopRequested has been received
await utils.waitForElement('#onBroadcastingStopRequested');
expect(await utils.isPresent('#onBroadcastingStopRequested')).toBeTrue();
expect(await utils.isPresent('#broadcasting-tag')).toBeFalse();
});
it('should receive the onRoomCreated event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.waitForElement('#onRoomCreated');
expect(await utils.isPresent('#onRoomCreated')).toBeTrue();
expect(await utils.isPresent('#onReadyToJoin')).toBeFalse();
});
// PARTICIPANT EVENTS
it('should receive onParticipantCreated event from LOCAL participant', async () => {
const participantName = 'TEST_USER';
await browser.get(`${url}&participantName=${participantName}&prejoin=false`);
await utils.waitForElement(`#${participantName}-onParticipantCreated`);
expect(await utils.isPresent(`#${participantName}-onParticipantCreated`)).toBeTrue();
});
it('should receive the onParticipantLeft event', async () => {
await browser.get(`${url}&prejoin=false&redirectToHome=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to leave button
const leaveButton = await utils.waitForElement('#leave-btn');
expect(await utils.isPresent('#leave-btn')).toBeTrue();
await leaveButton.click();
await utils.waitForElement('#events');
// Checking if onParticipantLeft has been received
await utils.waitForElement('#onParticipantLeft');
expect(await utils.isPresent('#onParticipantLeft')).toBeTrue();
});
// * ROOM EVENTS
//TODO: Implement a mechanism to emulate network disconnection
// it('should receive the onRoomDisconnected event', async () => {
// await browser.get(`${url}&prejoin=false`);
// await utils.checkSessionIsPresent();
// await utils.checkToolbarIsPresent();
// // Emulate network disconnection
// await utils.forceCloseWebsocket();
// // Checking if onRoomDisconnected has been received
// await utils.waitForElement('#onRoomDisconnected');
// expect(await utils.isPresent('#onRoomDisconnected')).toBeTrue();
// });
});

View File

@ -1,84 +0,0 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { OpenViduComponentsPO } from './utils.po.test';
import { TestAppConfig } from './selenium.conf';
let url = '';
describe('Testing Internal Directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
url = `${TestAppConfig.appUrl}&roomName=INTERNAL_DIRECTIVES_${Math.floor(Math.random() * 1000)}`;
});
afterEach(async () => {
try {
} catch (error) {}
await browser.sleep(500);
await browser.quit();
});
it('should show/hide toolbar view recording button with toolbarViewRecordingsButton directive', async () => {
await browser.get(`${url}&prejoin=false&toolbarViewRecordingsButton=true`);
await utils.checkSessionIsPresent();
await utils.toggleToolbarMoreOptions();
expect(await utils.isPresent('#view-recordings-btn')).toBeTrue();
await browser.get(`${url}&prejoin=false`);
await browser.navigate().refresh();
await utils.checkSessionIsPresent();
await utils.toggleToolbarMoreOptions();
expect(await utils.isPresent('#view-recordings-btn')).toBeFalse();
});
it('should show/hide participant name in prejoin with prejoinDisplayParticipantName directive', async () => {
await browser.get(`${url}&prejoin=true`);
await utils.checkPrejoinIsPresent();
expect(await utils.isPresent('.participant-name-container')).toBeTrue();
await browser.get(`${url}&prejoin=true&prejoinDisplayParticipantName=false`);
await browser.navigate().refresh();
await utils.checkPrejoinIsPresent();
expect(await utils.isPresent('.participant-name-container')).toBeFalse();
});
it('should show/hide view recordings button with recordingActivityViewRecordingsButton directive', async () => {
await browser.get(`${url}&prejoin=false&recordingActivityViewRecordingsButton=true`);
await utils.checkSessionIsPresent();
await utils.togglePanel('activities');
await utils.clickOn('#recording-activity');
expect(await utils.isPresent('#view-recordings-btn')).toBeTrue();
await browser.get(`${url}&prejoin=false`);
await browser.navigate().refresh();
await utils.checkSessionIsPresent();
await utils.togglePanel('activities');
await utils.clickOn('#recording-activity');
expect(await utils.isPresent('#view-recordings-btn')).toBeFalse();
});
it('should show/hide start/stop recording buttons with recordingActivityStartStopRecordingButton directive', async () => {
await browser.get(`${url}&prejoin=false&recordingActivityStartStopRecordingButton=false`);
await utils.checkSessionIsPresent();
await utils.togglePanel('activities');
await utils.clickOn('#recording-activity');
expect(await utils.isPresent('#start-recording-btn')).toBeFalse();
await browser.sleep(3000);
await browser.get(`${url}&prejoin=false`);
await browser.navigate().refresh();
await utils.checkSessionIsPresent();
await utils.togglePanel('activities');
await utils.clickOn('#recording-activity');
expect(await utils.isPresent('#start-recording-btn')).toBeTrue();
});
});

View File

@ -1,177 +0,0 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { getBrowserOptionsWithoutDevices, TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('Media Devices: Virtual Device Replacement and Permissions Handling', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should allow selecting and replacing the video track with a custom virtual device in the prejoin page', async () => {
const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;';
await browser.get(`${url}&fakeDevices=true`);
let videoDevices = await utils.waitForElement('#video-dropdown');
await videoDevices.click();
let element = await utils.waitForElement('#option-custom_fake_video_1');
await element.click();
let videoLabel;
await browser.sleep(1000);
videoLabel = await browser.executeScript<string>(script);
expect(videoLabel).toEqual('custom_fake_video_1');
await videoDevices.click();
element = await utils.waitForElement('#option-fake_device_0');
await element.click();
await browser.sleep(1000);
videoLabel = await browser.executeScript<string>(script);
expect(videoLabel).toEqual('fake_device_0');
});
it('should allow selecting and replacing the video track with a custom virtual device in the videoconference page', async () => {
const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;';
await browser.get(`${url}&prejoin=false&fakeDevices=true`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).toBeTrue();
await browser.sleep(500);
await utils.clickOn('#video-opt');
expect(await utils.isPresent('ov-video-devices-select')).toBeTrue();
let videoDevices = await utils.waitForElement('#video-dropdown');
await videoDevices.click();
let element = await utils.waitForElement('#option-custom_fake_video_1');
await element.click();
let videoLabel;
await browser.sleep(1000);
videoLabel = await browser.executeScript<string>(script);
expect(videoLabel).toEqual('custom_fake_video_1');
await videoDevices.click();
element = await utils.waitForElement('#option-fake_device_0');
await element.click();
await browser.sleep(1000);
videoLabel = await browser.executeScript<string>(script);
expect(videoLabel).toEqual('fake_device_0');
});
it('should replace the screen track with a custom virtual device', async () => {
const script = 'return document.getElementsByClassName("OV_video-element screen-type")[0].srcObject.getVideoTracks()[0].label;';
await browser.get(`${url}&prejoin=false&fakeDevices=true`);
await utils.checkLayoutPresent();
await utils.checkToolbarIsPresent();
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
let screenLabel = await browser.executeScript<string>(script);
expect(screenLabel).not.toEqual('custom_fake_screen');
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('#replace-screen-button');
await utils.clickOn('#replace-screen-button');
await browser.sleep(1000);
screenLabel = await browser.executeScript<string>(script);
expect(screenLabel).toEqual('custom_fake_screen');
});
});
describe('Media Devices: UI Behavior Without Media Device Permissions', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(getBrowserOptionsWithoutDevices())
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should camera and microphone buttons be disabled in the prejoin page when permissions are denied', async () => {
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#no-video-device-message');
await utils.waitForElement('#no-audio-device-message');
expect(await utils.isPresent('#backgrounds-button')).toBeFalse();
});
it('should camera and microphone buttons be disabled in the room page when permissions are denied', async () => {
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
let button = await utils.waitForElement('#camera-btn');
expect(await button.isEnabled()).toBeFalse();
button = await utils.waitForElement('#mic-btn');
expect(await button.isEnabled()).toBeFalse();
});
it('should camera and microphone buttons be disabled in the room page without prejoin when permissions are denied', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
let button = await utils.waitForElement('#camera-btn');
expect(await button.isEnabled()).toBeFalse();
button = await utils.waitForElement('#mic-btn');
expect(await button.isEnabled()).toBeFalse();
});
it('should show an audio and video device warning in settings when permissions are denied', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).toBeTrue();
await utils.clickOn('#video-opt');
expect(await utils.isPresent('ov-video-devices-select')).toBeTrue();
await utils.waitForElement('#no-video-device-message');
await utils.clickOn('#audio-opt');
expect(await utils.isPresent('ov-audio-devices-select')).toBeTrue();
await utils.waitForElement('#no-audio-device-message');
});
});

View File

@ -1,380 +0,0 @@
import { Builder, By, WebDriver } from 'selenium-webdriver';
import { NestedConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = NestedConfig.appUrl;
describe('OpenVidu Components ATTRIBUTE toolbar directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should HIDE the CHAT PANEL BUTTON', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#chatPanelButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Check if chat button does not exist
expect(await utils.isPresent('chat-panel-btn')).toBeFalse();
});
it('should HIDE the PARTICIPANTS PANEL BUTTON', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#participantsPanelButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Check if participants button does not exist
expect(await utils.isPresent('participants-panel-btn')).toBeFalse();
});
it('should HIDE the ACTIVITIES PANEL BUTTON', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#activitiesPanelButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Check if participants button does not exist
expect(await utils.isPresent('activities-panel-btn')).toBeFalse();
});
it('should HIDE the DISPLAY LOGO', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#displayLogo-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
expect(await utils.isPresent('branding-logo')).toBeFalse();
});
it('should HIDE the DISPLAY ROOM name', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#displayRoomName-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
expect(await utils.isPresent('session-name')).toBeFalse();
});
it('should HIDE the FULLSCREEN button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#fullscreenButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
await utils.waitForElement('#more-options-menu');
// Checking if fullscreen button is not present
expect(await utils.isPresent('#fullscreen-btn')).toBeFalse();
});
it('should HIDE the STREAMING button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#broadcastingButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
await utils.waitForElement('#more-options-menu');
// Checking if fullscreen button is not present
expect(await utils.isPresent('#broadcasting-btn')).toBeFalse();
});
it('should HIDE the LEAVE button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#leaveButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
expect(await utils.isPresent('leave-btn')).toBeFalse();
});
it('should HIDE the SCREENSHARE button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#screenshareButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
expect(await utils.isPresent('screenshare-btn')).toBeFalse();
});
});
describe('OpenVidu Components ATTRIBUTE stream directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should HIDE the AUDIO detector', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#displayAudioDetection-checkbox');
await utils.clickOn('#apply-btn');
await utils.waitForElement('#session-container');
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('audio-wave-container')).toBeFalse();
});
it('should HIDE the PARTICIPANT NAME', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#displayParticipantName-checkbox');
await utils.clickOn('#apply-btn');
await utils.waitForElement('#session-container');
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('participant-name-container')).toBeFalse();
});
it('should HIDE the SETTINGS button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#settingsButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('settings-container')).toBeFalse();
});
});
describe('OpenVidu Components ATTRIBUTE participant panels directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should HIDE the participant MUTE button', async () => {
const fixedSession = `${url}?sessionId=fixedNameTesting`;
await browser.get(`${fixedSession}`);
await utils.clickOn('#ovParticipantPanelItem-checkbox');
await utils.clickOn('#muteButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.clickOn('#participants-panel-btn');
await utils.waitForElement('#participants-container');
// Starting new browser for adding a new participant
const newTabScript = `window.open("${fixedSession}")`;
await browser.executeScript(newTabScript);
// Get tabs opened
const tabs = await browser.getAllWindowHandles();
// Focus on the last tab
browser.switchTo().window(tabs[1]);
await utils.clickOn('#apply-btn');
// Switch to first tab
await browser.switchTo().window(tabs[0]);
await utils.waitForElement('#remote-participant-item');
expect(await utils.isPresent('mute-btn')).toBeFalse();
});
});
describe('OpenVidu Components ATTRIBUTE activity panel directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should HIDE the RECORDING activity', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovActivitiesPanel-checkbox');
await utils.clickOn('#recordingActivity-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#activities-panel-btn');
await browser.sleep(500);
await utils.waitForElement('#custom-activities-panel');
expect(await utils.isPresent('ov-recording-activity')).toBeFalse();
});
it('should HIDE the STREAMING activity', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovActivitiesPanel-checkbox');
await utils.clickOn('#broadcastingActivity-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#activities-panel-btn');
await browser.sleep(500);
await utils.waitForElement('#custom-activities-panel');
await utils.waitForElement('ov-recording-activity');
expect(await utils.isPresent('ov-broadcasting-activity')).toBeFalse();
});
});

View File

@ -1,186 +0,0 @@
import { Builder, By, WebDriver } from 'selenium-webdriver';
import { NestedConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = NestedConfig.appUrl;
describe('OpenVidu Components EVENTS', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should receive the onParticipantLeft event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Clicking to leave button
await utils.clickOn('#leave-btn');
// Checking if onLeaveButtonClicked has been received
await utils.waitForElement('#onParticipantLeft');
expect(await utils.isPresent('#onParticipantLeft')).toBeTrue();
});
it('should receive the onVideoEnabledChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#camera-btn');
await utils.waitForElement('#onVideoEnabledChanged');
expect(await utils.isPresent('#onVideoEnabledChanged')).toBeTrue();
});
it('should receive the onAudioEnabledChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#mic-btn');
await utils.waitForElement('#onAudioEnabledChanged');
expect(await utils.isPresent('#onAudioEnabledChanged')).toBeTrue();
});
it('should receive the onScreenShareEnabledChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#screenshare-btn');
await utils.waitForElement('#onScreenShareEnabledChanged');
expect(await utils.isPresent('#onScreenShareEnabledChanged')).toBeTrue();
});
it('should receive the onFullscreenEnabledChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.toggleFullscreenFromToolbar();
await browser.sleep(1000);
await utils.waitForElement('#onFullscreenEnabledChanged');
expect(await utils.isPresent('#onFullscreenEnabledChanged')).toBeTrue();
});
it('should receive the onRecordingStartRequested event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.toggleRecordingFromToolbar();
await utils.waitForElement('#onRecordingStartRequested');
expect(await utils.isPresent('#onRecordingStartRequested')).toBeTrue();
});
it('should receive the onParticipantsPanelStatusChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.togglePanel('participants');
await utils.waitForElement('#onParticipantsPanelStatusChanged');
expect(await utils.isPresent('#onParticipantsPanelStatusChanged')).toBeTrue();
});
it('should receive the onChatPanelStatusChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.togglePanel('chat');
await utils.waitForElement('#onChatPanelStatusChanged');
expect(await utils.isPresent('#onChatPanelStatusChanged')).toBeTrue();
});
it('should receive the onActivitiesPanelStatusChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.togglePanel('activities');
await utils.waitForElement('#onActivitiesPanelStatusChanged');
expect(await utils.isPresent('#onActivitiesPanelStatusChanged')).toBeTrue();
});
it('should receive the onSettingsPanelStatusChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await utils.waitForElement('#onSettingsPanelStatusChanged');
expect(await utils.isPresent('#onSettingsPanelStatusChanged')).toBeTrue();
});
});

View File

@ -1,806 +0,0 @@
import { Builder, By, WebDriver } from 'selenium-webdriver';
import { NestedConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = NestedConfig.appUrl;
describe('E2E: Toolbar structural directive scenarios', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should render only the custom toolbar (no additional buttons, no default toolbar)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom toolbar is present in DOM
await utils.waitForElement('#custom-toolbar');
expect(await utils.isPresent('#custom-toolbar')).toBeTrue();
// Check if additional buttons element has not been rendered
expect(await utils.isPresent('#custom-toolbar-additional-buttons')).toBeFalse();
// Check if default toolbar is not present
expect(await utils.isPresent('#default-toolbar')).toBeFalse();
});
it('should render the custom toolbar with additional custom buttons and hide the default toolbar', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#ovToolbarAdditionalButtons-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom toolbar is present in DOM
await utils.waitForElement('#custom-toolbar');
expect(await utils.isPresent('#custom-toolbar')).toBeTrue();
// Check if additional buttons element has been rendered;
await utils.waitForElement('#custom-toolbar-additional-buttons');
expect(await utils.isPresent('#custom-toolbar-additional-buttons')).toBeTrue();
const element = await browser.findElements(By.id('toolbar-additional-btn'));
expect(element.length).toEqual(2);
// Check if default toolbar is not present
expect(await utils.isPresent('#default-toolbar')).toBeFalse();
});
it('should render the custom toolbar with additional custom panel buttons and hide the default toolbar', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#ovToolbarAdditionalPanelButtons-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom toolbar is present in DOM
await utils.waitForElement('#custom-toolbar');
expect(await utils.isPresent('#custom-toolbar')).toBeTrue();
// Check if additional buttons element has been rendered;
await utils.waitForElement('#custom-toolbar-additional-panel-buttons');
expect(await utils.isPresent('#custom-toolbar-additional-panel-buttons')).toBeTrue();
const element = await browser.findElements(By.id('toolbar-additional-panel-btn'));
expect(element.length).toEqual(1);
// Check if default toolbar is not present
expect(await utils.isPresent('#default-toolbar')).toBeFalse();
});
it('should render only additional toolbar buttons (default toolbar visible, no custom toolbar)', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovToolbarAdditionalButtons-checkbox');
await utils.clickOn('#apply-btn');
// Check if default toolbar is present
await utils.waitForElement('#default-toolbar');
expect(await utils.isPresent('#default-toolbar')).toBeTrue();
// Check if additional buttons are present
await utils.waitForElement('#custom-toolbar-additional-buttons');
expect(await utils.isPresent('#custom-toolbar-additional-buttons')).toBeTrue();
element = await browser.findElements(By.id('toolbar-additional-btn'));
expect(element.length).toEqual(3);
// Check if custom toolbar not is present
expect(await utils.isPresent('#custom-toolbar')).toBeFalse();
});
it('should render only additional toolbar panel buttons (default toolbar visible, no custom toolbar)', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovToolbarAdditionalPanelButtons-checkbox');
await utils.clickOn('#apply-btn');
// Check if default toolbar is present
await utils.waitForElement('#default-toolbar');
expect(await utils.isPresent('#default-toolbar')).toBeTrue();
// Check if additional buttons are present
await utils.waitForElement('#custom-toolbar-additional-panel-buttons');
expect(await utils.isPresent('#custom-toolbar-additional-panel-buttons')).toBeTrue();
element = await browser.findElements(By.id('toolbar-additional-panel-btn'));
expect(element.length).toEqual(1);
// Check if custom toolbar not is present
expect(await utils.isPresent('#custom-toolbar')).toBeFalse();
});
});
describe('E2E: Panel structural directive scenarios', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should render an additional custom panel with default panels', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovAdditionalPanels-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Open additional panel
await utils.clickOn('#toolbar-additional-panel-btn');
await browser.sleep(500);
// Check if custom panel is present
expect(await utils.isPresent('#custom-additional-panel')).toBeTrue();
element = await utils.waitForElement('#additional-panel-title');
expect(await element.getAttribute('innerText')).toEqual('NEW PANEL');
// Open the participants panel
await utils.clickOn('#participants-panel-btn');
await browser.sleep(500);
// Check if default panel is present
expect(await utils.isPresent('#default-participants-panel')).toBeTrue();
// Open additional panel again
await utils.clickOn('#toolbar-additional-panel-btn');
expect(await utils.isPresent('#custom-additional-panel')).toBeTrue();
// Close the additional panel
await utils.clickOn('#toolbar-additional-panel-btn');
expect(await utils.isPresent('#custom-additional-panel')).toBeFalse();
});
it('should render only the custom panel container (no children, no default panels)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
// Click on button for opening panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is not present
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render the custom panel container with an additional panel only', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovAdditionalPanels-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening additional panel
await utils.clickOn('#toolbar-additional-panel-btn');
// Check if custom panel is present
element = await browser.findElements(By.id('custom-additional-panel'));
expect(element.length).toEqual(1);
element = await utils.waitForElement('#additional-panel-title');
expect(await utils.isPresent('#additional-panel-title')).toBeTrue();
expect(await element.getAttribute('innerText')).toEqual('NEW PANEL');
await utils.clickOn('#toolbar-additional-panel-btn');
expect(await utils.isPresent('#custom-additional-panel')).toBeFalse();
});
it('should render the custom panel container with a custom chat panel only', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovChatPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is not present
await utils.waitForElement('#custom-chat-panel');
expect(await utils.isPresent('#custom-chat-panel')).toBeTrue();
});
it('should render the custom panel container with a custom activities panel only', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovActivitiesPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening chat panel
await utils.clickOn('#activities-panel-btn');
// Check if default activities panel is not present
expect(await utils.isPresent('#default-activities-panel')).toBeFalse();
// Check if custom chat panel is not present
element = await utils.waitForElement('#custom-activities-panel');
expect(await utils.isPresent('#custom-activities-panel')).toBeTrue();
element = await utils.waitForElement('#activities-panel-title');
expect(await element.getAttribute('innerText')).toEqual('CUSTOM ACTIVITIES PANEL');
});
it('should render the custom panel container with a custom participants panel only (no children)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is not present
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render the custom panel container with a custom participants panel and a custom participant item', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#ovParticipantPanelItem-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is present
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Check if custom participant panel item is present
await utils.waitForElement('#custom-participants-panel-item');
expect(await utils.isPresent('#custom-participants-panel-item')).toBeTrue();
// Check if default participant panel item is not present
expect(await utils.isPresent('#default-participant-panel-item')).toBeFalse();
});
it('should render the custom panel container with a custom participants panel, custom participant item, and custom item element', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#ovParticipantPanelItem-checkbox');
await utils.clickOn('#ovParticipantPanelItemElements-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if custom participant panel is present
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Check if custom participant panel item is present
await utils.waitForElement('#custom-participants-panel-item');
expect(await utils.isPresent('#custom-participants-panel-item')).toBeTrue();
// Check if custom participant panel item element is present
await utils.waitForElement('#custom-participants-panel-item-element');
expect(await utils.isPresent('#custom-participants-panel-item-element')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if default participant panel item is not present
expect(await utils.isPresent('#default-participant-panel-item')).toBeFalse();
});
it('should render only a custom activities panel (no default panel)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovActivitiesPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening additional panel
await utils.clickOn('#activities-panel-btn');
// Check if default panel is not present
expect(await utils.isPresent('#default-activities-panel')).toBeFalse();
// Check if custom panel is present
await utils.waitForElement('#custom-activities-panel');
expect(await utils.isPresent('#custom-activities-panel')).toBeTrue();
// Check if activities panel is has content
await utils.waitForElement('#activities-container');
expect(await utils.isPresent('#activities-container')).toBeTrue();
});
it('should render only a custom additional panel (no default panel)', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovAdditionalPanels-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening additional panel
await utils.clickOn('#toolbar-additional-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-additional-panel');
expect(await utils.isPresent('#custom-additional-panel')).toBeTrue();
element = await utils.waitForElement('#additional-panel-title');
expect(await element.getAttribute('innerText')).toEqual('NEW PANEL');
await utils.clickOn('#toolbar-additional-panel-btn');
expect(await utils.isPresent('#custom-additional-panel')).toBeFalse();
});
it('should render only a custom chat panel (no custom panel container)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovChatPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
expect(await utils.isPresent('#custom-panels')).toBeFalse();
// Check if default panel is not present
await utils.waitForElement('#default-panel');
expect(await utils.isPresent('#default-panel')).toBeTrue();
// Check if default participant panel is not present
await utils.waitForElement('#default-participants-panel');
expect(await utils.isPresent('#default-participants-panel')).toBeTrue();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is present
await utils.waitForElement('#custom-chat-panel');
expect(await utils.isPresent('#custom-chat-panel')).toBeTrue();
});
it('should render only a custom participants panel (no custom panel container)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
expect(await utils.isPresent('#custom-panels')).toBeFalse();
// Check if default panel is not present
await utils.waitForElement('#default-panel');
expect(await utils.isPresent('#default-panel')).toBeTrue();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is present
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is present
await utils.waitForElement('#default-chat-panel');
expect(await utils.isPresent('#default-chat-panel')).toBeTrue();
// Check if custom chat panel is not present
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render only a custom participant panel item (no custom panel container)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovParticipantPanelItem-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
expect(await utils.isPresent('#custom-panels')).toBeFalse();
// Check if default panel is not present
await utils.waitForElement('#default-panel');
expect(await utils.isPresent('#default-panel')).toBeTrue();
// Check if default participant panel is not present
await utils.waitForElement('#default-participants-panel');
expect(await utils.isPresent('#default-participants-panel')).toBeTrue();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
await utils.waitForElement('#custom-participants-panel-item');
expect(await utils.isPresent('#custom-participants-panel-item')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is present
await utils.waitForElement('#default-chat-panel');
expect(await utils.isPresent('#default-chat-panel')).toBeTrue();
// Check if custom chat panel is not present
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render only a custom participant panel item element (no custom panel container)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovParticipantPanelItemElements-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if default participant panel is not present
await utils.waitForElement('#default-participants-panel');
expect(await utils.isPresent('#default-participants-panel')).toBeTrue();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
expect(await utils.isPresent('#custom-participants-panel-item')).toBeFalse();
expect(await utils.isPresent('#custom-participants-panel-item')).toBeFalse();
await utils.waitForElement('#custom-participants-panel-item-element');
expect(await utils.isPresent('#custom-participants-panel-item-element')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is present
await utils.waitForElement('#default-chat-panel');
expect(await utils.isPresent('#default-chat-panel')).toBeTrue();
// Check if custom chat panel is not present;
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render the custom panel container with both custom chat and participants panels', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovChatPanel-checkbox');
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is present
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is present
await utils.waitForElement('#custom-chat-panel');
expect(await utils.isPresent('#custom-chat-panel')).toBeTrue();
});
});
describe('E2E: Layout and stream structural directive scenarios', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should render only the custom layout (no stream, no default layout)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovLayout-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom layout is present
await utils.waitForElement('#custom-layout');
expect(await utils.isPresent('#custom-layout')).toBeTrue();
// Check if default layout is not present
expect(await utils.isPresent('#default-layout')).toBeFalse();
// Check if custom stream is not present
expect(await utils.isPresent('#custom-stream')).toBeFalse();
// Check if video is not present
expect(await utils.isPresent('video')).toBeFalse();
});
it('should render the custom layout with a custom stream (no default layout/stream)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovLayout-checkbox');
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom layout is present
await utils.waitForElement('#custom-layout');
expect(await utils.isPresent('#custom-layout')).toBeTrue();
// Check if default layout is not present
expect(await utils.isPresent('default-layout')).toBeFalse();
// Check if custom stream is present
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('#custom-stream')).toBeTrue();
// Check if default stream is not present
expect(await utils.isPresent('default-stream')).toBeFalse();
// Check if video is present
await utils.waitForElement('video');
expect(await utils.isPresent('video')).toBeTrue();
});
it('should render only a custom stream (no custom layout, no default stream)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#apply-btn');
// Check if default layout is not present
await utils.waitForElement('#default-layout');
expect(await utils.isPresent('#default-layout')).toBeTrue();
// Check if custom stream is present
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('#custom-stream')).toBeTrue();
// Check if custom layout is not present
expect(await utils.isPresent('#custom-layout')).toBeFalse();
// Check if default stream is not present
expect(await utils.isPresent('default-stream')).toBeFalse();
// Check if video is present
await utils.waitForElement('video');
expect(await utils.isPresent('video')).toBeTrue();
});
});

View File

@ -1,132 +0,0 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('Panels: UI Navigation and Section Switching', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should open and close the CHAT panel and verify its content', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
const chatButton = await utils.waitForElement('#chat-panel-btn');
await chatButton.click();
await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue();
await utils.waitForElement('.messages-container');
expect(await utils.isPresent('.messages-container')).toBeTrue();
await chatButton.click();
expect(await utils.isPresent('.input-container')).toBeFalse();
expect(await utils.isPresent('.messages-container')).toBeFalse();
});
it('should open and close the PARTICIPANTS panel and verify its content', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
const participantBtn = await utils.waitForElement('#participants-panel-btn');
await participantBtn.click();
await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.local-participant-container');
expect(await utils.isPresent('.local-participant-container')).toBeTrue();
await utils.waitForElement('ov-participant-panel-item');
expect(await utils.isPresent('ov-participant-panel-item')).toBeTrue();
await participantBtn.click();
expect(await utils.isPresent('.local-participant-container')).toBeFalse();
expect(await utils.isPresent('ov-participant-panel-item')).toBeFalse();
});
it('should open and close the ACTIVITIES panel and verify its content', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
const activitiesBtn = await utils.waitForElement('#activities-panel-btn');
await activitiesBtn.click();
await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('#activities-container');
expect(await utils.isPresent('#activities-container')).toBeTrue();
await utils.waitForElement('#recording-activity');
expect(await utils.isPresent('#recording-activity')).toBeTrue();
await activitiesBtn.click();
expect(await utils.isPresent('#activities-container')).toBeFalse();
expect(await utils.isPresent('#recording-activity')).toBeFalse();
});
it('should open the SETTINGS panel and verify its content', async () => {
let element;
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
element = await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('#default-settings-panel')).toBeTrue();
});
it('should switch between PARTICIPANTS and CHAT panels and verify correct content is shown', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
const chatButton = await utils.waitForElement('#chat-panel-btn');
await chatButton.click();
await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('.sidenav-menu')).toBeTrue();
await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue();
expect(await utils.isPresent('.messages-container')).toBeTrue();
const participantBtn = await utils.waitForElement('#participants-panel-btn');
await participantBtn.click();
await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('.local-participant-container')).toBeTrue();
expect(await utils.isPresent('ov-participant-panel-item')).toBeTrue();
await chatButton.click();
await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('.input-container')).toBeTrue();
expect(await utils.isPresent('.messages-container')).toBeTrue();
expect(await utils.isPresent('.local-participant-container')).toBeFalse();
expect(await utils.isPresent('ov-participant-panel-item')).toBeFalse();
await chatButton.click();
expect(await utils.getNumberOfElements('.input-container')).toEqual(0);
expect(await utils.isPresent('messages-container')).toBeFalse();
});
it('should switch between sections in the SETTINGS panel and verify correct content is shown', async () => {
let element;
await browser.get(`${url}&prejoin=false`);
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('.sidenav-menu')).toBeTrue();
await browser.sleep(500);
element = await utils.waitForElement('#general-opt');
await element.click();
expect(await utils.isPresent('ov-participant-name-input')).toBeTrue();
element = await utils.waitForElement('#video-opt');
await element.click();
expect(await utils.isPresent('ov-video-devices-select')).toBeTrue();
element = await utils.waitForElement('#audio-opt');
await element.click();
expect(await utils.isPresent('ov-audio-devices-select')).toBeTrue();
});
});

View File

@ -1,316 +0,0 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('E2E: Screensharing features', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should toggle screensharing on and off twice, updating video count', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
// Enable screensharing
await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('video')).toEqual(2);
// Disable screensharing
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('video')).toEqual(1);
// Enable again
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('video')).toEqual(2);
// Disable again
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('video')).toEqual(1);
});
it('should show screenshare and muted camera (camera off, screenshare on)', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
// Mute camera
await utils.waitForElement('#camera-btn');
await utils.clickOn('#camera-btn');
// Enable screensharing
const screenshareButton = await utils.waitForElement('#screenshare-btn');
expect(await screenshareButton.isDisplayed()).toBeTrue();
await screenshareButton.click();
await browser.sleep(500);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('video')).toEqual(2);
// Disable screensharing
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('video')).toEqual(1);
});
it('should display screensharing with a single pinned video', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
// Enable screensharing
const screenshareButton = await utils.waitForElement('#screenshare-btn');
expect(await screenshareButton.isDisplayed()).toBeTrue();
await screenshareButton.click();
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
});
it('should replace pinned video when a second participant starts screensharing', async () => {
const roomName = 'screensharingE2E';
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
// First participant screenshares
await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn');
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Second participant joins and screenshares
const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript);
const tabs = await browser.getAllWindowHandles();
await browser.switchTo().window(tabs[1]);
await utils.checkLayoutPresent();
await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(4);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Switch back to first tab and check
await browser.switchTo().window(tabs[0]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(4);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
});
it('should unpin screensharing and restore previous pinned video when disabled', async () => {
const roomName = 'screensharingtwoE2E';
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
// First participant screenshares
await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Second participant joins and screenshares
const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[1]);
await utils.checkLayoutPresent();
await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(4);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Disable screensharing for second participant
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('video')).toEqual(3);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Switch back to first tab and check
await browser.switchTo().window(tabs[0]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(3);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
});
it('should correctly share screen with microphone muted and maintain proper track state', async () => {
// Helper for inspecting stream tracks
const getMediaTracks = (className: string) => {
return `
const tracks = document.getElementsByClassName('${className}')[0].srcObject.getTracks();
return tracks.map(track => ({
kind: track.kind,
enabled: track.enabled,
id: track.id,
label: track.label
}));`;
};
// Setup: Navigate to room and skip prejoin
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
// Step 1: First mute the microphone
const micButton = await utils.waitForElement('#mic-btn');
await micButton.click();
// Step 2: Start screen sharing
await utils.clickOn('#screenshare-btn');
// Step 3: Verify both streams are present
await utils.waitForElement('.screen-type');
expect(await utils.getNumberOfElements('video')).toEqual(2);
// Step 4: Verify screen share track properties
const screenTracks: any[] = await browser.executeScript(getMediaTracks('screen-type'));
expect(screenTracks.length).toEqual(1);
expect(screenTracks[0].kind).toEqual('video');
expect(screenTracks[0].enabled).toBeTrue();
// Step 5: Verify microphone status indicators for both streams
// await utils.waitForElement('#status-mic');
// const micStatusCount = await utils.getNumberOfElements('#status-mic');
// expect(micStatusCount).toEqual(2);
// Step 6: Stop screen sharing and verify stream count
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(1);
});
// it('should show and hide CAMERA stream when muting video with screensharing', async () => {
// await browser.get(`${url}&prejoin=false`);
// await utils.checkLayoutPresent();
// // Clicking to screensharing button
// const screenshareButton = await utils.waitForElement('#screenshare-btn');
// expect(await screenshareButton.isDisplayed()).toBeTrue();
// await screenshareButton.click();
// await utils.waitForElement('.OV_big');
// expect(await utils.getNumberOfElements('video')).toEqual(2);
// const muteVideoButton = await utils.waitForElement('#camera-btn');
// await muteVideoButton.click();
// expect(await utils.getNumberOfElements('video')).toEqual(1);
// });
// it('should screenshare has audio active when camera is muted', async () => {
// let isAudioEnabled;
// const audioEnableScript = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;';
// await browser.get(`${url}&prejoin=false`);
// await utils.checkLayoutPresent();
// // Clicking to screensharing button
// const screenshareButton = await utils.waitForElement('#screenshare-btn');
// expect(await utils.isPresent('#screenshare-btn')).toBeTrue();
// await screenshareButton.click();
// await utils.waitForElement('.OV_big');
// expect(await utils.getNumberOfElements('video')).toEqual(2);
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(1);
// // Muting camera video
// const muteVideoButton = await utils.waitForElement('#camera-btn');
// await muteVideoButton.click();
// expect(await utils.getNumberOfElements('video')).toEqual(1);
// await browser.sleep(500);
// expect(await utils.isPresent('#status-mic')).toBeFalse();
// // Checking if audio is muted after join the room
// isAudioEnabled = await browser.executeScript(audioEnableScript);
// expect(isAudioEnabled).toBeTrue();
// // Unmuting camera
// await muteVideoButton.click();
// await browser.sleep(1000);
// await utils.waitForElement('.camera-type');
// expect(await utils.getNumberOfElements('video')).toEqual(2);
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(1);
// });
// it('should camera come back with audio muted when screensharing', async () => {
// let element, isAudioEnabled;
// const getAudioScript = (className: string) => {
// return `return document.getElementsByClassName('${className}')[0].srcObject.getAudioTracks()[0].enabled;`;
// };
// await browser.get(`${url}&prejoin=false`);
// await utils.checkLayoutPresent();
// // Clicking to screensharing button
// const screenshareButton = await utils.waitForElement('#screenshare-btn');
// await screenshareButton.click();
// await utils.waitForElement('.screen-type');
// expect(await utils.getNumberOfElements('video')).toEqual(2);
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(1);
// // Mute camera
// const muteVideoButton = await utils.waitForElement('#camera-btn');
// await muteVideoButton.click();
// expect(await utils.getNumberOfElements('video')).toEqual(1);
// expect(await utils.isPresent('#status-mic')).toBeFalse();
// // Checking if audio is muted after join the room
// isAudioEnabled = await browser.executeScript(getAudioScript('screen-type'));
// expect(isAudioEnabled).toBeTrue();
// // Mute audio
// const muteAudioButton = await utils.waitForElement('#mic-btn');
// await muteAudioButton.click();
// await utils.waitForElement('#status-mic');
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(1);
// isAudioEnabled = await browser.executeScript(getAudioScript('screen-type'));
// expect(isAudioEnabled).toBeFalse();
// // Unmute camera
// await muteVideoButton.click();
// await utils.waitForElement('.camera-type');
// expect(await utils.getNumberOfElements('video')).toEqual(2);
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(2);
// isAudioEnabled = await browser.executeScript(getAudioScript('camera-type'));
// expect(isAudioEnabled).toBeFalse();
// });
});

View File

@ -1,76 +0,0 @@
import { Capabilities } from 'selenium-webdriver';
import * as chrome from 'selenium-webdriver/chrome';
import { LAUNCH_MODE } from './config';
interface BrowserConfig {
appUrl: string;
seleniumAddress: string;
browserCapabilities: Capabilities;
browserOptions: chrome.Options;
browserName: string;
}
const audioPath = LAUNCH_MODE === 'CI' ? `e2e-assets/audio_test.wav` : 'e2e/assets/audio_test.wav';
const chromeArguments = [
'--window-size=1300,1000',
// '--headless',
'--use-fake-ui-for-media-stream',
'--use-fake-device-for-media-stream',
`--use-file-for-fake-audio-capture=${audioPath}`
];
const chromeArgumentsCI = [
'--window-size=1300,1000',
'--headless',
'--no-sandbox',
'--disable-gpu',
'--disable-popup-blocking',
'--no-first-run',
'--no-default-browser-check',
'--disable-dev-shm-usage',
'--disable-background-networking',
'--disable-default-apps',
'--use-fake-ui-for-media-stream',
'--use-fake-device-for-media-stream',
`--use-file-for-fake-audio-capture=${audioPath}`,
'--autoplay-policy=no-user-gesture-required',
'--allow-file-access-from-files'
];
const chromeArgumentsWithoutMediaDevices = ['--headless', '--window-size=1300,900', '--deny-permission-prompts'];
const chromeArgumentsWithoutMediaDevicesCI = [
'--window-size=1300,900',
'--headless',
'--no-sandbox',
'--disable-gpu',
'--disable-popup-blocking',
'--no-first-run',
'--no-default-browser-check',
'--disable-dev-shm-usage',
'--disable-background-networking',
'--disable-default-apps',
'--deny-permission-prompts'
];
export const TestAppConfig: BrowserConfig = {
appUrl: 'http://localhost:4200/#/call?staticVideos=false',
seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:4444/wd/hub' : '',
browserName: 'chrome',
browserCapabilities: Capabilities.chrome().set('acceptInsecureCerts', true),
browserOptions: new chrome.Options().addArguments(...(LAUNCH_MODE === 'CI' ? chromeArgumentsCI : chromeArguments))
};
export const NestedConfig: BrowserConfig = {
appUrl: 'http://localhost:4200/#/testing',
seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:4444/wd/hub' : '',
browserName: 'Chrome',
browserCapabilities: Capabilities.chrome().set('acceptInsecureCerts', true),
browserOptions: new chrome.Options().addArguments(...(LAUNCH_MODE === 'CI' ? chromeArgumentsCI : chromeArguments))
};
export function getBrowserOptionsWithoutDevices() {
if (LAUNCH_MODE === 'CI') {
return new chrome.Options().addArguments(...chromeArgumentsWithoutMediaDevicesCI);
} else {
return new chrome.Options().addArguments(...chromeArgumentsWithoutMediaDevices);
}
}

View File

@ -1,773 +0,0 @@
import { Builder, ILocation, IRectangle, ISize, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('Stream rendering and media toggling scenarios', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should not render any video element when joining with video disabled', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=false`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(1);
});
it('should render a video element but no audio when joining with audio muted', async () => {
await browser.get(`${url}&prejoin=true&audioEnabled=false`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
});
it('should render both video and audio elements when joining with both enabled', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=true`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1);
});
it('should add a screen share video/audio when sharing screen with both camera and mic muted', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=false`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(1); //screen sharse video
expect(await utils.getNumberOfElements('audio')).toEqual(1); //screen share audio
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
});
it('should add a screen share video/audio when sharing screen with both camera and mic enabled', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=true`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1);
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(2); //screen share audio and local audio
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1);
});
/* ------------ Checking video/audio elements with two participants ------------ */
it('should not render any video/audio elements when two participants join with both video and audio muted', async () => {
const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[0]);
await browser.sleep(1000);
await utils.waitForElement('.OV_stream.remote');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await browser.switchTo().window(tabs[1]);
await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
});
it('should render two video elements and no audio when two participants join with audio muted', async () => {
const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
const tabs = await utils.openTab(fixedUrl);
await browser.sleep(1000);
await browser.switchTo().window(tabs[0]);
await utils.waitForElement('.OV_stream.remote');
await browser.sleep(2000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await browser.switchTo().window(tabs[1]);
await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
});
it('should not render any video elements but should render two audio elements when two participants join with video disabled', async () => {
const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(1);
const tabs = await utils.openTab(fixedUrl);
await browser.sleep(1000);
await browser.switchTo().window(tabs[0]);
await utils.waitForElement('.OV_stream.remote');
await browser.sleep(2000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(2);
await browser.switchTo().window(tabs[1]);
await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(2);
});
it('should add a screen share video/audio when a participant with both video and audio muted shares their screen (two participants)', async () => {
const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[1]);
await utils.waitForElement('.OV_stream.local');
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios
await browser.switchTo().window(tabs[0]);
await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios
await browser.switchTo().window(tabs[1]);
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await browser.switchTo().window(tabs[0]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
});
it('should add a screen share video/audio when a remote participant with both video and audio enabled shares their screen', async () => {
const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=true`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[1]);
await utils.waitForElement('.OV_stream.local');
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3);
expect(await utils.getNumberOfElements('video')).toEqual(3);
expect(await utils.getNumberOfElements('audio')).toEqual(3); // screen share audios and local audio and remote audio
await browser.switchTo().window(tabs[0]);
await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3);
expect(await utils.getNumberOfElements('video')).toEqual(3);
expect(await utils.getNumberOfElements('audio')).toEqual(3); // screen share audios and local audio and remote audio
await browser.switchTo().window(tabs[1]);
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(2);
await browser.switchTo().window(tabs[0]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(2);
});
it('should add a screen share video/audio for both participants when both share their screen with video/audio muted', async () => {
const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[1]);
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(4);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(2); // screen share audios
await browser.switchTo().window(tabs[0]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(4);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(2); // screen share audios
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios
await browser.switchTo().window(tabs[1]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios
});
});
describe('Stream UI controls and interaction features', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should show the PIN button over the LOCAL video', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#pin-btn');
expect(await utils.isPresent('#pin-btn')).toBeTrue();
});
it('should show the PIN button over the REMOTE video', async () => {
const roomName = 'pinE2E';
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
// Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript);
const tabs = await browser.getAllWindowHandles();
await browser.switchTo().window(tabs[1]);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
await utils.waitForElement('.OV_stream.remote');
await utils.hoverOn('.OV_stream.remote');
await utils.waitForElement('#pin-btn');
expect(await utils.isPresent('#pin-btn')).toBeTrue();
});
it('should show the SILENCE button ONLY over the REMOTE video', async () => {
const roomName = 'silenceE2E';
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.local');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream.local #silence-btn')).toEqual(0);
// Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript);
const tabs = await browser.getAllWindowHandles();
await browser.switchTo().window(tabs[1]);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.remote');
await utils.hoverOn('.OV_stream.remote');
await utils.waitForElement('.OV_stream.remote #silence-btn');
expect(await utils.isPresent('.OV_stream.remote #silence-btn')).toBeTrue();
expect(await utils.getNumberOfElements('.OV_stream.remote #silence-btn')).toEqual(1);
await utils.hoverOn('.OV_stream.local');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream.local #silence-btn')).toEqual(0);
});
it('should show the MINIMIZE button ONLY over the LOCAL video', async () => {
const roomName = 'minimizeE2E';
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn');
expect(await utils.isPresent('#minimize-btn')).toBeTrue();
// Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript);
const tabs = await browser.getAllWindowHandles();
await browser.switchTo().window(tabs[1]);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.remote');
expect(await utils.getNumberOfElements('#minimize-btn')).toEqual(0);
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn');
expect(await utils.isPresent('#minimize-btn')).toBeTrue();
});
it('should minimize the LOCAL video', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
const stream = await utils.waitForElement('.OV_stream.local');
const streamProps: IRectangle = await stream.getRect();
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn');
await utils.clickOn('#minimize-btn');
await browser.sleep(900);
const minimizeStream = await utils.waitForElement('.OV_stream.local');
const minimizeStreamProps: IRectangle = await minimizeStream.getRect();
expect(streamProps.height).not.toEqual(minimizeStreamProps.height);
expect(streamProps.width).not.toEqual(minimizeStreamProps.width);
expect(minimizeStreamProps.x).toBeLessThan(100);
expect(minimizeStreamProps.y).toBeLessThan(100);
});
it('should MAXIMIZE the local video', async () => {
await browser.get(`${url}&prejoin=false&audioEnabled=false`);
const marginX = 5;
await utils.checkLayoutPresent();
await utils.subscribeToDropEvent();
await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn');
await utils.clickOn('#minimize-btn');
await browser.sleep(500);
await utils.dragToRight(300, 300);
await browser.sleep(500);
let stream = await utils.waitForElement('.OV_stream.local');
let streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(300 + marginX);
expect(streamProps.y).toEqual(300);
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn');
await utils.clickOn('#minimize-btn');
await browser.sleep(1500);
stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect();
expect(streamProps.x).toBeLessThan(300 + marginX);
expect(streamProps.y).toEqual(1); //.OV_publisher element has 1px of padding
});
it('should be able to dragg the minimized LOCAL video', async () => {
await browser.get(`${url}&prejoin=false&audioEnabled=false`);
const marginX = 5;
await utils.checkLayoutPresent();
await utils.subscribeToDropEvent();
await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn');
await utils.clickOn('#minimize-btn');
await browser.sleep(500);
await utils.dragToRight(300, 300);
await browser.sleep(500);
const stream = await utils.waitForElement('.OV_stream.local');
const streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(300 + marginX);
expect(streamProps.y).toEqual(300);
});
it('should be the MINIMIZED video ALWAYS VISIBLE when toggling panels', async () => {
await browser.get(`${url}&prejoin=false&audioEnabled=false`);
const marginX = 5;
await utils.checkLayoutPresent();
await utils.subscribeToDropEvent();
// Minimize stream element
await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn');
await utils.clickOn('#minimize-btn');
await browser.sleep(500);
await utils.dragToRight(900, 0);
await browser.sleep(500);
let stream = await utils.waitForElement('.OV_stream.local');
let streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(900 + marginX);
expect(streamProps.y).toEqual(0);
// Open chat panel
await utils.clickOn('#chat-panel-btn');
await browser.sleep(1000);
stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect();
let lastX = streamProps.x;
expect(streamProps.x).toBeLessThan(900 + marginX);
expect(streamProps.y).toEqual(0);
// Close chat panel
await utils.clickOn('#chat-panel-btn');
await browser.sleep(1000);
stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect();
expect(streamProps.x).toBeGreaterThanOrEqual(lastX + marginX);
expect(streamProps.y).toEqual(0);
});
it('should be the MINIMIZED video go to the right when panel closes', async () => {
await browser.get(`${url}&prejoin=false&audioEnabled=false`);
const waitTimeout = 1000;
const marginX = 5;
const newX = 641 - marginX;
await utils.checkLayoutPresent();
await utils.subscribeToDropEvent();
// Open chat panel
await utils.waitForElement('.OV_stream.local');
await utils.waitForElement('#chat-panel-btn');
await utils.clickOn('#chat-panel-btn');
// Minimize stream element
await browser.sleep(waitTimeout);
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn');
await utils.clickOn('#minimize-btn');
await browser.sleep(waitTimeout);
await utils.dragToRight(newX, 0);
await browser.sleep(waitTimeout);
let stream = await utils.waitForElement('.OV_stream.local');
let streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(newX + marginX);
expect(streamProps.y).toEqual(0);
// Close chat panel
// There is a unstable behaviour simulating the drag and drop with selenium (the stream is not moved the first time)
// So we are going to open and close the chat panel two times to ensure the stream is moved
await utils.clickOn('#chat-panel-btn');
await browser.sleep(waitTimeout);
await utils.clickOn('#chat-panel-btn');
await browser.sleep(waitTimeout);
await utils.clickOn('#chat-panel-btn');
await browser.sleep(1000);
stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect();
let lastX = streamProps.x;
expect(streamProps.x).toBeGreaterThanOrEqual(newX + marginX);
expect(streamProps.y).toEqual(0);
// Open chat panel
await utils.clickOn('#chat-panel-btn');
await browser.sleep(waitTimeout);
stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect();
expect(streamProps.x).toBeLessThan(lastX + marginX);
expect(streamProps.y).toEqual(0);
});
it('should be the MINIMIZED video ALWAYS VISIBLE when toggling from small to bigger panel', async () => {
await browser.get(`${url}&prejoin=false&audioEnabled=false`);
const marginX = 5;
await utils.checkLayoutPresent();
await utils.subscribeToDropEvent();
// Minimize stream element
await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn');
await utils.clickOn('#minimize-btn');
await browser.sleep(500);
await utils.dragToRight(900, 0);
await browser.sleep(500);
let stream = await utils.waitForElement('.OV_stream.local');
let streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(900 + marginX);
expect(streamProps.y).toEqual(0);
// Open chat panel
await utils.clickOn('#chat-panel-btn');
await browser.sleep(1000);
stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect();
let lastX = streamProps.x;
expect(streamProps.x).toBeLessThan(900 + marginX);
expect(streamProps.y).toEqual(0);
// Open settings panel
await utils.togglePanel('settings');
await browser.sleep(1000);
stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect();
expect(streamProps.x).toBeLessThan(lastX + marginX);
expect(streamProps.y).toEqual(0);
lastX = streamProps.x;
// Open chat panel
await utils.clickOn('#chat-panel-btn');
await browser.sleep(1000);
stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect();
expect(streamProps.x).toBeGreaterThanOrEqual(lastX + marginX);
expect(streamProps.y).toEqual(0);
});
it('should show the audio detection elements when participant is speaking', async () => {
const roomName = 'speakingE2E';
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
await browser.get(`${fixedUrl}&audioEnabled=false`);
await utils.checkLayoutPresent();
// Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript);
await browser.sleep(1000);
const tabs = await browser.getAllWindowHandles();
await browser.switchTo().window(tabs[0]);
await utils.waitForElement('.OV_stream.remote.speaking');
expect(await utils.getNumberOfElements('.OV_stream.remote.speaking')).toEqual(1);
// Check only one element is marked as speaker due to the local participant is muted
await utils.waitForElement('.OV_stream.speaking');
expect(await utils.getNumberOfElements('.OV_stream.speaking')).toEqual(1);
});
});
describe('Video playback reliability with different media states', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should play the participant video with only audio', async () => {
const roomName = 'audioOnlyE2E';
const fixedUrl = `${url}&roomName=${roomName}`;
await browser.get(fixedUrl);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
// Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript);
const tabs = await browser.getAllWindowHandles();
await browser.switchTo().window(tabs[1]);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#camera-button');
await utils.clickOn('#join-button');
// Go to first tab
await browser.switchTo().window(tabs[0]);
// Wait until NO_STREAM_PLAYING_EVENT exception timeout is reached
await browser.sleep(6000);
const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT');
expect(exceptionQuantity).toEqual(0);
});
it('should play the participant video with only video', async () => {
const roomName = 'videoOnlyE2E';
const fixedUrl = `${url}&roomName=${roomName}`;
await browser.get(fixedUrl);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
// Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript);
const tabs = await browser.getAllWindowHandles();
await browser.switchTo().window(tabs[1]);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#microphone-button');
await utils.clickOn('#join-button');
// Go to first tab
await browser.switchTo().window(tabs[0]);
// Wait until NO_STREAM_PLAYING_EVENT exception timeout is reached
await browser.sleep(6000);
const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT');
expect(exceptionQuantity).toEqual(0);
});
});

View File

@ -1,64 +0,0 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('Toolbar button functionality for local media control', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should toggle mute/unmute on the local microphone and update the icon accordingly', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
const micButton = await utils.waitForElement('#mic-btn');
await micButton.click();
await utils.waitForElement('#mic-btn #mic_off');
expect(await utils.isPresent('#mic-btn #mic_off')).toBeTrue();
await micButton.click();
await utils.waitForElement('#mic-btn #mic');
expect(await utils.isPresent('#mic-btn #mic')).toBeTrue();
});
it('should toggle mute/unmute on the local camera and update the icon accordingly', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
const cameraButton = await utils.waitForElement('#camera-btn');
await cameraButton.click();
await utils.waitForElement('#camera-btn #videocam_off');
expect(await utils.isPresent('#camera-btn #videocam_off')).toBeTrue();
await cameraButton.click();
await utils.waitForElement('#camera-btn #videocam');
expect(await utils.isPresent('#camera-btn #videocam')).toBeTrue();
});
});

View File

@ -1,13 +0,0 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"strict": true,
"outDir": "./dist",
"lib": ["es6"],
"types": [ "jasmine", "node" ],
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
}

View File

@ -1,282 +0,0 @@
import { By, until, WebDriver, WebElement } from 'selenium-webdriver';
import * as fs from 'fs';
import { PNG } from 'pngjs';
import pixelmatch from 'pixelmatch';
type PNGWithMetadata = PNG & { data: Buffer };
export class OpenViduComponentsPO {
private TIMEOUT = 10 * 1000;
private POLL_TIMEOUT = 1 * 1000;
constructor(private browser: WebDriver) {}
async waitForElement(selector: string): Promise<WebElement> {
return await this.browser.wait(
until.elementLocated(By.css(selector)),
this.TIMEOUT,
`Time out waiting for ${selector}`,
this.POLL_TIMEOUT
);
}
async getNumberOfElements(selector: string): Promise<number> {
return (await this.browser.findElements(By.css(selector))).length;
}
async isPresent(selector: string): Promise<boolean> {
const elements = await this.browser.findElements(By.css(selector));
return elements.length > 0;
}
async sendKeys(selector: string, keys: string): Promise<void> {
const element = await this.waitForElement(selector);
await element.sendKeys(keys);
}
async checkPrejoinIsPresent(): Promise<void> {
await this.waitForElement('#prejoin-container');
expect(await this.isPresent('#prejoin-container')).toBe(true);
}
async checkSessionIsPresent() {
await this.waitForElement('#call-container');
expect(await this.isPresent('#call-container')).toBe(true);
await this.waitForElement('#session-container');
expect(await this.isPresent('#session-container')).toBe(true);
}
async checkLayoutPresent(): Promise<void> {
await this.waitForElement('#layout-container');
expect(await this.isPresent('#layout-container')).toBe(true);
await this.waitForElement('#layout');
expect(await this.isPresent('#layout')).toBe(true);
}
async checkStreamIsPresent(): Promise<void> {
await this.waitForElement('.OV_stream');
expect(await this.isPresent('.OV_stream')).toBe(true);
}
async checkVideoElementIsPresent(): Promise<void> {
await this.waitForElement('video');
expect(await this.isPresent('video')).toBe(true);
}
async checkToolbarIsPresent(): Promise<void> {
await this.waitForElement('#toolbar');
await this.waitForElement('#media-buttons-container');
expect(await this.isPresent('#media-buttons-container')).toBe(true);
}
async chceckProFeatureAlertIsPresent(): Promise<void> {
await this.waitForElement('ov-pro-feature-template');
expect(await this.isPresent('ov-pro-feature-template')).toBe(true);
}
async clickOn(selector: string): Promise<void> {
const element = await this.waitForElement(selector);
await element.click();
}
async hoverOn(selector: string): Promise<void> {
const element = await this.waitForElement(selector);
const action = this.browser.actions().move({ origin: element, duration: 1000 });
return action.perform();
}
async openTab(url: string): Promise<string[]> {
const newTabScript = `window.open("${url}")`;
await this.browser.executeScript(newTabScript);
return this.browser.getAllWindowHandles();
}
async subscribeToDropEvent(): Promise<void> {
const script = `
document.dispatchEvent(new Event("webcomponentTestingEndedDragAndDropEvent"));
`;
await this.browser.executeScript(script);
}
async dragToRight(x: number, y: number): Promise<void> {
const script = `
document.dispatchEvent(new CustomEvent("webcomponentTestingEndedDragAndDropRightEvent", {detail: {x: arguments[0], y: arguments[1]}}));
`;
await this.browser.executeScript(script, x, y);
}
async toggleToolbarMoreOptions(): Promise<void> {
await this.waitForElement('#more-options-btn');
expect(await this.isPresent('#more-options-btn')).toBe(true);
await this.clickOn('#more-options-btn');
await this.browser.sleep(500);
await this.waitForElement('#more-options-menu');
}
async disableScreenShare(): Promise<void> {
await this.waitForElement('#screenshare-btn');
await this.clickOn('#screenshare-btn');
await this.browser.sleep(500);
await this.waitForElement('#screenshare-menu');
await this.clickOn('#disable-screen-button');
await this.browser.sleep(1000);
}
async toggleRecordingFromToolbar() {
// Open more options menu
await this.toggleToolbarMoreOptions();
await this.waitForElement('#recording-btn');
expect(await this.isPresent('#recording-btn')).toBe(true);
await this.clickOn('#recording-btn');
}
async toggleFullscreenFromToolbar() {
// Open more options menu
await this.toggleToolbarMoreOptions();
await this.waitForElement('#fullscreen-btn');
expect(await this.isPresent('#fullscreen-btn')).toBe(true);
await this.clickOn('#fullscreen-btn');
}
async leaveRoom() {
try {
// Close any open panels or menus clicking on the body
await this.clickOn('body');
await this.browser.sleep(300);
// Verify that the leave button is present
await this.waitForElement('#leave-btn');
// Click on the leave button
await this.clickOn('#leave-btn');
// Verify that the session container is no longer present
await this.browser.wait(
async () => {
return !(await this.isPresent('#session-container'));
},
this.TIMEOUT,
'Session container should disappear after leaving room'
);
// Wait for the prejoin container to be present again
await this.browser.sleep(500);
// Verify that there are no video elements left in the DOM
const videoCount = await this.getNumberOfElements('video');
if (videoCount > 0) {
console.warn(`Warning: ${videoCount} video elements still present after leaving room`);
}
} catch (error) {
console.error('Error during leaveRoom:', error);
throw error;
}
}
async togglePanel(panelName: string) {
switch (panelName) {
case 'activities':
await this.waitForElement('#activities-panel-btn');
expect(await this.isPresent('#activities-panel-btn')).toBe(true);
await this.clickOn('#activities-panel-btn');
break;
case 'chat':
await this.waitForElement('#chat-panel-btn');
await this.clickOn('#chat-panel-btn');
break;
case 'participants':
await this.waitForElement('#participants-panel-btn');
await this.clickOn('#participants-panel-btn');
break;
case 'backgrounds':
await this.waitForElement('#more-options-btn');
await this.clickOn('#more-options-btn');
await this.browser.sleep(500);
await this.waitForElement('#virtual-bg-btn');
await this.clickOn('#virtual-bg-btn');
await this.browser.sleep(1000);
break;
case 'settings':
await this.toggleToolbarMoreOptions();
await this.waitForElement('#toolbar-settings-btn');
await this.clickOn('#toolbar-settings-btn');
break;
}
await this.browser.sleep(500);
}
async applyBackground(bgId: string) {
await this.waitForElement('ov-background-effects-panel');
await this.browser.sleep(1000);
await this.waitForElement(`#effect-${bgId}`);
await this.clickOn(`#effect-${bgId}`);
await this.browser.sleep(2000);
}
async applyVirtualBackgroundFromPrejoin(bgId: string): Promise<void> {
await this.waitForElement('#backgrounds-button');
await this.clickOn('#backgrounds-button');
await this.applyBackground(bgId);
await this.clickOn('#backgrounds-button');
}
async saveScreenshot(filename: string, element: WebElement) {
const image = await element.takeScreenshot();
fs.writeFileSync(filename, image, 'base64');
}
async expectVirtualBackgroundApplied(
img1Name: string,
img2Name: string,
{
threshold = 0.4,
minDiffPixels = 500,
debug = false
}: {
threshold?: number;
minDiffPixels?: number;
debug?: boolean;
} = {}
): Promise<void> {
const beforeImg = PNG.sync.read(fs.readFileSync(img1Name));
const afterImg = PNG.sync.read(fs.readFileSync(img2Name));
const { width, height } = beforeImg;
const diff = new PNG({ width, height });
// const numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, width, height, {
// threshold: 0.4
// // alpha: 0.5,
// // includeAA: false,
// // diffColor: [255, 0, 0]
// });
const numDiffPixels = pixelmatch(beforeImg.data, afterImg.data, diff.data, width, height, {
threshold
// includeAA: true
});
if (numDiffPixels <= minDiffPixels) {
// Sólo guardar los archivos de debug si falla la prueba
if (debug) {
fs.writeFileSync('before.png', PNG.sync.write(beforeImg));
fs.writeFileSync('after.png', PNG.sync.write(afterImg));
fs.writeFileSync('diff.png', PNG.sync.write(diff));
}
}
expect(numDiffPixels).toBeGreaterThan(minDiffPixels, 'The virtual background was not applied correctly');
// fs.writeFileSync('diff.png', PNG.sync.write(diff));
// expect(numDiffPixels).to.be.greaterThan(500, 'The virtual background was not applied correctly');
}
}

View File

@ -1,155 +0,0 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('Prejoin: Virtual Backgrounds', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should close BACKGROUNDS on prejoin page when VIDEO is disabled', async () => {
let element;
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
const backgroundButton = await utils.waitForElement('#backgrounds-button');
expect(await utils.isPresent('#backgrounds-button')).toBeTrue();
expect(await backgroundButton.isEnabled()).toBeTrue();
await utils.clickOn('#backgrounds-button');
await browser.sleep(500);
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).toBeTrue();
await utils.clickOn('#camera-button');
await browser.sleep(500);
element = await utils.waitForElement('#video-poster');
expect(await utils.isPresent('#video-poster')).toBeTrue();
expect(await backgroundButton.isDisplayed()).toBeTrue();
expect(await backgroundButton.isEnabled()).toBeFalse();
await browser.sleep(1000);
expect(await utils.getNumberOfElements('#background-effects-container')).toBe(0);
});
it('should open and close BACKGROUNDS panel on prejoin page', async () => {
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
const backgroundButton = await utils.waitForElement('#backgrounds-button');
expect(await utils.isPresent('#backgrounds-button')).toBeTrue();
expect(await backgroundButton.isEnabled()).toBeTrue();
await backgroundButton.click();
await browser.sleep(500);
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).toBeTrue();
await utils.clickOn('#backgrounds-button');
await browser.sleep(1000);
expect(await utils.getNumberOfElements('#background-effects-container')).toBe(0);
});
it('should apply a background effect on prejoin page', async () => {
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
let videoElement = await utils.waitForElement('.OV_video-element');
await utils.saveScreenshot('before.png', videoElement);
await utils.applyVirtualBackgroundFromPrejoin('1');
await browser.sleep(1000);
videoElement = await utils.waitForElement('.OV_video-element');
await utils.saveScreenshot('after.png', videoElement);
await utils.expectVirtualBackgroundApplied('before.png', 'after.png');
});
});
describe('Room: Virtual Backgrounds', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should open and close BACKGROUNDS panel in the room', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('backgrounds');
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).toBeTrue();
await utils.togglePanel('backgrounds');
await browser.sleep(1000);
expect(await utils.getNumberOfElements('#background-effects-container')).toBe(0);
});
it('should apply a background effect in the room', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
await utils.togglePanel('backgrounds');
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).toBeTrue();
let videoElement = await utils.waitForElement('.OV_video-element');
await utils.saveScreenshot('before.png', videoElement);
await utils.applyBackground('1');
await browser.sleep(1000);
videoElement = await utils.waitForElement('.OV_video-element');
await utils.saveScreenshot('after.png', videoElement);
await utils.expectVirtualBackgroundApplied('before.png', 'after.png');
});
});

View File

@ -1,27 +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 '/latest/' projects src | xargs sed -i -e 's|/latest/|/'${BASEHREF_VERSION}'/|g'
# Replace testapp README by openvidu-components-angular README
mv README.md README-testapp.md
cp ./projects/openvidu-components-angular/README.md .
# Generate Compodoc
npm run doc:build
# Return links to "stable" version
grep -rl '/'${BASEHREF_VERSION}'/' projects src | xargs sed -i -e 's|/'${BASEHREF_VERSION}'/|/latest/|g'
# Undo changes with READMEs
rm README.md
mv README-testapp.md README.md
# Clean previous docs from openvidu.io repo and copy new ones
npm run doc:clean-copy

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