diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 674766d7ca..ab2d4a1de5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,4 @@ @raviteja83 @KaustubhKumar05 -@amar-1995 @hdz-666 @ygit \ No newline at end of file diff --git a/README.md b/README.md index 07fd77dc2a..05ee429921 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,30 @@ # Web SDKs +[![Lint, Test and Build](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml/badge.svg)](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml) +[![Activity](https://img.shields.io/github/commit-activity/m/100mslive/web-sdks.svg)](https://www.100ms.live/docs/javascript/v2/release-notes/release-notes) +[![License](https://img.shields.io/npm/l/@100mslive/hms-video-store)](https://www.100ms.live/) +[![Documentation](https://img.shields.io/badge/Read-Documentation-blue)](https://www.100ms.live/docs/javascript/v2/quickstart/javascript-quickstart) +[![Register](https://img.shields.io/badge/Contact-Know%20More-blue)](https://dashboard.100ms.live/register) + This monorepo contains all the packages required to integrate 100ms on the web. ## What is included? The packages folder contains all the SDK's of 100ms. Here is a brief overview of them: + | Directory | Package | Description | Link | |--|--|--|--| | `hms-video-store` | `@100mslive/hms-video-store` | This package contains the core SDK and the reactive store parts. | [README](./packages/hms-video-store) | -| `react-icons` | `@100mslive/react-icons` | This contains all the icons used in the 100ms prebuilt. | [README](./packages/react-icons) | | `react-sdk` | `@100mslive/react-sdk` | This contains the base React Hooks and some commonly used functionalities as React Hooks. | [README](./packages/react-sdk) | | `roomkit-react` | `@100mslive/roomkit-react`| This contains the React components used in the Prebuilt and the Prebuilt component itself. | [README](./packages/roomkit-react) | -| `roomkit-web` | `@100mslive/roomkit-web` | This is a web component port of the `HMSPrebuilt` component from the `roomkit-react`. If you are not using React,this can be used as a web component. | [README](./packages/roomkit-web)| +| `roomkit-web` | `@100mslive/roomkit-web` | This is a web component port of the `HMSPrebuilt` component from the `roomkit-react`. If you are not using React, this can be used as a web component. | [README](./packages/roomkit-web)| +| `hls-player` | `@100mslive/hls-player` | This is a HLS player offered by 100ms that can be used to play live video streams. | [README](./packages/hls-player) | +| `hms-whiteboard` | `@100mslive/hms-whiteboard` | This contains APIs for integrating Whiteboard collaboration into your conferencing sessions. | [README](./packages/hms-whiteboard) | +| `hms-virtual-background` | `@100mslive/hms-virtual-background` | This contains the Virtual Background plugin provided by 100ms. | [README](./packages/hms-virtual-background) | +| `react-icons` | `@100mslive/react-icons` | This contains all the icons used in the 100ms prebuilt. | [README](./packages/react-icons) | For full documentation, visit [100ms.live/docs](https://www.100ms.live/docs) -
- ## How to integrate? The 100ms SDK gives you everything you need to build scalable, high-quality live video and audio experiences. @@ -26,19 +34,17 @@ The 100ms SDK gives you everything you need to build scalable, high-quality live 1. ## Custom UI - 100ms SDKs are powerful and highly extensible to build and support all custom experiences and UI. - **Related packages include:** `@100mslive/react-sdk`, `@100mslive/hms-video-store` and `@100mslive/react-icons`. - - Get started with integrating the SDK using the [How to Guide](https://www.100ms.live/docs/javascript/v2/how-to-guides/install-the-sdk/integration).
+ - Get started with integrating the SDK using the [How to Guide](https://www.100ms.live/docs/javascript/v2/how-to-guides/install-the-sdk/integration). > Navigate to `react-sdk` for the base React Hooks and some commonly used functionalities by clicking [here](./packages/react-sdk). 2. ## 100ms Prebuilt - 100ms Prebuilt is a high-level abstraction with no-code customization that enables you to embed video conferencing and/or live streaming UI—with a few lines of code. - **Related packages include:** `roomkit-react` and `roomkit-web`. - - Get started with 100ms Prebuilt using the [Prebuilt Quickstart for Web](https://www.100ms.live/docs/javascript/v2/quickstart/prebuilt-quickstart).
+ - Get started with 100ms Prebuilt using the [Prebuilt Quickstart for Web](https://www.100ms.live/docs/javascript/v2/quickstart/prebuilt-quickstart). > Navigate to `roomkit-react` for the React components used in Prebuilt and the Prebuilt component itself by clicking [here](./packages/roomkit-react). -
- ![Banner](prebuilt-banner.png) ### 100ms Prebuilt Cross Platform Support @@ -51,9 +57,6 @@ The 100ms SDK gives you everything you need to build scalable, high-quality live | Flutter | [100ms-flutter](https://github.com/100mslive/100ms-flutter/tree/main/packages/hms_room_kit) | [Link](https://www.100ms.live/docs/flutter/v2/quickstart/prebuilt) | [hms_room_kit/example](https://github.com/100mslive/100ms-flutter/tree/main/packages/hms_room_kit/example) | | React Native | [100ms-react-native](https://github.com/100mslive/100ms-react-native/tree/main/packages/react-native-room-kit) | [Link](https://www.100ms.live/docs/react-native/v2/quickstart/prebuilt) | [react-native-room-kit/example](https://github.com/100mslive/100ms-react-native/tree/main/packages/react-native-room-kit/example) | -
-
- ## Setup ### Local Setup @@ -62,7 +65,7 @@ The 100ms SDK gives you everything you need to build scalable, high-quality live if you are using a different version in other projects, use [nvm](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating) to manage node versions. -``` +```bash git clone https://github.com/100mslive/web-sdks.git cd web-sdks yarn install @@ -71,7 +74,7 @@ yarn build ### Running Sample Prebuilt -``` +```bash cd examples/prebuilt-react-integration yarn dev ``` @@ -86,9 +89,7 @@ Run `yarn start` by navigating to the package you are making changes to, the cha For example, if you are making changes in roomkit-react(prebuilt), run `yarn start` in that package. The sample app should auto reload. -> Note: Make sure `yarn build` is run atleast once before using `yarn start` - -
+> Note: Make sure `yarn build` is run atleast once before using `yarn start`. ### Deploying Your Changes @@ -104,17 +105,14 @@ For reference: ![Project Settings](./project-settings.png) -
Once the app has been deployed, you can append the room code at the end of the deployment URL to preview your changes -
- ### Maintaining A Forked Version -Tthe following command will build the roomkit-react package and generate a .tgz file: +The following command will build the roomkit-react package and generate a .tgz file: -``` +```bash yarn && yarn build; cd packages/roomkit-react; yarn pack @@ -142,8 +140,6 @@ import { HMSPrebuilt } from '@100mslive/roomkit-react'; We welcome external contributors or anyone excited to help improve 100ms SDKs. If you'd like to get involved, check out our [contribution guide](./DEVELOPER.MD), and get started exploring the codebase. -
- ## Community & Support - [GitHub Issues](https://github.com/100mslive/web-sdks/issues) - Submit any bugs or errors you encounter using the Web SDKs. diff --git a/examples/prebuilt-react-integration/package.json b/examples/prebuilt-react-integration/package.json index 8c838ca36b..643cb76447 100644 --- a/examples/prebuilt-react-integration/package.json +++ b/examples/prebuilt-react-integration/package.json @@ -10,7 +10,7 @@ "preview": "vite preview" }, "dependencies": { - "@100mslive/roomkit-react": "0.3.24", + "@100mslive/roomkit-react": "0.3.25", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/package.json b/package.json index 52db340ecb..652b546ff7 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "private": true, "devDependencies": { "@babel/core": "^7.8.0", - "@commitlint/cli": "^15.0.0", - "@commitlint/config-conventional": "^15.0.0", + "@commitlint/cli": "^19.6.0", + "@commitlint/config-conventional": "^19.6.0", "@size-limit/file": "^5.0.3", "@types/jest": "^27.0.3", "@types/node": "^16.11.17", diff --git a/packages/hls-player/README.md b/packages/hls-player/README.md index 0dda1052f8..0f8e8f1dd5 100644 --- a/packages/hls-player/README.md +++ b/packages/hls-player/README.md @@ -1,17 +1,348 @@ -`@100mslive/hls-player` is currently a wrapper on hls.js with easy to use interface and few add-ons for [100ms's interactive live streaming feature](https://www.100ms.live/docs/javascript/v2/how--to-guides/record-and-live-stream/hls/hls). +# 100ms HLS Player -Sample usage: +[![Lint, Test and Build](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml/badge.svg)](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml) +[![Bundle Size](https://badgen.net/bundlephobia/minzip/@100mslive/hls-player)](https://bundlephobia.com/result?p=@100mslive/hls-player) +[![License](https://img.shields.io/npm/l/@100mslive/hls-player)](https://www.100ms.live/) +![Tree shaking](https://badgen.net/bundlephobia/tree-shaking/@100mslive/hls-player) +The `HMSHLSPlayer` is an HLS player offered by 100ms that can be used to play HLS streams. The player takes a URL and video element to play the stream. + +## How to integrate HLS Player SDK + +You can use Node package manager or yarn to add HMSHLSPlayer sdk to your project. +Use [@100mslive/hls-player](https://www.npmjs.com/package/@100mslive/hls-player) as the package source. + +```bash +npm i @100mslive/hls-player +``` + +## HMSHLSPlayer methods + +Below shows all the methods exposed from player + +```javascript +interface IHMSHLSPlayer { + /** + * @returns get html video element + */ + getVideoElement(): HTMLVideoElement; + + /** + * set video volumne + * @param { volume } - in range [0,100] + */ + setVolume(volume: number): void; + /** + * + * @returns returns HMSHLSLayer which represents current + * quality. + */ + getLayer(): HMSHLSLayer | null; + /** + * + * @param { HMSHLSLayer } layer - layer we want to set the stream to. + * set { height: auto } to set the layer to auto + */ + setLayer(layer: HMSHLSLayer): void; + /** + * move the video to Live + */ + seekToLivePosition(): Promise; + /** + * play stream + * call this when autoplay error is received + */ + play(): Promise; + /** + * pause stream + */ + pause(): void; + + /** + * It will update the video element current time + * @param seekValue Pass currentTime in second + */ + seekTo(seekValue: number): void; +} +``` + +### How to use Player's HLS Stream + +You create an instance of HMSHLSPlayer like below: + +```javascript +import { HMSHLSPlayer } from '@100mslive/hls-player'; + +// hls url should be provided which player will run. +// second parameter is optional, if you had video element then pass to player else we will create one. +const hlsPlayer = new HMSHLSPlayer(hlsURL, videoEl) + +// if video element is not present, we will create a new video element which can be attached to your ui. +const videoEl = hlsPlayer.getVideoElement(); +``` + +### How to pause and resume the playback + +You call play/pause on the hlsPlayer instance like below: + +```javascript +// return Promise +hmsPlayer.play() + +hmsPlayer.pause() +``` + +### How to seek forward or backward + +You use `seekTo` methods on the hlsPlayer to seek to any position in video, below is given example: + +```javascript +// seekValue Pass currentTime in second +hmsPlayer.seekTo(5) +``` + +### How to seek to live position + +You use `seekToLivePosition` methods on the hlsPlayer instance to go to the live poition like below: + +```javascript +hmsPlayer.seekToLivePosition() +``` + +### How to change volume of HLS playback + +Use volume property to change the volume of HLS player. The volume level is between 0-100. Volume is set to 100 by default. + +```javascript +hlsPlayer.setVolume(50); ``` -import { - HLSPlaybackState, -} from "@100mslive/hls-player"; -// hlsUrl is the url in which the hls stream is ongoing -// videoElement is the video element where you want to play the stream -const player = new HMSHLSPlayer(hlsUrl, videoElement); -player.play() +### Set video quality level to hls player +```javascript +/** +* +* @returns returns HMSHLSLayer which represents current +* quality. +*/ +hlsPlayer.getLayer(): HMSHLSLayer | null; +/** +* +* @param { HMSHLSLayer } layer - layer we want to set the stream to. +* set { height: auto } to set the layer to auto +*/ +hlsPlayer.setLayer(layer: HMSHLSLayer): void; + +// quality interface +interface HMSHLSLayer { + readonly bitrate: number; + readonly height?: number; + readonly id?: number; + readonly width?: number; + url: string; + resolution?: string; +} ``` -More details to be added soon. +## Events exposed from HMSHLSPlayer + +We are exposing events from our hls player. + +```javascript +enum HMSHLSPlayerEvents { + TIMED_METADATA_LOADED = 'timed-metadata', + SEEK_POS_BEHIND_LIVE_EDGE = 'seek-pos-behind-live-edge', + + CURRENT_TIME = 'current-time', + AUTOPLAY_BLOCKED = 'autoplay-blocked', + + MANIFEST_LOADED = 'manifest-loaded', + LAYER_UPDATED = 'layer-updated', + + ERROR = 'error', + PLAYBACK_STATE = 'playback-state', + STATS = 'stats', +} +``` + +### Playback state + +```javascript +enum HLSPlaybackState { + playing, + paused, +} +interface HMSHLSPlaybackState { + state: HLSPlaybackState; +} +hlsPlayer.on(HMSHLSPlayerEvents.PLAYBACK_STATE, (event: HMSHLSPlayerEvents, data: HMSHLSPlaybackState): void => {}); +``` + +### HLS Stats + +```javascript +interface HlsPlayerStats { + /** Estimated bandwidth in bits/sec. Could be used to show connection speed. */ + bandwidthEstimate?: number; + /** The bitrate of the current level that is playing. Given in bits/sec */ + bitrate?: number; + /** the amount of video available in forward buffer. Given in ms */ + bufferedDuration?: number; + /** how far is the current playback from live edge.*/ + distanceFromLive?: number; + /** total Frames dropped since started watching the stream. */ + droppedFrames?: number; + /** the m3u8 url of the current level that is being played */ + url?: string; + /** the resolution of the level of the video that is being played */ + videoSize?: { + height: number; + width: number; + }; +} + +hlsPlayer.on(HMSHLSPlayerEvents.STATS, (event: HMSHLSPlayerEvents, data: HlsPlayerStats): void => {}); +``` + +### Manifest loaded data + +Hls player will provide a manifest which will provide a data like different quality layer once url is loaded. + +```javascript +interface HMSHLSManifestLoaded { + layers: HMSHLSLayer[]; +} +hlsPlayer.on(HMSHLSPlayerEvents.MANIFEST_LOADED, (event: HMSHLSPlayerEvents, data: HMSHLSManifestLoaded): void => {}); +``` + +### Quality changed data + +```javascript +interface HMSHLSLayerUpdated { + layer: HMSHLSLayer; +} +hlsPlayer.on(HMSHLSPlayerEvents.LAYER_UPDATED, (event: HMSHLSPlayerEvents, data: HMSHLSLayerUpdated): void => {}); +``` + +### Live Event + +Player will let you know if player is plaaying video live or not + +```javascript +interface HMSHLSStreamLive { + isLive: boolean; +} +hlsPlayer.on(HMSHLSPlayerEvents.SEEK_POS_BEHIND_LIVE_EDGE, (event: HMSHLSPlayerEvents, data: HMSHLSStreamLive): void => {}); +``` + +### HLS timed-metadata + +HLS player will parse and send the timed-metadata. + +```javascript +interface HMSHLSCue { + id?: string; + payload: string; + duration: number; + startDate: Date; + endDate?: Date; +} +hlsPlayer.on(HMSHLSPlayerEvents.TIMED_METADATA_LOADED, (event: HMSHLSPlayerEvents, data: HMSHLSCue): void => {}); +``` + +### Error handling + +```javascript +interface HMSHLSException { + name: string, + message: string, + description: string, + isTerminal: boolean, // decide if player error will automatically restart(if false) +} +hlsPlayer.on(HMSHLSPlayerEvents.ERROR, (event: HMSHLSPlayerEvents, data: HMSHLSException): void => {}); +hlsPlayer.on(HMSHLSPlayerEvents.AUTOPLAY_BLOCKED, (event: HMSHLSPlayerEvents, data: HMSHLSException): void => {}); +``` + +### Video current time + +```javascript +hlsPlayer.on(HMSHLSPlayerEvents.CURRENT_TIME, (event: HMSHLSPlayerEvents, data: number): void => {}); +``` + +### Example for events usage + +Below are the simple example of how to use hls player's event + +```javascript +const isPlaying = false; +const playbackEventHandler = data => isPlaying = data.state === HLSPlaybackState.paused; +hlsPlayer.on(HMSHLSPlayerEvents.PLAYBACK_STATE, playbackEventHandler); +``` + +## HLS Player example + +Below is a simple example in which hls-player will be used in your app. + +```javascript +// Vanilla JavaScript Example +import { HLSPlaybackState, HMSHLSPlayer, HMSHLSPlayerEvents } from "@100mslive/hls-player"; + +const videoEl; // reference for video element +const hlsUrl; // reference to hls url + +// variable to handle ui and take some actions +let isLive = true, isPaused = false, isAutoBlockedPaused = false; + +const handleError = data => console.error("[HLSView] error in hls", data); +const handleNoLongerLive = ({ isLive }) => isLive = isLive; + +const playbackEventHandler = data => isPaused = (data.state === HLSPlaybackState.paused); + +const handleAutoplayBlock = data => isAutoBlockedPaused = !!data; + +const hlsPlayer = new HMSHLSPlayer(hlsUrl, videoEl); + +hlsPlayer.on(HMSHLSPlayerEvents.SEEK_POS_BEHIND_LIVE_EDGE, handleNoLongerLive); +hlsPlayer.on(HMSHLSPlayerEvents.ERROR, handleError); +hlsPlayer.on(HMSHLSPlayerEvents.PLAYBACK_STATE, playbackEventHandler); +hlsPlayer.on(HMSHLSPlayerEvents.AUTOPLAY_BLOCKED, handleAutoplayBlock); +``` + +```jsx +// React Example +import { HLSPlaybackState, HMSHLSPlayer, HMSHLSPlayerEvents } from "@100mslive/hls-player"; +import { useEffect, useState } from 'react'; + +const videoEl; // reference for video element +const hlsUrl; // reference to hls url + +// variable to handle ui and take some actions +const [isVideoLive, setIsVideoLive] = useState(true); +const [isHlsAutoplayBlocked, setIsHlsAutoplayBlocked] = useState(false); +const [isPaused, setIsPaused] = useState(false); + +useEffect(() => { + const handleError = data => console.error("[HLSView] error in hls", data); + const handleNoLongerLive = ({ isLive }) => { + setIsVideoLive(isLive); + }; + + const playbackEventHandler = data => + setIsPaused(data.state === HLSPlaybackState.paused); + + const handleAutoplayBlock = data => setIsHlsAutoplayBlocked(!!data); + const hlsPlayer = new HMSHLSPlayer(hlsUrl, videoEl); + + hlsPlayer.on(HMSHLSPlayerEvents.SEEK_POS_BEHIND_LIVE_EDGE, handleNoLongerLive); + hlsPlayer.on(HMSHLSPlayerEvents.ERROR, handleError); + hlsPlayer.on(HMSHLSPlayerEvents.PLAYBACK_STATE, playbackEventHandler); + hlsPlayer.on(HMSHLSPlayerEvents.AUTOPLAY_BLOCKED, handleAutoplayBlock); + return () => { + hlsPlayer.off(HMSHLSPlayerEvents.SEEK_POS_BEHIND_LIVE_EDGE, handleNoLongerLive); + hlsPlayer.off(HMSHLSPlayerEvents.ERROR, handleError); + hlsPlayer.off(HMSHLSPlayerEvents.PLAYBACK_STATE, playbackEventHandler); + hlsPlayer.off(HMSHLSPlayerEvents.AUTOPLAY_BLOCKED, handleAutoplayBlock); + } +}, []); + +``` diff --git a/packages/hls-player/package.json b/packages/hls-player/package.json index af5c74f56e..b0e2fff8e9 100644 --- a/packages/hls-player/package.json +++ b/packages/hls-player/package.json @@ -1,6 +1,6 @@ { "name": "@100mslive/hls-player", - "version": "0.3.24", + "version": "0.3.25", "description": "HLS client library which uses HTML5 Video element and Media Source Extension for playback", "main": "dist/index.cjs.js", "module": "dist/index.js", @@ -36,8 +36,16 @@ "author": "100ms", "license": "MIT", "dependencies": { - "@100mslive/hls-stats": "0.4.24", + "@100mslive/hls-stats": "0.4.25", "eventemitter2": "^6.4.9", "hls.js": "1.4.12" - } + }, + "keywords": [ + "hls", + "video", + "player", + "webrtc", + "conferencing", + "100ms" + ] } diff --git a/packages/hls-stats/README.md b/packages/hls-stats/README.md index d422d6ecd0..1af6ec5ed8 100644 --- a/packages/hls-stats/README.md +++ b/packages/hls-stats/README.md @@ -1,5 +1,11 @@ # @100mslive/hms-stats +[![Lint, Test and Build](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml/badge.svg)](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml) +[![Bundle Size](https://badgen.net/bundlephobia/minzip/@100mslive/hls-stats)](https://bundlephobia.com/result?p=@100mslive/hls-stats) +[![License](https://img.shields.io/npm/l/@100mslive/hls-stats)](https://www.100ms.live/) +![Tree shaking](https://badgen.net/bundlephobia/tree-shaking/@100mslive/hls-stats) + + A simple library for HLS stats for Hls.js. ## Installation diff --git a/packages/hls-stats/package.json b/packages/hls-stats/package.json index fc3522ae93..6b7f077190 100644 --- a/packages/hls-stats/package.json +++ b/packages/hls-stats/package.json @@ -1,6 +1,6 @@ { "name": "@100mslive/hls-stats", - "version": "0.4.24", + "version": "0.4.25", "description": "A simple library that provides stats for your hls stream", "main": "dist/index.cjs.js", "module": "dist/index.js", diff --git a/packages/hms-video-store/README.md b/packages/hms-video-store/README.md index 8c37e598cd..1d2f8b5f8e 100644 --- a/packages/hms-video-store/README.md +++ b/packages/hms-video-store/README.md @@ -1,8 +1,9 @@ # 100ms Reactive Store [![NPM](https://badgen.net/npm/v/@100mslive/hms-video-store?color=green)](https://www.npmjs.com/package/@100mslive/hms-video-store) -![Test](https://github.com/100mslive/hms-video-store/actions/workflows/main.yaml/badge.svg) +[![Lint, Test and Build](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml/badge.svg)](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml) [![Bundle Size](https://badgen.net/bundlephobia/minzip/@100mslive/hms-video-store)](https://bundlephobia.com/result?p=@100mslive/hms-video-store) +[![License](https://img.shields.io/npm/l/@100mslive/hms-video-store)](https://www.100ms.live/) ![Tree shaking](https://badgen.net/bundlephobia/tree-shaking/@100mslive/hms-video-store) ![Architecture](images/architecture.png) diff --git a/packages/hms-video-store/package.json b/packages/hms-video-store/package.json index dcfed593f6..d16cebb840 100644 --- a/packages/hms-video-store/package.json +++ b/packages/hms-video-store/package.json @@ -1,5 +1,5 @@ { - "version": "0.12.24", + "version": "0.12.25", "license": "MIT", "repository": { "type": "git", diff --git a/packages/hms-video-store/src/IHMSActions.ts b/packages/hms-video-store/src/IHMSActions.ts index 01cc905201..bcfe6dbdbc 100644 --- a/packages/hms-video-store/src/IHMSActions.ts +++ b/packages/hms-video-store/src/IHMSActions.ts @@ -40,6 +40,7 @@ import { IHMSSessionStoreActions, } from './schema'; import { HMSRoleChangeRequest } from './selectors'; +import { HMSStats } from './webrtc-stats'; /** * The below interface defines our SDK API Surface for taking room related actions. @@ -587,4 +588,10 @@ export interface IHMSActions = [ - // @ts-ignore - { googEchoCancellation: { exact: true } }, - // @ts-ignore - { googExperimentalEchoCancellation: { exact: true } }, - // @ts-ignore + ...standardMediaConstraints, { autoGainControl: { exact: true } }, // @ts-ignore { noiseSuppression: { exact: true } }, - // @ts-ignore - { googHighpassFilter: { exact: true } }, - // @ts-ignore - { googAudioMirroring: { exact: true } }, ]; - volume(volume: number) { if (!(0.0 <= volume && volume <= 1.0)) { throw Error('volume can only be in range [0.0, 1.0]'); diff --git a/packages/hms-video-store/src/media/settings/constants.ts b/packages/hms-video-store/src/media/settings/constants.ts new file mode 100644 index 0000000000..e1f0e8f4d2 --- /dev/null +++ b/packages/hms-video-store/src/media/settings/constants.ts @@ -0,0 +1,8 @@ +export const standardMediaConstraints = [ + { echoCancellation: { exact: true } }, + { highpassFilter: { exact: true } }, + { audioMirroring: { exact: true } }, + // These options can vary depending on the audio plugin + // { autoGainControl: { exact: true } }, + // { noiseSuppression: { exact: true } }, +]; diff --git a/packages/hms-video-store/src/plugins/audio/HMSAudioPluginsManager.ts b/packages/hms-video-store/src/plugins/audio/HMSAudioPluginsManager.ts index 75100bb9a5..fa84d76ed4 100644 --- a/packages/hms-video-store/src/plugins/audio/HMSAudioPluginsManager.ts +++ b/packages/hms-video-store/src/plugins/audio/HMSAudioPluginsManager.ts @@ -4,6 +4,8 @@ import AnalyticsEventFactory from '../../analytics/AnalyticsEventFactory'; import { ErrorFactory } from '../../error/ErrorFactory'; import { HMSAction } from '../../error/HMSAction'; import { EventBus } from '../../events/EventBus'; +import { HMSAudioTrackSettingsBuilder } from '../../media/settings'; +import { standardMediaConstraints } from '../../media/settings/constants'; import { HMSLocalAudioTrack } from '../../media/tracks'; import Room from '../../sdk/models/HMSRoom'; import HMSLogger from '../../utils/logger'; @@ -74,7 +76,7 @@ export class HMSAudioPluginsManager { } switch (plugin.getName()) { - case 'HMSKrispPlugin': + case 'HMSKrispPlugin': { if (!this.room?.isNoiseCancellationEnabled) { const errorMessage = 'Krisp Noise Cancellation is not enabled for this room'; if (this.pluginsMap.size === 0) { @@ -85,7 +87,23 @@ export class HMSAudioPluginsManager { } } this.eventBus.analytics.publish(AnalyticsEventFactory.krispStart()); + const { settings } = this.hmsTrack; + const newAudioTrackSettings = new HMSAudioTrackSettingsBuilder() + .codec(settings.codec) + .maxBitrate(settings.maxBitrate) + .deviceId(settings.deviceId!) + .advanced([ + ...standardMediaConstraints, + // @ts-ignore + { autoGainControl: { exact: false } }, + // @ts-ignore + { noiseSuppression: { exact: false } }, + ]) + .audioMode(settings.audioMode) + .build(); + await this.hmsTrack.setSettings(newAudioTrackSettings); break; + } default: } @@ -162,9 +180,25 @@ export class HMSAudioPluginsManager { async removePlugin(plugin: HMSAudioPlugin) { switch (plugin.getName()) { - case 'HMSKrispPlugin': + case 'HMSKrispPlugin': { this.eventBus.analytics.publish(AnalyticsEventFactory.krispStop()); + const { settings } = this.hmsTrack; + const newAudioTrackSettings = new HMSAudioTrackSettingsBuilder() + .codec(settings.codec) + .maxBitrate(settings.maxBitrate) + .deviceId(settings.deviceId!) + .advanced([ + ...standardMediaConstraints, + // @ts-ignore + { autoGainControl: { exact: true } }, + // @ts-ignore + { noiseSuppression: { exact: true } }, + ]) + .audioMode(settings.audioMode) + .build(); + await this.hmsTrack.setSettings(newAudioTrackSettings); break; + } default: break; } diff --git a/packages/hms-video-store/src/reactive-store/HMSSDKActions.ts b/packages/hms-video-store/src/reactive-store/HMSSDKActions.ts index a6f77818a4..9b880b70f7 100644 --- a/packages/hms-video-store/src/reactive-store/HMSSDKActions.ts +++ b/packages/hms-video-store/src/reactive-store/HMSSDKActions.ts @@ -92,6 +92,7 @@ import { selectVideoTrackByID, } from '../selectors'; import { FindPeerByNameRequestParams } from '../signal/interfaces'; +import { HMSStats } from '../webrtc-stats'; /** * This class implements the IHMSActions interface for 100ms SDK. It connects with SDK @@ -1671,4 +1672,24 @@ export class HMSSDKActions> = (fn, name) => { return this.store.namedSetState(fn, name); }; + + /** + * @internal + * This will be used by beam to check if the recording should continue, it will pass __hms.stats + * It will poll at a fixed interval and start an exit timer if the method fails twice consecutively + * The exit timer is stopped if the method returns true before that + * @param hmsStats + */ + hasActiveElements(hmsStats: HMSStats): boolean { + const isWhiteboardPresent = Object.keys(this.store.getState().whiteboards).length > 0; + const isQuizOrPollPresent = Object.keys(this.store.getState().polls).length > 0; + const peerCount = Object.keys(this.store.getState().peers).length > 0; + const remoteTracks = hmsStats.getState().remoteTrackStats; + return ( + peerCount && + (isWhiteboardPresent || + isQuizOrPollPresent || + Object.values(remoteTracks).some(track => track && typeof track.bitrate === 'number' && track.bitrate > 0)) + ); + } } diff --git a/packages/hms-video-store/src/sdk/RoleChangeManager.ts b/packages/hms-video-store/src/sdk/RoleChangeManager.ts index c4be09307b..c13b2519bf 100644 --- a/packages/hms-video-store/src/sdk/RoleChangeManager.ts +++ b/packages/hms-video-store/src/sdk/RoleChangeManager.ts @@ -1,6 +1,6 @@ import { Store } from './store'; import { DeviceManager } from '../device-manager'; -import { HMSRole } from '../interfaces'; +import { DeviceType, HMSRole } from '../interfaces'; import InitialSettings from '../interfaces/settings'; import { SimulcastLayers } from '../interfaces/simulcast-layers'; import { HMSPeerUpdate, HMSTrackUpdate, HMSUpdateListener } from '../interfaces/update-listener'; @@ -144,14 +144,49 @@ export default class RoleChangeManager { } private getSettings(): InitialSettings { - const initialSettings = this.store.getConfig()?.settings; + const { isAudioMuted, isVideoMuted } = this.getMutedStatus(); + const { audioInputDeviceId, audioOutputDeviceId } = this.getAudioDeviceSettings(); + const videoDeviceId = this.getVideoInputDeviceId(); + return { + isAudioMuted: isAudioMuted, + isVideoMuted: isVideoMuted, + audioInputDeviceId: audioInputDeviceId, + audioOutputDeviceId: audioOutputDeviceId, + videoDeviceId: videoDeviceId, + }; + } + private getMutedStatus(): { isAudioMuted: boolean; isVideoMuted: boolean } { + const initialSettings = this.store.getConfig()?.settings; return { isAudioMuted: initialSettings?.isAudioMuted ?? true, isVideoMuted: initialSettings?.isVideoMuted ?? true, - audioInputDeviceId: initialSettings?.audioInputDeviceId || 'default', - audioOutputDeviceId: initialSettings?.audioOutputDeviceId || 'default', - videoDeviceId: initialSettings?.videoDeviceId || 'default', }; } + + private getAudioDeviceSettings(): { audioInputDeviceId: string; audioOutputDeviceId: string } { + const initialSettings = this.store.getConfig()?.settings; + const audioInputDeviceId = + this.deviceManager.currentSelection[DeviceType.audioInput]?.deviceId || + initialSettings?.audioInputDeviceId || + 'default'; + const audioOutputDeviceId = + this.deviceManager.currentSelection[DeviceType.audioOutput]?.deviceId || + initialSettings?.audioOutputDeviceId || + 'default'; + + return { + audioInputDeviceId, + audioOutputDeviceId, + }; + } + + private getVideoInputDeviceId(): string { + const initialSettings = this.store.getConfig()?.settings; + return ( + this.deviceManager.currentSelection[DeviceType.videoInput]?.deviceId || + initialSettings?.videoDeviceId || + 'default' + ); + } } diff --git a/packages/hms-video-store/src/sdk/index.ts b/packages/hms-video-store/src/sdk/index.ts index fe6040e751..50bd36b0eb 100644 --- a/packages/hms-video-store/src/sdk/index.ts +++ b/packages/hms-video-store/src/sdk/index.ts @@ -1350,6 +1350,7 @@ export class HMSSdk implements HMSInterface { } private handleLocalRoleUpdate = async ({ oldRole, newRole }: { oldRole: HMSRole; newRole: HMSRole }) => { + this.deviceManager.currentSelection = this.deviceManager.getCurrentSelection(); await this.transport.handleLocalRoleUpdate({ oldRole, newRole }); await this.roleChangeManager?.handleLocalPeerRoleUpdate({ oldRole, newRole }); await this.interactivityCenter.whiteboard.handleLocalRoleUpdate(); diff --git a/packages/hms-virtual-background/README.md b/packages/hms-virtual-background/README.md index ac8a38d7c4..75ec13a8ec 100755 --- a/packages/hms-virtual-background/README.md +++ b/packages/hms-virtual-background/README.md @@ -1,13 +1,243 @@ -## installation +# Virtual Background with Effects SDK -**with npm:** +[![Lint, Test and Build](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml/badge.svg)](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml) +[![Bundle Size](https://badgen.net/bundlephobia/minzip/@100mslive/hms-virtual-background)](https://bundlephobia.com/result?p=@100mslive/hms-virtual-background) +[![License](https://img.shields.io/npm/l/@100mslive/hms-virtual-background)](https://www.100ms.live/) +![Tree shaking](https://badgen.net/bundlephobia/tree-shaking/@100mslive/hms-virtual-background) -```npm i --save @100mslive/hms-virtual-background``` +Virtual background plugin helps in customising one’s background. The customising options are blurring the background or replacing it with a static image. This guide provides an overview of usage of the virtual background plugin of 100ms. -**with yarn** +## Pre-requisites -```yarn add @100mslive/hms-virtual-background``` +Get the 100ms VirtualBackground Package** (Supported since version 1.11.28) -## Usage +```bash section=GetHMSVirtualBackgroundPackage sectionIndex=1 +npm install --save @100mslive/hms-virtual-background@latest +``` -Refer our [docs](https://www.100ms.live/docs/javascript/v2/plugins/virtual-background#start-and-stop-virtual-background) for usage. \ No newline at end of file +## Features + +The following features are currently supported under the `HMSEffectsPlugin` class: + +```js +/** + * Sets the blur intensity. + * @param {number} blur - The blur intensity, ranging from 0 to 1. + * @returns {void} + */ +setBlur(blur) {} + +/** + * Sets the virtual background using the provided media URL. + * @param {HMSEffectsBackground} url - The media URL to set as the virtual background. + * Alternatively, the background image can be downloaded beforehand and passed to setBackground as objectURL + * @returns {void} + */ +setBackground(url) {} + +/** + * Retrieves the name of the plugin. + * @returns {string} The name of the plugin, 'HMSEffects'. + */ +getName() {} + +/** + * Retrieves the currently enabled background type or media URL. + * @returns {string|MediaStream|MediaStreamTrack|HTMLVideoElement} The background type or media URL. + */ +getBackground() {} + +/** + * Sets the preset quality of the virtual background. + * Options: "balanced" | "quality" + * The 'quality' preset has a higher CPU usage than the 'balanced' preset which is the default + * @param {string} preset - The preset quality to set. + * @returns {Promise} + */ +setPreset(preset) {} + +/** + * Retrieves the active preset quality of the virtual background. + * @returns {string} The active preset quality. + */ +getPreset() {} + +/** + * Clears all applied filters. + * @returns {void} + */ +removeEffects() {} + +/** + * Stops the plugin. + * @returns {void} + */ +stop() {} + +``` + +Callbacks supported by the plugin: + +On initialization, after the required resources are downloaded by the plugin: + +``` +const effectsPlugin = new HMSEffectsPlugin(, () => console.log("Plugin initialised")); + +``` + +On resolution change (on device rotation or when the video aspect ratio changes): + +``` +effectsPlugin.onResolutionChange = (width: number, height: number) => { + console.log(`Resolution changed to ${width}x${height}`) +} +``` + +## Instantiate Virtual Background + +The SDK key for effects is needed to instantiate the `HMSEffectsPlugin` class: + +```jsx + const effectsKey = useHMSStore(selectEffectsKey); +``` + +It is recommended to initialise the object in a separate file to prevent multiple initialisations on re-renders and keep the UI level code independent of internal calls by the SDK. + + +```js +import { HMSEffectsPlugin, HMSVirtualBackgroundTypes } from '@100mslive/hms-virtual-background'; + +export class VBPlugin { + private effectsPlugin?: HMSEffectsPlugin | undefined; + + initialisePlugin = (effectsSDKKey?: string) => { + if (this.getVBObject()) { + return; + } + if (effectsSDKKey) { + this.effectsPlugin = new HMSEffectsPlugin(effectsSDKKey); + } + }; + + getBackground = () => { + return this.effectsPlugin?.getBackground(); + }; + + getBlurAmount = () => { + return this.effectsPlugin?.getBlurAmount(); + }; + + getVBObject = () => { + return this.effectsPlugin; + }; + + getName = () => { + return this.effectsPlugin?.getName(); + }; + + setBlur = async (blurPower: number) => { + this.effectsPlugin?.setBlur(blurPower); + }; + + setBackground = async (mediaURL: string) => { + this.effectsPlugin?.setBackground(mediaURL); + }; + + setPreset = (preset: string) => { + this.effectsPlugin.setPreset(preset); + }; + + getPreset = () => { + return this.effectsPlugin?.getPreset() || ''; + }; + + removeEffects = async () => { + this.effectsPlugin?.removeEffects(); + }; + + reset = () => { + this.effectsPlugin = undefined; + }; +} + +export const VBHandler = new VBPlugin(); +``` + +## Building the UI + +The following snippet illustrates how to add the plugin to the video and manage the UI state to preserve configuration: + +```jsx +import { + selectEffectsKey, + selectIsLocalVideoPluginPresent + selectLocalVideoTrackID, + useHMSStore, +} from '@100mslive/react-sdk'; +import { + HMSEffectsPlugin, + HMSVirtualBackgroundTypes +} from '@100mslive/hms-virtual-background'; +import { VBHandler } from './VBHandler'; + +export const VirtualBackgroundPicker = () => { + const hmsActions = useHMSActions(); + // Get the effects SDK key here + const effectsKey = useHMSStore(selectEffectsKey); + const trackId = useHMSStore(selectLocalVideoTrackID); + const isPluginAdded = useHMSStore(selectIsLocalVideoPluginPresent(VBHandler?.getName() || '')); + + // State can be used to show active selection + const [background, setBackground] = useState( + VBHandler.getBackground() as string | HMSVirtualBackgroundTypes, + ); + + useEffect(() => { + if (!track?.id) { + return; + } + if (!isPluginAdded) { + let vbObject = VBHandler.getVBObject(); + if (!vbObject) { + VBHandler.initialisePlugin(effectsKey); + vbObject = VBHandler.getVBObject(); + if (effectsKey) { + hmsActions.addPluginsToVideoStream([vbObject as HMSEffectsPlugin]); + } + } + } + }, [hmsActions, isPluginAdded, effectsKey, track?.id]); + + // UI code for media picker can go here +} + +``` + +This handles initialisation and adding the plugin to the video stream. The plugin takes a few seconds on first load during initialisation. Subsequent filter and effect selections should take less than a second to reflect. + +The methods can be called via the `VBHandler` object: +```jsx +const setBackground = async(mediaURL : string) => { + await VBHandler?.setBackground(mediaURL); + // The selection can be highlighted using the activeBackground state + setActiveBackground(mediaURL); +} + +const setBlur = async(blurAmount: number) => { + await VBHandler?.setBlur(blurAmount); + setActiveBackground(HMSVirtualBackgroundTypes.BLUR); +} + +const removeEffects = async() => { + await VBHandler.removeEffects(); + setActiveBackground(HMSVirtualBackgroundTypes.NONE); +} +``` + + +The full implementation can be viewed in the [roomkit-react package](https://github.com/100mslive/web-sdks/blob/main/packages/roomkit-react/src/Prebuilt/components/VirtualBackground/VBPicker.tsx). + + +## Supported Browsers + +Effects virtual background is supported on Safari, Firefox and Chromium based browsers.
diff --git a/packages/hms-virtual-background/package.json b/packages/hms-virtual-background/package.json index 3948195b3d..0df815ac53 100755 --- a/packages/hms-virtual-background/package.json +++ b/packages/hms-virtual-background/package.json @@ -1,5 +1,5 @@ { - "version": "1.13.24", + "version": "1.13.25", "license": "MIT", "name": "@100mslive/hms-virtual-background", "author": "100ms", @@ -62,10 +62,10 @@ "format": "prettier --write src/**/*.ts" }, "peerDependencies": { - "@100mslive/hms-video-store": "0.12.24" + "@100mslive/hms-video-store": "0.12.25" }, "devDependencies": { - "@100mslive/hms-video-store": "0.12.24" + "@100mslive/hms-video-store": "0.12.25" }, "dependencies": { "@mediapipe/selfie_segmentation": "^0.1.1632777926", diff --git a/packages/hms-whiteboard/README.md b/packages/hms-whiteboard/README.md new file mode 100644 index 0000000000..23137323d7 --- /dev/null +++ b/packages/hms-whiteboard/README.md @@ -0,0 +1,125 @@ +# 100ms Whiteboard + +[![Lint, Test and Build](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml/badge.svg)](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml) +[![Bundle Size](https://badgen.net/bundlephobia/minzip/@100mslive/hms-whiteboard)](https://bundlephobia.com/result?p=@100mslive/hms-whiteboard) +[![License](https://img.shields.io/npm/l/@100mslive/hms-whiteboard)](https://www.100ms.live/) +![Tree shaking](https://badgen.net/bundlephobia/tree-shaking/@100mslive/hms-whiteboard) + +The 100ms SDK provides robust APIs for integrating whiteboard collaboration into your conferencing sessions. Participants can engage in real-time by drawing, writing, and collaborating on a shared digital whiteboard. This documentation outlines how to implement the start and stop functionality for a whiteboard and display it within an iframe or embed it as a React component. + +## Requirements + +- React 18 or higher +- Webpack 5 or higher if you're using it to bundle your app +- User roles must be configured to enable whiteboard functionality via the 100ms dashboard for starting or viewing the whiteboard. [Refer here](https://www.100ms.live/docs/get-started/v2/get-started/features/whiteboard#enabling-and-configuring-the-whiteboard). +- If you're on React and are not using the `@100mslive/roomkit-react` package, install the `@100mslive/hms-whiteboard` package. + +```bash +yarn add @100mslive/hms-whiteboard +``` + +## Opening and Closing the Whiteboard + +JavaScript users can use the `selectPermissions` selector which fetches the whiteboard specific permissions array from the local peer's role permissions. + +React users can check for the `toggle` function returned by the utility hook `useWhiteboard`. + +```js +// Vanilla JavaScript Example +import { selectPermissions, selectWhiteboard } from '@100mslive/hms-video-store'; + +const permissions = hmsStore.getState(selectPermissions)?.whiteboard; // Array<'read' | 'write' | 'admin'> +const isAdmin = !!permissions?.includes('admin'); +const whiteboard = hmsStore.getState(selectWhiteboard); +const isOwner = whiteboard?.owner === localPeerUserId; + +const toggle = async () => { + if (!isAdmin) { + return; + } + + if (whiteboard?.open) { + isOwner && (await actions.interactivityCenter.whiteboard.close()); + } else { + await actions.interactivityCenter.whiteboard.open(); + } +}; + +// usage +const toggleButton = document.getElementById('toggle-whiteboard'); +// non-admin users cannot toggle the whiteboard +toggleButton.disabled = !isAdmin; +toggleButton.onclick = toggle; +``` + +```jsx +// React Example +import React from 'react'; +import { useWhiteboard } from '@100mslive/react-sdk'; + +export const WhiteboardToggle = () => { + const { toggle, open, isOwner } = useWhiteboard(); + + // non-admin users cannot toggle the whiteboard + if (!toggle) { + return null; + } + + return ( + + ); +}; +``` + +## Displaying the Collaborative Whiteboard + +You can display the whiteboard when it's open by embedding it as an iframe or as a React component for more fine-grained controls, if your app is built using React. + +```js +// Vanilla JavaScript Example +import { selectWhiteboard } from '@100mslive/hms-video-store'; + +const whiteboard = hmsStore.subscribe((whiteboard) => { + if (whiteboard?.open && whiteboard?.url) { + const whiteboardIframe = document.createElement('iframe'); + whiteboardIframe.src = whiteboard.url; + } else { + const whiteboardIframe = document.getElementById('whiteboard'); + whiteboardIframe?.remove(); + } +}, selectWhiteboard); +``` + +```jsx +// React Example +import React from 'react'; +import { useWhiteboard } from '@100mslive/react-sdk'; +import { Whiteboard } from '@100mslive/hms-whiteboard'; +import '@100mslive/hms-whiteboard/index.css'; + +const WhiteboardEmbed = () => { + const { token, endpoint } = useWhiteboard(); + + if (!token) { + return null; + } + + return ( +
+ { + console.log(store, editor); + }} + /> +
+ ); +}; +``` + +Whiteboard related CSS needs to be imported in your app's top level CSS files using `@import '@100mslive/hms-whiteboard/index.css';`(recommended) or in one of your top level JS file using `import '@100mslive/hms-whiteboard/index.css';`. + +Note that if you're using `@100mslive/roomkit-react` you'll need to import `@100mslive/roomkit-react/index.css` accordingly. diff --git a/packages/hms-whiteboard/package.json b/packages/hms-whiteboard/package.json index 1323e9215e..5692c97880 100644 --- a/packages/hms-whiteboard/package.json +++ b/packages/hms-whiteboard/package.json @@ -2,7 +2,7 @@ "name": "@100mslive/hms-whiteboard", "author": "100ms", "license": "MIT", - "version": "0.0.14", + "version": "0.0.15", "main": "dist/index.cjs.js", "module": "dist/index.js", "types": "dist/index.d.ts", @@ -53,10 +53,19 @@ "grpc-tools": "^1.12.4", "react": "^18.1.0", "react-dom": "^18.1.0", - "rollup": "^2.70.1", + "rollup": "^2.79.2", "rollup-plugin-esbuild": "^4.9.3", "rollup-plugin-import-css": "^3.5.0", "rollup-plugin-terser": "^7.0.2", "typescript": "^5.2.2" - } + }, + "keywords": [ + "whiteboard", + "tldraw", + "streaming", + "video", + "webrtc", + "conferencing", + "100ms" + ] } diff --git a/packages/react-icons/README.md b/packages/react-icons/README.md index 1e3d592ef4..b9477db9d3 100644 --- a/packages/react-icons/README.md +++ b/packages/react-icons/README.md @@ -1,4 +1,10 @@ -# React Icon Library +# 100ms React Icon Library + +[![Lint, Test and Build](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml/badge.svg)](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml) +[![Bundle Size](https://badgen.net/bundlephobia/minzip/@100mslive/react-icons)](https://bundlephobia.com/result?p=@100mslive/react-icons) +[![License](https://img.shields.io/npm/l/@100mslive/react-icons)](https://www.100ms.live/) +![Tree shaking](https://badgen.net/bundlephobia/tree-shaking/@100mslive/react-icons) + Using the package in your application diff --git a/packages/react-icons/package.json b/packages/react-icons/package.json index 757f6c0f1a..84da688e74 100644 --- a/packages/react-icons/package.json +++ b/packages/react-icons/package.json @@ -4,7 +4,7 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "typings": "dist/index.d.ts", - "version": "0.10.24", + "version": "0.10.25", "author": "100ms", "license": "MIT", "repository": { @@ -38,7 +38,7 @@ "@svgr/cli": "^6.3.1", "@types/react": "^18.1.0", "react": "^18.1.0", - "rollup": "^2.70.1", + "rollup": "^2.79.2", "rollup-plugin-esbuild": "^4.9.3", "rollup-plugin-terser": "^7.0.2" }, diff --git a/packages/react-sdk/README.md b/packages/react-sdk/README.md index 1cee52f0d3..c3f27ddd1a 100644 --- a/packages/react-sdk/README.md +++ b/packages/react-sdk/README.md @@ -1,4 +1,10 @@ -### About +# 100ms React SDK + +[![Lint, Test and Build](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml/badge.svg)](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml) +[![Bundle Size](https://badgen.net/bundlephobia/minzip/@100mslive/react-sdk)](https://bundlephobia.com/result?p=@100mslive/react-sdk) +[![License](https://img.shields.io/npm/l/@100mslive/react-sdk)](https://www.100ms.live/) +![Tree shaking](https://badgen.net/bundlephobia/tree-shaking/@100mslive/react-sdk) + This package is a react wrapper over the core SDK(`@100mslive/hms-video-store`). An easy way to use the store, actions and notifications are provided via diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index eaa962bbec..8af62c9e6d 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -4,7 +4,7 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "typings": "dist/index.d.ts", - "version": "0.10.24", + "version": "0.10.25", "author": "100ms", "license": "MIT", "repository": { @@ -40,7 +40,7 @@ "@rollup/plugin-typescript": "^8.3.1", "@types/react": "^18.1.0", "react": "^18.1.0", - "rollup": "^2.70.1", + "rollup": "^2.79.2", "rollup-plugin-esbuild": "^4.9.3", "rollup-plugin-terser": "^7.0.2" }, @@ -48,7 +48,7 @@ "react": ">=16.8 <19.0.0" }, "dependencies": { - "@100mslive/hms-video-store": "0.12.24", + "@100mslive/hms-video-store": "0.12.25", "react-resize-detector": "^7.0.0", "zustand": "^3.6.2" } diff --git a/packages/roomkit-react/README.md b/packages/roomkit-react/README.md index b4a8f1f1ae..d1e12f6c91 100644 --- a/packages/roomkit-react/README.md +++ b/packages/roomkit-react/README.md @@ -1,12 +1,17 @@ ![Banner](https://github.com/100mslive/web-sdks/blob/06c65259912db6ccd8617f2ecb6fef51429251ec/prebuilt-banner.png) +[![Lint, Test and Build](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml/badge.svg)](https://github.com/100mslive/web-sdks/actions/workflows/lint-test-build.yml) +[![Bundle Size](https://badgen.net/bundlephobia/minzip/@100mslive/roomkit-react)](https://bundlephobia.com/result?p=@100mslive/roomkit-react) +[![License](https://img.shields.io/npm/l/@100mslive/roomkit-react)](https://www.100ms.live/) +![Tree shaking](https://badgen.net/bundlephobia/tree-shaking/@100mslive/roomkit-react) + # Room Kit React Library 100ms Room Kit provides simple & easy to use UI components to build Live Streaming & Video Conferencing experiences in your apps. ## Installation -``` +```bash // npm npm install @100mslive/roomkit-react@latest --save @@ -16,7 +21,7 @@ yarn add @100mslive/roomkit-react@latest ## Usage -#### Using room code +### Using room code ```jsx import { HMSPrebuilt } from '@100mslive/roomkit-react' @@ -28,7 +33,7 @@ export default App() { } ``` -#### Using authToken +### Using authToken ```jsx import { HMSPrebuilt } from '@100mslive/roomkit-react' diff --git a/packages/roomkit-react/package.json b/packages/roomkit-react/package.json index a816c1bea7..851029877e 100644 --- a/packages/roomkit-react/package.json +++ b/packages/roomkit-react/package.json @@ -10,7 +10,7 @@ "prebuilt", "roomkit" ], - "version": "0.3.24", + "version": "0.3.25", "author": "100ms", "license": "MIT", "repository": { @@ -75,12 +75,12 @@ "react": ">=17.0.2 <19.0.0" }, "dependencies": { - "@100mslive/hls-player": "0.3.24", + "@100mslive/hls-player": "0.3.25", "@100mslive/hms-noise-cancellation": "0.0.1", - "@100mslive/hms-virtual-background": "1.13.24", - "@100mslive/hms-whiteboard": "0.0.14", - "@100mslive/react-icons": "0.10.24", - "@100mslive/react-sdk": "0.10.24", + "@100mslive/hms-virtual-background": "1.13.25", + "@100mslive/hms-whiteboard": "0.0.15", + "@100mslive/react-icons": "0.10.25", + "@100mslive/react-sdk": "0.10.25", "@100mslive/types-prebuilt": "0.12.12", "@emoji-mart/data": "^1.0.6", "@emoji-mart/react": "^1.0.1", diff --git a/packages/roomkit-react/src/Prebuilt/components/ConferenceScreen.tsx b/packages/roomkit-react/src/Prebuilt/components/ConferenceScreen.tsx index 428c44470c..3061a10157 100644 --- a/packages/roomkit-react/src/Prebuilt/components/ConferenceScreen.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/ConferenceScreen.tsx @@ -1,9 +1,8 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef } from 'react'; import { DefaultConferencingScreen_Elements } from '@100mslive/types-prebuilt'; import { v4 as uuid } from 'uuid'; import { HMSRoomState, - selectAppData, selectIsConnectedToRoom, selectRoomState, useAwayNotifications, @@ -34,7 +33,7 @@ import { // @ts-ignore: No implicit Any import { useAuthToken, useSetAppDataByKey } from './AppData/useUISettings'; import { useLandscapeHLSStream, useMobileHLSStream } from '../common/hooks'; -import { APP_DATA, isAndroid, isIOS, isIPadOS } from '../common/constants'; +import { APP_DATA } from '../common/constants'; export const ConferenceScreen = () => { const { userName, endpoints, onJoin: onJoinFunc } = useHMSPrebuiltContext(); @@ -43,13 +42,11 @@ export const ConferenceScreen = () => { const roomState = useHMSStore(selectRoomState); const isConnectedToRoom = useHMSStore(selectIsConnectedToRoom); const hmsActions = useHMSActions(); - const [hideControls, setHideControls] = useState(false); - const dropdownList = useHMSStore(selectAppData(APP_DATA.dropdownList)); + const authTokenInAppData = useAuthToken(); const headerRef = useRef(null); const footerRef = useRef(null); - const isMobileDevice = isAndroid || isIOS || isIPadOS; - const dropdownListRef = useRef(); + const [isHLSStarted] = useSetAppDataByKey(APP_DATA.hlsStarted); const { requestPermission } = useAwayNotifications(); @@ -60,28 +57,6 @@ export const ConferenceScreen = () => { const isLandscapeHLSStream = useLandscapeHLSStream(); const isMwebHLSStream = isMobileHLSStream || isLandscapeHLSStream; - const toggleControls = () => { - if (dropdownListRef.current?.length === 0 && isMobileDevice && !isMwebHLSStream) { - setHideControls(value => !value); - } - }; - - useEffect(() => { - let timeout: undefined | ReturnType; - dropdownListRef.current = dropdownList || []; - if (dropdownListRef.current && dropdownListRef.current.length === 0) { - clearTimeout(timeout); - timeout = setTimeout(() => { - if (dropdownListRef.current && dropdownListRef.current.length === 0) { - setHideControls(isMobileDevice); - } - }, 5000); - } - return () => { - clearTimeout(timeout); - }; - }, [dropdownList, hideControls, isMobileDevice]); - useEffect(() => { if ( authTokenInAppData && @@ -128,8 +103,6 @@ export const ConferenceScreen = () => { return ; } - const hideControlsForStreaming = isMwebHLSStream ? true : hideControls; - return ( <> {isHLSStarted ? ( @@ -144,7 +117,7 @@ export const ConferenceScreen = () => { css={{ h: '$18', transition: 'margin 0.3s ease-in-out', - marginTop: hideControlsForStreaming ? `-${headerRef.current?.clientHeight}px` : 'none', + marginTop: isMwebHLSStream ? `-${headerRef.current?.clientHeight}px` : 'none', '@md': { h: '$17', }, @@ -170,13 +143,12 @@ export const ConferenceScreen = () => { }} id="conferencing" data-testid="conferencing" - onClick={toggleControls} > {screenProps.elements ? ( ) : null} @@ -188,7 +160,7 @@ export const ConferenceScreen = () => { maxHeight: '$24', transition: 'margin 0.3s ease-in-out', bg: '$background_dim', - marginBottom: hideControlsForStreaming ? `-${footerRef.current?.clientHeight}px` : undefined, + marginBottom: isMwebHLSStream ? `-${footerRef.current?.clientHeight}px` : undefined, '@md': { maxHeight: 'unset', bg: screenProps.screenType === 'hls_live_streaming' ? 'transparent' : '$background_dim', diff --git a/packages/roomkit-react/src/Prebuilt/components/PIP/PIPChat.tsx b/packages/roomkit-react/src/Prebuilt/components/PIP/PIPChat.tsx index e849836b90..e8a75cdbb5 100644 --- a/packages/roomkit-react/src/Prebuilt/components/PIP/PIPChat.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/PIP/PIPChat.tsx @@ -72,7 +72,7 @@ export const PIPChat = () => { ]); return ( -
+