mirror of https://github.com/OpenVidu/openvidu.git
OpenVidu v3
parent
d9bc9b3700
commit
eccc37156a
|
@ -1,248 +0,0 @@
|
|||
#!/bin/bash -x
|
||||
set -eu -o pipefail
|
||||
|
||||
############################################################################
|
||||
# Any function offered by this file that is not path agnostic assumes that #
|
||||
# the path is located where the first command of each function requires it #
|
||||
############################################################################
|
||||
|
||||
CLEAN_ENVIRONMENT=false
|
||||
BUILD_OV_BROWSER=false
|
||||
BUILD_OV_NODE_CLIENT=false
|
||||
BUILD_OV_JAVA_CLIENT=false
|
||||
BUILD_OV_PARENT=false
|
||||
BUILD_OV_TESTAPP=false
|
||||
BUILD_OV_SERVER_DASHBOARD=false
|
||||
BUILD_OV_SERVER=false
|
||||
BUILD_OV_SERVER_DEPENDENCY=false
|
||||
BUILD_OV_SERVER_PRO_INSPECTOR=false
|
||||
BUILD_OV_SERVER_PRO=false
|
||||
CHECK_AND_PREPARE_KURENTO_SNAPSHOT=false
|
||||
|
||||
if [[ -n ${1:-} ]]; then
|
||||
case "${1:-}" in
|
||||
|
||||
--clean-environment)
|
||||
CLEAN_ENVIRONMENT=true
|
||||
;;
|
||||
|
||||
--build-openvidu-browser)
|
||||
BUILD_OV_BROWSER=true
|
||||
;;
|
||||
|
||||
--build-openvidu-node-client)
|
||||
BUILD_OV_NODE_CLIENT=true
|
||||
;;
|
||||
|
||||
--build-openvidu-java-client)
|
||||
BUILD_OV_JAVA_CLIENT=true
|
||||
;;
|
||||
|
||||
--build-openvidu-parent)
|
||||
BUILD_OV_PARENT=true
|
||||
;;
|
||||
|
||||
--build-openvidu-testapp)
|
||||
BUILD_OV_TESTAPP=true
|
||||
;;
|
||||
|
||||
--build-openvidu-server-dashboard)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide LINK_LOCAL_DEPENDENCIES as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUILD_OV_SERVER_DASHBOARD=true
|
||||
LINK_LOCAL_DEPENDENCIES="${2}"
|
||||
;;
|
||||
|
||||
--build-openvidu-server)
|
||||
BUILD_OV_SERVER=true
|
||||
;;
|
||||
|
||||
--build-openvidu-server-dependency)
|
||||
BUILD_OV_SERVER_DEPENDENCY=true
|
||||
;;
|
||||
|
||||
--build-openvidu-server-pro-inspector)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide LINK_LOCAL_DEPENDENCIES as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUILD_OV_SERVER_PRO_INSPECTOR=true
|
||||
LINK_LOCAL_DEPENDENCIES="${2}"
|
||||
;;
|
||||
|
||||
--build-openvidu-server-pro)
|
||||
BUILD_OV_SERVER_PRO=true
|
||||
;;
|
||||
--check-and-prepare-kurento-snapshot)
|
||||
CHECK_AND_PREPARE_KURENTO_SNAPSHOT=true
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unrecognized method $1"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
else
|
||||
echo "Must provide a method to execute as first parameter when calling the script"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Clean environment
|
||||
# -------------
|
||||
if [[ "${CLEAN_ENVIRONMENT}" == true ]]; then
|
||||
|
||||
# Remove all running containers except test container and runner container
|
||||
ids=$(docker ps -a -q)
|
||||
for id in $ids; do
|
||||
DOCKER_IMAGE=$(docker inspect --format='{{.Config.Image}}' $id)
|
||||
if [[ "${DOCKER_IMAGE}" != *"$TEST_IMAGE"* ]] &&
|
||||
[[ "${DOCKER_IMAGE}" != *"runner-image"* ]]; then
|
||||
echo "Removing container image '$DOCKER_IMAGE' with id '$id'"
|
||||
docker stop $id && docker rm $id
|
||||
fi
|
||||
done
|
||||
|
||||
# Clean /opt/openvidu contents
|
||||
rm -rf /opt/openvidu/*
|
||||
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build openvidu-browser
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_BROWSER}" == true ]]; then
|
||||
pushd openvidu-browser || exit 1
|
||||
npm install
|
||||
npm run build
|
||||
npm link
|
||||
npm pack
|
||||
mv openvidu-browser-*.tgz /opt/openvidu
|
||||
npm run browserify
|
||||
npm run browserify-prod
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build openvidu-node-client
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_NODE_CLIENT}" == true ]]; then
|
||||
pushd openvidu-node-client
|
||||
npm install
|
||||
npm run build
|
||||
npm link
|
||||
npm pack
|
||||
mv openvidu-node-client-*.tgz /opt/openvidu
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build openvidu-java-client
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_JAVA_CLIENT}" == true ]]; then
|
||||
pushd openvidu-java-client
|
||||
MVN_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
|
||||
mvn -B clean compile package
|
||||
mvn -B install:install-file -Dfile=target/openvidu-java-client-${MVN_VERSION}.jar \
|
||||
-DgroupId=io.openvidu \
|
||||
-DartifactId=openvidu-java-client \
|
||||
-Dversion=${MVN_VERSION} -Dpackaging=jar
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build openvidu-parent
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_PARENT}" == true ]]; then
|
||||
mvn -B -DskipTests=true -Dmaven.artifact.threads=1 clean install
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build openvidu-testapp
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_TESTAPP}" == true ]]; then
|
||||
pushd openvidu-testapp
|
||||
npm install
|
||||
npm link openvidu-browser openvidu-node-client
|
||||
export NG_CLI_ANALYTICS="false" && ./node_modules/@angular/cli/bin/ng.js build --configuration production --output-path=/opt/openvidu/testapp
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build openvidu-server dashboard
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_SERVER_DASHBOARD}" == true ]]; then
|
||||
pushd openvidu-server/src/dashboard
|
||||
npm install
|
||||
if [[ "${LINK_LOCAL_DEPENDENCIES}" == true ]]; then
|
||||
npm link openvidu-browser openvidu-node-client
|
||||
fi
|
||||
npm run build-prod
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build openvidu-server
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_SERVER}" == true ]]; then
|
||||
pushd openvidu-server
|
||||
mvn -B -DskipTests=true clean package
|
||||
mv target/openvidu-server-*.jar /opt/openvidu
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build openvidu-server dependency
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_SERVER_DEPENDENCY}" == true ]]; then
|
||||
pushd openvidu-server
|
||||
mvn -B -DskipTests=true -Pdependency clean install
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build Inspector
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_SERVER_PRO_INSPECTOR}" == true ]]; then
|
||||
pushd dashboard
|
||||
npm install
|
||||
if [[ "${LINK_LOCAL_DEPENDENCIES}" == true ]]; then
|
||||
npm link openvidu-browser openvidu-node-client
|
||||
fi
|
||||
npm run build-server-prod
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Build openvidu-server-pro
|
||||
# -------------
|
||||
if [[ "${BUILD_OV_SERVER_PRO}" == true ]]; then
|
||||
pushd openvidu-server-pro
|
||||
mvn -B -DskipTests=true clean package
|
||||
mv target/openvidu-server-pro-*.jar /opt/openvidu
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Check kurento version from pom.xml
|
||||
# If kurento version is a snapshot, configure snapshot builds
|
||||
# -------------
|
||||
if [[ "${CHECK_AND_PREPARE_KURENTO_SNAPSHOT}" == true ]]; then
|
||||
# Check if kurento version is a snapshot
|
||||
KURENTO_VERSION=$(awk -F'[<>]' '/<version.kurento>/ {print $3}' pom.xml)
|
||||
if [[ "${KURENTO_VERSION}" == *"-SNAPSHOT" ]] && [[ -n "${KURENTO_SNAPSHOTS_URL:-}" ]]; then
|
||||
echo "Kurento version is a SNAPSHOT: ${KURENTO_VERSION}"
|
||||
mkdir -p /etc/maven
|
||||
chmod -R 777 /etc/maven
|
||||
pushd /etc/maven
|
||||
rm -f settings.xml
|
||||
curl https://raw.githubusercontent.com/OpenVidu/openvidu/master/ci-scripts/kurento-snapshots.xml -o settings.xml
|
||||
sed -i "s|KURENTO_SNAPSHOTS_URL|${KURENTO_SNAPSHOTS_URL}|g" settings.xml
|
||||
popd
|
||||
else
|
||||
echo "Kurento version is not a SNAPSHOT: ${KURENTO_VERSION}"
|
||||
fi
|
||||
fi
|
|
@ -1,307 +0,0 @@
|
|||
#!/bin/bash -x
|
||||
set -eu -o pipefail
|
||||
|
||||
############################################################################
|
||||
# Any function offered by this file that is not path agnostic assumes that #
|
||||
# the path is located where the first command of each function requires it #
|
||||
############################################################################
|
||||
|
||||
# Bump versions
|
||||
BUMP_NPM_PROJECT_VERSION=false
|
||||
BUMP_NPM_DEPENDENCY_VERSION=false
|
||||
BUMP_MAVEN_PROJECT_VERSION=false
|
||||
BUMP_MAVEN_PROPERTY_VERSION=false
|
||||
BUMP_MAVEN_DEPENDENCY_VERSION=false
|
||||
BUMP_DOCKER_COMPOSE_SERVICE_VERSION=false
|
||||
BUMP_DOCKER_COMPOSE_HEADER_VERSION=false
|
||||
BUMP_DOCKER_IMAGE_VERSION_IN_FILES=false
|
||||
BUMP_APPLICATION_PROPERTIES_VAR_VALUE=false
|
||||
WAIT_FOR_NPM_DEPENDENCY=false
|
||||
GENERIC_SED=false
|
||||
|
||||
if [[ -n ${1:-} ]]; then
|
||||
case "${1:-}" in
|
||||
|
||||
--bump-npm-project-version)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide VERSION as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_NPM_PROJECT_VERSION=true
|
||||
VERSION="${2}"
|
||||
;;
|
||||
|
||||
--bump-npm-dependency-version)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide DEPENDENCY as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${3:-}" ]]; then
|
||||
echo "Must provide VERSION as 2nd parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_NPM_DEPENDENCY_VERSION=true
|
||||
DEPENDENCY="${2}"
|
||||
VERSION="${3}"
|
||||
TYPE_OF_DEPENDENCY="${4:-dependencies}" # [dependencies, devDependencies, peerDependencies, optionalDependencies]
|
||||
;;
|
||||
|
||||
--bump-maven-project-version)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide VERSION as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_MAVEN_PROJECT_VERSION=true
|
||||
VERSION="${2}"
|
||||
;;
|
||||
|
||||
--bump-maven-property-version)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide PROPERTY as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${3:-}" ]]; then
|
||||
echo "Must provide VERSION as 2nd parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_MAVEN_PROPERTY_VERSION=true
|
||||
PROPERTY="${2}"
|
||||
VERSION="${3}"
|
||||
;;
|
||||
|
||||
--bump-maven-dependency-version)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide DEPENDENCY as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${3:-}" ]]; then
|
||||
echo "Must provide VERSION as 2nd parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_MAVEN_DEPENDENCY_VERSION=true
|
||||
DEPENDENCY="${2}"
|
||||
VERSION="${3}"
|
||||
;;
|
||||
|
||||
--bump-docker-compose-service-version)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide DOCKER_COMPOSE_FILE as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${3:-}" ]]; then
|
||||
echo "Must provide SERVICE_IMAGE as 2nd parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${4:-}" ]]; then
|
||||
echo "Must provide VERSION as 3rd parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_DOCKER_COMPOSE_SERVICE_VERSION=true
|
||||
DOCKER_COMPOSE_FILE="${2}"
|
||||
SERVICE_IMAGE="${3}"
|
||||
VERSION="${4}"
|
||||
;;
|
||||
|
||||
--bump-docker-compose-header-version)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide DOCKER_COMPOSE_FILE as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${3:-}" ]]; then
|
||||
echo "Must provide HEADER as 2nd parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${4:-}" ]]; then
|
||||
echo "Must provide VERSION as 3rd parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_DOCKER_COMPOSE_HEADER_VERSION=true
|
||||
DOCKER_COMPOSE_FILE="${2}"
|
||||
HEADER="${3}"
|
||||
VERSION="${4}"
|
||||
;;
|
||||
|
||||
--bump-docker-image-version-in-files)
|
||||
if [[ -z "${3:-}" ]]; then
|
||||
echo "Must provide FILE_NAME_PATTERN as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${4:-}" ]]; then
|
||||
echo "Must provide IMAGE as 2nd parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${4:-}" ]]; then
|
||||
echo "Must provide VERSION as 3rd parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_DOCKER_IMAGE_VERSION_IN_FILES=true
|
||||
FILE_NAME_PATTERN="${2}"
|
||||
IMAGE="${3}"
|
||||
VERSION="${4}"
|
||||
;;
|
||||
|
||||
--bump-application-properties-var-value)
|
||||
if [[ -z "${3:-}" ]]; then
|
||||
echo "Must provide APPLICATION_PROPERTIES_FILE as 2nd parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${4:-}" ]]; then
|
||||
echo "Must provide VARIABLE as 3rd parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${4:-}" ]]; then
|
||||
echo "Must provide VALUE as 4th parameter"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_APPLICATION_PROPERTIES_VAR_VALUE=true
|
||||
APPLICATION_PROPERTIES_FILE="${2}"
|
||||
VARIABLE="${3}"
|
||||
VALUE="${4}"
|
||||
;;
|
||||
|
||||
--wait-for-npm-dependency)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide DEPENDENCY as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${3:-}" ]]; then
|
||||
echo "Must provide VERSION as 2nd parameter"
|
||||
exit 1
|
||||
fi
|
||||
WAIT_FOR_NPM_DEPENDENCY=true
|
||||
DEPENDENCY="${2}"
|
||||
VERSION="${3}"
|
||||
;;
|
||||
|
||||
--generic-sed)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "Must provide FILE as 1st parameter"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${3:-}" ]]; then
|
||||
echo "Must provide SED_EXPRESSION as 2nd parameter"
|
||||
exit 1
|
||||
fi
|
||||
GENERIC_SED=true
|
||||
FILE="${2}"
|
||||
SED_EXPRESSION="${3}"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unrecognized method $1"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
else
|
||||
echo "Must provide a method to execute as first parameter when calling the script"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
compareFiles() {
|
||||
if cmp -s "$1" "$1-AUX"; then
|
||||
rm -f $1-AUX
|
||||
echo "Error: no changes has been made to $1"
|
||||
echo "Trying to change \"$2\" to \"$3\""
|
||||
exit 1
|
||||
else
|
||||
cp -f "$1-AUX" "$1"
|
||||
rm -f "$1-AUX"
|
||||
fi
|
||||
}
|
||||
|
||||
# -------------
|
||||
# Bump NPM project version
|
||||
# -------------
|
||||
if [[ "${BUMP_NPM_PROJECT_VERSION}" == true ]]; then
|
||||
npm version ${VERSION} --git-tag-version=false --commit-hooks=false
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Bump NPM project dependency
|
||||
# -------------
|
||||
if [[ "${BUMP_NPM_DEPENDENCY_VERSION}" == true ]]; then
|
||||
jq -j ".${TYPE_OF_DEPENDENCY}.\"${DEPENDENCY}\" = \"${VERSION}\"" package.json >package.json-AUX
|
||||
compareFiles package.json version $VERSION
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Bump Maven project version
|
||||
# -------------
|
||||
if [[ "${BUMP_MAVEN_PROJECT_VERSION}" == true ]]; then
|
||||
cp pom.xml pom.xml-AUX
|
||||
mvn -DskipTests=true versions:set -DnewVersion="${VERSION}"
|
||||
mv pom.xml changed-pom.xml && mv pom.xml-AUX pom.xml && mv changed-pom.xml pom.xml-AUX
|
||||
compareFiles pom.xml "<version>" $VERSION
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Bump Maven project property
|
||||
# -------------
|
||||
if [[ "${BUMP_MAVEN_PROPERTY_VERSION}" == true ]]; then
|
||||
mvn --batch-mode \
|
||||
-DskipTests=true \
|
||||
versions:set-property \
|
||||
-Dproperty="${PROPERTY}" \
|
||||
-DnewVersion="${VERSION}"
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Bump Maven dependency property
|
||||
# -------------
|
||||
if [[ "${BUMP_MAVEN_DEPENDENCY_VERSION}" == true ]]; then
|
||||
mvn --batch-mode versions:use-dep-version -Dincludes=$DEPENDENCY -DdepVersion=$VERSION -DforceVersion=true
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Bump docker-compose.yml service version
|
||||
# -------------
|
||||
if [[ "${BUMP_DOCKER_COMPOSE_SERVICE_VERSION}" == true ]]; then
|
||||
sed -r "s|image:\s+${SERVICE_IMAGE}:[[:alnum:]._-]+|image: ${SERVICE_IMAGE}:${VERSION}|g" ${DOCKER_COMPOSE_FILE} >${DOCKER_COMPOSE_FILE}-AUX
|
||||
compareFiles $DOCKER_COMPOSE_FILE $SERVICE_IMAGE $VERSION
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Bump docker-compose.yml header version
|
||||
# -------------
|
||||
if [[ "${BUMP_DOCKER_COMPOSE_HEADER_VERSION}" == true ]]; then
|
||||
sed -r "s|#\s+${HEADER}:\s+[[:alnum:]._-]+|# ${HEADER}: ${VERSION}|g" ${DOCKER_COMPOSE_FILE} >${DOCKER_COMPOSE_FILE}-AUX
|
||||
compareFiles $DOCKER_COMPOSE_FILE $HEADER $VERSION
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Bump Docker image version in files
|
||||
# -------------
|
||||
if [[ "${BUMP_DOCKER_IMAGE_VERSION_IN_FILES}" == true ]]; then
|
||||
find . -type f -name ${FILE_NAME_PATTERN} | xargs sed -i -r "s|${IMAGE}:[[:alnum:]._-]+|${IMAGE}:${VERSION}|g"
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Bump application.properties variable value
|
||||
# -------------
|
||||
if [[ "${BUMP_APPLICATION_PROPERTIES_VAR_VALUE}" == true ]]; then
|
||||
sed -r "s%${VARIABLE}((:|=)\s*).*$%${VARIABLE}\1${VALUE}%g" ${APPLICATION_PROPERTIES_FILE} >${APPLICATION_PROPERTIES_FILE}-AUX
|
||||
compareFiles $APPLICATION_PROPERTIES_FILE $VARIABLE $VALUE
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Wait for NPM dependency to be available
|
||||
# -------------
|
||||
if [[ "${WAIT_FOR_NPM_DEPENDENCY}" == true ]]; then
|
||||
CHECK_VERSION_AVAILABILTY="npm show ${DEPENDENCY}@${VERSION} version || echo ''"
|
||||
VERSION_AUX=$(eval "${CHECK_VERSION_AVAILABILTY}")
|
||||
until [[ "${VERSION_AUX}" == "${VERSION}" ]]; do
|
||||
echo "Waiting for ${DEPENDENCY}@${VERSION} to be available in NPM..."
|
||||
sleep 2
|
||||
VERSION_AUX=$(eval "${CHECK_VERSION_AVAILABILTY}")
|
||||
done
|
||||
echo "${DEPENDENCY}@${VERSION} already available in NPM"
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Generic sed replacement
|
||||
# -------------
|
||||
if [[ "${GENERIC_SED}" == true ]]; then
|
||||
sed -r "$SED_EXPRESSION" ${FILE} >${FILE}-AUX
|
||||
compareFiles $FILE "(generic sed)" "$SED_EXPRESSION"
|
||||
fi
|
|
@ -1,161 +0,0 @@
|
|||
#!/bin/bash -x
|
||||
set -eu -o pipefail
|
||||
|
||||
############################################################################
|
||||
# Any function offered by this file that is not path agnostic assumes that #
|
||||
# the path is located where the first command of each function requires it #
|
||||
############################################################################
|
||||
|
||||
# CI flags
|
||||
PREPARE_TEST_ENVIRONMENT=false
|
||||
USE_SPECIFIC_KURENTO_JAVA_COMMIT=false
|
||||
SERVE_OV_TESTAPP=false
|
||||
CHECK_AND_PREPARE_KURENTO_SNAPSHOT=false
|
||||
|
||||
if [[ -n ${1:-} ]]; then
|
||||
case "${1:-}" in
|
||||
|
||||
--prepare-test-environment)
|
||||
PREPARE_TEST_ENVIRONMENT=true
|
||||
;;
|
||||
|
||||
--check-and-prepare-kurento-snapshot)
|
||||
CHECK_AND_PREPARE_KURENTO_SNAPSHOT=true
|
||||
;;
|
||||
|
||||
--use-specific-kurento-java-commit)
|
||||
USE_SPECIFIC_KURENTO_JAVA_COMMIT=true
|
||||
;;
|
||||
|
||||
--serve-openvidu-testapp)
|
||||
SERVE_OV_TESTAPP=true
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unrecognized method $1"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
else
|
||||
echo "Must provide a method to execute as first parameter when calling the script"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Prepare build
|
||||
# -------------
|
||||
if [[ "${PREPARE_TEST_ENVIRONMENT}" == true ]]; then
|
||||
|
||||
# Connect e2e test container to network bridge so it is vissible for browser and media server containers
|
||||
if [[ -n "${TEST_IMAGE}" ]]; then
|
||||
E2E_CONTAINER_ID="$(docker ps | grep "$TEST_IMAGE" | awk '{ print $1 }')" || echo "Docker container not found for image ${TEST_IMAGE}"
|
||||
if [[ -n "${E2E_CONTAINER_ID}" ]]; then
|
||||
docker network connect bridge "${E2E_CONTAINER_ID}"
|
||||
else
|
||||
echo "Could not connect test docker container to docker bridge, because no running container was found for image \"${TEST_IMAGE}\""
|
||||
fi
|
||||
else
|
||||
echo "No TEST_IMAGE env var provided. Skipping network bridge connection"
|
||||
fi
|
||||
|
||||
# Prepare directory for OpenVidu recordings
|
||||
sudo mkdir -p /opt/openvidu/recordings && sudo chmod 777 /opt/openvidu/recordings
|
||||
# Prepare directory for OpenVidu Android apps
|
||||
sudo mkdir -p /opt/openvidu/android && sudo chmod 777 /opt/openvidu/android
|
||||
|
||||
# Download fake videos
|
||||
FAKE_VIDEO1=/opt/openvidu/barcode.y4m
|
||||
FAKE_VIDEO2=/opt/openvidu/girl.mjpeg
|
||||
if [ ! -f ${FAKE_VIDEO1} ]; then
|
||||
sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/barcode.y4m --create-dirs --output /opt/openvidu/barcode.y4m
|
||||
else
|
||||
echo "File ${FAKE_VIDEO1} already exists"
|
||||
fi
|
||||
if [ ! -f ${FAKE_VIDEO2} ]; then
|
||||
sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/girl.mjpeg --create-dirs --output /opt/openvidu/girl.mjpeg
|
||||
else
|
||||
echo "File ${FAKE_VIDEO2} already exists"
|
||||
fi
|
||||
|
||||
# Download fake audios
|
||||
FAKE_AUDIO1=/opt/openvidu/fakeaudio.wav
|
||||
FAKE_AUDIO2=/opt/openvidu/stt-test.wav
|
||||
if [ ! -f ${FAKE_AUDIO1} ]; then
|
||||
sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/fakeaudio.wav --create-dirs --output /opt/openvidu/fakeaudio.wav
|
||||
else
|
||||
echo "File ${FAKE_AUDIO1} already exists"
|
||||
fi
|
||||
if [ ! -f ${FAKE_AUDIO2} ]; then
|
||||
sudo curl --location https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/stt-test.wav --create-dirs --output /opt/openvidu/stt-test.wav
|
||||
else
|
||||
echo "File ${FAKE_AUDIO2} already exists"
|
||||
fi
|
||||
|
||||
# Download recording custom layout
|
||||
sudo curl --location https://raw.githubusercontent.com/OpenVidu/openvidu/master/openvidu-test-e2e/docker/my-custom-layout/index.html --create-dirs --output /opt/openvidu/test-layouts/layout1/index.html
|
||||
|
||||
# Open permissions for /opt/openvidu folder
|
||||
sudo chmod -R 777 /opt/openvidu
|
||||
|
||||
# Pull browser images
|
||||
# Pull chrome image if env variable CHROME_VERSION is set
|
||||
if [[ -n "${CHROME_VERSION:-}" ]]; then
|
||||
docker pull selenium/standalone-chrome:"${CHROME_VERSION}"
|
||||
fi
|
||||
# Pull firefox image if env variable FIREFOX_VERSION is set
|
||||
if [[ -n "${FIREFOX_VERSION:-}" ]]; then
|
||||
docker pull selenium/standalone-firefox:"${FIREFOX_VERSION}"
|
||||
fi
|
||||
# Pull edge image if env variable EDGE_VERSION is set
|
||||
if [[ -n "${EDGE_VERSION:-}" ]]; then
|
||||
docker pull selenium/standalone-edge:"${EDGE_VERSION}"
|
||||
fi
|
||||
# Pull Docker Android image if env variable DOCKER_ANDROID_IMAGE is set
|
||||
if [[ -n "${DOCKER_ANDROID_IMAGE:-}" ]]; then
|
||||
docker pull "${DOCKER_ANDROID_IMAGE}"
|
||||
fi
|
||||
|
||||
# Pull mediasoup and kurento
|
||||
if [[ -n "${MEDIASOUP_CONTROLLER_VERSION:-}" ]]; then
|
||||
docker pull openvidu/mediasoup-controller:"${MEDIASOUP_CONTROLLER_VERSION}"
|
||||
fi
|
||||
if [[ -n "${KURENTO_MEDIA_SERVER_IMAGE:-}" ]]; then
|
||||
docker pull "${KURENTO_MEDIA_SERVER_IMAGE}"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Use a specific kurento-java commit other than the configured in openvidu-parent pom.xml
|
||||
# -------------
|
||||
if [[ "${USE_SPECIFIC_KURENTO_JAVA_COMMIT}" == true ]]; then
|
||||
|
||||
git clone https://github.com/Kurento/kurento.git
|
||||
pushd kurento/clients/java
|
||||
git checkout -f "${KURENTO_JAVA_COMMIT}"
|
||||
MVN_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
|
||||
mvn -B -Dmaven.artifact.threads=1 clean install
|
||||
popd
|
||||
rm -rf kurento
|
||||
mvn -B versions:set-property \
|
||||
-Dproperty=version.kurento \
|
||||
-DnewVersion="${MVN_VERSION}"
|
||||
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Serve openvidu-testapp
|
||||
# -------------
|
||||
if [[ "${SERVE_OV_TESTAPP}" == true ]]; then
|
||||
# Generate certificate
|
||||
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 \
|
||||
-subj "/CN=www.mydom.com/O=My Company LTD./C=US" \
|
||||
-keyout /opt/openvidu/testapp/key.pem \
|
||||
-out /opt/openvidu/testapp/cert.pem
|
||||
|
||||
# Serve TestApp
|
||||
pushd /opt/openvidu/testapp
|
||||
http-server -S -p 4200 &>/opt/openvidu/testapp.log &
|
||||
popd
|
||||
fi
|
|
@ -1,43 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<pluginGroups></pluginGroups>
|
||||
<proxies></proxies>
|
||||
<servers></servers>
|
||||
<mirrors></mirrors>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>default</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>kurento-github-public</id>
|
||||
<name>Kurento GitHub Maven packages (public access)</name>
|
||||
<url>KURENTO_SNAPSHOTS_URL</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>kurento-github-public</id>
|
||||
<name>Kurento GitHub Maven packages (public access)</name>
|
||||
<url>KURENTO_SNAPSHOTS_URL</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
</settings>
|
|
@ -1,241 +0,0 @@
|
|||
#!/bin/bash -x
|
||||
set -eu -o pipefail
|
||||
|
||||
############################################################################
|
||||
# Any function offered by this file that is not path agnostic assumes that #
|
||||
# the path is located where the first command of each function requires it #
|
||||
############################################################################
|
||||
|
||||
OV_INTEGRATION_TESTS=false
|
||||
OV_UNIT_TESTS=false
|
||||
OV_E2E_KURENTO=false
|
||||
OV_E2E_MEDIASOUP=false
|
||||
LAUNCH_OV_KURENTO=false
|
||||
LAUNCH_OV_MEDIASOUP=false
|
||||
|
||||
function environmentLaunch {
|
||||
local MEDIA_SERVER="$1"
|
||||
|
||||
# Get e2e container id
|
||||
local E2E_CONTAINER_ID
|
||||
E2E_CONTAINER_ID="$(docker ps | grep "$TEST_IMAGE" | awk '{ print $1 }')"
|
||||
|
||||
# Get e2e container IP so services running can be accessed by browser and media server containers
|
||||
local E2E_CONTAINER_IP
|
||||
E2E_CONTAINER_IP="$(docker inspect "$E2E_CONTAINER_ID" | awk '/bridge/,/IPAddress/' | grep IPAddress | cut -d'"' -f4)"
|
||||
|
||||
# Kurento and mediasoup needs to run as network host, so we need Docker host IP.
|
||||
local DOCKER_HOST_IP
|
||||
DOCKER_HOST_IP="$(docker inspect bridge --format '{{with index .IPAM.Config 0}}{{or .Gateway .Subnet}}{{end}}' | sed -r 's|\.0/[[:digit:]]+$|.1|')"
|
||||
|
||||
if [[ "${MEDIA_SERVER}" == "kurento" ]]; then
|
||||
docker run -e KMS_UID=$(id -u) --network=host --detach=true --volume=/opt/openvidu/recordings:/opt/openvidu/recordings "${KURENTO_MEDIA_SERVER_IMAGE}"
|
||||
while true; do
|
||||
RC="$(curl \
|
||||
--silent \
|
||||
--no-buffer \
|
||||
--write-out '%{http_code}' \
|
||||
--header "Connection: Upgrade" \
|
||||
--header "Upgrade: websocket" \
|
||||
--header "Host: ${DOCKER_HOST_IP}" \
|
||||
--header "Origin: ${DOCKER_HOST_IP}" \
|
||||
"http://${DOCKER_HOST_IP}:8888/kurento" || echo '')"
|
||||
|
||||
if [[ "$RC" == "500" ]]; then
|
||||
break
|
||||
else
|
||||
echo "Waiting for ${MEDIA_SERVER}..."
|
||||
sleep 1
|
||||
fi
|
||||
done
|
||||
elif [[ "${MEDIA_SERVER}" == "mediasoup" ]]; then
|
||||
LOG_DATE=$(printf '%(%Y-%m-%d-%H-%M-%S)T')
|
||||
docker run --network=host --restart=always \
|
||||
--env=KMS_MIN_PORT=40000 \
|
||||
--env=KMS_MAX_PORT=65535 \
|
||||
--env=OPENVIDU_PRO_LICENSE="${OPENVIDU_PRO_LICENSE}" \
|
||||
--env=OPENVIDU_PRO_LICENSE_API="${OPENVIDU_PRO_LICENSE_API}" \
|
||||
--env=WEBRTC_LISTENIPS_0_ANNOUNCEDIP="${DOCKER_HOST_IP}" \
|
||||
--env=WEBRTC_LISTENIPS_0_IP="${DOCKER_HOST_IP}" \
|
||||
--volume=/opt/openvidu/recordings:/opt/openvidu/recordings \
|
||||
openvidu/mediasoup-controller:"${MEDIASOUP_CONTROLLER_VERSION}" >& /opt/openvidu/mediasoup-controller-${LOG_DATE}.log &
|
||||
until $(curl --insecure --output /dev/null --silent http://${DOCKER_HOST_IP}:8888/kurento); do
|
||||
echo "Waiting for ${MEDIA_SERVER}..."
|
||||
sleep 1
|
||||
done
|
||||
else
|
||||
echo "Not valid media server"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${DOCKER_RECORDING_VERSION}" != "default" ]; then
|
||||
echo "Using custom openvidu-recording tag: ${DOCKER_RECORDING_VERSION}"
|
||||
java -jar -DKMS_URIS="[\"ws://${DOCKER_HOST_IP}:8888/kurento\"]" \
|
||||
-DDOMAIN_OR_PUBLIC_IP="${E2E_CONTAINER_IP}" \
|
||||
-DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true \
|
||||
-DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts \
|
||||
-DOPENVIDU_RECORDING_VERSION="${DOCKER_RECORDING_VERSION}" -DOPENVIDU_WEBHOOK=true \
|
||||
-DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook \
|
||||
/opt/openvidu/openvidu-server-*.jar &>/opt/openvidu/openvidu-server-"${MEDIA_SERVER}".log &
|
||||
else
|
||||
echo "Using default openvidu-recording tag"
|
||||
java -jar -DKMS_URIS="[\"ws://${DOCKER_HOST_IP}:8888/kurento\"]" \
|
||||
-DDOMAIN_OR_PUBLIC_IP="${E2E_CONTAINER_IP}" \
|
||||
-DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true \
|
||||
-DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts -DOPENVIDU_WEBHOOK=true \
|
||||
-DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook \
|
||||
/opt/openvidu/openvidu-server-*.jar &>/opt/openvidu/openvidu-server-"${MEDIA_SERVER}".log &
|
||||
fi
|
||||
until $(curl --insecure --output /dev/null --silent --head --fail https://OPENVIDUAPP:MY_SECRET@localhost:4443/); do
|
||||
echo "Waiting for openvidu-server..."
|
||||
sleep 2
|
||||
done
|
||||
}
|
||||
|
||||
function openviduE2ETests {
|
||||
local MEDIA_SERVER="$1"
|
||||
|
||||
# Get e2e container id
|
||||
local E2E_CONTAINER_ID
|
||||
E2E_CONTAINER_ID="$(docker ps | grep "$TEST_IMAGE" | awk '{ print $1 }')"
|
||||
|
||||
# Get e2e container IP so services running can be accessed by browser and media server containers
|
||||
local E2E_CONTAINER_IP
|
||||
E2E_CONTAINER_IP="$(docker inspect "$E2E_CONTAINER_ID" | awk '/bridge/,/IPAddress/' | grep IPAddress | cut -d'"' -f4)"
|
||||
|
||||
# Kurento and mediasoup needs to run as network host, so we need Docker host IP.
|
||||
local DOCKER_HOST_IP
|
||||
DOCKER_HOST_IP="$(docker network inspect bridge | grep Subnet | cut -d'"' -f4 | cut -d'/' -f1 | sed 's/.$/1/' | grep 172)"
|
||||
|
||||
pushd openvidu-test-e2e
|
||||
if [[ "${MEDIA_SERVER}" == "kurento" ]]; then
|
||||
|
||||
mvn -DMEDIA_SERVER_IMAGE="${KURENTO_MEDIA_SERVER_IMAGE}" \
|
||||
-DOPENVIDU_URL="https://${E2E_CONTAINER_IP}:4443" \
|
||||
-DCHROME_VERSION="${CHROME_VERSION}" \
|
||||
-DFIREFOX_VERSION="${FIREFOX_VERSION}" \
|
||||
-DEDGE_VERSION="${EDGE_VERSION}" \
|
||||
-Dtest=OpenViduTestAppE2eTest \
|
||||
-DAPP_URL="https://${E2E_CONTAINER_IP}:4200" \
|
||||
-DEXTERNAL_CUSTOM_LAYOUT_URL="http://${E2E_CONTAINER_IP}:4114" \
|
||||
-DREMOTE_URL_CHROME="http://${DOCKER_HOST_IP}:6666/wd/hub/" \
|
||||
-DREMOTE_URL_FIREFOX="http://${DOCKER_HOST_IP}:6667/wd/hub/" \
|
||||
-DREMOTE_URL_OPERA="http://${DOCKER_HOST_IP}:6668/wd/hub/" \
|
||||
-DREMOTE_URL_EDGE="http://${DOCKER_HOST_IP}:6669/wd/hub/" \
|
||||
-DEXTERNAL_CUSTOM_LAYOUT_PARAMS="sessionId,CUSTOM_LAYOUT_SESSION,secret,MY_SECRET" \
|
||||
test
|
||||
|
||||
elif [[ "${MEDIA_SERVER}" == "mediasoup" ]]; then
|
||||
|
||||
mvn -DMEDIA_SERVER_IMAGE="openvidu/mediasoup-controller:${MEDIASOUP_CONTROLLER_VERSION}" \
|
||||
-DOPENVIDU_URL="https://${E2E_CONTAINER_IP}:4443" \
|
||||
-DCHROME_VERSION="${CHROME_VERSION}" \
|
||||
-DFIREFOX_VERSION="${FIREFOX_VERSION}" \
|
||||
-DEDGE_VERSION="${EDGE_VERSION}" \
|
||||
-Dtest=OpenViduTestAppE2eTest \
|
||||
-DAPP_URL="https://${E2E_CONTAINER_IP}:4200" \
|
||||
-DEXTERNAL_CUSTOM_LAYOUT_URL="http://${E2E_CONTAINER_IP}:4114" \
|
||||
-DREMOTE_URL_CHROME="http://${DOCKER_HOST_IP}:6666/wd/hub/" \
|
||||
-DREMOTE_URL_FIREFOX="http://${DOCKER_HOST_IP}:6667/wd/hub/" \
|
||||
-DREMOTE_URL_OPERA="http://${DOCKER_HOST_IP}:6668/wd/hub/" \
|
||||
-DREMOTE_URL_EDGE="http://${DOCKER_HOST_IP}:6669/wd/hub/" \
|
||||
-DEXTERNAL_CUSTOM_LAYOUT_PARAMS="sessionId,CUSTOM_LAYOUT_SESSION,secret,MY_SECRET" \
|
||||
-DOPENVIDU_PRO_LICENSE="${OPENVIDU_PRO_LICENSE}" \
|
||||
-DOPENVIDU_PRO_LICENSE_API="${OPENVIDU_PRO_LICENSE_API}" \
|
||||
test
|
||||
|
||||
else
|
||||
echo "Not valid media server"
|
||||
exit 1
|
||||
fi
|
||||
stopMediaServer
|
||||
kill -9 $(pgrep -f /opt/openvidu/openvidu-server) || true
|
||||
popd
|
||||
}
|
||||
|
||||
function stopMediaServer {
|
||||
# Remove Kurento Media Server
|
||||
declare -a arr=("kurento/kurento-media-server"
|
||||
"openvidu/mediasoup-controller:")
|
||||
for image in "${arr[@]}"; do
|
||||
docker ps -a | awk '{ print $1,$2 }' | grep "${image}" | awk '{ print $1 }' | xargs -I {} docker rm -f {} || true
|
||||
done
|
||||
docker ps -a
|
||||
}
|
||||
|
||||
# Environment variables
|
||||
if [[ -n ${1:-} ]]; then
|
||||
case "${1:-}" in
|
||||
--openvidu-server-unit-tests)
|
||||
OV_UNIT_TESTS=true
|
||||
;;
|
||||
--openvidu-server-integration-tests)
|
||||
OV_INTEGRATION_TESTS=true
|
||||
;;
|
||||
--openvidu-e2e-tests-kurento)
|
||||
OV_E2E_KURENTO=true
|
||||
;;
|
||||
--openvidu-e2e-tests-mediasoup)
|
||||
OV_E2E_MEDIASOUP=true
|
||||
;;
|
||||
--environment-launch-kurento)
|
||||
LAUNCH_OV_KURENTO=true
|
||||
;;
|
||||
--environment-launch-mediasoup)
|
||||
LAUNCH_OV_MEDIASOUP=true
|
||||
;;
|
||||
*)
|
||||
echo "Unrecognized method $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
echo "Must provide a method to execute as first parameter when calling the script"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# openvidu-server unit tests
|
||||
# -------------
|
||||
if [[ "${OV_UNIT_TESTS}" == true ]]; then
|
||||
pushd openvidu-server
|
||||
mvn -B -Dtest=io.openvidu.server.test.unit.*Test test
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# openvidu-server integration tests
|
||||
# -------------
|
||||
if [[ "${OV_INTEGRATION_TESTS}" == true ]]; then
|
||||
pushd openvidu-server
|
||||
mvn -B -Dtest=io.openvidu.server.test.integration.*Test test
|
||||
popd
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# OpenVidu E2E Tests Kurento
|
||||
# -------------
|
||||
if [[ "${OV_E2E_KURENTO}" == true ]]; then
|
||||
openviduE2ETests "kurento"
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# OpenVidu E2E Tests mediasoup
|
||||
# -------------
|
||||
if [[ "${OV_E2E_MEDIASOUP}" == true ]]; then
|
||||
openviduE2ETests "mediasoup"
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Environment launch Kurento
|
||||
# -------------
|
||||
if [[ "${LAUNCH_OV_KURENTO}" == true ]]; then
|
||||
environmentLaunch "kurento"
|
||||
fi
|
||||
|
||||
# -------------
|
||||
# Environment launch mediasoup
|
||||
# -------------
|
||||
if [[ "${LAUNCH_OV_MEDIASOUP}" == true ]]; then
|
||||
environmentLaunch "mediasoup"
|
||||
fi
|
|
@ -1,62 +0,0 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
docs/
|
||||
lib/
|
||||
static/**
|
|
@ -1 +0,0 @@
|
|||
docs/
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 140,
|
||||
"trailingComma": "none",
|
||||
"semi": true,
|
||||
"bracketSpacing": true,
|
||||
"useTabs": false,
|
||||
"jsxSingleQuote": true,
|
||||
"tabWidth": 4
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
SEARCH_STRING_1="<K extends keyof SessionEventMap>(type: K,"
|
||||
REPLACE_STRING_1="<K extends keyof SessionEventMap>(type: K | string,"
|
||||
SEARCH_STRING_2='\[key: `signal:\${string}`\]: SignalEvent;'
|
||||
sed -i "s~${SEARCH_STRING_1}~${REPLACE_STRING_1}~g" ts4.4/lib/OpenVidu/Session.d.ts
|
||||
sed -i "/${SEARCH_STRING_2}/d" ts4.4/lib/OpenViduInternal/Events/EventMap/SessionEventMap.d.ts
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"include": ["../src"],
|
||||
"exclude": ["../config", "../docs", "../lib", "../node_modules", "../ts4.4"],
|
||||
"typedocOptions": {
|
||||
"name": "OpenVidu Browser",
|
||||
"entryPoints": ["../src/index.ts"],
|
||||
"out": "../docs",
|
||||
"theme": "default",
|
||||
"readme": "none",
|
||||
"includeVersion": true,
|
||||
"validation": {
|
||||
"notExported": true,
|
||||
"invalidLink": true
|
||||
},
|
||||
"excludeExternals": true,
|
||||
"excludePrivate": true,
|
||||
"excludeProtected": true,
|
||||
"excludeInternal": true
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"array-type": [
|
||||
true,
|
||||
"array"
|
||||
],
|
||||
"ban-types": {
|
||||
"options": [
|
||||
[
|
||||
"Object",
|
||||
"Avoid using the `Object` type. Did you mean `object`?"
|
||||
],
|
||||
[
|
||||
"Function",
|
||||
"Avoid using the `Function` type. Prefer a specific function type, like `() => void`, or use `ts.AnyFunction`."
|
||||
],
|
||||
[
|
||||
"Boolean",
|
||||
"Avoid using the `Boolean` type. Did you mean `boolean`?"
|
||||
],
|
||||
[
|
||||
"Number",
|
||||
"Avoid using the `Number` type. Did you mean `number`?"
|
||||
],
|
||||
[
|
||||
"String",
|
||||
"Avoid using the `String` type. Did you mean `string`?"
|
||||
]
|
||||
]
|
||||
},
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space"
|
||||
],
|
||||
"curly": [
|
||||
true,
|
||||
"ignore-same-line"
|
||||
],
|
||||
"indent": [
|
||||
true,
|
||||
"spaces",
|
||||
2
|
||||
],
|
||||
"interface-name": [
|
||||
true,
|
||||
"never-prefix"
|
||||
],
|
||||
"interface-over-type-literal": true,
|
||||
"jsdoc-format": true,
|
||||
"no-inferrable-types": true,
|
||||
"no-internal-module": true,
|
||||
"no-null-keyword": false,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-trailing-whitespace": [
|
||||
true,
|
||||
"ignore-template-strings"
|
||||
],
|
||||
"no-var-keyword": true,
|
||||
"object-literal-shorthand": true,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-whitespace"
|
||||
],
|
||||
"prefer-const": true,
|
||||
"quotemark": [
|
||||
true,
|
||||
"single",
|
||||
"avoid-escape",
|
||||
"avoid-template"
|
||||
],
|
||||
"semicolon": [
|
||||
true,
|
||||
"always",
|
||||
"ignore-bound-class-methods"
|
||||
],
|
||||
"space-within-parens": true,
|
||||
"triple-equals": true,
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
],
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-module",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
],
|
||||
"no-implicit-dependencies": [
|
||||
true,
|
||||
"dev"
|
||||
],
|
||||
"object-literal-key-quotes": [
|
||||
true,
|
||||
"consistent-as-needed"
|
||||
],
|
||||
"variable-name": [
|
||||
true,
|
||||
"ban-keywords",
|
||||
"check-format",
|
||||
"allow-leading-underscore"
|
||||
],
|
||||
"arrow-parens": false,
|
||||
"arrow-return-shorthand": false,
|
||||
"forin": false,
|
||||
"member-access": false,
|
||||
"no-conditional-assignment": false,
|
||||
"no-console": false,
|
||||
"no-debugger": false,
|
||||
"no-empty-interface": false,
|
||||
"no-eval": false,
|
||||
"no-object-literal-type-assertion": false,
|
||||
"no-shadowed-variable": false,
|
||||
"no-submodule-imports": false,
|
||||
"no-var-requires": false,
|
||||
"ordered-imports": false,
|
||||
"prefer-conditional-expression": false,
|
||||
"radix": false,
|
||||
"trailing-comma": false,
|
||||
"align": false,
|
||||
"eofline": false,
|
||||
"max-line-length": false,
|
||||
"no-consecutive-blank-lines": false,
|
||||
"space-before-function-paren": false,
|
||||
"ban-comma-operator": false,
|
||||
"max-classes-per-file": false,
|
||||
"member-ordering": false,
|
||||
"no-angle-bracket-type-assertion": false,
|
||||
"no-bitwise": false,
|
||||
"no-namespace": false,
|
||||
"no-reference": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-variable-per-declaration": false,
|
||||
"unified-signatures": false
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ -z "$BASEHREF_VERSION" ]]; then
|
||||
echo "Example of use: \"BASEHREF_VERSION=2.12.0 ${0}\"" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Replace version from "stable" to the specified one in all TypeDoc links
|
||||
grep -rl '/en/stable/' src | xargs sed -i -e 's|/en/stable/|/en/'${BASEHREF_VERSION}'/|g'
|
||||
|
||||
# Generate TypeDoc
|
||||
./node_modules/typedoc/bin/typedoc --tsconfig ./config/tsconfig.json
|
||||
|
||||
# Return links to "stable" version
|
||||
grep -rl '/en/'${BASEHREF_VERSION}'/' src | xargs sed -i -e 's|/en/'${BASEHREF_VERSION}'/|/en/stable/|g'
|
||||
|
||||
# Clean previous docs from openvidu.io-docs repo and copy new ones
|
||||
rm -rf ../../openvidu.io-docs/docs/api/openvidu-browser/*
|
||||
cp -R ./docs/. ../../openvidu.io-docs/docs/api/openvidu-browser
|
File diff suppressed because it is too large
Load Diff
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"author": "OpenVidu",
|
||||
"dependencies": {
|
||||
"events": "3.3.0",
|
||||
"freeice": "2.2.2",
|
||||
"hark": "1.2.3",
|
||||
"inherits": "2.0.4",
|
||||
"jsnlog": "2.30.0",
|
||||
"mime": "3.0.0",
|
||||
"platform": "1.3.6",
|
||||
"semver": "7.6.2",
|
||||
"uuid": "9.0.1",
|
||||
"wolfy87-eventemitter": "5.2.9"
|
||||
},
|
||||
"description": "OpenVidu Browser",
|
||||
"devDependencies": {
|
||||
"@types/node": "18.11.9",
|
||||
"@types/platform": "1.3.4",
|
||||
"browserify": "17.0.0",
|
||||
"terser": "5.15.1",
|
||||
"tsify": "5.0.4",
|
||||
"tslint": "6.1.3",
|
||||
"typedoc": "0.23.21",
|
||||
"typescript": "4.9.3"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"main": "lib/index.js",
|
||||
"name": "openvidu-browser",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/OpenVidu/openvidu"
|
||||
},
|
||||
"scripts": {
|
||||
"browserify": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../static/js/openvidu-browser-$VERSION.js -v",
|
||||
"browserify-prod": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js --debug Main.ts -p [ tsify ] --exclude kurento-browser-extensions | ../node_modules/terser/bin/terser --source-map content=inline --output ../static/js/openvidu-browser-$VERSION.min.js",
|
||||
"build": "cd src/OpenVidu && ./../../node_modules/typescript/bin/tsc && cd ../.. && ./node_modules/typescript/bin/tsc --declaration src/index.ts --outDir ./lib --sourceMap --target es5 --lib dom,es5,es2015.promise,scripthost && rm -rf ./ts4.4 && mkdir -p ./ts4.4/lib && cp -r ./lib ./ts4.4 && find ./ts4.4/lib -type f ! -iname '*.d.ts' -delete && ./config/replace_for_ts44.sh",
|
||||
"docs": "./generate-docs.sh"
|
||||
},
|
||||
"types": "lib/index.d.ts",
|
||||
"typesVersions": {
|
||||
"<4.4": {
|
||||
"*": [
|
||||
"ts4.4/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": "2.30.0"
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import { OpenVidu } from './OpenVidu/OpenVidu';
|
||||
import { JL } from 'jsnlog';
|
||||
|
||||
if (typeof globalThis !== 'undefined') {
|
||||
globalThis['OpenVidu'] = OpenVidu;
|
||||
}
|
||||
|
||||
// Disable jsnlog when library is loaded
|
||||
JL.setOptions({ enabled: false });
|
|
@ -1,217 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Session } from './Session';
|
||||
import { Stream } from './Stream';
|
||||
import { LocalConnectionOptions } from '../OpenViduInternal/Interfaces/Private/LocalConnectionOptions';
|
||||
import { RemoteConnectionOptions } from '../OpenViduInternal/Interfaces/Private/RemoteConnectionOptions';
|
||||
import { InboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/InboundStreamOptions';
|
||||
import { StreamOptionsServer } from '../OpenViduInternal/Interfaces/Private/StreamOptionsServer';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
import { ExceptionEvent, ExceptionEventName } from '../OpenViduInternal/Events/ExceptionEvent';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* Represents each one of the user's connection to the session (the local one and other user's connections).
|
||||
* Therefore each {@link Session} and {@link Stream} object has an attribute of type Connection
|
||||
*/
|
||||
export class Connection {
|
||||
/**
|
||||
* Unique identifier of the connection
|
||||
*/
|
||||
connectionId: string;
|
||||
|
||||
/**
|
||||
* Time when this connection was created in OpenVidu Server (UTC milliseconds)
|
||||
*/
|
||||
creationTime: number;
|
||||
|
||||
/**
|
||||
* Data associated to this connection (and therefore to certain user). This is an important field:
|
||||
* it allows you to broadcast all the information you want for each user (a username, for example)
|
||||
*/
|
||||
data: string;
|
||||
|
||||
/**
|
||||
* Role of the connection.
|
||||
* - `SUBSCRIBER`: can subscribe to published Streams of other users by calling {@link Session.subscribe}
|
||||
* - `PUBLISHER`: SUBSCRIBER permissions + can publish their own Streams by calling {@link Session.publish}
|
||||
* - `MODERATOR`: SUBSCRIBER + PUBLISHER permissions + can force the unpublishing or disconnection over a third-party Stream or Connection by call {@link Session.forceUnpublish} and {@link Session.forceDisconnect}
|
||||
*
|
||||
* **Only defined for the local connection. In remote connections will be `undefined`**
|
||||
*/
|
||||
role: string;
|
||||
|
||||
/**
|
||||
* Whether the streams published by this Connection will be recorded or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording/#individual-recording-selection) <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
|
||||
*
|
||||
* **Only defined for the local connection. In remote connections will be `undefined`**
|
||||
*/
|
||||
record: boolean;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
stream?: Stream;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
localOptions: LocalConnectionOptions | undefined;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
remoteOptions: RemoteConnectionOptions | undefined;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
disposed = false;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
rpcSessionId: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(private session: Session, connectionOptions: LocalConnectionOptions | RemoteConnectionOptions) {
|
||||
let msg = "'Connection' created ";
|
||||
if (!!(<LocalConnectionOptions>connectionOptions).role) {
|
||||
// Connection is local
|
||||
this.localOptions = <LocalConnectionOptions>connectionOptions;
|
||||
this.connectionId = this.localOptions.id;
|
||||
this.creationTime = this.localOptions.createdAt;
|
||||
this.data = this.localOptions.metadata;
|
||||
this.rpcSessionId = this.localOptions.sessionId;
|
||||
this.role = this.localOptions.role;
|
||||
this.record = this.localOptions.record;
|
||||
msg += '(local)';
|
||||
} else {
|
||||
// Connection is remote
|
||||
this.remoteOptions = <RemoteConnectionOptions>connectionOptions;
|
||||
this.connectionId = this.remoteOptions.id;
|
||||
this.creationTime = this.remoteOptions.createdAt;
|
||||
if (this.remoteOptions.metadata) {
|
||||
this.data = this.remoteOptions.metadata;
|
||||
}
|
||||
if (this.remoteOptions.streams) {
|
||||
this.initRemoteStreams(this.remoteOptions.streams);
|
||||
}
|
||||
msg += "(remote) with 'connectionId' [" + this.remoteOptions.id + ']';
|
||||
}
|
||||
logger.info(msg);
|
||||
}
|
||||
|
||||
/* Hidden methods */
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
sendIceCandidate(candidate: RTCIceCandidate): void {
|
||||
|
||||
if (!this.disposed) {
|
||||
logger.debug((!!this.stream!.outboundStreamOpts ? 'Local' : 'Remote') + 'candidate for' + this.connectionId, candidate);
|
||||
|
||||
this.session.openvidu.sendRequest(
|
||||
'onIceCandidate',
|
||||
{
|
||||
endpointName: this.connectionId,
|
||||
candidate: candidate.candidate,
|
||||
sdpMid: candidate.sdpMid,
|
||||
sdpMLineIndex: candidate.sdpMLineIndex
|
||||
},
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error('Error sending ICE candidate: ' + JSON.stringify(error));
|
||||
this.session.emitEvent('exception', [
|
||||
new ExceptionEvent(
|
||||
this.session,
|
||||
ExceptionEventName.ICE_CANDIDATE_ERROR,
|
||||
this.session,
|
||||
'There was an unexpected error on the server-side processing an ICE candidate generated and sent by the client-side',
|
||||
error
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
logger.warn(`Connection ${this.connectionId} disposed when trying to send an ICE candidate. ICE candidate not sent`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initRemoteStreams(options: StreamOptionsServer[]): void {
|
||||
// This is ready for supporting multiple streams per Connection object. Right now the loop will always run just once
|
||||
// this.stream should also be replaced by a collection of streams to support multiple streams per Connection
|
||||
options.forEach((opts) => {
|
||||
const streamOptions: InboundStreamOptions = {
|
||||
id: opts.id,
|
||||
createdAt: opts.createdAt,
|
||||
connection: this,
|
||||
hasAudio: opts.hasAudio,
|
||||
hasVideo: opts.hasVideo,
|
||||
audioActive: opts.audioActive,
|
||||
videoActive: opts.videoActive,
|
||||
typeOfVideo: opts.typeOfVideo,
|
||||
frameRate: opts.frameRate,
|
||||
videoDimensions: !!opts.videoDimensions ? JSON.parse(opts.videoDimensions) : undefined,
|
||||
filter: !!opts.filter ? opts.filter : undefined
|
||||
};
|
||||
const stream = new Stream(this.session, streamOptions);
|
||||
|
||||
this.addStream(stream);
|
||||
});
|
||||
|
||||
logger.info(
|
||||
"Remote 'Connection' with 'connectionId' [" + this.connectionId + '] is now configured for receiving Streams with options: ',
|
||||
this.stream!.inboundStreamOpts
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
addStream(stream: Stream): void {
|
||||
stream.connection = this;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
removeStream(): void {
|
||||
delete this.stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
dispose(): void {
|
||||
this.disposed = true;
|
||||
this.removeStream();
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from '../OpenViduInternal/Events/Event';
|
||||
import { EventMap } from '../OpenViduInternal/Events/EventMap/EventMap';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
|
||||
import EventEmitter = require('wolfy87-eventemitter');
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
export abstract class EventDispatcher {
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
userHandlerArrowHandler: WeakMap<(event: Event) => void, (event: Event) => void> = new WeakMap();
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
ee = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Adds function `handler` to handle event `type`
|
||||
*
|
||||
* @returns The EventDispatcher object
|
||||
*/
|
||||
abstract on<K extends keyof EventMap>(type: K, handler: (event: EventMap[K]) => void): this;
|
||||
|
||||
/**
|
||||
* Adds function `handler` to handle event `type` just once. The handler will be automatically removed after first execution
|
||||
*
|
||||
* @returns The object that dispatched the event
|
||||
*/
|
||||
abstract once<K extends keyof EventMap>(type: K, handler: (event: EventMap[K]) => void): this;
|
||||
|
||||
/**
|
||||
* Removes a `handler` from event `type`. If no handler is provided, all handlers will be removed from the event
|
||||
*
|
||||
* @returns The object that dispatched the event
|
||||
*/
|
||||
abstract off<K extends keyof EventMap>(type: K, handler?: (event: EventMap[K]) => void): this;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
onAux(type: string, message: string, handler: (event: Event) => void): EventDispatcher {
|
||||
const arrowHandler = (event) => {
|
||||
if (event) {
|
||||
logger.debug(message, event);
|
||||
} else {
|
||||
logger.debug(message);
|
||||
}
|
||||
handler(event);
|
||||
};
|
||||
this.userHandlerArrowHandler.set(handler, arrowHandler);
|
||||
this.ee.on(type, arrowHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
onceAux(type: string, message: string, handler: (event: Event) => void): EventDispatcher {
|
||||
const arrowHandler = (event) => {
|
||||
if (event) {
|
||||
logger.debug(message, event);
|
||||
} else {
|
||||
logger.debug(message);
|
||||
}
|
||||
handler(event);
|
||||
// Remove handler from map after first and only execution
|
||||
this.userHandlerArrowHandler.delete(handler);
|
||||
};
|
||||
this.userHandlerArrowHandler.set(handler, arrowHandler);
|
||||
this.ee.once(type, arrowHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
offAux(type: string, handler?: (event: Event) => void): EventDispatcher {
|
||||
if (!handler) {
|
||||
this.ee.removeAllListeners(type);
|
||||
} else {
|
||||
// Must remove internal arrow function handler paired with user handler
|
||||
const arrowHandler = this.userHandlerArrowHandler.get(handler);
|
||||
if (!!arrowHandler) {
|
||||
this.ee.off(type, arrowHandler);
|
||||
}
|
||||
this.userHandlerArrowHandler.delete(handler);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,285 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Stream } from './Stream';
|
||||
import { FilterEvent } from '../OpenViduInternal/Events/FilterEvent';
|
||||
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
|
||||
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* **WARNING**: experimental option. This interface may change in the near future
|
||||
*
|
||||
* Video/audio filter applied to a Stream. See {@link Stream.applyFilter}
|
||||
*/
|
||||
export class Filter {
|
||||
/**
|
||||
* Type of filter applied. This is the name of the remote class identifying the filter to apply in Kurento Media Server.
|
||||
* For example: `"FaceOverlayFilter"`, `"GStreamerFilter"`.
|
||||
*
|
||||
* You can get this property in `*.kmd.json` files defining the Kurento filters. For example, for GStreamerFilter that's
|
||||
* [here](https://github.com/Kurento/kms-filters/blob/53a452fac71d61795952e3d2202156c6b00f6d65/src/server/interface/filters.GStreamerFilter.kmd.json#L4)
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* Parameters used to initialize the filter.
|
||||
* These correspond to the constructor parameters used in the filter in Kurento Media Server (except for `mediaPipeline` parameter, which is never needed).
|
||||
*
|
||||
* For example: for `filter.type = "GStreamerFilter"` could be `filter.options = {"command": "videobalance saturation=0.0"}`
|
||||
*
|
||||
* You can get this property in `*.kmd.json` files defining the Kurento filters. For example, for GStreamerFilter that's
|
||||
* [here](https://github.com/Kurento/kms-filters/blob/53a452fac71d61795952e3d2202156c6b00f6d65/src/server/interface/filters.GStreamerFilter.kmd.json#L13-L31)
|
||||
*/
|
||||
options: Object;
|
||||
|
||||
/**
|
||||
* Value passed the last time {@link Filter.execMethod} was called. If `undefined` this method has not been called yet.
|
||||
*
|
||||
* You can use this value to know the current status of any applied filter
|
||||
*/
|
||||
lastExecMethod?: {
|
||||
method: string;
|
||||
params: Object;
|
||||
};
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
handlers: Map<string, (event: FilterEvent) => void> = new Map();
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
stream: Stream;
|
||||
private logger: OpenViduLogger;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(type: string, options: Object) {
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a filter method. Available methods are specific for each filter
|
||||
*
|
||||
* @param method Name of the method
|
||||
* @param params Parameters of the method
|
||||
*/
|
||||
execMethod(method: string, params: Object): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.info('Executing filter method to stream ' + this.stream.streamId);
|
||||
|
||||
let finalParams;
|
||||
|
||||
const successExecMethod = (triggerEvent) => {
|
||||
logger.info('Filter method successfully executed on Stream ' + this.stream.streamId);
|
||||
const oldValue = (<any>Object).assign({}, this.stream.filter);
|
||||
this.stream.filter!.lastExecMethod = { method, params: finalParams };
|
||||
if (triggerEvent) {
|
||||
this.stream.session.emitEvent('streamPropertyChanged', [
|
||||
new StreamPropertyChangedEvent(
|
||||
this.stream.session,
|
||||
this.stream,
|
||||
'filter',
|
||||
this.stream.filter!,
|
||||
oldValue,
|
||||
'execFilterMethod'
|
||||
)
|
||||
]);
|
||||
this.stream.streamManager.emitEvent('streamPropertyChanged', [
|
||||
new StreamPropertyChangedEvent(
|
||||
this.stream.streamManager,
|
||||
this.stream,
|
||||
'filter',
|
||||
this.stream.filter!,
|
||||
oldValue,
|
||||
'execFilterMethod'
|
||||
)
|
||||
]);
|
||||
}
|
||||
return resolve();
|
||||
};
|
||||
|
||||
if (this.type.startsWith('VB:')) {
|
||||
if (typeof params === 'string') {
|
||||
try {
|
||||
params = JSON.parse(params);
|
||||
} catch (error) {
|
||||
return reject(new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'Wrong params syntax: ' + error));
|
||||
}
|
||||
}
|
||||
|
||||
finalParams = params;
|
||||
|
||||
if (method === 'update') {
|
||||
if (!this.stream.virtualBackgroundSinkElements?.VB) {
|
||||
return reject(
|
||||
new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, 'There is no Virtual Background filter applied')
|
||||
);
|
||||
} else {
|
||||
this.stream.virtualBackgroundSinkElements.VB.updateValues(params)
|
||||
.then(() => successExecMethod(false))
|
||||
.catch((error) => {
|
||||
if (error.name === OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR) {
|
||||
return reject(new OpenViduError(error.name, error.message));
|
||||
} else {
|
||||
return reject(
|
||||
new OpenViduError(
|
||||
OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR,
|
||||
'Error updating values on Virtual Background filter: ' + error
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return reject(
|
||||
new OpenViduError(OpenViduErrorName.VIRTUAL_BACKGROUND_ERROR, `Unknown Virtual Background method "${method}"`)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let stringParams;
|
||||
if (typeof params !== 'string') {
|
||||
try {
|
||||
stringParams = JSON.stringify(params);
|
||||
} catch (error) {
|
||||
const errorMsg = "'params' property must be a JSON formatted object";
|
||||
logger.error(errorMsg);
|
||||
return reject(errorMsg);
|
||||
}
|
||||
} else {
|
||||
stringParams = <string>params;
|
||||
}
|
||||
|
||||
finalParams = stringParams;
|
||||
|
||||
this.stream.session.openvidu.sendRequest(
|
||||
'execFilterMethod',
|
||||
{ streamId: this.stream.streamId, method, params: stringParams },
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error('Error executing filter method for Stream ' + this.stream.streamId, error);
|
||||
if (error.code === 401) {
|
||||
return reject(
|
||||
new OpenViduError(
|
||||
OpenViduErrorName.OPENVIDU_PERMISSION_DENIED,
|
||||
"You don't have permissions to execute a filter method"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return reject(error);
|
||||
}
|
||||
} else {
|
||||
return successExecMethod(true);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to certain filter event. Available events are specific for each filter
|
||||
*
|
||||
* @param eventType Event to which subscribe to.
|
||||
* @param handler Function to execute upon event dispatched. It receives as parameter a {@link FilterEvent} object
|
||||
*
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully attached to the filter and rejected with an Error object if not
|
||||
*/
|
||||
addEventListener(eventType: string, handler: (event: FilterEvent) => void): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.info('Adding filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId);
|
||||
this.stream.session.openvidu.sendRequest(
|
||||
'addFilterEventListener',
|
||||
{ streamId: this.stream.streamId, eventType },
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
'Error adding filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId,
|
||||
error
|
||||
);
|
||||
if (error.code === 401) {
|
||||
return reject(
|
||||
new OpenViduError(
|
||||
OpenViduErrorName.OPENVIDU_PERMISSION_DENIED,
|
||||
"You don't have permissions to add a filter event listener"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return reject(error);
|
||||
}
|
||||
} else {
|
||||
this.handlers.set(eventType, handler);
|
||||
logger.info(
|
||||
'Filter event listener to event ' + eventType + ' successfully applied on Stream ' + this.stream.streamId
|
||||
);
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes certain filter event listener previously set.
|
||||
*
|
||||
* @param eventType Event to unsubscribe from.
|
||||
*
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully removed from the filter and rejected with an Error object in other case
|
||||
*/
|
||||
removeEventListener(eventType: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.info('Removing filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId);
|
||||
this.stream.session.openvidu.sendRequest(
|
||||
'removeFilterEventListener',
|
||||
{ streamId: this.stream.streamId, eventType },
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
'Error removing filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId,
|
||||
error
|
||||
);
|
||||
if (error.code === 401) {
|
||||
return reject(
|
||||
new OpenViduError(
|
||||
OpenViduErrorName.OPENVIDU_PERMISSION_DENIED,
|
||||
"You don't have permissions to add a filter event listener"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return reject(error);
|
||||
}
|
||||
} else {
|
||||
this.handlers.delete(eventType);
|
||||
logger.info(
|
||||
'Filter event listener to event ' + eventType + ' successfully removed on Stream ' + this.stream.streamId
|
||||
);
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,416 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Stream } from './Stream';
|
||||
import { LocalRecorderState } from '../OpenViduInternal/Enums/LocalRecorderState';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
|
||||
import Mime = require('mime/lite');
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
/**
|
||||
* Easy recording of {@link Stream} objects straightaway from the browser. Initialized with {@link OpenVidu.initLocalRecorder} method
|
||||
*/
|
||||
export class LocalRecorder {
|
||||
state: LocalRecorderState;
|
||||
|
||||
private connectionId: string;
|
||||
private mediaRecorder: MediaRecorder;
|
||||
private chunks: any[] = [];
|
||||
private blob?: Blob;
|
||||
private id: string;
|
||||
private videoPreviewSrc: string;
|
||||
private videoPreview: HTMLVideoElement;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(private stream: Stream) {
|
||||
platform = PlatformUtils.getInstance();
|
||||
this.connectionId = !!this.stream.connection ? this.stream.connection.connectionId : 'default-connection';
|
||||
this.id = this.stream.streamId + '_' + this.connectionId + '_localrecord';
|
||||
this.state = LocalRecorderState.READY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the recording of the Stream. {@link state} property must be `READY`. After method succeeds is set to `RECORDING`
|
||||
*
|
||||
* @param options The [MediaRecorder.options](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#parameters) to be used to record this Stream.
|
||||
* For example:
|
||||
*
|
||||
* ```javascript
|
||||
* var OV = new OpenVidu();
|
||||
* var publisher = await OV.initPublisherAsync();
|
||||
* var localRecorder = OV.initLocalRecorder(publisher.stream);
|
||||
* var options = {
|
||||
* mimeType: 'video/webm;codecs=vp8',
|
||||
* audioBitsPerSecond:128000,
|
||||
* videoBitsPerSecond:2500000
|
||||
* };
|
||||
* localRecorder.record(options);
|
||||
* ```
|
||||
*
|
||||
* If not specified, the default options preferred by the platform will be used.
|
||||
*
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording successfully started and rejected with an Error object if not
|
||||
*/
|
||||
record(options?: any): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (typeof options === 'string' || options instanceof String) {
|
||||
return reject(
|
||||
`When calling LocalRecorder.record(options) parameter 'options' cannot be a string. Must be an object like { mimeType: "${options}" }`
|
||||
);
|
||||
}
|
||||
if (typeof MediaRecorder === 'undefined') {
|
||||
logger.error(
|
||||
'MediaRecorder not supported on your device. See compatibility in https://caniuse.com/#search=MediaRecorder'
|
||||
);
|
||||
throw Error(
|
||||
'MediaRecorder not supported on your device. See compatibility in https://caniuse.com/#search=MediaRecorder'
|
||||
);
|
||||
}
|
||||
if (this.state !== LocalRecorderState.READY) {
|
||||
throw Error(
|
||||
"'LocalRecord.record()' needs 'LocalRecord.state' to be 'READY' (current value: '" +
|
||||
this.state +
|
||||
"'). Call 'LocalRecorder.clean()' or init a new LocalRecorder before"
|
||||
);
|
||||
}
|
||||
logger.log("Starting local recording of stream '" + this.stream.streamId + "' of connection '" + this.connectionId + "'");
|
||||
|
||||
if (!options) {
|
||||
options = { mimeType: 'video/webm' };
|
||||
} else if (!options.mimeType) {
|
||||
options.mimeType = 'video/webm';
|
||||
}
|
||||
|
||||
this.mediaRecorder = new MediaRecorder(this.stream.getMediaStream(), options);
|
||||
this.mediaRecorder.start();
|
||||
} catch (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
this.mediaRecorder.ondataavailable = (e) => {
|
||||
if (e.data.size > 0) {
|
||||
this.chunks.push(e.data);
|
||||
}
|
||||
};
|
||||
|
||||
this.mediaRecorder.onerror = (e) => {
|
||||
logger.error('MediaRecorder error: ', e);
|
||||
};
|
||||
|
||||
this.mediaRecorder.onstart = () => {
|
||||
logger.log('MediaRecorder started (state=' + this.mediaRecorder.state + ')');
|
||||
};
|
||||
|
||||
this.mediaRecorder.onstop = () => {
|
||||
this.onStopDefault();
|
||||
};
|
||||
|
||||
this.mediaRecorder.onpause = () => {
|
||||
logger.log('MediaRecorder paused (state=' + this.mediaRecorder.state + ')');
|
||||
};
|
||||
|
||||
this.mediaRecorder.onresume = () => {
|
||||
logger.log('MediaRecorder resumed (state=' + this.mediaRecorder.state + ')');
|
||||
};
|
||||
|
||||
this.state = LocalRecorderState.RECORDING;
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the recording of the Stream. {@link state} property must be `RECORDING` or `PAUSED`. After method succeeds is set to `FINISHED`
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording successfully stopped and rejected with an Error object if not
|
||||
*/
|
||||
stop(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (this.state === LocalRecorderState.READY || this.state === LocalRecorderState.FINISHED) {
|
||||
throw Error(
|
||||
"'LocalRecord.stop()' needs 'LocalRecord.state' to be 'RECORDING' or 'PAUSED' (current value: '" +
|
||||
this.state +
|
||||
"'). Call 'LocalRecorder.start()' before"
|
||||
);
|
||||
}
|
||||
this.mediaRecorder.onstop = () => {
|
||||
this.onStopDefault();
|
||||
return resolve();
|
||||
};
|
||||
this.mediaRecorder.stop();
|
||||
} catch (e) {
|
||||
return reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the recording of the Stream. {@link state} property must be `RECORDING`. After method succeeds is set to `PAUSED`
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording was successfully paused and rejected with an Error object if not
|
||||
*/
|
||||
pause(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (this.state !== LocalRecorderState.RECORDING) {
|
||||
return reject(
|
||||
Error(
|
||||
"'LocalRecord.pause()' needs 'LocalRecord.state' to be 'RECORDING' (current value: '" +
|
||||
this.state +
|
||||
"'). Call 'LocalRecorder.start()' or 'LocalRecorder.resume()' before"
|
||||
)
|
||||
);
|
||||
}
|
||||
this.mediaRecorder.pause();
|
||||
this.state = LocalRecorderState.PAUSED;
|
||||
return resolve();
|
||||
} catch (error) {
|
||||
return reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the recording of the Stream. {@link state} property must be `PAUSED`. After method succeeds is set to `RECORDING`
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording was successfully resumed and rejected with an Error object if not
|
||||
*/
|
||||
resume(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (this.state !== LocalRecorderState.PAUSED) {
|
||||
throw Error(
|
||||
"'LocalRecord.resume()' needs 'LocalRecord.state' to be 'PAUSED' (current value: '" +
|
||||
this.state +
|
||||
"'). Call 'LocalRecorder.pause()' before"
|
||||
);
|
||||
}
|
||||
this.mediaRecorder.resume();
|
||||
this.state = LocalRecorderState.RECORDING;
|
||||
return resolve();
|
||||
} catch (error) {
|
||||
return reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Previews the recording, appending a new HTMLVideoElement to element with id `parentId`. {@link state} property must be `FINISHED`
|
||||
*/
|
||||
preview(parentElement): HTMLVideoElement {
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
throw Error(
|
||||
"'LocalRecord.preview()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" +
|
||||
this.state +
|
||||
"'). Call 'LocalRecorder.stop()' before"
|
||||
);
|
||||
}
|
||||
|
||||
this.videoPreview = document.createElement('video');
|
||||
|
||||
this.videoPreview.id = this.id;
|
||||
this.videoPreview.autoplay = true;
|
||||
|
||||
if (platform.isSafariBrowser()) {
|
||||
this.videoPreview.playsInline = true;
|
||||
}
|
||||
|
||||
if (typeof parentElement === 'string') {
|
||||
const parentElementDom = document.getElementById(parentElement);
|
||||
if (parentElementDom) {
|
||||
this.videoPreview = parentElementDom.appendChild(this.videoPreview);
|
||||
}
|
||||
} else {
|
||||
this.videoPreview = parentElement.appendChild(this.videoPreview);
|
||||
}
|
||||
|
||||
this.videoPreview.src = this.videoPreviewSrc;
|
||||
|
||||
return this.videoPreview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully stops and cleans the current recording (WARNING: it is completely dismissed). Sets {@link state} to `READY` so the recording can start again
|
||||
*/
|
||||
clean(): void {
|
||||
const f = () => {
|
||||
delete this.blob;
|
||||
this.chunks = [];
|
||||
this.state = LocalRecorderState.READY;
|
||||
};
|
||||
if (this.state === LocalRecorderState.RECORDING || this.state === LocalRecorderState.PAUSED) {
|
||||
this.stop()
|
||||
.then(() => f())
|
||||
.catch(() => f());
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the recorded video through the browser. {@link state} property must be `FINISHED`
|
||||
*/
|
||||
download(): void {
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
throw Error(
|
||||
"'LocalRecord.download()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" +
|
||||
this.state +
|
||||
"'). Call 'LocalRecorder.stop()' before"
|
||||
);
|
||||
} else {
|
||||
const a: HTMLAnchorElement = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild(a);
|
||||
|
||||
const url = globalThis.URL.createObjectURL(<any>this.blob);
|
||||
a.href = url;
|
||||
a.download = this.id + '.' + Mime.getExtension(this.blob!.type);
|
||||
a.click();
|
||||
globalThis.URL.revokeObjectURL(url);
|
||||
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw Blob file. Methods preview, download, uploadAsBinary and uploadAsMultipartfile use this same file to perform their specific actions. {@link state} property must be `FINISHED`
|
||||
*/
|
||||
getBlob(): Blob {
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
throw Error("Call 'LocalRecord.stop()' before getting Blob file");
|
||||
} else {
|
||||
return this.blob!;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the recorded video as a binary file performing an HTTP/POST operation to URL `endpoint`. {@link state} property must be `FINISHED`. Optional HTTP headers can be passed as second parameter. For example:
|
||||
* ```
|
||||
* var headers = {
|
||||
* "Cookie": "$Version=1; Skin=new;",
|
||||
* "Authorization":"Basic QWxhZGpbjpuIHNlctZQ=="
|
||||
* }
|
||||
* ```
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved with the `http.responseText` from server if the operation was successful and rejected with the failed `http.status` if not
|
||||
*/
|
||||
uploadAsBinary(endpoint: string, headers?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
return reject(
|
||||
Error(
|
||||
"'LocalRecord.uploadAsBinary()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" +
|
||||
this.state +
|
||||
"'). Call 'LocalRecorder.stop()' before"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const http = new XMLHttpRequest();
|
||||
http.open('POST', endpoint, true);
|
||||
|
||||
if (typeof headers === 'object') {
|
||||
for (const key of Object.keys(headers)) {
|
||||
http.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
}
|
||||
|
||||
http.onreadystatechange = () => {
|
||||
if (http.readyState === 4) {
|
||||
if (http.status.toString().charAt(0) === '2') {
|
||||
// Success response from server (HTTP status standard: 2XX is success)
|
||||
return resolve(http.responseText);
|
||||
} else {
|
||||
return reject(http.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
http.send(this.blob);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the recorded video as a multipart file performing an HTTP/POST operation to URL `endpoint`. {@link state} property must be `FINISHED`. Optional HTTP headers can be passed as second parameter. For example:
|
||||
* ```
|
||||
* var headers = {
|
||||
* "Cookie": "$Version=1; Skin=new;",
|
||||
* "Authorization":"Basic QWxhZGpbjpuIHNlctZQ=="
|
||||
* }
|
||||
* ```
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved with the `http.responseText` from server if the operation was successful and rejected with the failed `http.status` if not:
|
||||
*/
|
||||
uploadAsMultipartfile(endpoint: string, headers?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
return reject(
|
||||
Error(
|
||||
"'LocalRecord.uploadAsMultipartfile()' needs 'LocalRecord.state' to be 'FINISHED' (current value: '" +
|
||||
this.state +
|
||||
"'). Call 'LocalRecorder.stop()' before"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const http = new XMLHttpRequest();
|
||||
http.open('POST', endpoint, true);
|
||||
|
||||
if (typeof headers === 'object') {
|
||||
for (const key of Object.keys(headers)) {
|
||||
http.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
}
|
||||
|
||||
const sendable = new FormData();
|
||||
sendable.append('file', this.blob!, this.id + '.' + Mime.getExtension(this.blob!.type));
|
||||
|
||||
http.onreadystatechange = () => {
|
||||
if (http.readyState === 4) {
|
||||
if (http.status.toString().charAt(0) === '2') {
|
||||
// Success response from server (HTTP status standard: 2XX is success)
|
||||
return resolve(http.responseText);
|
||||
} else {
|
||||
return reject(http.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
http.send(sendable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Private methods */
|
||||
|
||||
private onStopDefault(): void {
|
||||
logger.log('MediaRecorder stopped (state=' + this.mediaRecorder.state + ')');
|
||||
|
||||
this.blob = new Blob(this.chunks, { type: this.mediaRecorder.mimeType });
|
||||
this.chunks = [];
|
||||
|
||||
this.videoPreviewSrc = globalThis.URL.createObjectURL(this.blob);
|
||||
|
||||
this.state = LocalRecorderState.FINISHED;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,875 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { OpenVidu } from './OpenVidu';
|
||||
import { Session } from './Session';
|
||||
import { Stream } from './Stream';
|
||||
import { StreamManager } from './StreamManager';
|
||||
import { PublisherProperties } from '../OpenViduInternal/Interfaces/Public/PublisherProperties';
|
||||
import { PublisherEventMap } from '../OpenViduInternal/Events/EventMap/PublisherEventMap';
|
||||
import { StreamEvent } from '../OpenViduInternal/Events/StreamEvent';
|
||||
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
|
||||
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
|
||||
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
|
||||
import { TypeOfVideo } from '../OpenViduInternal/Enums/TypeOfVideo';
|
||||
import { StreamEventReason } from '../OpenViduInternal/Events/Types/Types';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
/**
|
||||
* Packs local media streams. Participants can publish it to a session. Initialized with {@link OpenVidu.initPublisher} method.
|
||||
*
|
||||
* See available event listeners at {@link PublisherEventMap}.
|
||||
*/
|
||||
export class Publisher extends StreamManager {
|
||||
/**
|
||||
* Whether the Publisher has been granted access to the requested input devices or not
|
||||
*/
|
||||
accessAllowed = false;
|
||||
|
||||
/**
|
||||
* Whether you have called {@link Publisher.subscribeToRemote} with value `true` or `false` (*false* by default)
|
||||
*/
|
||||
isSubscribedToRemote = false;
|
||||
|
||||
/**
|
||||
* The {@link Session} to which the Publisher belongs
|
||||
*/
|
||||
session: Session; // Initialized by Session.publish(Publisher)
|
||||
|
||||
private accessDenied = false;
|
||||
protected properties: PublisherProperties;
|
||||
private permissionDialogTimeout: NodeJS.Timer;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
openvidu: OpenVidu;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
videoReference: HTMLVideoElement;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
screenShareResizeInterval: NodeJS.Timer;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(targEl: string | HTMLElement | undefined, properties: PublisherProperties, openvidu: OpenVidu) {
|
||||
super(
|
||||
new Stream(!!openvidu.session ? openvidu.session : new Session(openvidu), {
|
||||
publisherProperties: properties,
|
||||
mediaConstraints: {}
|
||||
}),
|
||||
targEl
|
||||
);
|
||||
platform = PlatformUtils.getInstance();
|
||||
this.properties = properties;
|
||||
this.openvidu = openvidu;
|
||||
|
||||
this.stream.ee.on('local-stream-destroyed', (reason: StreamEventReason) => {
|
||||
this.stream.isLocalStreamPublished = false;
|
||||
const streamEvent = new StreamEvent(true, this, 'streamDestroyed', this.stream, reason);
|
||||
this.emitEvent('streamDestroyed', [streamEvent]);
|
||||
streamEvent.callDefaultBehavior();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish or unpublish the audio stream (if available). Calling this method twice in a row passing same `enabled` value will have no effect
|
||||
*
|
||||
* #### Events dispatched
|
||||
*
|
||||
* > _Only if `Session.publish(Publisher)` has been called for this Publisher_
|
||||
*
|
||||
* The {@link Session} object of the local participant will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"audioActive"` and `reason` set to `"publishAudio"`
|
||||
* The {@link Publisher} object of the local participant will also dispatch the exact same event
|
||||
*
|
||||
* The {@link Session} object of every other participant connected to the session will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"audioActive"` and `reason` set to `"publishAudio"`
|
||||
* The respective {@link Subscriber} object of every other participant receiving this Publisher's stream will also dispatch the exact same event
|
||||
*
|
||||
* See {@link StreamPropertyChangedEvent} to learn more.
|
||||
*
|
||||
* @param enabled `true` to publish the audio stream, `false` to unpublish it
|
||||
*/
|
||||
publishAudio(enabled: boolean): void {
|
||||
if (this.stream.audioActive !== enabled) {
|
||||
const affectedMediaStream: MediaStream = this.stream.displayMyRemote()
|
||||
? this.stream.localMediaStreamWhenSubscribedToRemote!
|
||||
: this.stream.getMediaStream();
|
||||
affectedMediaStream.getAudioTracks().forEach((track) => {
|
||||
track.enabled = enabled;
|
||||
});
|
||||
if (!!this.session && !!this.stream.streamId) {
|
||||
this.session.openvidu.sendRequest(
|
||||
'streamPropertyChanged',
|
||||
{
|
||||
streamId: this.stream.streamId,
|
||||
property: 'audioActive',
|
||||
newValue: enabled,
|
||||
reason: 'publishAudio'
|
||||
},
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error("Error sending 'streamPropertyChanged' event", error);
|
||||
} else {
|
||||
this.session.emitEvent('streamPropertyChanged', [
|
||||
new StreamPropertyChangedEvent(this.session, this.stream, 'audioActive', enabled, !enabled, 'publishAudio')
|
||||
]);
|
||||
this.emitEvent('streamPropertyChanged', [
|
||||
new StreamPropertyChangedEvent(this, this.stream, 'audioActive', enabled, !enabled, 'publishAudio')
|
||||
]);
|
||||
this.session.sendVideoData(this.stream.streamManager);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
this.stream.audioActive = enabled;
|
||||
logger.info("'Publisher' has " + (enabled ? 'published' : 'unpublished') + ' its audio stream');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish or unpublish the video stream (if available). Calling this method twice in a row passing same `enabled` value will have no effect
|
||||
*
|
||||
* #### Events dispatched
|
||||
*
|
||||
* > _Only if `Session.publish(Publisher)` has been called for this Publisher_
|
||||
*
|
||||
* The {@link Session} object of the local participant will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"videoActive"` and `reason` set to `"publishVideo"`
|
||||
* The {@link Publisher} object of the local participant will also dispatch the exact same event
|
||||
*
|
||||
* The {@link Session} object of every other participant connected to the session will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"videoActive"` and `reason` set to `"publishVideo"`
|
||||
* The respective {@link Subscriber} object of every other participant receiving this Publisher's stream will also dispatch the exact same event
|
||||
*
|
||||
* See {@link StreamPropertyChangedEvent} to learn more.
|
||||
*
|
||||
* @param enabled `true` to publish the video stream, `false` to unpublish it
|
||||
* @param resource
|
||||
*
|
||||
* If parameter **`enabled`** is `false`, this optional parameter is of type boolean. It can be set to `true` to forcibly free the hardware resource associated to the video track, or can be set to `false` to keep the access to the hardware resource.
|
||||
* Not freeing the resource makes the operation much more efficient, but depending on the platform two side-effects can be introduced: the video device may not be accessible by other applications and the access light of
|
||||
* webcams may remain on. This is platform-dependent: some browsers will not present the side-effects even when not freeing the resource.
|
||||
*
|
||||
* If parameter **`enabled`** is `true`, this optional parameter is of type [MediaStreamTrack](https://developer.mozilla.org/docs/Web/API/MediaStreamTrack). It can be set to force the restoration of the video track with a custom track. This may be
|
||||
* useful if the Publisher was unpublished freeing the hardware resource, and openvidu-browser is not able to successfully re-create the video track as it was before unpublishing. In this way previous track settings will be ignored and this MediaStreamTrack
|
||||
* will be used instead.
|
||||
*/
|
||||
publishVideo<T extends boolean>(enabled: T, resource?: T extends false ? boolean : MediaStreamTrack): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (this.stream.videoActive !== enabled) {
|
||||
const affectedMediaStream: MediaStream = this.stream.displayMyRemote()
|
||||
? this.stream.localMediaStreamWhenSubscribedToRemote!
|
||||
: this.stream.getMediaStream();
|
||||
let mustRestartMediaStream = false;
|
||||
affectedMediaStream.getVideoTracks().forEach((track) => {
|
||||
track.enabled = enabled;
|
||||
if (!enabled && resource === true) {
|
||||
track.stop();
|
||||
} else if (enabled && track.readyState === 'ended') {
|
||||
// Resource was freed
|
||||
mustRestartMediaStream = true;
|
||||
}
|
||||
});
|
||||
|
||||
// There is a Virtual Background filter applied that must be removed in case the hardware must be freed
|
||||
if (!enabled && resource === true && !!this.stream.filter && this.stream.filter.type.startsWith('VB:')) {
|
||||
this.stream.lastVBFilter = this.stream.filter; // Save the filter to be re-applied in case of unmute
|
||||
await this.stream.removeFilterAux(true);
|
||||
}
|
||||
|
||||
if (mustRestartMediaStream) {
|
||||
const oldVideoTrack = affectedMediaStream.getVideoTracks()[0];
|
||||
affectedMediaStream.removeTrack(oldVideoTrack);
|
||||
|
||||
const replaceVideoTrack = async (tr: MediaStreamTrack) => {
|
||||
affectedMediaStream.addTrack(tr);
|
||||
if (this.stream.isLocalStreamPublished) {
|
||||
await this.replaceTrackInRtcRtpSender(tr);
|
||||
}
|
||||
if (!!this.stream.lastVBFilter) {
|
||||
setTimeout(async () => {
|
||||
let options = this.stream.lastVBFilter!.options;
|
||||
const lastExecMethod = this.stream.lastVBFilter!.lastExecMethod;
|
||||
if (!!lastExecMethod && lastExecMethod.method === 'update') {
|
||||
options = Object.assign({}, options, lastExecMethod.params);
|
||||
}
|
||||
await this.stream.applyFilter(this.stream.lastVBFilter!.type, options);
|
||||
delete this.stream.lastVBFilter;
|
||||
}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
if (!!resource && resource instanceof MediaStreamTrack) {
|
||||
await replaceVideoTrack(resource);
|
||||
} else {
|
||||
try {
|
||||
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: this.stream.lastVideoTrackConstraints
|
||||
});
|
||||
await replaceVideoTrack(mediaStream.getVideoTracks()[0]);
|
||||
} catch (error) {
|
||||
return reject(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!!this.session && !!this.stream.streamId) {
|
||||
this.session.openvidu.sendRequest(
|
||||
'streamPropertyChanged',
|
||||
{
|
||||
streamId: this.stream.streamId,
|
||||
property: 'videoActive',
|
||||
newValue: enabled,
|
||||
reason: 'publishVideo'
|
||||
},
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error("Error sending 'streamPropertyChanged' event", error);
|
||||
} else {
|
||||
this.session.emitEvent('streamPropertyChanged', [
|
||||
new StreamPropertyChangedEvent(
|
||||
this.session,
|
||||
this.stream,
|
||||
'videoActive',
|
||||
enabled,
|
||||
!enabled,
|
||||
'publishVideo'
|
||||
)
|
||||
]);
|
||||
this.emitEvent('streamPropertyChanged', [
|
||||
new StreamPropertyChangedEvent(this, this.stream, 'videoActive', enabled, !enabled, 'publishVideo')
|
||||
]);
|
||||
this.session.sendVideoData(this.stream.streamManager);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
this.stream.videoActive = enabled;
|
||||
logger.info("'Publisher' has " + (enabled ? 'published' : 'unpublished') + ' its video stream');
|
||||
return resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method before {@link Session.publish} if you prefer to subscribe to your Publisher's remote stream instead of using the local stream, as any other user would do.
|
||||
*/
|
||||
subscribeToRemote(value?: boolean): void {
|
||||
value = value !== undefined ? value : true;
|
||||
this.isSubscribedToRemote = value;
|
||||
this.stream.subscribeToMyRemote(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link EventDispatcher.on}
|
||||
*/
|
||||
on<K extends keyof PublisherEventMap>(type: K, handler: (event: PublisherEventMap[K]) => void): this {
|
||||
super.on(<any>type, handler);
|
||||
|
||||
if (type === 'streamCreated') {
|
||||
if (!!this.stream && this.stream.isLocalStreamPublished) {
|
||||
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
|
||||
} else {
|
||||
this.stream.ee.on('stream-created-by-publisher', () => {
|
||||
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (type === 'accessAllowed') {
|
||||
if (this.accessAllowed) {
|
||||
this.emitEvent('accessAllowed', []);
|
||||
}
|
||||
}
|
||||
if (type === 'accessDenied') {
|
||||
if (this.accessDenied) {
|
||||
this.emitEvent('accessDenied', []);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link EventDispatcher.once}
|
||||
*/
|
||||
once<K extends keyof PublisherEventMap>(type: K, handler: (event: PublisherEventMap[K]) => void): this {
|
||||
super.once(<any>type, handler);
|
||||
|
||||
if (type === 'streamCreated') {
|
||||
if (!!this.stream && this.stream.isLocalStreamPublished) {
|
||||
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
|
||||
} else {
|
||||
this.stream.ee.once('stream-created-by-publisher', () => {
|
||||
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (type === 'accessAllowed') {
|
||||
if (this.accessAllowed) {
|
||||
this.emitEvent('accessAllowed', []);
|
||||
}
|
||||
}
|
||||
if (type === 'accessDenied') {
|
||||
if (this.accessDenied) {
|
||||
this.emitEvent('accessDenied', []);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link EventDispatcher.off}
|
||||
*/
|
||||
off<K extends keyof PublisherEventMap>(type: K, handler?: (event: PublisherEventMap[K]) => void): this {
|
||||
super.off(<any>type, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current video or audio track with a different one. This allows you to replace an ongoing track with a different one
|
||||
* without having to renegotiate the whole WebRTC connection (that is, initializing a new Publisher, unpublishing the previous one
|
||||
* and publishing the new one).
|
||||
*
|
||||
* You can get this new MediaStreamTrack by using the native Web API or simply with {@link OpenVidu.getUserMedia} method.
|
||||
*
|
||||
* **WARNING: this method has been proven to work in the majority of cases, but there may be some combinations of published/replaced tracks that may be incompatible
|
||||
* between them and break the connection in OpenVidu Server. A complete renegotiation may be the only solution in this case.
|
||||
* Visit [RTCRtpSender.replaceTrack](https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/replaceTrack) documentation for further details.**
|
||||
*
|
||||
* @param track The [MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) object to replace the current one.
|
||||
* If it is an audio track, the current audio track will be the replaced one. If it is a video track, the current video track will be the replaced one.
|
||||
*
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the track was successfully replaced and rejected with an Error object in other case
|
||||
*/
|
||||
async replaceTrack(track: MediaStreamTrack): Promise<void> {
|
||||
return this.replaceTrackAux(track, true);
|
||||
}
|
||||
|
||||
/* Hidden methods */
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initialize(): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let constraints: MediaStreamConstraints = {};
|
||||
let constraintsAux: MediaStreamConstraints = {};
|
||||
const timeForDialogEvent = 2000;
|
||||
let startTime;
|
||||
|
||||
const errorCallback = (openViduError: OpenViduError) => {
|
||||
this.accessDenied = true;
|
||||
this.accessAllowed = false;
|
||||
logger.error(`Publisher initialization failed. ${openViduError.name}: ${openViduError.message}`);
|
||||
return reject(openViduError);
|
||||
};
|
||||
|
||||
const successCallback = (mediaStream: MediaStream) => {
|
||||
this.accessAllowed = true;
|
||||
this.accessDenied = false;
|
||||
|
||||
if (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack) {
|
||||
mediaStream.removeTrack(mediaStream.getAudioTracks()[0]);
|
||||
mediaStream.addTrack(<MediaStreamTrack>this.properties.audioSource);
|
||||
}
|
||||
|
||||
if (typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack) {
|
||||
mediaStream.removeTrack(mediaStream.getVideoTracks()[0]);
|
||||
mediaStream.addTrack(<MediaStreamTrack>this.properties.videoSource);
|
||||
}
|
||||
|
||||
// Apply PublisherProperties.publishAudio and PublisherProperties.publishVideo
|
||||
if (!!mediaStream.getAudioTracks()[0]) {
|
||||
const enabled =
|
||||
this.stream.audioActive !== undefined && this.stream.audioActive !== null
|
||||
? this.stream.audioActive
|
||||
: !!this.stream.outboundStreamOpts.publisherProperties.publishAudio;
|
||||
mediaStream.getAudioTracks()[0].enabled = enabled;
|
||||
}
|
||||
if (!!mediaStream.getVideoTracks()[0]) {
|
||||
const enabled =
|
||||
this.stream.videoActive !== undefined && this.stream.videoActive !== null
|
||||
? this.stream.videoActive
|
||||
: !!this.stream.outboundStreamOpts.publisherProperties.publishVideo;
|
||||
mediaStream.getVideoTracks()[0].enabled = enabled;
|
||||
}
|
||||
|
||||
// Set Content Hint on all MediaStreamTracks
|
||||
for (const track of mediaStream.getAudioTracks()) {
|
||||
if (!track.contentHint?.length) {
|
||||
// contentHint for audio: "", "speech", "speech-recognition", "music".
|
||||
// https://w3c.github.io/mst-content-hint/#audio-content-hints
|
||||
track.contentHint = '';
|
||||
logger.info(`Audio track Content Hint set: '${track.contentHint}'`);
|
||||
}
|
||||
}
|
||||
for (const track of mediaStream.getVideoTracks()) {
|
||||
if (!track.contentHint?.length) {
|
||||
// contentHint for video: "", "motion", "detail", "text".
|
||||
// https://w3c.github.io/mst-content-hint/#video-content-hints
|
||||
switch (this.stream.typeOfVideo) {
|
||||
case TypeOfVideo.SCREEN:
|
||||
track.contentHint = 'detail';
|
||||
break;
|
||||
case TypeOfVideo.CUSTOM:
|
||||
logger.warn('CUSTOM type video track was provided without Content Hint!');
|
||||
track.contentHint = 'motion';
|
||||
break;
|
||||
case TypeOfVideo.CAMERA:
|
||||
case TypeOfVideo.IPCAM:
|
||||
default:
|
||||
track.contentHint = 'motion';
|
||||
break;
|
||||
}
|
||||
logger.info(`Video track Content Hint set: '${track.contentHint}'`);
|
||||
}
|
||||
}
|
||||
|
||||
this.initializeVideoReference(mediaStream);
|
||||
|
||||
if (!this.stream.displayMyRemote()) {
|
||||
// When we are subscribed to our remote we don't still set the MediaStream object in the video elements to
|
||||
// avoid early 'streamPlaying' event
|
||||
this.stream.updateMediaStreamInVideos();
|
||||
}
|
||||
delete this.firstVideoElement;
|
||||
|
||||
if (this.stream.isSendVideo()) {
|
||||
// Has video track
|
||||
this.getVideoDimensions().then((dimensions) => {
|
||||
this.stream.videoDimensions = {
|
||||
width: dimensions.width,
|
||||
height: dimensions.height
|
||||
};
|
||||
|
||||
if (this.stream.isSendScreen()) {
|
||||
|
||||
if(this.stream.isSendAudio() && mediaStream.getAudioTracks().length === 0){
|
||||
// If sending audio is enabled and there are no audio tracks in the mediaStream, disable audio for screen sharing.
|
||||
this.stream.audioActive = false;
|
||||
this.stream.hasAudio = false;
|
||||
this.stream.outboundStreamOpts.publisherProperties.publishAudio = false;
|
||||
this.stream.outboundStreamOpts.publisherProperties.audioSource = false;
|
||||
}
|
||||
|
||||
// Set interval to listen for screen resize events
|
||||
this.screenShareResizeInterval = setInterval(() => {
|
||||
const settings: MediaTrackSettings = mediaStream.getVideoTracks()[0].getSettings();
|
||||
const newWidth = settings.width;
|
||||
const newHeight = settings.height;
|
||||
const widthChanged = newWidth != null && newWidth !== this.stream.videoDimensions.width;
|
||||
const heightChanged = newHeight != null && newHeight !== this.stream.videoDimensions.height;
|
||||
if (this.stream.isLocalStreamPublished && (widthChanged || heightChanged)) {
|
||||
this.openvidu.sendVideoDimensionsChangedEvent(
|
||||
this,
|
||||
'screenResized',
|
||||
this.stream.videoDimensions.width,
|
||||
this.stream.videoDimensions.height,
|
||||
newWidth || 0,
|
||||
newHeight || 0
|
||||
);
|
||||
}
|
||||
}, 650);
|
||||
}
|
||||
|
||||
this.stream.isLocalStreamReadyToPublish = true;
|
||||
this.stream.ee.emitEvent('stream-ready-to-publish', []);
|
||||
});
|
||||
} else {
|
||||
// Only audio track (no videoDimensions)
|
||||
this.stream.isLocalStreamReadyToPublish = true;
|
||||
this.stream.ee.emitEvent('stream-ready-to-publish', []);
|
||||
}
|
||||
|
||||
return resolve();
|
||||
};
|
||||
|
||||
const getMediaSuccess = async (mediaStream: MediaStream, definedAudioConstraint) => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
if (this.stream.isSendScreen() && this.properties.audioSource !== 'screen' && this.stream.isSendAudio()) {
|
||||
// When getting desktop as user media audio constraint must be false. Now we can ask for it if required
|
||||
constraintsAux.audio = definedAudioConstraint;
|
||||
constraintsAux.video = false;
|
||||
startTime = Date.now();
|
||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
||||
|
||||
try {
|
||||
const audioOnlyStream = await navigator.mediaDevices.getUserMedia(constraintsAux);
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
mediaStream.addTrack(audioOnlyStream.getAudioTracks()[0]);
|
||||
successCallback(mediaStream);
|
||||
} catch (error) {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
mediaStream.getAudioTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
mediaStream.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
errorCallback(this.openvidu.generateAudioDeviceError(error, constraints));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
successCallback(mediaStream);
|
||||
}
|
||||
};
|
||||
|
||||
const getMediaError = async (error) => {
|
||||
logger.error(`getMediaError: ${error.toString()}`);
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
if (error.name === 'Error') {
|
||||
// Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError'
|
||||
error.name = error.constructor.name;
|
||||
}
|
||||
let errorName, errorMessage;
|
||||
switch (error.name.toLowerCase()) {
|
||||
case 'notfounderror':
|
||||
try {
|
||||
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: constraints.video
|
||||
});
|
||||
mediaStream.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
} catch (error) {
|
||||
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
}
|
||||
|
||||
break;
|
||||
case 'notallowederror':
|
||||
errorName = this.stream.isSendScreen()
|
||||
? OpenViduErrorName.SCREEN_CAPTURE_DENIED
|
||||
: OpenViduErrorName.DEVICE_ACCESS_DENIED;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
case 'overconstrainederror':
|
||||
try {
|
||||
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: constraints.video
|
||||
});
|
||||
mediaStream.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage =
|
||||
"Audio input device with deviceId '" +
|
||||
(<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.audio).deviceId!!).exact +
|
||||
"' not found";
|
||||
} else {
|
||||
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||
errorMessage =
|
||||
"Audio input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
} catch (error) {
|
||||
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||
errorMessage =
|
||||
"Video input device with deviceId '" +
|
||||
(<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact +
|
||||
"' not found";
|
||||
} else {
|
||||
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||
errorMessage =
|
||||
"Video input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
}
|
||||
|
||||
break;
|
||||
case 'aborterror':
|
||||
case 'notreadableerror':
|
||||
errorName = OpenViduErrorName.DEVICE_ALREADY_IN_USE;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
default:
|
||||
errorName = OpenViduErrorName.GENERIC_ERROR;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const myConstraints = await this.openvidu.generateMediaConstraints(this.properties);
|
||||
if (
|
||||
(!!myConstraints.videoTrack && !!myConstraints.audioTrack) ||
|
||||
(!!myConstraints.audioTrack && myConstraints.constraints?.video === false) ||
|
||||
(!!myConstraints.videoTrack && myConstraints.constraints?.audio === false)
|
||||
) {
|
||||
// No need to call getUserMedia at all. MediaStreamTracks already provided
|
||||
successCallback(this.openvidu.addAlreadyProvidedTracks(myConstraints, new MediaStream(), this.stream));
|
||||
} else {
|
||||
constraints = myConstraints.constraints;
|
||||
|
||||
const outboundStreamOptions = {
|
||||
mediaConstraints: constraints,
|
||||
publisherProperties: this.properties
|
||||
};
|
||||
this.stream.setOutboundStreamOptions(outboundStreamOptions);
|
||||
|
||||
const definedAudioConstraint = constraints.audio === undefined ? true : constraints.audio;
|
||||
constraintsAux.audio = this.stream.isSendScreen() ? false : definedAudioConstraint;
|
||||
constraintsAux.video = constraints.video;
|
||||
startTime = Date.now();
|
||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
||||
|
||||
try {
|
||||
if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && !platform.isElectron()) {
|
||||
const mediaStream = await navigator.mediaDevices['getDisplayMedia']({ video: true, audio: this.properties.audioSource === 'screen' });
|
||||
this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream);
|
||||
await getMediaSuccess(mediaStream, definedAudioConstraint);
|
||||
} else {
|
||||
this.stream.lastVideoTrackConstraints = constraintsAux.video;
|
||||
const mediaStream = await navigator.mediaDevices.getUserMedia(constraintsAux);
|
||||
this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream, this.stream);
|
||||
await getMediaSuccess(mediaStream, definedAudioConstraint);
|
||||
}
|
||||
} catch (error) {
|
||||
await getMediaError(error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
errorCallback(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
async replaceTrackAux(track: MediaStreamTrack, updateLastConstraints: boolean): Promise<void> {
|
||||
// Set field "enabled" of the new track to the previous value
|
||||
const trackOriginalEnabledValue: boolean = track.enabled;
|
||||
if (track.kind === 'video') {
|
||||
track.enabled = this.stream.videoActive;
|
||||
} else if (track.kind === 'audio') {
|
||||
track.enabled = this.stream.audioActive;
|
||||
}
|
||||
try {
|
||||
if (this.stream.isLocalStreamPublished) {
|
||||
// Only if the Publisher has been published is necessary to call native Web API RTCRtpSender.replaceTrack
|
||||
// If it has not been published yet, replacing it on the MediaStream object is enough
|
||||
this.replaceTrackInMediaStream(track, updateLastConstraints);
|
||||
return await this.replaceTrackInRtcRtpSender(track);
|
||||
} else {
|
||||
// Publisher not published. Simply replace the track on the local MediaStream
|
||||
return this.replaceTrackInMediaStream(track, updateLastConstraints);
|
||||
}
|
||||
} catch (error) {
|
||||
track.enabled = trackOriginalEnabledValue;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*
|
||||
* To obtain the videoDimensions we wait for the video reference to have enough metadata
|
||||
* and then try to use MediaStreamTrack.getSettingsMethod(). If not available, then we
|
||||
* use the HTMLVideoElement properties videoWidth and videoHeight
|
||||
*/
|
||||
getVideoDimensions(): Promise<{ width: number; height: number }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Ionic iOS and Safari iOS supposedly require the video element to actually exist inside the DOM
|
||||
const requiresDomInsertion: boolean = (platform.isIonicIos() || platform.isIOSWithSafari()) && (this.videoReference.readyState < 1);
|
||||
|
||||
let loadedmetadataListener;
|
||||
const resolveDimensions = () => {
|
||||
let width: number;
|
||||
let height: number;
|
||||
if (typeof this.stream.getMediaStream().getVideoTracks()[0].getSettings === 'function') {
|
||||
const settings = this.stream.getMediaStream().getVideoTracks()[0].getSettings();
|
||||
width = settings.width || this.videoReference.videoWidth;
|
||||
height = settings.height || this.videoReference.videoHeight;
|
||||
} else {
|
||||
logger.warn('MediaStreamTrack does not have getSettings method on ' + platform.getDescription());
|
||||
width = this.videoReference.videoWidth;
|
||||
height = this.videoReference.videoHeight;
|
||||
}
|
||||
|
||||
if (loadedmetadataListener != null) {
|
||||
this.videoReference.removeEventListener('loadedmetadata', loadedmetadataListener);
|
||||
}
|
||||
if (requiresDomInsertion) {
|
||||
document.body.removeChild(this.videoReference);
|
||||
}
|
||||
|
||||
return resolve({ width, height });
|
||||
};
|
||||
|
||||
if (this.videoReference.readyState >= 1) {
|
||||
// The video already has metadata available
|
||||
// No need of loadedmetadata event
|
||||
resolveDimensions();
|
||||
} else {
|
||||
// The video does not have metadata available yet
|
||||
// Must listen to loadedmetadata event
|
||||
loadedmetadataListener = () => {
|
||||
if (!this.videoReference.videoWidth) {
|
||||
let interval = setInterval(() => {
|
||||
if (!!this.videoReference.videoWidth) {
|
||||
clearInterval(interval);
|
||||
resolveDimensions();
|
||||
}
|
||||
}, 40);
|
||||
} else {
|
||||
resolveDimensions();
|
||||
}
|
||||
};
|
||||
this.videoReference.addEventListener('loadedmetadata', loadedmetadataListener);
|
||||
if (requiresDomInsertion) {
|
||||
document.body.appendChild(this.videoReference);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
reestablishStreamPlayingEvent() {
|
||||
if (this.ee.getListeners('streamPlaying').length > 0) {
|
||||
this.addPlayEventToFirstVideo();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initializeVideoReference(mediaStream: MediaStream) {
|
||||
this.videoReference = document.createElement('video');
|
||||
this.videoReference.style.display = 'none';
|
||||
this.videoReference.muted = true;
|
||||
this.videoReference.autoplay = true;
|
||||
this.videoReference.controls = false;
|
||||
if (
|
||||
platform.isSafariBrowser() ||
|
||||
(platform.isIPhoneOrIPad() &&
|
||||
(platform.isChromeMobileBrowser() ||
|
||||
platform.isEdgeMobileBrowser() ||
|
||||
platform.isOperaMobileBrowser() ||
|
||||
platform.isFirefoxMobileBrowser()))
|
||||
) {
|
||||
this.videoReference.playsInline = true;
|
||||
}
|
||||
this.stream.setMediaStream(mediaStream);
|
||||
if (!!this.firstVideoElement) {
|
||||
this.createVideoElement(this.firstVideoElement.targetElement, <VideoInsertMode>this.properties.insertMode);
|
||||
}
|
||||
this.videoReference.srcObject = this.stream.getMediaStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
replaceTrackInMediaStream(track: MediaStreamTrack, updateLastConstraints: boolean): void {
|
||||
const mediaStream: MediaStream = this.stream.displayMyRemote()
|
||||
? this.stream.localMediaStreamWhenSubscribedToRemote!
|
||||
: this.stream.getMediaStream();
|
||||
let removedTrack: MediaStreamTrack;
|
||||
if (track.kind === 'video') {
|
||||
removedTrack = mediaStream.getVideoTracks()[0];
|
||||
if (updateLastConstraints) {
|
||||
this.stream.lastVideoTrackConstraints = track.getConstraints();
|
||||
}
|
||||
} else {
|
||||
removedTrack = mediaStream.getAudioTracks()[0];
|
||||
}
|
||||
removedTrack.enabled = false;
|
||||
removedTrack.stop();
|
||||
mediaStream.removeTrack(removedTrack);
|
||||
mediaStream.addTrack(track);
|
||||
const trackInfo = {
|
||||
oldLabel: removedTrack?.label || '',
|
||||
newLabel: track?.label || ''
|
||||
};
|
||||
if (track.kind === 'video' && updateLastConstraints) {
|
||||
this.openvidu.sendNewVideoDimensionsIfRequired(this, 'trackReplaced', 50, 30);
|
||||
this.openvidu.sendTrackChangedEvent(this, trackInfo.oldLabel, trackInfo.newLabel, 'videoTrack');
|
||||
if (this.stream.isLocalStreamPublished) {
|
||||
this.session.sendVideoData(this.stream.streamManager, 5, true, 5);
|
||||
}
|
||||
} else if (track.kind === 'audio' && updateLastConstraints) {
|
||||
this.openvidu.sendTrackChangedEvent(this, trackInfo.oldLabel, trackInfo.newLabel, 'audioTrack');
|
||||
}
|
||||
if (track.kind === 'audio') {
|
||||
this.stream.disableHarkSpeakingEvent(false);
|
||||
this.stream.disableHarkStoppedSpeakingEvent(false);
|
||||
this.stream.disableHarkVolumeChangeEvent(false);
|
||||
this.stream.initHarkEvents();
|
||||
}
|
||||
}
|
||||
|
||||
/* Private methods */
|
||||
|
||||
private setPermissionDialogTimer(waitTime: number): void {
|
||||
this.permissionDialogTimeout = setTimeout(() => {
|
||||
this.emitEvent('accessDialogOpened', []);
|
||||
}, waitTime);
|
||||
}
|
||||
|
||||
private clearPermissionDialogTimer(startTime: number, waitTime: number): void {
|
||||
clearTimeout(this.permissionDialogTimeout);
|
||||
if (Date.now() - startTime > waitTime) {
|
||||
// Permission dialog was shown and now is closed
|
||||
this.emitEvent('accessDialogClosed', []);
|
||||
}
|
||||
}
|
||||
|
||||
private async replaceTrackInRtcRtpSender(track: MediaStreamTrack): Promise<void> {
|
||||
const senders: RTCRtpSender[] = this.stream.getRTCPeerConnection().getSenders();
|
||||
let sender: RTCRtpSender | undefined;
|
||||
if (track.kind === 'video') {
|
||||
sender = senders.find((s) => !!s.track && s.track.kind === 'video');
|
||||
if (!sender) {
|
||||
throw new Error("There's no replaceable track for that kind of MediaStreamTrack in this Publisher object");
|
||||
}
|
||||
} else if (track.kind === 'audio') {
|
||||
sender = senders.find((s) => !!s.track && s.track.kind === 'audio');
|
||||
if (!sender) {
|
||||
throw new Error("There's no replaceable track for that kind of MediaStreamTrack in this Publisher object");
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unknown track kind ' + track.kind);
|
||||
}
|
||||
await (sender as RTCRtpSender).replaceTrack(track);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,621 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Stream } from './Stream';
|
||||
import { Subscriber } from './Subscriber';
|
||||
import { EventDispatcher } from './EventDispatcher';
|
||||
import { StreamManagerVideo } from '../OpenViduInternal/Interfaces/Public/StreamManagerVideo';
|
||||
import { StreamManagerEventMap } from '../OpenViduInternal/Events/EventMap/StreamManagerEventMap';
|
||||
import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent';
|
||||
import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent';
|
||||
import { ExceptionEvent, ExceptionEventName } from '../OpenViduInternal/Events/ExceptionEvent';
|
||||
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
/**
|
||||
* Interface in charge of displaying the media streams in the HTML DOM. This wraps any {@link Publisher} and {@link Subscriber} object.
|
||||
* You can insert as many video players fo the same Stream as you want by calling {@link StreamManager.addVideoElement} or
|
||||
* {@link StreamManager.createVideoElement}.
|
||||
* The use of StreamManager wrapper is particularly useful when you don't need to differentiate between Publisher or Subscriber streams or just
|
||||
* want to directly manage your own video elements (even more than one video element per Stream). This scenario is pretty common in
|
||||
* declarative, MVC frontend frameworks such as Angular, React or Vue.js
|
||||
*
|
||||
* See available event listeners at {@link StreamManagerEventMap}.
|
||||
*/
|
||||
export abstract class StreamManager extends EventDispatcher {
|
||||
/**
|
||||
* The Stream represented in the DOM by the Publisher/Subscriber
|
||||
*/
|
||||
stream: Stream;
|
||||
|
||||
/**
|
||||
* All the videos displaying the Stream of this Publisher/Subscriber
|
||||
*/
|
||||
videos: StreamManagerVideo[] = [];
|
||||
|
||||
/**
|
||||
* Whether the Stream represented in the DOM is local or remote
|
||||
* - `false` for {@link Publisher}
|
||||
* - `true` for {@link Subscriber}
|
||||
*/
|
||||
remote: boolean;
|
||||
|
||||
/**
|
||||
* The DOM HTMLElement assigned as target element when creating the video for the Publisher/Subscriber. This property is only defined if:
|
||||
* - {@link Publisher} has been initialized by calling method {@link OpenVidu.initPublisher} with a valid `targetElement` parameter
|
||||
* - {@link Subscriber} has been initialized by calling method {@link Session.subscribe} with a valid `targetElement` parameter
|
||||
*/
|
||||
targetElement: HTMLElement;
|
||||
|
||||
/**
|
||||
* `id` attribute of the DOM video element displaying the Publisher/Subscriber's stream. This property is only defined if:
|
||||
* - {@link Publisher} has been initialized by calling method {@link OpenVidu.initPublisher} with a valid `targetElement` parameter
|
||||
* - {@link Subscriber} has been initialized by calling method {@link Session.subscribe} with a valid `targetElement` parameter
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
protected firstVideoElement?: StreamManagerVideo;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
protected element: HTMLElement;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
protected canPlayListener: EventListener;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
private streamPlayingEventExceptionTimeout?: NodeJS.Timeout;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
private lazyLaunchVideoElementCreatedEvent = false;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(stream: Stream, targetElement?: HTMLElement | string) {
|
||||
super();
|
||||
platform = PlatformUtils.getInstance();
|
||||
this.stream = stream;
|
||||
this.stream.streamManager = this;
|
||||
this.remote = !this.stream.isLocal();
|
||||
|
||||
if (!!targetElement) {
|
||||
let targEl;
|
||||
if (typeof targetElement === 'string') {
|
||||
targEl = document.getElementById(targetElement);
|
||||
} else if (targetElement instanceof HTMLElement) {
|
||||
targEl = targetElement;
|
||||
}
|
||||
|
||||
if (!!targEl) {
|
||||
this.firstVideoElement = {
|
||||
targetElement: targEl,
|
||||
video: document.createElement('video'),
|
||||
id: '',
|
||||
canplayListenerAdded: false
|
||||
};
|
||||
if (
|
||||
platform.isSafariBrowser() ||
|
||||
(platform.isIPhoneOrIPad() &&
|
||||
(platform.isChromeMobileBrowser() ||
|
||||
platform.isEdgeMobileBrowser() ||
|
||||
platform.isOperaMobileBrowser() ||
|
||||
platform.isFirefoxMobileBrowser()))
|
||||
) {
|
||||
this.firstVideoElement.video.playsInline = true;
|
||||
}
|
||||
this.targetElement = targEl;
|
||||
this.element = targEl;
|
||||
}
|
||||
}
|
||||
|
||||
this.canPlayListener = () => {
|
||||
this.deactivateStreamPlayingEventExceptionTimeout();
|
||||
this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link EventDispatcher.on}
|
||||
*/
|
||||
on<K extends keyof StreamManagerEventMap>(type: K, handler: (event: StreamManagerEventMap[K]) => void): this {
|
||||
super.onAux(type, "Event '" + type + "' triggered by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'", handler);
|
||||
|
||||
if (type === 'videoElementCreated') {
|
||||
if (!!this.stream && this.lazyLaunchVideoElementCreatedEvent) {
|
||||
this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(this.videos[0].video, this, 'videoElementCreated')]);
|
||||
this.lazyLaunchVideoElementCreatedEvent = false;
|
||||
}
|
||||
}
|
||||
if (type === 'streamPlaying') {
|
||||
if (
|
||||
this.videos[0] &&
|
||||
this.videos[0].video &&
|
||||
this.videos[0].video.currentTime > 0 &&
|
||||
this.videos[0].video.paused === false &&
|
||||
this.videos[0].video.ended === false &&
|
||||
this.videos[0].video.readyState === 4
|
||||
) {
|
||||
this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]);
|
||||
}
|
||||
}
|
||||
if (this.stream.hasAudio) {
|
||||
if (type === 'publisherStartSpeaking') {
|
||||
this.stream.enableHarkSpeakingEvent();
|
||||
}
|
||||
if (type === 'publisherStopSpeaking') {
|
||||
this.stream.enableHarkStoppedSpeakingEvent();
|
||||
}
|
||||
if (type === 'streamAudioVolumeChange') {
|
||||
this.stream.enableHarkVolumeChangeEvent(false);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link EventDispatcher.once}
|
||||
*/
|
||||
once<K extends keyof StreamManagerEventMap>(type: K, handler: (event: StreamManagerEventMap[K]) => void): this {
|
||||
super.onceAux(type, "Event '" + type + "' triggered once by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'", handler);
|
||||
|
||||
if (type === 'videoElementCreated') {
|
||||
if (!!this.stream && this.lazyLaunchVideoElementCreatedEvent) {
|
||||
this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(this.videos[0].video, this, 'videoElementCreated')]);
|
||||
}
|
||||
}
|
||||
if (type === 'streamPlaying') {
|
||||
if (
|
||||
this.videos[0] &&
|
||||
this.videos[0].video &&
|
||||
this.videos[0].video.currentTime > 0 &&
|
||||
this.videos[0].video.paused === false &&
|
||||
this.videos[0].video.ended === false &&
|
||||
this.videos[0].video.readyState === 4
|
||||
) {
|
||||
this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]);
|
||||
}
|
||||
}
|
||||
if (this.stream.hasAudio) {
|
||||
if (type === 'publisherStartSpeaking') {
|
||||
this.stream.enableOnceHarkSpeakingEvent();
|
||||
}
|
||||
if (type === 'publisherStopSpeaking') {
|
||||
this.stream.enableOnceHarkStoppedSpeakingEvent();
|
||||
}
|
||||
if (type === 'streamAudioVolumeChange') {
|
||||
this.stream.enableOnceHarkVolumeChangeEvent(false);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link EventDispatcher.off}
|
||||
*/
|
||||
off<K extends keyof StreamManagerEventMap>(type: K, handler?: (event: StreamManagerEventMap[K]) => void): this {
|
||||
super.offAux(type, handler);
|
||||
|
||||
if (type === 'publisherStartSpeaking') {
|
||||
// Both StreamManager and Session can have "publisherStartSpeaking" event listeners
|
||||
const remainingStartSpeakingEventListeners =
|
||||
this.ee.getListeners(type).length + this.stream.session.ee.getListeners(type).length;
|
||||
if (remainingStartSpeakingEventListeners === 0) {
|
||||
this.stream.disableHarkSpeakingEvent(false);
|
||||
}
|
||||
}
|
||||
if (type === 'publisherStopSpeaking') {
|
||||
// Both StreamManager and Session can have "publisherStopSpeaking" event listeners
|
||||
const remainingStopSpeakingEventListeners =
|
||||
this.ee.getListeners(type).length + this.stream.session.ee.getListeners(type).length;
|
||||
if (remainingStopSpeakingEventListeners === 0) {
|
||||
this.stream.disableHarkStoppedSpeakingEvent(false);
|
||||
}
|
||||
}
|
||||
if (type === 'streamAudioVolumeChange') {
|
||||
// Only StreamManager can have "streamAudioVolumeChange" event listeners
|
||||
const remainingVolumeEventListeners = this.ee.getListeners(type).length;
|
||||
if (remainingVolumeEventListeners === 0) {
|
||||
this.stream.disableHarkVolumeChangeEvent(false);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes `video` element parameter display this {@link stream}. This is useful when you are
|
||||
* [managing the video elements on your own](/en/stable/cheatsheet/manage-videos/#you-take-care-of-the-video-players)
|
||||
*
|
||||
* Calling this method with a video already added to other Publisher/Subscriber will cause the video element to be
|
||||
* disassociated from that previous Publisher/Subscriber and to be associated to this one.
|
||||
*
|
||||
* @returns 1 if the video wasn't associated to any other Publisher/Subscriber and has been successfully added to this one.
|
||||
* 0 if the video was already added to this Publisher/Subscriber. -1 if the video was previously associated to any other
|
||||
* Publisher/Subscriber and has been successfully disassociated from that one and properly added to this one.
|
||||
*/
|
||||
addVideoElement(video: HTMLVideoElement): number {
|
||||
this.initializeVideoProperties(video);
|
||||
|
||||
if (!this.remote && this.stream.displayMyRemote()) {
|
||||
if (video.srcObject !== this.stream.getMediaStream()) {
|
||||
video.srcObject = this.stream.getMediaStream();
|
||||
}
|
||||
}
|
||||
|
||||
// If the video element is already part of this StreamManager do nothing
|
||||
for (const v of this.videos) {
|
||||
if (v.video === video) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
let returnNumber = 1;
|
||||
|
||||
for (const streamManager of this.stream.session.streamManagers) {
|
||||
if (streamManager.disassociateVideo(video)) {
|
||||
returnNumber = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.stream.session.streamManagers.forEach((streamManager) => {
|
||||
streamManager.disassociateVideo(video);
|
||||
});
|
||||
|
||||
this.pushNewStreamManagerVideo({
|
||||
video,
|
||||
id: video.id,
|
||||
canplayListenerAdded: false
|
||||
});
|
||||
|
||||
logger.info('New video element associated to ', this);
|
||||
|
||||
return returnNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new video element displaying this {@link stream}. This allows you to have multiple video elements displaying the same media stream.
|
||||
*
|
||||
* #### Events dispatched
|
||||
*
|
||||
* The Publisher/Subscriber object will dispatch a `videoElementCreated` event once the HTML video element has been added to DOM. See {@link VideoElementEvent}
|
||||
*
|
||||
* @param targetElement HTML DOM element (or its `id` attribute) in which the video element of the Publisher/Subscriber will be inserted
|
||||
* @param insertMode How the video element will be inserted accordingly to `targetElemet`
|
||||
*
|
||||
* @returns The created HTMLVideoElement
|
||||
*/
|
||||
createVideoElement(targetElement?: string | HTMLElement, insertMode?: VideoInsertMode): HTMLVideoElement {
|
||||
let targEl;
|
||||
if (typeof targetElement === 'string') {
|
||||
targEl = document.getElementById(targetElement);
|
||||
if (!targEl) {
|
||||
throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement);
|
||||
}
|
||||
} else if (targetElement instanceof HTMLElement) {
|
||||
targEl = targetElement;
|
||||
} else {
|
||||
throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement);
|
||||
}
|
||||
|
||||
const video = this.createVideo();
|
||||
this.initializeVideoProperties(video);
|
||||
|
||||
let insMode = !!insertMode ? insertMode : VideoInsertMode.APPEND;
|
||||
switch (insMode) {
|
||||
case VideoInsertMode.AFTER:
|
||||
targEl.parentNode!!.insertBefore(video, targEl.nextSibling);
|
||||
break;
|
||||
case VideoInsertMode.APPEND:
|
||||
targEl.appendChild(video);
|
||||
break;
|
||||
case VideoInsertMode.BEFORE:
|
||||
targEl.parentNode!!.insertBefore(video, targEl);
|
||||
break;
|
||||
case VideoInsertMode.PREPEND:
|
||||
targEl.insertBefore(video, targEl.childNodes[0]);
|
||||
break;
|
||||
case VideoInsertMode.REPLACE:
|
||||
targEl.parentNode!!.replaceChild(video, targEl);
|
||||
break;
|
||||
default:
|
||||
insMode = VideoInsertMode.APPEND;
|
||||
targEl.appendChild(video);
|
||||
break;
|
||||
}
|
||||
|
||||
const v: StreamManagerVideo = {
|
||||
targetElement: targEl,
|
||||
video,
|
||||
insertMode: insMode,
|
||||
id: video.id,
|
||||
canplayListenerAdded: false
|
||||
};
|
||||
this.pushNewStreamManagerVideo(v);
|
||||
|
||||
this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(v.video, this, 'videoElementCreated')]);
|
||||
this.lazyLaunchVideoElementCreatedEvent = !!this.firstVideoElement;
|
||||
|
||||
return video;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current configuration for the {@link PublisherSpeakingEvent} feature and the [StreamManagerEvent.streamAudioVolumeChange](/en/stable/api/openvidu-browser/classes/StreamManagerEvent.html) feature for this specific
|
||||
* StreamManager audio stream, overriding the global options set with {@link OpenVidu.setAdvancedConfiguration}. This way you can customize the audio events options
|
||||
* for each specific StreamManager and change them dynamically.
|
||||
*
|
||||
* @param publisherSpeakingEventsOptions New options to be applied to this StreamManager's audio stream. It is an object which includes the following optional properties:
|
||||
* - `interval`: (number) how frequently the analyser polls the audio stream to check if speaking has started/stopped or audio volume has changed. Default **100** (ms)
|
||||
* - `threshold`: (number) the volume at which _publisherStartSpeaking_, _publisherStopSpeaking_ events will be fired. Default **-50** (dB)
|
||||
*/
|
||||
updatePublisherSpeakingEventsOptions(publisherSpeakingEventsOptions: { interval?: number; threshold?: number }): void {
|
||||
const currentHarkOptions = !!this.stream.harkOptions
|
||||
? this.stream.harkOptions
|
||||
: this.stream.session.openvidu.advancedConfiguration.publisherSpeakingEventsOptions || {};
|
||||
const newInterval =
|
||||
typeof publisherSpeakingEventsOptions.interval === 'number'
|
||||
? publisherSpeakingEventsOptions.interval
|
||||
: typeof currentHarkOptions.interval === 'number'
|
||||
? currentHarkOptions.interval
|
||||
: 100;
|
||||
const newThreshold =
|
||||
typeof publisherSpeakingEventsOptions.threshold === 'number'
|
||||
? publisherSpeakingEventsOptions.threshold
|
||||
: typeof currentHarkOptions.threshold === 'number'
|
||||
? currentHarkOptions.threshold
|
||||
: -50;
|
||||
this.stream.harkOptions = {
|
||||
interval: newInterval,
|
||||
threshold: newThreshold
|
||||
};
|
||||
if (!!this.stream.speechEvent) {
|
||||
this.stream.speechEvent.setInterval(newInterval);
|
||||
this.stream.speechEvent.setThreshold(newThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hidden methods */
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initializeVideoProperties(video: HTMLVideoElement): void {
|
||||
if (!(!this.remote && this.stream.displayMyRemote())) {
|
||||
// Avoid setting the MediaStream into the srcObject if remote subscription before publishing
|
||||
if (video.srcObject !== this.stream.getMediaStream()) {
|
||||
// If srcObject already set don't do it again
|
||||
video.srcObject = this.stream.getMediaStream();
|
||||
}
|
||||
}
|
||||
video.autoplay = true;
|
||||
video.controls = false;
|
||||
|
||||
if (
|
||||
platform.isSafariBrowser() ||
|
||||
(platform.isIPhoneOrIPad() &&
|
||||
(platform.isChromeMobileBrowser() ||
|
||||
platform.isEdgeMobileBrowser() ||
|
||||
platform.isOperaMobileBrowser() ||
|
||||
platform.isFirefoxMobileBrowser()))
|
||||
) {
|
||||
video.playsInline = true;
|
||||
}
|
||||
|
||||
if (!video.id) {
|
||||
video.id = (this.remote ? 'remote-' : 'local-') + 'video-' + this.stream.streamId;
|
||||
// DEPRECATED property: assign once the property id if the user provided a valid targetElement
|
||||
if (!this.id && !!this.targetElement) {
|
||||
this.id = video.id;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.remote && this.isMirroredVideo(video)) {
|
||||
// Subscriber video associated to a previously mirrored video element
|
||||
this.removeMirrorVideo(video);
|
||||
} else if (!this.remote && !this.stream.displayMyRemote()) {
|
||||
// Publisher video
|
||||
video.muted = true;
|
||||
if (this.isMirroredVideo(video) && !this.stream.outboundStreamOpts.publisherProperties.mirror) {
|
||||
// If the video was already rotated and now is set to not mirror
|
||||
this.removeMirrorVideo(video);
|
||||
} else if (this.stream.outboundStreamOpts.publisherProperties.mirror && !this.stream.isSendScreen()) {
|
||||
// If the video is now set to mirror and is not screen share
|
||||
this.mirrorVideo(video);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
removeAllVideos(): void {
|
||||
for (let i = this.stream.session.streamManagers.length - 1; i >= 0; --i) {
|
||||
if (this.stream.session.streamManagers[i] === this) {
|
||||
this.stream.session.streamManagers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.videos.forEach((streamManagerVideo) => {
|
||||
// Remove oncanplay event listener (only OpenVidu browser listener, not the user ones)
|
||||
if (!!streamManagerVideo.video && !!streamManagerVideo.video.removeEventListener) {
|
||||
streamManagerVideo.video.removeEventListener('canplay', this.canPlayListener);
|
||||
}
|
||||
streamManagerVideo.canplayListenerAdded = false;
|
||||
if (!!streamManagerVideo.targetElement) {
|
||||
// Only remove from DOM videos created by OpenVidu Browser (those generated by passing a valid targetElement in OpenVidu.initPublisher
|
||||
// and Session.subscribe or those created by StreamManager.createVideoElement). All this videos triggered a videoElementCreated event
|
||||
streamManagerVideo.video.parentNode!.removeChild(streamManagerVideo.video);
|
||||
this.ee.emitEvent('videoElementDestroyed', [
|
||||
new VideoElementEvent(streamManagerVideo.video, this, 'videoElementDestroyed')
|
||||
]);
|
||||
}
|
||||
// Remove srcObject from the video
|
||||
this.removeSrcObject(streamManagerVideo);
|
||||
// Remove from collection of videos every video managed by OpenVidu Browser
|
||||
this.videos = this.videos.filter((v) => !v.targetElement);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
disassociateVideo(video: HTMLVideoElement): boolean {
|
||||
let disassociated = false;
|
||||
for (let i = 0; i < this.videos.length; i++) {
|
||||
if (this.videos[i].video === video) {
|
||||
this.videos[i].video.removeEventListener('canplay', this.canPlayListener);
|
||||
this.videos.splice(i, 1);
|
||||
disassociated = true;
|
||||
logger.info('Video element disassociated from ', this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return disassociated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
addPlayEventToFirstVideo() {
|
||||
if (!!this.videos[0] && !!this.videos[0].video && !this.videos[0].canplayListenerAdded) {
|
||||
this.activateStreamPlayingEventExceptionTimeout();
|
||||
this.videos[0].video.addEventListener('canplay', this.canPlayListener);
|
||||
this.videos[0].canplayListenerAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
updateMediaStream(mediaStream: MediaStream) {
|
||||
this.videos.forEach((streamManagerVideo) => {
|
||||
streamManagerVideo.video.srcObject = mediaStream;
|
||||
if (platform.isIonicIos()) {
|
||||
// iOS Ionic. LIMITATION: must reinsert the video in the DOM for
|
||||
// the media stream to be updated
|
||||
const vParent = streamManagerVideo.video.parentElement;
|
||||
const newVideo = streamManagerVideo.video;
|
||||
vParent!!.replaceChild(newVideo, streamManagerVideo.video);
|
||||
streamManagerVideo.video = newVideo;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
emitEvent(type: string, eventArray: any[]): void {
|
||||
this.ee.emitEvent(type, eventArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
createVideo(): HTMLVideoElement {
|
||||
return document.createElement('video');
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
removeSrcObject(streamManagerVideo: StreamManagerVideo) {
|
||||
streamManagerVideo.video.srcObject = null;
|
||||
this.deactivateStreamPlayingEventExceptionTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
abstract replaceTrackInMediaStream(track: MediaStreamTrack, updateLastConstraints: boolean): void;
|
||||
|
||||
/* Private methods */
|
||||
|
||||
protected pushNewStreamManagerVideo(streamManagerVideo: StreamManagerVideo) {
|
||||
this.videos.push(streamManagerVideo);
|
||||
this.addPlayEventToFirstVideo();
|
||||
if (this.stream.session.streamManagers.indexOf(this) === -1) {
|
||||
this.stream.session.streamManagers.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
private mirrorVideo(video: HTMLVideoElement): void {
|
||||
if (!platform.isIonicIos()) {
|
||||
video.style.transform = 'rotateY(180deg)';
|
||||
video.style.webkitTransform = 'rotateY(180deg)';
|
||||
}
|
||||
}
|
||||
|
||||
private removeMirrorVideo(video: HTMLVideoElement): void {
|
||||
video.style.transform = 'unset';
|
||||
video.style.webkitTransform = 'unset';
|
||||
}
|
||||
|
||||
private isMirroredVideo(video: HTMLVideoElement): boolean {
|
||||
return video.style.transform === 'rotateY(180deg)' || video.style.webkitTransform === 'rotateY(180deg)';
|
||||
}
|
||||
|
||||
private activateStreamPlayingEventExceptionTimeout() {
|
||||
if (!this.remote) {
|
||||
// ExceptionEvent NO_STREAM_PLAYING_EVENT is only for subscribers
|
||||
return;
|
||||
}
|
||||
if (this.streamPlayingEventExceptionTimeout != null) {
|
||||
// The timeout is already activated
|
||||
return;
|
||||
}
|
||||
// Trigger ExceptionEvent NO_STREAM_PLAYING_EVENT if after timeout there is no 'canplay' event
|
||||
const msTimeout = this.stream.session.openvidu.advancedConfiguration.noStreamPlayingEventExceptionTimeout || 4000;
|
||||
this.streamPlayingEventExceptionTimeout = setTimeout(() => {
|
||||
const msg =
|
||||
'StreamManager of Stream ' +
|
||||
this.stream.streamId +
|
||||
' (' +
|
||||
(this.remote ? 'Subscriber' : 'Publisher') +
|
||||
') did not trigger "streamPlaying" event in ' +
|
||||
msTimeout +
|
||||
' ms';
|
||||
logger.warn(msg);
|
||||
this.stream.session.emitEvent('exception', [
|
||||
new ExceptionEvent(this.stream.session, ExceptionEventName.NO_STREAM_PLAYING_EVENT, (<any>this) as Subscriber, msg)
|
||||
]);
|
||||
delete this.streamPlayingEventExceptionTimeout;
|
||||
}, msTimeout);
|
||||
}
|
||||
|
||||
private deactivateStreamPlayingEventExceptionTimeout() {
|
||||
clearTimeout(this.streamPlayingEventExceptionTimeout as any);
|
||||
delete this.streamPlayingEventExceptionTimeout;
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Stream } from './Stream';
|
||||
import { StreamManager } from './StreamManager';
|
||||
import { SubscriberProperties } from '../OpenViduInternal/Interfaces/Public/SubscriberProperties';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* Packs remote media streams. Participants automatically receive them when others publish their streams. Initialized with {@link Session.subscribe} method
|
||||
*
|
||||
* See available event listeners at {@link StreamManagerEventMap}.
|
||||
*/
|
||||
export class Subscriber extends StreamManager {
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
properties: SubscriberProperties;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(stream: Stream, targEl: string | HTMLElement | undefined, properties: SubscriberProperties) {
|
||||
super(stream, targEl);
|
||||
this.element = this.targetElement;
|
||||
this.stream = stream;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe or unsubscribe from the audio stream (if available). Calling this method twice in a row passing same value will have no effect
|
||||
* @param value `true` to subscribe to the audio stream, `false` to unsubscribe from it
|
||||
*/
|
||||
subscribeToAudio(value: boolean): Subscriber {
|
||||
this.stream
|
||||
.getMediaStream()
|
||||
.getAudioTracks()
|
||||
.forEach((track) => {
|
||||
track.enabled = value;
|
||||
});
|
||||
this.stream.audioActive = value;
|
||||
logger.info("'Subscriber' has " + (value ? 'subscribed to' : 'unsubscribed from') + ' its audio stream');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe or unsubscribe from the video stream (if available). Calling this method twice in a row passing same value will have no effect
|
||||
* @param value `true` to subscribe to the video stream, `false` to unsubscribe from it
|
||||
*/
|
||||
subscribeToVideo(value: boolean): Subscriber {
|
||||
this.stream
|
||||
.getMediaStream()
|
||||
.getVideoTracks()
|
||||
.forEach((track) => {
|
||||
track.enabled = value;
|
||||
});
|
||||
this.stream.videoActive = value;
|
||||
logger.info("'Subscriber' has " + (value ? 'subscribed to' : 'unsubscribed from') + ' its video stream');
|
||||
return this;
|
||||
}
|
||||
|
||||
/* Hidden methods */
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
replaceTrackInMediaStream(track: MediaStreamTrack, updateLastConstraints: boolean): void {
|
||||
const mediaStream: MediaStream = this.stream.getMediaStream();
|
||||
let removedTrack: MediaStreamTrack;
|
||||
if (track.kind === 'video') {
|
||||
removedTrack = mediaStream.getVideoTracks()[0];
|
||||
if (updateLastConstraints) {
|
||||
this.stream.lastVideoTrackConstraints = track.getConstraints();
|
||||
}
|
||||
} else {
|
||||
removedTrack = mediaStream.getAudioTracks()[0];
|
||||
}
|
||||
mediaStream.removeTrack(removedTrack);
|
||||
removedTrack.stop();
|
||||
mediaStream.addTrack(track);
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
//"allowUnusedLabels": true,
|
||||
"allowUnreachableCode": false,
|
||||
"buildOnSave": false,
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"emitBOM": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015.promise",
|
||||
"es5",
|
||||
"scripthost"
|
||||
],
|
||||
"module": "commonjs",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
//"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
//"noUnusedLocals": true,
|
||||
//"noUnusedParameters": true,
|
||||
"outDir": "../../lib",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressExcessPropertyErrors": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"target": "es5"
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
export enum LocalRecorderState {
|
||||
READY = 'READY',
|
||||
RECORDING = 'RECORDING',
|
||||
PAUSED = 'PAUSED',
|
||||
FINISHED = 'FINISHED'
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines property {@link OpenViduError.name}
|
||||
*/
|
||||
export enum OpenViduErrorName {
|
||||
/**
|
||||
* Browser is not supported by OpenVidu.
|
||||
* Returned upon unsuccessful {@link Session.connect}
|
||||
*/
|
||||
BROWSER_NOT_SUPPORTED = 'BROWSER_NOT_SUPPORTED',
|
||||
|
||||
/**
|
||||
* The user hasn't granted permissions to the required input device when the browser asked for them.
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
DEVICE_ACCESS_DENIED = 'DEVICE_ACCESS_DENIED',
|
||||
|
||||
/**
|
||||
* The required input device is probably being used by other process when the browser asked for it.
|
||||
* This error can also be triggered when the user granted permission to use the devices but a hardware
|
||||
* error occurred at the OS, browser or web page level, which prevented access to the device.
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
DEVICE_ALREADY_IN_USE = 'DEVICE_ALREADY_IN_USE',
|
||||
|
||||
/**
|
||||
* The user hasn't granted permissions to capture some desktop screen when the browser asked for them.
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED',
|
||||
|
||||
/**
|
||||
* Browser does not support screen sharing.
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
SCREEN_SHARING_NOT_SUPPORTED = 'SCREEN_SHARING_NOT_SUPPORTED',
|
||||
|
||||
/**
|
||||
* Only for Chrome, there's no screen sharing extension installed
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
SCREEN_EXTENSION_NOT_INSTALLED = 'SCREEN_EXTENSION_NOT_INSTALLED',
|
||||
|
||||
/**
|
||||
* Only for Chrome, the screen sharing extension is installed but is disabled
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
SCREEN_EXTENSION_DISABLED = 'SCREEN_EXTENSION_DISABLED',
|
||||
|
||||
/**
|
||||
* No video input device found with the provided deviceId (property {@link PublisherProperties.videoSource})
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
INPUT_VIDEO_DEVICE_NOT_FOUND = 'INPUT_VIDEO_DEVICE_NOT_FOUND',
|
||||
|
||||
/**
|
||||
* No audio input device found with the provided deviceId (property {@link PublisherProperties.audioSource})
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
INPUT_AUDIO_DEVICE_NOT_FOUND = 'INPUT_AUDIO_DEVICE_NOT_FOUND',
|
||||
|
||||
/**
|
||||
* There was an unknown error when trying to access the specified audio device
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
INPUT_AUDIO_DEVICE_GENERIC_ERROR = 'INPUT_AUDIO_DEVICE_GENERIC_ERROR',
|
||||
|
||||
/**
|
||||
* Method {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia} has been called with properties `videoSource` and `audioSource` of
|
||||
* {@link PublisherProperties} parameter both set to *false* or *null*
|
||||
*/
|
||||
NO_INPUT_SOURCE_SET = 'NO_INPUT_SOURCE_SET',
|
||||
|
||||
/**
|
||||
* Some media property of {@link PublisherProperties} such as `frameRate` or `resolution` is not supported
|
||||
* by the input devices (whenever it is possible they are automatically adjusted to the most similar value).
|
||||
* Returned upon unsuccessful {@link OpenVidu.initPublisher} or {@link OpenVidu.getUserMedia}
|
||||
*/
|
||||
PUBLISHER_PROPERTIES_ERROR = 'PUBLISHER_PROPERTIES_ERROR',
|
||||
|
||||
/**
|
||||
* The client tried to call a method without the required permissions. This can occur for methods {@link Session.publish},
|
||||
* {@link Session.forceUnpublish}, {@link Session.forceDisconnect}, {@link Stream.applyFilter}, {@link Stream.removeFilter}
|
||||
*/
|
||||
OPENVIDU_PERMISSION_DENIED = 'OPENVIDU_PERMISSION_DENIED',
|
||||
|
||||
/**
|
||||
* There is no connection to the Session. This error will be thrown when any method requiring a connection to
|
||||
* openvidu-server is called before successfully calling method {@link Session.connect}
|
||||
*/
|
||||
OPENVIDU_NOT_CONNECTED = 'OPENVIDU_NOT_CONNECTED',
|
||||
|
||||
/**
|
||||
* Error related to [Virtual Background](/en/stable/advanced-features/virtual-background/)
|
||||
*/
|
||||
VIRTUAL_BACKGROUND_ERROR = 'VIRTUAL_BACKGROUND_ERROR',
|
||||
|
||||
/**
|
||||
* Generic error
|
||||
*/
|
||||
GENERIC_ERROR = 'GENERIC_ERROR'
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple object to identify runtime errors on the client side
|
||||
*/
|
||||
export class OpenViduError {
|
||||
/**
|
||||
* Uniquely identifying name of the error
|
||||
*/
|
||||
name: OpenViduErrorName;
|
||||
|
||||
/**
|
||||
* Full description of the error
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(name: OpenViduErrorName, message: string) {
|
||||
this.name = name;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
export enum TypeOfVideo {
|
||||
CAMERA = 'CAMERA',
|
||||
SCREEN = 'SCREEN',
|
||||
CUSTOM = 'CUSTOM',
|
||||
IPCAM = 'IPCAM'
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* How the video will be inserted in the DOM for Publishers and Subscribers. See {@link PublisherProperties.insertMode} and {@link SubscriberProperties.insertMode}
|
||||
*/
|
||||
export enum VideoInsertMode {
|
||||
/**
|
||||
* Video inserted after the target element (as next sibling)
|
||||
*/
|
||||
AFTER = 'AFTER',
|
||||
/**
|
||||
* Video inserted as last child of the target element
|
||||
*/
|
||||
APPEND = 'APPEND',
|
||||
/**
|
||||
* Video inserted before the target element (as previous sibling)
|
||||
*/
|
||||
BEFORE = 'BEFORE',
|
||||
/**
|
||||
* Video inserted as first child of the target element
|
||||
*/
|
||||
PREPEND = 'PREPEND',
|
||||
/**
|
||||
* Video replaces target element
|
||||
*/
|
||||
REPLACE = 'REPLACE'
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { ConnectionEventReason } from './Types/Types';
|
||||
|
||||
/**
|
||||
* Triggered by:
|
||||
* - {@link SessionEventMap.connectionCreated}
|
||||
* - {@link SessionEventMap.connectionDestroyed}
|
||||
*/
|
||||
export class ConnectionEvent extends Event {
|
||||
/**
|
||||
* Connection object that was created or destroyed
|
||||
*/
|
||||
connection: Connection;
|
||||
|
||||
/**
|
||||
* For `connectionDestroyed` event:
|
||||
* - "disconnect": the remote user has called `Session.disconnect()`
|
||||
* - "forceDisconnectByUser": the remote user has been evicted from the Session by other user calling `Session.forceDisconnect()`
|
||||
* - "forceDisconnectByServer": the remote user has been evicted from the Session by the application
|
||||
* - "sessionClosedByServer": the Session has been closed by the application
|
||||
* - "networkDisconnect": the remote user network connection has dropped
|
||||
* - "nodeCrashed": a node has crashed in the server side
|
||||
*
|
||||
* For `connectionCreated` event an empty string
|
||||
*/
|
||||
reason: ConnectionEventReason;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(cancelable: boolean, target: Session, type: string, connection: Connection, reason: ConnectionEventReason) {
|
||||
super(cancelable, target, type);
|
||||
this.connection = connection;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { Event } from './Event';
|
||||
|
||||
/**
|
||||
* **This feature is part of OpenVidu
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
|
||||
* and
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
|
||||
* editions**
|
||||
*
|
||||
* Triggered by {@link SessionEventMap.connectionPropertyChanged}
|
||||
*/
|
||||
export class ConnectionPropertyChangedEvent extends Event {
|
||||
/**
|
||||
* The Connection whose property has changed
|
||||
*/
|
||||
connection: Connection;
|
||||
|
||||
/**
|
||||
* The property of the stream that changed. This value is either `"role"` or `"record"`
|
||||
*/
|
||||
changedProperty: string;
|
||||
|
||||
/**
|
||||
* New value of the property (after change, current value)
|
||||
*/
|
||||
newValue: Object;
|
||||
|
||||
/**
|
||||
* Previous value of the property (before change)
|
||||
*/
|
||||
oldValue: Object;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, connection: Connection, changedProperty: string, newValue: Object, oldValue: Object) {
|
||||
super(false, target, 'connectionPropertyChanged');
|
||||
this.connection = connection;
|
||||
this.changedProperty = changedProperty;
|
||||
this.newValue = newValue;
|
||||
this.oldValue = oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() {}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Filter } from '../../OpenVidu/Filter';
|
||||
import { StreamManager } from '../../OpenVidu/StreamManager';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
|
||||
export abstract class Event {
|
||||
/**
|
||||
* Whether the event has a default behavior that may be prevented by calling {@link Event.preventDefault}
|
||||
*/
|
||||
cancelable: boolean;
|
||||
|
||||
/**
|
||||
* The object that dispatched the event
|
||||
*/
|
||||
target: Session | StreamManager | Filter;
|
||||
|
||||
/**
|
||||
* The type of event. This is the same string you pass as first parameter when calling method `on()` of any object implementing {@link EventDispatcher} interface
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
hasBeenPrevented = false;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(cancelable: boolean, target: Session | StreamManager | Filter, type: string) {
|
||||
this.cancelable = cancelable;
|
||||
this.target = target;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the default beahivour of the event has been prevented or not. Call {@link Event.preventDefault} to prevent it
|
||||
*/
|
||||
isDefaultPrevented(): boolean {
|
||||
return this.hasBeenPrevented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the default behavior of the event. The following events have a default behavior:
|
||||
*
|
||||
* - `sessionDisconnected`: dispatched by {@link Session} object, automatically unsubscribes the leaving participant from every Subscriber object of the session (this includes closing the RTCPeerConnection and disposing all MediaStreamTracks)
|
||||
* and also deletes any HTML video element associated to each Subscriber (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement` in method {@link Session.subscribe} or
|
||||
* by calling {@link Subscriber.createVideoElement}). For every video removed, each Subscriber object will also dispatch a `videoElementDestroyed` event.
|
||||
*
|
||||
* - `streamDestroyed`:
|
||||
* - If dispatched by a {@link Publisher} (*you* have unpublished): automatically stops all media tracks and deletes any HTML video element associated to it (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement`
|
||||
* in method {@link OpenVidu.initPublisher} or by calling {@link Publisher.createVideoElement}). For every video removed, the Publisher object will also dispatch a `videoElementDestroyed` event.
|
||||
* - If dispatched by {@link Session} (*other user* has unpublished): automatically unsubscribes the proper Subscriber object from the session (this includes closing the RTCPeerConnection and disposing all MediaStreamTracks)
|
||||
* and also deletes any HTML video element associated to that Subscriber (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement` in method {@link Session.subscribe} or
|
||||
* by calling {@link Subscriber.createVideoElement}). For every video removed, the Subscriber object will also dispatch a `videoElementDestroyed` event.
|
||||
*/
|
||||
preventDefault() {
|
||||
// tslint:disable-next-line:no-empty
|
||||
this.callDefaultBehavior = () => {};
|
||||
this.hasBeenPrevented = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
abstract callDefaultBehavior();
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* All OpenVidu Browser events inherit from this interface
|
||||
*/
|
||||
export interface EventMap {}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { StreamEvent } from '../StreamEvent';
|
||||
import { StreamManagerEventMap } from './StreamManagerEventMap';
|
||||
|
||||
/**
|
||||
* Events dispatched by {@link Publisher} object. Manage event listeners with
|
||||
* {@link Publisher.on}, {@link Publisher.once} and {@link Publisher.off} methods.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```javascript
|
||||
* publisher.on('accessDenied', () => {
|
||||
* console.error('Camera access has been denied!');
|
||||
* }
|
||||
*
|
||||
* publisher.off('accessDenied');
|
||||
* ```
|
||||
*/
|
||||
export interface PublisherEventMap extends StreamManagerEventMap {
|
||||
/**
|
||||
* Event dispatched when the {@link Publisher} has been published to the session (see {@link Session.publish}).
|
||||
*/
|
||||
streamCreated: StreamEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when the {@link Publisher} has been unpublished from the session.
|
||||
*/
|
||||
streamDestroyed: StreamEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when a Publisher tries to access some media input device and has the required permissions to do so.
|
||||
*
|
||||
* This happens when calling {@link OpenVidu.initPublisher} or {@link OpenVidu.initPublisherAsync} and the application
|
||||
* has permissions to use the devices. This usually means the user has accepted the permissions dialog that the
|
||||
* browser will show when trying to access the camera/microphone/screen.
|
||||
*/
|
||||
accessAllowed: never;
|
||||
|
||||
/**
|
||||
* Event dispatched when a Publisher tries to access some media input device and does NOT have the required permissions to do so.
|
||||
*
|
||||
* This happens when calling {@link OpenVidu.initPublisher} or {@link OpenVidu.initPublisherAsync} and the application
|
||||
* lacks the required permissions to use the devices. This usually means the user has NOT accepted the permissions dialog that the
|
||||
* browser will show when trying to access the camera/microphone/screen.
|
||||
*/
|
||||
accessDenied: never;
|
||||
|
||||
/**
|
||||
* Event dispatched when the pop-up shown by the browser to request permissions for the input media devices is opened.
|
||||
*
|
||||
* You can use this event to alert the user about granting permissions for your website. Note that this event is artificially
|
||||
* generated based only on time intervals when accessing media devices. A heavily overloaded client device that simply takes more
|
||||
* than usual to access the media device could produce a false trigger of this event.
|
||||
*/
|
||||
accessDialogOpened: never;
|
||||
|
||||
/**
|
||||
* Event dispatched after the user clicks on "Allow" or "Block" in the pop-up shown by the browser to request permissions
|
||||
* for the input media devices.
|
||||
*
|
||||
* This event can only be triggered after an {@link accessDialogOpened} event has been previously triggered.
|
||||
*/
|
||||
accessDialogClosed: never;
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { EventMap } from './EventMap';
|
||||
import { ConnectionEvent } from '../ConnectionEvent';
|
||||
import { ConnectionPropertyChangedEvent } from '../ConnectionPropertyChangedEvent';
|
||||
import { ExceptionEvent } from '../ExceptionEvent';
|
||||
import { NetworkQualityLevelChangedEvent } from '../NetworkQualityLevelChangedEvent';
|
||||
import { PublisherSpeakingEvent } from '../PublisherSpeakingEvent';
|
||||
import { RecordingEvent } from '../RecordingEvent';
|
||||
import { SessionDisconnectedEvent } from '../SessionDisconnectedEvent';
|
||||
import { SignalEvent } from '../SignalEvent';
|
||||
import { SpeechToTextEvent } from '../SpeechToTextEvent';
|
||||
import { StreamEvent } from '../StreamEvent';
|
||||
import { StreamPropertyChangedEvent } from '../StreamPropertyChangedEvent';
|
||||
|
||||
/**
|
||||
* Events dispatched by {@link Session} object. Manage event listeners with
|
||||
* {@link Session.on}, {@link Session.once} and {@link Session.off} methods.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```javascript
|
||||
* session.on('connectionCreated', (event) => {
|
||||
* console.log('Connection ' + event.connection.connectionId + ' created');
|
||||
* }
|
||||
*
|
||||
* session.off('connectionDestroyed');
|
||||
* ```
|
||||
*/
|
||||
export interface SessionEventMap extends EventMap {
|
||||
/**
|
||||
* Event dispatched when a new user has connected to the session.
|
||||
*
|
||||
* It is fired for both the local user and remote users.
|
||||
*/
|
||||
connectionCreated: ConnectionEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when a remote user has left the session.
|
||||
*
|
||||
* For the local user see {@link sessionDisconnected}.
|
||||
*/
|
||||
connectionDestroyed: ConnectionEvent;
|
||||
|
||||
/**
|
||||
* **This feature is part of OpenVidu
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
|
||||
* and
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
|
||||
* editions**
|
||||
*
|
||||
* Event dispatched when a property of the local {@link Connection} object changes.
|
||||
*
|
||||
* It is fired only for the local user.
|
||||
*
|
||||
* The properties that may change are {@link Connection.role} and {@link Connection.record}.
|
||||
* The only way the Connection properties may change is by updating them through:
|
||||
*
|
||||
* - [API REST](/en/stable/reference-docs/REST-API/#patch-connection)
|
||||
* - [openvidu-java-client](/en/stable/reference-docs/openvidu-java-client/#update-a-connection)
|
||||
* - [openvidu-node-client](/en/stable/reference-docs/openvidu-node-client/#update-a-connection)<br><br>
|
||||
*/
|
||||
connectionPropertyChanged: ConnectionPropertyChangedEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when the local user has left the session.
|
||||
*
|
||||
* For remote users see {@link connectionDestroyed}.
|
||||
*/
|
||||
sessionDisconnected: SessionDisconnectedEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when a user has started publishing media to the session (see {@link Session.publish}).
|
||||
*
|
||||
* It is fired for both the local user and remote users.
|
||||
*/
|
||||
streamCreated: StreamEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when a user stops publishing media to the session.
|
||||
*
|
||||
* It is fired for both the local user and remote users.
|
||||
*/
|
||||
streamDestroyed: StreamEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when a Stream undergoes any change in any of its mutable properties
|
||||
* (see {@link StreamPropertyChangedEvent.changedProperty}).
|
||||
*
|
||||
* It is fired for both remote streams (owned by a {@link Subscriber}) or local streams (owned by a {@link Publisher}).
|
||||
*/
|
||||
streamPropertyChanged: StreamPropertyChangedEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when a user has started speaking.
|
||||
*
|
||||
* It is fired for both the local user and remote users.
|
||||
*
|
||||
* Extra information:
|
||||
* - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true).
|
||||
* - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}.
|
||||
*/
|
||||
publisherStartSpeaking: PublisherSpeakingEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when a user has stopped speaking.
|
||||
*
|
||||
* It is fired for both the local user and remote users.
|
||||
*
|
||||
* Extra information:
|
||||
* - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true).
|
||||
* - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}.
|
||||
*/
|
||||
publisherStopSpeaking: PublisherSpeakingEvent;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
[key: `signal:${string}`]: SignalEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when a signal is received (see [Send text messages between users](/en/stable/cheatsheet/send-messages)).
|
||||
*
|
||||
* If the listener is added as **`signal:TYPE`**, only signals of type **`TYPE`** will trigger the event.
|
||||
*/
|
||||
signal: SignalEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when the session has started being recorded.
|
||||
*
|
||||
* Property **`OPENVIDU_RECORDING_NOTIFICATION`** of [the OpenVidu deployment configuration](/en/stable/reference-docs/openvidu-config/)
|
||||
* defines which users should receive this events (by default, only users with role `PUBLISHER` or `MODERATOR`)
|
||||
*/
|
||||
recordingStarted: RecordingEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when the session has stopped being recorded.
|
||||
*
|
||||
* Property **`OPENVIDU_RECORDING_NOTIFICATION`** of [the OpenVidu deployment configuration](/en/stable/reference-docs/openvidu-config/)
|
||||
* defines which users should receive this events (by default, only users with role `PUBLISHER` or `MODERATOR`)
|
||||
*/
|
||||
recordingStopped: RecordingEvent;
|
||||
|
||||
/**
|
||||
* **This feature is part of OpenVidu
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
|
||||
* and
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
|
||||
* editions**
|
||||
*
|
||||
* Event dispatched when the session has started being broadcasted. See [Broadcast to YouTube/Twitch](/en/stable/advanced-features/broadcast/)
|
||||
*/
|
||||
broadcastStarted: never;
|
||||
|
||||
/**
|
||||
* **This feature is part of OpenVidu
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
|
||||
* and
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
|
||||
* editions**
|
||||
*
|
||||
* Event dispatched when the session has stopped being broadcasted. See [Broadcast to YouTube/Twitch](/en/stable/advanced-features/broadcast/)
|
||||
*/
|
||||
broadcastStopped: never;
|
||||
|
||||
/**
|
||||
* **This feature is part of OpenVidu
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
|
||||
* and
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
|
||||
* editions**
|
||||
*
|
||||
* Event dispatched when the network quality level of a {@link Connection} changes. See [network quality](/en/stable/advanced-features/network-quality/).
|
||||
*/
|
||||
networkQualityLevelChanged: NetworkQualityLevelChangedEvent;
|
||||
|
||||
/**
|
||||
* **This feature is part of OpenVidu
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">PRO</a>
|
||||
* and
|
||||
* <a href="https://docs.openvidu.io/en/stable/openvidu-enterprise/" style="display: inline-block; background-color: rgb(156, 39, 176); color: white; font-weight: bold; padding: 0px 5px; margin: 0 2px 0 2px; border-radius: 3px; font-size: 13px; line-height:21px; text-decoration: none; font-family: Montserrat, sans-serif">ENTERPRISE</a>
|
||||
* editions**
|
||||
*
|
||||
* Event dispatched when a speech-to-text message has been received for certain Stream. See [Speech To Text](/en/stable/advanced-features/speech-to-text/).
|
||||
*/
|
||||
speechToTextMessage: SpeechToTextEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when the local user has lost its connection to the session, and starts the automatic reconnection process.
|
||||
*
|
||||
* See [Reconnection events](/en/stable/advanced-features/automatic-reconnection/#reconnection-events).
|
||||
*/
|
||||
reconnecting: never;
|
||||
|
||||
/**
|
||||
* Event dispatched when the local user has successfully recovered its connection to the session after losing it.
|
||||
*
|
||||
* If the connection was recovered but OpenVidu Server already evicted the user due to timeout, then this event will
|
||||
* not be dispatched. A {@link sessionDisconnected} event with reason `networkDisconnect` will be triggered instead.
|
||||
*
|
||||
* See [Reconnection events](/en/stable/advanced-features/automatic-reconnection/#reconnection-events).
|
||||
*/
|
||||
reconnected: never;
|
||||
|
||||
/**
|
||||
* This event acts as a global handler for asynchronous errors that may be triggered for multiple reasons and from multiple origins.
|
||||
* To see the different types of exceptions go to {@link ExceptionEventName}.
|
||||
*/
|
||||
exception: ExceptionEvent;
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { EventMap } from './EventMap';
|
||||
import { PublisherSpeakingEvent } from '../PublisherSpeakingEvent';
|
||||
import { StreamManagerEvent } from '../StreamManagerEvent';
|
||||
import { StreamPropertyChangedEvent } from '../StreamPropertyChangedEvent';
|
||||
import { VideoElementEvent } from '../VideoElementEvent';
|
||||
|
||||
/**
|
||||
* Events dispatched by {@link StreamManager} object. Manage event listeners with
|
||||
* {@link StreamManager.on}, {@link StreamManager.once} and {@link StreamManager.off} methods.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```javascript
|
||||
* streamManager.on('videoElementCreated', (event) => {
|
||||
* console.log('New video element created:', event.element);
|
||||
* }
|
||||
*
|
||||
* streamManager.off('videoElementCreated');
|
||||
* ```
|
||||
*/
|
||||
export interface StreamManagerEventMap extends EventMap {
|
||||
/**
|
||||
* Event dispatched when a new HTML video element has been inserted into DOM by OpenVidu Browser library. See
|
||||
* [Manage video players](/en/stable/cheatsheet/manage-videos) section.
|
||||
*/
|
||||
videoElementCreated: VideoElementEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when an HTML video element has been removed from DOM by OpenVidu Browser library. See
|
||||
* [Manage video players](/en/stable/cheatsheet/manage-videos) section.
|
||||
*/
|
||||
videoElementDestroyed: VideoElementEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when the media stream starts playing (one of its videos has media and has begun to play).
|
||||
* This event will be dispatched when these 3 conditions are met:
|
||||
* 1. The StreamManager has no video associated in the DOM.
|
||||
* 2. It is associated to one video.
|
||||
* 3. That video starts playing. Internally the expected Web API event is [HTMLMediaElement.canplay](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event).
|
||||
*/
|
||||
streamPlaying: StreamManagerEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when the volume of the media stream's audio track changes. Only applies if {@link Stream.hasAudio} is `true`.
|
||||
* The frequency this event is fired with is defined by property `interval` of
|
||||
* {@link OpenViduAdvancedConfiguration.publisherSpeakingEventsOptions} (default 100ms)
|
||||
*/
|
||||
streamAudioVolumeChange: StreamManagerEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when a Stream undergoes any change in any of its mutable properties
|
||||
* (see {@link StreamPropertyChangedEvent.changedProperty}).
|
||||
*/
|
||||
streamPropertyChanged: StreamPropertyChangedEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when the user owning the stream has started speaking.
|
||||
*
|
||||
* Extra information:
|
||||
* - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true).
|
||||
* - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}.
|
||||
*/
|
||||
publisherStartSpeaking: PublisherSpeakingEvent;
|
||||
|
||||
/**
|
||||
* Event dispatched when the user owning the stream has stopped speaking.
|
||||
*
|
||||
* Extra information:
|
||||
* - This event will only be triggered for **streams that have audio tracks** ({@link Stream.hasAudio} must be true).
|
||||
* - Further configuration can be applied on how the event is dispatched by setting property `publisherSpeakingEventsOptions` in the call of {@link OpenVidu.setAdvancedConfiguration}.
|
||||
*/
|
||||
publisherStopSpeaking: PublisherSpeakingEvent;
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { Stream } from '../../OpenVidu/Stream';
|
||||
import { Subscriber } from '../../OpenVidu/Subscriber';
|
||||
import { Event } from './Event';
|
||||
|
||||
/**
|
||||
* Defines property {@link ExceptionEvent.name}
|
||||
*/
|
||||
export enum ExceptionEventName {
|
||||
/**
|
||||
* There was an unexpected error on the server-side processing an ICE candidate generated and sent by the client-side.
|
||||
*
|
||||
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Session} object.
|
||||
*/
|
||||
ICE_CANDIDATE_ERROR = 'ICE_CANDIDATE_ERROR',
|
||||
|
||||
/**
|
||||
* The [ICE connection state](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState)
|
||||
* of an [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) reached `failed` status.
|
||||
*
|
||||
* This is a terminal error that won't have any kind of possible recovery. If the client is still connected to OpenVidu Server,
|
||||
* then an automatic reconnection process of the media stream is immediately performed. If the ICE connection has broken due to
|
||||
* a total network drop, then no automatic reconnection process will be possible.
|
||||
*
|
||||
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Stream} object.
|
||||
*/
|
||||
ICE_CONNECTION_FAILED = 'ICE_CONNECTION_FAILED',
|
||||
|
||||
/**
|
||||
* The [ICE connection state](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState)
|
||||
* of an [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) reached `disconnected` status.
|
||||
*
|
||||
* This is not a terminal error, and it is possible for the ICE connection to be reconnected. If the client is still connected to
|
||||
* OpenVidu Server and after certain timeout the ICE connection has not reached a success or terminal status, then an automatic
|
||||
* reconnection process of the media stream is performed. If the ICE connection has broken due to a total network drop, then no
|
||||
* automatic reconnection process will be possible.
|
||||
*
|
||||
* You can customize the timeout for the reconnection attempt with property {@link OpenViduAdvancedConfiguration.iceConnectionDisconnectedExceptionTimeout},
|
||||
* which by default is 4000 milliseconds.
|
||||
*
|
||||
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Stream} object.
|
||||
*/
|
||||
ICE_CONNECTION_DISCONNECTED = 'ICE_CONNECTION_DISCONNECTED',
|
||||
|
||||
/**
|
||||
* A {@link Subscriber} object has not fired event `streamPlaying` after certain timeout. `streamPlaying` event belongs to {@link StreamManagerEvent}
|
||||
* category. It wraps Web API native event [canplay](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event).
|
||||
*
|
||||
* OpenVidu Browser can take care of the video players (see [here](/en/stable/cheatsheet/manage-videos/#let-openvidu-take-care-of-the-video-players)),
|
||||
* or you can take care of video players on your own (see [here](/en/stable/cheatsheet/manage-videos/#you-take-care-of-the-video-players)).
|
||||
* Either way, whenever a {@link Subscriber} object is commanded to attach its {@link Stream} to a video element, it is supposed to fire `streamPlaying`
|
||||
* event shortly after. If it does not, then we can safely assume that something wrong has happened while playing the remote video and the
|
||||
* application may be notified through this specific ExceptionEvent.
|
||||
*
|
||||
* The timeout can be configured with property {@link OpenViduAdvancedConfiguration.noStreamPlayingEventExceptionTimeout}. By default it is 4000 milliseconds.
|
||||
*
|
||||
* This is just an informative exception. It only means that a remote Stream that is supposed to be playing by a video player has not done so
|
||||
* in a reasonable time. But the lack of the event can be caused by multiple reasons. If a Subscriber is not playing its Stream, the origin
|
||||
* of the problem could be located at the Publisher side. Or may be caused by a transient network problem. But it also could be a problem with
|
||||
* autoplay permissions. Bottom line, the cause can be very varied, and depending on the application the lack of the event could even be expected.
|
||||
*
|
||||
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Subscriber} object.
|
||||
*/
|
||||
NO_STREAM_PLAYING_EVENT = 'NO_STREAM_PLAYING_EVENT',
|
||||
|
||||
/**
|
||||
* There has been a server-side disconnection of the Speech To Text module. From the moment this exception is fired to the moment method
|
||||
* {@link Session.subscribeToSpeechToText} is called again, the transcription of the audio stream will not be available and no {@link SpeechToTextEvent}
|
||||
* will be fired.
|
||||
*
|
||||
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Session} object.
|
||||
*/
|
||||
SPEECH_TO_TEXT_DISCONNECTED = 'SPEECH_TO_TEXT_DISCONNECTED',
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered by {@link SessionEventMap.exception}
|
||||
*/
|
||||
export class ExceptionEvent extends Event {
|
||||
/**
|
||||
* Name of the exception
|
||||
*/
|
||||
name: ExceptionEventName;
|
||||
|
||||
/**
|
||||
* Object affected by the exception. Depending on the {@link ExceptionEvent.name} property:
|
||||
* - {@link Session}: `ICE_CANDIDATE_ERROR`
|
||||
* - {@link Stream}: `ICE_CONNECTION_FAILED`, `ICE_CONNECTION_DISCONNECTED`
|
||||
* - {@link Subscriber}: `NO_STREAM_PLAYING_EVENT`
|
||||
*/
|
||||
origin: Session | Stream | Subscriber;
|
||||
|
||||
/**
|
||||
* Informative description of the exception
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* Any extra information associated to the exception
|
||||
*/
|
||||
data?: any;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(session: Session, name: ExceptionEventName, origin: Session | Stream | Subscriber, message: string, data?: any) {
|
||||
super(false, session, 'exception');
|
||||
this.name = name;
|
||||
this.origin = origin;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Filter } from '../../OpenVidu/Filter';
|
||||
|
||||
/**
|
||||
* Defines every event dispatched by audio/video stream filters. You can subscribe to filter events by calling {@link Filter.addEventListener}
|
||||
*/
|
||||
export class FilterEvent extends Event {
|
||||
/**
|
||||
* Data of the event
|
||||
*/
|
||||
data: Object;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Filter, eventType: string, data: Object) {
|
||||
super(false, target, eventType);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() {}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
|
||||
/**
|
||||
* Triggered by {@link SessionEventMap.networkQualityLevelChanged}
|
||||
*/
|
||||
export class NetworkQualityLevelChangedEvent extends Event {
|
||||
/**
|
||||
* New value of the network quality level
|
||||
*/
|
||||
newValue: number;
|
||||
|
||||
/**
|
||||
* Old value of the network quality level
|
||||
*/
|
||||
oldValue: number;
|
||||
|
||||
/**
|
||||
* Connection for whom the network quality level changed
|
||||
*/
|
||||
connection: Connection;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, newValue: number, oldValue: number, connection: Connection) {
|
||||
super(false, target, 'networkQualityLevelChanged');
|
||||
this.newValue = newValue;
|
||||
this.oldValue = oldValue;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() {}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { StreamManager } from '../../OpenVidu/StreamManager';
|
||||
|
||||
/**
|
||||
* Triggered by:
|
||||
* - `publisherStartSpeaking` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#publisherStartSpeaking) and [StreamManager](/en/stable/api/openvidu-browser/interfaces/StreamManagerEventMap.html#publisherStartSpeaking) objects)
|
||||
* - `publisherStopSpeaking` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#publisherStopSpeaking) and [StreamManager](/en/stable/api/openvidu-browser/interfaces/StreamManagerEventMap.html#publisherStopSpeaking) objects)
|
||||
*/
|
||||
export class PublisherSpeakingEvent extends Event {
|
||||
/**
|
||||
* The client that started or stopped speaking
|
||||
*/
|
||||
connection: Connection;
|
||||
|
||||
/**
|
||||
* The streamId of the Stream affected by the speaking event
|
||||
*/
|
||||
streamId: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session | StreamManager, type: string, connection: Connection, streamId: string) {
|
||||
super(false, target, type);
|
||||
this.type = type;
|
||||
this.connection = connection;
|
||||
this.streamId = streamId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() {}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { RecordingEventReason } from './Types/Types';
|
||||
|
||||
/**
|
||||
* Triggered by:
|
||||
* - {@link SessionEventMap.recordingStarted}
|
||||
* - {@link SessionEventMap.recordingStopped}
|
||||
*/
|
||||
export class RecordingEvent extends Event {
|
||||
/**
|
||||
* The recording ID generated in openvidu-server
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The recording name you supplied to openvidu-server. For example, to name your recording file MY_RECORDING:
|
||||
* - With **API REST**: POST to `/api/recordings/start` passing JSON body `{"session":"sessionId","name":"MY_RECORDING"}`
|
||||
* - With **openvidu-java-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, new RecordingProperties.Builder().name("MY_RECORDING").build())`
|
||||
* - With **openvidu-node-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, {name: "MY_RECORDING"})`
|
||||
*
|
||||
* If no name is supplied, this property will be undefined and the recorded file will be named after property {@link id}
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* For 'recordingStopped' event:
|
||||
* - "recordingStoppedByServer": the recording has been gracefully stopped by the application
|
||||
* - "sessionClosedByServer": the Session has been closed by the application
|
||||
* - "automaticStop": see [Automatic stop of recordings](/en/stable/advanced-features/recording/#automatic-stop-of-recordings)
|
||||
* - "nodeCrashed": a node has crashed in the server side
|
||||
*
|
||||
* For 'recordingStarted' empty string
|
||||
*/
|
||||
reason?: RecordingEventReason;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, type: string, id: string, name: string, reason?: RecordingEventReason) {
|
||||
super(false, target, type);
|
||||
this.id = id;
|
||||
if (name !== id) {
|
||||
this.name = name;
|
||||
}
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { OpenViduLogger } from '../Logger/OpenViduLogger';
|
||||
import { ConnectionEventReason } from './Types/Types';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* Triggered by {@link SessionEventMap.sessionDisconnected}
|
||||
*/
|
||||
export class SessionDisconnectedEvent extends Event {
|
||||
/**
|
||||
* - "disconnect": you have called `Session.disconnect()`
|
||||
* - "forceDisconnectByUser": you have been evicted from the Session by other user calling `Session.forceDisconnect()`
|
||||
* - "forceDisconnectByServer": you have been evicted from the Session by the application
|
||||
* - "sessionClosedByServer": the Session has been closed by the application
|
||||
* - "networkDisconnect": your network connection has dropped. Before a SessionDisconnectedEvent with this reason is triggered,
|
||||
* Session object will always have previously dispatched a `reconnecting` event. If the reconnection process succeeds,
|
||||
* Session object will dispatch a `reconnected` event. If it fails, Session object will dispatch a SessionDisconnectedEvent
|
||||
* with reason "networkDisconnect"
|
||||
* - "nodeCrashed": a node has crashed in the server side. You can use this reason to ask your application's backend to reconnect
|
||||
* to a new session to replace the crashed one
|
||||
*/
|
||||
reason: ConnectionEventReason;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, reason: ConnectionEventReason) {
|
||||
super(true, target, 'sessionDisconnected');
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
callDefaultBehavior() {
|
||||
logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Session'");
|
||||
|
||||
const session = <Session>this.target;
|
||||
|
||||
// Dispose and delete all remote Connections
|
||||
session.remoteConnections.forEach((remoteConnection) => {
|
||||
const connectionId = remoteConnection.connectionId;
|
||||
if (!!session.remoteConnections.get(connectionId)?.stream) {
|
||||
session.remoteConnections.get(connectionId)?.stream!.disposeWebRtcPeer();
|
||||
session.remoteConnections.get(connectionId)?.stream!.disposeMediaStream();
|
||||
if (session.remoteConnections.get(connectionId)?.stream!.streamManager) {
|
||||
session.remoteConnections.get(connectionId)?.stream!.streamManager.removeAllVideos();
|
||||
}
|
||||
const streamId = session.remoteConnections.get(connectionId)?.stream?.streamId;
|
||||
if (!!streamId) {
|
||||
session.remoteStreamsCreated.delete(streamId);
|
||||
}
|
||||
session.remoteConnections.get(connectionId)?.dispose();
|
||||
}
|
||||
session.remoteConnections.delete(connectionId);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
|
||||
/**
|
||||
* Triggered by {@link SessionEventMap.signal}
|
||||
*/
|
||||
export class SignalEvent extends Event {
|
||||
/**
|
||||
* The type of signal. It is string `"signal"` for those signals sent with no {@link SignalOptions.type} property, and `"signal:type"` if was sent with a
|
||||
* valid {@link SignalOptions.type} property.
|
||||
*
|
||||
* The client must be specifically subscribed to `Session.on('signal:type', function(signalEvent) {...})` to trigger that type of signal.
|
||||
*
|
||||
* Subscribing to `Session.on('signal', function(signalEvent) {...})` will trigger all signals, no matter their type.
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* The message of the signal (can be empty)
|
||||
*/
|
||||
data?: string;
|
||||
|
||||
/**
|
||||
* The client that sent the signal. This property is undefined if the signal
|
||||
* was directly generated by the application server (not by other client)
|
||||
*/
|
||||
from?: Connection;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, type?: string, data?: string, from?: Connection) {
|
||||
super(false, target, 'signal');
|
||||
if (!!type) {
|
||||
this.type = 'signal:' + type;
|
||||
}
|
||||
this.data = data;
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() {}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { SpeechToTextEventReason } from './Types/Types';
|
||||
|
||||
/**
|
||||
* Triggered by {@link SessionEventMap.speechToTextMessage}
|
||||
*/
|
||||
export class SpeechToTextEvent extends Event {
|
||||
|
||||
/**
|
||||
* The {@link Connection} owning the Stream that produced the speech-to-text event.
|
||||
* In other words, this is the participant that spoke and produced this transcription event.
|
||||
*/
|
||||
connection: Connection;
|
||||
|
||||
/**
|
||||
* The text of the event. This is the transcription for this specific piece of audio stream
|
||||
*/
|
||||
text: string;
|
||||
|
||||
/**
|
||||
* All speech-to-text events are generated
|
||||
*/
|
||||
reason: SpeechToTextEventReason;
|
||||
|
||||
/**
|
||||
* The original event from the speech to text engine. This can vary depending on the engine
|
||||
*/
|
||||
raw: string;
|
||||
|
||||
/**
|
||||
* [BCP-47](https://tools.ietf.org/html/bcp47) language tag (like "en-US" or "es-ES") of the recognized text. This will be the same as the language provided
|
||||
* in method {@link Session.subscribeToSpeechToText} method
|
||||
*/
|
||||
lang: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, connection: Connection, text: string, reason: SpeechToTextEventReason, raw: string, lang: string) {
|
||||
super(false, target, 'speechToTextMessage');
|
||||
this.connection = connection;
|
||||
this.text = text;
|
||||
this.reason = reason;
|
||||
this.raw = raw;
|
||||
this.lang = lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Publisher } from '../../OpenVidu/Publisher';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { Stream } from '../../OpenVidu/Stream';
|
||||
import { OpenViduLogger } from '../Logger/OpenViduLogger';
|
||||
import { StreamEventReason } from './Types/Types';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* Triggered by:
|
||||
* - `streamCreated` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#streamCreated) and [Publisher](/en/stable/api/openvidu-browser/interfaces/PublisherEventMap.html#streamCreated) objects)
|
||||
* - `streamDestroyed` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#streamDestroyed) and [Publisher](/en/stable/api/openvidu-browser/interfaces/PublisherEventMap.html#streamDestroyed) objects)
|
||||
*/
|
||||
export class StreamEvent extends Event {
|
||||
/**
|
||||
* Stream object that was created or destroyed
|
||||
*/
|
||||
stream: Stream;
|
||||
|
||||
/**
|
||||
* For 'streamDestroyed' event:
|
||||
* - "unpublish": method `Session.unpublish()` has been called
|
||||
* - "disconnect": method `Session.disconnect()` has been called
|
||||
* - "forceUnpublishByUser": some user has called `Session.forceUnpublish()` over the Stream
|
||||
* - "forceDisconnectByUser": some user has called `Session.forceDisconnect()` over the Stream
|
||||
* - "forceUnpublishByServer": the user's stream has been unpublished from the Session by the application
|
||||
* - "forceDisconnectByServer": the user has been evicted from the Session by the application
|
||||
* - "sessionClosedByServer": the Session has been closed by the application
|
||||
* - "networkDisconnect": the user's network connection has dropped
|
||||
* - "nodeCrashed": a node has crashed in the server side
|
||||
*
|
||||
* For 'streamCreated' empty string
|
||||
*/
|
||||
reason: StreamEventReason;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(cancelable: boolean, target: Session | Publisher, type: string, stream: Stream, reason: StreamEventReason) {
|
||||
super(cancelable, target, type);
|
||||
this.stream = stream;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
callDefaultBehavior() {
|
||||
if (this.type === 'streamDestroyed') {
|
||||
if (this.target instanceof Session) {
|
||||
// Remote Stream
|
||||
logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Session'");
|
||||
this.stream.disposeWebRtcPeer();
|
||||
} else if (this.target instanceof Publisher) {
|
||||
// Local Stream
|
||||
logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Publisher'");
|
||||
clearInterval((<Publisher>this.target).screenShareResizeInterval);
|
||||
this.stream.isLocalStreamReadyToPublish = false;
|
||||
|
||||
// Delete Publisher object from OpenVidu publishers array
|
||||
const openviduPublishers = (<Publisher>this.target).openvidu.publishers;
|
||||
for (let i = 0; i < openviduPublishers.length; i++) {
|
||||
if (openviduPublishers[i] === <Publisher>this.target) {
|
||||
openviduPublishers.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispose the MediaStream local object
|
||||
this.stream.disposeMediaStream();
|
||||
|
||||
// Remove from DOM all video elements associated to this Stream, if there's a StreamManager defined
|
||||
// (method Session.subscribe must have been called)
|
||||
if (this.stream.streamManager) this.stream.streamManager.removeAllVideos();
|
||||
|
||||
// Delete stream from Session.remoteStreamsCreated map
|
||||
this.stream.session.remoteStreamsCreated.delete(this.stream.streamId);
|
||||
|
||||
// Delete StreamOptionsServer from remote Connection
|
||||
const remoteConnection = this.stream.session.remoteConnections.get(this.stream.connection.connectionId);
|
||||
if (!!remoteConnection && !!remoteConnection.remoteOptions) {
|
||||
const streamOptionsServer = remoteConnection.remoteOptions.streams;
|
||||
for (let i = streamOptionsServer.length - 1; i >= 0; --i) {
|
||||
if (streamOptionsServer[i].id === this.stream.streamId) {
|
||||
streamOptionsServer.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { StreamManager } from '../../OpenVidu/StreamManager';
|
||||
|
||||
/**
|
||||
* Triggered by:
|
||||
* - {@link StreamManagerEventMap.streamPlaying}
|
||||
* - {@link StreamManagerEventMap.streamAudioVolumeChange}
|
||||
*/
|
||||
export class StreamManagerEvent extends Event {
|
||||
/**
|
||||
* For `streamAudioVolumeChange` event:
|
||||
* - `{newValue: number, oldValue: number}`: new and old audio volume values. These values are between -100 (silence) and 0 (loudest possible volume).
|
||||
* They are not exact and depend on how the browser is managing the audio track, but -100 and 0 can be taken as limit values.
|
||||
*
|
||||
* For `streamPlaying` event undefined
|
||||
*/
|
||||
value: Object | undefined;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: StreamManager, type: string, value: Object | undefined) {
|
||||
super(false, target, type);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() {}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { Stream } from '../../OpenVidu/Stream';
|
||||
import { StreamManager } from '../../OpenVidu/StreamManager';
|
||||
import { StreamPropertyChangedEventReason, ChangedPropertyType } from './Types/Types';
|
||||
|
||||
/**
|
||||
* Triggered by `streamPropertyChanged` (available for [Session](/en/stable/api/openvidu-browser/interfaces/SessionEventMap.html#streamPropertyChanged) and [StreamManager](/en/stable/api/openvidu-browser/interfaces/StreamManagerEventMap.html#streamPropertyChanged) objects)
|
||||
*/
|
||||
export class StreamPropertyChangedEvent extends Event {
|
||||
/**
|
||||
* The Stream whose property has changed. You can always identify the user publishing the changed stream by consulting property {@link Stream.connection}
|
||||
*/
|
||||
stream: Stream;
|
||||
|
||||
/**
|
||||
* The property of the stream that changed. This value is either `"videoActive"`, `"audioActive"`, `"videoTrack"`, `"audioTrack"`, `"videoDimensions"` or `"filter"`
|
||||
*/
|
||||
changedProperty: ChangedPropertyType;
|
||||
|
||||
/**
|
||||
* Cause of the change on the stream's property:
|
||||
* - For `videoActive`: `"publishVideo"`
|
||||
* - For `audioActive`: `"publishAudio"`
|
||||
* - For `videoTrack`: `"trackReplaced"`
|
||||
* - For `audioTrack`: `"trackReplaced"`
|
||||
* - For `videoDimensions`: `"deviceRotated"`, `"screenResized"` or `"trackReplaced"`
|
||||
* - For `filter`: `"applyFilter"`, `"execFilterMethod"` or `"removeFilter"`
|
||||
*/
|
||||
reason: StreamPropertyChangedEventReason;
|
||||
|
||||
/**
|
||||
* New value of the property (after change, current value)
|
||||
*/
|
||||
newValue: Object;
|
||||
|
||||
/**
|
||||
* Previous value of the property (before change)
|
||||
*/
|
||||
oldValue: Object;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(
|
||||
target: Session | StreamManager,
|
||||
stream: Stream,
|
||||
changedProperty: ChangedPropertyType,
|
||||
newValue: Object,
|
||||
oldValue: Object,
|
||||
reason: StreamPropertyChangedEventReason
|
||||
) {
|
||||
super(false, target, 'streamPropertyChanged');
|
||||
this.stream = stream;
|
||||
this.changedProperty = changedProperty;
|
||||
this.newValue = newValue;
|
||||
this.oldValue = oldValue;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
export type ChangedPropertyType =
|
||||
'videoActive' |
|
||||
'audioActive' |
|
||||
'videoTrack' |
|
||||
'audioTrack' |
|
||||
'videoDimensions' |
|
||||
'filter';
|
||||
|
||||
export type StreamPropertyChangedEventReason =
|
||||
'publishVideo' |
|
||||
'publishAudio' |
|
||||
'trackReplaced' |
|
||||
'deviceRotated' |
|
||||
'screenResized' |
|
||||
'applyFilter' |
|
||||
'execFilterMethod' |
|
||||
'removeFilter';
|
||||
|
||||
export type ConnectionEventReason =
|
||||
'disconnect' |
|
||||
'forceDisconnectByUser' |
|
||||
'forceDisconnectByServer' |
|
||||
'sessionClosedByServer' |
|
||||
'networkDisconnect' |
|
||||
'nodeCrashed' |
|
||||
'';
|
||||
|
||||
export type StreamEventReason =
|
||||
ConnectionEventReason |
|
||||
'unpublish' |
|
||||
'forceUnpublishByUser' |
|
||||
'forceUnpublishByServer';
|
||||
|
||||
export type RecordingEventReason =
|
||||
'recordingStoppedByServer' |
|
||||
'sessionClosedByServer' |
|
||||
'automaticStop' |
|
||||
'nodeCrashed';
|
||||
|
||||
export type SpeechToTextEventReason =
|
||||
'recognizing' |
|
||||
'recognized';
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { StreamManager } from '../../OpenVidu/StreamManager';
|
||||
|
||||
/**
|
||||
* Triggered by:
|
||||
* - {@link StreamManagerEventMap.videoElementCreated}
|
||||
* - {@link StreamManagerEventMap.videoElementDestroyed}
|
||||
*/
|
||||
export class VideoElementEvent extends Event {
|
||||
/**
|
||||
* Video element that was created or destroyed
|
||||
*/
|
||||
element: HTMLVideoElement;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(element: HTMLVideoElement, target: StreamManager, type: string) {
|
||||
super(false, target, type);
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() {}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
export interface CustomMediaStreamConstraints {
|
||||
constraints: MediaStreamConstraints;
|
||||
audioTrack: MediaStreamTrack | undefined;
|
||||
videoTrack: MediaStreamTrack | undefined;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
export interface IceServerProperties {
|
||||
url: string;
|
||||
username?: string;
|
||||
credential?: string;
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Connection } from '../../../OpenVidu/Connection';
|
||||
import { Filter } from '../../../OpenVidu/Filter';
|
||||
import { TypeOfVideo } from '../../Enums/TypeOfVideo';
|
||||
|
||||
export interface InboundStreamOptions {
|
||||
id: string;
|
||||
createdAt: number;
|
||||
connection: Connection;
|
||||
hasAudio: boolean;
|
||||
hasVideo: boolean;
|
||||
audioActive: boolean;
|
||||
videoActive: boolean;
|
||||
typeOfVideo: TypeOfVideo;
|
||||
frameRate: number;
|
||||
videoDimensions: { width: number; height: number };
|
||||
filter?: Filter;
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { RemoteConnectionOptions } from './RemoteConnectionOptions';
|
||||
import { IceServerProperties } from './IceServerProperties';
|
||||
|
||||
export interface LocalConnectionOptions {
|
||||
id: string;
|
||||
finalUserId: string;
|
||||
createdAt: number;
|
||||
metadata: string;
|
||||
value: RemoteConnectionOptions[];
|
||||
session: string; // OpenVidu Session identifier
|
||||
sessionId: string; // JSON-RPC session identifier
|
||||
role: string;
|
||||
record: boolean;
|
||||
coturnIp: string;
|
||||
coturnPort: number;
|
||||
turnUsername: string;
|
||||
turnCredential: string;
|
||||
version: string;
|
||||
mediaServer: string;
|
||||
videoSimulcast: boolean;
|
||||
life: number;
|
||||
customIceServers?: IceServerProperties[];
|
||||
recordingId?: string; // Defined if the session is being recorded and the client must be notified
|
||||
recordingName?: string; // Defined if the session is being recorded and the client must be notified
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { PublisherProperties } from '../Public/PublisherProperties';
|
||||
|
||||
export interface OutboundStreamOptions {
|
||||
publisherProperties: PublisherProperties;
|
||||
mediaConstraints: MediaStreamConstraints;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { StreamOptionsServer } from './StreamOptionsServer';
|
||||
|
||||
export interface RemoteConnectionOptions {
|
||||
id: string;
|
||||
createdAt: number;
|
||||
metadata: string;
|
||||
streams: StreamOptionsServer[];
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
export interface SessionOptions {
|
||||
sessionId: string;
|
||||
participantId: string;
|
||||
metadata: string;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Connection } from '../../../OpenVidu/Connection';
|
||||
|
||||
export interface SignalOptions {
|
||||
type?: string;
|
||||
to?: Connection[];
|
||||
data?: string;
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Filter } from '../../../OpenVidu/Filter';
|
||||
import { TypeOfVideo } from '../../Enums/TypeOfVideo';
|
||||
|
||||
export interface StreamOptionsServer {
|
||||
id: string;
|
||||
createdAt: number;
|
||||
hasAudio: boolean;
|
||||
hasVideo: boolean;
|
||||
audioActive: boolean;
|
||||
videoActive: boolean;
|
||||
typeOfVideo: TypeOfVideo;
|
||||
frameRate: number;
|
||||
videoDimensions: string;
|
||||
filter: Filter;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* See {@link Session.capabilities}
|
||||
*/
|
||||
export interface Capabilities {
|
||||
/**
|
||||
* `true` if the client can call {@link Session.forceDisconnect}, `false` if not
|
||||
*/
|
||||
forceDisconnect: boolean;
|
||||
|
||||
/**
|
||||
* `true` if the client can call {@link Session.forceUnpublish}, `false` if not
|
||||
*/
|
||||
forceUnpublish: boolean;
|
||||
|
||||
/**
|
||||
* `true` if the client can call {@link Session.publish}, `false` if not
|
||||
*/
|
||||
publish: boolean;
|
||||
|
||||
/**
|
||||
* `true` if the client can call {@link Session.subscribe}, `false` if not (true for every user for now)
|
||||
*/
|
||||
subscribe: boolean;
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* See {@link OpenVidu.getDevices}
|
||||
*/
|
||||
export interface Device {
|
||||
/**
|
||||
* The kind of device
|
||||
*/
|
||||
kind: 'videoinput' | 'audioinput';
|
||||
|
||||
/**
|
||||
* Unique ID for the device. Use it on `audioSource` or `videoSource` properties of {@link PublisherProperties}
|
||||
*/
|
||||
deviceId: string;
|
||||
|
||||
/**
|
||||
* Description of the device. An empty string if the user hasn't granted permissions to the site to access the device
|
||||
*/
|
||||
label: string;
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* See {@link OpenVidu.setAdvancedConfiguration}
|
||||
*/
|
||||
export interface OpenViduAdvancedConfiguration {
|
||||
/**
|
||||
* Array of [RTCIceServer](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer) to be used by OpenVidu Browser. By default OpenVidu will generate the required credentials to use the COTURN server hosted along OpenVidu Server.
|
||||
* You can also set this property to string 'freeice' to force the use of free STUN servers instead (got thanks to [freeice](https://github.com/DamonOehlman/freeice) library).
|
||||
*
|
||||
* > **WARNING**: this value has priority over the standard `iceServers` property of {@link rtcConfiguration}. It will override any value in `OpenViduAdvancedConfiguration.rtcConfiguration.iceServers`
|
||||
*/
|
||||
iceServers?: RTCIceServer[] | string;
|
||||
|
||||
/**
|
||||
* Custom configuration for all [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#parameters) objects. This object will be passed as is to all RTCPeerConnection constructor (all Publishers and Subscribers).
|
||||
*/
|
||||
rtcConfiguration?: RTCConfiguration;
|
||||
|
||||
/**
|
||||
* URL to a custom screen share extension for Chrome (always based on ours: [openvidu-screen-sharing-chrome-extension](https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension)) to be used instead of the default one.
|
||||
* Must be something like this: `https://chrome.google.com/webstore/detail/YOUR_WEBSTORE_EXTENSION_NAME/YOUR_EXTENSION_ID`
|
||||
*/
|
||||
screenShareChromeExtension?: string;
|
||||
|
||||
/**
|
||||
* Custom configuration for the {@link PublisherSpeakingEvent} feature and the [StreamManagerEvent.streamAudioVolumeChange](/en/stable/api/openvidu-browser/classes/StreamManagerEvent.html) feature. It is an object which includes the following optional properties:
|
||||
* - `interval`: (number) how frequently the analyser polls the audio stream to check if speaking has started/stopped or audio volume has changed. Default **100** (ms)
|
||||
* - `threshold`: (number) the volume at which _publisherStartSpeaking_ and _publisherStopSpeaking_ events will be fired. Default **-50** (dB)
|
||||
*
|
||||
* This sets the global default configuration that will affect all streams, but you can later customize these values for each specific stream by calling {@link StreamManager.updatePublisherSpeakingEventsOptions}
|
||||
*/
|
||||
publisherSpeakingEventsOptions?: {
|
||||
interval?: number;
|
||||
threshold?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines the automatic reconnection process policy. Whenever the client's network drops, OpenVidu Browser starts a reconnection process with OpenVidu Server. After network is recovered, OpenVidu Browser automatically
|
||||
* inspects all of its media streams to see their status. For any of them that are broken, it asks OpenVidu Server for a forced and silent reconnection.
|
||||
*
|
||||
* This policy is technically enough to recover any broken media connection after a network drop, but in practice it has been proven that OpenVidu Browser may think a media connection has properly recovered when in fact it has not.
|
||||
* This is not a common case, but it may occur. This property allows **forcing OpenVidu Browser to reconnect all of its outgoing and incoming media streams** after a network drop regardless of their supposed status.
|
||||
*
|
||||
* Default to `false`.
|
||||
*/
|
||||
forceMediaReconnectionAfterNetworkDrop?: boolean;
|
||||
|
||||
/**
|
||||
* The milliseconds that must elapse after triggering {@link ExceptionEvent} of name [`ICE_CONNECTION_DISCONNECTED`](/en/stable/api/openvidu-browser/enums/ExceptionEventName.html#ICE_CONNECTION_DISCONNECTED) to perform an automatic reconnection process of the affected media stream.
|
||||
* This automatic reconnection process can only take place if the client still has network connection to OpenVidu Server. If the ICE connection has broken because of a total network drop,
|
||||
* then no reconnection process will be possible at all.
|
||||
*
|
||||
* Default to `4000`.
|
||||
*/
|
||||
iceConnectionDisconnectedExceptionTimeout?: number;
|
||||
|
||||
/**
|
||||
* The milliseconds that must elapse for the {@link ExceptionEvent} of name [`NO_STREAM_PLAYING_EVENT`](/en/stable/api/openvidu-browser/enums/ExceptionEventName.html#NO_STREAM_PLAYING_EVENT) to be fired.
|
||||
*
|
||||
* Default to `4000`.
|
||||
*/
|
||||
noStreamPlayingEventExceptionTimeout?: number;
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Filter } from '../../../OpenVidu/Filter';
|
||||
import { VideoInsertMode } from '../../Enums/VideoInsertMode';
|
||||
|
||||
/**
|
||||
* See {@link OpenVidu.initPublisher}
|
||||
*/
|
||||
export interface PublisherProperties {
|
||||
/**
|
||||
* Which device should provide the audio source. Can be:
|
||||
* - Property `deviceId` of a {@link Device}
|
||||
* - `"screen"` to share the screen audio when {@link videoSource} is set to `"screen"`. If {@link videoSource} is not set to `"screen"` this will result in no audio source and a video-only publisher.
|
||||
* - A MediaStreamTrack obtained from a MediaStream object with {@link OpenVidu.getUserMedia}
|
||||
* - `false` or null to have a video-only publisher
|
||||
* @default _Default microphone_
|
||||
*/
|
||||
audioSource?: string | MediaStreamTrack | boolean;
|
||||
|
||||
/**
|
||||
* Desired framerate of the video in frames per second.
|
||||
* Limiting the framerate has always effect on browsers Chrome and Opera. Firefox requires that the input device explicitly supports the desired framerate.
|
||||
* @default undefined
|
||||
*/
|
||||
frameRate?: number;
|
||||
|
||||
/**
|
||||
* How the video element of the publisher should be inserted in the DOM
|
||||
* @default VideoInsertMode.APPEND
|
||||
*/
|
||||
insertMode?: VideoInsertMode | string;
|
||||
|
||||
/**
|
||||
* Whether the publisher's video will be mirrored in the page or not. Only affects the local view of the publisher in the browser (remote streams will not be mirrored). If `videoSource` is set to "screen" this property is fixed to `false`
|
||||
* @default true
|
||||
*/
|
||||
mirror?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to initially publish to the session with the audio unmuted or muted. Only makes sense if property `audioSource` is NOT set to *false* or *null*. You can change the audio state later during the session with {@link Publisher.publishAudio}
|
||||
* @default true
|
||||
*/
|
||||
publishAudio?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to initially publish to the session with the video enabled or disabled. Only makes sense if property `videoSource` is NOT set to *false* or *null*. You can change the video state later during the session with {@link Publisher.publishVideo}
|
||||
* @default true
|
||||
*/
|
||||
publishVideo?: boolean;
|
||||
|
||||
/**
|
||||
* Resolution of the video: `"320x240"`, `"640x480"`, `"1280x720"` (low, medium and high quality respectively)
|
||||
* @default "640x480"
|
||||
*/
|
||||
resolution?: string;
|
||||
|
||||
/**
|
||||
* Which device should provide the video source. Can be:
|
||||
* - Property `deviceId` of a {@link Device}
|
||||
* - `"screen"` to screen-share. We provide a default screen-shraring extension for Chrome that can run in any domain, but you can customize it so it has your own icon, your own name, etc. Visit this
|
||||
* [GitHub repository](https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension/) to learn how. Once you have uploaded your own extension to Chrome Web Store,
|
||||
* simply call `OpenVidu.setAdvancedConfiguration({screenShareChromeExtension : "https://chrome.google.com/webstore/detail/YOUR_EXTENSION_NAME/YOUR_EXTENSION_ID"})` before calling `OpenVidu.initPublisher(targetElement, {videoSource: "screen"})`.
|
||||
* For Firefox (<66) `"screen"` string will ask for permissions to share the entire screen. To ask for a specific window or application, use `"window"` string instead (this only applies to Firefox).
|
||||
* - A MediaStreamTrack obtained from a MediaStream object with {@link OpenVidu.getUserMedia}
|
||||
* - `false` or null to have an audio-only publisher
|
||||
* @default _Default camera_
|
||||
*/
|
||||
videoSource?: string | MediaStreamTrack | boolean;
|
||||
|
||||
/**
|
||||
* Use Simulcast video on WebRTC Publishers.
|
||||
* Senders will encode duplicate video streams with different qualities,
|
||||
* so the media server is able to select the most appropriate quality stream
|
||||
* for each Subscriber.
|
||||
* This setting is honored only if OpenVidu Server was configured to use the
|
||||
* mediasoup media server. Otherwise, Simulcast will be disabled.
|
||||
*/
|
||||
videoSimulcast?: boolean;
|
||||
|
||||
/**
|
||||
* **WARNING**: experimental option. This property may change in the near future
|
||||
*
|
||||
* Define a filter to apply in the Publisher's stream
|
||||
*/
|
||||
filter?: Filter;
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Connection } from '../../../OpenVidu/Connection';
|
||||
|
||||
/**
|
||||
* See {@link Session.signal}
|
||||
*/
|
||||
export interface SignalOptions {
|
||||
/**
|
||||
* The actual message of the signal.
|
||||
*/
|
||||
data?: string;
|
||||
|
||||
/**
|
||||
* The participants to whom to send the signal. They will only receive it if they are subscribed to
|
||||
* event `Session.on('signal')`. If empty or undefined, the signal will be send to all participants.
|
||||
*/
|
||||
to?: Connection[];
|
||||
|
||||
/**
|
||||
* The type of the signal. Participants subscribed to event `Session.on('signal:type')` will
|
||||
* receive it. Participants subscribed to `Session.on('signal')` will receive all signals.
|
||||
*/
|
||||
type?: string;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { VideoInsertMode } from '../../Enums/VideoInsertMode';
|
||||
|
||||
export interface StreamManagerVideo {
|
||||
/**
|
||||
* DOM video element displaying the StreamManager's stream
|
||||
*/
|
||||
video: HTMLVideoElement;
|
||||
|
||||
/**
|
||||
* `id` attribute of the DOM video element displaying the StreamManager's stream
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The DOM HTMLElement assigned as target element when creating a video for the StreamManager. This property is defined when:
|
||||
* - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing a valid `targetElement` parameter.
|
||||
* - {@link StreamManager.createVideoElement} has been called.
|
||||
*
|
||||
* This property is undefined when:
|
||||
* - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing *null* or *undefined* as `targetElement` parameter.
|
||||
* - {@link StreamManager.addVideoElement} has been called.
|
||||
*/
|
||||
targetElement?: HTMLElement;
|
||||
|
||||
/**
|
||||
* How the DOM video element should be inserted with respect to `targetElement`. This property is defined when:
|
||||
* - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing a valid `targetElement` parameter.
|
||||
* - {@link StreamManager.createVideoElement} has been called.
|
||||
*
|
||||
* This property is undefined when:
|
||||
* - {@link OpenVidu.initPublisher} or {@link Session.subscribe} methods have been called passing *null* or *undefined* as `targetElement` parameter.
|
||||
* - {@link StreamManager.addVideoElement} has been called.
|
||||
*/
|
||||
insertMode?: VideoInsertMode;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
canplayListenerAdded: boolean;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { VideoInsertMode } from '../../Enums/VideoInsertMode';
|
||||
|
||||
/**
|
||||
* See {@link Session.subscribe}
|
||||
*/
|
||||
export interface SubscriberProperties {
|
||||
/**
|
||||
* How the video element of the subscriber should be inserted in the DOM
|
||||
* @default VideoInsertMode.APPEND
|
||||
*/
|
||||
insertMode?: VideoInsertMode | string;
|
||||
|
||||
/**
|
||||
* Whether to initially subscribe to the audio track of the stream or not. You can change the audio state later with {@link Subscriber.subscribeToAudio}
|
||||
* @default true
|
||||
*/
|
||||
subscribeToAudio?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to initially subscribe to the video track of the stream or not. You can change the video state later with {@link Subscriber.subscribeToVideo}
|
||||
* @default true
|
||||
*/
|
||||
subscribeToVideo?: boolean;
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
function Mapper() {
|
||||
var sources = {};
|
||||
|
||||
this.forEach = function (callback) {
|
||||
for (var key in sources) {
|
||||
var source = sources[key];
|
||||
|
||||
for (var key2 in source) callback(source[key2]);
|
||||
}
|
||||
};
|
||||
|
||||
this.get = function (id, source) {
|
||||
var ids = sources[source];
|
||||
if (ids == undefined) return undefined;
|
||||
|
||||
return ids[id];
|
||||
};
|
||||
|
||||
this.remove = function (id, source) {
|
||||
var ids = sources[source];
|
||||
if (ids == undefined) return;
|
||||
|
||||
delete ids[id];
|
||||
|
||||
// Check it's empty
|
||||
for (var i in ids) {
|
||||
return false;
|
||||
}
|
||||
|
||||
delete sources[source];
|
||||
};
|
||||
|
||||
this.set = function (value, id, source) {
|
||||
if (value == undefined) return this.remove(id, source);
|
||||
|
||||
var ids = sources[source];
|
||||
if (ids == undefined) sources[source] = ids = {};
|
||||
|
||||
ids[id] = value;
|
||||
};
|
||||
}
|
||||
|
||||
Mapper.prototype.pop = function (id, source) {
|
||||
var value = this.get(id, source);
|
||||
if (value == undefined) return undefined;
|
||||
|
||||
this.remove(id, source);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
module.exports = Mapper;
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2014 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
var JsonRpcClient = require('./jsonrpcclient');
|
||||
|
||||
exports.JsonRpcClient = JsonRpcClient;
|
|
@ -1,280 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2014 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
var RpcBuilder = require('../');
|
||||
var WebSocketWithReconnection = require('./transports/webSocketWithReconnection');
|
||||
var OpenViduLogger = require('../../../Logger/OpenViduLogger').OpenViduLogger;
|
||||
|
||||
Date.now =
|
||||
Date.now ||
|
||||
function () {
|
||||
return +new Date();
|
||||
};
|
||||
|
||||
var PING_INTERVAL = 5000;
|
||||
|
||||
var RECONNECTING = 'RECONNECTING';
|
||||
var CONNECTED = 'CONNECTED';
|
||||
var DISCONNECTED = 'DISCONNECTED';
|
||||
|
||||
var Logger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
*
|
||||
* heartbeat: interval in ms for each heartbeat message,
|
||||
* <pre>
|
||||
* ws : {
|
||||
* uri : URI to conntect to,
|
||||
* onconnected : callback method to invoke when connection is successful,
|
||||
* ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached),
|
||||
* onreconnecting : callback method to invoke when the client is reconnecting,
|
||||
* onreconnected : callback method to invoke when the client successfully reconnects,
|
||||
* onerror : callback method to invoke when there is an error
|
||||
* },
|
||||
* rpc : {
|
||||
* requestTimeout : timeout for a request,
|
||||
* sessionStatusChanged: callback method for changes in session status,
|
||||
* mediaRenegotiation: mediaRenegotiation
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
function JsonRpcClient(configuration) {
|
||||
var self = this;
|
||||
|
||||
var wsConfig = configuration.ws;
|
||||
|
||||
var notReconnectIfNumLessThan = -1;
|
||||
|
||||
var pingNextNum = 0;
|
||||
var enabledPings = true;
|
||||
var pingPongStarted = false;
|
||||
var pingInterval;
|
||||
|
||||
var status = DISCONNECTED;
|
||||
|
||||
var onreconnecting = wsConfig.onreconnecting;
|
||||
var onreconnected = wsConfig.onreconnected;
|
||||
var onconnected = wsConfig.onconnected;
|
||||
var onerror = wsConfig.onerror;
|
||||
|
||||
configuration.rpc.pull = function (params, request) {
|
||||
request.reply(null, 'push');
|
||||
};
|
||||
|
||||
wsConfig.onreconnecting = function () {
|
||||
Logger.debug('--------- ONRECONNECTING -----------');
|
||||
if (status === RECONNECTING) {
|
||||
Logger.error('Websocket already in RECONNECTING state when receiving a new ONRECONNECTING message. Ignoring it');
|
||||
return;
|
||||
}
|
||||
|
||||
stopPing();
|
||||
|
||||
status = RECONNECTING;
|
||||
if (onreconnecting) {
|
||||
onreconnecting();
|
||||
}
|
||||
};
|
||||
|
||||
wsConfig.onreconnected = function () {
|
||||
Logger.debug('--------- ONRECONNECTED -----------');
|
||||
if (status === CONNECTED) {
|
||||
Logger.error('Websocket already in CONNECTED state when receiving a new ONRECONNECTED message. Ignoring it');
|
||||
return;
|
||||
}
|
||||
status = CONNECTED;
|
||||
|
||||
updateNotReconnectIfLessThan();
|
||||
|
||||
if (onreconnected) {
|
||||
onreconnected();
|
||||
}
|
||||
};
|
||||
|
||||
wsConfig.onconnected = function () {
|
||||
Logger.debug('--------- ONCONNECTED -----------');
|
||||
if (status === CONNECTED) {
|
||||
Logger.error('Websocket already in CONNECTED state when receiving a new ONCONNECTED message. Ignoring it');
|
||||
return;
|
||||
}
|
||||
status = CONNECTED;
|
||||
|
||||
enabledPings = true;
|
||||
usePing();
|
||||
|
||||
if (onconnected) {
|
||||
onconnected();
|
||||
}
|
||||
};
|
||||
|
||||
wsConfig.onerror = function (error) {
|
||||
Logger.debug('--------- ONERROR -----------');
|
||||
|
||||
status = DISCONNECTED;
|
||||
|
||||
stopPing();
|
||||
|
||||
if (onerror) {
|
||||
onerror(error);
|
||||
}
|
||||
};
|
||||
|
||||
var ws = new WebSocketWithReconnection(wsConfig);
|
||||
|
||||
Logger.debug('Connecting websocket to URI: ' + wsConfig.uri);
|
||||
|
||||
var rpcBuilderOptions = {
|
||||
request_timeout: configuration.rpc.requestTimeout,
|
||||
ping_request_timeout: configuration.rpc.heartbeatRequestTimeout
|
||||
};
|
||||
|
||||
var rpc = new RpcBuilder(RpcBuilder.packers.JsonRPC, rpcBuilderOptions, ws, function (request) {
|
||||
Logger.debug('Received request: ' + JSON.stringify(request));
|
||||
|
||||
try {
|
||||
var func = configuration.rpc[request.method];
|
||||
|
||||
if (func === undefined) {
|
||||
Logger.error('Method ' + request.method + ' not registered in client');
|
||||
} else {
|
||||
func(request.params, request);
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.error('Exception processing request: ' + JSON.stringify(request));
|
||||
Logger.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
this.send = function (method, params, callback) {
|
||||
var requestTime = Date.now();
|
||||
|
||||
rpc.encode(method, params, function (error, result) {
|
||||
if (error) {
|
||||
try {
|
||||
Logger.error(
|
||||
'ERROR:' +
|
||||
error.message +
|
||||
' in Request: method:' +
|
||||
method +
|
||||
' params:' +
|
||||
JSON.stringify(params) +
|
||||
' request:' +
|
||||
error.request
|
||||
);
|
||||
if (error.data) {
|
||||
Logger.error('ERROR DATA:' + JSON.stringify(error.data));
|
||||
}
|
||||
} catch (e) {}
|
||||
error.requestTime = requestTime;
|
||||
}
|
||||
if (callback) {
|
||||
if (result != undefined && result.value !== 'pong') {
|
||||
Logger.debug('Response: ' + JSON.stringify(result));
|
||||
}
|
||||
callback(error, result);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function updateNotReconnectIfLessThan() {
|
||||
Logger.debug('notReconnectIfNumLessThan = ' + pingNextNum + ' (old=' + notReconnectIfNumLessThan + ')');
|
||||
notReconnectIfNumLessThan = pingNextNum;
|
||||
}
|
||||
|
||||
function sendPing() {
|
||||
if (enabledPings) {
|
||||
var params = null;
|
||||
if (pingNextNum == 0 || pingNextNum == notReconnectIfNumLessThan) {
|
||||
params = {
|
||||
interval: configuration.heartbeat || PING_INTERVAL
|
||||
};
|
||||
}
|
||||
pingNextNum++;
|
||||
|
||||
self.send(
|
||||
'ping',
|
||||
params,
|
||||
(function (pingNum) {
|
||||
return function (error, result) {
|
||||
if (error) {
|
||||
Logger.debug('Error in ping request #' + pingNum + ' (' + error.message + ')');
|
||||
if (pingNum > notReconnectIfNumLessThan) {
|
||||
enabledPings = false;
|
||||
updateNotReconnectIfLessThan();
|
||||
Logger.debug('Server did not respond to ping message #' + pingNum + '. Reconnecting... ');
|
||||
ws.reconnectWs();
|
||||
}
|
||||
}
|
||||
};
|
||||
})(pingNextNum)
|
||||
);
|
||||
} else {
|
||||
Logger.debug('Trying to send ping, but ping is not enabled');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If configuration.hearbeat has any value, the ping-pong will work with the interval
|
||||
* of configuration.hearbeat
|
||||
*/
|
||||
function usePing() {
|
||||
if (!pingPongStarted) {
|
||||
Logger.debug('Starting ping (if configured)');
|
||||
pingPongStarted = true;
|
||||
|
||||
if (configuration.heartbeat != undefined) {
|
||||
pingInterval = setInterval(sendPing, configuration.heartbeat);
|
||||
sendPing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stopPing() {
|
||||
clearInterval(pingInterval);
|
||||
pingPongStarted = false;
|
||||
enabledPings = false;
|
||||
pingNextNum = -1;
|
||||
rpc.cancel();
|
||||
}
|
||||
|
||||
this.close = function (code, reason) {
|
||||
Logger.debug('Closing with code: ' + code + ' because: ' + reason);
|
||||
if (pingInterval != undefined) {
|
||||
Logger.debug('Clearing ping interval');
|
||||
clearInterval(pingInterval);
|
||||
}
|
||||
pingPongStarted = false;
|
||||
enabledPings = false;
|
||||
ws.close(code, reason);
|
||||
};
|
||||
|
||||
this.reconnect = function () {
|
||||
ws.reconnectWs();
|
||||
};
|
||||
|
||||
this.resetPing = function () {
|
||||
enabledPings = true;
|
||||
pingNextNum = 0;
|
||||
usePing();
|
||||
};
|
||||
|
||||
this.getReadyState = function () {
|
||||
return ws.getReadyState();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = JsonRpcClient;
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2014 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
var WebSocketWithReconnection = require('./webSocketWithReconnection');
|
||||
|
||||
exports.WebSocketWithReconnection = WebSocketWithReconnection;
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2013-2015 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var OpenViduLogger = require('../../../../Logger/OpenViduLogger').OpenViduLogger;
|
||||
var Logger = OpenViduLogger.getInstance();
|
||||
|
||||
var MAX_RETRIES = 2000; // Forever...
|
||||
var RETRY_TIME_MS = 3000; // FIXME: Implement exponential wait times...
|
||||
|
||||
var CONNECTING = 0;
|
||||
var OPEN = 1;
|
||||
var CLOSING = 2;
|
||||
var CLOSED = 3;
|
||||
|
||||
/*
|
||||
config = {
|
||||
uri : wsUri,
|
||||
onconnected : callback method to invoke when connection is successful,
|
||||
ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached),
|
||||
onreconnecting : callback method to invoke when the client is reconnecting,
|
||||
onreconnected : callback method to invoke when the client successfully reconnects,
|
||||
};
|
||||
*/
|
||||
function WebSocketWithReconnection(config) {
|
||||
var closing = false;
|
||||
var registerMessageHandler;
|
||||
var wsUri = config.uri;
|
||||
var reconnecting = false;
|
||||
|
||||
var ws = new WebSocket(wsUri);
|
||||
|
||||
ws.onopen = () => {
|
||||
Logger.debug('WebSocket connected to ' + wsUri);
|
||||
if (config.onconnected) {
|
||||
config.onconnected();
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
Logger.error('Could not connect to ' + wsUri + ' (invoking onerror if defined)', error);
|
||||
if (config.onerror) {
|
||||
config.onerror(error);
|
||||
}
|
||||
};
|
||||
|
||||
var reconnectionOnClose = () => {
|
||||
if (ws.readyState === CLOSED) {
|
||||
if (closing) {
|
||||
Logger.debug('Connection closed by user');
|
||||
} else {
|
||||
if (config.ismasternodecrashed()) {
|
||||
Logger.error('Master Node has crashed. Stopping reconnection process');
|
||||
} else {
|
||||
Logger.debug('Connection closed unexpectedly. Reconnecting...');
|
||||
reconnect(MAX_RETRIES, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Logger.debug('Close callback from previous websocket. Ignoring it');
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = reconnectionOnClose;
|
||||
|
||||
function reconnect(maxRetries, numRetries) {
|
||||
Logger.debug('reconnect (attempt #' + numRetries + ', max=' + maxRetries + ')');
|
||||
if (numRetries === 1) {
|
||||
if (reconnecting) {
|
||||
Logger.warn('Trying to reconnect when already reconnecting... Ignoring this reconnection.');
|
||||
return;
|
||||
} else {
|
||||
reconnecting = true;
|
||||
}
|
||||
if (config.onreconnecting) {
|
||||
config.onreconnecting();
|
||||
}
|
||||
}
|
||||
reconnectAux(maxRetries, numRetries);
|
||||
}
|
||||
|
||||
function addReconnectionQueryParamsIfMissing(uriString) {
|
||||
var searchParams = new URLSearchParams(new URL(uriString).search);
|
||||
if (!searchParams.has('reconnect')) {
|
||||
uriString = Array.from(searchParams).length > 0 ? uriString + '&reconnect=true' : uriString + '?reconnect=true';
|
||||
}
|
||||
return uriString;
|
||||
}
|
||||
|
||||
function reconnectAux(maxRetries, numRetries) {
|
||||
Logger.debug('Reconnection attempt #' + numRetries);
|
||||
ws.close(4104, 'Connection closed for reconnection');
|
||||
|
||||
wsUri = addReconnectionQueryParamsIfMissing(wsUri);
|
||||
ws = new WebSocket(wsUri);
|
||||
|
||||
ws.onopen = () => {
|
||||
Logger.debug('Reconnected to ' + wsUri + ' after ' + numRetries + ' attempts...');
|
||||
reconnecting = false;
|
||||
registerMessageHandler();
|
||||
if (config.onreconnected) {
|
||||
config.onreconnected();
|
||||
}
|
||||
ws.onclose = reconnectionOnClose;
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
Logger.warn('Reconnection error: ', error);
|
||||
if (numRetries === maxRetries) {
|
||||
if (config.ondisconnect) {
|
||||
config.ondisconnect();
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
reconnect(maxRetries, numRetries + 1);
|
||||
}, RETRY_TIME_MS);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.close = (code, reason) => {
|
||||
closing = true;
|
||||
ws.close(code, reason);
|
||||
};
|
||||
|
||||
this.reconnectWs = () => {
|
||||
Logger.debug('reconnectWs');
|
||||
reconnect(MAX_RETRIES, 1);
|
||||
};
|
||||
|
||||
this.send = (message) => {
|
||||
ws.send(message);
|
||||
};
|
||||
|
||||
this.addEventListener = (type, callback) => {
|
||||
registerMessageHandler = () => {
|
||||
ws.addEventListener(type, callback);
|
||||
};
|
||||
registerMessageHandler();
|
||||
};
|
||||
|
||||
this.getReadyState = () => {
|
||||
return ws.readyState;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = WebSocketWithReconnection;
|
|
@ -1,687 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2014 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
var defineProperty_IE8 = false;
|
||||
if (Object.defineProperty) {
|
||||
try {
|
||||
Object.defineProperty({}, 'x', {});
|
||||
} catch (e) {
|
||||
defineProperty_IE8 = true;
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function (oThis) {
|
||||
if (typeof this !== 'function') {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function () {},
|
||||
fBound = function () {
|
||||
return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
fNOP.prototype = this.prototype;
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var inherits = require('inherits');
|
||||
|
||||
var packers = require('./packers');
|
||||
var Mapper = require('./Mapper');
|
||||
|
||||
var BASE_TIMEOUT = 5000;
|
||||
|
||||
function unifyResponseMethods(responseMethods) {
|
||||
if (!responseMethods) return {};
|
||||
|
||||
for (var key in responseMethods) {
|
||||
var value = responseMethods[key];
|
||||
|
||||
if (typeof value == 'string')
|
||||
responseMethods[key] = {
|
||||
response: value
|
||||
};
|
||||
}
|
||||
|
||||
return responseMethods;
|
||||
}
|
||||
|
||||
function unifyTransport(transport) {
|
||||
if (!transport) return;
|
||||
|
||||
// Transport as a function
|
||||
if (transport instanceof Function)
|
||||
return {
|
||||
send: transport
|
||||
};
|
||||
|
||||
// WebSocket & DataChannel
|
||||
if (transport.send instanceof Function) return transport;
|
||||
|
||||
// Message API (Inter-window & WebWorker)
|
||||
if (transport.postMessage instanceof Function) {
|
||||
transport.send = transport.postMessage;
|
||||
return transport;
|
||||
}
|
||||
|
||||
// Stream API
|
||||
if (transport.write instanceof Function) {
|
||||
transport.send = transport.write;
|
||||
return transport;
|
||||
}
|
||||
|
||||
// Transports that only can receive messages, but not send
|
||||
if (transport.onmessage !== undefined) return;
|
||||
if (transport.pause instanceof Function) return;
|
||||
|
||||
throw new SyntaxError('Transport is not a function nor a valid object');
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of a RPC notification
|
||||
*
|
||||
* @class
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {String} method -method of the notification
|
||||
* @param params - parameters of the notification
|
||||
*/
|
||||
function RpcNotification(method, params) {
|
||||
if (defineProperty_IE8) {
|
||||
this.method = method;
|
||||
this.params = params;
|
||||
} else {
|
||||
Object.defineProperty(this, 'method', {
|
||||
value: method,
|
||||
enumerable: true
|
||||
});
|
||||
Object.defineProperty(this, 'params', {
|
||||
value: params,
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {object} packer
|
||||
*
|
||||
* @param {object} [options]
|
||||
*
|
||||
* @param {object} [transport]
|
||||
*
|
||||
* @param {Function} [onRequest]
|
||||
*/
|
||||
function RpcBuilder(packer, options, transport, onRequest) {
|
||||
var self = this;
|
||||
|
||||
if (!packer) throw new SyntaxError('Packer is not defined');
|
||||
|
||||
if (!packer.pack || !packer.unpack) throw new SyntaxError('Packer is invalid');
|
||||
|
||||
var responseMethods = unifyResponseMethods(packer.responseMethods);
|
||||
|
||||
if (options instanceof Function) {
|
||||
if (transport != undefined) throw new SyntaxError("There can't be parameters after onRequest");
|
||||
|
||||
onRequest = options;
|
||||
transport = undefined;
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
if (options && options.send instanceof Function) {
|
||||
if (transport && !(transport instanceof Function)) throw new SyntaxError('Only a function can be after transport');
|
||||
|
||||
onRequest = transport;
|
||||
transport = options;
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
if (transport instanceof Function) {
|
||||
if (onRequest != undefined) throw new SyntaxError("There can't be parameters after onRequest");
|
||||
|
||||
onRequest = transport;
|
||||
transport = undefined;
|
||||
}
|
||||
|
||||
if (transport && transport.send instanceof Function)
|
||||
if (onRequest && !(onRequest instanceof Function)) throw new SyntaxError('Only a function can be after transport');
|
||||
|
||||
options = options || {};
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
if (onRequest) this.on('request', onRequest);
|
||||
|
||||
if (defineProperty_IE8) this.peerID = options.peerID;
|
||||
else
|
||||
Object.defineProperty(this, 'peerID', {
|
||||
value: options.peerID
|
||||
});
|
||||
|
||||
var max_retries = options.max_retries || 0;
|
||||
|
||||
function transportMessage(event) {
|
||||
self.decode(event.data || event);
|
||||
}
|
||||
|
||||
this.getTransport = function () {
|
||||
return transport;
|
||||
};
|
||||
this.setTransport = function (value) {
|
||||
// Remove listener from old transport
|
||||
if (transport) {
|
||||
// W3C transports
|
||||
if (transport.removeEventListener) transport.removeEventListener('message', transportMessage);
|
||||
// Node.js Streams API
|
||||
else if (transport.removeListener) transport.removeListener('data', transportMessage);
|
||||
}
|
||||
|
||||
// Set listener on new transport
|
||||
if (value) {
|
||||
// W3C transports
|
||||
if (value.addEventListener) value.addEventListener('message', transportMessage);
|
||||
// Node.js Streams API
|
||||
else if (value.addListener) value.addListener('data', transportMessage);
|
||||
}
|
||||
|
||||
transport = unifyTransport(value);
|
||||
};
|
||||
|
||||
if (!defineProperty_IE8)
|
||||
Object.defineProperty(this, 'transport', {
|
||||
get: this.getTransport.bind(this),
|
||||
set: this.setTransport.bind(this)
|
||||
});
|
||||
|
||||
this.setTransport(transport);
|
||||
|
||||
var request_timeout = options.request_timeout || BASE_TIMEOUT;
|
||||
var ping_request_timeout = options.ping_request_timeout || request_timeout;
|
||||
var response_timeout = options.response_timeout || BASE_TIMEOUT;
|
||||
var duplicates_timeout = options.duplicates_timeout || BASE_TIMEOUT;
|
||||
|
||||
var requestID = 0;
|
||||
|
||||
var requests = new Mapper();
|
||||
var responses = new Mapper();
|
||||
var processedResponses = new Mapper();
|
||||
|
||||
var message2Key = {};
|
||||
|
||||
/**
|
||||
* Store the response to prevent to process duplicate request later
|
||||
*/
|
||||
function storeResponse(message, id, dest) {
|
||||
var response = {
|
||||
message: message,
|
||||
/** Timeout to auto-clean old responses */
|
||||
timeout: setTimeout(function () {
|
||||
responses.remove(id, dest);
|
||||
}, response_timeout)
|
||||
};
|
||||
|
||||
responses.set(response, id, dest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the response to ignore duplicated messages later
|
||||
*/
|
||||
function storeProcessedResponse(ack, from) {
|
||||
var timeout = setTimeout(function () {
|
||||
processedResponses.remove(ack, from);
|
||||
}, duplicates_timeout);
|
||||
|
||||
processedResponses.set(timeout, ack, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of a RPC request
|
||||
*
|
||||
* @class
|
||||
* @extends RpcNotification
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {String} method -method of the notification
|
||||
* @param params - parameters of the notification
|
||||
* @param {Integer} id - identifier of the request
|
||||
* @param [from] - source of the notification
|
||||
*/
|
||||
function RpcRequest(method, params, id, from, transport) {
|
||||
RpcNotification.call(this, method, params);
|
||||
|
||||
this.getTransport = function () {
|
||||
return transport;
|
||||
};
|
||||
this.setTransport = function (value) {
|
||||
transport = unifyTransport(value);
|
||||
};
|
||||
|
||||
if (!defineProperty_IE8)
|
||||
Object.defineProperty(this, 'transport', {
|
||||
get: this.getTransport.bind(this),
|
||||
set: this.setTransport.bind(this)
|
||||
});
|
||||
|
||||
var response = responses.get(id, from);
|
||||
|
||||
/**
|
||||
* @constant {Boolean} duplicated
|
||||
*/
|
||||
if (!(transport || self.getTransport())) {
|
||||
if (defineProperty_IE8) this.duplicated = Boolean(response);
|
||||
else
|
||||
Object.defineProperty(this, 'duplicated', {
|
||||
value: Boolean(response)
|
||||
});
|
||||
}
|
||||
|
||||
var responseMethod = responseMethods[method];
|
||||
|
||||
this.pack = packer.pack.bind(packer, this, id);
|
||||
|
||||
/**
|
||||
* Generate a response to this request
|
||||
*
|
||||
* @param {Error} [error]
|
||||
* @param {*} [result]
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
this.reply = function (error, result, transport) {
|
||||
// Fix optional parameters
|
||||
if (error instanceof Function || (error && error.send instanceof Function)) {
|
||||
if (result != undefined) throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
transport = error;
|
||||
result = null;
|
||||
error = undefined;
|
||||
} else if (result instanceof Function || (result && result.send instanceof Function)) {
|
||||
if (transport != undefined) throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
transport = result;
|
||||
result = null;
|
||||
}
|
||||
|
||||
transport = unifyTransport(transport);
|
||||
|
||||
// Duplicated request, remove old response timeout
|
||||
if (response) clearTimeout(response.timeout);
|
||||
|
||||
if (from != undefined) {
|
||||
if (error) error.dest = from;
|
||||
|
||||
if (result) result.dest = from;
|
||||
}
|
||||
|
||||
var message;
|
||||
|
||||
// New request or overriden one, create new response with provided data
|
||||
if (error || result != undefined) {
|
||||
if (self.peerID != undefined) {
|
||||
if (error) error.from = self.peerID;
|
||||
else result.from = self.peerID;
|
||||
}
|
||||
|
||||
// Protocol indicates that responses has own request methods
|
||||
if (responseMethod) {
|
||||
if (responseMethod.error == undefined && error)
|
||||
message = {
|
||||
error: error
|
||||
};
|
||||
else {
|
||||
var method = error ? responseMethod.error : responseMethod.response;
|
||||
|
||||
message = {
|
||||
method: method,
|
||||
params: error || result
|
||||
};
|
||||
}
|
||||
} else
|
||||
message = {
|
||||
error: error,
|
||||
result: result
|
||||
};
|
||||
|
||||
message = packer.pack(message, id);
|
||||
}
|
||||
|
||||
// Duplicate & not-overriden request, re-send old response
|
||||
else if (response) message = response.message;
|
||||
// New empty reply, response null value
|
||||
else
|
||||
message = packer.pack(
|
||||
{
|
||||
result: null
|
||||
},
|
||||
id
|
||||
);
|
||||
|
||||
// Store the response to prevent to process a duplicated request later
|
||||
storeResponse(message, id, from);
|
||||
|
||||
// Return the stored response so it can be directly send back
|
||||
transport = transport || this.getTransport() || self.getTransport();
|
||||
|
||||
if (transport) return transport.send(message);
|
||||
|
||||
return message;
|
||||
};
|
||||
}
|
||||
inherits(RpcRequest, RpcNotification);
|
||||
|
||||
function cancel(message) {
|
||||
var key = message2Key[message];
|
||||
if (!key) return;
|
||||
|
||||
delete message2Key[message];
|
||||
|
||||
var request = requests.pop(key.id, key.dest);
|
||||
if (!request) return;
|
||||
|
||||
clearTimeout(request.timeout);
|
||||
|
||||
// Start duplicated responses timeout
|
||||
storeProcessedResponse(key.id, key.dest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow to cancel a request and don't wait for a response
|
||||
*
|
||||
* If `message` is not given, cancel all the request
|
||||
*/
|
||||
this.cancel = function (message) {
|
||||
if (message) return cancel(message);
|
||||
|
||||
for (var message in message2Key) cancel(message);
|
||||
};
|
||||
|
||||
this.close = function () {
|
||||
// Prevent to receive new messages
|
||||
var transport = this.getTransport();
|
||||
if (transport && transport.close) transport.close(4003, 'Cancel request');
|
||||
|
||||
// Request & processed responses
|
||||
this.cancel();
|
||||
|
||||
processedResponses.forEach(clearTimeout);
|
||||
|
||||
// Responses
|
||||
responses.forEach(function (response) {
|
||||
clearTimeout(response.timeout);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates and encode a JsonRPC 2.0 message
|
||||
*
|
||||
* @param {String} method -method of the notification
|
||||
* @param params - parameters of the notification
|
||||
* @param [dest] - destination of the notification
|
||||
* @param {object} [transport] - transport where to send the message
|
||||
* @param [callback] - function called when a response to this request is
|
||||
* received. If not defined, a notification will be send instead
|
||||
*
|
||||
* @returns {string} A raw JsonRPC 2.0 request or notification string
|
||||
*/
|
||||
this.encode = function (method, params, dest, transport, callback) {
|
||||
// Fix optional parameters
|
||||
if (params instanceof Function) {
|
||||
if (dest != undefined) throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
callback = params;
|
||||
transport = undefined;
|
||||
dest = undefined;
|
||||
params = undefined;
|
||||
} else if (dest instanceof Function) {
|
||||
if (transport != undefined) throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
callback = dest;
|
||||
transport = undefined;
|
||||
dest = undefined;
|
||||
} else if (transport instanceof Function) {
|
||||
if (callback != undefined) throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
callback = transport;
|
||||
transport = undefined;
|
||||
}
|
||||
|
||||
if (self.peerID != undefined) {
|
||||
params = params || {};
|
||||
|
||||
params.from = self.peerID;
|
||||
}
|
||||
|
||||
if (dest != undefined) {
|
||||
params = params || {};
|
||||
|
||||
params.dest = dest;
|
||||
}
|
||||
|
||||
// Encode message
|
||||
var message = {
|
||||
method: method,
|
||||
params: params
|
||||
};
|
||||
|
||||
if (callback) {
|
||||
var id = requestID++;
|
||||
var retried = 0;
|
||||
|
||||
message = packer.pack(message, id);
|
||||
|
||||
function dispatchCallback(error, result) {
|
||||
self.cancel(message);
|
||||
|
||||
callback(error, result);
|
||||
}
|
||||
|
||||
var request = {
|
||||
message: message,
|
||||
callback: dispatchCallback,
|
||||
responseMethods: responseMethods[method] || {}
|
||||
};
|
||||
|
||||
var encode_transport = unifyTransport(transport);
|
||||
|
||||
function sendRequest(transport) {
|
||||
var rt = method === 'ping' ? ping_request_timeout : request_timeout;
|
||||
request.timeout = setTimeout(timeout, rt * Math.pow(2, retried++));
|
||||
message2Key[message] = {
|
||||
id: id,
|
||||
dest: dest
|
||||
};
|
||||
requests.set(request, id, dest);
|
||||
|
||||
transport = transport || encode_transport || self.getTransport();
|
||||
if (transport) return transport.send(message);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
function retry(transport) {
|
||||
transport = unifyTransport(transport);
|
||||
|
||||
console.warn(retried + ' retry for request message:', message);
|
||||
|
||||
var timeout = processedResponses.pop(id, dest);
|
||||
clearTimeout(timeout);
|
||||
|
||||
return sendRequest(transport);
|
||||
}
|
||||
|
||||
function timeout() {
|
||||
if (retried < max_retries) return retry(transport);
|
||||
|
||||
var error = new Error('Request has timed out');
|
||||
error.request = message;
|
||||
|
||||
error.retry = retry;
|
||||
|
||||
dispatchCallback(error);
|
||||
}
|
||||
|
||||
return sendRequest(transport);
|
||||
}
|
||||
|
||||
// Return the packed message
|
||||
message = packer.pack(message);
|
||||
|
||||
transport = transport || this.getTransport();
|
||||
if (transport) return transport.send(message);
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode and process a JsonRPC 2.0 message
|
||||
*
|
||||
* @param {string} message - string with the content of the message
|
||||
*
|
||||
* @returns {RpcNotification|RpcRequest|undefined} - the representation of the
|
||||
* notification or the request. If a response was processed, it will return
|
||||
* `undefined` to notify that it was processed
|
||||
*
|
||||
* @throws {TypeError} - Message is not defined
|
||||
*/
|
||||
this.decode = function (message, transport) {
|
||||
if (!message) throw new TypeError('Message is not defined');
|
||||
|
||||
try {
|
||||
message = packer.unpack(message);
|
||||
} catch (e) {
|
||||
// Ignore invalid messages
|
||||
return console.debug(e, message);
|
||||
}
|
||||
|
||||
var id = message.id;
|
||||
var ack = message.ack;
|
||||
var method = message.method;
|
||||
var params = message.params || {};
|
||||
|
||||
var from = params.from;
|
||||
var dest = params.dest;
|
||||
|
||||
// Ignore messages send by us
|
||||
if (self.peerID != undefined && from == self.peerID) return;
|
||||
|
||||
// Notification
|
||||
if (id == undefined && ack == undefined) {
|
||||
var notification = new RpcNotification(method, params);
|
||||
|
||||
if (self.emit('request', notification)) return;
|
||||
return notification;
|
||||
}
|
||||
|
||||
function processRequest() {
|
||||
// If we have a transport and it's a duplicated request, reply inmediatly
|
||||
transport = unifyTransport(transport) || self.getTransport();
|
||||
if (transport) {
|
||||
var response = responses.get(id, from);
|
||||
if (response) return transport.send(response.message);
|
||||
}
|
||||
|
||||
var idAck = id != undefined ? id : ack;
|
||||
var request = new RpcRequest(method, params, idAck, from, transport);
|
||||
|
||||
if (self.emit('request', request)) return;
|
||||
return request;
|
||||
}
|
||||
|
||||
function processResponse(request, error, result) {
|
||||
request.callback(error, result);
|
||||
}
|
||||
|
||||
function duplicatedResponse(timeout) {
|
||||
console.warn('Response already processed', message);
|
||||
|
||||
// Update duplicated responses timeout
|
||||
clearTimeout(timeout);
|
||||
storeProcessedResponse(ack, from);
|
||||
}
|
||||
|
||||
// Request, or response with own method
|
||||
if (method) {
|
||||
// Check if it's a response with own method
|
||||
if (dest == undefined || dest == self.peerID) {
|
||||
var request = requests.get(ack, from);
|
||||
if (request) {
|
||||
var responseMethods = request.responseMethods;
|
||||
|
||||
if (method == responseMethods.error) return processResponse(request, params);
|
||||
|
||||
if (method == responseMethods.response) return processResponse(request, null, params);
|
||||
|
||||
return processRequest();
|
||||
}
|
||||
|
||||
var processed = processedResponses.get(ack, from);
|
||||
if (processed) return duplicatedResponse(processed);
|
||||
}
|
||||
|
||||
// Request
|
||||
return processRequest();
|
||||
}
|
||||
|
||||
var error = message.error;
|
||||
var result = message.result;
|
||||
|
||||
// Ignore responses not send to us
|
||||
if (error && error.dest && error.dest != self.peerID) return;
|
||||
if (result && result.dest && result.dest != self.peerID) return;
|
||||
|
||||
// Response
|
||||
var request = requests.get(ack, from);
|
||||
if (!request) {
|
||||
var processed = processedResponses.get(ack, from);
|
||||
if (processed) return duplicatedResponse(processed);
|
||||
|
||||
return console.warn('No callback was defined for this message', message);
|
||||
}
|
||||
|
||||
// Process response
|
||||
processResponse(request, error, result);
|
||||
};
|
||||
}
|
||||
inherits(RpcBuilder, EventEmitter);
|
||||
|
||||
RpcBuilder.RpcNotification = RpcNotification;
|
||||
|
||||
module.exports = RpcBuilder;
|
||||
|
||||
var clients = require('./clients');
|
||||
var transports = require('./clients/transports');
|
||||
|
||||
RpcBuilder.clients = clients;
|
||||
RpcBuilder.clients.transports = transports;
|
||||
RpcBuilder.packers = packers;
|
|
@ -1,85 +0,0 @@
|
|||
/**
|
||||
* JsonRPC 2.0 packer
|
||||
*/
|
||||
|
||||
/**
|
||||
* Pack a JsonRPC 2.0 message
|
||||
*
|
||||
* @param {Object} message - object to be packaged. It requires to have all the
|
||||
* fields needed by the JsonRPC 2.0 message that it's going to be generated
|
||||
*
|
||||
* @return {String} - the stringified JsonRPC 2.0 message
|
||||
*/
|
||||
function pack(message, id) {
|
||||
var result = {
|
||||
jsonrpc: '2.0'
|
||||
};
|
||||
|
||||
// Request
|
||||
if (message.method) {
|
||||
result.method = message.method;
|
||||
|
||||
if (message.params) result.params = message.params;
|
||||
|
||||
// Request is a notification
|
||||
if (id != undefined) result.id = id;
|
||||
}
|
||||
|
||||
// Response
|
||||
else if (id != undefined) {
|
||||
if (message.error) {
|
||||
if (message.result !== undefined) throw new TypeError('Both result and error are defined');
|
||||
|
||||
result.error = message.error;
|
||||
} else if (message.result !== undefined) result.result = message.result;
|
||||
else throw new TypeError('No result or error is defined');
|
||||
|
||||
result.id = id;
|
||||
}
|
||||
|
||||
return JSON.stringify(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a JsonRPC 2.0 message
|
||||
*
|
||||
* @param {String} message - string with the content of the JsonRPC 2.0 message
|
||||
*
|
||||
* @throws {TypeError} - Invalid JsonRPC version
|
||||
*
|
||||
* @return {Object} - object filled with the JsonRPC 2.0 message content
|
||||
*/
|
||||
function unpack(message) {
|
||||
var result = message;
|
||||
|
||||
if (typeof message === 'string' || message instanceof String) {
|
||||
result = JSON.parse(message);
|
||||
}
|
||||
|
||||
// Check if it's a valid message
|
||||
|
||||
var version = result.jsonrpc;
|
||||
if (version !== '2.0') throw new TypeError("Invalid JsonRPC version '" + version + "': " + message);
|
||||
|
||||
// Response
|
||||
if (result.method == undefined) {
|
||||
if (result.id == undefined) throw new TypeError('Invalid message: ' + message);
|
||||
|
||||
var result_defined = result.result !== undefined;
|
||||
var error_defined = result.error !== undefined;
|
||||
|
||||
// Check only result or error is defined, not both or none
|
||||
if (result_defined && error_defined) throw new TypeError('Both result and error are defined: ' + message);
|
||||
|
||||
if (!result_defined && !error_defined) throw new TypeError('No result or error is defined: ' + message);
|
||||
|
||||
result.ack = result.id;
|
||||
delete result.id;
|
||||
}
|
||||
|
||||
// Return unpacked message
|
||||
return result;
|
||||
}
|
||||
|
||||
exports.pack = pack;
|
||||
exports.unpack = unpack;
|
|
@ -1,10 +0,0 @@
|
|||
function pack(message) {
|
||||
throw new TypeError('Not yet implemented');
|
||||
}
|
||||
|
||||
function unpack(message) {
|
||||
throw new TypeError('Not yet implemented');
|
||||
}
|
||||
|
||||
exports.pack = pack;
|
||||
exports.unpack = unpack;
|
|
@ -1,5 +0,0 @@
|
|||
var JsonRPC = require('./JsonRPC');
|
||||
var XmlRPC = require('./XmlRPC');
|
||||
|
||||
exports.JsonRPC = JsonRPC;
|
||||
exports.XmlRPC = XmlRPC;
|
|
@ -1,41 +0,0 @@
|
|||
type ConsoleFunction = (...data: any) => void;
|
||||
export class ConsoleLogger {
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
logger: Console;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
log: ConsoleFunction;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
info: ConsoleFunction;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
debug: ConsoleFunction;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
warn: ConsoleFunction;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
error: ConsoleFunction;
|
||||
|
||||
constructor(console: Console) {
|
||||
this.logger = console;
|
||||
(this.log = console.log),
|
||||
(this.info = console.info),
|
||||
(this.debug = console.debug),
|
||||
(this.warn = console.warn),
|
||||
(this.error = console.error);
|
||||
}
|
||||
}
|
|
@ -1,285 +0,0 @@
|
|||
import { JL } from 'jsnlog';
|
||||
import { OpenVidu } from '../../OpenVidu/OpenVidu';
|
||||
import { ConsoleLogger } from './ConsoleLogger';
|
||||
import { OpenViduLoggerConfiguration } from './OpenViduLoggerConfiguration';
|
||||
|
||||
export class OpenViduLogger {
|
||||
private static instance: OpenViduLogger;
|
||||
|
||||
private JSNLOG_URL: string = '/openvidu/elk/openvidu-browser-logs';
|
||||
private MAX_JSNLOG_BATCH_LOG_MESSAGES: number = 100;
|
||||
private MAX_MSECONDS_BATCH_MESSAGES: number = 5000;
|
||||
private MAX_LENGTH_STRING_JSON: number = 1000;
|
||||
|
||||
private defaultConsoleLogger: ConsoleLogger = new ConsoleLogger(globalThis.console);
|
||||
|
||||
private currentAppender: any;
|
||||
|
||||
private isProdMode = false;
|
||||
private isJSNLogSetup = false;
|
||||
|
||||
// This two variables are used to restart JSNLog
|
||||
// on different sessions and different userIds
|
||||
private loggingSessionId: string | undefined;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
static configureJSNLog(openVidu: OpenVidu, token: string) {
|
||||
try {
|
||||
// If dev mode or...
|
||||
if (
|
||||
globalThis['LOG_JSNLOG_RESULTS'] ||
|
||||
// If instance is created and it is OpenVidu Pro
|
||||
(this.instance &&
|
||||
openVidu.isAtLeastPro &&
|
||||
// If logs are enabled
|
||||
this.instance.isOpenViduBrowserLogsDebugActive(openVidu) &&
|
||||
// Only reconfigure it if session or finalUserId has changed
|
||||
this.instance.canConfigureJSNLog(openVidu, this.instance))
|
||||
) {
|
||||
// Check if app logs can be sent
|
||||
// and replace console.log function to send
|
||||
// logs of the application
|
||||
if (openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug_app) {
|
||||
this.instance.replaceWindowConsole();
|
||||
}
|
||||
|
||||
// isJSNLogSetup will not be true until completed setup
|
||||
this.instance.isJSNLogSetup = false;
|
||||
this.instance.info('Configuring JSNLogs.');
|
||||
|
||||
const finalUserId = openVidu.finalUserId;
|
||||
const sessionId = openVidu.session.sessionId;
|
||||
|
||||
const beforeSendCallback = (xhr) => {
|
||||
// If 401 or 403 or 404 modify ready and status so JSNLog don't retry to send logs
|
||||
// https://github.com/mperdeck/jsnlog.js/blob/v2.30.0/jsnlog.ts#L805-L818
|
||||
const parentReadyStateFunction = xhr.onreadystatechange;
|
||||
xhr.onreadystatechange = () => {
|
||||
if (this.isInvalidResponse(xhr)) {
|
||||
Object.defineProperty(xhr, 'readyState', { value: 4 });
|
||||
Object.defineProperty(xhr, 'status', { value: 200 });
|
||||
// Disable JSNLog too to not send periodically errors
|
||||
this.instance.disableLogger();
|
||||
}
|
||||
parentReadyStateFunction();
|
||||
};
|
||||
|
||||
// Headers to identify and authenticate logs
|
||||
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(`${finalUserId}%/%${sessionId}` + ':' + token));
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
// Additional headers for OpenVidu
|
||||
xhr.setRequestHeader('OV-Final-User-Id', finalUserId);
|
||||
xhr.setRequestHeader('OV-Session-Id', sessionId);
|
||||
xhr.setRequestHeader('OV-Token', token);
|
||||
};
|
||||
|
||||
// Creation of the appender.
|
||||
this.instance.currentAppender = JL.createAjaxAppender(`appender-${finalUserId}-${sessionId}`);
|
||||
this.instance.currentAppender.setOptions({
|
||||
beforeSend: beforeSendCallback,
|
||||
maxBatchSize: 1000,
|
||||
batchSize: this.instance.MAX_JSNLOG_BATCH_LOG_MESSAGES,
|
||||
batchTimeout: this.instance.MAX_MSECONDS_BATCH_MESSAGES
|
||||
});
|
||||
|
||||
// Avoid circular dependencies
|
||||
const logSerializer = (obj): string => {
|
||||
const getCircularReplacer = () => {
|
||||
const seen = new WeakSet();
|
||||
return (key, value) => {
|
||||
if (typeof value === 'object' && value != null) {
|
||||
if (seen.has(value) || (globalThis.HTMLElement && value instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
seen.add(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
};
|
||||
|
||||
// Cut long messages
|
||||
let stringifyJson = JSON.stringify(obj, getCircularReplacer());
|
||||
if (stringifyJson.length > this.instance.MAX_LENGTH_STRING_JSON) {
|
||||
stringifyJson = `${stringifyJson.substring(0, this.instance.MAX_LENGTH_STRING_JSON)}...`;
|
||||
}
|
||||
|
||||
if (globalThis['LOG_JSNLOG_RESULTS']) {
|
||||
console.log(stringifyJson);
|
||||
}
|
||||
|
||||
return stringifyJson;
|
||||
};
|
||||
|
||||
// Initialize JL to send logs
|
||||
JL.setOptions({
|
||||
defaultAjaxUrl: openVidu.httpUri + this.instance.JSNLOG_URL,
|
||||
serialize: logSerializer,
|
||||
enabled: true
|
||||
});
|
||||
JL().setOptions({
|
||||
appenders: [this.instance.currentAppender]
|
||||
});
|
||||
|
||||
this.instance.isJSNLogSetup = true;
|
||||
this.instance.loggingSessionId = sessionId;
|
||||
this.instance.info('JSNLog configured.');
|
||||
}
|
||||
} catch (e) {
|
||||
// Print error
|
||||
console.error('Error configuring JSNLog: ');
|
||||
console.error(e);
|
||||
// Restore defaults values just in case any exception happen-
|
||||
this.instance.disableLogger();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
static getInstance(): OpenViduLogger {
|
||||
if (!OpenViduLogger.instance) {
|
||||
OpenViduLogger.instance = new OpenViduLogger();
|
||||
}
|
||||
return OpenViduLogger.instance;
|
||||
}
|
||||
|
||||
private static isInvalidResponse(xhr: XMLHttpRequest) {
|
||||
return xhr.status == 401 || xhr.status == 403 || xhr.status == 404 || xhr.status == 0;
|
||||
}
|
||||
|
||||
private canConfigureJSNLog(openVidu: OpenVidu, logger: OpenViduLogger): boolean {
|
||||
return openVidu.session.sessionId != logger.loggingSessionId;
|
||||
}
|
||||
|
||||
private isOpenViduBrowserLogsDebugActive(openVidu: OpenVidu) {
|
||||
return (
|
||||
openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug ||
|
||||
openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug_app
|
||||
);
|
||||
}
|
||||
|
||||
// Return console functions with jsnlog integration
|
||||
private getConsoleWithJSNLog() {
|
||||
return (function (openViduLogger: OpenViduLogger) {
|
||||
return {
|
||||
log: function (...args) {
|
||||
openViduLogger.defaultConsoleLogger.log.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
|
||||
if (openViduLogger.isJSNLogSetup) {
|
||||
JL().info(arguments);
|
||||
}
|
||||
},
|
||||
info: function (...args) {
|
||||
openViduLogger.defaultConsoleLogger.info.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
|
||||
if (openViduLogger.isJSNLogSetup) {
|
||||
JL().info(arguments);
|
||||
}
|
||||
},
|
||||
debug: function (...args) {
|
||||
openViduLogger.defaultConsoleLogger.debug.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
|
||||
},
|
||||
warn: function (...args) {
|
||||
openViduLogger.defaultConsoleLogger.warn.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
|
||||
if (openViduLogger.isJSNLogSetup) {
|
||||
JL().warn(arguments);
|
||||
}
|
||||
},
|
||||
error: function (...args) {
|
||||
openViduLogger.defaultConsoleLogger.error.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
|
||||
if (openViduLogger.isJSNLogSetup) {
|
||||
JL().error(arguments);
|
||||
}
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
}
|
||||
|
||||
private replaceWindowConsole() {
|
||||
globalThis.console = this.defaultConsoleLogger.logger;
|
||||
globalThis.console.log = this.getConsoleWithJSNLog().log;
|
||||
globalThis.console.info = this.getConsoleWithJSNLog().info;
|
||||
globalThis.console.debug = this.getConsoleWithJSNLog().debug;
|
||||
globalThis.console.warn = this.getConsoleWithJSNLog().warn;
|
||||
globalThis.console.error = this.getConsoleWithJSNLog().error;
|
||||
}
|
||||
|
||||
private disableLogger() {
|
||||
JL.setOptions({ enabled: false });
|
||||
this.isJSNLogSetup = false;
|
||||
this.loggingSessionId = undefined;
|
||||
this.currentAppender = undefined;
|
||||
globalThis.console = this.defaultConsoleLogger.logger;
|
||||
globalThis.console.log = this.defaultConsoleLogger.log;
|
||||
globalThis.console.info = this.defaultConsoleLogger.info;
|
||||
globalThis.console.debug = this.defaultConsoleLogger.debug;
|
||||
globalThis.console.warn = this.defaultConsoleLogger.warn;
|
||||
globalThis.console.error = this.defaultConsoleLogger.error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
log(...args: any[]) {
|
||||
if (!this.isProdMode) {
|
||||
this.defaultConsoleLogger.log.apply(this.defaultConsoleLogger.logger, arguments);
|
||||
}
|
||||
if (this.isJSNLogSetup) {
|
||||
JL().info(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
debug(...args: any[]) {
|
||||
if (!this.isProdMode) {
|
||||
this.defaultConsoleLogger.debug.apply(this.defaultConsoleLogger.logger, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
info(...args: any[]) {
|
||||
if (!this.isProdMode) {
|
||||
this.defaultConsoleLogger.info.apply(this.defaultConsoleLogger.logger, arguments);
|
||||
}
|
||||
if (this.isJSNLogSetup) {
|
||||
JL().info(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
warn(...args: any[]) {
|
||||
this.defaultConsoleLogger.warn.apply(this.defaultConsoleLogger.logger, arguments);
|
||||
if (this.isJSNLogSetup) {
|
||||
JL().warn(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
error(...args: any[]) {
|
||||
this.defaultConsoleLogger.error.apply(this.defaultConsoleLogger.logger, arguments);
|
||||
if (this.isJSNLogSetup) {
|
||||
JL().error(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
flush() {
|
||||
if (this.isJSNLogSetup && this.currentAppender != null) {
|
||||
this.currentAppender.sendBatch();
|
||||
}
|
||||
}
|
||||
|
||||
enableProdMode() {
|
||||
this.isProdMode = true;
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
export enum OpenViduLoggerConfiguration {
|
||||
disabled = 'disabled',
|
||||
debug = 'debug',
|
||||
debug_app = 'debug_app'
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
// Last time updated on June 08, 2018
|
||||
|
||||
// Latest file can be found here: https://cdn.webrtc-experiment.com/getScreenId.js
|
||||
|
||||
// Muaz Khan - www.MuazKhan.com
|
||||
// MIT License - www.WebRTC-Experiment.com/licence
|
||||
// Documentation - https://github.com/muaz-khan/getScreenId.
|
||||
|
||||
// ______________
|
||||
// getScreenId.js
|
||||
|
||||
/*
|
||||
getScreenId(function (error, sourceId, screen_constraints) {
|
||||
// error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome'
|
||||
// sourceId == null || 'string' || 'firefox'
|
||||
|
||||
if(microsoftEdge) {
|
||||
navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure);
|
||||
}
|
||||
else {
|
||||
navigator.mediaDevices.getUserMedia(screen_constraints).then(onSuccess)catch(onFailure);
|
||||
}
|
||||
}, 'pass second parameter only if you want system audio');
|
||||
*/
|
||||
|
||||
globalThis.getScreenId = function (firefoxString, callback, custom_parameter) {
|
||||
if (navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob)) {
|
||||
// microsoft edge => navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure);
|
||||
callback({
|
||||
video: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// for Firefox:
|
||||
// sourceId == 'firefox'
|
||||
// screen_constraints = {...}
|
||||
if (!!navigator.mozGetUserMedia) {
|
||||
callback(null, 'firefox', {
|
||||
video: {
|
||||
mozMediaSource: firefoxString,
|
||||
mediaSource: firefoxString
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
globalThis.addEventListener('message', onIFrameCallback);
|
||||
|
||||
function onIFrameCallback(event) {
|
||||
if (!event.data) return;
|
||||
|
||||
if (event.data.chromeMediaSourceId) {
|
||||
if (event.data.chromeMediaSourceId === 'PermissionDeniedError') {
|
||||
callback('permission-denied');
|
||||
} else {
|
||||
callback(
|
||||
null,
|
||||
event.data.chromeMediaSourceId,
|
||||
getScreenConstraints(null, event.data.chromeMediaSourceId, event.data.canRequestAudioTrack)
|
||||
);
|
||||
}
|
||||
|
||||
// this event listener is no more needed
|
||||
globalThis.removeEventListener('message', onIFrameCallback);
|
||||
}
|
||||
|
||||
if (event.data.chromeExtensionStatus) {
|
||||
callback(event.data.chromeExtensionStatus, null, getScreenConstraints(event.data.chromeExtensionStatus));
|
||||
|
||||
// this event listener is no more needed
|
||||
globalThis.removeEventListener('message', onIFrameCallback);
|
||||
}
|
||||
}
|
||||
|
||||
if (!custom_parameter) {
|
||||
setTimeout(postGetSourceIdMessage, 100);
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
postGetSourceIdMessage(custom_parameter);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
function getScreenConstraints(error, sourceId, canRequestAudioTrack) {
|
||||
var screen_constraints = {
|
||||
audio: false,
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: error ? 'screen' : 'desktop',
|
||||
maxWidth: globalThis.screen.width > 1920 ? globalThis.screen.width : 1920,
|
||||
maxHeight: globalThis.screen.height > 1080 ? globalThis.screen.height : 1080
|
||||
},
|
||||
optional: []
|
||||
}
|
||||
};
|
||||
|
||||
if (!!canRequestAudioTrack) {
|
||||
screen_constraints.audio = {
|
||||
mandatory: {
|
||||
chromeMediaSource: error ? 'screen' : 'desktop'
|
||||
// echoCancellation: true
|
||||
},
|
||||
optional: []
|
||||
};
|
||||
}
|
||||
|
||||
if (sourceId) {
|
||||
screen_constraints.video.mandatory.chromeMediaSourceId = sourceId;
|
||||
|
||||
if (screen_constraints.audio && screen_constraints.audio.mandatory) {
|
||||
screen_constraints.audio.mandatory.chromeMediaSourceId = sourceId;
|
||||
}
|
||||
}
|
||||
|
||||
return screen_constraints;
|
||||
}
|
||||
|
||||
function postGetSourceIdMessage(custom_parameter) {
|
||||
if (!iframe) {
|
||||
loadIFrame(function () {
|
||||
postGetSourceIdMessage(custom_parameter);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iframe.isLoaded) {
|
||||
setTimeout(function () {
|
||||
postGetSourceIdMessage(custom_parameter);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!custom_parameter) {
|
||||
iframe.contentWindow.postMessage(
|
||||
{
|
||||
captureSourceId: true
|
||||
},
|
||||
'*'
|
||||
);
|
||||
} else if (!!custom_parameter.forEach) {
|
||||
iframe.contentWindow.postMessage(
|
||||
{
|
||||
captureCustomSourceId: custom_parameter
|
||||
},
|
||||
'*'
|
||||
);
|
||||
} else {
|
||||
iframe.contentWindow.postMessage(
|
||||
{
|
||||
captureSourceIdWithAudio: true
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var iframe;
|
||||
|
||||
// this function is used in RTCMultiConnection v3
|
||||
globalThis.getScreenConstraints = function (callback) {
|
||||
loadIFrame(function () {
|
||||
getScreenId(function (error, sourceId, screen_constraints) {
|
||||
if (!screen_constraints) {
|
||||
screen_constraints = {
|
||||
video: true
|
||||
};
|
||||
}
|
||||
|
||||
callback(error, screen_constraints.video);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function loadIFrame(loadCallback) {
|
||||
if (iframe) {
|
||||
loadCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
iframe = document.createElement('iframe');
|
||||
iframe.onload = function () {
|
||||
iframe.isLoaded = true;
|
||||
loadCallback();
|
||||
};
|
||||
iframe.src = 'https://openvidu.github.io/openvidu-screen-sharing-chrome-extension/';
|
||||
iframe.style.display = 'none';
|
||||
(document.body || document.documentElement).appendChild(iframe);
|
||||
}
|
||||
|
||||
globalThis.getChromeExtensionStatus = function (callback) {
|
||||
// for Firefox:
|
||||
if (!!navigator.mozGetUserMedia) {
|
||||
callback('installed-enabled');
|
||||
return;
|
||||
}
|
||||
|
||||
globalThis.addEventListener('message', onIFrameCallback);
|
||||
|
||||
function onIFrameCallback(event) {
|
||||
if (!event.data) return;
|
||||
|
||||
if (event.data.chromeExtensionStatus) {
|
||||
callback(event.data.chromeExtensionStatus);
|
||||
|
||||
// this event listener is no more needed
|
||||
globalThis.removeEventListener('message', onIFrameCallback);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(postGetChromeExtensionStatusMessage, 100);
|
||||
};
|
||||
|
||||
function postGetChromeExtensionStatusMessage() {
|
||||
if (!iframe) {
|
||||
loadIFrame(postGetChromeExtensionStatusMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iframe.isLoaded) {
|
||||
setTimeout(postGetChromeExtensionStatusMessage, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
iframe.contentWindow.postMessage(
|
||||
{
|
||||
getChromeExtensionStatus: true
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
|
||||
exports.getScreenId = globalThis.getScreenId;
|
|
@ -1,162 +0,0 @@
|
|||
// global variables
|
||||
var chromeMediaSource = 'screen';
|
||||
var sourceId;
|
||||
var screenCallback;
|
||||
|
||||
if (typeof window !== 'undefined' && typeof navigator !== 'undefined' && typeof navigator.userAgent !== 'undefined') {
|
||||
var isFirefox = typeof window.InstallTrigger !== 'undefined';
|
||||
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
|
||||
var isChrome = !!window.chrome && !isOpera;
|
||||
|
||||
window.addEventListener('message', function (event) {
|
||||
if (event.origin != window.location.origin) {
|
||||
return;
|
||||
}
|
||||
onMessageCallback(event.data);
|
||||
});
|
||||
}
|
||||
|
||||
// and the function that handles received messages
|
||||
function onMessageCallback(data) {
|
||||
// "cancel" button is clicked
|
||||
if (data == 'PermissionDeniedError') {
|
||||
if (screenCallback) return screenCallback('PermissionDeniedError');
|
||||
else throw new Error('PermissionDeniedError');
|
||||
}
|
||||
// extension notified his presence
|
||||
if (data == 'rtcmulticonnection-extension-loaded') {
|
||||
chromeMediaSource = 'desktop';
|
||||
}
|
||||
// extension shared temp sourceId
|
||||
if (data.sourceId && screenCallback) {
|
||||
screenCallback((sourceId = data.sourceId), data.canRequestAudioTrack === true);
|
||||
}
|
||||
}
|
||||
|
||||
// this method can be used to check if chrome extension is installed & enabled.
|
||||
function isChromeExtensionAvailable(callback) {
|
||||
if (!callback) return;
|
||||
if (chromeMediaSource == 'desktop') return callback(true);
|
||||
|
||||
// ask extension if it is available
|
||||
window.postMessage('are-you-there', '*');
|
||||
setTimeout(function () {
|
||||
if (chromeMediaSource == 'screen') {
|
||||
callback(false);
|
||||
} else callback(true);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// this function can be used to get "source-id" from the extension
|
||||
function getSourceId(callback) {
|
||||
if (!callback) throw '"callback" parameter is mandatory.';
|
||||
if (sourceId) return callback(sourceId);
|
||||
screenCallback = callback;
|
||||
window.postMessage('get-sourceId', '*');
|
||||
}
|
||||
|
||||
// this function can be used to get "source-id" from the extension
|
||||
function getCustomSourceId(arr, callback) {
|
||||
if (!arr || !arr.forEach) throw '"arr" parameter is mandatory and it must be an array.';
|
||||
if (!callback) throw '"callback" parameter is mandatory.';
|
||||
|
||||
if (sourceId) return callback(sourceId);
|
||||
|
||||
screenCallback = callback;
|
||||
window.postMessage(
|
||||
{
|
||||
'get-custom-sourceId': arr
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
||||
|
||||
// this function can be used to get "source-id" from the extension
|
||||
function getSourceIdWithAudio(callback) {
|
||||
if (!callback) throw '"callback" parameter is mandatory.';
|
||||
if (sourceId) return callback(sourceId);
|
||||
|
||||
screenCallback = callback;
|
||||
window.postMessage('audio-plus-tab', '*');
|
||||
}
|
||||
|
||||
function getChromeExtensionStatus(extensionid, callback) {
|
||||
if (isFirefox) return callback('not-chrome');
|
||||
if (arguments.length != 2) {
|
||||
callback = extensionid;
|
||||
extensionid = 'lfcgfepafnobdloecchnfaclibenjold'; // default extension-id
|
||||
}
|
||||
var image = document.createElement('img');
|
||||
image.src = 'chrome-extension://' + extensionid + '/icon.png';
|
||||
image.onload = function () {
|
||||
chromeMediaSource = 'screen';
|
||||
window.postMessage('are-you-there', '*');
|
||||
setTimeout(function () {
|
||||
if (chromeMediaSource == 'screen') {
|
||||
callback('installed-disabled');
|
||||
} else callback('installed-enabled');
|
||||
}, 2000);
|
||||
};
|
||||
image.onerror = function () {
|
||||
callback('not-installed');
|
||||
};
|
||||
}
|
||||
|
||||
function getScreenConstraintsWithAudio(callback) {
|
||||
getScreenConstraints(callback, true);
|
||||
}
|
||||
|
||||
// this function explains how to use above methods/objects
|
||||
function getScreenConstraints(callback, captureSourceIdWithAudio) {
|
||||
sourceId = '';
|
||||
var firefoxScreenConstraints = {
|
||||
mozMediaSource: 'window',
|
||||
mediaSource: 'window'
|
||||
};
|
||||
if (isFirefox) return callback(null, firefoxScreenConstraints);
|
||||
// this statement defines getUserMedia constraints
|
||||
// that will be used to capture content of screen
|
||||
var screen_constraints = {
|
||||
mandatory: {
|
||||
chromeMediaSource: chromeMediaSource,
|
||||
maxWidth: screen.width > 1920 ? screen.width : 1920,
|
||||
maxHeight: screen.height > 1080 ? screen.height : 1080
|
||||
},
|
||||
optional: []
|
||||
};
|
||||
// this statement verifies chrome extension availability
|
||||
// if installed and available then it will invoke extension API
|
||||
// otherwise it will fallback to command-line based screen capturing API
|
||||
if (chromeMediaSource == 'desktop' && !sourceId) {
|
||||
if (captureSourceIdWithAudio) {
|
||||
getSourceIdWithAudio(function (sourceId, canRequestAudioTrack) {
|
||||
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
|
||||
|
||||
if (canRequestAudioTrack) {
|
||||
screen_constraints.canRequestAudioTrack = true;
|
||||
}
|
||||
callback(sourceId == 'PermissionDeniedError' ? sourceId : null, screen_constraints);
|
||||
});
|
||||
} else {
|
||||
getSourceId(function (sourceId) {
|
||||
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
|
||||
callback(sourceId == 'PermissionDeniedError' ? sourceId : null, screen_constraints);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// this statement sets gets 'sourceId" and sets "chromeMediaSourceId"
|
||||
if (chromeMediaSource == 'desktop') {
|
||||
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
|
||||
}
|
||||
|
||||
// now invoking native getUserMedia API
|
||||
callback(null, screen_constraints);
|
||||
}
|
||||
|
||||
exports.getScreenConstraints = getScreenConstraints;
|
||||
exports.getScreenConstraintsWithAudio = getScreenConstraintsWithAudio;
|
||||
exports.isChromeExtensionAvailable = isChromeExtensionAvailable;
|
||||
exports.getChromeExtensionStatus = getChromeExtensionStatus;
|
||||
exports.getSourceId = getSourceId;
|
|
@ -1,233 +0,0 @@
|
|||
import platform = require('platform');
|
||||
|
||||
export class PlatformUtils {
|
||||
protected static instance: PlatformUtils;
|
||||
constructor() {}
|
||||
|
||||
static getInstance(): PlatformUtils {
|
||||
if (!this.instance) {
|
||||
this.instance = new PlatformUtils();
|
||||
}
|
||||
return PlatformUtils.instance;
|
||||
}
|
||||
|
||||
public isChromeBrowser(): boolean {
|
||||
return platform.name === 'Chrome';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isSafariBrowser(): boolean {
|
||||
return platform.name === 'Safari';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isChromeMobileBrowser(): boolean {
|
||||
return platform.name === 'Chrome Mobile';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isFirefoxBrowser(): boolean {
|
||||
return platform.name === 'Firefox';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isFirefoxMobileBrowser(): boolean {
|
||||
return platform.name === 'Firefox Mobile' || platform.name === 'Firefox for iOS';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isOperaBrowser(): boolean {
|
||||
return platform.name === 'Opera';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isOperaMobileBrowser(): boolean {
|
||||
return platform.name === 'Opera Mobile';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isEdgeBrowser(): boolean {
|
||||
const version = platform?.version ? parseFloat(platform.version) : -1;
|
||||
return platform.name === 'Microsoft Edge' && version >= 80;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isEdgeMobileBrowser(): boolean {
|
||||
const version = platform?.version ? parseFloat(platform.version) : -1;
|
||||
return platform.name === 'Microsoft Edge' && (platform.os?.family === 'Android' || platform.os?.family === 'iOS') && version > 45;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isAndroidBrowser(): boolean {
|
||||
return platform.name === 'Android Browser';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isElectron(): boolean {
|
||||
return platform.name === 'Electron';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isNodeJs(): boolean {
|
||||
return platform.name === 'Node.js';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isSamsungBrowser(): boolean {
|
||||
return platform.name === 'Samsung Internet Mobile' || platform.name === 'Samsung Internet';
|
||||
}
|
||||
|
||||
// TODO: This method exists to overcome bug https://github.com/bestiejs/platform.js/issues/184
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isMotorolaEdgeDevice(): boolean {
|
||||
return platform.product?.toLowerCase().includes('motorola edge') || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isIPhoneOrIPad(): boolean {
|
||||
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
|
||||
const isTouchable = 'ontouchend' in document;
|
||||
const isIPad = /\b(\w*Macintosh\w*)\b/.test(userAgent) && isTouchable;
|
||||
const isIPhone = /\b(\w*iPhone\w*)\b/.test(userAgent) && /\b(\w*Mobile\w*)\b/.test(userAgent) && isTouchable;
|
||||
return isIPad || isIPhone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isIOSWithSafari(): boolean {
|
||||
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
|
||||
return (
|
||||
this.isIPhoneOrIPad() &&
|
||||
/\b(\w*Apple\w*)\b/.test(navigator.vendor) &&
|
||||
/\b(\w*Safari\w*)\b/.test(userAgent) &&
|
||||
!/\b(\w*CriOS\w*)\b/.test(userAgent) &&
|
||||
!/\b(\w*FxiOS\w*)\b/.test(userAgent)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isIonicIos(): boolean {
|
||||
return this.isIPhoneOrIPad() && platform.ua!!.indexOf('Safari') === -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isIonicAndroid(): boolean {
|
||||
return platform.os!!.family === 'Android' && platform.name == 'Android Browser';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isMobileDevice(): boolean {
|
||||
return platform.os!!.family === 'iOS' || platform.os!!.family === 'Android';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isReactNative(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isChromium(): boolean {
|
||||
return (
|
||||
this.isChromeBrowser() ||
|
||||
this.isChromeMobileBrowser() ||
|
||||
this.isOperaBrowser() ||
|
||||
this.isOperaMobileBrowser() ||
|
||||
this.isEdgeBrowser() ||
|
||||
this.isEdgeMobileBrowser() ||
|
||||
this.isSamsungBrowser() ||
|
||||
this.isIonicAndroid() ||
|
||||
this.isIonicIos() ||
|
||||
this.isElectron() ||
|
||||
// TODO: remove when possible
|
||||
this.isMotorolaEdgeDevice()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public canScreenShare(): boolean {
|
||||
const version = platform?.version ? parseFloat(platform.version) : -1;
|
||||
// Reject mobile devices
|
||||
if (this.isMobileDevice()) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
this.isChromeBrowser() ||
|
||||
this.isFirefoxBrowser() ||
|
||||
this.isOperaBrowser() ||
|
||||
this.isElectron() ||
|
||||
this.isEdgeBrowser() ||
|
||||
(this.isSafariBrowser() && version >= 13)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public getName(): string {
|
||||
return platform.name || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public getVersion(): string {
|
||||
return platform.version || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public getFamily(): string {
|
||||
return platform.os!!.family || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public getDescription(): string {
|
||||
return platform.description || '';
|
||||
}
|
||||
}
|
|
@ -1,600 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import freeice = require('freeice');
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { TypeOfVideo } from '../Enums/TypeOfVideo';
|
||||
import { ExceptionEventName } from '../Events/ExceptionEvent';
|
||||
import { OpenViduLogger } from '../Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../Utils/Platform';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
export interface WebRtcPeerConfiguration {
|
||||
mediaConstraints: {
|
||||
audio: boolean;
|
||||
video: boolean;
|
||||
};
|
||||
simulcast: boolean;
|
||||
mediaServer: string;
|
||||
onIceCandidate: (event: RTCIceCandidate) => void;
|
||||
onIceConnectionStateException: (exceptionName: ExceptionEventName, message: string, data?: any) => void;
|
||||
iceServers?: RTCIceServer[];
|
||||
rtcConfiguration?: RTCConfiguration;
|
||||
mediaStream?: MediaStream | null;
|
||||
mode?: 'sendonly' | 'recvonly' | 'sendrecv';
|
||||
id?: string;
|
||||
typeOfVideo: TypeOfVideo | undefined;
|
||||
}
|
||||
|
||||
export class WebRtcPeer {
|
||||
pc: RTCPeerConnection;
|
||||
remoteCandidatesQueue: RTCIceCandidate[] = [];
|
||||
localCandidatesQueue: RTCIceCandidate[] = [];
|
||||
|
||||
// Same as WebRtcPeerConfiguration but without optional fields.
|
||||
protected configuration: Required<WebRtcPeerConfiguration>;
|
||||
|
||||
private iceCandidateList: RTCIceCandidate[] = [];
|
||||
|
||||
constructor(configuration: WebRtcPeerConfiguration) {
|
||||
platform = PlatformUtils.getInstance();
|
||||
|
||||
this.configuration = {
|
||||
...configuration,
|
||||
iceServers: !!configuration.iceServers && configuration.iceServers.length > 0 ? configuration.iceServers : freeice(),
|
||||
rtcConfiguration: configuration.rtcConfiguration !== undefined ? configuration.rtcConfiguration : {},
|
||||
mediaStream: configuration.mediaStream !== undefined ? configuration.mediaStream : null,
|
||||
mode: !!configuration.mode ? configuration.mode : 'sendrecv',
|
||||
id: !!configuration.id ? configuration.id : this.generateUniqueId()
|
||||
};
|
||||
// prettier-ignore
|
||||
logger.debug(`[WebRtcPeer] configuration:\n${JSON.stringify(this.configuration, null, 2)}`);
|
||||
|
||||
let rtcConfiguration: RTCConfiguration = this.configuration.rtcConfiguration
|
||||
? this.configuration.rtcConfiguration
|
||||
: { iceServers: this.configuration.iceServers };
|
||||
if (!rtcConfiguration.iceServers && this.configuration.iceServers) {
|
||||
rtcConfiguration.iceServers = this.configuration.iceServers;
|
||||
}
|
||||
this.pc = new RTCPeerConnection(rtcConfiguration);
|
||||
|
||||
this.pc.addEventListener('icecandidate', (event: RTCPeerConnectionIceEvent) => {
|
||||
if (event.candidate !== null) {
|
||||
// `RTCPeerConnectionIceEvent.candidate` is supposed to be an RTCIceCandidate:
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectioniceevent-candidate
|
||||
//
|
||||
// But in practice, it is actually an RTCIceCandidateInit that can be used to
|
||||
// obtain a proper candidate, using the RTCIceCandidate constructor:
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-constructor
|
||||
const candidateInit: RTCIceCandidateInit = event.candidate as RTCIceCandidateInit;
|
||||
const iceCandidate = new RTCIceCandidate(candidateInit);
|
||||
|
||||
this.configuration.onIceCandidate(iceCandidate);
|
||||
if (iceCandidate.candidate !== '') {
|
||||
this.localCandidatesQueue.push(iceCandidate);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.pc.addEventListener('signalingstatechange', async () => {
|
||||
if (this.pc.signalingState === 'stable') {
|
||||
// SDP Offer/Answer finished. Add stored remote candidates.
|
||||
while (this.iceCandidateList.length > 0) {
|
||||
let candidate = this.iceCandidateList.shift();
|
||||
try {
|
||||
await this.pc.addIceCandidate(<RTCIceCandidate>candidate);
|
||||
} catch (error) {
|
||||
logger.error('Error when calling RTCPeerConnection#addIceCandidate for RTCPeerConnection ' + this.getId(), error);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return this.configuration.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method frees the resources used by WebRtcPeer
|
||||
*/
|
||||
dispose() {
|
||||
logger.debug('Disposing WebRtcPeer');
|
||||
if (this.pc) {
|
||||
if (this.pc.signalingState === 'closed') {
|
||||
return;
|
||||
}
|
||||
this.pc.close();
|
||||
this.remoteCandidatesQueue = [];
|
||||
this.localCandidatesQueue = [];
|
||||
}
|
||||
}
|
||||
|
||||
// DEPRECATED LEGACY METHOD: Old WebRTC versions don't implement
|
||||
// Transceivers, and instead depend on the deprecated
|
||||
// "offerToReceiveAudio" and "offerToReceiveVideo".
|
||||
private createOfferLegacy(): Promise<RTCSessionDescriptionInit> {
|
||||
if (!!this.configuration.mediaStream) {
|
||||
this.deprecatedPeerConnectionTrackApi();
|
||||
}
|
||||
|
||||
const hasAudio = this.configuration.mediaConstraints.audio;
|
||||
const hasVideo = this.configuration.mediaConstraints.video;
|
||||
|
||||
const options: RTCOfferOptions = {
|
||||
offerToReceiveAudio: this.configuration.mode !== 'sendonly' && hasAudio,
|
||||
offerToReceiveVideo: this.configuration.mode !== 'sendonly' && hasVideo
|
||||
};
|
||||
|
||||
logger.debug('[createOfferLegacy] RTCPeerConnection.createOffer() options:', JSON.stringify(options));
|
||||
|
||||
return this.pc.createOffer(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an SDP offer from the local RTCPeerConnection to send to the other peer.
|
||||
* Only if the negotiation was initiated by this peer.
|
||||
*/
|
||||
async createOffer(): Promise<RTCSessionDescriptionInit> {
|
||||
// TODO: Delete this conditional when all supported browsers are
|
||||
// modern enough to implement the Transceiver methods.
|
||||
if (!('addTransceiver' in this.pc)) {
|
||||
logger.warn(
|
||||
'[createOffer] Method RTCPeerConnection.addTransceiver() is NOT available; using LEGACY offerToReceive{Audio,Video}'
|
||||
);
|
||||
return this.createOfferLegacy();
|
||||
} else {
|
||||
logger.debug('[createOffer] Method RTCPeerConnection.addTransceiver() is available; using it');
|
||||
}
|
||||
|
||||
// Spec doc: https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver
|
||||
|
||||
if (this.configuration.mode !== 'recvonly') {
|
||||
// To send media, assume that all desired media tracks have been
|
||||
// already added by higher level code to our MediaStream.
|
||||
|
||||
if (!this.configuration.mediaStream) {
|
||||
throw new Error(
|
||||
`[WebRtcPeer.createOffer] Direction is '${this.configuration.mode}', but no stream was configured to be sent`
|
||||
);
|
||||
}
|
||||
|
||||
for (const track of this.configuration.mediaStream.getTracks()) {
|
||||
const tcInit: RTCRtpTransceiverInit = {
|
||||
direction: this.configuration.mode,
|
||||
streams: [this.configuration.mediaStream]
|
||||
};
|
||||
|
||||
if (track.kind === 'video' && this.configuration.simulcast) {
|
||||
// Check if the requested size is enough to ask for 3 layers.
|
||||
const trackSettings = track.getSettings();
|
||||
const trackConsts = track.getConstraints();
|
||||
|
||||
const trackWidth: number =
|
||||
trackSettings.width ?? (trackConsts.width as ConstrainULongRange).ideal ?? (trackConsts.width as number) ?? 0;
|
||||
const trackHeight: number =
|
||||
trackSettings.height ?? (trackConsts.height as ConstrainULongRange).ideal ?? (trackConsts.height as number) ?? 0;
|
||||
logger.info(`[createOffer] Video track dimensions: ${trackWidth}x${trackHeight}`);
|
||||
|
||||
const trackPixels = trackWidth * trackHeight;
|
||||
let maxLayers = 0;
|
||||
if (trackPixels >= 960 * 540) {
|
||||
maxLayers = 3;
|
||||
} else if (trackPixels >= 480 * 270) {
|
||||
maxLayers = 2;
|
||||
} else {
|
||||
maxLayers = 1;
|
||||
}
|
||||
|
||||
tcInit.sendEncodings = [];
|
||||
for (let l = 0; l < maxLayers; l++) {
|
||||
const layerDiv = 2 ** (maxLayers - l - 1);
|
||||
|
||||
const encoding: RTCRtpEncodingParameters = {
|
||||
rid: 'rdiv' + layerDiv.toString(),
|
||||
|
||||
// @ts-ignore -- Property missing from DOM types.
|
||||
scalabilityMode: 'L1T1'
|
||||
};
|
||||
|
||||
if (['detail', 'text'].includes(track.contentHint)) {
|
||||
// Prioritize best resolution, for maximum picture detail.
|
||||
encoding.scaleResolutionDownBy = 1.0;
|
||||
|
||||
// @ts-ignore -- Property missing from DOM types.
|
||||
encoding.maxFramerate = Math.floor(30 / layerDiv);
|
||||
} else {
|
||||
encoding.scaleResolutionDownBy = layerDiv;
|
||||
}
|
||||
|
||||
tcInit.sendEncodings.push(encoding);
|
||||
}
|
||||
}
|
||||
|
||||
const tc = this.pc.addTransceiver(track, tcInit);
|
||||
|
||||
if (track.kind === 'video') {
|
||||
let sendParams = tc.sender.getParameters();
|
||||
let needSetParams = false;
|
||||
|
||||
if (!sendParams.degradationPreference?.length) {
|
||||
// degradationPreference for video: "balanced", "maintain-framerate", "maintain-resolution".
|
||||
// https://www.w3.org/TR/2018/CR-webrtc-20180927/#dom-rtcdegradationpreference
|
||||
if (['detail', 'text'].includes(track.contentHint)) {
|
||||
sendParams.degradationPreference = 'maintain-resolution';
|
||||
} else {
|
||||
sendParams.degradationPreference = 'balanced';
|
||||
}
|
||||
|
||||
logger.info(`[createOffer] Video sender Degradation Preference set: ${sendParams.degradationPreference}`);
|
||||
|
||||
// FIXME: Firefox implements degradationPreference on each individual encoding!
|
||||
// (set it on every element of the sendParams.encodings array)
|
||||
|
||||
needSetParams = true;
|
||||
}
|
||||
|
||||
// FIXME: Check that the simulcast encodings were applied.
|
||||
// Firefox doesn't implement `RTCRtpTransceiverInit.sendEncodings`
|
||||
// so the only way to enable simulcast is with `RTCRtpSender.setParameters()`.
|
||||
//
|
||||
// This next block can be deleted when Firefox fixes bug #1396918:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
|
||||
//
|
||||
// NOTE: This is done in a way that is compatible with all browsers, to save on
|
||||
// browser-conditional code. The idea comes from WebRTC Adapter.js:
|
||||
// * https://github.com/webrtcHacks/adapter/issues/998
|
||||
// * https://github.com/webrtcHacks/adapter/blob/v7.7.0/src/js/firefox/firefox_shim.js#L231-L255
|
||||
if (this.configuration.simulcast) {
|
||||
if (sendParams.encodings?.length !== tcInit.sendEncodings!.length) {
|
||||
sendParams.encodings = tcInit.sendEncodings!;
|
||||
|
||||
needSetParams = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needSetParams) {
|
||||
logger.debug(`[createOffer] Setting new RTCRtpSendParameters to video sender`);
|
||||
try {
|
||||
await tc.sender.setParameters(sendParams);
|
||||
} catch (error) {
|
||||
let message = `[WebRtcPeer.createOffer] Cannot set RTCRtpSendParameters to video sender`;
|
||||
if (error instanceof Error) {
|
||||
message += `: ${error.message}`;
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG: Uncomment for details.
|
||||
// if (track.kind === "video" && this.configuration.simulcast) {
|
||||
// // Print browser capabilities.
|
||||
// // prettier-ignore
|
||||
// logger.debug(`[createOffer] Transceiver send capabilities (static):\n${JSON.stringify(RTCRtpSender.getCapabilities?.("video"), null, 2)}`);
|
||||
// // prettier-ignore
|
||||
// logger.debug(`[createOffer] Transceiver recv capabilities (static):\n${JSON.stringify(RTCRtpReceiver.getCapabilities?.("video"), null, 2)}`);
|
||||
|
||||
// // Print requested Transceiver encodings and parameters.
|
||||
// // prettier-ignore
|
||||
// logger.debug(`[createOffer] Transceiver send encodings (requested):\n${JSON.stringify(tcInit.sendEncodings, null, 2)}`);
|
||||
// // prettier-ignore
|
||||
// logger.debug(`[createOffer] Transceiver send parameters (accepted):\n${JSON.stringify(tc.sender.getParameters(), null, 2)}`);
|
||||
// }
|
||||
}
|
||||
} else {
|
||||
// To just receive media, create new recvonly transceivers.
|
||||
for (const kind of ['audio', 'video']) {
|
||||
// Check if the media kind should be used.
|
||||
if (!this.configuration.mediaConstraints[kind]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.configuration.mediaStream = new MediaStream();
|
||||
this.pc.addTransceiver(kind, {
|
||||
direction: this.configuration.mode,
|
||||
streams: [this.configuration.mediaStream]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let sdpOffer: RTCSessionDescriptionInit;
|
||||
try {
|
||||
sdpOffer = await this.pc.createOffer();
|
||||
} catch (error) {
|
||||
let message = `[WebRtcPeer.createOffer] Browser failed creating an SDP Offer`;
|
||||
if (error instanceof Error) {
|
||||
message += `: ${error.message}`;
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return sdpOffer;
|
||||
}
|
||||
|
||||
deprecatedPeerConnectionTrackApi() {
|
||||
for (const track of this.configuration.mediaStream!.getTracks()) {
|
||||
this.pc.addTrack(track, this.configuration.mediaStream!);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an SDP answer from the local RTCPeerConnection to send to the other peer
|
||||
* Only if the negotiation was initiated by the other peer
|
||||
*/
|
||||
createAnswer(): Promise<RTCSessionDescriptionInit> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// TODO: Delete this conditional when all supported browsers are
|
||||
// modern enough to implement the Transceiver methods.
|
||||
if ('getTransceivers' in this.pc) {
|
||||
logger.debug('[createAnswer] Method RTCPeerConnection.getTransceivers() is available; using it');
|
||||
|
||||
// Ensure that the PeerConnection already contains one Transceiver
|
||||
// for each kind of media.
|
||||
// The Transceivers should have been already created internally by
|
||||
// the PC itself, when `pc.setRemoteDescription(sdpOffer)` was called.
|
||||
|
||||
for (const kind of ['audio', 'video']) {
|
||||
// Check if the media kind should be used.
|
||||
if (!this.configuration.mediaConstraints[kind]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let tc = this.pc.getTransceivers().find((tc) => tc.receiver.track.kind === kind);
|
||||
|
||||
if (tc) {
|
||||
// Enforce our desired direction.
|
||||
tc.direction = this.configuration.mode;
|
||||
} else {
|
||||
return reject(new Error(`${kind} requested, but no transceiver was created from remote description`));
|
||||
}
|
||||
}
|
||||
|
||||
this.pc
|
||||
.createAnswer()
|
||||
.then((sdpAnswer) => resolve(sdpAnswer))
|
||||
.catch((error) => reject(error));
|
||||
} else {
|
||||
// TODO: Delete else branch when all supported browsers are
|
||||
// modern enough to implement the Transceiver methods
|
||||
|
||||
let offerAudio,
|
||||
offerVideo = true;
|
||||
if (!!this.configuration.mediaConstraints) {
|
||||
offerAudio =
|
||||
typeof this.configuration.mediaConstraints.audio === 'boolean' ? this.configuration.mediaConstraints.audio : true;
|
||||
offerVideo =
|
||||
typeof this.configuration.mediaConstraints.video === 'boolean' ? this.configuration.mediaConstraints.video : true;
|
||||
const constraints: RTCOfferOptions = {
|
||||
offerToReceiveAudio: offerAudio,
|
||||
offerToReceiveVideo: offerVideo
|
||||
};
|
||||
(this.pc as RTCPeerConnection).createAnswer(constraints)
|
||||
.then((sdpAnswer) => resolve(sdpAnswer))
|
||||
.catch((error) => reject(error));
|
||||
}
|
||||
}
|
||||
|
||||
// else, there is nothing to do; the legacy createAnswer() options do
|
||||
// not offer any control over which tracks are included in the answer.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This peer initiated negotiation. Step 1/4 of SDP offer-answer protocol
|
||||
*/
|
||||
processLocalOffer(offer: RTCSessionDescriptionInit): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pc
|
||||
.setLocalDescription(offer)
|
||||
.then(() => {
|
||||
const localDescription = this.pc.localDescription;
|
||||
if (!!localDescription) {
|
||||
logger.debug('Local description set', localDescription.sdp);
|
||||
return resolve();
|
||||
} else {
|
||||
return reject('Local description is not defined');
|
||||
}
|
||||
})
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Other peer initiated negotiation. Step 2/4 of SDP offer-answer protocol
|
||||
*/
|
||||
processRemoteOffer(sdpOffer: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const offer: RTCSessionDescriptionInit = {
|
||||
type: 'offer',
|
||||
sdp: sdpOffer
|
||||
};
|
||||
logger.debug('SDP offer received, setting remote description', offer);
|
||||
|
||||
if (this.pc.signalingState === 'closed') {
|
||||
return reject('RTCPeerConnection is closed when trying to set remote description');
|
||||
}
|
||||
this.setRemoteDescription(offer)
|
||||
.then(() => resolve())
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Other peer initiated negotiation. Step 3/4 of SDP offer-answer protocol
|
||||
*/
|
||||
processLocalAnswer(answer: RTCSessionDescriptionInit): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.debug('SDP answer created, setting local description');
|
||||
if (this.pc.signalingState === 'closed') {
|
||||
return reject('RTCPeerConnection is closed when trying to set local description');
|
||||
}
|
||||
this.pc
|
||||
.setLocalDescription(answer)
|
||||
.then(() => resolve())
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This peer initiated negotiation. Step 4/4 of SDP offer-answer protocol
|
||||
*/
|
||||
processRemoteAnswer(sdpAnswer: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const answer: RTCSessionDescriptionInit = {
|
||||
type: 'answer',
|
||||
sdp: sdpAnswer
|
||||
};
|
||||
logger.debug('SDP answer received, setting remote description');
|
||||
|
||||
if (this.pc.signalingState === 'closed') {
|
||||
return reject('RTCPeerConnection is closed when trying to set remote description');
|
||||
}
|
||||
this.setRemoteDescription(answer)
|
||||
.then(() => {
|
||||
// DEBUG: Uncomment for details.
|
||||
// {
|
||||
// const tc = this.pc.getTransceivers().find((tc) => tc.sender.track?.kind === "video");
|
||||
// // prettier-ignore
|
||||
// logger.debug(`[processRemoteAnswer] Transceiver send parameters (effective):\n${JSON.stringify(tc?.sender.getParameters(), null, 2)}`);
|
||||
// }
|
||||
|
||||
resolve();
|
||||
})
|
||||
.catch((error) => reject(error));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
async setRemoteDescription(sdp: RTCSessionDescriptionInit): Promise<void> {
|
||||
return this.pc.setRemoteDescription(sdp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function invoked when an ICE candidate is received
|
||||
*/
|
||||
addIceCandidate(iceCandidate: RTCIceCandidate): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.debug('Remote ICE candidate received', iceCandidate);
|
||||
this.remoteCandidatesQueue.push(iceCandidate);
|
||||
switch (this.pc.signalingState) {
|
||||
case 'closed':
|
||||
reject(new Error('PeerConnection object is closed'));
|
||||
break;
|
||||
case 'stable':
|
||||
if (!!this.pc.remoteDescription) {
|
||||
this.pc
|
||||
.addIceCandidate(iceCandidate)
|
||||
.then(() => resolve())
|
||||
.catch((error) => reject(error));
|
||||
} else {
|
||||
this.iceCandidateList.push(iceCandidate);
|
||||
resolve();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.iceCandidateList.push(iceCandidate);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addIceConnectionStateChangeListener(otherId: string) {
|
||||
this.pc.addEventListener('iceconnectionstatechange', () => {
|
||||
const iceConnectionState: RTCIceConnectionState = this.pc.iceConnectionState;
|
||||
switch (iceConnectionState) {
|
||||
case 'disconnected':
|
||||
// Possible network disconnection
|
||||
const msg1 =
|
||||
'IceConnectionState of RTCPeerConnection ' +
|
||||
this.configuration.id +
|
||||
' (' +
|
||||
otherId +
|
||||
') change to "disconnected". Possible network disconnection';
|
||||
logger.warn(msg1);
|
||||
this.configuration.onIceConnectionStateException(ExceptionEventName.ICE_CONNECTION_DISCONNECTED, msg1);
|
||||
break;
|
||||
case 'failed':
|
||||
const msg2 = 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') to "failed"';
|
||||
logger.error(msg2);
|
||||
this.configuration.onIceConnectionStateException(ExceptionEventName.ICE_CONNECTION_FAILED, msg2);
|
||||
break;
|
||||
case 'closed':
|
||||
logger.log(
|
||||
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "closed"'
|
||||
);
|
||||
break;
|
||||
case 'new':
|
||||
logger.log('IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "new"');
|
||||
break;
|
||||
case 'checking':
|
||||
logger.log(
|
||||
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "checking"'
|
||||
);
|
||||
break;
|
||||
case 'connected':
|
||||
logger.log(
|
||||
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "connected"'
|
||||
);
|
||||
break;
|
||||
case 'completed':
|
||||
logger.log(
|
||||
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "completed"'
|
||||
);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
generateUniqueId(): string {
|
||||
return uuidv4();
|
||||
}
|
||||
}
|
||||
|
||||
export class WebRtcPeerRecvonly extends WebRtcPeer {
|
||||
constructor(configuration: WebRtcPeerConfiguration) {
|
||||
configuration.mode = 'recvonly';
|
||||
super(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
export class WebRtcPeerSendonly extends WebRtcPeer {
|
||||
constructor(configuration: WebRtcPeerConfiguration) {
|
||||
configuration.mode = 'sendonly';
|
||||
super(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
export class WebRtcPeerSendrecv extends WebRtcPeer {
|
||||
constructor(configuration: WebRtcPeerConfiguration) {
|
||||
configuration.mode = 'sendrecv';
|
||||
super(configuration);
|
||||
}
|
||||
}
|
|
@ -1,471 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// tslint:disable:no-string-literal
|
||||
|
||||
import { Stream } from '../../OpenVidu/Stream';
|
||||
import { OpenViduLogger } from '../Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../Utils/Platform';
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
interface WebrtcStatsConfig {
|
||||
interval: number;
|
||||
httpEndpoint: string;
|
||||
}
|
||||
|
||||
interface JSONStatsResponse {
|
||||
'@timestamp': string;
|
||||
participant_id: string;
|
||||
session_id: string;
|
||||
platform: string;
|
||||
platform_description: string;
|
||||
stream: string;
|
||||
webrtc_stats: IWebrtcStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common WebRtcSTats for latest Chromium and Firefox versions
|
||||
*/
|
||||
interface IWebrtcStats {
|
||||
inbound?: {
|
||||
audio:
|
||||
| {
|
||||
bytesReceived: number;
|
||||
packetsReceived: number;
|
||||
packetsLost: number;
|
||||
jitter: number;
|
||||
}
|
||||
| {};
|
||||
video:
|
||||
| {
|
||||
bytesReceived: number;
|
||||
packetsReceived: number;
|
||||
packetsLost: number;
|
||||
jitter?: number; // Firefox
|
||||
jitterBufferDelay?: number; // Chrome
|
||||
framesDecoded: number;
|
||||
firCount: number;
|
||||
nackCount: number;
|
||||
pliCount: number;
|
||||
frameHeight?: number; // Chrome
|
||||
frameWidth?: number; // Chrome
|
||||
framesDropped?: number; // Chrome
|
||||
framesReceived?: number; // Chrome
|
||||
}
|
||||
| {};
|
||||
};
|
||||
outbound?: {
|
||||
audio:
|
||||
| {
|
||||
bytesSent: number;
|
||||
packetsSent: number;
|
||||
}
|
||||
| {};
|
||||
video:
|
||||
| {
|
||||
bytesSent: number;
|
||||
packetsSent: number;
|
||||
firCount: number;
|
||||
framesEncoded: number;
|
||||
nackCount: number;
|
||||
pliCount: number;
|
||||
qpSum: number;
|
||||
frameHeight?: number; // Chrome
|
||||
frameWidth?: number; // Chrome
|
||||
framesSent?: number; // Chrome
|
||||
}
|
||||
| {};
|
||||
};
|
||||
candidatepair?: {
|
||||
currentRoundTripTime?: number; // Chrome
|
||||
availableOutgoingBitrate?: number; //Chrome
|
||||
// availableIncomingBitrate?: number // No support for any browsers (https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidatePairStats/availableIncomingBitrate)
|
||||
};
|
||||
}
|
||||
|
||||
export class WebRtcStats {
|
||||
private readonly STATS_ITEM_NAME = 'webrtc-stats-config';
|
||||
|
||||
private webRtcStatsEnabled = false;
|
||||
private webRtcStatsIntervalId: NodeJS.Timer;
|
||||
private statsInterval = 1;
|
||||
private POST_URL: string;
|
||||
|
||||
constructor(private stream: Stream) {
|
||||
platform = PlatformUtils.getInstance();
|
||||
}
|
||||
|
||||
public isEnabled(): boolean {
|
||||
return this.webRtcStatsEnabled;
|
||||
}
|
||||
|
||||
public initWebRtcStats(): void {
|
||||
let webrtcObj;
|
||||
// When cross-site (aka third-party) cookies are blocked by the browser,
|
||||
// accessing localStorage in a third-party iframe throws a DOMException.
|
||||
try {
|
||||
webrtcObj = localStorage.getItem(this.STATS_ITEM_NAME);
|
||||
}
|
||||
catch(e){}
|
||||
|
||||
if (!!webrtcObj) {
|
||||
this.webRtcStatsEnabled = true;
|
||||
const webrtcStatsConfig: WebrtcStatsConfig = JSON.parse(webrtcObj);
|
||||
// webrtc object found in local storage
|
||||
logger.warn(
|
||||
'WebRtc stats enabled for stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId
|
||||
);
|
||||
logger.warn('localStorage item: ' + JSON.stringify(webrtcStatsConfig));
|
||||
|
||||
this.POST_URL = webrtcStatsConfig.httpEndpoint;
|
||||
this.statsInterval = webrtcStatsConfig.interval; // Interval in seconds
|
||||
|
||||
this.webRtcStatsIntervalId = setInterval(async () => {
|
||||
await this.sendStatsToHttpEndpoint();
|
||||
}, this.statsInterval * 1000);
|
||||
} else {
|
||||
logger.debug('WebRtc stats not enabled');
|
||||
}
|
||||
}
|
||||
|
||||
// {
|
||||
// "localCandidate": {
|
||||
// "id": "RTCIceCandidate_/r4P1y2Q",
|
||||
// "timestamp": 1616080155617,
|
||||
// "type": "local-candidate",
|
||||
// "transportId": "RTCTransport_0_1",
|
||||
// "isRemote": false,
|
||||
// "networkType": "wifi",
|
||||
// "ip": "123.45.67.89",
|
||||
// "port": 63340,
|
||||
// "protocol": "udp",
|
||||
// "candidateType": "srflx",
|
||||
// "priority": 1686052607,
|
||||
// "deleted": false,
|
||||
// "raw": [
|
||||
// "candidate:3345412921 1 udp 1686052607 123.45.67.89 63340 typ srflx raddr 192.168.1.31 rport 63340 generation 0 ufrag 0ZtT network-id 1 network-cost 10",
|
||||
// "candidate:58094482 1 udp 41885695 98.76.54.32 44431 typ relay raddr 123.45.67.89 rport 63340 generation 0 ufrag 0ZtT network-id 1 network-cost 10"
|
||||
// ]
|
||||
// },
|
||||
// "remoteCandidate": {
|
||||
// "id": "RTCIceCandidate_1YO18gph",
|
||||
// "timestamp": 1616080155617,
|
||||
// "type": "remote-candidate",
|
||||
// "transportId": "RTCTransport_0_1",
|
||||
// "isRemote": true,
|
||||
// "ip": "12.34.56.78",
|
||||
// "port": 64989,
|
||||
// "protocol": "udp",
|
||||
// "candidateType": "srflx",
|
||||
// "priority": 1679819263,
|
||||
// "deleted": false,
|
||||
// "raw": [
|
||||
// "candidate:16 1 UDP 1679819263 12.34.56.78 64989 typ srflx raddr 172.19.0.1 rport 64989",
|
||||
// "candidate:16 1 UDP 1679819263 12.34.56.78 64989 typ srflx raddr 172.19.0.1 rport 64989"
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// Have been tested in:
|
||||
// - Linux Desktop:
|
||||
// - Chrome 89.0.4389.90
|
||||
// - Opera 74.0.3911.218
|
||||
// - Firefox 86
|
||||
// - Microsoft Edge 91.0.825.0
|
||||
// - Electron 11.3.0 (Chromium 87.0.4280.141)
|
||||
// - Windows Desktop:
|
||||
// - Chrome 89.0.4389.90
|
||||
// - Opera 74.0.3911.232
|
||||
// - Firefox 86.0.1
|
||||
// - Microsoft Edge 89.0.774.54
|
||||
// - Electron 11.3.0 (Chromium 87.0.4280.141)
|
||||
// - MacOS Desktop:
|
||||
// - Chrome 89.0.4389.90
|
||||
// - Firefox 87.0
|
||||
// - Opera 75.0.3969.93
|
||||
// - Microsoft Edge 89.0.774.57
|
||||
// - Safari 14.0 (14610.1.28.1.9)
|
||||
// - Electron 11.3.0 (Chromium 87.0.4280.141)
|
||||
// - Android:
|
||||
// - Chrome Mobile 89.0.4389.90
|
||||
// - Opera 62.3.3146.57763
|
||||
// - Firefox Mobile 86.6.1
|
||||
// - Microsoft Edge Mobile 46.02.4.5147
|
||||
// - Ionic 5
|
||||
// - React Native 0.64
|
||||
// - iOS:
|
||||
// - Safari Mobile
|
||||
// - ¿Ionic?
|
||||
// - ¿React Native?
|
||||
public getSelectedIceCandidateInfo(): Promise<any> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const statsReport: any = await this.stream.getRTCPeerConnection().getStats();
|
||||
let transportStat;
|
||||
const candidatePairs: Map<string, any> = new Map();
|
||||
const localCandidates: Map<string, any> = new Map();
|
||||
const remoteCandidates: Map<string, any> = new Map();
|
||||
statsReport.forEach((stat: any) => {
|
||||
if (stat.type === 'transport' && (platform.isChromium() || platform.isSafariBrowser() || platform.isReactNative())) {
|
||||
transportStat = stat;
|
||||
}
|
||||
switch (stat.type) {
|
||||
case 'candidate-pair':
|
||||
candidatePairs.set(stat.id, stat);
|
||||
break;
|
||||
case 'local-candidate':
|
||||
localCandidates.set(stat.id, stat);
|
||||
break;
|
||||
case 'remote-candidate':
|
||||
remoteCandidates.set(stat.id, stat);
|
||||
break;
|
||||
}
|
||||
});
|
||||
let selectedCandidatePair;
|
||||
if (transportStat != null) {
|
||||
const selectedCandidatePairId = transportStat.selectedCandidatePairId;
|
||||
selectedCandidatePair = candidatePairs.get(selectedCandidatePairId);
|
||||
} else {
|
||||
// This is basically Firefox
|
||||
const length = candidatePairs.size;
|
||||
const iterator = candidatePairs.values();
|
||||
for (let i = 0; i < length; i++) {
|
||||
const candidatePair = iterator.next().value;
|
||||
if (candidatePair['selected']) {
|
||||
selectedCandidatePair = candidatePair;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const localCandidateId = selectedCandidatePair.localCandidateId;
|
||||
const remoteCandidateId = selectedCandidatePair.remoteCandidateId;
|
||||
let finalLocalCandidate = localCandidates.get(localCandidateId);
|
||||
if (!!finalLocalCandidate) {
|
||||
const candList = this.stream.getLocalIceCandidateList();
|
||||
const cand = candList.filter((c: RTCIceCandidate) => {
|
||||
return (
|
||||
!!c.candidate &&
|
||||
(c.candidate.indexOf(finalLocalCandidate.ip) >= 0 || c.candidate.indexOf(finalLocalCandidate.address) >= 0) &&
|
||||
c.candidate.indexOf(finalLocalCandidate.port) >= 0
|
||||
);
|
||||
});
|
||||
finalLocalCandidate.raw = [];
|
||||
for (let c of cand) {
|
||||
finalLocalCandidate.raw.push(c.candidate);
|
||||
}
|
||||
} else {
|
||||
finalLocalCandidate = 'ERROR: No active local ICE candidate. Probably ICE-TCP is being used';
|
||||
}
|
||||
|
||||
let finalRemoteCandidate = remoteCandidates.get(remoteCandidateId);
|
||||
if (!!finalRemoteCandidate) {
|
||||
const candList = this.stream.getRemoteIceCandidateList();
|
||||
const cand = candList.filter((c: RTCIceCandidate) => {
|
||||
return (
|
||||
!!c.candidate &&
|
||||
(c.candidate.indexOf(finalRemoteCandidate.ip) >= 0 || c.candidate.indexOf(finalRemoteCandidate.address) >= 0) &&
|
||||
c.candidate.indexOf(finalRemoteCandidate.port) >= 0
|
||||
);
|
||||
});
|
||||
finalRemoteCandidate.raw = [];
|
||||
for (let c of cand) {
|
||||
finalRemoteCandidate.raw.push(c.candidate);
|
||||
}
|
||||
} else {
|
||||
finalRemoteCandidate = 'ERROR: No active remote ICE candidate. Probably ICE-TCP is being used';
|
||||
}
|
||||
|
||||
return resolve({
|
||||
localCandidate: finalLocalCandidate,
|
||||
remoteCandidate: finalRemoteCandidate
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public stopWebRtcStats() {
|
||||
if (this.webRtcStatsEnabled) {
|
||||
clearInterval(this.webRtcStatsIntervalId);
|
||||
logger.warn(
|
||||
'WebRtc stats stopped for disposed stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendStats(url: string, response: JSONStatsResponse): Promise<void> {
|
||||
try {
|
||||
const configuration: RequestInit = {
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(response),
|
||||
method: 'POST'
|
||||
};
|
||||
await fetch(url, configuration);
|
||||
} catch (error) {
|
||||
logger.error(`sendStats error: ${JSON.stringify(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendStatsToHttpEndpoint(): Promise<void> {
|
||||
try {
|
||||
const webrtcStats: IWebrtcStats = await this.getCommonStats();
|
||||
const response = this.generateJSONStatsResponse(webrtcStats);
|
||||
await this.sendStats(this.POST_URL, response);
|
||||
} catch (error) {
|
||||
logger.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Have been tested in:
|
||||
// - Linux Desktop:
|
||||
// - Chrome 89.0.4389.90
|
||||
// - Opera 74.0.3911.218
|
||||
// - Firefox 86
|
||||
// - Microsoft Edge 91.0.825.0
|
||||
// - Electron 11.3.0 (Chromium 87.0.4280.141)
|
||||
// - Windows Desktop:
|
||||
// - Chrome 89.0.4389.90
|
||||
// - Opera 74.0.3911.232
|
||||
// - Firefox 86.0.1
|
||||
// - Microsoft Edge 89.0.774.54
|
||||
// - Electron 11.3.0 (Chromium 87.0.4280.141)
|
||||
// - MacOS Desktop:
|
||||
// - Chrome 89.0.4389.90
|
||||
// - Opera 75.0.3969.93
|
||||
// - Firefox 87.0
|
||||
// - Microsoft Edge 89.0.774.57
|
||||
// - Safari 14.0 (14610.1.28.1.9)
|
||||
// - Electron 11.3.0 (Chromium 87.0.4280.141)
|
||||
// - Android:
|
||||
// - Chrome Mobile 89.0.4389.90
|
||||
// - Opera 62.3.3146.57763
|
||||
// - Firefox Mobile 86.6.1
|
||||
// - Microsoft Edge Mobile 46.02.4.5147
|
||||
// - Ionic 5
|
||||
// - React Native 0.64
|
||||
// - iOS:
|
||||
// - Safari Mobile
|
||||
// - ¿Ionic?
|
||||
// - ¿React Native?
|
||||
public async getCommonStats(): Promise<IWebrtcStats> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const statsReport: any = await this.stream.getRTCPeerConnection().getStats();
|
||||
const response: IWebrtcStats = this.getWebRtcStatsResponseOutline();
|
||||
const videoTrackStats = ['framesReceived', 'framesDropped', 'framesSent', 'frameHeight', 'frameWidth'];
|
||||
const candidatePairStats = ['availableOutgoingBitrate', 'currentRoundTripTime'];
|
||||
|
||||
statsReport.forEach((stat: any) => {
|
||||
let mediaType = stat.mediaType != null ? stat.mediaType : stat.kind;
|
||||
const addStat = (direction: string, key: string): void => {
|
||||
if (stat[key] != null && response[direction] != null) {
|
||||
if (!mediaType && videoTrackStats.indexOf(key) > -1) {
|
||||
mediaType = 'video';
|
||||
}
|
||||
if (direction != null && mediaType != null && key != null && response[direction][mediaType] != null) {
|
||||
response[direction][mediaType][key] = Number(stat[key]);
|
||||
} else if (direction != null && key != null && candidatePairStats.includes(key)) {
|
||||
// candidate-pair-stats
|
||||
response[direction][key] = Number(stat[key]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
switch (stat.type) {
|
||||
case 'outbound-rtp':
|
||||
addStat('outbound', 'bytesSent');
|
||||
addStat('outbound', 'packetsSent');
|
||||
addStat('outbound', 'framesEncoded');
|
||||
addStat('outbound', 'nackCount');
|
||||
addStat('outbound', 'firCount');
|
||||
addStat('outbound', 'pliCount');
|
||||
addStat('outbound', 'qpSum');
|
||||
break;
|
||||
case 'inbound-rtp':
|
||||
addStat('inbound', 'bytesReceived');
|
||||
addStat('inbound', 'packetsReceived');
|
||||
addStat('inbound', 'packetsLost');
|
||||
addStat('inbound', 'jitter');
|
||||
addStat('inbound', 'framesDecoded');
|
||||
addStat('inbound', 'nackCount');
|
||||
addStat('inbound', 'firCount');
|
||||
addStat('inbound', 'pliCount');
|
||||
break;
|
||||
case 'track':
|
||||
addStat('inbound', 'jitterBufferDelay');
|
||||
addStat('inbound', 'framesReceived');
|
||||
addStat('outbound', 'framesDropped');
|
||||
addStat('outbound', 'framesSent');
|
||||
addStat(this.stream.isLocal() ? 'outbound' : 'inbound', 'frameHeight');
|
||||
addStat(this.stream.isLocal() ? 'outbound' : 'inbound', 'frameWidth');
|
||||
break;
|
||||
case 'candidate-pair':
|
||||
addStat('candidatepair', 'currentRoundTripTime');
|
||||
addStat('candidatepair', 'availableOutgoingBitrate');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Delete candidatepair from response if null
|
||||
if (!response?.candidatepair || Object.keys(<Object>response.candidatepair).length === 0) {
|
||||
delete response.candidatepair;
|
||||
}
|
||||
|
||||
return resolve(response);
|
||||
} catch (error) {
|
||||
logger.error('Error getting common stats: ', error);
|
||||
return reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private generateJSONStatsResponse(stats: IWebrtcStats): JSONStatsResponse {
|
||||
return {
|
||||
'@timestamp': new Date().toISOString(),
|
||||
participant_id: this.stream.connection.data,
|
||||
session_id: this.stream.session.sessionId,
|
||||
platform: platform.getName(),
|
||||
platform_description: platform.getDescription(),
|
||||
stream: 'webRTC',
|
||||
webrtc_stats: stats
|
||||
};
|
||||
}
|
||||
|
||||
private getWebRtcStatsResponseOutline(): IWebrtcStats {
|
||||
if (this.stream.isLocal()) {
|
||||
return {
|
||||
outbound: {
|
||||
audio: {},
|
||||
video: {}
|
||||
},
|
||||
candidatepair: {}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
inbound: {
|
||||
audio: {},
|
||||
video: {}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
import { JL } from 'jsnlog';
|
||||
|
||||
export { OpenVidu } from './OpenVidu/OpenVidu';
|
||||
export { Session } from './OpenVidu/Session';
|
||||
export { Publisher } from './OpenVidu/Publisher';
|
||||
export { Subscriber } from './OpenVidu/Subscriber';
|
||||
export { StreamManager } from './OpenVidu/StreamManager';
|
||||
export { Stream } from './OpenVidu/Stream';
|
||||
export { Connection } from './OpenVidu/Connection';
|
||||
export { LocalRecorder } from './OpenVidu/LocalRecorder';
|
||||
export { Filter } from './OpenVidu/Filter';
|
||||
|
||||
export { LocalRecorderState } from './OpenViduInternal/Enums/LocalRecorderState';
|
||||
export { OpenViduError, OpenViduErrorName } from './OpenViduInternal/Enums/OpenViduError';
|
||||
export { TypeOfVideo } from './OpenViduInternal/Enums/TypeOfVideo';
|
||||
export { VideoInsertMode } from './OpenViduInternal/Enums/VideoInsertMode';
|
||||
|
||||
export { Event } from './OpenViduInternal/Events/Event';
|
||||
export { ConnectionEvent } from './OpenViduInternal/Events/ConnectionEvent';
|
||||
export { PublisherSpeakingEvent } from './OpenViduInternal/Events/PublisherSpeakingEvent';
|
||||
export { RecordingEvent } from './OpenViduInternal/Events/RecordingEvent';
|
||||
export { SessionDisconnectedEvent } from './OpenViduInternal/Events/SessionDisconnectedEvent';
|
||||
export { SignalEvent } from './OpenViduInternal/Events/SignalEvent';
|
||||
export { StreamEvent } from './OpenViduInternal/Events/StreamEvent';
|
||||
export { StreamManagerEvent } from './OpenViduInternal/Events/StreamManagerEvent';
|
||||
export { VideoElementEvent } from './OpenViduInternal/Events/VideoElementEvent';
|
||||
export { StreamPropertyChangedEvent } from './OpenViduInternal/Events/StreamPropertyChangedEvent';
|
||||
export { ConnectionPropertyChangedEvent } from './OpenViduInternal/Events/ConnectionPropertyChangedEvent';
|
||||
export { FilterEvent } from './OpenViduInternal/Events/FilterEvent';
|
||||
export { NetworkQualityLevelChangedEvent } from './OpenViduInternal/Events/NetworkQualityLevelChangedEvent';
|
||||
export { SpeechToTextEvent } from './OpenViduInternal/Events/SpeechToTextEvent';
|
||||
export { ExceptionEvent, ExceptionEventName } from './OpenViduInternal/Events/ExceptionEvent';
|
||||
|
||||
export { Capabilities } from './OpenViduInternal/Interfaces/Public/Capabilities';
|
||||
export { Device } from './OpenViduInternal/Interfaces/Public/Device';
|
||||
export { EventDispatcher } from './OpenVidu/EventDispatcher';
|
||||
export { OpenViduAdvancedConfiguration } from './OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration';
|
||||
export { PublisherProperties } from './OpenViduInternal/Interfaces/Public/PublisherProperties';
|
||||
export { SignalOptions } from './OpenViduInternal/Interfaces/Public/SignalOptions';
|
||||
export { StreamManagerVideo } from './OpenViduInternal/Interfaces/Public/StreamManagerVideo';
|
||||
export { SubscriberProperties } from './OpenViduInternal/Interfaces/Public/SubscriberProperties';
|
||||
|
||||
export { EventMap } from './OpenViduInternal/Events/EventMap/EventMap';
|
||||
export { SessionEventMap } from './OpenViduInternal/Events/EventMap/SessionEventMap';
|
||||
export { StreamManagerEventMap } from './OpenViduInternal/Events/EventMap/StreamManagerEventMap';
|
||||
export { PublisherEventMap } from './OpenViduInternal/Events/EventMap/PublisherEventMap';
|
||||
|
||||
export * from './OpenViduInternal/Events/Types/Types';
|
||||
|
||||
// Disable jsnlog when library is loaded
|
||||
JL.setOptions({ enabled: false });
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
//"allowUnusedLabels": true,
|
||||
"allowUnreachableCode": false,
|
||||
"buildOnSave": false,
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"emitBOM": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015.promise",
|
||||
"es5",
|
||||
"scripthost"
|
||||
],
|
||||
"module": "commonjs",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
//"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
//"noUnusedLocals": true,
|
||||
//"noUnusedParameters": true,
|
||||
"outDir": "../../lib",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"rootDir": "./src",
|
||||
"skipDefaultLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressExcessPropertyErrors": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"target": "es5"
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
/target/
|
|
@ -1,14 +0,0 @@
|
|||
[](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml)
|
||||
[](https://docs.openvidu.io/en/stable/?badge=stable)
|
||||
[](https://hub.docker.com/r/openvidu/)
|
||||
[](https://openvidu.discourse.group/)
|
||||
|
||||
[![][OpenViduLogo]](https://openvidu.io)
|
||||
|
||||
openvidu-client
|
||||
===
|
||||
|
||||
Internal Java client used by [openvidu-server](https://github.com/OpenVidu/openvidu/tree/master/openvidu-server). Can be used to implement an Android client.
|
||||
|
||||
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120
|
|
@ -1,106 +0,0 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.openvidu</groupId>
|
||||
<artifactId>openvidu-parent</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openvidu-client</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>OpenVidu Client</name>
|
||||
<description>
|
||||
OpenVidu client library for the client-side of OpenVidu Server
|
||||
</description>
|
||||
<url>https://github.com/OpenVidu/openvidu</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<organization>
|
||||
<name>OpenVidu</name>
|
||||
<url>https://github.com/OpenVidu/openvidu</url>
|
||||
</organization>
|
||||
|
||||
<scm>
|
||||
<url>${openvidu.scm.url}</url>
|
||||
<connection>scm:git:${openvidu.scm.connection}</connection>
|
||||
<developerConnection>scm:git:${openvidu.scm.connection}</developerConnection>
|
||||
<tag>develop</tag>
|
||||
</scm>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>openvidu.io</id>
|
||||
<name>-openvidu.io Community</name>
|
||||
<organization>OpenVidu</organization>
|
||||
<organizationUrl>https://openvidu.io</organizationUrl>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.kurento</groupId>
|
||||
<artifactId>kurento-jsonrpc-client</artifactId>
|
||||
<version>${version.kurento}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.kurento</groupId>
|
||||
<artifactId>kurento-jsonrpc-client-jetty</artifactId>
|
||||
<version>${version.kurento}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>${version.junit}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${version.mockito.core}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>default</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>default</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
|
@ -1,217 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client;
|
||||
|
||||
import static io.openvidu.client.internal.ProtocolElements.CUSTOMREQUEST_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERID_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERSTREAMID_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERSTREAMS_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_ROOM_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_USER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.LEAVEROOM_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_CANDIDATE_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_EPNAME_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_SDPMIDPARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_SDPMLINEINDEX_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_DOLOOPBACK_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_SDPANSWER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_SDPOFFER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SDPANSWER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SDPOFFER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SENDER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.SENDMESSAGE_MESSAGE_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.SENDMESSAGE_ROOM_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.UNPUBLISHVIDEO_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.UNSUBSCRIBEFROMVIDEO_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.UNSUBSCRIBEFROMVIDEO_SENDER_PARAM;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.kurento.jsonrpc.client.JsonRpcClient;
|
||||
import org.kurento.jsonrpc.client.JsonRpcClientWebSocket;
|
||||
import org.kurento.jsonrpc.client.JsonRpcWSConnectionListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import io.openvidu.client.internal.JsonRoomUtils;
|
||||
import io.openvidu.client.internal.Notification;
|
||||
|
||||
/**
|
||||
* Java client for the room server.
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class OpenViduClient {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OpenViduClient.class);
|
||||
|
||||
private JsonRpcClient client;
|
||||
private ServerJsonRpcHandler handler;
|
||||
|
||||
public OpenViduClient(String wsUri) {
|
||||
this(new JsonRpcClientWebSocket(wsUri, new JsonRpcWSConnectionListener() {
|
||||
|
||||
@Override
|
||||
public void reconnected(boolean sameServer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected() {
|
||||
log.warn("JsonRpcWebsocket connection: Disconnected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionFailed() {
|
||||
log.warn("JsonRpcWebsocket connection: Connection failed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconnecting() {
|
||||
log.warn("JsonRpcWebsocket connection: is reconnecting");
|
||||
}
|
||||
}, new SslContextFactory(true)));
|
||||
}
|
||||
|
||||
public OpenViduClient(JsonRpcClient client) {
|
||||
this.client = client;
|
||||
this.handler = new ServerJsonRpcHandler();
|
||||
this.client.setServerRequestHandler(this.handler);
|
||||
}
|
||||
|
||||
public OpenViduClient(JsonRpcClient client, ServerJsonRpcHandler handler) {
|
||||
this.client = client;
|
||||
this.handler = handler;
|
||||
this.client.setServerRequestHandler(this.handler);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.client.close();
|
||||
}
|
||||
|
||||
public Map<String, List<String>> joinRoom(String roomName, String userName)
|
||||
throws IOException {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(JOINROOM_ROOM_PARAM, roomName);
|
||||
params.addProperty(JOINROOM_USER_PARAM, userName);
|
||||
|
||||
JsonElement result = client.sendRequest(JOINROOM_METHOD, params);
|
||||
Map<String, List<String>> peers = new HashMap<String, List<String>>();
|
||||
JsonArray jsonPeers = JsonRoomUtils.getResponseProperty(result, "value", JsonArray.class);
|
||||
if (jsonPeers.size() > 0) {
|
||||
Iterator<JsonElement> peerIt = jsonPeers.iterator();
|
||||
while (peerIt.hasNext()) {
|
||||
JsonElement peer = peerIt.next();
|
||||
String peerId = JsonRoomUtils.getResponseProperty(peer, JOINROOM_PEERID_PARAM,
|
||||
String.class);
|
||||
List<String> streams = new ArrayList<String>();
|
||||
JsonArray jsonStreams = JsonRoomUtils.getResponseProperty(peer, JOINROOM_PEERSTREAMS_PARAM,
|
||||
JsonArray.class, true);
|
||||
if (jsonStreams != null) {
|
||||
Iterator<JsonElement> streamIt = jsonStreams.iterator();
|
||||
while (streamIt.hasNext()) {
|
||||
streams.add(JsonRoomUtils.getResponseProperty(streamIt.next(),
|
||||
JOINROOM_PEERSTREAMID_PARAM, String.class));
|
||||
}
|
||||
}
|
||||
peers.put(peerId, streams);
|
||||
}
|
||||
}
|
||||
return peers;
|
||||
}
|
||||
|
||||
public void leaveRoom() throws IOException {
|
||||
client.sendRequest(LEAVEROOM_METHOD, new JsonObject());
|
||||
}
|
||||
|
||||
public String publishVideo(String sdpOffer, boolean doLoopback) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(PUBLISHVIDEO_SDPOFFER_PARAM, sdpOffer);
|
||||
params.addProperty(PUBLISHVIDEO_DOLOOPBACK_PARAM, doLoopback);
|
||||
JsonElement result = client.sendRequest(PUBLISHVIDEO_METHOD, params);
|
||||
return JsonRoomUtils.getResponseProperty(result, PUBLISHVIDEO_SDPANSWER_PARAM, String.class);
|
||||
}
|
||||
|
||||
public void unpublishVideo() throws IOException {
|
||||
client.sendRequest(UNPUBLISHVIDEO_METHOD, new JsonObject());
|
||||
}
|
||||
|
||||
// sender should look like 'username_streamId'
|
||||
public String receiveVideoFrom(String sender, String sdpOffer) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(RECEIVEVIDEO_SENDER_PARAM, sender);
|
||||
params.addProperty(RECEIVEVIDEO_SDPOFFER_PARAM, sdpOffer);
|
||||
JsonElement result = client.sendRequest(RECEIVEVIDEO_METHOD, params);
|
||||
return JsonRoomUtils.getResponseProperty(result, RECEIVEVIDEO_SDPANSWER_PARAM, String.class);
|
||||
}
|
||||
|
||||
// sender should look like 'username_streamId'
|
||||
public void unsubscribeFromVideo(String sender) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(UNSUBSCRIBEFROMVIDEO_SENDER_PARAM, sender);
|
||||
client.sendRequest(UNSUBSCRIBEFROMVIDEO_METHOD, params);
|
||||
}
|
||||
|
||||
public void onIceCandidate(String endpointName, String candidate, String sdpMid,
|
||||
int sdpMLineIndex) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(ONICECANDIDATE_EPNAME_PARAM, endpointName);
|
||||
params.addProperty(ONICECANDIDATE_CANDIDATE_PARAM, candidate);
|
||||
params.addProperty(ONICECANDIDATE_SDPMIDPARAM, sdpMid);
|
||||
params.addProperty(ONICECANDIDATE_SDPMLINEINDEX_PARAM, sdpMLineIndex);
|
||||
client.sendRequest(ONICECANDIDATE_METHOD, params);
|
||||
}
|
||||
|
||||
public void sendMessage(String userName, String roomName, String message) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(SENDMESSAGE_MESSAGE_PARAM, message);
|
||||
client.sendRequest(SENDMESSAGE_ROOM_METHOD, params);
|
||||
}
|
||||
|
||||
public JsonElement customRequest(JsonObject customReqParams) throws IOException {
|
||||
return client.sendRequest(CUSTOMREQUEST_METHOD, customReqParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the notifications list maintained by this client to obtain new events sent by server.
|
||||
* This method blocks until there is a notification to return. This is a one-time operation for
|
||||
* the returned element.
|
||||
*
|
||||
* @return a server notification object, null when interrupted while waiting
|
||||
*/
|
||||
public Notification getServerNotification() {
|
||||
return this.handler.getNotification();
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.openvidu.client;
|
||||
|
||||
import org.kurento.jsonrpc.JsonRpcErrorException;
|
||||
|
||||
public class OpenViduException extends JsonRpcErrorException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static enum Code {
|
||||
GENERIC_ERROR_CODE(999), WRONG_PATH_CODE(998), WRONG_OPENVIDU_EDITION_ERROR_CODE(997),
|
||||
SERVICE_NOT_ENABLED_ERROR_CODE(996),
|
||||
|
||||
TRANSPORT_ERROR_CODE(803), TRANSPORT_RESPONSE_ERROR_CODE(802), TRANSPORT_REQUEST_ERROR_CODE(801),
|
||||
|
||||
MEDIA_TYPE_STREAM_INCOMPATIBLE_WITH_RECORDING_PROPERTIES_ERROR_CODE(309),
|
||||
MEDIA_TYPE_RECORDING_PROPERTIES_ERROR_CODE(308), MEDIA_MUTE_ERROR_CODE(307),
|
||||
MEDIA_NOT_A_WEB_ENDPOINT_ERROR_CODE(306), MEDIA_RTP_ENDPOINT_ERROR_CODE(305),
|
||||
MEDIA_WEBRTC_ENDPOINT_ERROR_CODE(304), MEDIA_ENDPOINT_ERROR_CODE(303), MEDIA_SDP_ERROR_CODE(302),
|
||||
MEDIA_GENERIC_ERROR_CODE(301),
|
||||
|
||||
ROOM_CANNOT_BE_CREATED_ERROR_CODE(204), ROOM_CLOSED_ERROR_CODE(203), ROOM_NOT_FOUND_ERROR_CODE(202),
|
||||
ROOM_GENERIC_ERROR_CODE(201),
|
||||
|
||||
USER_ALREADY_STREAMING_ERROR_CODE(106), USER_NOT_STREAMING_ERROR_CODE(105),
|
||||
EXISTING_USER_IN_ROOM_ERROR_CODE(104), USER_CLOSED_ERROR_CODE(103), USER_NOT_FOUND_ERROR_CODE(102),
|
||||
USER_GENERIC_ERROR_CODE(10),
|
||||
|
||||
USER_UNAUTHORIZED_ERROR_CODE(401), ROLE_NOT_FOUND_ERROR_CODE(402), SESSIONID_CANNOT_BE_CREATED_ERROR_CODE(403),
|
||||
TOKEN_CANNOT_BE_CREATED_ERROR_CODE(404), EXISTING_FILTER_ALREADY_APPLIED_ERROR_CODE(405),
|
||||
FILTER_NOT_APPLIED_ERROR_CODE(406), FILTER_EVENT_LISTENER_NOT_FOUND_ERROR_CODE(407),
|
||||
PUBLISHER_ENDPOINT_NOT_FOUND_ERROR_CODE(408),
|
||||
|
||||
USER_METADATA_FORMAT_INVALID_ERROR_CODE(500),
|
||||
|
||||
SIGNAL_FORMAT_INVALID_ERROR_CODE(600), SIGNAL_TO_INVALID_ERROR_CODE(601),
|
||||
|
||||
BROADCAST_CONCURRENT_ERROR_CODE(711), BROADCAST_START_ERROR_CODE(710), DOCKER_NOT_FOUND(709), RECORDING_PATH_NOT_VALID(708), RECORDING_FILE_EMPTY_ERROR(707),
|
||||
RECORDING_DELETE_ERROR_CODE(706), RECORDING_LIST_ERROR_CODE(705), RECORDING_STOP_ERROR_CODE(704),
|
||||
RECORDING_START_ERROR_CODE(703), RECORDING_REPORT_ERROR_CODE(702), RECORDING_COMPLETION_ERROR_CODE(701),
|
||||
|
||||
FORCED_CODEC_NOT_FOUND_IN_SDPOFFER(800),
|
||||
|
||||
MEDIA_NODE_NOT_FOUND(900), MEDIA_NODE_STATUS_WRONG(901), MEDIA_NODE_CONNECTION_ERROR_CODE(902);
|
||||
|
||||
private int value;
|
||||
|
||||
private Code(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
private Code code = Code.GENERIC_ERROR_CODE;
|
||||
|
||||
public OpenViduException(Code code, String message) {
|
||||
super(code.getValue(), message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCodeValue() {
|
||||
return code.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,222 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import org.kurento.jsonrpc.DefaultJsonRpcHandler;
|
||||
import org.kurento.jsonrpc.Transaction;
|
||||
import org.kurento.jsonrpc.message.Request;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import io.openvidu.client.internal.IceCandidate;
|
||||
import io.openvidu.client.internal.IceCandidateInfo;
|
||||
import io.openvidu.client.internal.JsonRoomUtils;
|
||||
import io.openvidu.client.internal.MediaErrorInfo;
|
||||
import io.openvidu.client.internal.Notification;
|
||||
import io.openvidu.client.internal.ParticipantEvictedInfo;
|
||||
import io.openvidu.client.internal.ParticipantJoinedInfo;
|
||||
import io.openvidu.client.internal.ParticipantLeftInfo;
|
||||
import io.openvidu.client.internal.ParticipantPublishedInfo;
|
||||
import io.openvidu.client.internal.ParticipantUnpublishedInfo;
|
||||
import io.openvidu.client.internal.ProtocolElements;
|
||||
import io.openvidu.client.internal.RoomClosedInfo;
|
||||
import io.openvidu.client.internal.SendMessageInfo;
|
||||
|
||||
/**
|
||||
* Service that handles server JSON-RPC events.
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class ServerJsonRpcHandler extends DefaultJsonRpcHandler<JsonObject> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ServerJsonRpcHandler.class);
|
||||
|
||||
private BlockingQueue<Notification> notifications = new ArrayBlockingQueue<Notification>(100);
|
||||
|
||||
@Override
|
||||
public void handleRequest(Transaction transaction, Request<JsonObject> request) throws Exception {
|
||||
Notification notif = null;
|
||||
try {
|
||||
switch (request.getMethod()) {
|
||||
case ProtocolElements.ICECANDIDATE_METHOD:
|
||||
notif = iceCandidate(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.MEDIAERROR_METHOD:
|
||||
notif = mediaError(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTJOINED_METHOD:
|
||||
notif = participantJoined(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTLEFT_METHOD:
|
||||
notif = participantLeft(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTEVICTED_METHOD:
|
||||
notif = participantEvicted(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTPUBLISHED_METHOD:
|
||||
notif = participantPublished(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD:
|
||||
notif = participantUnpublished(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.ROOMCLOSED_METHOD:
|
||||
notif = roomClosed(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD:
|
||||
notif = participantSendMessage(transaction, request);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unrecognized request " + request.getMethod());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Exception processing request {}", request, e);
|
||||
transaction.sendError(e);
|
||||
return;
|
||||
}
|
||||
if (notif != null) {
|
||||
try {
|
||||
notifications.put(notif);
|
||||
log.debug("Enqueued notification {}", notif);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted when enqueuing notification {}", notif, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Notification participantSendMessage(Transaction transaction,
|
||||
Request<JsonObject> request) {
|
||||
String data = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTSENDMESSAGE_DATA_PARAM, String.class);
|
||||
String from = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTSENDMESSAGE_FROM_PARAM, String.class);
|
||||
String type = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTSENDMESSAGE_TYPE_PARAM, String.class);
|
||||
SendMessageInfo eventInfo = new SendMessageInfo(data, from, type);
|
||||
log.debug("Recvd send message event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification roomClosed(Transaction transaction, Request<JsonObject> request) {
|
||||
String room = JsonRoomUtils.getRequestParam(request, ProtocolElements.ROOMCLOSED_ROOM_PARAM,
|
||||
String.class);
|
||||
RoomClosedInfo eventInfo = new RoomClosedInfo(room);
|
||||
log.debug("Recvd room closed event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantUnpublished(Transaction transaction,
|
||||
Request<JsonObject> request) {
|
||||
String name = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTUNPUBLISHED_NAME_PARAM, String.class);
|
||||
ParticipantUnpublishedInfo eventInfo = new ParticipantUnpublishedInfo(name);
|
||||
log.debug("Recvd participant unpublished event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantPublished(Transaction transaction, Request<JsonObject> request) {
|
||||
String id = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTPUBLISHED_USER_PARAM, String.class);
|
||||
JsonArray jsonStreams = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTPUBLISHED_STREAMS_PARAM, JsonArray.class);
|
||||
Iterator<JsonElement> streamIt = jsonStreams.iterator();
|
||||
List<String> streams = new ArrayList<String>();
|
||||
while (streamIt.hasNext()) {
|
||||
streams.add(JsonRoomUtils.getResponseProperty(streamIt.next(),
|
||||
ProtocolElements.PARTICIPANTPUBLISHED_STREAMID_PARAM, String.class));
|
||||
}
|
||||
ParticipantPublishedInfo eventInfo = new ParticipantPublishedInfo(id, streams);
|
||||
log.debug("Recvd published event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantEvicted(Transaction transaction, Request<JsonObject> request) {
|
||||
ParticipantEvictedInfo eventInfo = new ParticipantEvictedInfo();
|
||||
log.debug("Recvd participant evicted event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantLeft(Transaction transaction, Request<JsonObject> request) {
|
||||
String name = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTLEFT_NAME_PARAM, String.class);
|
||||
ParticipantLeftInfo eventInfo = new ParticipantLeftInfo(name);
|
||||
log.debug("Recvd participant left event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantJoined(Transaction transaction, Request<JsonObject> request) {
|
||||
String id = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTJOINED_USER_PARAM, String.class);
|
||||
ParticipantJoinedInfo eventInfo = new ParticipantJoinedInfo(id);
|
||||
log.debug("Recvd participant joined event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification mediaError(Transaction transaction, Request<JsonObject> request) {
|
||||
String description = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.MEDIAERROR_ERROR_PARAM, String.class);
|
||||
MediaErrorInfo eventInfo = new MediaErrorInfo(description);
|
||||
log.debug("Recvd media error event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification iceCandidate(Transaction transaction, Request<JsonObject> request) {
|
||||
|
||||
String candidate = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.ICECANDIDATE_CANDIDATE_PARAM, String.class);
|
||||
String sdpMid = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.ICECANDIDATE_SDPMID_PARAM, String.class);
|
||||
int sdpMLineIndex = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.ICECANDIDATE_SDPMLINEINDEX_PARAM, Integer.class);
|
||||
|
||||
IceCandidate iceCandidate = new IceCandidate(candidate, sdpMid, sdpMLineIndex);
|
||||
|
||||
String endpoint = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.ICECANDIDATE_EPNAME_PARAM, String.class);
|
||||
|
||||
IceCandidateInfo eventInfo = new IceCandidateInfo(iceCandidate, endpoint);
|
||||
log.debug("Recvd ICE candidate event {}", eventInfo);
|
||||
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until an element is available and then returns it by removing it from the queue.
|
||||
*
|
||||
* @return a {@link Notification} from the queue, null when interrupted
|
||||
* @see BlockingQueue#take()
|
||||
*/
|
||||
public Notification getNotification() {
|
||||
try {
|
||||
Notification notif = notifications.take();
|
||||
log.debug("Dequeued notification {}", notif);
|
||||
return notif;
|
||||
} catch (InterruptedException e) {
|
||||
log.info("Interrupted while polling notifications' queue");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package io.openvidu.client.internal;
|
||||
|
||||
public class IceCandidate {
|
||||
|
||||
private String candidate;
|
||||
private String sdpMid;
|
||||
private int sdpMLineIndex;
|
||||
|
||||
public IceCandidate(String candidate, String sdpMid, int sdpMLineIndex) {
|
||||
super();
|
||||
this.candidate = candidate;
|
||||
this.sdpMid = sdpMid;
|
||||
this.sdpMLineIndex = sdpMLineIndex;
|
||||
}
|
||||
|
||||
public String getCandidate() {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
public String getSdpMid() {
|
||||
return sdpMid;
|
||||
}
|
||||
|
||||
public int getSdpMLineIndex() {
|
||||
return sdpMLineIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IceCandidate [candidate=" + candidate + ", sdpMid=" + sdpMid + ", sdpMLineIndex="
|
||||
+ sdpMLineIndex + "]";
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class IceCandidateInfo extends Notification {
|
||||
|
||||
private IceCandidate iceCandidate;
|
||||
private String endpointName;
|
||||
|
||||
public IceCandidateInfo(IceCandidate iceCandidate, String endpointName) {
|
||||
super(ProtocolElements.ICECANDIDATE_METHOD);
|
||||
this.iceCandidate = iceCandidate;
|
||||
this.endpointName = endpointName;
|
||||
}
|
||||
|
||||
public IceCandidate getIceCandidate() {
|
||||
return iceCandidate;
|
||||
}
|
||||
|
||||
public void setIceCandidate(IceCandidate iceCandidate) {
|
||||
this.iceCandidate = iceCandidate;
|
||||
}
|
||||
|
||||
public String getEndpointName() {
|
||||
return endpointName;
|
||||
}
|
||||
|
||||
public void setEndpointName(String endpointName) {
|
||||
this.endpointName = endpointName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (getMethod() != null) {
|
||||
builder.append("method=").append(getMethod()).append(", ");
|
||||
}
|
||||
if (endpointName != null) {
|
||||
builder.append("endpointName=").append(endpointName).append(", ");
|
||||
}
|
||||
if (iceCandidate != null) {
|
||||
builder.append("iceCandidate=[sdpMLineIndex= ").append(iceCandidate.getSdpMLineIndex())
|
||||
.append(", sdpMid=").append(iceCandidate.getSdpMid()).append(", candidate=")
|
||||
.append(iceCandidate.getCandidate()).append("]");
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class ParticipantEvictedInfo extends Notification {
|
||||
|
||||
public ParticipantEvictedInfo() {
|
||||
super(ProtocolElements.PARTICIPANTEVICTED_METHOD);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue