From 83b7b4831be84c0c59b8959a0a9a0ba77dd22fd0 Mon Sep 17 00:00:00 2001 From: Adrian Czerwiec <33912477+czerwiukk@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:01:00 +0100 Subject: [PATCH] FCE-747 | integrate new protobufs in webrtc and ts client (#181) ## Description Implements protobuf communication in `webrtc-client` as well as in `ts-client`. ## Important changes - External libraries like `protobufs` or `webrtc-client` are automatically bundled by `tsup`, because they're `devDependencies`. - Bundling type declarations of the external libraries is handled by the `--dts-resolve` `tsup` flag. - `build` script in root `package.json` uses `yarn workspaces` with `--topological-dev` flag to preserve the build order while using `devDependencies`. - Typescript can resolve untranspiled `*.ts` files from the `@fishjam-client/protobufs` package, because I added path aliases to `webrtc-client/tsconfig.json` and `webrtc-client/tsconfig.json`. - `e2e/ts-client` has been renamed to `e2e/webrtc-client` because it actually tests the webrtc endpoint only. ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality to not work as expected) --- .github/workflows/docs.yaml | 4 +- .github/workflows/fishjam-chat.yaml | 4 + .github/workflows/node.yaml | 5 +- .github/workflows/release.yaml | 16 +- .../workflows/{playwright.yaml => tests.yaml} | 12 +- .gitmodules | 4 +- .../react-client/docker-compose-test.yaml | 3 +- e2e-tests/react-client/scenarios/utils.ts | 67 +- e2e-tests/ts-client/docker-compose-test.yaml | 28 - .../app/.eslintignore | 0 .../app/.gitignore | 0 .../app/index.html | 0 .../app/package.json | 4 +- .../app/playwright.config.ts | 0 .../app/src/App.tsx | 56 +- .../app/src/MockComponent.tsx | 17 +- .../app/src/MuteTrackTest.tsx | 13 +- .../app/src/VideoPlayer.tsx | 0 .../app/src/VideoPlayerWithDetector.tsx | 0 .../app/src/main.tsx | 0 .../app/src/mocks.ts | 0 .../app/src/vite-env.d.ts | 0 .../app/tsconfig.json | 0 .../app/tsconfig.node.json | 0 .../app/vite.config.ts | 0 .../webrtc-client/docker-compose-test.yaml | 29 + .../scenarios/basic.spec.ts | 67 +- .../scenarios/raceCondition.spec.ts | 0 .../scenarios/replaceTrack.spec.ts | 0 .../scenarios/utils.ts | 159 +- .../setup/globalSetupState.ts | 0 .../setup/setupFishjam.ts | 16 +- .../setup/teardownFishjam.ts | 0 .../src/components/CallToolbar.tsx | 26 +- .../src/components/JoinRoomCard.tsx | 10 + .../react-client/fishjam-chat/src/main.tsx | 2 +- examples/ts-client/simple-app/src/main.ts | 13 +- package.json | 8 +- .../fishjam/media_events/peer/peer.ts | 1502 ++++++++++ .../fishjam/media_events/server/server.ts | 2570 +++++++++++++++++ .../protobufs/fishjam/media_events/shared.ts | 196 ++ .../fishjam/peer_notifications.ts | 132 +- .../protobufs/fishjam/server_notifications.ts | 2379 +++++++++++++++ packages/protobufs/index.ts | 0 packages/protobufs/package.json | 19 + packages/protobufs/protobuf.sh | 24 + packages/protobufs/protos | 1 + .../src/hooks/internal/useTrackManager.ts | 6 +- packages/react-client/src/index.ts | 2 +- packages/react-client/src/types/public.ts | 12 +- packages/react-client/src/utils/bandwidth.ts | 5 +- packages/react-client/src/utils/track.ts | 20 +- packages/ts-client/package.json | 27 +- packages/ts-client/scripts/protobuf.sh | 18 - packages/ts-client/src/FishjamClient.ts | 44 +- packages/ts-client/src/index.ts | 1 - packages/ts-client/src/protos/index.ts | 1 - packages/ts-client/src/types.ts | 4 +- packages/ts-client/src/version.ts | 3 + packages/ts-client/tsconfig.json | 8 +- packages/webrtc-client/package.json | 21 +- .../webrtc-client/src/ConnectionManager.ts | 61 +- packages/webrtc-client/src/bitrate.ts | 14 +- packages/webrtc-client/src/index.ts | 5 +- packages/webrtc-client/src/internal.ts | 15 +- packages/webrtc-client/src/mediaEvent.ts | 35 +- packages/webrtc-client/src/tracks/Local.ts | 195 +- .../webrtc-client/src/tracks/LocalTrack.ts | 65 +- .../src/tracks/LocalTrackManager.ts | 12 +- packages/webrtc-client/src/tracks/Remote.ts | 122 +- .../webrtc-client/src/tracks/RemoteTrack.ts | 23 +- .../webrtc-client/src/tracks/TrackCommon.ts | 5 +- .../webrtc-client/src/tracks/encodings.ts | 7 + .../src/tracks/muteTrackUtils.ts | 11 +- .../webrtc-client/src/tracks/transceivers.ts | 24 +- packages/webrtc-client/src/types.ts | 69 +- .../src/voiceActivityDetection.ts | 5 - packages/webrtc-client/src/webRTCEndpoint.ts | 409 ++- .../events/bandwidthEstimationEvent.test.ts | 39 +- .../tests/events/connectedEvent.test.ts | 57 +- .../events/encodingSwitchedEvent.test.ts | 43 +- .../tests/events/endpointAddedEvent.test.ts | 33 +- .../tests/events/endpointRemovedEvent.test.ts | 33 +- .../tests/events/endpointUpdatedEvent.test.ts | 36 +- .../tests/events/trackAddedEvent.test.ts | 77 +- .../tests/events/trackRemovedEvent.test.ts | 11 +- .../tests/events/trackUpdatedEvent.test.ts | 29 +- .../tests/events/vadNotificationEvent.test.ts | 26 +- packages/webrtc-client/tests/fixtures.ts | 445 ++- .../tests/methods/addTrackMethod.test.ts | 15 +- .../tests/methods/connectMethod.test.ts | 12 +- .../tests/methods/disconnectMethod.test.ts | 6 +- packages/webrtc-client/tests/schema.ts | 178 -- packages/webrtc-client/tests/utils.ts | 18 +- packages/webrtc-client/tsconfig.json | 9 +- yarn.lock | 868 +++--- 96 files changed, 8598 insertions(+), 1972 deletions(-) rename .github/workflows/{playwright.yaml => tests.yaml} (78%) delete mode 100644 e2e-tests/ts-client/docker-compose-test.yaml rename e2e-tests/{ts-client => webrtc-client}/app/.eslintignore (100%) rename e2e-tests/{ts-client => webrtc-client}/app/.gitignore (100%) rename e2e-tests/{ts-client => webrtc-client}/app/index.html (100%) rename e2e-tests/{ts-client => webrtc-client}/app/package.json (94%) rename e2e-tests/{ts-client => webrtc-client}/app/playwright.config.ts (100%) rename e2e-tests/{ts-client => webrtc-client}/app/src/App.tsx (84%) rename e2e-tests/{ts-client => webrtc-client}/app/src/MockComponent.tsx (94%) rename e2e-tests/{ts-client => webrtc-client}/app/src/MuteTrackTest.tsx (92%) rename e2e-tests/{ts-client => webrtc-client}/app/src/VideoPlayer.tsx (100%) rename e2e-tests/{ts-client => webrtc-client}/app/src/VideoPlayerWithDetector.tsx (100%) rename e2e-tests/{ts-client => webrtc-client}/app/src/main.tsx (100%) rename e2e-tests/{ts-client => webrtc-client}/app/src/mocks.ts (100%) rename e2e-tests/{ts-client => webrtc-client}/app/src/vite-env.d.ts (100%) rename e2e-tests/{ts-client => webrtc-client}/app/tsconfig.json (100%) rename e2e-tests/{ts-client => webrtc-client}/app/tsconfig.node.json (100%) rename e2e-tests/{ts-client => webrtc-client}/app/vite.config.ts (100%) create mode 100644 e2e-tests/webrtc-client/docker-compose-test.yaml rename e2e-tests/{ts-client => webrtc-client}/scenarios/basic.spec.ts (76%) rename e2e-tests/{ts-client => webrtc-client}/scenarios/raceCondition.spec.ts (100%) rename e2e-tests/{ts-client => webrtc-client}/scenarios/replaceTrack.spec.ts (100%) rename e2e-tests/{ts-client => webrtc-client}/scenarios/utils.ts (67%) rename e2e-tests/{ts-client => webrtc-client}/setup/globalSetupState.ts (100%) rename e2e-tests/{ts-client => webrtc-client}/setup/setupFishjam.ts (54%) rename e2e-tests/{ts-client => webrtc-client}/setup/teardownFishjam.ts (100%) create mode 100644 packages/protobufs/fishjam/media_events/peer/peer.ts create mode 100644 packages/protobufs/fishjam/media_events/server/server.ts create mode 100644 packages/protobufs/fishjam/media_events/shared.ts rename packages/{ts-client/src/protos => protobufs}/fishjam/peer_notifications.ts (83%) create mode 100644 packages/protobufs/fishjam/server_notifications.ts create mode 100644 packages/protobufs/index.ts create mode 100644 packages/protobufs/package.json create mode 100755 packages/protobufs/protobuf.sh create mode 160000 packages/protobufs/protos delete mode 100755 packages/ts-client/scripts/protobuf.sh delete mode 100644 packages/ts-client/src/protos/index.ts create mode 100644 packages/ts-client/src/version.ts delete mode 100644 packages/webrtc-client/src/voiceActivityDetection.ts delete mode 100644 packages/webrtc-client/tests/schema.ts diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 450c8495..8611d158 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -31,6 +31,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + - name: Install Protoc + uses: arduino/setup-protoc@v3 - name: Setup Pages uses: actions/configure-pages@v3 - name: Install node dependencies @@ -46,4 +48,4 @@ jobs: path: "docs" - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 \ No newline at end of file + uses: actions/deploy-pages@v2 diff --git a/.github/workflows/fishjam-chat.yaml b/.github/workflows/fishjam-chat.yaml index 14786f78..9a0f2ad0 100644 --- a/.github/workflows/fishjam-chat.yaml +++ b/.github/workflows/fishjam-chat.yaml @@ -22,6 +22,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Setup Node.js uses: actions/setup-node@v4 with: diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 9f2ec93d..7307fa9a 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -19,11 +19,14 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Use Node.js ${{ matrix.node-version }} ๐Ÿ›Ž๏ธ uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - cache: 'npm' + cache: "npm" - name: Install dependencies โฌ‡๏ธ run: yarn --immutable diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b60764a8..f629805e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,14 +10,16 @@ jobs: id-token: write steps: - uses: actions/checkout@v4 + - name: Install Protoc + uses: arduino/setup-protoc@v3 # Setup .npmrc file to publish to npm - uses: actions/setup-node@v4 with: - node-version: '20.x' - registry-url: 'https://registry.npmjs.org' + node-version: "20.x" + registry-url: "https://registry.npmjs.org" - run: yarn - run: yarn build - - run: yarn npm publish --access public + - run: yarn npm publish --access public working-directory: packages/ts-client env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -31,11 +33,11 @@ jobs: # Setup .npmrc file to publish to npm - uses: actions/setup-node@v4 with: - node-version: '20.x' - registry-url: 'https://registry.npmjs.org' + node-version: "20.x" + registry-url: "https://registry.npmjs.org" - run: yarn - run: yarn build - - run: yarn npm publish --access public + - run: yarn npm publish --access public working-directory: packages/react-client env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/playwright.yaml b/.github/workflows/tests.yaml similarity index 78% rename from .github/workflows/playwright.yaml rename to .github/workflows/tests.yaml index 27b28e48..8a8a3f65 100644 --- a/.github/workflows/playwright.yaml +++ b/.github/workflows/tests.yaml @@ -1,4 +1,4 @@ -name: Playwright Tests +name: Tests ๐Ÿงช on: push: @@ -15,6 +15,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - uses: actions/setup-node@v4 with: node-version: 20 @@ -26,14 +29,17 @@ jobs: - name: Build ๐Ÿ“ฆ run: yarn build + - name: Run unit tests ๐Ÿงช + run: yarn test:unit + - name: Install Playwright Browsers ๐Ÿงญ run: npx playwright install --with-deps - name: Login to GitHub Container Registry run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin - - name: Pull Fishjam for ts-client tests - run: docker compose -f e2e-tests/ts-client/docker-compose-test.yaml pull fishjam + - name: Pull Fishjam for webrtc-client tests + run: docker compose -f e2e-tests/webrtc-client/docker-compose-test.yaml pull fishjam - name: Pull Fishjam for react-client tests run: docker compose -f e2e-tests/react-client/docker-compose-test.yaml pull fishjam diff --git a/.gitmodules b/.gitmodules index b9346de2..597a4aec 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ -[submodule "packages/ts-client/protos"] - path = packages/ts-client/protos +[submodule "packages/protobufs/protos"] + path = packages/protobufs/protos url = https://github.com/fishjam-cloud/protos.git branch = master diff --git a/e2e-tests/react-client/docker-compose-test.yaml b/e2e-tests/react-client/docker-compose-test.yaml index a6d98b16..2edfeada 100644 --- a/e2e-tests/react-client/docker-compose-test.yaml +++ b/e2e-tests/react-client/docker-compose-test.yaml @@ -2,9 +2,10 @@ version: "3" services: fishjam: - image: "ghcr.io/fishjam-cloud/fishjam:0.9.0" + image: "ghcr.io/fishjam-cloud/fishjam:0.10.0-dev" container_name: fishjam restart: on-failure + platform: linux/amd64 healthcheck: test: > curl --fail -H "authorization: Bearer development" http://localhost:5002/room || exit 1 diff --git a/e2e-tests/react-client/scenarios/utils.ts b/e2e-tests/react-client/scenarios/utils.ts index 89b0d256..f7dce9c8 100644 --- a/e2e-tests/react-client/scenarios/utils.ts +++ b/e2e-tests/react-client/scenarios/utils.ts @@ -1,7 +1,10 @@ import type { Page } from "@playwright/test"; import { expect, test } from "@playwright/test"; -export const joinRoomAndAddScreenShare = async (page: Page, roomId: string): Promise => +export const joinRoomAndAddScreenShare = async ( + page: Page, + roomId: string +): Promise => test.step("Join room and add track", async () => { const peerRequest = await createPeer(page, roomId); try { @@ -12,25 +15,39 @@ export const joinRoomAndAddScreenShare = async (page: Page, roomId: string): Pro await test.step("Join room", async () => { await page.getByPlaceholder("token").fill(peerToken); - await page.getByRole("button", { name: "Connect", exact: true }).click(); + await page + .getByRole("button", { name: "Connect", exact: true }) + .click(); await expect(page.getByText("Status: connected")).toBeVisible(); }); await test.step("Add screenshare", async () => { - await page.getByRole("button", { name: "Start screen share", exact: true }).click(); + await page + .getByRole("button", { name: "Start screen share", exact: true }) + .click(); }); return peerId; } catch (e) { // todo fix - throw { status: peerRequest.status(), response: await peerRequest.json() }; + throw { + status: peerRequest.status(), + response: await peerRequest.json(), + }; } }); -export const assertThatRemoteTracksAreVisible = async (page: Page, otherClientIds: string[]) => { +export const assertThatRemoteTracksAreVisible = async ( + page: Page, + otherClientIds: string[] +) => { await test.step("Assert that remote tracks are visible", () => Promise.all( - otherClientIds.map((peerId) => expect(page.locator(`css=video[data-peer-id="${peerId}"]`)).toBeVisible()), + otherClientIds.map((peerId) => + expect( + page.locator(`css=video[data-peer-id="${peerId}"]`) + ).toBeVisible() + ) )); }; @@ -38,7 +55,11 @@ export const assertThatOtherVideoIsPlaying = async (page: Page) => { await test.step("Assert that media is working", async () => { const getDecodedFrames: () => Promise = () => page.evaluate(async () => { - const client = (window as typeof window & { client: { getStatistics: () => Promise } }).client; + const client = ( + window as typeof window & { + client: { getStatistics: () => Promise }; + } + ).client; const stats = await client.getStatistics(); for (const stat of stats?.values() ?? []) { if (stat.type === "inbound-rtp") { @@ -48,28 +69,40 @@ export const assertThatOtherVideoIsPlaying = async (page: Page) => { return 0; }); const firstMeasure = await getDecodedFrames(); - await expect(async () => expect((await getDecodedFrames()) > firstMeasure).toBe(true)).toPass(); + await expect(async () => + expect((await getDecodedFrames()) > firstMeasure).toBe(true) + ).toPass(); }); }; export const createRoom = async (page: Page, maxPeers?: number) => await test.step("Create room", async () => { const data = { + videoCodec: "vp8", ...(maxPeers ? { maxPeers } : {}), }; - const roomRequest = await page.request.post("http://localhost:5002/room", { data }); + const roomRequest = await page.request.post("http://localhost:5002/room", { + data, + }); return (await roomRequest.json()).data.room.id as string; }); -export const createPeer = async (page: Page, roomId: string, enableSimulcast: boolean = true) => +export const createPeer = async ( + page: Page, + roomId: string, + enableSimulcast: boolean = true +) => await test.step("Create room", async () => { - return await page.request.post("http://localhost:5002/room/" + roomId + "/peer", { - data: { - type: "webrtc", - options: { - enableSimulcast, + return await page.request.post( + "http://localhost:5002/room/" + roomId + "/peer", + { + data: { + type: "webrtc", + options: { + enableSimulcast, + }, }, - }, - }); + } + ); }); diff --git a/e2e-tests/ts-client/docker-compose-test.yaml b/e2e-tests/ts-client/docker-compose-test.yaml deleted file mode 100644 index 6583dc8b..00000000 --- a/e2e-tests/ts-client/docker-compose-test.yaml +++ /dev/null @@ -1,28 +0,0 @@ -version: '3' - -services: - fishjam: - image: "ghcr.io/fishjam-cloud/fishjam:0.9.0" - container_name: fishjam - restart: on-failure - healthcheck: - test: > - curl --fail -H "authorization: Bearer development" - http://localhost:5002/room || exit 1 - interval: 3s - retries: 2 - timeout: 2s - start_period: 30s - environment: - FJ_PORT: 5002 - FJ_HOST: 'localhost:5002' - FJ_WEBRTC_TURN_IP: '${EXTERNAL_IP:-127.0.0.1}' - FJ_WEBRTC_TURN_LISTEN_IP: '0.0.0.0' - FJ_WEBRTC_TURN_PORT_RANGE: '50000-50050' - FJ_WEBRTC_TURN_TCP_PORT: '49999' - FJ_SERVER_API_TOKEN: 'development' - FJ_CHECK_ORIGIN: 'false' - ports: - - '5002:5002' - - '49999:49999' - - '50000-50050:50000-50050/udp' diff --git a/e2e-tests/ts-client/app/.eslintignore b/e2e-tests/webrtc-client/app/.eslintignore similarity index 100% rename from e2e-tests/ts-client/app/.eslintignore rename to e2e-tests/webrtc-client/app/.eslintignore diff --git a/e2e-tests/ts-client/app/.gitignore b/e2e-tests/webrtc-client/app/.gitignore similarity index 100% rename from e2e-tests/ts-client/app/.gitignore rename to e2e-tests/webrtc-client/app/.gitignore diff --git a/e2e-tests/ts-client/app/index.html b/e2e-tests/webrtc-client/app/index.html similarity index 100% rename from e2e-tests/ts-client/app/index.html rename to e2e-tests/webrtc-client/app/index.html diff --git a/e2e-tests/ts-client/app/package.json b/e2e-tests/webrtc-client/app/package.json similarity index 94% rename from e2e-tests/ts-client/app/package.json rename to e2e-tests/webrtc-client/app/package.json index cbbcb0ca..6c1be841 100644 --- a/e2e-tests/ts-client/app/package.json +++ b/e2e-tests/webrtc-client/app/package.json @@ -1,7 +1,7 @@ { - "name": "@fishjam-e2e/ts-client-e2e", + "name": "@fishjam-e2e/webrtc-client-e2e", "private": true, - "version": "0.0.0", + "version": "0.10.0", "type": "module", "license": "Apache-2.0", "scripts": { diff --git a/e2e-tests/ts-client/app/playwright.config.ts b/e2e-tests/webrtc-client/app/playwright.config.ts similarity index 100% rename from e2e-tests/ts-client/app/playwright.config.ts rename to e2e-tests/webrtc-client/app/playwright.config.ts diff --git a/e2e-tests/ts-client/app/src/App.tsx b/e2e-tests/webrtc-client/app/src/App.tsx similarity index 84% rename from e2e-tests/ts-client/app/src/App.tsx rename to e2e-tests/webrtc-client/app/src/App.tsx index 292a3dd2..4884e47e 100644 --- a/e2e-tests/ts-client/app/src/App.tsx +++ b/e2e-tests/webrtc-client/app/src/App.tsx @@ -2,17 +2,20 @@ import type { Endpoint, SerializedMediaEvent, TrackContext, - Encoding, WebRTCEndpointEvents, TrackContextEvents, BandwidthLimit, SimulcastConfig, } from "@fishjam-cloud/ts-client"; import { WebRTCEndpoint } from "@fishjam-cloud/ts-client"; -import { PeerMessage } from "@fishjam-cloud/ts-client/protos"; +import { PeerMessage } from "@fishjam-cloud/protobufs/fishjamPeer"; +import { Variant } from "@fishjam-cloud/protobufs/shared"; import { useEffect, useState, useSyncExternalStore } from "react"; import { MockComponent } from "./MockComponent"; import { VideoPlayerWithDetector } from "./VideoPlayerWithDetector"; +import { MediaEvent as ServerMediaEvent } from "@fishjam-cloud/protobufs/server"; +import { MediaEvent as PeerMediaEvent } from "@fishjam-cloud/protobufs/peer"; +import packageJson from "../package.json"; /* eslint-disable no-console */ @@ -101,18 +104,23 @@ function connect(token: string, metadata: EndpointMetadata) { websocket.binaryType = "arraybuffer"; function socketOpenHandler(_event: Event) { - const message = PeerMessage.encode({ authRequest: { token } }).finish(); + const message = PeerMessage.encode({ + authRequest: { token, sdkVersion: `web-${packageJson.version}` }, + }).finish(); websocket.send(message); } websocket.addEventListener("open", socketOpenHandler); webrtc.on("sendMediaEvent", (mediaEvent: SerializedMediaEvent) => { - console.log(`%c(${clientId}) - Send: ${mediaEvent}`, "color:blue"); - const message = PeerMessage.encode({ - mediaEvent: { data: mediaEvent }, - }).finish(); - websocket.send(message); + const peerMediaEvent = PeerMediaEvent.decode(mediaEvent); + console.log( + `%c(${clientId}) - Send: ${peerMediaEvent}`, + "color:blue", + peerMediaEvent, + ); + const wrappedMediaEvent = PeerMessage.encode({ peerMediaEvent }).finish(); + websocket.send(wrappedMediaEvent); }); /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -138,8 +146,12 @@ function connect(token: string, metadata: EndpointMetadata) { webrtc.connect(metadata); } else if (data.authRequest !== undefined) { console.warn("Received unexpected control message: authRequest"); - } else if (data.mediaEvent !== undefined) { - webrtc.receiveMediaEvent(data.mediaEvent.data); + } else if (data.serverMediaEvent !== undefined) { + const serverEvent = ServerMediaEvent.encode( + data.serverMediaEvent, + ).finish(); + + webrtc.receiveMediaEvent(serverEvent); } } catch (e) { console.warn(`Received invalid control message, error: ${e}`); @@ -174,8 +186,8 @@ async function addScreenshareTrack(): Promise { const trackMetadata: TrackMetadata = { goodTrack: "screenshare" }; const simulcastConfig: SimulcastConfig = { enabled: false, - activeEncodings: [], - disabledEncodings: [], + enabledVariants: [], + disabledVariants: [], }; const maxBandwidth: BandwidthLimit = 0; @@ -211,7 +223,7 @@ export function App() { () => remoteTracksStore.snapshot(), ); - const setEncoding = (trackId: string, encoding: Encoding) => { + const setEncoding = (trackId: string, encoding: Variant) => { webrtc.setTargetTrackEncoding(trackId, encoding); }; @@ -268,9 +280,21 @@ export function App() {
{stream?.id}
- - - + + +
), diff --git a/e2e-tests/ts-client/app/src/MockComponent.tsx b/e2e-tests/webrtc-client/app/src/MockComponent.tsx similarity index 94% rename from e2e-tests/ts-client/app/src/MockComponent.tsx rename to e2e-tests/webrtc-client/app/src/MockComponent.tsx index e1db7938..ff9da5da 100644 --- a/e2e-tests/ts-client/app/src/MockComponent.tsx +++ b/e2e-tests/webrtc-client/app/src/MockComponent.tsx @@ -1,10 +1,11 @@ import { createStream } from "./mocks"; import { VideoPlayer } from "./VideoPlayer"; import { useRef, useState } from "react"; -import type { - BandwidthLimit, - SimulcastConfig, - WebRTCEndpoint, +import { + Variant, + type BandwidthLimit, + type SimulcastConfig, + type WebRTCEndpoint, } from "@fishjam-cloud/ts-client"; import { MuteTrackTest } from "./MuteTrackTest"; @@ -78,8 +79,12 @@ export const MockComponent = ({ webrtc }: Props) => { const simulcastConfig: SimulcastConfig = { enabled: true, - activeEncodings: ["h", "m", "l"], - disabledEncodings: [], + enabledVariants: [ + Variant.VARIANT_LOW, + Variant.VARIANT_MEDIUM, + Variant.VARIANT_HIGH, + ], + disabledVariants: [], }; const maxBandwidth: BandwidthLimit = 0; diff --git a/e2e-tests/ts-client/app/src/MuteTrackTest.tsx b/e2e-tests/webrtc-client/app/src/MuteTrackTest.tsx similarity index 92% rename from e2e-tests/ts-client/app/src/MuteTrackTest.tsx rename to e2e-tests/webrtc-client/app/src/MuteTrackTest.tsx index 333340e6..3785fca5 100644 --- a/e2e-tests/ts-client/app/src/MuteTrackTest.tsx +++ b/e2e-tests/webrtc-client/app/src/MuteTrackTest.tsx @@ -2,7 +2,10 @@ import type { WebRTCEndpoint } from "@fishjam-cloud/ts-client"; import { brain2Mock, heart2Mock } from "./MockComponent"; import { useEffect, useState } from "react"; import { VideoPlayer } from "./VideoPlayer"; -import type { WebRTCEndpointEvents } from "@fishjam-cloud/webrtc-client"; +import { + Variant, + type WebRTCEndpointEvents, +} from "@fishjam-cloud/webrtc-client"; type Props = { webrtc: WebRTCEndpoint; @@ -47,8 +50,12 @@ export const MuteTrackTest = ({ webrtc }: Props) => { { goodTrack: "camera" }, { enabled: true, - activeEncodings: ["l", "m", "h"], - disabledEncodings: [], + enabledVariants: [ + Variant.VARIANT_LOW, + Variant.VARIANT_MEDIUM, + Variant.VARIANT_HIGH, + ], + disabledVariants: [], }, ); }; diff --git a/e2e-tests/ts-client/app/src/VideoPlayer.tsx b/e2e-tests/webrtc-client/app/src/VideoPlayer.tsx similarity index 100% rename from e2e-tests/ts-client/app/src/VideoPlayer.tsx rename to e2e-tests/webrtc-client/app/src/VideoPlayer.tsx diff --git a/e2e-tests/ts-client/app/src/VideoPlayerWithDetector.tsx b/e2e-tests/webrtc-client/app/src/VideoPlayerWithDetector.tsx similarity index 100% rename from e2e-tests/ts-client/app/src/VideoPlayerWithDetector.tsx rename to e2e-tests/webrtc-client/app/src/VideoPlayerWithDetector.tsx diff --git a/e2e-tests/ts-client/app/src/main.tsx b/e2e-tests/webrtc-client/app/src/main.tsx similarity index 100% rename from e2e-tests/ts-client/app/src/main.tsx rename to e2e-tests/webrtc-client/app/src/main.tsx diff --git a/e2e-tests/ts-client/app/src/mocks.ts b/e2e-tests/webrtc-client/app/src/mocks.ts similarity index 100% rename from e2e-tests/ts-client/app/src/mocks.ts rename to e2e-tests/webrtc-client/app/src/mocks.ts diff --git a/e2e-tests/ts-client/app/src/vite-env.d.ts b/e2e-tests/webrtc-client/app/src/vite-env.d.ts similarity index 100% rename from e2e-tests/ts-client/app/src/vite-env.d.ts rename to e2e-tests/webrtc-client/app/src/vite-env.d.ts diff --git a/e2e-tests/ts-client/app/tsconfig.json b/e2e-tests/webrtc-client/app/tsconfig.json similarity index 100% rename from e2e-tests/ts-client/app/tsconfig.json rename to e2e-tests/webrtc-client/app/tsconfig.json diff --git a/e2e-tests/ts-client/app/tsconfig.node.json b/e2e-tests/webrtc-client/app/tsconfig.node.json similarity index 100% rename from e2e-tests/ts-client/app/tsconfig.node.json rename to e2e-tests/webrtc-client/app/tsconfig.node.json diff --git a/e2e-tests/ts-client/app/vite.config.ts b/e2e-tests/webrtc-client/app/vite.config.ts similarity index 100% rename from e2e-tests/ts-client/app/vite.config.ts rename to e2e-tests/webrtc-client/app/vite.config.ts diff --git a/e2e-tests/webrtc-client/docker-compose-test.yaml b/e2e-tests/webrtc-client/docker-compose-test.yaml new file mode 100644 index 00000000..9ea96fb0 --- /dev/null +++ b/e2e-tests/webrtc-client/docker-compose-test.yaml @@ -0,0 +1,29 @@ +version: "3" + +services: + fishjam: + image: "ghcr.io/fishjam-cloud/fishjam:0.10.0-dev" + container_name: fishjam + restart: on-failure + platform: linux/amd64 + healthcheck: + test: > + curl --fail -H "authorization: Bearer development" + http://localhost:5002/room || exit 1 + interval: 3s + retries: 2 + timeout: 2s + start_period: 30s + environment: + FJ_PORT: 5002 + FJ_HOST: "localhost:5002" + FJ_WEBRTC_TURN_IP: "${EXTERNAL_IP:-127.0.0.1}" + FJ_WEBRTC_TURN_LISTEN_IP: "0.0.0.0" + FJ_WEBRTC_TURN_PORT_RANGE: "50000-50050" + FJ_WEBRTC_TURN_TCP_PORT: "49999" + FJ_SERVER_API_TOKEN: "development" + FJ_CHECK_ORIGIN: "false" + ports: + - "5002:5002" + - "49999:49999" + - "50000-50050:50000-50050/udp" diff --git a/e2e-tests/ts-client/scenarios/basic.spec.ts b/e2e-tests/webrtc-client/scenarios/basic.spec.ts similarity index 76% rename from e2e-tests/ts-client/scenarios/basic.spec.ts rename to e2e-tests/webrtc-client/scenarios/basic.spec.ts index f371ade0..a412ad40 100644 --- a/e2e-tests/ts-client/scenarios/basic.spec.ts +++ b/e2e-tests/webrtc-client/scenarios/basic.spec.ts @@ -1,28 +1,29 @@ -import { test, expect } from '@playwright/test'; +import { test, expect } from "@playwright/test"; + import { assertThatRemoteTracksAreVisible, assertThatOtherVideoIsPlaying, createRoom, joinRoomAndAddScreenShare, throwIfRemoteTracksAreNotPresent, -} from './utils'; +} from "./utils"; -test('Displays basic UI', async ({ page }) => { - await page.goto('/'); +test("Displays basic UI", async ({ page }) => { + await page.goto("/"); - await expect(page.getByPlaceholder('token')).toBeVisible(); + await expect(page.getByPlaceholder("token")).toBeVisible(); await expect( - page.getByRole('button', { name: 'Connect', exact: true }), + page.getByRole("button", { name: "Connect", exact: true }) ).toBeVisible(); await expect( - page.getByRole('button', { name: 'Start screenshare', exact: true }), + page.getByRole("button", { name: "Start screenshare", exact: true }) ).toBeVisible(); }); -test('Connect 2 peers to 1 room', async ({ page: firstPage, context }) => { +test("Connect 2 peers to 1 room", async ({ page: firstPage, context }) => { const secondPage = await context.newPage(); - await firstPage.goto('/'); - await secondPage.goto('/'); + await firstPage.goto("/"); + await secondPage.goto("/"); const roomId = await createRoom(firstPage); @@ -43,8 +44,8 @@ test("Peer doesn't disconnect when trying to set incorrect track encoding", asyn context, }) => { const secondPage = await context.newPage(); - await firstPage.goto('/'); - await secondPage.goto('/'); + await firstPage.goto("/"); + await secondPage.goto("/"); const roomId = await createRoom(firstPage); @@ -53,11 +54,11 @@ test("Peer doesn't disconnect when trying to set incorrect track encoding", asyn await assertThatRemoteTracksAreVisible(firstPage, [secondClientId]); await assertThatOtherVideoIsPlaying(firstPage); - await firstPage.getByRole('button', { name: 'l', exact: true }).click(); + await firstPage.getByRole("button", { name: "Low", exact: true }).click(); await assertThatOtherVideoIsPlaying(firstPage); }); -test('Client properly sees 3 other peers', async ({ page, context }) => { +test("Client properly sees 3 other peers", async ({ page, context }) => { const pages = [ page, ...(await Promise.all([...Array(3)].map(() => context.newPage()))), @@ -67,23 +68,23 @@ test('Client properly sees 3 other peers', async ({ page, context }) => { const peerIds = await Promise.all( pages.map(async (page) => { - await page.goto('/'); + await page.goto("/"); return await joinRoomAndAddScreenShare(page, roomId); - }), + }) ); await Promise.all( pages.map(async (page, idx) => { await assertThatRemoteTracksAreVisible( page, - peerIds.filter((id) => id !== peerIds[idx]), + peerIds.filter((id) => id !== peerIds[idx]) ); await assertThatOtherVideoIsPlaying(page); - }), + }) ); }); -test('Peer see peers just in the same room', async ({ page, context }) => { +test("Peer see peers just in the same room", async ({ page, context }) => { const [p1r1, p2r1, p1r2, p2r2] = [ page, ...(await Promise.all([...Array(3)].map(() => context.newPage()))), @@ -98,43 +99,43 @@ test('Peer see peers just in the same room', async ({ page, context }) => { const firstRoomPeerIds = await Promise.all( firstRoomPages.map(async (page) => { - await page.goto('/'); + await page.goto("/"); return await joinRoomAndAddScreenShare(page, firstRoomId); - }), + }) ); const secondRoomPeerIds = await Promise.all( secondRoomPages.map(async (page) => { - await page.goto('/'); + await page.goto("/"); return await joinRoomAndAddScreenShare(page, secondRoomId); - }), + }) ); await Promise.all([ ...firstRoomPages.map(async (page, idx) => { await assertThatRemoteTracksAreVisible( page, - firstRoomPeerIds.filter((id) => id !== firstRoomPeerIds[idx]), + firstRoomPeerIds.filter((id) => id !== firstRoomPeerIds[idx]) ); await expect( - throwIfRemoteTracksAreNotPresent(page, secondRoomPeerIds), + throwIfRemoteTracksAreNotPresent(page, secondRoomPeerIds) ).rejects.toThrow(); await assertThatOtherVideoIsPlaying(page); }), ...secondRoomPages.map(async (page, idx) => { await assertThatRemoteTracksAreVisible( page, - secondRoomPeerIds.filter((id) => id !== secondRoomPeerIds[idx]), + secondRoomPeerIds.filter((id) => id !== secondRoomPeerIds[idx]) ); await expect( - throwIfRemoteTracksAreNotPresent(page, firstRoomPeerIds), + throwIfRemoteTracksAreNotPresent(page, firstRoomPeerIds) ).rejects.toThrow(); await assertThatOtherVideoIsPlaying(page); }), ]); }); -test('Client throws an error if joining room at max capacity', async ({ +test("Client throws an error if joining room at max capacity", async ({ page, context, }) => { @@ -147,20 +148,20 @@ test('Client throws an error if joining room at max capacity', async ({ await Promise.all( [page1, page2].map(async (page) => { - await page.goto('/'); + await page.goto("/"); return await joinRoomAndAddScreenShare(page, roomId); - }), + }) ); - await overflowingPage.goto('/'); + await overflowingPage.goto("/"); await expect( - joinRoomAndAddScreenShare(overflowingPage, roomId), + joinRoomAndAddScreenShare(overflowingPage, roomId) ).rejects.toEqual( expect.objectContaining({ status: 503, response: { errors: `Reached webrtc peers limit in room ${roomId}`, }, - }), + }) ); }); diff --git a/e2e-tests/ts-client/scenarios/raceCondition.spec.ts b/e2e-tests/webrtc-client/scenarios/raceCondition.spec.ts similarity index 100% rename from e2e-tests/ts-client/scenarios/raceCondition.spec.ts rename to e2e-tests/webrtc-client/scenarios/raceCondition.spec.ts diff --git a/e2e-tests/ts-client/scenarios/replaceTrack.spec.ts b/e2e-tests/webrtc-client/scenarios/replaceTrack.spec.ts similarity index 100% rename from e2e-tests/ts-client/scenarios/replaceTrack.spec.ts rename to e2e-tests/webrtc-client/scenarios/replaceTrack.spec.ts diff --git a/e2e-tests/ts-client/scenarios/utils.ts b/e2e-tests/webrtc-client/scenarios/utils.ts similarity index 67% rename from e2e-tests/ts-client/scenarios/utils.ts rename to e2e-tests/webrtc-client/scenarios/utils.ts index 66c76dfa..1e373587 100644 --- a/e2e-tests/ts-client/scenarios/utils.ts +++ b/e2e-tests/webrtc-client/scenarios/utils.ts @@ -1,12 +1,12 @@ -import type { Page, TestInfo } from '@playwright/test'; -import { expect, test } from '@playwright/test'; -import { v4 as uuidv4 } from 'uuid'; +import type { Page, TestInfo } from "@playwright/test"; +import { expect, test } from "@playwright/test"; +import { v4 as uuidv4 } from "uuid"; export const TO_PASS_TIMEOUT_MILLIS = 10 * 1000; // 10 seconds export const addScreenShare = async (page: Page) => - await test.step('Add screenshare', async () => { + await test.step("Add screenshare", async () => { await page - .getByRole('button', { name: 'Start screenshare', exact: true }) + .getByRole("button", { name: "Start screenshare", exact: true }) .click(); }); @@ -16,9 +16,9 @@ const expectWithLongerTimeout = expect.configure({ export const createAndJoinPeer = async ( page: Page, - roomId: string, + roomId: string ): Promise => - test.step('Create and join peer', async () => { + test.step("Create and join peer", async () => { const peerRequest = await createPeer(page, roomId); try { const { @@ -26,12 +26,12 @@ export const createAndJoinPeer = async ( token: peerToken, } = (await peerRequest.json()).data; - await test.step('Join room', async () => { - await page.getByPlaceholder('token').fill(peerToken); + await test.step("Join room", async () => { + await page.getByPlaceholder("token").fill(peerToken); await page - .getByRole('button', { name: 'Connect', exact: true }) + .getByRole("button", { name: "Connect", exact: true }) .click(); - await expect(page.locator('#connection-status')).toContainText('true'); + await expect(page.locator("#connection-status")).toContainText("true"); }); return peerId; @@ -47,9 +47,9 @@ export const joinRoom = async ( page: Page, roomId: string, metadata?: any, - waitForConnection: boolean = true, + waitForConnection: boolean = true ): Promise => - test.step('Join room', async () => { + test.step("Join room", async () => { const peerRequest = await createPeer(page, roomId); try { const { @@ -57,17 +57,17 @@ export const joinRoom = async ( token: peerToken, } = (await peerRequest.json()).data; - await page.getByPlaceholder('token').fill(peerToken); + await page.getByPlaceholder("token").fill(peerToken); if (metadata !== undefined) { await page - .getByPlaceholder('endpoint metadata') + .getByPlaceholder("endpoint metadata") .fill(JSON.stringify(metadata)); } else { - await page.getByPlaceholder('endpoint metadata').clear(); + await page.getByPlaceholder("endpoint metadata").clear(); } - await page.getByRole('button', { name: 'Connect', exact: true }).click(); + await page.getByRole("button", { name: "Connect", exact: true }).click(); if (waitForConnection) { - await expect(page.locator('#connection-status')).toContainText('true'); + await expect(page.locator("#connection-status")).toContainText("true"); } return peerId; @@ -81,9 +81,9 @@ export const joinRoom = async ( export const joinRoomAndAddScreenShare = async ( page: Page, - roomId: string, + roomId: string ): Promise => - test.step('Join room and add track', async () => { + test.step("Join room and add track", async () => { const peerRequest = await createPeer(page, roomId); try { const { @@ -91,12 +91,12 @@ export const joinRoomAndAddScreenShare = async ( token: peerToken, } = (await peerRequest.json()).data; - await test.step('Join room', async () => { - await page.getByPlaceholder('token').fill(peerToken); + await test.step("Join room", async () => { + await page.getByPlaceholder("token").fill(peerToken); await page - .getByRole('button', { name: 'Connect', exact: true }) + .getByRole("button", { name: "Connect", exact: true }) .click(); - await expect(page.locator('#connection-status')).toContainText('true'); + await expect(page.locator("#connection-status")).toContainText("true"); }); await addScreenShare(page); @@ -112,12 +112,12 @@ export const joinRoomAndAddScreenShare = async ( export const throwIfRemoteTracksAreNotPresent = async ( page: Page, - otherClientIds: string[], + otherClientIds: string[] ) => { - await test.step('Assert that remote tracks are visible', async () => { + await test.step("Assert that remote tracks are visible", async () => { for (const peerId of otherClientIds) { await expect( - page.locator(`css=video[data-peer-id="${peerId}"]`), + page.locator(`css=video[data-peer-id="${peerId}"]`) ).toBeVisible(); } }); @@ -125,29 +125,29 @@ export const throwIfRemoteTracksAreNotPresent = async ( export const assertThatRemoteTracksAreVisible = async ( page: Page, - otherClientIds: string[], + otherClientIds: string[] ) => { - await test.step('Assert that remote tracks are visible', async () => { + await test.step("Assert that remote tracks are visible", async () => { const responses = await Promise.allSettled( otherClientIds.map((peerId) => - page.locator(`css=video[data-peer-id="${peerId}"]`), - ), + page.locator(`css=video[data-peer-id="${peerId}"]`) + ) ); - const isAnyRejected = responses.some((e) => e.status === 'rejected'); + const isAnyRejected = responses.some((e) => e.status === "rejected"); expect(isAnyRejected).toBe(false); }); }; type WebrtcClient = { webrtc?: { - getStatistics: () => Promise, - connectionManager: { getConnection: () => RTCPeerConnection | undefined } - } + getStatistics: () => Promise; + connectionManager: { getConnection: () => RTCPeerConnection | undefined }; + }; }; type WindowType = typeof window; export const assertThatOtherVideoIsPlaying = async (page: Page) => { - await test.step('Assert that media is working', async () => { + await test.step("Assert that media is working", async () => { const getDecodedFrames: () => Promise = () => page.evaluate(async () => { const webrtc = (window as WindowType & WebrtcClient)?.webrtc; @@ -155,7 +155,7 @@ export const assertThatOtherVideoIsPlaying = async (page: Page) => { if (!window || !webrtc) return -1; const stats = await webrtc.getStatistics(); for (const stat of stats.values()) { - if (stat.type === 'inbound-rtp') { + if (stat.type === "inbound-rtp") { return stat.framesDecoded; } } @@ -163,7 +163,7 @@ export const assertThatOtherVideoIsPlaying = async (page: Page) => { }); const firstMeasure = await getDecodedFrames(); await expectWithLongerTimeout(async () => - expect((await getDecodedFrames()) > firstMeasure).toBe(true), + expect((await getDecodedFrames()) > firstMeasure).toBe(true) ).toPass(); }); }; @@ -171,26 +171,27 @@ export const assertThatOtherVideoIsPlaying = async (page: Page) => { export const takeScreenshot = async ( page: Page, testInfo: TestInfo, - name?: string, + name?: string ) => - await test.step('Take screenshot', async () => { + await test.step("Take screenshot", async () => { const screenShotId = uuidv4(); const screenshot = await page.screenshot({ path: `test-results/screenshots/${screenShotId}.png`, }); - await testInfo.attach(name ?? 'screenshot', { + await testInfo.attach(name ?? "screenshot", { body: screenshot, - contentType: 'image/png', + contentType: "image/png", }); }); export const createRoom = async (page: Page, maxPeers?: number) => - await test.step('Create room', async () => { + await test.step("Create room", async () => { const data = { + videoCodec: "vp8", ...(maxPeers ? { maxPeers } : {}), }; - const roomRequest = await page.request.post('http://localhost:5002/room', { + const roomRequest = await page.request.post("http://localhost:5002/room", { data, }); return (await roomRequest.json()).data.room.id as string; @@ -199,55 +200,55 @@ export const createRoom = async (page: Page, maxPeers?: number) => export const createPeer = async ( page: Page, roomId: string, - enableSimulcast: boolean = true, + enableSimulcast: boolean = true ) => - await test.step('Create room', async () => { + await test.step("Create room", async () => { return await page.request.post( - 'http://localhost:5002/room/' + roomId + '/peer', + "http://localhost:5002/room/" + roomId + "/peer", { data: { - type: 'webrtc', + type: "webrtc", options: { enableSimulcast, }, }, - }, + } ); }); export const clickButton = async (page: Page, name: string) => await test.step(`Click '${name}' button`, async () => { - await page.getByRole('button', { name: name, exact: true }).click(); + await page.getByRole("button", { name: name, exact: true }).click(); }); export const removeTrack = async (page: Page, button: string) => await test.step(`Remove track ${button}`, async () => { - await page.getByRole('button', { name: button, exact: true }).click(); + await page.getByRole("button", { name: button, exact: true }).click(); }); export const addAndReplaceTrack = async (page: Page) => - await test.step('Add and replace track', async () => + await test.step("Add and replace track", async () => await page - .getByRole('button', { - name: 'Add and replace a heart', + .getByRole("button", { + name: "Add and replace a heart", exact: true, }) .click()); export const addAndRemoveTrack = async (page: Page) => - await test.step('Add and remove track', async () => + await test.step("Add and remove track", async () => await page - .getByRole('button', { - name: 'Add and remove a heart', + .getByRole("button", { + name: "Add and remove a heart", exact: true, }) .click()); export const addBothMockTracks = async (page: Page) => - await test.step('Add both tracks', async () => + await test.step("Add both tracks", async () => await page - .getByRole('button', { - name: 'Add both', + .getByRole("button", { + name: "Add both", exact: true, }) .click()); @@ -255,49 +256,49 @@ export const addBothMockTracks = async (page: Page) => export const assertThatAllTracksAreReady = async ( page: Page, otherClientId: string, - tracks: number, + tracks: number ) => await test.step(`Assert that all (${tracks}) tracks are ready`, async () => expectWithLongerTimeout( - page.locator(`div[data-endpoint-id="${otherClientId}"]`), + page.locator(`div[data-endpoint-id="${otherClientId}"]`) ).toHaveCount(tracks)); export const assertThatTrackBackgroundColorIsOk = async ( page: Page, otherClientId: string, - color: string, + color: string ) => await test.step(`Assert that track background color is ${color}`, () => { page.locator( - `xpath=//div[@data-endpoint-id="${otherClientId}"]//div[@data-color-name="${color}"]`, + `xpath=//div[@data-endpoint-id="${otherClientId}"]//div[@data-color-name="${color}"]` ); return expectWithLongerTimeout( page.locator( - `xpath=//div[@data-endpoint-id="${otherClientId}"]//div[@data-color-name="${color}"]`, - ), + `xpath=//div[@data-endpoint-id="${otherClientId}"]//div[@data-color-name="${color}"]` + ) ).toBeVisible(); }); -const DECODED_FRAMES = 'data-decoded-frames'; +const DECODED_FRAMES = "data-decoded-frames"; const getDecodedFrameDifference = async (page: Page, otherClientId: string) => { const locator = page.locator( - `xpath=//div[@data-endpoint-id="${otherClientId}"]//span[@${DECODED_FRAMES}]`, + `xpath=//div[@data-endpoint-id="${otherClientId}"]//span[@${DECODED_FRAMES}]` ); const decodedFrames = - (await locator.getAttribute(DECODED_FRAMES)) ?? 'unknown'; + (await locator.getAttribute(DECODED_FRAMES)) ?? "unknown"; await page.waitForTimeout(300); const decodedFrames2 = - (await locator.getAttribute(DECODED_FRAMES)) ?? 'unknown'; + (await locator.getAttribute(DECODED_FRAMES)) ?? "unknown"; return Number.parseInt(decodedFrames2) - Number.parseInt(decodedFrames); }; export const assertThatTrackIsPlaying = async ( page: Page, - otherClientId: string, + otherClientId: string ) => await test.step(`Assert that track is playing`, () => expectWithLongerTimeout @@ -306,7 +307,7 @@ export const assertThatTrackIsPlaying = async ( export const assertThatTrackStopped = async ( page: Page, - otherClientId: string, + otherClientId: string ) => await test.step(`Assert that track stopped`, () => expectWithLongerTimeout @@ -315,30 +316,30 @@ export const assertThatTrackStopped = async ( export const assertThatTrackReplaceStatusIsSuccess = async ( page: Page, - replaceStatus: string, + replaceStatus: string ) => await test.step(`Assert that track background color is ${replaceStatus}`, async () => await expectWithLongerTimeout( - page.locator(`xpath=//span[@data-replace-status="${replaceStatus}"]`), + page.locator(`xpath=//span[@data-replace-status="${replaceStatus}"]`) ).toBeVisible()); const NOT_EMPTY_TEXT = /\S/; export const assertThatTrackIdIsNotEmpty = async ( page: Page, - locator: string, + locator: string ) => - await test.step('Assert that track id is not empty', async () => + await test.step("Assert that track id is not empty", async () => await expectWithLongerTimeout(page.locator(locator)).toContainText( - NOT_EMPTY_TEXT, + NOT_EMPTY_TEXT )); export const assertThatBothTrackAreDifferent = async ( page: Page, testInfo: TestInfo, - name?: string, + name?: string ) => { - await test.step('Assert that both tracks are different', async () => { + await test.step("Assert that both tracks are different", async () => { const locator1 = `(//div[@data-name="stream-id"])[1]`; const locator2 = `(//div[@data-name="stream-id"])[2]`; diff --git a/e2e-tests/ts-client/setup/globalSetupState.ts b/e2e-tests/webrtc-client/setup/globalSetupState.ts similarity index 100% rename from e2e-tests/ts-client/setup/globalSetupState.ts rename to e2e-tests/webrtc-client/setup/globalSetupState.ts diff --git a/e2e-tests/ts-client/setup/setupFishjam.ts b/e2e-tests/webrtc-client/setup/setupFishjam.ts similarity index 54% rename from e2e-tests/ts-client/setup/setupFishjam.ts rename to e2e-tests/webrtc-client/setup/setupFishjam.ts index 3ac876cc..101da9d1 100644 --- a/e2e-tests/ts-client/setup/setupFishjam.ts +++ b/e2e-tests/webrtc-client/setup/setupFishjam.ts @@ -1,23 +1,23 @@ -import { DockerComposeEnvironment, Wait } from 'testcontainers'; -import { setupState } from './globalSetupState'; -import { type NetworkInterfaceInfo, networkInterfaces } from 'os'; +import { DockerComposeEnvironment, Wait } from "testcontainers"; +import { setupState } from "./globalSetupState"; +import { type NetworkInterfaceInfo, networkInterfaces } from "os"; export default async function setupFishjam() { const EXTERNAL_IP = Object.values(networkInterfaces()) .flat() .filter((x): x is NetworkInterfaceInfo => x !== undefined) - .filter(({ family }) => family === 'IPv4') + .filter(({ family }) => family === "IPv4") .filter(({ internal }) => !internal) .map(({ address }) => address)[0]; setupState.fishjamContainer = await new DockerComposeEnvironment( - '../.', - 'docker-compose-test.yaml', + "../.", + "docker-compose-test.yaml" ) .withEnvironment({ EXTERNAL_IP }) .withWaitStrategy( - 'fishjam', - Wait.forLogMessage('Access FishjamWeb.Endpoint at'), + "fishjam", + Wait.forLogMessage("Access FishjamWeb.Endpoint at") ) .up(); } diff --git a/e2e-tests/ts-client/setup/teardownFishjam.ts b/e2e-tests/webrtc-client/setup/teardownFishjam.ts similarity index 100% rename from e2e-tests/ts-client/setup/teardownFishjam.ts rename to e2e-tests/webrtc-client/setup/teardownFishjam.ts diff --git a/examples/react-client/fishjam-chat/src/components/CallToolbar.tsx b/examples/react-client/fishjam-chat/src/components/CallToolbar.tsx index d9ef9df4..ba313a2c 100644 --- a/examples/react-client/fishjam-chat/src/components/CallToolbar.tsx +++ b/examples/react-client/fishjam-chat/src/components/CallToolbar.tsx @@ -20,20 +20,22 @@ import { SettingsSheet } from "./SettingsSheet"; export const CallToolbar = () => { const { leaveRoom } = useConnection(); - const onHangUp = async () => { - leaveRoom(); - }; - const { startStreaming, stream: screenStream, stopStreaming, } = useScreenShare(); - const { toggleDevice: toggleCamera, stream: cameraStream } = useCamera(); - const { toggleDevice: toggleMic, stream: micStream } = useMicrophone(); + const camera = useCamera(); + const mic = useMicrophone(); + + const onHangUp = async () => { + camera.stopStreaming(); + mic.stopStreaming(); + leaveRoom(); + }; - const MicIcon = micStream ? Mic : MicOff; - const CameraIcon = cameraStream ? Video : VideoOff; + const MicIcon = mic.stream ? Mic : MicOff; + const CameraIcon = camera.stream ? Video : VideoOff; const ScreenshareIcon = screenStream ? MonitorOff : MonitorUp; const toggleScreenShare = async () => { @@ -58,16 +60,16 @@ export const CallToolbar = () => { diff --git a/examples/react-client/fishjam-chat/src/components/JoinRoomCard.tsx b/examples/react-client/fishjam-chat/src/components/JoinRoomCard.tsx index a81f12e3..c5c3dde2 100644 --- a/examples/react-client/fishjam-chat/src/components/JoinRoomCard.tsx +++ b/examples/react-client/fishjam-chat/src/components/JoinRoomCard.tsx @@ -1,5 +1,7 @@ import { useInitializeDevices, + useCamera, + useMicrophone, useConnection, } from "@fishjam-cloud/react-client"; @@ -37,6 +39,9 @@ type Props = React.HTMLAttributes; export const JoinRoomCard: FC = (props) => { const { initializeDevices } = useInitializeDevices(); + const camera = useCamera(); + const mic = useMicrophone(); + const { joinRoom } = useConnection(); const persistedValues = getPersistedFormValues(); @@ -70,6 +75,11 @@ export const JoinRoomCard: FC = (props) => { peerToken, peerMetadata: { displayName: peerName }, }); + + await Promise.all([ + camera.startStreaming({ simulcast: false }), + mic.startStreaming({ simulcast: false }), + ]); }; return ( diff --git a/examples/react-client/fishjam-chat/src/main.tsx b/examples/react-client/fishjam-chat/src/main.tsx index b59a7c67..cc335ebd 100644 --- a/examples/react-client/fishjam-chat/src/main.tsx +++ b/examples/react-client/fishjam-chat/src/main.tsx @@ -7,7 +7,7 @@ import { BlurProvider } from "./components/BlurToggle.tsx"; ReactDOM.createRoot(document.getElementById("root")!).render( - + diff --git a/examples/ts-client/simple-app/src/main.ts b/examples/ts-client/simple-app/src/main.ts index 11ec46a7..7c3e7677 100644 --- a/examples/ts-client/simple-app/src/main.ts +++ b/examples/ts-client/simple-app/src/main.ts @@ -1,7 +1,8 @@ import "./style.css"; import { createStream } from "./createMockStream"; -import type { Encoding, Peer, TrackMetadata } from "@fishjam-cloud/ts-client"; +import type { Peer, TrackMetadata } from "@fishjam-cloud/ts-client"; +import { Variant } from "@fishjam-cloud/ts-client"; import { FishjamClient } from "@fishjam-cloud/ts-client"; const SCREEN_SHARING_MEDIA_CONSTRAINTS = { @@ -41,7 +42,11 @@ const screenSharingContainer = document.querySelector( "#screen-sharing-container", )!; const templateVideoPlayer = document.querySelector("#video-player-template")!; -const ENCODINGS: Encoding[] = ["l", "m", "h"]; +const ENCODINGS: Variant[] = [ + Variant.VARIANT_LOW, + Variant.VARIANT_MEDIUM, + Variant.VARIANT_HIGH, +]; const elementsToShowIfConnected = document.querySelectorAll(".show-if-connected"); @@ -180,7 +185,7 @@ client.on("peerLeft", (peer) => { const setupSimulcastCheckbox = ( element: DocumentFragment, trackId: string, - encoding: "l" | "m" | "h", + encoding: Variant, ) => { const simulcastInputL: HTMLInputElement | null = element.querySelector( @@ -294,7 +299,7 @@ client.on("trackAdded", (ctx) => { const activeEncodingElement = document.querySelector( `div[data-track-id="${ctx.trackId}"] .simulcast-active-encoding`, )!; - activeEncodingElement.innerHTML = ctx.encoding ?? ""; + activeEncodingElement.innerHTML = `${ctx.encoding}`; }); ctx.on("voiceActivityChanged", () => {}); }); diff --git a/package.json b/package.json index 12a5777f..22df386b 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,20 @@ "license": "MIT", "private": true, "workspaces": [ + "packages/protobufs", "packages/ts-client", "packages/react-client", "packages/webrtc-client", "examples/ts-client/*", "examples/react-client/*", - "e2e-tests/ts-client/app", + "e2e-tests/webrtc-client/app", "e2e-tests/react-client/app" ], "packageManager": "yarn@4.4.0", "scripts": { - "build": "yarn workspaces foreach -Apt run build", + "build": "yarn workspaces foreach -Ap --topological-dev run build", "test:unit": "yarn workspace @fishjam-cloud/webrtc-client test", - "test:e2e": "yarn workspace @fishjam-e2e/ts-client-e2e e2e && yarn workspace @fishjam-e2e/react-client-e2e e2e", + "test:e2e": "yarn workspace @fishjam-e2e/webrtc-client-e2e e2e && yarn workspace @fishjam-e2e/react-client-e2e e2e", "tsc": "yarn workspaces foreach -Ap run tsc || echo 'โŒ Type errors! โŒ' ", "format": "yarn workspaces foreach -A -p run format || echo 'โŒ Formatting issues! โŒ'", "format:check": "yarn workspaces foreach -A -p run format:check", @@ -25,6 +26,7 @@ "docs": "typedoc" }, "devDependencies": { + "@fishjam-cloud/protobufs": "workspace:^", "@typescript-eslint/eslint-plugin": "^8.16.0", "@typescript-eslint/parser": "^8.16.0", "eslint": "^8.57.1", diff --git a/packages/protobufs/fishjam/media_events/peer/peer.ts b/packages/protobufs/fishjam/media_events/peer/peer.ts new file mode 100644 index 00000000..e1df3a4f --- /dev/null +++ b/packages/protobufs/fishjam/media_events/peer/peer.ts @@ -0,0 +1,1502 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.4.0 +// protoc v5.28.2 +// source: fishjam/media_events/peer/peer.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; +import { Candidate, Variant, variantFromJSON, variantToJSON } from "../shared"; + +export const protobufPackage = "fishjam.media_events.peer"; + +/** Defines any type of message sent from Peer to Membrane RTC Engine */ +export interface MediaEvent { + connect?: MediaEvent_Connect | undefined; + disconnect?: MediaEvent_Disconnect | undefined; + updateEndpointMetadata?: MediaEvent_UpdateEndpointMetadata | undefined; + updateTrackMetadata?: MediaEvent_UpdateTrackMetadata | undefined; + renegotiateTracks?: MediaEvent_RenegotiateTracks | undefined; + candidate?: Candidate | undefined; + sdpOffer?: MediaEvent_SdpOffer | undefined; + trackBitrates?: MediaEvent_TrackBitrates | undefined; + enableTrackVariant?: MediaEvent_EnableTrackVariant | undefined; + disableTrackVariant?: MediaEvent_DisableTrackVariant | undefined; + setTargetTrackVariant?: MediaEvent_SetTargetTrackVariant | undefined; +} + +export interface MediaEvent_VariantBitrate { + variant: Variant; + bitrate: number; +} + +/** Sent when a peer wants to join WebRTC Endpoint. */ +export interface MediaEvent_Connect { + metadataJson: string; +} + +/** Sent when a peer disconnects from WebRTC Endpoint. */ +export interface MediaEvent_Disconnect { +} + +/** Sent when a peer wants to update its metadata */ +export interface MediaEvent_UpdateEndpointMetadata { + metadataJson: string; +} + +/** Sent when a peer wants to update its track's metadata */ +export interface MediaEvent_UpdateTrackMetadata { + trackId: string; + metadataJson: string; +} + +/** Sent when peer wants to renegatiate connection due to adding a track or removing a track */ +export interface MediaEvent_RenegotiateTracks { +} + +/** + * Sent as a response to `offerData` media event during renegotiation + * Maps contain only information about current peer's `sendonly` tracks. + * The "mid" is an identifier used to associate an RTP packet with an MLine from the SDP offer/answer. + */ +export interface MediaEvent_SdpOffer { + /** The value of the `sessionDescription.sdp` */ + sdp: string; + trackIdToMetadataJson: { [key: string]: string }; + /** Maps track_id to its bitrate. The track_id in the TrackBitrates message is ignored (we use the map key), so it can be ommited. */ + trackIdToBitrates: { [key: string]: MediaEvent_TrackBitrates }; + midToTrackId: { [key: string]: string }; +} + +export interface MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry { + key: string; + value: string; +} + +export interface MediaEvent_SdpOffer_TrackIdToBitratesEntry { + key: string; + value: MediaEvent_TrackBitrates | undefined; +} + +export interface MediaEvent_SdpOffer_MidToTrackIdEntry { + key: string; + value: string; +} + +/** Sent when Peer wants to update its track's bitrate */ +export interface MediaEvent_TrackBitrates { + trackId: string; + /** Bitrate of each variant. For non-simulcast tracks use VARIANT_UNSPECIFIED. */ + variantBitrates: MediaEvent_VariantBitrate[]; +} + +/** Sent when client disables one of the track variants */ +export interface MediaEvent_DisableTrackVariant { + trackId: string; + variant: Variant; +} + +/** Sent when client enables one of the track variants */ +export interface MediaEvent_EnableTrackVariant { + trackId: string; + variant: Variant; +} + +export interface MediaEvent_SetTargetTrackVariant { + trackId: string; + variant: Variant; +} + +function createBaseMediaEvent(): MediaEvent { + return { + connect: undefined, + disconnect: undefined, + updateEndpointMetadata: undefined, + updateTrackMetadata: undefined, + renegotiateTracks: undefined, + candidate: undefined, + sdpOffer: undefined, + trackBitrates: undefined, + enableTrackVariant: undefined, + disableTrackVariant: undefined, + setTargetTrackVariant: undefined, + }; +} + +export const MediaEvent: MessageFns = { + encode(message: MediaEvent, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.connect !== undefined) { + MediaEvent_Connect.encode(message.connect, writer.uint32(10).fork()).join(); + } + if (message.disconnect !== undefined) { + MediaEvent_Disconnect.encode(message.disconnect, writer.uint32(18).fork()).join(); + } + if (message.updateEndpointMetadata !== undefined) { + MediaEvent_UpdateEndpointMetadata.encode(message.updateEndpointMetadata, writer.uint32(26).fork()).join(); + } + if (message.updateTrackMetadata !== undefined) { + MediaEvent_UpdateTrackMetadata.encode(message.updateTrackMetadata, writer.uint32(34).fork()).join(); + } + if (message.renegotiateTracks !== undefined) { + MediaEvent_RenegotiateTracks.encode(message.renegotiateTracks, writer.uint32(42).fork()).join(); + } + if (message.candidate !== undefined) { + Candidate.encode(message.candidate, writer.uint32(50).fork()).join(); + } + if (message.sdpOffer !== undefined) { + MediaEvent_SdpOffer.encode(message.sdpOffer, writer.uint32(58).fork()).join(); + } + if (message.trackBitrates !== undefined) { + MediaEvent_TrackBitrates.encode(message.trackBitrates, writer.uint32(66).fork()).join(); + } + if (message.enableTrackVariant !== undefined) { + MediaEvent_EnableTrackVariant.encode(message.enableTrackVariant, writer.uint32(74).fork()).join(); + } + if (message.disableTrackVariant !== undefined) { + MediaEvent_DisableTrackVariant.encode(message.disableTrackVariant, writer.uint32(82).fork()).join(); + } + if (message.setTargetTrackVariant !== undefined) { + MediaEvent_SetTargetTrackVariant.encode(message.setTargetTrackVariant, writer.uint32(90).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.connect = MediaEvent_Connect.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.disconnect = MediaEvent_Disconnect.decode(reader, reader.uint32()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.updateEndpointMetadata = MediaEvent_UpdateEndpointMetadata.decode(reader, reader.uint32()); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.updateTrackMetadata = MediaEvent_UpdateTrackMetadata.decode(reader, reader.uint32()); + continue; + } + case 5: { + if (tag !== 42) { + break; + } + + message.renegotiateTracks = MediaEvent_RenegotiateTracks.decode(reader, reader.uint32()); + continue; + } + case 6: { + if (tag !== 50) { + break; + } + + message.candidate = Candidate.decode(reader, reader.uint32()); + continue; + } + case 7: { + if (tag !== 58) { + break; + } + + message.sdpOffer = MediaEvent_SdpOffer.decode(reader, reader.uint32()); + continue; + } + case 8: { + if (tag !== 66) { + break; + } + + message.trackBitrates = MediaEvent_TrackBitrates.decode(reader, reader.uint32()); + continue; + } + case 9: { + if (tag !== 74) { + break; + } + + message.enableTrackVariant = MediaEvent_EnableTrackVariant.decode(reader, reader.uint32()); + continue; + } + case 10: { + if (tag !== 82) { + break; + } + + message.disableTrackVariant = MediaEvent_DisableTrackVariant.decode(reader, reader.uint32()); + continue; + } + case 11: { + if (tag !== 90) { + break; + } + + message.setTargetTrackVariant = MediaEvent_SetTargetTrackVariant.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent { + return { + connect: isSet(object.connect) ? MediaEvent_Connect.fromJSON(object.connect) : undefined, + disconnect: isSet(object.disconnect) ? MediaEvent_Disconnect.fromJSON(object.disconnect) : undefined, + updateEndpointMetadata: isSet(object.updateEndpointMetadata) + ? MediaEvent_UpdateEndpointMetadata.fromJSON(object.updateEndpointMetadata) + : undefined, + updateTrackMetadata: isSet(object.updateTrackMetadata) + ? MediaEvent_UpdateTrackMetadata.fromJSON(object.updateTrackMetadata) + : undefined, + renegotiateTracks: isSet(object.renegotiateTracks) + ? MediaEvent_RenegotiateTracks.fromJSON(object.renegotiateTracks) + : undefined, + candidate: isSet(object.candidate) ? Candidate.fromJSON(object.candidate) : undefined, + sdpOffer: isSet(object.sdpOffer) ? MediaEvent_SdpOffer.fromJSON(object.sdpOffer) : undefined, + trackBitrates: isSet(object.trackBitrates) ? MediaEvent_TrackBitrates.fromJSON(object.trackBitrates) : undefined, + enableTrackVariant: isSet(object.enableTrackVariant) + ? MediaEvent_EnableTrackVariant.fromJSON(object.enableTrackVariant) + : undefined, + disableTrackVariant: isSet(object.disableTrackVariant) + ? MediaEvent_DisableTrackVariant.fromJSON(object.disableTrackVariant) + : undefined, + setTargetTrackVariant: isSet(object.setTargetTrackVariant) + ? MediaEvent_SetTargetTrackVariant.fromJSON(object.setTargetTrackVariant) + : undefined, + }; + }, + + toJSON(message: MediaEvent): unknown { + const obj: any = {}; + if (message.connect !== undefined) { + obj.connect = MediaEvent_Connect.toJSON(message.connect); + } + if (message.disconnect !== undefined) { + obj.disconnect = MediaEvent_Disconnect.toJSON(message.disconnect); + } + if (message.updateEndpointMetadata !== undefined) { + obj.updateEndpointMetadata = MediaEvent_UpdateEndpointMetadata.toJSON(message.updateEndpointMetadata); + } + if (message.updateTrackMetadata !== undefined) { + obj.updateTrackMetadata = MediaEvent_UpdateTrackMetadata.toJSON(message.updateTrackMetadata); + } + if (message.renegotiateTracks !== undefined) { + obj.renegotiateTracks = MediaEvent_RenegotiateTracks.toJSON(message.renegotiateTracks); + } + if (message.candidate !== undefined) { + obj.candidate = Candidate.toJSON(message.candidate); + } + if (message.sdpOffer !== undefined) { + obj.sdpOffer = MediaEvent_SdpOffer.toJSON(message.sdpOffer); + } + if (message.trackBitrates !== undefined) { + obj.trackBitrates = MediaEvent_TrackBitrates.toJSON(message.trackBitrates); + } + if (message.enableTrackVariant !== undefined) { + obj.enableTrackVariant = MediaEvent_EnableTrackVariant.toJSON(message.enableTrackVariant); + } + if (message.disableTrackVariant !== undefined) { + obj.disableTrackVariant = MediaEvent_DisableTrackVariant.toJSON(message.disableTrackVariant); + } + if (message.setTargetTrackVariant !== undefined) { + obj.setTargetTrackVariant = MediaEvent_SetTargetTrackVariant.toJSON(message.setTargetTrackVariant); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent { + return MediaEvent.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent { + const message = createBaseMediaEvent(); + message.connect = (object.connect !== undefined && object.connect !== null) + ? MediaEvent_Connect.fromPartial(object.connect) + : undefined; + message.disconnect = (object.disconnect !== undefined && object.disconnect !== null) + ? MediaEvent_Disconnect.fromPartial(object.disconnect) + : undefined; + message.updateEndpointMetadata = + (object.updateEndpointMetadata !== undefined && object.updateEndpointMetadata !== null) + ? MediaEvent_UpdateEndpointMetadata.fromPartial(object.updateEndpointMetadata) + : undefined; + message.updateTrackMetadata = (object.updateTrackMetadata !== undefined && object.updateTrackMetadata !== null) + ? MediaEvent_UpdateTrackMetadata.fromPartial(object.updateTrackMetadata) + : undefined; + message.renegotiateTracks = (object.renegotiateTracks !== undefined && object.renegotiateTracks !== null) + ? MediaEvent_RenegotiateTracks.fromPartial(object.renegotiateTracks) + : undefined; + message.candidate = (object.candidate !== undefined && object.candidate !== null) + ? Candidate.fromPartial(object.candidate) + : undefined; + message.sdpOffer = (object.sdpOffer !== undefined && object.sdpOffer !== null) + ? MediaEvent_SdpOffer.fromPartial(object.sdpOffer) + : undefined; + message.trackBitrates = (object.trackBitrates !== undefined && object.trackBitrates !== null) + ? MediaEvent_TrackBitrates.fromPartial(object.trackBitrates) + : undefined; + message.enableTrackVariant = (object.enableTrackVariant !== undefined && object.enableTrackVariant !== null) + ? MediaEvent_EnableTrackVariant.fromPartial(object.enableTrackVariant) + : undefined; + message.disableTrackVariant = (object.disableTrackVariant !== undefined && object.disableTrackVariant !== null) + ? MediaEvent_DisableTrackVariant.fromPartial(object.disableTrackVariant) + : undefined; + message.setTargetTrackVariant = + (object.setTargetTrackVariant !== undefined && object.setTargetTrackVariant !== null) + ? MediaEvent_SetTargetTrackVariant.fromPartial(object.setTargetTrackVariant) + : undefined; + return message; + }, +}; + +function createBaseMediaEvent_VariantBitrate(): MediaEvent_VariantBitrate { + return { variant: 0, bitrate: 0 }; +} + +export const MediaEvent_VariantBitrate: MessageFns = { + encode(message: MediaEvent_VariantBitrate, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.variant !== 0) { + writer.uint32(8).int32(message.variant); + } + if (message.bitrate !== 0) { + writer.uint32(16).int32(message.bitrate); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_VariantBitrate { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_VariantBitrate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.variant = reader.int32() as any; + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.bitrate = reader.int32(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_VariantBitrate { + return { + variant: isSet(object.variant) ? variantFromJSON(object.variant) : 0, + bitrate: isSet(object.bitrate) ? globalThis.Number(object.bitrate) : 0, + }; + }, + + toJSON(message: MediaEvent_VariantBitrate): unknown { + const obj: any = {}; + if (message.variant !== 0) { + obj.variant = variantToJSON(message.variant); + } + if (message.bitrate !== 0) { + obj.bitrate = Math.round(message.bitrate); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_VariantBitrate { + return MediaEvent_VariantBitrate.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_VariantBitrate { + const message = createBaseMediaEvent_VariantBitrate(); + message.variant = object.variant ?? 0; + message.bitrate = object.bitrate ?? 0; + return message; + }, +}; + +function createBaseMediaEvent_Connect(): MediaEvent_Connect { + return { metadataJson: "" }; +} + +export const MediaEvent_Connect: MessageFns = { + encode(message: MediaEvent_Connect, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.metadataJson !== "") { + writer.uint32(10).string(message.metadataJson); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_Connect { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_Connect(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.metadataJson = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_Connect { + return { metadataJson: isSet(object.metadataJson) ? globalThis.String(object.metadataJson) : "" }; + }, + + toJSON(message: MediaEvent_Connect): unknown { + const obj: any = {}; + if (message.metadataJson !== "") { + obj.metadataJson = message.metadataJson; + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_Connect { + return MediaEvent_Connect.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_Connect { + const message = createBaseMediaEvent_Connect(); + message.metadataJson = object.metadataJson ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_Disconnect(): MediaEvent_Disconnect { + return {}; +} + +export const MediaEvent_Disconnect: MessageFns = { + encode(_: MediaEvent_Disconnect, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_Disconnect { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_Disconnect(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(_: any): MediaEvent_Disconnect { + return {}; + }, + + toJSON(_: MediaEvent_Disconnect): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): MediaEvent_Disconnect { + return MediaEvent_Disconnect.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): MediaEvent_Disconnect { + const message = createBaseMediaEvent_Disconnect(); + return message; + }, +}; + +function createBaseMediaEvent_UpdateEndpointMetadata(): MediaEvent_UpdateEndpointMetadata { + return { metadataJson: "" }; +} + +export const MediaEvent_UpdateEndpointMetadata: MessageFns = { + encode(message: MediaEvent_UpdateEndpointMetadata, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.metadataJson !== "") { + writer.uint32(10).string(message.metadataJson); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_UpdateEndpointMetadata { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_UpdateEndpointMetadata(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.metadataJson = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_UpdateEndpointMetadata { + return { metadataJson: isSet(object.metadataJson) ? globalThis.String(object.metadataJson) : "" }; + }, + + toJSON(message: MediaEvent_UpdateEndpointMetadata): unknown { + const obj: any = {}; + if (message.metadataJson !== "") { + obj.metadataJson = message.metadataJson; + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_UpdateEndpointMetadata { + return MediaEvent_UpdateEndpointMetadata.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_UpdateEndpointMetadata { + const message = createBaseMediaEvent_UpdateEndpointMetadata(); + message.metadataJson = object.metadataJson ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_UpdateTrackMetadata(): MediaEvent_UpdateTrackMetadata { + return { trackId: "", metadataJson: "" }; +} + +export const MediaEvent_UpdateTrackMetadata: MessageFns = { + encode(message: MediaEvent_UpdateTrackMetadata, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.trackId !== "") { + writer.uint32(10).string(message.trackId); + } + if (message.metadataJson !== "") { + writer.uint32(18).string(message.metadataJson); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_UpdateTrackMetadata { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_UpdateTrackMetadata(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.metadataJson = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_UpdateTrackMetadata { + return { + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + metadataJson: isSet(object.metadataJson) ? globalThis.String(object.metadataJson) : "", + }; + }, + + toJSON(message: MediaEvent_UpdateTrackMetadata): unknown { + const obj: any = {}; + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.metadataJson !== "") { + obj.metadataJson = message.metadataJson; + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_UpdateTrackMetadata { + return MediaEvent_UpdateTrackMetadata.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_UpdateTrackMetadata { + const message = createBaseMediaEvent_UpdateTrackMetadata(); + message.trackId = object.trackId ?? ""; + message.metadataJson = object.metadataJson ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_RenegotiateTracks(): MediaEvent_RenegotiateTracks { + return {}; +} + +export const MediaEvent_RenegotiateTracks: MessageFns = { + encode(_: MediaEvent_RenegotiateTracks, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_RenegotiateTracks { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_RenegotiateTracks(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(_: any): MediaEvent_RenegotiateTracks { + return {}; + }, + + toJSON(_: MediaEvent_RenegotiateTracks): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): MediaEvent_RenegotiateTracks { + return MediaEvent_RenegotiateTracks.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): MediaEvent_RenegotiateTracks { + const message = createBaseMediaEvent_RenegotiateTracks(); + return message; + }, +}; + +function createBaseMediaEvent_SdpOffer(): MediaEvent_SdpOffer { + return { sdp: "", trackIdToMetadataJson: {}, trackIdToBitrates: {}, midToTrackId: {} }; +} + +export const MediaEvent_SdpOffer: MessageFns = { + encode(message: MediaEvent_SdpOffer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.sdp !== "") { + writer.uint32(10).string(message.sdp); + } + Object.entries(message.trackIdToMetadataJson).forEach(([key, value]) => { + MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry.encode({ key: key as any, value }, writer.uint32(18).fork()) + .join(); + }); + Object.entries(message.trackIdToBitrates).forEach(([key, value]) => { + MediaEvent_SdpOffer_TrackIdToBitratesEntry.encode({ key: key as any, value }, writer.uint32(26).fork()).join(); + }); + Object.entries(message.midToTrackId).forEach(([key, value]) => { + MediaEvent_SdpOffer_MidToTrackIdEntry.encode({ key: key as any, value }, writer.uint32(34).fork()).join(); + }); + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_SdpOffer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_SdpOffer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.sdp = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + const entry2 = MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry.decode(reader, reader.uint32()); + if (entry2.value !== undefined) { + message.trackIdToMetadataJson[entry2.key] = entry2.value; + } + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + const entry3 = MediaEvent_SdpOffer_TrackIdToBitratesEntry.decode(reader, reader.uint32()); + if (entry3.value !== undefined) { + message.trackIdToBitrates[entry3.key] = entry3.value; + } + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + const entry4 = MediaEvent_SdpOffer_MidToTrackIdEntry.decode(reader, reader.uint32()); + if (entry4.value !== undefined) { + message.midToTrackId[entry4.key] = entry4.value; + } + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_SdpOffer { + return { + sdp: isSet(object.sdp) ? globalThis.String(object.sdp) : "", + trackIdToMetadataJson: isObject(object.trackIdToMetadataJson) + ? Object.entries(object.trackIdToMetadataJson).reduce<{ [key: string]: string }>((acc, [key, value]) => { + acc[key] = String(value); + return acc; + }, {}) + : {}, + trackIdToBitrates: isObject(object.trackIdToBitrates) + ? Object.entries(object.trackIdToBitrates).reduce<{ [key: string]: MediaEvent_TrackBitrates }>( + (acc, [key, value]) => { + acc[key] = MediaEvent_TrackBitrates.fromJSON(value); + return acc; + }, + {}, + ) + : {}, + midToTrackId: isObject(object.midToTrackId) + ? Object.entries(object.midToTrackId).reduce<{ [key: string]: string }>((acc, [key, value]) => { + acc[key] = String(value); + return acc; + }, {}) + : {}, + }; + }, + + toJSON(message: MediaEvent_SdpOffer): unknown { + const obj: any = {}; + if (message.sdp !== "") { + obj.sdp = message.sdp; + } + if (message.trackIdToMetadataJson) { + const entries = Object.entries(message.trackIdToMetadataJson); + if (entries.length > 0) { + obj.trackIdToMetadataJson = {}; + entries.forEach(([k, v]) => { + obj.trackIdToMetadataJson[k] = v; + }); + } + } + if (message.trackIdToBitrates) { + const entries = Object.entries(message.trackIdToBitrates); + if (entries.length > 0) { + obj.trackIdToBitrates = {}; + entries.forEach(([k, v]) => { + obj.trackIdToBitrates[k] = MediaEvent_TrackBitrates.toJSON(v); + }); + } + } + if (message.midToTrackId) { + const entries = Object.entries(message.midToTrackId); + if (entries.length > 0) { + obj.midToTrackId = {}; + entries.forEach(([k, v]) => { + obj.midToTrackId[k] = v; + }); + } + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_SdpOffer { + return MediaEvent_SdpOffer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_SdpOffer { + const message = createBaseMediaEvent_SdpOffer(); + message.sdp = object.sdp ?? ""; + message.trackIdToMetadataJson = Object.entries(object.trackIdToMetadataJson ?? {}).reduce< + { [key: string]: string } + >((acc, [key, value]) => { + if (value !== undefined) { + acc[key] = globalThis.String(value); + } + return acc; + }, {}); + message.trackIdToBitrates = Object.entries(object.trackIdToBitrates ?? {}).reduce< + { [key: string]: MediaEvent_TrackBitrates } + >((acc, [key, value]) => { + if (value !== undefined) { + acc[key] = MediaEvent_TrackBitrates.fromPartial(value); + } + return acc; + }, {}); + message.midToTrackId = Object.entries(object.midToTrackId ?? {}).reduce<{ [key: string]: string }>( + (acc, [key, value]) => { + if (value !== undefined) { + acc[key] = globalThis.String(value); + } + return acc; + }, + {}, + ); + return message; + }, +}; + +function createBaseMediaEvent_SdpOffer_TrackIdToMetadataJsonEntry(): MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry { + return { key: "", value: "" }; +} + +export const MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry: MessageFns< + MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry +> = { + encode( + message: MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry, + writer: BinaryWriter = new BinaryWriter(), + ): BinaryWriter { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== "") { + writer.uint32(18).string(message.value); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_SdpOffer_TrackIdToMetadataJsonEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.value = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? globalThis.String(object.value) : "", + }; + }, + + toJSON(message: MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== "") { + obj.value = message.value; + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry { + return MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_SdpOffer_TrackIdToMetadataJsonEntry { + const message = createBaseMediaEvent_SdpOffer_TrackIdToMetadataJsonEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_SdpOffer_TrackIdToBitratesEntry(): MediaEvent_SdpOffer_TrackIdToBitratesEntry { + return { key: "", value: undefined }; +} + +export const MediaEvent_SdpOffer_TrackIdToBitratesEntry: MessageFns = { + encode(message: MediaEvent_SdpOffer_TrackIdToBitratesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + MediaEvent_TrackBitrates.encode(message.value, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_SdpOffer_TrackIdToBitratesEntry { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_SdpOffer_TrackIdToBitratesEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.value = MediaEvent_TrackBitrates.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_SdpOffer_TrackIdToBitratesEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? MediaEvent_TrackBitrates.fromJSON(object.value) : undefined, + }; + }, + + toJSON(message: MediaEvent_SdpOffer_TrackIdToBitratesEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== undefined) { + obj.value = MediaEvent_TrackBitrates.toJSON(message.value); + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_SdpOffer_TrackIdToBitratesEntry { + return MediaEvent_SdpOffer_TrackIdToBitratesEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_SdpOffer_TrackIdToBitratesEntry { + const message = createBaseMediaEvent_SdpOffer_TrackIdToBitratesEntry(); + message.key = object.key ?? ""; + message.value = (object.value !== undefined && object.value !== null) + ? MediaEvent_TrackBitrates.fromPartial(object.value) + : undefined; + return message; + }, +}; + +function createBaseMediaEvent_SdpOffer_MidToTrackIdEntry(): MediaEvent_SdpOffer_MidToTrackIdEntry { + return { key: "", value: "" }; +} + +export const MediaEvent_SdpOffer_MidToTrackIdEntry: MessageFns = { + encode(message: MediaEvent_SdpOffer_MidToTrackIdEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== "") { + writer.uint32(18).string(message.value); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_SdpOffer_MidToTrackIdEntry { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_SdpOffer_MidToTrackIdEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.value = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_SdpOffer_MidToTrackIdEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? globalThis.String(object.value) : "", + }; + }, + + toJSON(message: MediaEvent_SdpOffer_MidToTrackIdEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== "") { + obj.value = message.value; + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_SdpOffer_MidToTrackIdEntry { + return MediaEvent_SdpOffer_MidToTrackIdEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_SdpOffer_MidToTrackIdEntry { + const message = createBaseMediaEvent_SdpOffer_MidToTrackIdEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_TrackBitrates(): MediaEvent_TrackBitrates { + return { trackId: "", variantBitrates: [] }; +} + +export const MediaEvent_TrackBitrates: MessageFns = { + encode(message: MediaEvent_TrackBitrates, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.trackId !== "") { + writer.uint32(10).string(message.trackId); + } + for (const v of message.variantBitrates) { + MediaEvent_VariantBitrate.encode(v!, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_TrackBitrates { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_TrackBitrates(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.variantBitrates.push(MediaEvent_VariantBitrate.decode(reader, reader.uint32())); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_TrackBitrates { + return { + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + variantBitrates: globalThis.Array.isArray(object?.variantBitrates) + ? object.variantBitrates.map((e: any) => MediaEvent_VariantBitrate.fromJSON(e)) + : [], + }; + }, + + toJSON(message: MediaEvent_TrackBitrates): unknown { + const obj: any = {}; + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.variantBitrates?.length) { + obj.variantBitrates = message.variantBitrates.map((e) => MediaEvent_VariantBitrate.toJSON(e)); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_TrackBitrates { + return MediaEvent_TrackBitrates.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_TrackBitrates { + const message = createBaseMediaEvent_TrackBitrates(); + message.trackId = object.trackId ?? ""; + message.variantBitrates = object.variantBitrates?.map((e) => MediaEvent_VariantBitrate.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseMediaEvent_DisableTrackVariant(): MediaEvent_DisableTrackVariant { + return { trackId: "", variant: 0 }; +} + +export const MediaEvent_DisableTrackVariant: MessageFns = { + encode(message: MediaEvent_DisableTrackVariant, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.trackId !== "") { + writer.uint32(10).string(message.trackId); + } + if (message.variant !== 0) { + writer.uint32(16).int32(message.variant); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_DisableTrackVariant { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_DisableTrackVariant(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.variant = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_DisableTrackVariant { + return { + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + variant: isSet(object.variant) ? variantFromJSON(object.variant) : 0, + }; + }, + + toJSON(message: MediaEvent_DisableTrackVariant): unknown { + const obj: any = {}; + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.variant !== 0) { + obj.variant = variantToJSON(message.variant); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_DisableTrackVariant { + return MediaEvent_DisableTrackVariant.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_DisableTrackVariant { + const message = createBaseMediaEvent_DisableTrackVariant(); + message.trackId = object.trackId ?? ""; + message.variant = object.variant ?? 0; + return message; + }, +}; + +function createBaseMediaEvent_EnableTrackVariant(): MediaEvent_EnableTrackVariant { + return { trackId: "", variant: 0 }; +} + +export const MediaEvent_EnableTrackVariant: MessageFns = { + encode(message: MediaEvent_EnableTrackVariant, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.trackId !== "") { + writer.uint32(10).string(message.trackId); + } + if (message.variant !== 0) { + writer.uint32(16).int32(message.variant); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_EnableTrackVariant { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_EnableTrackVariant(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.variant = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_EnableTrackVariant { + return { + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + variant: isSet(object.variant) ? variantFromJSON(object.variant) : 0, + }; + }, + + toJSON(message: MediaEvent_EnableTrackVariant): unknown { + const obj: any = {}; + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.variant !== 0) { + obj.variant = variantToJSON(message.variant); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_EnableTrackVariant { + return MediaEvent_EnableTrackVariant.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_EnableTrackVariant { + const message = createBaseMediaEvent_EnableTrackVariant(); + message.trackId = object.trackId ?? ""; + message.variant = object.variant ?? 0; + return message; + }, +}; + +function createBaseMediaEvent_SetTargetTrackVariant(): MediaEvent_SetTargetTrackVariant { + return { trackId: "", variant: 0 }; +} + +export const MediaEvent_SetTargetTrackVariant: MessageFns = { + encode(message: MediaEvent_SetTargetTrackVariant, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.trackId !== "") { + writer.uint32(10).string(message.trackId); + } + if (message.variant !== 0) { + writer.uint32(16).int32(message.variant); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_SetTargetTrackVariant { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_SetTargetTrackVariant(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.variant = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_SetTargetTrackVariant { + return { + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + variant: isSet(object.variant) ? variantFromJSON(object.variant) : 0, + }; + }, + + toJSON(message: MediaEvent_SetTargetTrackVariant): unknown { + const obj: any = {}; + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.variant !== 0) { + obj.variant = variantToJSON(message.variant); + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_SetTargetTrackVariant { + return MediaEvent_SetTargetTrackVariant.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_SetTargetTrackVariant { + const message = createBaseMediaEvent_SetTargetTrackVariant(); + message.trackId = object.trackId ?? ""; + message.variant = object.variant ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/packages/protobufs/fishjam/media_events/server/server.ts b/packages/protobufs/fishjam/media_events/server/server.ts new file mode 100644 index 00000000..5acf2fc4 --- /dev/null +++ b/packages/protobufs/fishjam/media_events/server/server.ts @@ -0,0 +1,2570 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.4.0 +// protoc v5.28.2 +// source: fishjam/media_events/server/server.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; +import { Candidate, Variant, variantFromJSON, variantToJSON } from "../shared"; + +export const protobufPackage = "fishjam.media_events.server"; + +/** Defines any type of message sent from Membrane RTC Engine to Peer */ +export interface MediaEvent { + endpointUpdated?: MediaEvent_EndpointUpdated | undefined; + trackUpdated?: MediaEvent_TrackUpdated | undefined; + tracksAdded?: MediaEvent_TracksAdded | undefined; + tracksRemoved?: MediaEvent_TracksRemoved | undefined; + endpointAdded?: MediaEvent_EndpointAdded | undefined; + endpointRemoved?: MediaEvent_EndpointRemoved | undefined; + connected?: MediaEvent_Connected | undefined; + error?: MediaEvent_Error | undefined; + offerData?: MediaEvent_OfferData | undefined; + candidate?: Candidate | undefined; + sdpAnswer?: MediaEvent_SdpAnswer | undefined; + vadNotification?: MediaEvent_VadNotification | undefined; + trackVariantSwitched?: MediaEvent_TrackVariantSwitched | undefined; + trackVariantDisabled?: MediaEvent_TrackVariantDisabled | undefined; + trackVariantEnabled?: MediaEvent_TrackVariantEnabled | undefined; +} + +/** SCHEMAS */ +export interface MediaEvent_Track { + metadataJson: string; + simulcastConfig: MediaEvent_Track_SimulcastConfig | undefined; +} + +export interface MediaEvent_Track_SimulcastConfig { + enabled: boolean; + enabledVariants: Variant[]; + disabledVariants: Variant[]; +} + +export interface MediaEvent_Endpoint { + endpointType: string; + metadataJson: string; + trackIdToTrack: { [key: string]: MediaEvent_Track }; +} + +export interface MediaEvent_Endpoint_TrackIdToTrackEntry { + key: string; + value: MediaEvent_Track | undefined; +} + +export interface MediaEvent_IceServer { + credential: string; + urls: string[]; + username: string; +} + +/** Sent when metadata of one of the endpoints was updated */ +export interface MediaEvent_EndpointUpdated { + endpointId: string; + metadataJson: string; +} + +/** Sent when metadata of one of the tracks was updated */ +export interface MediaEvent_TrackUpdated { + endpointId: string; + trackId: string; + metadataJson: string; +} + +/** Sent to informs that one of the peers has added one or more tracks. */ +export interface MediaEvent_TracksAdded { + endpointId: string; + trackIdToTrack: { [key: string]: MediaEvent_Track }; +} + +export interface MediaEvent_TracksAdded_TrackIdToTrackEntry { + key: string; + value: MediaEvent_Track | undefined; +} + +/** Sent to informs that one of the peers has removed one or more tracks. */ +export interface MediaEvent_TracksRemoved { + endpointId: string; + trackIds: string[]; +} + +/** Sent to all peers in the room after a new endpoint was added. */ +export interface MediaEvent_EndpointAdded { + endpointId: string; + metadataJson: string; +} + +/** Sent to the peer after connecting to the WebRTC Endpoint. */ +export interface MediaEvent_Connected { + endpointId: string; + endpointIdToEndpoint: { [key: string]: MediaEvent_Endpoint }; + iceServers: MediaEvent_IceServer[]; +} + +export interface MediaEvent_Connected_EndpointIdToEndpointEntry { + key: string; + value: MediaEvent_Endpoint | undefined; +} + +/** Sent to all remaining peers in the room after some endpoint was removed. */ +export interface MediaEvent_EndpointRemoved { + endpointId: string; +} + +/** Sent to inform that an error occurred on the server providing a message to show */ +export interface MediaEvent_Error { + message: string; +} + +/** Sent to inform about the number of audio and video tracks that will be sent from the engine to the peer */ +export interface MediaEvent_OfferData { + tracksTypes: MediaEvent_OfferData_TrackTypes | undefined; +} + +export interface MediaEvent_OfferData_TrackTypes { + audio: number; + video: number; +} + +/** Sent after receiving `SdpOffer` from Peer */ +export interface MediaEvent_SdpAnswer { + /** The value of the `sessionDescription.sdp` */ + sdp: string; + midToTrackId: { [key: string]: string }; +} + +export interface MediaEvent_SdpAnswer_MidToTrackIdEntry { + key: string; + value: string; +} + +/** + * Sent to inform that the track denoted by `trackId` has changed their voice actiivty + * For this notification to work, the server must be configured to use VAD extension + * and the sender must support it. + */ +export interface MediaEvent_VadNotification { + trackId: string; + status: MediaEvent_VadNotification_Status; +} + +export enum MediaEvent_VadNotification_Status { + STATUS_UNSPECIFIED = 0, + STATUS_SILENCE = 1, + STATUS_SPEECH = 2, + UNRECOGNIZED = -1, +} + +export function mediaEvent_VadNotification_StatusFromJSON(object: any): MediaEvent_VadNotification_Status { + switch (object) { + case 0: + case "STATUS_UNSPECIFIED": + return MediaEvent_VadNotification_Status.STATUS_UNSPECIFIED; + case 1: + case "STATUS_SILENCE": + return MediaEvent_VadNotification_Status.STATUS_SILENCE; + case 2: + case "STATUS_SPEECH": + return MediaEvent_VadNotification_Status.STATUS_SPEECH; + case -1: + case "UNRECOGNIZED": + default: + return MediaEvent_VadNotification_Status.UNRECOGNIZED; + } +} + +export function mediaEvent_VadNotification_StatusToJSON(object: MediaEvent_VadNotification_Status): string { + switch (object) { + case MediaEvent_VadNotification_Status.STATUS_UNSPECIFIED: + return "STATUS_UNSPECIFIED"; + case MediaEvent_VadNotification_Status.STATUS_SILENCE: + return "STATUS_SILENCE"; + case MediaEvent_VadNotification_Status.STATUS_SPEECH: + return "STATUS_SPEECH"; + case MediaEvent_VadNotification_Status.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +/** Informs that track's variant has been changed */ +export interface MediaEvent_TrackVariantSwitched { + endpointId: string; + trackId: string; + variant: Variant; +} + +/** Sent when track's variant has been disabled */ +export interface MediaEvent_TrackVariantDisabled { + endpointId: string; + trackId: string; + variant: Variant; +} + +/** Sent when track's variant has been enabled */ +export interface MediaEvent_TrackVariantEnabled { + endpointId: string; + trackId: string; + variant: Variant; +} + +function createBaseMediaEvent(): MediaEvent { + return { + endpointUpdated: undefined, + trackUpdated: undefined, + tracksAdded: undefined, + tracksRemoved: undefined, + endpointAdded: undefined, + endpointRemoved: undefined, + connected: undefined, + error: undefined, + offerData: undefined, + candidate: undefined, + sdpAnswer: undefined, + vadNotification: undefined, + trackVariantSwitched: undefined, + trackVariantDisabled: undefined, + trackVariantEnabled: undefined, + }; +} + +export const MediaEvent: MessageFns = { + encode(message: MediaEvent, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointUpdated !== undefined) { + MediaEvent_EndpointUpdated.encode(message.endpointUpdated, writer.uint32(10).fork()).join(); + } + if (message.trackUpdated !== undefined) { + MediaEvent_TrackUpdated.encode(message.trackUpdated, writer.uint32(18).fork()).join(); + } + if (message.tracksAdded !== undefined) { + MediaEvent_TracksAdded.encode(message.tracksAdded, writer.uint32(26).fork()).join(); + } + if (message.tracksRemoved !== undefined) { + MediaEvent_TracksRemoved.encode(message.tracksRemoved, writer.uint32(34).fork()).join(); + } + if (message.endpointAdded !== undefined) { + MediaEvent_EndpointAdded.encode(message.endpointAdded, writer.uint32(42).fork()).join(); + } + if (message.endpointRemoved !== undefined) { + MediaEvent_EndpointRemoved.encode(message.endpointRemoved, writer.uint32(50).fork()).join(); + } + if (message.connected !== undefined) { + MediaEvent_Connected.encode(message.connected, writer.uint32(58).fork()).join(); + } + if (message.error !== undefined) { + MediaEvent_Error.encode(message.error, writer.uint32(66).fork()).join(); + } + if (message.offerData !== undefined) { + MediaEvent_OfferData.encode(message.offerData, writer.uint32(74).fork()).join(); + } + if (message.candidate !== undefined) { + Candidate.encode(message.candidate, writer.uint32(82).fork()).join(); + } + if (message.sdpAnswer !== undefined) { + MediaEvent_SdpAnswer.encode(message.sdpAnswer, writer.uint32(90).fork()).join(); + } + if (message.vadNotification !== undefined) { + MediaEvent_VadNotification.encode(message.vadNotification, writer.uint32(98).fork()).join(); + } + if (message.trackVariantSwitched !== undefined) { + MediaEvent_TrackVariantSwitched.encode(message.trackVariantSwitched, writer.uint32(106).fork()).join(); + } + if (message.trackVariantDisabled !== undefined) { + MediaEvent_TrackVariantDisabled.encode(message.trackVariantDisabled, writer.uint32(114).fork()).join(); + } + if (message.trackVariantEnabled !== undefined) { + MediaEvent_TrackVariantEnabled.encode(message.trackVariantEnabled, writer.uint32(122).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointUpdated = MediaEvent_EndpointUpdated.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.trackUpdated = MediaEvent_TrackUpdated.decode(reader, reader.uint32()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.tracksAdded = MediaEvent_TracksAdded.decode(reader, reader.uint32()); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.tracksRemoved = MediaEvent_TracksRemoved.decode(reader, reader.uint32()); + continue; + } + case 5: { + if (tag !== 42) { + break; + } + + message.endpointAdded = MediaEvent_EndpointAdded.decode(reader, reader.uint32()); + continue; + } + case 6: { + if (tag !== 50) { + break; + } + + message.endpointRemoved = MediaEvent_EndpointRemoved.decode(reader, reader.uint32()); + continue; + } + case 7: { + if (tag !== 58) { + break; + } + + message.connected = MediaEvent_Connected.decode(reader, reader.uint32()); + continue; + } + case 8: { + if (tag !== 66) { + break; + } + + message.error = MediaEvent_Error.decode(reader, reader.uint32()); + continue; + } + case 9: { + if (tag !== 74) { + break; + } + + message.offerData = MediaEvent_OfferData.decode(reader, reader.uint32()); + continue; + } + case 10: { + if (tag !== 82) { + break; + } + + message.candidate = Candidate.decode(reader, reader.uint32()); + continue; + } + case 11: { + if (tag !== 90) { + break; + } + + message.sdpAnswer = MediaEvent_SdpAnswer.decode(reader, reader.uint32()); + continue; + } + case 12: { + if (tag !== 98) { + break; + } + + message.vadNotification = MediaEvent_VadNotification.decode(reader, reader.uint32()); + continue; + } + case 13: { + if (tag !== 106) { + break; + } + + message.trackVariantSwitched = MediaEvent_TrackVariantSwitched.decode(reader, reader.uint32()); + continue; + } + case 14: { + if (tag !== 114) { + break; + } + + message.trackVariantDisabled = MediaEvent_TrackVariantDisabled.decode(reader, reader.uint32()); + continue; + } + case 15: { + if (tag !== 122) { + break; + } + + message.trackVariantEnabled = MediaEvent_TrackVariantEnabled.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent { + return { + endpointUpdated: isSet(object.endpointUpdated) + ? MediaEvent_EndpointUpdated.fromJSON(object.endpointUpdated) + : undefined, + trackUpdated: isSet(object.trackUpdated) ? MediaEvent_TrackUpdated.fromJSON(object.trackUpdated) : undefined, + tracksAdded: isSet(object.tracksAdded) ? MediaEvent_TracksAdded.fromJSON(object.tracksAdded) : undefined, + tracksRemoved: isSet(object.tracksRemoved) ? MediaEvent_TracksRemoved.fromJSON(object.tracksRemoved) : undefined, + endpointAdded: isSet(object.endpointAdded) ? MediaEvent_EndpointAdded.fromJSON(object.endpointAdded) : undefined, + endpointRemoved: isSet(object.endpointRemoved) + ? MediaEvent_EndpointRemoved.fromJSON(object.endpointRemoved) + : undefined, + connected: isSet(object.connected) ? MediaEvent_Connected.fromJSON(object.connected) : undefined, + error: isSet(object.error) ? MediaEvent_Error.fromJSON(object.error) : undefined, + offerData: isSet(object.offerData) ? MediaEvent_OfferData.fromJSON(object.offerData) : undefined, + candidate: isSet(object.candidate) ? Candidate.fromJSON(object.candidate) : undefined, + sdpAnswer: isSet(object.sdpAnswer) ? MediaEvent_SdpAnswer.fromJSON(object.sdpAnswer) : undefined, + vadNotification: isSet(object.vadNotification) + ? MediaEvent_VadNotification.fromJSON(object.vadNotification) + : undefined, + trackVariantSwitched: isSet(object.trackVariantSwitched) + ? MediaEvent_TrackVariantSwitched.fromJSON(object.trackVariantSwitched) + : undefined, + trackVariantDisabled: isSet(object.trackVariantDisabled) + ? MediaEvent_TrackVariantDisabled.fromJSON(object.trackVariantDisabled) + : undefined, + trackVariantEnabled: isSet(object.trackVariantEnabled) + ? MediaEvent_TrackVariantEnabled.fromJSON(object.trackVariantEnabled) + : undefined, + }; + }, + + toJSON(message: MediaEvent): unknown { + const obj: any = {}; + if (message.endpointUpdated !== undefined) { + obj.endpointUpdated = MediaEvent_EndpointUpdated.toJSON(message.endpointUpdated); + } + if (message.trackUpdated !== undefined) { + obj.trackUpdated = MediaEvent_TrackUpdated.toJSON(message.trackUpdated); + } + if (message.tracksAdded !== undefined) { + obj.tracksAdded = MediaEvent_TracksAdded.toJSON(message.tracksAdded); + } + if (message.tracksRemoved !== undefined) { + obj.tracksRemoved = MediaEvent_TracksRemoved.toJSON(message.tracksRemoved); + } + if (message.endpointAdded !== undefined) { + obj.endpointAdded = MediaEvent_EndpointAdded.toJSON(message.endpointAdded); + } + if (message.endpointRemoved !== undefined) { + obj.endpointRemoved = MediaEvent_EndpointRemoved.toJSON(message.endpointRemoved); + } + if (message.connected !== undefined) { + obj.connected = MediaEvent_Connected.toJSON(message.connected); + } + if (message.error !== undefined) { + obj.error = MediaEvent_Error.toJSON(message.error); + } + if (message.offerData !== undefined) { + obj.offerData = MediaEvent_OfferData.toJSON(message.offerData); + } + if (message.candidate !== undefined) { + obj.candidate = Candidate.toJSON(message.candidate); + } + if (message.sdpAnswer !== undefined) { + obj.sdpAnswer = MediaEvent_SdpAnswer.toJSON(message.sdpAnswer); + } + if (message.vadNotification !== undefined) { + obj.vadNotification = MediaEvent_VadNotification.toJSON(message.vadNotification); + } + if (message.trackVariantSwitched !== undefined) { + obj.trackVariantSwitched = MediaEvent_TrackVariantSwitched.toJSON(message.trackVariantSwitched); + } + if (message.trackVariantDisabled !== undefined) { + obj.trackVariantDisabled = MediaEvent_TrackVariantDisabled.toJSON(message.trackVariantDisabled); + } + if (message.trackVariantEnabled !== undefined) { + obj.trackVariantEnabled = MediaEvent_TrackVariantEnabled.toJSON(message.trackVariantEnabled); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent { + return MediaEvent.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent { + const message = createBaseMediaEvent(); + message.endpointUpdated = (object.endpointUpdated !== undefined && object.endpointUpdated !== null) + ? MediaEvent_EndpointUpdated.fromPartial(object.endpointUpdated) + : undefined; + message.trackUpdated = (object.trackUpdated !== undefined && object.trackUpdated !== null) + ? MediaEvent_TrackUpdated.fromPartial(object.trackUpdated) + : undefined; + message.tracksAdded = (object.tracksAdded !== undefined && object.tracksAdded !== null) + ? MediaEvent_TracksAdded.fromPartial(object.tracksAdded) + : undefined; + message.tracksRemoved = (object.tracksRemoved !== undefined && object.tracksRemoved !== null) + ? MediaEvent_TracksRemoved.fromPartial(object.tracksRemoved) + : undefined; + message.endpointAdded = (object.endpointAdded !== undefined && object.endpointAdded !== null) + ? MediaEvent_EndpointAdded.fromPartial(object.endpointAdded) + : undefined; + message.endpointRemoved = (object.endpointRemoved !== undefined && object.endpointRemoved !== null) + ? MediaEvent_EndpointRemoved.fromPartial(object.endpointRemoved) + : undefined; + message.connected = (object.connected !== undefined && object.connected !== null) + ? MediaEvent_Connected.fromPartial(object.connected) + : undefined; + message.error = (object.error !== undefined && object.error !== null) + ? MediaEvent_Error.fromPartial(object.error) + : undefined; + message.offerData = (object.offerData !== undefined && object.offerData !== null) + ? MediaEvent_OfferData.fromPartial(object.offerData) + : undefined; + message.candidate = (object.candidate !== undefined && object.candidate !== null) + ? Candidate.fromPartial(object.candidate) + : undefined; + message.sdpAnswer = (object.sdpAnswer !== undefined && object.sdpAnswer !== null) + ? MediaEvent_SdpAnswer.fromPartial(object.sdpAnswer) + : undefined; + message.vadNotification = (object.vadNotification !== undefined && object.vadNotification !== null) + ? MediaEvent_VadNotification.fromPartial(object.vadNotification) + : undefined; + message.trackVariantSwitched = (object.trackVariantSwitched !== undefined && object.trackVariantSwitched !== null) + ? MediaEvent_TrackVariantSwitched.fromPartial(object.trackVariantSwitched) + : undefined; + message.trackVariantDisabled = (object.trackVariantDisabled !== undefined && object.trackVariantDisabled !== null) + ? MediaEvent_TrackVariantDisabled.fromPartial(object.trackVariantDisabled) + : undefined; + message.trackVariantEnabled = (object.trackVariantEnabled !== undefined && object.trackVariantEnabled !== null) + ? MediaEvent_TrackVariantEnabled.fromPartial(object.trackVariantEnabled) + : undefined; + return message; + }, +}; + +function createBaseMediaEvent_Track(): MediaEvent_Track { + return { metadataJson: "", simulcastConfig: undefined }; +} + +export const MediaEvent_Track: MessageFns = { + encode(message: MediaEvent_Track, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.metadataJson !== "") { + writer.uint32(10).string(message.metadataJson); + } + if (message.simulcastConfig !== undefined) { + MediaEvent_Track_SimulcastConfig.encode(message.simulcastConfig, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_Track { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_Track(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.metadataJson = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.simulcastConfig = MediaEvent_Track_SimulcastConfig.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_Track { + return { + metadataJson: isSet(object.metadataJson) ? globalThis.String(object.metadataJson) : "", + simulcastConfig: isSet(object.simulcastConfig) + ? MediaEvent_Track_SimulcastConfig.fromJSON(object.simulcastConfig) + : undefined, + }; + }, + + toJSON(message: MediaEvent_Track): unknown { + const obj: any = {}; + if (message.metadataJson !== "") { + obj.metadataJson = message.metadataJson; + } + if (message.simulcastConfig !== undefined) { + obj.simulcastConfig = MediaEvent_Track_SimulcastConfig.toJSON(message.simulcastConfig); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_Track { + return MediaEvent_Track.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_Track { + const message = createBaseMediaEvent_Track(); + message.metadataJson = object.metadataJson ?? ""; + message.simulcastConfig = (object.simulcastConfig !== undefined && object.simulcastConfig !== null) + ? MediaEvent_Track_SimulcastConfig.fromPartial(object.simulcastConfig) + : undefined; + return message; + }, +}; + +function createBaseMediaEvent_Track_SimulcastConfig(): MediaEvent_Track_SimulcastConfig { + return { enabled: false, enabledVariants: [], disabledVariants: [] }; +} + +export const MediaEvent_Track_SimulcastConfig: MessageFns = { + encode(message: MediaEvent_Track_SimulcastConfig, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.enabled !== false) { + writer.uint32(8).bool(message.enabled); + } + writer.uint32(18).fork(); + for (const v of message.enabledVariants) { + writer.int32(v); + } + writer.join(); + writer.uint32(26).fork(); + for (const v of message.disabledVariants) { + writer.int32(v); + } + writer.join(); + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_Track_SimulcastConfig { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_Track_SimulcastConfig(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.enabled = reader.bool(); + continue; + } + case 2: { + if (tag === 16) { + message.enabledVariants.push(reader.int32() as any); + + continue; + } + + if (tag === 18) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.enabledVariants.push(reader.int32() as any); + } + + continue; + } + + break; + } + case 3: { + if (tag === 24) { + message.disabledVariants.push(reader.int32() as any); + + continue; + } + + if (tag === 26) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.disabledVariants.push(reader.int32() as any); + } + + continue; + } + + break; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_Track_SimulcastConfig { + return { + enabled: isSet(object.enabled) ? globalThis.Boolean(object.enabled) : false, + enabledVariants: globalThis.Array.isArray(object?.enabledVariants) + ? object.enabledVariants.map((e: any) => variantFromJSON(e)) + : [], + disabledVariants: globalThis.Array.isArray(object?.disabledVariants) + ? object.disabledVariants.map((e: any) => variantFromJSON(e)) + : [], + }; + }, + + toJSON(message: MediaEvent_Track_SimulcastConfig): unknown { + const obj: any = {}; + if (message.enabled !== false) { + obj.enabled = message.enabled; + } + if (message.enabledVariants?.length) { + obj.enabledVariants = message.enabledVariants.map((e) => variantToJSON(e)); + } + if (message.disabledVariants?.length) { + obj.disabledVariants = message.disabledVariants.map((e) => variantToJSON(e)); + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_Track_SimulcastConfig { + return MediaEvent_Track_SimulcastConfig.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_Track_SimulcastConfig { + const message = createBaseMediaEvent_Track_SimulcastConfig(); + message.enabled = object.enabled ?? false; + message.enabledVariants = object.enabledVariants?.map((e) => e) || []; + message.disabledVariants = object.disabledVariants?.map((e) => e) || []; + return message; + }, +}; + +function createBaseMediaEvent_Endpoint(): MediaEvent_Endpoint { + return { endpointType: "", metadataJson: "", trackIdToTrack: {} }; +} + +export const MediaEvent_Endpoint: MessageFns = { + encode(message: MediaEvent_Endpoint, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointType !== "") { + writer.uint32(18).string(message.endpointType); + } + if (message.metadataJson !== "") { + writer.uint32(26).string(message.metadataJson); + } + Object.entries(message.trackIdToTrack).forEach(([key, value]) => { + MediaEvent_Endpoint_TrackIdToTrackEntry.encode({ key: key as any, value }, writer.uint32(34).fork()).join(); + }); + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_Endpoint { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_Endpoint(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 2: { + if (tag !== 18) { + break; + } + + message.endpointType = reader.string(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.metadataJson = reader.string(); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + const entry4 = MediaEvent_Endpoint_TrackIdToTrackEntry.decode(reader, reader.uint32()); + if (entry4.value !== undefined) { + message.trackIdToTrack[entry4.key] = entry4.value; + } + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_Endpoint { + return { + endpointType: isSet(object.endpointType) ? globalThis.String(object.endpointType) : "", + metadataJson: isSet(object.metadataJson) ? globalThis.String(object.metadataJson) : "", + trackIdToTrack: isObject(object.trackIdToTrack) + ? Object.entries(object.trackIdToTrack).reduce<{ [key: string]: MediaEvent_Track }>((acc, [key, value]) => { + acc[key] = MediaEvent_Track.fromJSON(value); + return acc; + }, {}) + : {}, + }; + }, + + toJSON(message: MediaEvent_Endpoint): unknown { + const obj: any = {}; + if (message.endpointType !== "") { + obj.endpointType = message.endpointType; + } + if (message.metadataJson !== "") { + obj.metadataJson = message.metadataJson; + } + if (message.trackIdToTrack) { + const entries = Object.entries(message.trackIdToTrack); + if (entries.length > 0) { + obj.trackIdToTrack = {}; + entries.forEach(([k, v]) => { + obj.trackIdToTrack[k] = MediaEvent_Track.toJSON(v); + }); + } + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_Endpoint { + return MediaEvent_Endpoint.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_Endpoint { + const message = createBaseMediaEvent_Endpoint(); + message.endpointType = object.endpointType ?? ""; + message.metadataJson = object.metadataJson ?? ""; + message.trackIdToTrack = Object.entries(object.trackIdToTrack ?? {}).reduce<{ [key: string]: MediaEvent_Track }>( + (acc, [key, value]) => { + if (value !== undefined) { + acc[key] = MediaEvent_Track.fromPartial(value); + } + return acc; + }, + {}, + ); + return message; + }, +}; + +function createBaseMediaEvent_Endpoint_TrackIdToTrackEntry(): MediaEvent_Endpoint_TrackIdToTrackEntry { + return { key: "", value: undefined }; +} + +export const MediaEvent_Endpoint_TrackIdToTrackEntry: MessageFns = { + encode(message: MediaEvent_Endpoint_TrackIdToTrackEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + MediaEvent_Track.encode(message.value, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_Endpoint_TrackIdToTrackEntry { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_Endpoint_TrackIdToTrackEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.value = MediaEvent_Track.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_Endpoint_TrackIdToTrackEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? MediaEvent_Track.fromJSON(object.value) : undefined, + }; + }, + + toJSON(message: MediaEvent_Endpoint_TrackIdToTrackEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== undefined) { + obj.value = MediaEvent_Track.toJSON(message.value); + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_Endpoint_TrackIdToTrackEntry { + return MediaEvent_Endpoint_TrackIdToTrackEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_Endpoint_TrackIdToTrackEntry { + const message = createBaseMediaEvent_Endpoint_TrackIdToTrackEntry(); + message.key = object.key ?? ""; + message.value = (object.value !== undefined && object.value !== null) + ? MediaEvent_Track.fromPartial(object.value) + : undefined; + return message; + }, +}; + +function createBaseMediaEvent_IceServer(): MediaEvent_IceServer { + return { credential: "", urls: [], username: "" }; +} + +export const MediaEvent_IceServer: MessageFns = { + encode(message: MediaEvent_IceServer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.credential !== "") { + writer.uint32(10).string(message.credential); + } + for (const v of message.urls) { + writer.uint32(18).string(v!); + } + if (message.username !== "") { + writer.uint32(26).string(message.username); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_IceServer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_IceServer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.credential = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.urls.push(reader.string()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.username = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_IceServer { + return { + credential: isSet(object.credential) ? globalThis.String(object.credential) : "", + urls: globalThis.Array.isArray(object?.urls) ? object.urls.map((e: any) => globalThis.String(e)) : [], + username: isSet(object.username) ? globalThis.String(object.username) : "", + }; + }, + + toJSON(message: MediaEvent_IceServer): unknown { + const obj: any = {}; + if (message.credential !== "") { + obj.credential = message.credential; + } + if (message.urls?.length) { + obj.urls = message.urls; + } + if (message.username !== "") { + obj.username = message.username; + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_IceServer { + return MediaEvent_IceServer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_IceServer { + const message = createBaseMediaEvent_IceServer(); + message.credential = object.credential ?? ""; + message.urls = object.urls?.map((e) => e) || []; + message.username = object.username ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_EndpointUpdated(): MediaEvent_EndpointUpdated { + return { endpointId: "", metadataJson: "" }; +} + +export const MediaEvent_EndpointUpdated: MessageFns = { + encode(message: MediaEvent_EndpointUpdated, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + if (message.metadataJson !== "") { + writer.uint32(18).string(message.metadataJson); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_EndpointUpdated { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_EndpointUpdated(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.metadataJson = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_EndpointUpdated { + return { + endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "", + metadataJson: isSet(object.metadataJson) ? globalThis.String(object.metadataJson) : "", + }; + }, + + toJSON(message: MediaEvent_EndpointUpdated): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + if (message.metadataJson !== "") { + obj.metadataJson = message.metadataJson; + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_EndpointUpdated { + return MediaEvent_EndpointUpdated.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_EndpointUpdated { + const message = createBaseMediaEvent_EndpointUpdated(); + message.endpointId = object.endpointId ?? ""; + message.metadataJson = object.metadataJson ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_TrackUpdated(): MediaEvent_TrackUpdated { + return { endpointId: "", trackId: "", metadataJson: "" }; +} + +export const MediaEvent_TrackUpdated: MessageFns = { + encode(message: MediaEvent_TrackUpdated, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + if (message.trackId !== "") { + writer.uint32(18).string(message.trackId); + } + if (message.metadataJson !== "") { + writer.uint32(26).string(message.metadataJson); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_TrackUpdated { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_TrackUpdated(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.metadataJson = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_TrackUpdated { + return { + endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "", + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + metadataJson: isSet(object.metadataJson) ? globalThis.String(object.metadataJson) : "", + }; + }, + + toJSON(message: MediaEvent_TrackUpdated): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.metadataJson !== "") { + obj.metadataJson = message.metadataJson; + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_TrackUpdated { + return MediaEvent_TrackUpdated.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_TrackUpdated { + const message = createBaseMediaEvent_TrackUpdated(); + message.endpointId = object.endpointId ?? ""; + message.trackId = object.trackId ?? ""; + message.metadataJson = object.metadataJson ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_TracksAdded(): MediaEvent_TracksAdded { + return { endpointId: "", trackIdToTrack: {} }; +} + +export const MediaEvent_TracksAdded: MessageFns = { + encode(message: MediaEvent_TracksAdded, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + Object.entries(message.trackIdToTrack).forEach(([key, value]) => { + MediaEvent_TracksAdded_TrackIdToTrackEntry.encode({ key: key as any, value }, writer.uint32(18).fork()).join(); + }); + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_TracksAdded { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_TracksAdded(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + const entry2 = MediaEvent_TracksAdded_TrackIdToTrackEntry.decode(reader, reader.uint32()); + if (entry2.value !== undefined) { + message.trackIdToTrack[entry2.key] = entry2.value; + } + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_TracksAdded { + return { + endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "", + trackIdToTrack: isObject(object.trackIdToTrack) + ? Object.entries(object.trackIdToTrack).reduce<{ [key: string]: MediaEvent_Track }>((acc, [key, value]) => { + acc[key] = MediaEvent_Track.fromJSON(value); + return acc; + }, {}) + : {}, + }; + }, + + toJSON(message: MediaEvent_TracksAdded): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + if (message.trackIdToTrack) { + const entries = Object.entries(message.trackIdToTrack); + if (entries.length > 0) { + obj.trackIdToTrack = {}; + entries.forEach(([k, v]) => { + obj.trackIdToTrack[k] = MediaEvent_Track.toJSON(v); + }); + } + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_TracksAdded { + return MediaEvent_TracksAdded.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_TracksAdded { + const message = createBaseMediaEvent_TracksAdded(); + message.endpointId = object.endpointId ?? ""; + message.trackIdToTrack = Object.entries(object.trackIdToTrack ?? {}).reduce<{ [key: string]: MediaEvent_Track }>( + (acc, [key, value]) => { + if (value !== undefined) { + acc[key] = MediaEvent_Track.fromPartial(value); + } + return acc; + }, + {}, + ); + return message; + }, +}; + +function createBaseMediaEvent_TracksAdded_TrackIdToTrackEntry(): MediaEvent_TracksAdded_TrackIdToTrackEntry { + return { key: "", value: undefined }; +} + +export const MediaEvent_TracksAdded_TrackIdToTrackEntry: MessageFns = { + encode(message: MediaEvent_TracksAdded_TrackIdToTrackEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + MediaEvent_Track.encode(message.value, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_TracksAdded_TrackIdToTrackEntry { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_TracksAdded_TrackIdToTrackEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.value = MediaEvent_Track.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_TracksAdded_TrackIdToTrackEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? MediaEvent_Track.fromJSON(object.value) : undefined, + }; + }, + + toJSON(message: MediaEvent_TracksAdded_TrackIdToTrackEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== undefined) { + obj.value = MediaEvent_Track.toJSON(message.value); + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_TracksAdded_TrackIdToTrackEntry { + return MediaEvent_TracksAdded_TrackIdToTrackEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_TracksAdded_TrackIdToTrackEntry { + const message = createBaseMediaEvent_TracksAdded_TrackIdToTrackEntry(); + message.key = object.key ?? ""; + message.value = (object.value !== undefined && object.value !== null) + ? MediaEvent_Track.fromPartial(object.value) + : undefined; + return message; + }, +}; + +function createBaseMediaEvent_TracksRemoved(): MediaEvent_TracksRemoved { + return { endpointId: "", trackIds: [] }; +} + +export const MediaEvent_TracksRemoved: MessageFns = { + encode(message: MediaEvent_TracksRemoved, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + for (const v of message.trackIds) { + writer.uint32(18).string(v!); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_TracksRemoved { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_TracksRemoved(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.trackIds.push(reader.string()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_TracksRemoved { + return { + endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "", + trackIds: globalThis.Array.isArray(object?.trackIds) ? object.trackIds.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: MediaEvent_TracksRemoved): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + if (message.trackIds?.length) { + obj.trackIds = message.trackIds; + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_TracksRemoved { + return MediaEvent_TracksRemoved.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_TracksRemoved { + const message = createBaseMediaEvent_TracksRemoved(); + message.endpointId = object.endpointId ?? ""; + message.trackIds = object.trackIds?.map((e) => e) || []; + return message; + }, +}; + +function createBaseMediaEvent_EndpointAdded(): MediaEvent_EndpointAdded { + return { endpointId: "", metadataJson: "" }; +} + +export const MediaEvent_EndpointAdded: MessageFns = { + encode(message: MediaEvent_EndpointAdded, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + if (message.metadataJson !== "") { + writer.uint32(18).string(message.metadataJson); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_EndpointAdded { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_EndpointAdded(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.metadataJson = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_EndpointAdded { + return { + endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "", + metadataJson: isSet(object.metadataJson) ? globalThis.String(object.metadataJson) : "", + }; + }, + + toJSON(message: MediaEvent_EndpointAdded): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + if (message.metadataJson !== "") { + obj.metadataJson = message.metadataJson; + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_EndpointAdded { + return MediaEvent_EndpointAdded.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_EndpointAdded { + const message = createBaseMediaEvent_EndpointAdded(); + message.endpointId = object.endpointId ?? ""; + message.metadataJson = object.metadataJson ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_Connected(): MediaEvent_Connected { + return { endpointId: "", endpointIdToEndpoint: {}, iceServers: [] }; +} + +export const MediaEvent_Connected: MessageFns = { + encode(message: MediaEvent_Connected, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + Object.entries(message.endpointIdToEndpoint).forEach(([key, value]) => { + MediaEvent_Connected_EndpointIdToEndpointEntry.encode({ key: key as any, value }, writer.uint32(18).fork()) + .join(); + }); + for (const v of message.iceServers) { + MediaEvent_IceServer.encode(v!, writer.uint32(26).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_Connected { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_Connected(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + const entry2 = MediaEvent_Connected_EndpointIdToEndpointEntry.decode(reader, reader.uint32()); + if (entry2.value !== undefined) { + message.endpointIdToEndpoint[entry2.key] = entry2.value; + } + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.iceServers.push(MediaEvent_IceServer.decode(reader, reader.uint32())); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_Connected { + return { + endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "", + endpointIdToEndpoint: isObject(object.endpointIdToEndpoint) + ? Object.entries(object.endpointIdToEndpoint).reduce<{ [key: string]: MediaEvent_Endpoint }>( + (acc, [key, value]) => { + acc[key] = MediaEvent_Endpoint.fromJSON(value); + return acc; + }, + {}, + ) + : {}, + iceServers: globalThis.Array.isArray(object?.iceServers) + ? object.iceServers.map((e: any) => MediaEvent_IceServer.fromJSON(e)) + : [], + }; + }, + + toJSON(message: MediaEvent_Connected): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + if (message.endpointIdToEndpoint) { + const entries = Object.entries(message.endpointIdToEndpoint); + if (entries.length > 0) { + obj.endpointIdToEndpoint = {}; + entries.forEach(([k, v]) => { + obj.endpointIdToEndpoint[k] = MediaEvent_Endpoint.toJSON(v); + }); + } + } + if (message.iceServers?.length) { + obj.iceServers = message.iceServers.map((e) => MediaEvent_IceServer.toJSON(e)); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_Connected { + return MediaEvent_Connected.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_Connected { + const message = createBaseMediaEvent_Connected(); + message.endpointId = object.endpointId ?? ""; + message.endpointIdToEndpoint = Object.entries(object.endpointIdToEndpoint ?? {}).reduce< + { [key: string]: MediaEvent_Endpoint } + >((acc, [key, value]) => { + if (value !== undefined) { + acc[key] = MediaEvent_Endpoint.fromPartial(value); + } + return acc; + }, {}); + message.iceServers = object.iceServers?.map((e) => MediaEvent_IceServer.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseMediaEvent_Connected_EndpointIdToEndpointEntry(): MediaEvent_Connected_EndpointIdToEndpointEntry { + return { key: "", value: undefined }; +} + +export const MediaEvent_Connected_EndpointIdToEndpointEntry: MessageFns< + MediaEvent_Connected_EndpointIdToEndpointEntry +> = { + encode( + message: MediaEvent_Connected_EndpointIdToEndpointEntry, + writer: BinaryWriter = new BinaryWriter(), + ): BinaryWriter { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + MediaEvent_Endpoint.encode(message.value, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_Connected_EndpointIdToEndpointEntry { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_Connected_EndpointIdToEndpointEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.value = MediaEvent_Endpoint.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_Connected_EndpointIdToEndpointEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? MediaEvent_Endpoint.fromJSON(object.value) : undefined, + }; + }, + + toJSON(message: MediaEvent_Connected_EndpointIdToEndpointEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== undefined) { + obj.value = MediaEvent_Endpoint.toJSON(message.value); + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_Connected_EndpointIdToEndpointEntry { + return MediaEvent_Connected_EndpointIdToEndpointEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_Connected_EndpointIdToEndpointEntry { + const message = createBaseMediaEvent_Connected_EndpointIdToEndpointEntry(); + message.key = object.key ?? ""; + message.value = (object.value !== undefined && object.value !== null) + ? MediaEvent_Endpoint.fromPartial(object.value) + : undefined; + return message; + }, +}; + +function createBaseMediaEvent_EndpointRemoved(): MediaEvent_EndpointRemoved { + return { endpointId: "" }; +} + +export const MediaEvent_EndpointRemoved: MessageFns = { + encode(message: MediaEvent_EndpointRemoved, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_EndpointRemoved { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_EndpointRemoved(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_EndpointRemoved { + return { endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "" }; + }, + + toJSON(message: MediaEvent_EndpointRemoved): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_EndpointRemoved { + return MediaEvent_EndpointRemoved.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_EndpointRemoved { + const message = createBaseMediaEvent_EndpointRemoved(); + message.endpointId = object.endpointId ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_Error(): MediaEvent_Error { + return { message: "" }; +} + +export const MediaEvent_Error: MessageFns = { + encode(message: MediaEvent_Error, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.message !== "") { + writer.uint32(10).string(message.message); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_Error { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_Error(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.message = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_Error { + return { message: isSet(object.message) ? globalThis.String(object.message) : "" }; + }, + + toJSON(message: MediaEvent_Error): unknown { + const obj: any = {}; + if (message.message !== "") { + obj.message = message.message; + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_Error { + return MediaEvent_Error.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_Error { + const message = createBaseMediaEvent_Error(); + message.message = object.message ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_OfferData(): MediaEvent_OfferData { + return { tracksTypes: undefined }; +} + +export const MediaEvent_OfferData: MessageFns = { + encode(message: MediaEvent_OfferData, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.tracksTypes !== undefined) { + MediaEvent_OfferData_TrackTypes.encode(message.tracksTypes, writer.uint32(10).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_OfferData { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_OfferData(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.tracksTypes = MediaEvent_OfferData_TrackTypes.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_OfferData { + return { + tracksTypes: isSet(object.tracksTypes) ? MediaEvent_OfferData_TrackTypes.fromJSON(object.tracksTypes) : undefined, + }; + }, + + toJSON(message: MediaEvent_OfferData): unknown { + const obj: any = {}; + if (message.tracksTypes !== undefined) { + obj.tracksTypes = MediaEvent_OfferData_TrackTypes.toJSON(message.tracksTypes); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_OfferData { + return MediaEvent_OfferData.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_OfferData { + const message = createBaseMediaEvent_OfferData(); + message.tracksTypes = (object.tracksTypes !== undefined && object.tracksTypes !== null) + ? MediaEvent_OfferData_TrackTypes.fromPartial(object.tracksTypes) + : undefined; + return message; + }, +}; + +function createBaseMediaEvent_OfferData_TrackTypes(): MediaEvent_OfferData_TrackTypes { + return { audio: 0, video: 0 }; +} + +export const MediaEvent_OfferData_TrackTypes: MessageFns = { + encode(message: MediaEvent_OfferData_TrackTypes, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.audio !== 0) { + writer.uint32(8).int32(message.audio); + } + if (message.video !== 0) { + writer.uint32(16).int32(message.video); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_OfferData_TrackTypes { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_OfferData_TrackTypes(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.audio = reader.int32(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.video = reader.int32(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_OfferData_TrackTypes { + return { + audio: isSet(object.audio) ? globalThis.Number(object.audio) : 0, + video: isSet(object.video) ? globalThis.Number(object.video) : 0, + }; + }, + + toJSON(message: MediaEvent_OfferData_TrackTypes): unknown { + const obj: any = {}; + if (message.audio !== 0) { + obj.audio = Math.round(message.audio); + } + if (message.video !== 0) { + obj.video = Math.round(message.video); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_OfferData_TrackTypes { + return MediaEvent_OfferData_TrackTypes.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_OfferData_TrackTypes { + const message = createBaseMediaEvent_OfferData_TrackTypes(); + message.audio = object.audio ?? 0; + message.video = object.video ?? 0; + return message; + }, +}; + +function createBaseMediaEvent_SdpAnswer(): MediaEvent_SdpAnswer { + return { sdp: "", midToTrackId: {} }; +} + +export const MediaEvent_SdpAnswer: MessageFns = { + encode(message: MediaEvent_SdpAnswer, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.sdp !== "") { + writer.uint32(10).string(message.sdp); + } + Object.entries(message.midToTrackId).forEach(([key, value]) => { + MediaEvent_SdpAnswer_MidToTrackIdEntry.encode({ key: key as any, value }, writer.uint32(18).fork()).join(); + }); + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_SdpAnswer { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_SdpAnswer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.sdp = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + const entry2 = MediaEvent_SdpAnswer_MidToTrackIdEntry.decode(reader, reader.uint32()); + if (entry2.value !== undefined) { + message.midToTrackId[entry2.key] = entry2.value; + } + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_SdpAnswer { + return { + sdp: isSet(object.sdp) ? globalThis.String(object.sdp) : "", + midToTrackId: isObject(object.midToTrackId) + ? Object.entries(object.midToTrackId).reduce<{ [key: string]: string }>((acc, [key, value]) => { + acc[key] = String(value); + return acc; + }, {}) + : {}, + }; + }, + + toJSON(message: MediaEvent_SdpAnswer): unknown { + const obj: any = {}; + if (message.sdp !== "") { + obj.sdp = message.sdp; + } + if (message.midToTrackId) { + const entries = Object.entries(message.midToTrackId); + if (entries.length > 0) { + obj.midToTrackId = {}; + entries.forEach(([k, v]) => { + obj.midToTrackId[k] = v; + }); + } + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_SdpAnswer { + return MediaEvent_SdpAnswer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_SdpAnswer { + const message = createBaseMediaEvent_SdpAnswer(); + message.sdp = object.sdp ?? ""; + message.midToTrackId = Object.entries(object.midToTrackId ?? {}).reduce<{ [key: string]: string }>( + (acc, [key, value]) => { + if (value !== undefined) { + acc[key] = globalThis.String(value); + } + return acc; + }, + {}, + ); + return message; + }, +}; + +function createBaseMediaEvent_SdpAnswer_MidToTrackIdEntry(): MediaEvent_SdpAnswer_MidToTrackIdEntry { + return { key: "", value: "" }; +} + +export const MediaEvent_SdpAnswer_MidToTrackIdEntry: MessageFns = { + encode(message: MediaEvent_SdpAnswer_MidToTrackIdEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== "") { + writer.uint32(18).string(message.value); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_SdpAnswer_MidToTrackIdEntry { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_SdpAnswer_MidToTrackIdEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.value = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_SdpAnswer_MidToTrackIdEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? globalThis.String(object.value) : "", + }; + }, + + toJSON(message: MediaEvent_SdpAnswer_MidToTrackIdEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== "") { + obj.value = message.value; + } + return obj; + }, + + create, I>>( + base?: I, + ): MediaEvent_SdpAnswer_MidToTrackIdEntry { + return MediaEvent_SdpAnswer_MidToTrackIdEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_SdpAnswer_MidToTrackIdEntry { + const message = createBaseMediaEvent_SdpAnswer_MidToTrackIdEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? ""; + return message; + }, +}; + +function createBaseMediaEvent_VadNotification(): MediaEvent_VadNotification { + return { trackId: "", status: 0 }; +} + +export const MediaEvent_VadNotification: MessageFns = { + encode(message: MediaEvent_VadNotification, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.trackId !== "") { + writer.uint32(10).string(message.trackId); + } + if (message.status !== 0) { + writer.uint32(16).int32(message.status); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_VadNotification { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_VadNotification(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.status = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_VadNotification { + return { + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + status: isSet(object.status) ? mediaEvent_VadNotification_StatusFromJSON(object.status) : 0, + }; + }, + + toJSON(message: MediaEvent_VadNotification): unknown { + const obj: any = {}; + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.status !== 0) { + obj.status = mediaEvent_VadNotification_StatusToJSON(message.status); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_VadNotification { + return MediaEvent_VadNotification.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MediaEvent_VadNotification { + const message = createBaseMediaEvent_VadNotification(); + message.trackId = object.trackId ?? ""; + message.status = object.status ?? 0; + return message; + }, +}; + +function createBaseMediaEvent_TrackVariantSwitched(): MediaEvent_TrackVariantSwitched { + return { endpointId: "", trackId: "", variant: 0 }; +} + +export const MediaEvent_TrackVariantSwitched: MessageFns = { + encode(message: MediaEvent_TrackVariantSwitched, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + if (message.trackId !== "") { + writer.uint32(18).string(message.trackId); + } + if (message.variant !== 0) { + writer.uint32(24).int32(message.variant); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_TrackVariantSwitched { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_TrackVariantSwitched(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 3: { + if (tag !== 24) { + break; + } + + message.variant = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_TrackVariantSwitched { + return { + endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "", + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + variant: isSet(object.variant) ? variantFromJSON(object.variant) : 0, + }; + }, + + toJSON(message: MediaEvent_TrackVariantSwitched): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.variant !== 0) { + obj.variant = variantToJSON(message.variant); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_TrackVariantSwitched { + return MediaEvent_TrackVariantSwitched.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_TrackVariantSwitched { + const message = createBaseMediaEvent_TrackVariantSwitched(); + message.endpointId = object.endpointId ?? ""; + message.trackId = object.trackId ?? ""; + message.variant = object.variant ?? 0; + return message; + }, +}; + +function createBaseMediaEvent_TrackVariantDisabled(): MediaEvent_TrackVariantDisabled { + return { endpointId: "", trackId: "", variant: 0 }; +} + +export const MediaEvent_TrackVariantDisabled: MessageFns = { + encode(message: MediaEvent_TrackVariantDisabled, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + if (message.trackId !== "") { + writer.uint32(18).string(message.trackId); + } + if (message.variant !== 0) { + writer.uint32(24).int32(message.variant); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_TrackVariantDisabled { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_TrackVariantDisabled(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 3: { + if (tag !== 24) { + break; + } + + message.variant = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_TrackVariantDisabled { + return { + endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "", + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + variant: isSet(object.variant) ? variantFromJSON(object.variant) : 0, + }; + }, + + toJSON(message: MediaEvent_TrackVariantDisabled): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.variant !== 0) { + obj.variant = variantToJSON(message.variant); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_TrackVariantDisabled { + return MediaEvent_TrackVariantDisabled.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_TrackVariantDisabled { + const message = createBaseMediaEvent_TrackVariantDisabled(); + message.endpointId = object.endpointId ?? ""; + message.trackId = object.trackId ?? ""; + message.variant = object.variant ?? 0; + return message; + }, +}; + +function createBaseMediaEvent_TrackVariantEnabled(): MediaEvent_TrackVariantEnabled { + return { endpointId: "", trackId: "", variant: 0 }; +} + +export const MediaEvent_TrackVariantEnabled: MessageFns = { + encode(message: MediaEvent_TrackVariantEnabled, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.endpointId !== "") { + writer.uint32(10).string(message.endpointId); + } + if (message.trackId !== "") { + writer.uint32(18).string(message.trackId); + } + if (message.variant !== 0) { + writer.uint32(24).int32(message.variant); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): MediaEvent_TrackVariantEnabled { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMediaEvent_TrackVariantEnabled(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.endpointId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.trackId = reader.string(); + continue; + } + case 3: { + if (tag !== 24) { + break; + } + + message.variant = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): MediaEvent_TrackVariantEnabled { + return { + endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : "", + trackId: isSet(object.trackId) ? globalThis.String(object.trackId) : "", + variant: isSet(object.variant) ? variantFromJSON(object.variant) : 0, + }; + }, + + toJSON(message: MediaEvent_TrackVariantEnabled): unknown { + const obj: any = {}; + if (message.endpointId !== "") { + obj.endpointId = message.endpointId; + } + if (message.trackId !== "") { + obj.trackId = message.trackId; + } + if (message.variant !== 0) { + obj.variant = variantToJSON(message.variant); + } + return obj; + }, + + create, I>>(base?: I): MediaEvent_TrackVariantEnabled { + return MediaEvent_TrackVariantEnabled.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): MediaEvent_TrackVariantEnabled { + const message = createBaseMediaEvent_TrackVariantEnabled(); + message.endpointId = object.endpointId ?? ""; + message.trackId = object.trackId ?? ""; + message.variant = object.variant ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/packages/protobufs/fishjam/media_events/shared.ts b/packages/protobufs/fishjam/media_events/shared.ts new file mode 100644 index 00000000..c468061b --- /dev/null +++ b/packages/protobufs/fishjam/media_events/shared.ts @@ -0,0 +1,196 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.4.0 +// protoc v5.28.2 +// source: fishjam/media_events/shared.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; + +export const protobufPackage = "fishjam.media_events"; + +export enum Variant { + VARIANT_UNSPECIFIED = 0, + VARIANT_LOW = 1, + VARIANT_MEDIUM = 2, + VARIANT_HIGH = 3, + UNRECOGNIZED = -1, +} + +export function variantFromJSON(object: any): Variant { + switch (object) { + case 0: + case "VARIANT_UNSPECIFIED": + return Variant.VARIANT_UNSPECIFIED; + case 1: + case "VARIANT_LOW": + return Variant.VARIANT_LOW; + case 2: + case "VARIANT_MEDIUM": + return Variant.VARIANT_MEDIUM; + case 3: + case "VARIANT_HIGH": + return Variant.VARIANT_HIGH; + case -1: + case "UNRECOGNIZED": + default: + return Variant.UNRECOGNIZED; + } +} + +export function variantToJSON(object: Variant): string { + switch (object) { + case Variant.VARIANT_UNSPECIFIED: + return "VARIANT_UNSPECIFIED"; + case Variant.VARIANT_LOW: + return "VARIANT_LOW"; + case Variant.VARIANT_MEDIUM: + return "VARIANT_MEDIUM"; + case Variant.VARIANT_HIGH: + return "VARIANT_HIGH"; + case Variant.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +/** Contains information about an ICE candidate which will be sent to the peer/server */ +export interface Candidate { + candidate: string; + sdpMLineIndex: number; + sdpMid: string; + usernameFragment: string; +} + +function createBaseCandidate(): Candidate { + return { candidate: "", sdpMLineIndex: 0, sdpMid: "", usernameFragment: "" }; +} + +export const Candidate: MessageFns = { + encode(message: Candidate, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.candidate !== "") { + writer.uint32(10).string(message.candidate); + } + if (message.sdpMLineIndex !== 0) { + writer.uint32(16).int32(message.sdpMLineIndex); + } + if (message.sdpMid !== "") { + writer.uint32(26).string(message.sdpMid); + } + if (message.usernameFragment !== "") { + writer.uint32(34).string(message.usernameFragment); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): Candidate { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseCandidate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.candidate = reader.string(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.sdpMLineIndex = reader.int32(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.sdpMid = reader.string(); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.usernameFragment = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): Candidate { + return { + candidate: isSet(object.candidate) ? globalThis.String(object.candidate) : "", + sdpMLineIndex: isSet(object.sdpMLineIndex) ? globalThis.Number(object.sdpMLineIndex) : 0, + sdpMid: isSet(object.sdpMid) ? globalThis.String(object.sdpMid) : "", + usernameFragment: isSet(object.usernameFragment) ? globalThis.String(object.usernameFragment) : "", + }; + }, + + toJSON(message: Candidate): unknown { + const obj: any = {}; + if (message.candidate !== "") { + obj.candidate = message.candidate; + } + if (message.sdpMLineIndex !== 0) { + obj.sdpMLineIndex = Math.round(message.sdpMLineIndex); + } + if (message.sdpMid !== "") { + obj.sdpMid = message.sdpMid; + } + if (message.usernameFragment !== "") { + obj.usernameFragment = message.usernameFragment; + } + return obj; + }, + + create, I>>(base?: I): Candidate { + return Candidate.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Candidate { + const message = createBaseCandidate(); + message.candidate = object.candidate ?? ""; + message.sdpMLineIndex = object.sdpMLineIndex ?? 0; + message.sdpMid = object.sdpMid ?? ""; + message.usernameFragment = object.usernameFragment ?? ""; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/packages/ts-client/src/protos/fishjam/peer_notifications.ts b/packages/protobufs/fishjam/peer_notifications.ts similarity index 83% rename from packages/ts-client/src/protos/fishjam/peer_notifications.ts rename to packages/protobufs/fishjam/peer_notifications.ts index 1847d0d2..1968c26a 100644 --- a/packages/ts-client/src/protos/fishjam/peer_notifications.ts +++ b/packages/protobufs/fishjam/peer_notifications.ts @@ -1,11 +1,13 @@ // Code generated by protoc-gen-ts_proto. DO NOT EDIT. // versions: -// protoc-gen-ts_proto v2.2.3 -// protoc v5.26.1 -// source: protos/fishjam/peer_notifications.proto +// protoc-gen-ts_proto v2.4.0 +// protoc v5.28.2 +// source: fishjam/peer_notifications.proto /* eslint-disable */ import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; +import { MediaEvent } from "./media_events/peer/peer"; +import { MediaEvent as MediaEvent1 } from "./media_events/server/server"; export const protobufPackage = "fishjam"; @@ -15,6 +17,8 @@ export interface PeerMessage { authRequest?: PeerMessage_AuthRequest | undefined; mediaEvent?: PeerMessage_MediaEvent | undefined; rtcStatsReport?: PeerMessage_RTCStatsReport | undefined; + peerMediaEvent?: MediaEvent | undefined; + serverMediaEvent?: MediaEvent1 | undefined; } /** Response sent by FJ, confirming successfull authentication */ @@ -24,11 +28,7 @@ export interface PeerMessage_Authenticated { /** Request sent by peer, to authenticate to FJ server */ export interface PeerMessage_AuthRequest { token: string; -} - -/** Any type of WebRTC messages passed betweend FJ and peer */ -export interface PeerMessage_MediaEvent { - data: string; + sdkVersion: string; } /** @@ -39,8 +39,20 @@ export interface PeerMessage_RTCStatsReport { data: string; } +/** Any type of WebRTC messages passed betweend FJ and peer */ +export interface PeerMessage_MediaEvent { + data: string; +} + function createBasePeerMessage(): PeerMessage { - return { authenticated: undefined, authRequest: undefined, mediaEvent: undefined, rtcStatsReport: undefined }; + return { + authenticated: undefined, + authRequest: undefined, + mediaEvent: undefined, + rtcStatsReport: undefined, + peerMediaEvent: undefined, + serverMediaEvent: undefined, + }; } export const PeerMessage: MessageFns = { @@ -57,6 +69,12 @@ export const PeerMessage: MessageFns = { if (message.rtcStatsReport !== undefined) { PeerMessage_RTCStatsReport.encode(message.rtcStatsReport, writer.uint32(34).fork()).join(); } + if (message.peerMediaEvent !== undefined) { + MediaEvent.encode(message.peerMediaEvent, writer.uint32(42).fork()).join(); + } + if (message.serverMediaEvent !== undefined) { + MediaEvent1.encode(message.serverMediaEvent, writer.uint32(50).fork()).join(); + } return writer; }, @@ -99,6 +117,22 @@ export const PeerMessage: MessageFns = { message.rtcStatsReport = PeerMessage_RTCStatsReport.decode(reader, reader.uint32()); continue; } + case 5: { + if (tag !== 42) { + break; + } + + message.peerMediaEvent = MediaEvent.decode(reader, reader.uint32()); + continue; + } + case 6: { + if (tag !== 50) { + break; + } + + message.serverMediaEvent = MediaEvent1.decode(reader, reader.uint32()); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -116,6 +150,8 @@ export const PeerMessage: MessageFns = { rtcStatsReport: isSet(object.rtcStatsReport) ? PeerMessage_RTCStatsReport.fromJSON(object.rtcStatsReport) : undefined, + peerMediaEvent: isSet(object.peerMediaEvent) ? MediaEvent.fromJSON(object.peerMediaEvent) : undefined, + serverMediaEvent: isSet(object.serverMediaEvent) ? MediaEvent1.fromJSON(object.serverMediaEvent) : undefined, }; }, @@ -133,6 +169,12 @@ export const PeerMessage: MessageFns = { if (message.rtcStatsReport !== undefined) { obj.rtcStatsReport = PeerMessage_RTCStatsReport.toJSON(message.rtcStatsReport); } + if (message.peerMediaEvent !== undefined) { + obj.peerMediaEvent = MediaEvent.toJSON(message.peerMediaEvent); + } + if (message.serverMediaEvent !== undefined) { + obj.serverMediaEvent = MediaEvent1.toJSON(message.serverMediaEvent); + } return obj; }, @@ -153,6 +195,12 @@ export const PeerMessage: MessageFns = { message.rtcStatsReport = (object.rtcStatsReport !== undefined && object.rtcStatsReport !== null) ? PeerMessage_RTCStatsReport.fromPartial(object.rtcStatsReport) : undefined; + message.peerMediaEvent = (object.peerMediaEvent !== undefined && object.peerMediaEvent !== null) + ? MediaEvent.fromPartial(object.peerMediaEvent) + : undefined; + message.serverMediaEvent = (object.serverMediaEvent !== undefined && object.serverMediaEvent !== null) + ? MediaEvent1.fromPartial(object.serverMediaEvent) + : undefined; return message; }, }; @@ -201,7 +249,7 @@ export const PeerMessage_Authenticated: MessageFns = }; function createBasePeerMessage_AuthRequest(): PeerMessage_AuthRequest { - return { token: "" }; + return { token: "", sdkVersion: "" }; } export const PeerMessage_AuthRequest: MessageFns = { @@ -209,6 +257,9 @@ export const PeerMessage_AuthRequest: MessageFns = { if (message.token !== "") { writer.uint32(10).string(message.token); } + if (message.sdkVersion !== "") { + writer.uint32(18).string(message.sdkVersion); + } return writer; }, @@ -227,6 +278,14 @@ export const PeerMessage_AuthRequest: MessageFns = { message.token = reader.string(); continue; } + case 2: { + if (tag !== 18) { + break; + } + + message.sdkVersion = reader.string(); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -237,7 +296,10 @@ export const PeerMessage_AuthRequest: MessageFns = { }, fromJSON(object: any): PeerMessage_AuthRequest { - return { token: isSet(object.token) ? globalThis.String(object.token) : "" }; + return { + token: isSet(object.token) ? globalThis.String(object.token) : "", + sdkVersion: isSet(object.sdkVersion) ? globalThis.String(object.sdkVersion) : "", + }; }, toJSON(message: PeerMessage_AuthRequest): unknown { @@ -245,6 +307,9 @@ export const PeerMessage_AuthRequest: MessageFns = { if (message.token !== "") { obj.token = message.token; } + if (message.sdkVersion !== "") { + obj.sdkVersion = message.sdkVersion; + } return obj; }, @@ -254,26 +319,27 @@ export const PeerMessage_AuthRequest: MessageFns = { fromPartial, I>>(object: I): PeerMessage_AuthRequest { const message = createBasePeerMessage_AuthRequest(); message.token = object.token ?? ""; + message.sdkVersion = object.sdkVersion ?? ""; return message; }, }; -function createBasePeerMessage_MediaEvent(): PeerMessage_MediaEvent { +function createBasePeerMessage_RTCStatsReport(): PeerMessage_RTCStatsReport { return { data: "" }; } -export const PeerMessage_MediaEvent: MessageFns = { - encode(message: PeerMessage_MediaEvent, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { +export const PeerMessage_RTCStatsReport: MessageFns = { + encode(message: PeerMessage_RTCStatsReport, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.data !== "") { writer.uint32(10).string(message.data); } return writer; }, - decode(input: BinaryReader | Uint8Array, length?: number): PeerMessage_MediaEvent { + decode(input: BinaryReader | Uint8Array, length?: number): PeerMessage_RTCStatsReport { const reader = input instanceof BinaryReader ? input : new BinaryReader(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBasePeerMessage_MediaEvent(); + const message = createBasePeerMessage_RTCStatsReport(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -294,11 +360,11 @@ export const PeerMessage_MediaEvent: MessageFns = { return message; }, - fromJSON(object: any): PeerMessage_MediaEvent { + fromJSON(object: any): PeerMessage_RTCStatsReport { return { data: isSet(object.data) ? globalThis.String(object.data) : "" }; }, - toJSON(message: PeerMessage_MediaEvent): unknown { + toJSON(message: PeerMessage_RTCStatsReport): unknown { const obj: any = {}; if (message.data !== "") { obj.data = message.data; @@ -306,32 +372,32 @@ export const PeerMessage_MediaEvent: MessageFns = { return obj; }, - create, I>>(base?: I): PeerMessage_MediaEvent { - return PeerMessage_MediaEvent.fromPartial(base ?? ({} as any)); + create, I>>(base?: I): PeerMessage_RTCStatsReport { + return PeerMessage_RTCStatsReport.fromPartial(base ?? ({} as any)); }, - fromPartial, I>>(object: I): PeerMessage_MediaEvent { - const message = createBasePeerMessage_MediaEvent(); + fromPartial, I>>(object: I): PeerMessage_RTCStatsReport { + const message = createBasePeerMessage_RTCStatsReport(); message.data = object.data ?? ""; return message; }, }; -function createBasePeerMessage_RTCStatsReport(): PeerMessage_RTCStatsReport { +function createBasePeerMessage_MediaEvent(): PeerMessage_MediaEvent { return { data: "" }; } -export const PeerMessage_RTCStatsReport: MessageFns = { - encode(message: PeerMessage_RTCStatsReport, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { +export const PeerMessage_MediaEvent: MessageFns = { + encode(message: PeerMessage_MediaEvent, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.data !== "") { writer.uint32(10).string(message.data); } return writer; }, - decode(input: BinaryReader | Uint8Array, length?: number): PeerMessage_RTCStatsReport { + decode(input: BinaryReader | Uint8Array, length?: number): PeerMessage_MediaEvent { const reader = input instanceof BinaryReader ? input : new BinaryReader(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBasePeerMessage_RTCStatsReport(); + const message = createBasePeerMessage_MediaEvent(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -352,11 +418,11 @@ export const PeerMessage_RTCStatsReport: MessageFns return message; }, - fromJSON(object: any): PeerMessage_RTCStatsReport { + fromJSON(object: any): PeerMessage_MediaEvent { return { data: isSet(object.data) ? globalThis.String(object.data) : "" }; }, - toJSON(message: PeerMessage_RTCStatsReport): unknown { + toJSON(message: PeerMessage_MediaEvent): unknown { const obj: any = {}; if (message.data !== "") { obj.data = message.data; @@ -364,11 +430,11 @@ export const PeerMessage_RTCStatsReport: MessageFns return obj; }, - create, I>>(base?: I): PeerMessage_RTCStatsReport { - return PeerMessage_RTCStatsReport.fromPartial(base ?? ({} as any)); + create, I>>(base?: I): PeerMessage_MediaEvent { + return PeerMessage_MediaEvent.fromPartial(base ?? ({} as any)); }, - fromPartial, I>>(object: I): PeerMessage_RTCStatsReport { - const message = createBasePeerMessage_RTCStatsReport(); + fromPartial, I>>(object: I): PeerMessage_MediaEvent { + const message = createBasePeerMessage_MediaEvent(); message.data = object.data ?? ""; return message; }, diff --git a/packages/protobufs/fishjam/server_notifications.ts b/packages/protobufs/fishjam/server_notifications.ts new file mode 100644 index 00000000..a1c06491 --- /dev/null +++ b/packages/protobufs/fishjam/server_notifications.ts @@ -0,0 +1,2379 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.4.0 +// protoc v5.28.2 +// source: fishjam/server_notifications.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; + +export const protobufPackage = "fishjam"; + +/** Defines any type of message passed between FJ and server peer */ +export interface ServerMessage { + roomCrashed?: ServerMessage_RoomCrashed | undefined; + peerConnected?: ServerMessage_PeerConnected | undefined; + peerDisconnected?: ServerMessage_PeerDisconnected | undefined; + peerCrashed?: ServerMessage_PeerCrashed | undefined; + componentCrashed?: ServerMessage_ComponentCrashed | undefined; + authenticated?: ServerMessage_Authenticated | undefined; + authRequest?: ServerMessage_AuthRequest | undefined; + subscribeRequest?: ServerMessage_SubscribeRequest | undefined; + subscribeResponse?: ServerMessage_SubscribeResponse | undefined; + roomCreated?: ServerMessage_RoomCreated | undefined; + roomDeleted?: ServerMessage_RoomDeleted | undefined; + metricsReport?: ServerMessage_MetricsReport | undefined; + hlsPlayable?: ServerMessage_HlsPlayable | undefined; + hlsUploaded?: ServerMessage_HlsUploaded | undefined; + hlsUploadCrashed?: ServerMessage_HlsUploadCrashed | undefined; + peerMetadataUpdated?: ServerMessage_PeerMetadataUpdated | undefined; + trackAdded?: ServerMessage_TrackAdded | undefined; + trackRemoved?: ServerMessage_TrackRemoved | undefined; + trackMetadataUpdated?: ServerMessage_TrackMetadataUpdated | undefined; + peerAdded?: ServerMessage_PeerAdded | undefined; + peerDeleted?: ServerMessage_PeerDeleted | undefined; +} + +/** Defines message groups for which peer can subscribe */ +export enum ServerMessage_EventType { + EVENT_TYPE_UNSPECIFIED = 0, + EVENT_TYPE_SERVER_NOTIFICATION = 1, + EVENT_TYPE_METRICS = 2, + UNRECOGNIZED = -1, +} + +export function serverMessage_EventTypeFromJSON(object: any): ServerMessage_EventType { + switch (object) { + case 0: + case "EVENT_TYPE_UNSPECIFIED": + return ServerMessage_EventType.EVENT_TYPE_UNSPECIFIED; + case 1: + case "EVENT_TYPE_SERVER_NOTIFICATION": + return ServerMessage_EventType.EVENT_TYPE_SERVER_NOTIFICATION; + case 2: + case "EVENT_TYPE_METRICS": + return ServerMessage_EventType.EVENT_TYPE_METRICS; + case -1: + case "UNRECOGNIZED": + default: + return ServerMessage_EventType.UNRECOGNIZED; + } +} + +export function serverMessage_EventTypeToJSON(object: ServerMessage_EventType): string { + switch (object) { + case ServerMessage_EventType.EVENT_TYPE_UNSPECIFIED: + return "EVENT_TYPE_UNSPECIFIED"; + case ServerMessage_EventType.EVENT_TYPE_SERVER_NOTIFICATION: + return "EVENT_TYPE_SERVER_NOTIFICATION"; + case ServerMessage_EventType.EVENT_TYPE_METRICS: + return "EVENT_TYPE_METRICS"; + case ServerMessage_EventType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +/** Defines types of tracks being published by peers and component */ +export enum ServerMessage_TrackType { + TRACK_TYPE_UNSPECIFIED = 0, + TRACK_TYPE_VIDEO = 1, + TRACK_TYPE_AUDIO = 2, + UNRECOGNIZED = -1, +} + +export function serverMessage_TrackTypeFromJSON(object: any): ServerMessage_TrackType { + switch (object) { + case 0: + case "TRACK_TYPE_UNSPECIFIED": + return ServerMessage_TrackType.TRACK_TYPE_UNSPECIFIED; + case 1: + case "TRACK_TYPE_VIDEO": + return ServerMessage_TrackType.TRACK_TYPE_VIDEO; + case 2: + case "TRACK_TYPE_AUDIO": + return ServerMessage_TrackType.TRACK_TYPE_AUDIO; + case -1: + case "UNRECOGNIZED": + default: + return ServerMessage_TrackType.UNRECOGNIZED; + } +} + +export function serverMessage_TrackTypeToJSON(object: ServerMessage_TrackType): string { + switch (object) { + case ServerMessage_TrackType.TRACK_TYPE_UNSPECIFIED: + return "TRACK_TYPE_UNSPECIFIED"; + case ServerMessage_TrackType.TRACK_TYPE_VIDEO: + return "TRACK_TYPE_VIDEO"; + case ServerMessage_TrackType.TRACK_TYPE_AUDIO: + return "TRACK_TYPE_AUDIO"; + case ServerMessage_TrackType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +/** Notification sent when a room crashes */ +export interface ServerMessage_RoomCrashed { + roomId: string; +} + +/** Notification sent when a peer is added */ +export interface ServerMessage_PeerAdded { + roomId: string; + peerId: string; +} + +/** Notification sent when a peer is removed */ +export interface ServerMessage_PeerDeleted { + roomId: string; + peerId: string; +} + +/** Notification sent when a peer connects */ +export interface ServerMessage_PeerConnected { + roomId: string; + peerId: string; +} + +/** Notification sent when a peer disconnects from FJ */ +export interface ServerMessage_PeerDisconnected { + roomId: string; + peerId: string; +} + +/** Notification sent when a peer crashes */ +export interface ServerMessage_PeerCrashed { + roomId: string; + peerId: string; + reason: string; +} + +/** Notification sent when a component crashes */ +export interface ServerMessage_ComponentCrashed { + roomId: string; + componentId: string; +} + +/** Response sent by FJ, confirming successfull authentication */ +export interface ServerMessage_Authenticated { +} + +/** Request sent by peer, to authenticate to FJ server */ +export interface ServerMessage_AuthRequest { + token: string; +} + +/** Request sent by peer to subsribe for certain message type */ +export interface ServerMessage_SubscribeRequest { + eventType: ServerMessage_EventType; +} + +/** Response sent by FJ, confirming subscription for message type */ +export interface ServerMessage_SubscribeResponse { + eventType: ServerMessage_EventType; +} + +/** Notification sent when a room is created */ +export interface ServerMessage_RoomCreated { + roomId: string; +} + +/** Notification sent when a room is deleted */ +export interface ServerMessage_RoomDeleted { + roomId: string; +} + +/** Message containing WebRTC metrics from FJ */ +export interface ServerMessage_MetricsReport { + metrics: string; +} + +/** Notification sent when the HLS stream becomes available in a room */ +export interface ServerMessage_HlsPlayable { + roomId: string; + componentId: string; +} + +/** Notification sent when the HLS recording is successfully uploded to AWS S3 */ +export interface ServerMessage_HlsUploaded { + roomId: string; +} + +/** Notification sent when the upload of HLS recording to AWS S3 fails */ +export interface ServerMessage_HlsUploadCrashed { + roomId: string; +} + +/** Notification sent when peer updates its metadata */ +export interface ServerMessage_PeerMetadataUpdated { + roomId: string; + peerId: string; + metadata: string; +} + +/** Describes a media track */ +export interface ServerMessage_Track { + id: string; + type: ServerMessage_TrackType; + metadata: string; +} + +/** Notification sent when peer or component adds new track */ +export interface ServerMessage_TrackAdded { + roomId: string; + peerId?: string | undefined; + componentId?: string | undefined; + track: ServerMessage_Track | undefined; +} + +/** Notification sent when a track is removed */ +export interface ServerMessage_TrackRemoved { + roomId: string; + peerId?: string | undefined; + componentId?: string | undefined; + track: ServerMessage_Track | undefined; +} + +/** Notification sent when metadata of a multimedia track is updated */ +export interface ServerMessage_TrackMetadataUpdated { + roomId: string; + peerId?: string | undefined; + componentId?: string | undefined; + track: ServerMessage_Track | undefined; +} + +function createBaseServerMessage(): ServerMessage { + return { + roomCrashed: undefined, + peerConnected: undefined, + peerDisconnected: undefined, + peerCrashed: undefined, + componentCrashed: undefined, + authenticated: undefined, + authRequest: undefined, + subscribeRequest: undefined, + subscribeResponse: undefined, + roomCreated: undefined, + roomDeleted: undefined, + metricsReport: undefined, + hlsPlayable: undefined, + hlsUploaded: undefined, + hlsUploadCrashed: undefined, + peerMetadataUpdated: undefined, + trackAdded: undefined, + trackRemoved: undefined, + trackMetadataUpdated: undefined, + peerAdded: undefined, + peerDeleted: undefined, + }; +} + +export const ServerMessage: MessageFns = { + encode(message: ServerMessage, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomCrashed !== undefined) { + ServerMessage_RoomCrashed.encode(message.roomCrashed, writer.uint32(10).fork()).join(); + } + if (message.peerConnected !== undefined) { + ServerMessage_PeerConnected.encode(message.peerConnected, writer.uint32(18).fork()).join(); + } + if (message.peerDisconnected !== undefined) { + ServerMessage_PeerDisconnected.encode(message.peerDisconnected, writer.uint32(26).fork()).join(); + } + if (message.peerCrashed !== undefined) { + ServerMessage_PeerCrashed.encode(message.peerCrashed, writer.uint32(34).fork()).join(); + } + if (message.componentCrashed !== undefined) { + ServerMessage_ComponentCrashed.encode(message.componentCrashed, writer.uint32(42).fork()).join(); + } + if (message.authenticated !== undefined) { + ServerMessage_Authenticated.encode(message.authenticated, writer.uint32(50).fork()).join(); + } + if (message.authRequest !== undefined) { + ServerMessage_AuthRequest.encode(message.authRequest, writer.uint32(58).fork()).join(); + } + if (message.subscribeRequest !== undefined) { + ServerMessage_SubscribeRequest.encode(message.subscribeRequest, writer.uint32(66).fork()).join(); + } + if (message.subscribeResponse !== undefined) { + ServerMessage_SubscribeResponse.encode(message.subscribeResponse, writer.uint32(74).fork()).join(); + } + if (message.roomCreated !== undefined) { + ServerMessage_RoomCreated.encode(message.roomCreated, writer.uint32(82).fork()).join(); + } + if (message.roomDeleted !== undefined) { + ServerMessage_RoomDeleted.encode(message.roomDeleted, writer.uint32(90).fork()).join(); + } + if (message.metricsReport !== undefined) { + ServerMessage_MetricsReport.encode(message.metricsReport, writer.uint32(98).fork()).join(); + } + if (message.hlsPlayable !== undefined) { + ServerMessage_HlsPlayable.encode(message.hlsPlayable, writer.uint32(106).fork()).join(); + } + if (message.hlsUploaded !== undefined) { + ServerMessage_HlsUploaded.encode(message.hlsUploaded, writer.uint32(114).fork()).join(); + } + if (message.hlsUploadCrashed !== undefined) { + ServerMessage_HlsUploadCrashed.encode(message.hlsUploadCrashed, writer.uint32(122).fork()).join(); + } + if (message.peerMetadataUpdated !== undefined) { + ServerMessage_PeerMetadataUpdated.encode(message.peerMetadataUpdated, writer.uint32(130).fork()).join(); + } + if (message.trackAdded !== undefined) { + ServerMessage_TrackAdded.encode(message.trackAdded, writer.uint32(138).fork()).join(); + } + if (message.trackRemoved !== undefined) { + ServerMessage_TrackRemoved.encode(message.trackRemoved, writer.uint32(146).fork()).join(); + } + if (message.trackMetadataUpdated !== undefined) { + ServerMessage_TrackMetadataUpdated.encode(message.trackMetadataUpdated, writer.uint32(154).fork()).join(); + } + if (message.peerAdded !== undefined) { + ServerMessage_PeerAdded.encode(message.peerAdded, writer.uint32(162).fork()).join(); + } + if (message.peerDeleted !== undefined) { + ServerMessage_PeerDeleted.encode(message.peerDeleted, writer.uint32(170).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomCrashed = ServerMessage_RoomCrashed.decode(reader, reader.uint32()); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerConnected = ServerMessage_PeerConnected.decode(reader, reader.uint32()); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.peerDisconnected = ServerMessage_PeerDisconnected.decode(reader, reader.uint32()); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.peerCrashed = ServerMessage_PeerCrashed.decode(reader, reader.uint32()); + continue; + } + case 5: { + if (tag !== 42) { + break; + } + + message.componentCrashed = ServerMessage_ComponentCrashed.decode(reader, reader.uint32()); + continue; + } + case 6: { + if (tag !== 50) { + break; + } + + message.authenticated = ServerMessage_Authenticated.decode(reader, reader.uint32()); + continue; + } + case 7: { + if (tag !== 58) { + break; + } + + message.authRequest = ServerMessage_AuthRequest.decode(reader, reader.uint32()); + continue; + } + case 8: { + if (tag !== 66) { + break; + } + + message.subscribeRequest = ServerMessage_SubscribeRequest.decode(reader, reader.uint32()); + continue; + } + case 9: { + if (tag !== 74) { + break; + } + + message.subscribeResponse = ServerMessage_SubscribeResponse.decode(reader, reader.uint32()); + continue; + } + case 10: { + if (tag !== 82) { + break; + } + + message.roomCreated = ServerMessage_RoomCreated.decode(reader, reader.uint32()); + continue; + } + case 11: { + if (tag !== 90) { + break; + } + + message.roomDeleted = ServerMessage_RoomDeleted.decode(reader, reader.uint32()); + continue; + } + case 12: { + if (tag !== 98) { + break; + } + + message.metricsReport = ServerMessage_MetricsReport.decode(reader, reader.uint32()); + continue; + } + case 13: { + if (tag !== 106) { + break; + } + + message.hlsPlayable = ServerMessage_HlsPlayable.decode(reader, reader.uint32()); + continue; + } + case 14: { + if (tag !== 114) { + break; + } + + message.hlsUploaded = ServerMessage_HlsUploaded.decode(reader, reader.uint32()); + continue; + } + case 15: { + if (tag !== 122) { + break; + } + + message.hlsUploadCrashed = ServerMessage_HlsUploadCrashed.decode(reader, reader.uint32()); + continue; + } + case 16: { + if (tag !== 130) { + break; + } + + message.peerMetadataUpdated = ServerMessage_PeerMetadataUpdated.decode(reader, reader.uint32()); + continue; + } + case 17: { + if (tag !== 138) { + break; + } + + message.trackAdded = ServerMessage_TrackAdded.decode(reader, reader.uint32()); + continue; + } + case 18: { + if (tag !== 146) { + break; + } + + message.trackRemoved = ServerMessage_TrackRemoved.decode(reader, reader.uint32()); + continue; + } + case 19: { + if (tag !== 154) { + break; + } + + message.trackMetadataUpdated = ServerMessage_TrackMetadataUpdated.decode(reader, reader.uint32()); + continue; + } + case 20: { + if (tag !== 162) { + break; + } + + message.peerAdded = ServerMessage_PeerAdded.decode(reader, reader.uint32()); + continue; + } + case 21: { + if (tag !== 170) { + break; + } + + message.peerDeleted = ServerMessage_PeerDeleted.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage { + return { + roomCrashed: isSet(object.roomCrashed) ? ServerMessage_RoomCrashed.fromJSON(object.roomCrashed) : undefined, + peerConnected: isSet(object.peerConnected) + ? ServerMessage_PeerConnected.fromJSON(object.peerConnected) + : undefined, + peerDisconnected: isSet(object.peerDisconnected) + ? ServerMessage_PeerDisconnected.fromJSON(object.peerDisconnected) + : undefined, + peerCrashed: isSet(object.peerCrashed) ? ServerMessage_PeerCrashed.fromJSON(object.peerCrashed) : undefined, + componentCrashed: isSet(object.componentCrashed) + ? ServerMessage_ComponentCrashed.fromJSON(object.componentCrashed) + : undefined, + authenticated: isSet(object.authenticated) + ? ServerMessage_Authenticated.fromJSON(object.authenticated) + : undefined, + authRequest: isSet(object.authRequest) ? ServerMessage_AuthRequest.fromJSON(object.authRequest) : undefined, + subscribeRequest: isSet(object.subscribeRequest) + ? ServerMessage_SubscribeRequest.fromJSON(object.subscribeRequest) + : undefined, + subscribeResponse: isSet(object.subscribeResponse) + ? ServerMessage_SubscribeResponse.fromJSON(object.subscribeResponse) + : undefined, + roomCreated: isSet(object.roomCreated) ? ServerMessage_RoomCreated.fromJSON(object.roomCreated) : undefined, + roomDeleted: isSet(object.roomDeleted) ? ServerMessage_RoomDeleted.fromJSON(object.roomDeleted) : undefined, + metricsReport: isSet(object.metricsReport) + ? ServerMessage_MetricsReport.fromJSON(object.metricsReport) + : undefined, + hlsPlayable: isSet(object.hlsPlayable) ? ServerMessage_HlsPlayable.fromJSON(object.hlsPlayable) : undefined, + hlsUploaded: isSet(object.hlsUploaded) ? ServerMessage_HlsUploaded.fromJSON(object.hlsUploaded) : undefined, + hlsUploadCrashed: isSet(object.hlsUploadCrashed) + ? ServerMessage_HlsUploadCrashed.fromJSON(object.hlsUploadCrashed) + : undefined, + peerMetadataUpdated: isSet(object.peerMetadataUpdated) + ? ServerMessage_PeerMetadataUpdated.fromJSON(object.peerMetadataUpdated) + : undefined, + trackAdded: isSet(object.trackAdded) ? ServerMessage_TrackAdded.fromJSON(object.trackAdded) : undefined, + trackRemoved: isSet(object.trackRemoved) ? ServerMessage_TrackRemoved.fromJSON(object.trackRemoved) : undefined, + trackMetadataUpdated: isSet(object.trackMetadataUpdated) + ? ServerMessage_TrackMetadataUpdated.fromJSON(object.trackMetadataUpdated) + : undefined, + peerAdded: isSet(object.peerAdded) ? ServerMessage_PeerAdded.fromJSON(object.peerAdded) : undefined, + peerDeleted: isSet(object.peerDeleted) ? ServerMessage_PeerDeleted.fromJSON(object.peerDeleted) : undefined, + }; + }, + + toJSON(message: ServerMessage): unknown { + const obj: any = {}; + if (message.roomCrashed !== undefined) { + obj.roomCrashed = ServerMessage_RoomCrashed.toJSON(message.roomCrashed); + } + if (message.peerConnected !== undefined) { + obj.peerConnected = ServerMessage_PeerConnected.toJSON(message.peerConnected); + } + if (message.peerDisconnected !== undefined) { + obj.peerDisconnected = ServerMessage_PeerDisconnected.toJSON(message.peerDisconnected); + } + if (message.peerCrashed !== undefined) { + obj.peerCrashed = ServerMessage_PeerCrashed.toJSON(message.peerCrashed); + } + if (message.componentCrashed !== undefined) { + obj.componentCrashed = ServerMessage_ComponentCrashed.toJSON(message.componentCrashed); + } + if (message.authenticated !== undefined) { + obj.authenticated = ServerMessage_Authenticated.toJSON(message.authenticated); + } + if (message.authRequest !== undefined) { + obj.authRequest = ServerMessage_AuthRequest.toJSON(message.authRequest); + } + if (message.subscribeRequest !== undefined) { + obj.subscribeRequest = ServerMessage_SubscribeRequest.toJSON(message.subscribeRequest); + } + if (message.subscribeResponse !== undefined) { + obj.subscribeResponse = ServerMessage_SubscribeResponse.toJSON(message.subscribeResponse); + } + if (message.roomCreated !== undefined) { + obj.roomCreated = ServerMessage_RoomCreated.toJSON(message.roomCreated); + } + if (message.roomDeleted !== undefined) { + obj.roomDeleted = ServerMessage_RoomDeleted.toJSON(message.roomDeleted); + } + if (message.metricsReport !== undefined) { + obj.metricsReport = ServerMessage_MetricsReport.toJSON(message.metricsReport); + } + if (message.hlsPlayable !== undefined) { + obj.hlsPlayable = ServerMessage_HlsPlayable.toJSON(message.hlsPlayable); + } + if (message.hlsUploaded !== undefined) { + obj.hlsUploaded = ServerMessage_HlsUploaded.toJSON(message.hlsUploaded); + } + if (message.hlsUploadCrashed !== undefined) { + obj.hlsUploadCrashed = ServerMessage_HlsUploadCrashed.toJSON(message.hlsUploadCrashed); + } + if (message.peerMetadataUpdated !== undefined) { + obj.peerMetadataUpdated = ServerMessage_PeerMetadataUpdated.toJSON(message.peerMetadataUpdated); + } + if (message.trackAdded !== undefined) { + obj.trackAdded = ServerMessage_TrackAdded.toJSON(message.trackAdded); + } + if (message.trackRemoved !== undefined) { + obj.trackRemoved = ServerMessage_TrackRemoved.toJSON(message.trackRemoved); + } + if (message.trackMetadataUpdated !== undefined) { + obj.trackMetadataUpdated = ServerMessage_TrackMetadataUpdated.toJSON(message.trackMetadataUpdated); + } + if (message.peerAdded !== undefined) { + obj.peerAdded = ServerMessage_PeerAdded.toJSON(message.peerAdded); + } + if (message.peerDeleted !== undefined) { + obj.peerDeleted = ServerMessage_PeerDeleted.toJSON(message.peerDeleted); + } + return obj; + }, + + create, I>>(base?: I): ServerMessage { + return ServerMessage.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage { + const message = createBaseServerMessage(); + message.roomCrashed = (object.roomCrashed !== undefined && object.roomCrashed !== null) + ? ServerMessage_RoomCrashed.fromPartial(object.roomCrashed) + : undefined; + message.peerConnected = (object.peerConnected !== undefined && object.peerConnected !== null) + ? ServerMessage_PeerConnected.fromPartial(object.peerConnected) + : undefined; + message.peerDisconnected = (object.peerDisconnected !== undefined && object.peerDisconnected !== null) + ? ServerMessage_PeerDisconnected.fromPartial(object.peerDisconnected) + : undefined; + message.peerCrashed = (object.peerCrashed !== undefined && object.peerCrashed !== null) + ? ServerMessage_PeerCrashed.fromPartial(object.peerCrashed) + : undefined; + message.componentCrashed = (object.componentCrashed !== undefined && object.componentCrashed !== null) + ? ServerMessage_ComponentCrashed.fromPartial(object.componentCrashed) + : undefined; + message.authenticated = (object.authenticated !== undefined && object.authenticated !== null) + ? ServerMessage_Authenticated.fromPartial(object.authenticated) + : undefined; + message.authRequest = (object.authRequest !== undefined && object.authRequest !== null) + ? ServerMessage_AuthRequest.fromPartial(object.authRequest) + : undefined; + message.subscribeRequest = (object.subscribeRequest !== undefined && object.subscribeRequest !== null) + ? ServerMessage_SubscribeRequest.fromPartial(object.subscribeRequest) + : undefined; + message.subscribeResponse = (object.subscribeResponse !== undefined && object.subscribeResponse !== null) + ? ServerMessage_SubscribeResponse.fromPartial(object.subscribeResponse) + : undefined; + message.roomCreated = (object.roomCreated !== undefined && object.roomCreated !== null) + ? ServerMessage_RoomCreated.fromPartial(object.roomCreated) + : undefined; + message.roomDeleted = (object.roomDeleted !== undefined && object.roomDeleted !== null) + ? ServerMessage_RoomDeleted.fromPartial(object.roomDeleted) + : undefined; + message.metricsReport = (object.metricsReport !== undefined && object.metricsReport !== null) + ? ServerMessage_MetricsReport.fromPartial(object.metricsReport) + : undefined; + message.hlsPlayable = (object.hlsPlayable !== undefined && object.hlsPlayable !== null) + ? ServerMessage_HlsPlayable.fromPartial(object.hlsPlayable) + : undefined; + message.hlsUploaded = (object.hlsUploaded !== undefined && object.hlsUploaded !== null) + ? ServerMessage_HlsUploaded.fromPartial(object.hlsUploaded) + : undefined; + message.hlsUploadCrashed = (object.hlsUploadCrashed !== undefined && object.hlsUploadCrashed !== null) + ? ServerMessage_HlsUploadCrashed.fromPartial(object.hlsUploadCrashed) + : undefined; + message.peerMetadataUpdated = (object.peerMetadataUpdated !== undefined && object.peerMetadataUpdated !== null) + ? ServerMessage_PeerMetadataUpdated.fromPartial(object.peerMetadataUpdated) + : undefined; + message.trackAdded = (object.trackAdded !== undefined && object.trackAdded !== null) + ? ServerMessage_TrackAdded.fromPartial(object.trackAdded) + : undefined; + message.trackRemoved = (object.trackRemoved !== undefined && object.trackRemoved !== null) + ? ServerMessage_TrackRemoved.fromPartial(object.trackRemoved) + : undefined; + message.trackMetadataUpdated = (object.trackMetadataUpdated !== undefined && object.trackMetadataUpdated !== null) + ? ServerMessage_TrackMetadataUpdated.fromPartial(object.trackMetadataUpdated) + : undefined; + message.peerAdded = (object.peerAdded !== undefined && object.peerAdded !== null) + ? ServerMessage_PeerAdded.fromPartial(object.peerAdded) + : undefined; + message.peerDeleted = (object.peerDeleted !== undefined && object.peerDeleted !== null) + ? ServerMessage_PeerDeleted.fromPartial(object.peerDeleted) + : undefined; + return message; + }, +}; + +function createBaseServerMessage_RoomCrashed(): ServerMessage_RoomCrashed { + return { roomId: "" }; +} + +export const ServerMessage_RoomCrashed: MessageFns = { + encode(message: ServerMessage_RoomCrashed, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_RoomCrashed { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_RoomCrashed(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_RoomCrashed { + return { roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "" }; + }, + + toJSON(message: ServerMessage_RoomCrashed): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_RoomCrashed { + return ServerMessage_RoomCrashed.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_RoomCrashed { + const message = createBaseServerMessage_RoomCrashed(); + message.roomId = object.roomId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_PeerAdded(): ServerMessage_PeerAdded { + return { roomId: "", peerId: "" }; +} + +export const ServerMessage_PeerAdded: MessageFns = { + encode(message: ServerMessage_PeerAdded, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.peerId !== "") { + writer.uint32(18).string(message.peerId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_PeerAdded { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_PeerAdded(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_PeerAdded { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + peerId: isSet(object.peerId) ? globalThis.String(object.peerId) : "", + }; + }, + + toJSON(message: ServerMessage_PeerAdded): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.peerId !== "") { + obj.peerId = message.peerId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_PeerAdded { + return ServerMessage_PeerAdded.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_PeerAdded { + const message = createBaseServerMessage_PeerAdded(); + message.roomId = object.roomId ?? ""; + message.peerId = object.peerId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_PeerDeleted(): ServerMessage_PeerDeleted { + return { roomId: "", peerId: "" }; +} + +export const ServerMessage_PeerDeleted: MessageFns = { + encode(message: ServerMessage_PeerDeleted, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.peerId !== "") { + writer.uint32(18).string(message.peerId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_PeerDeleted { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_PeerDeleted(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_PeerDeleted { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + peerId: isSet(object.peerId) ? globalThis.String(object.peerId) : "", + }; + }, + + toJSON(message: ServerMessage_PeerDeleted): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.peerId !== "") { + obj.peerId = message.peerId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_PeerDeleted { + return ServerMessage_PeerDeleted.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_PeerDeleted { + const message = createBaseServerMessage_PeerDeleted(); + message.roomId = object.roomId ?? ""; + message.peerId = object.peerId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_PeerConnected(): ServerMessage_PeerConnected { + return { roomId: "", peerId: "" }; +} + +export const ServerMessage_PeerConnected: MessageFns = { + encode(message: ServerMessage_PeerConnected, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.peerId !== "") { + writer.uint32(18).string(message.peerId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_PeerConnected { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_PeerConnected(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_PeerConnected { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + peerId: isSet(object.peerId) ? globalThis.String(object.peerId) : "", + }; + }, + + toJSON(message: ServerMessage_PeerConnected): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.peerId !== "") { + obj.peerId = message.peerId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_PeerConnected { + return ServerMessage_PeerConnected.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_PeerConnected { + const message = createBaseServerMessage_PeerConnected(); + message.roomId = object.roomId ?? ""; + message.peerId = object.peerId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_PeerDisconnected(): ServerMessage_PeerDisconnected { + return { roomId: "", peerId: "" }; +} + +export const ServerMessage_PeerDisconnected: MessageFns = { + encode(message: ServerMessage_PeerDisconnected, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.peerId !== "") { + writer.uint32(18).string(message.peerId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_PeerDisconnected { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_PeerDisconnected(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_PeerDisconnected { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + peerId: isSet(object.peerId) ? globalThis.String(object.peerId) : "", + }; + }, + + toJSON(message: ServerMessage_PeerDisconnected): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.peerId !== "") { + obj.peerId = message.peerId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_PeerDisconnected { + return ServerMessage_PeerDisconnected.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): ServerMessage_PeerDisconnected { + const message = createBaseServerMessage_PeerDisconnected(); + message.roomId = object.roomId ?? ""; + message.peerId = object.peerId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_PeerCrashed(): ServerMessage_PeerCrashed { + return { roomId: "", peerId: "", reason: "" }; +} + +export const ServerMessage_PeerCrashed: MessageFns = { + encode(message: ServerMessage_PeerCrashed, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.peerId !== "") { + writer.uint32(18).string(message.peerId); + } + if (message.reason !== "") { + writer.uint32(26).string(message.reason); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_PeerCrashed { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_PeerCrashed(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerId = reader.string(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.reason = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_PeerCrashed { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + peerId: isSet(object.peerId) ? globalThis.String(object.peerId) : "", + reason: isSet(object.reason) ? globalThis.String(object.reason) : "", + }; + }, + + toJSON(message: ServerMessage_PeerCrashed): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.peerId !== "") { + obj.peerId = message.peerId; + } + if (message.reason !== "") { + obj.reason = message.reason; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_PeerCrashed { + return ServerMessage_PeerCrashed.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_PeerCrashed { + const message = createBaseServerMessage_PeerCrashed(); + message.roomId = object.roomId ?? ""; + message.peerId = object.peerId ?? ""; + message.reason = object.reason ?? ""; + return message; + }, +}; + +function createBaseServerMessage_ComponentCrashed(): ServerMessage_ComponentCrashed { + return { roomId: "", componentId: "" }; +} + +export const ServerMessage_ComponentCrashed: MessageFns = { + encode(message: ServerMessage_ComponentCrashed, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.componentId !== "") { + writer.uint32(18).string(message.componentId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_ComponentCrashed { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_ComponentCrashed(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.componentId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_ComponentCrashed { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + componentId: isSet(object.componentId) ? globalThis.String(object.componentId) : "", + }; + }, + + toJSON(message: ServerMessage_ComponentCrashed): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.componentId !== "") { + obj.componentId = message.componentId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_ComponentCrashed { + return ServerMessage_ComponentCrashed.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): ServerMessage_ComponentCrashed { + const message = createBaseServerMessage_ComponentCrashed(); + message.roomId = object.roomId ?? ""; + message.componentId = object.componentId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_Authenticated(): ServerMessage_Authenticated { + return {}; +} + +export const ServerMessage_Authenticated: MessageFns = { + encode(_: ServerMessage_Authenticated, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_Authenticated { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_Authenticated(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(_: any): ServerMessage_Authenticated { + return {}; + }, + + toJSON(_: ServerMessage_Authenticated): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): ServerMessage_Authenticated { + return ServerMessage_Authenticated.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): ServerMessage_Authenticated { + const message = createBaseServerMessage_Authenticated(); + return message; + }, +}; + +function createBaseServerMessage_AuthRequest(): ServerMessage_AuthRequest { + return { token: "" }; +} + +export const ServerMessage_AuthRequest: MessageFns = { + encode(message: ServerMessage_AuthRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.token !== "") { + writer.uint32(10).string(message.token); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_AuthRequest { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_AuthRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.token = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_AuthRequest { + return { token: isSet(object.token) ? globalThis.String(object.token) : "" }; + }, + + toJSON(message: ServerMessage_AuthRequest): unknown { + const obj: any = {}; + if (message.token !== "") { + obj.token = message.token; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_AuthRequest { + return ServerMessage_AuthRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_AuthRequest { + const message = createBaseServerMessage_AuthRequest(); + message.token = object.token ?? ""; + return message; + }, +}; + +function createBaseServerMessage_SubscribeRequest(): ServerMessage_SubscribeRequest { + return { eventType: 0 }; +} + +export const ServerMessage_SubscribeRequest: MessageFns = { + encode(message: ServerMessage_SubscribeRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.eventType !== 0) { + writer.uint32(8).int32(message.eventType); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_SubscribeRequest { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_SubscribeRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.eventType = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_SubscribeRequest { + return { eventType: isSet(object.eventType) ? serverMessage_EventTypeFromJSON(object.eventType) : 0 }; + }, + + toJSON(message: ServerMessage_SubscribeRequest): unknown { + const obj: any = {}; + if (message.eventType !== 0) { + obj.eventType = serverMessage_EventTypeToJSON(message.eventType); + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_SubscribeRequest { + return ServerMessage_SubscribeRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): ServerMessage_SubscribeRequest { + const message = createBaseServerMessage_SubscribeRequest(); + message.eventType = object.eventType ?? 0; + return message; + }, +}; + +function createBaseServerMessage_SubscribeResponse(): ServerMessage_SubscribeResponse { + return { eventType: 0 }; +} + +export const ServerMessage_SubscribeResponse: MessageFns = { + encode(message: ServerMessage_SubscribeResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.eventType !== 0) { + writer.uint32(8).int32(message.eventType); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_SubscribeResponse { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_SubscribeResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 8) { + break; + } + + message.eventType = reader.int32() as any; + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_SubscribeResponse { + return { eventType: isSet(object.eventType) ? serverMessage_EventTypeFromJSON(object.eventType) : 0 }; + }, + + toJSON(message: ServerMessage_SubscribeResponse): unknown { + const obj: any = {}; + if (message.eventType !== 0) { + obj.eventType = serverMessage_EventTypeToJSON(message.eventType); + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_SubscribeResponse { + return ServerMessage_SubscribeResponse.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): ServerMessage_SubscribeResponse { + const message = createBaseServerMessage_SubscribeResponse(); + message.eventType = object.eventType ?? 0; + return message; + }, +}; + +function createBaseServerMessage_RoomCreated(): ServerMessage_RoomCreated { + return { roomId: "" }; +} + +export const ServerMessage_RoomCreated: MessageFns = { + encode(message: ServerMessage_RoomCreated, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_RoomCreated { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_RoomCreated(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_RoomCreated { + return { roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "" }; + }, + + toJSON(message: ServerMessage_RoomCreated): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_RoomCreated { + return ServerMessage_RoomCreated.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_RoomCreated { + const message = createBaseServerMessage_RoomCreated(); + message.roomId = object.roomId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_RoomDeleted(): ServerMessage_RoomDeleted { + return { roomId: "" }; +} + +export const ServerMessage_RoomDeleted: MessageFns = { + encode(message: ServerMessage_RoomDeleted, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_RoomDeleted { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_RoomDeleted(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_RoomDeleted { + return { roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "" }; + }, + + toJSON(message: ServerMessage_RoomDeleted): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_RoomDeleted { + return ServerMessage_RoomDeleted.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_RoomDeleted { + const message = createBaseServerMessage_RoomDeleted(); + message.roomId = object.roomId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_MetricsReport(): ServerMessage_MetricsReport { + return { metrics: "" }; +} + +export const ServerMessage_MetricsReport: MessageFns = { + encode(message: ServerMessage_MetricsReport, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.metrics !== "") { + writer.uint32(10).string(message.metrics); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_MetricsReport { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_MetricsReport(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.metrics = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_MetricsReport { + return { metrics: isSet(object.metrics) ? globalThis.String(object.metrics) : "" }; + }, + + toJSON(message: ServerMessage_MetricsReport): unknown { + const obj: any = {}; + if (message.metrics !== "") { + obj.metrics = message.metrics; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_MetricsReport { + return ServerMessage_MetricsReport.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_MetricsReport { + const message = createBaseServerMessage_MetricsReport(); + message.metrics = object.metrics ?? ""; + return message; + }, +}; + +function createBaseServerMessage_HlsPlayable(): ServerMessage_HlsPlayable { + return { roomId: "", componentId: "" }; +} + +export const ServerMessage_HlsPlayable: MessageFns = { + encode(message: ServerMessage_HlsPlayable, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.componentId !== "") { + writer.uint32(18).string(message.componentId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_HlsPlayable { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_HlsPlayable(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.componentId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_HlsPlayable { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + componentId: isSet(object.componentId) ? globalThis.String(object.componentId) : "", + }; + }, + + toJSON(message: ServerMessage_HlsPlayable): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.componentId !== "") { + obj.componentId = message.componentId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_HlsPlayable { + return ServerMessage_HlsPlayable.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_HlsPlayable { + const message = createBaseServerMessage_HlsPlayable(); + message.roomId = object.roomId ?? ""; + message.componentId = object.componentId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_HlsUploaded(): ServerMessage_HlsUploaded { + return { roomId: "" }; +} + +export const ServerMessage_HlsUploaded: MessageFns = { + encode(message: ServerMessage_HlsUploaded, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_HlsUploaded { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_HlsUploaded(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_HlsUploaded { + return { roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "" }; + }, + + toJSON(message: ServerMessage_HlsUploaded): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_HlsUploaded { + return ServerMessage_HlsUploaded.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_HlsUploaded { + const message = createBaseServerMessage_HlsUploaded(); + message.roomId = object.roomId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_HlsUploadCrashed(): ServerMessage_HlsUploadCrashed { + return { roomId: "" }; +} + +export const ServerMessage_HlsUploadCrashed: MessageFns = { + encode(message: ServerMessage_HlsUploadCrashed, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_HlsUploadCrashed { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_HlsUploadCrashed(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_HlsUploadCrashed { + return { roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "" }; + }, + + toJSON(message: ServerMessage_HlsUploadCrashed): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_HlsUploadCrashed { + return ServerMessage_HlsUploadCrashed.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): ServerMessage_HlsUploadCrashed { + const message = createBaseServerMessage_HlsUploadCrashed(); + message.roomId = object.roomId ?? ""; + return message; + }, +}; + +function createBaseServerMessage_PeerMetadataUpdated(): ServerMessage_PeerMetadataUpdated { + return { roomId: "", peerId: "", metadata: "" }; +} + +export const ServerMessage_PeerMetadataUpdated: MessageFns = { + encode(message: ServerMessage_PeerMetadataUpdated, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.peerId !== "") { + writer.uint32(18).string(message.peerId); + } + if (message.metadata !== "") { + writer.uint32(26).string(message.metadata); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_PeerMetadataUpdated { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_PeerMetadataUpdated(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerId = reader.string(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.metadata = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_PeerMetadataUpdated { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + peerId: isSet(object.peerId) ? globalThis.String(object.peerId) : "", + metadata: isSet(object.metadata) ? globalThis.String(object.metadata) : "", + }; + }, + + toJSON(message: ServerMessage_PeerMetadataUpdated): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.peerId !== "") { + obj.peerId = message.peerId; + } + if (message.metadata !== "") { + obj.metadata = message.metadata; + } + return obj; + }, + + create, I>>( + base?: I, + ): ServerMessage_PeerMetadataUpdated { + return ServerMessage_PeerMetadataUpdated.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): ServerMessage_PeerMetadataUpdated { + const message = createBaseServerMessage_PeerMetadataUpdated(); + message.roomId = object.roomId ?? ""; + message.peerId = object.peerId ?? ""; + message.metadata = object.metadata ?? ""; + return message; + }, +}; + +function createBaseServerMessage_Track(): ServerMessage_Track { + return { id: "", type: 0, metadata: "" }; +} + +export const ServerMessage_Track: MessageFns = { + encode(message: ServerMessage_Track, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.type !== 0) { + writer.uint32(16).int32(message.type); + } + if (message.metadata !== "") { + writer.uint32(26).string(message.metadata); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_Track { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_Track(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + } + case 2: { + if (tag !== 16) { + break; + } + + message.type = reader.int32() as any; + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.metadata = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_Track { + return { + id: isSet(object.id) ? globalThis.String(object.id) : "", + type: isSet(object.type) ? serverMessage_TrackTypeFromJSON(object.type) : 0, + metadata: isSet(object.metadata) ? globalThis.String(object.metadata) : "", + }; + }, + + toJSON(message: ServerMessage_Track): unknown { + const obj: any = {}; + if (message.id !== "") { + obj.id = message.id; + } + if (message.type !== 0) { + obj.type = serverMessage_TrackTypeToJSON(message.type); + } + if (message.metadata !== "") { + obj.metadata = message.metadata; + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_Track { + return ServerMessage_Track.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_Track { + const message = createBaseServerMessage_Track(); + message.id = object.id ?? ""; + message.type = object.type ?? 0; + message.metadata = object.metadata ?? ""; + return message; + }, +}; + +function createBaseServerMessage_TrackAdded(): ServerMessage_TrackAdded { + return { roomId: "", peerId: undefined, componentId: undefined, track: undefined }; +} + +export const ServerMessage_TrackAdded: MessageFns = { + encode(message: ServerMessage_TrackAdded, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.peerId !== undefined) { + writer.uint32(18).string(message.peerId); + } + if (message.componentId !== undefined) { + writer.uint32(26).string(message.componentId); + } + if (message.track !== undefined) { + ServerMessage_Track.encode(message.track, writer.uint32(34).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_TrackAdded { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_TrackAdded(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerId = reader.string(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.componentId = reader.string(); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.track = ServerMessage_Track.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_TrackAdded { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + peerId: isSet(object.peerId) ? globalThis.String(object.peerId) : undefined, + componentId: isSet(object.componentId) ? globalThis.String(object.componentId) : undefined, + track: isSet(object.track) ? ServerMessage_Track.fromJSON(object.track) : undefined, + }; + }, + + toJSON(message: ServerMessage_TrackAdded): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.peerId !== undefined) { + obj.peerId = message.peerId; + } + if (message.componentId !== undefined) { + obj.componentId = message.componentId; + } + if (message.track !== undefined) { + obj.track = ServerMessage_Track.toJSON(message.track); + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_TrackAdded { + return ServerMessage_TrackAdded.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_TrackAdded { + const message = createBaseServerMessage_TrackAdded(); + message.roomId = object.roomId ?? ""; + message.peerId = object.peerId ?? undefined; + message.componentId = object.componentId ?? undefined; + message.track = (object.track !== undefined && object.track !== null) + ? ServerMessage_Track.fromPartial(object.track) + : undefined; + return message; + }, +}; + +function createBaseServerMessage_TrackRemoved(): ServerMessage_TrackRemoved { + return { roomId: "", peerId: undefined, componentId: undefined, track: undefined }; +} + +export const ServerMessage_TrackRemoved: MessageFns = { + encode(message: ServerMessage_TrackRemoved, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.peerId !== undefined) { + writer.uint32(18).string(message.peerId); + } + if (message.componentId !== undefined) { + writer.uint32(26).string(message.componentId); + } + if (message.track !== undefined) { + ServerMessage_Track.encode(message.track, writer.uint32(34).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_TrackRemoved { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_TrackRemoved(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerId = reader.string(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.componentId = reader.string(); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.track = ServerMessage_Track.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_TrackRemoved { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + peerId: isSet(object.peerId) ? globalThis.String(object.peerId) : undefined, + componentId: isSet(object.componentId) ? globalThis.String(object.componentId) : undefined, + track: isSet(object.track) ? ServerMessage_Track.fromJSON(object.track) : undefined, + }; + }, + + toJSON(message: ServerMessage_TrackRemoved): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.peerId !== undefined) { + obj.peerId = message.peerId; + } + if (message.componentId !== undefined) { + obj.componentId = message.componentId; + } + if (message.track !== undefined) { + obj.track = ServerMessage_Track.toJSON(message.track); + } + return obj; + }, + + create, I>>(base?: I): ServerMessage_TrackRemoved { + return ServerMessage_TrackRemoved.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ServerMessage_TrackRemoved { + const message = createBaseServerMessage_TrackRemoved(); + message.roomId = object.roomId ?? ""; + message.peerId = object.peerId ?? undefined; + message.componentId = object.componentId ?? undefined; + message.track = (object.track !== undefined && object.track !== null) + ? ServerMessage_Track.fromPartial(object.track) + : undefined; + return message; + }, +}; + +function createBaseServerMessage_TrackMetadataUpdated(): ServerMessage_TrackMetadataUpdated { + return { roomId: "", peerId: undefined, componentId: undefined, track: undefined }; +} + +export const ServerMessage_TrackMetadataUpdated: MessageFns = { + encode(message: ServerMessage_TrackMetadataUpdated, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.roomId !== "") { + writer.uint32(10).string(message.roomId); + } + if (message.peerId !== undefined) { + writer.uint32(18).string(message.peerId); + } + if (message.componentId !== undefined) { + writer.uint32(26).string(message.componentId); + } + if (message.track !== undefined) { + ServerMessage_Track.encode(message.track, writer.uint32(34).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): ServerMessage_TrackMetadataUpdated { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseServerMessage_TrackMetadataUpdated(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.roomId = reader.string(); + continue; + } + case 2: { + if (tag !== 18) { + break; + } + + message.peerId = reader.string(); + continue; + } + case 3: { + if (tag !== 26) { + break; + } + + message.componentId = reader.string(); + continue; + } + case 4: { + if (tag !== 34) { + break; + } + + message.track = ServerMessage_Track.decode(reader, reader.uint32()); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): ServerMessage_TrackMetadataUpdated { + return { + roomId: isSet(object.roomId) ? globalThis.String(object.roomId) : "", + peerId: isSet(object.peerId) ? globalThis.String(object.peerId) : undefined, + componentId: isSet(object.componentId) ? globalThis.String(object.componentId) : undefined, + track: isSet(object.track) ? ServerMessage_Track.fromJSON(object.track) : undefined, + }; + }, + + toJSON(message: ServerMessage_TrackMetadataUpdated): unknown { + const obj: any = {}; + if (message.roomId !== "") { + obj.roomId = message.roomId; + } + if (message.peerId !== undefined) { + obj.peerId = message.peerId; + } + if (message.componentId !== undefined) { + obj.componentId = message.componentId; + } + if (message.track !== undefined) { + obj.track = ServerMessage_Track.toJSON(message.track); + } + return obj; + }, + + create, I>>( + base?: I, + ): ServerMessage_TrackMetadataUpdated { + return ServerMessage_TrackMetadataUpdated.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + object: I, + ): ServerMessage_TrackMetadataUpdated { + const message = createBaseServerMessage_TrackMetadataUpdated(); + message.roomId = object.roomId ?? ""; + message.peerId = object.peerId ?? undefined; + message.componentId = object.componentId ?? undefined; + message.track = (object.track !== undefined && object.track !== null) + ? ServerMessage_Track.fromPartial(object.track) + : undefined; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; + fromJSON(object: any): T; + toJSON(message: T): unknown; + create, I>>(base?: I): T; + fromPartial, I>>(object: I): T; +} diff --git a/packages/protobufs/index.ts b/packages/protobufs/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/protobufs/package.json b/packages/protobufs/package.json new file mode 100644 index 00000000..143f0d05 --- /dev/null +++ b/packages/protobufs/package.json @@ -0,0 +1,19 @@ +{ + "name": "@fishjam-cloud/protobufs", + "version": "0.9.0", + "private": true, + "exports": { + "./peer": "./fishjam/media_events/peer/peer.ts", + "./server": "./fishjam/media_events/server/server.ts", + "./shared": "./fishjam/media_events/shared.ts", + "./fishjamPeer": "./fishjam/peer_notifications.ts", + "./fishjamServer": "./fishjam/server_notifications.ts" + }, + "scripts": { + "build": "./protobuf.sh" + }, + "devDependencies": { + "ts-proto": "^2.2.7", + "tsup": "^8.3.5" + } +} diff --git a/packages/protobufs/protobuf.sh b/packages/protobufs/protobuf.sh new file mode 100755 index 00000000..9446f9ae --- /dev/null +++ b/packages/protobufs/protobuf.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +set -e + +ROOTDIR=$(dirname $(dirname "$(readlink -f $0)")) + +cd $ROOTDIR + +printf "Synchronising submodules... " +git submodule sync --recursive >> /dev/null +git submodule update --recursive --remote --init >> /dev/null +printf "DONE\n" + + +cd protobufs/protos + +files=$(find fishjam -name "*.proto") + +for file in $files; do + printf "Compiling file $file... " + protoc --plugin=../../../node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=../ $file + printf "DONE\n" + count=$(($count + 1)) +done diff --git a/packages/protobufs/protos b/packages/protobufs/protos new file mode 160000 index 00000000..0bcb4209 --- /dev/null +++ b/packages/protobufs/protos @@ -0,0 +1 @@ +Subproject commit 0bcb420955d0441a355c49be0a492d9b827fbbe8 diff --git a/packages/react-client/src/hooks/internal/useTrackManager.ts b/packages/react-client/src/hooks/internal/useTrackManager.ts index 1d9533a4..a1d3e162 100644 --- a/packages/react-client/src/hooks/internal/useTrackManager.ts +++ b/packages/react-client/src/hooks/internal/useTrackManager.ts @@ -1,4 +1,4 @@ -import type { FishjamClient, TrackMetadata } from "@fishjam-cloud/ts-client"; +import { Variant, type FishjamClient, type TrackMetadata } from "@fishjam-cloud/ts-client"; import type { MediaManager, TrackManager } from "../../types/internal"; import { getConfigAndBandwidthFromProps, getRemoteOrLocalTrack } from "../../utils/track"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -49,7 +49,9 @@ export const useTrackManager = ({ const stop = useCallback(() => mediaManager.stop(), [mediaManager]); const startStreaming = useCallback( - async (props: StartStreamingProps = { simulcast: ["l", "m", "h"] }) => { + async ( + props: StartStreamingProps = { simulcast: [Variant.VARIANT_LOW, Variant.VARIANT_MEDIUM, Variant.VARIANT_HIGH] }, + ) => { if (currentTrackId) throw Error("Track already added"); const media = mediaManager.getMedia(); diff --git a/packages/react-client/src/index.ts b/packages/react-client/src/index.ts index 2ccb2764..093f8461 100644 --- a/packages/react-client/src/index.ts +++ b/packages/react-client/src/index.ts @@ -37,5 +37,5 @@ export type { VadStatus, EncodingReason, AuthErrorReason, - Encoding, + Variant, } from "@fishjam-cloud/ts-client"; diff --git a/packages/react-client/src/types/public.ts b/packages/react-client/src/types/public.ts index 12a1750c..740a3115 100644 --- a/packages/react-client/src/types/public.ts +++ b/packages/react-client/src/types/public.ts @@ -1,10 +1,10 @@ -import type { Encoding, SimulcastConfig, TrackMetadata, VadStatus } from "@fishjam-cloud/ts-client"; +import type { SimulcastConfig, TrackMetadata, VadStatus, Variant } from "@fishjam-cloud/ts-client"; import type { DeviceError, DeviceManagerStatus, TrackId, TrackManager } from "./internal"; export type Track = { stream: MediaStream | null; - encoding: Encoding | null; + encoding: Variant | null; trackId: TrackId; metadata?: TrackMetadata; simulcastConfig: SimulcastConfig | null; @@ -63,9 +63,13 @@ export type ScreenshareApi = { currentTracksMiddleware: TracksMiddleware | null; }; -export type SimulcastBandwidthLimits = Record; +export type SimulcastBandwidthLimits = { + [Variant.VARIANT_LOW]: number; + [Variant.VARIANT_MEDIUM]: number; + [Variant.VARIANT_HIGH]: number; +}; -export type StartStreamingProps = { simulcast?: Encoding[] | false }; +export type StartStreamingProps = { simulcast?: Variant[] | false }; export type BandwidthLimits = { singleStream: number; simulcast: SimulcastBandwidthLimits }; diff --git a/packages/react-client/src/utils/bandwidth.ts b/packages/react-client/src/utils/bandwidth.ts index 6e5ac956..3e885cfa 100644 --- a/packages/react-client/src/utils/bandwidth.ts +++ b/packages/react-client/src/utils/bandwidth.ts @@ -1,6 +1,9 @@ +import { Variant } from "@fishjam-cloud/ts-client"; import type { BandwidthLimits } from "../types/public"; +export const ALL_VARIANTS_SIMULCAST = [Variant.VARIANT_LOW, Variant.VARIANT_MEDIUM, Variant.VARIANT_HIGH] as const; + export const mergeWithDefaultBandwitdthLimits = (limits?: Partial): BandwidthLimits => ({ singleStream: limits?.singleStream ?? 0, - simulcast: limits?.simulcast ?? { l: 0, m: 0, h: 0 }, + simulcast: limits?.simulcast ?? { [Variant.VARIANT_LOW]: 0, [Variant.VARIANT_MEDIUM]: 0, [Variant.VARIANT_HIGH]: 0 }, }); diff --git a/packages/react-client/src/utils/track.ts b/packages/react-client/src/utils/track.ts index 5f3dfd31..352019e7 100644 --- a/packages/react-client/src/utils/track.ts +++ b/packages/react-client/src/utils/track.ts @@ -1,4 +1,5 @@ -import type { Encoding, FishjamClient, TrackContext, TrackMetadata } from "@fishjam-cloud/ts-client"; +import type { FishjamClient, SimulcastConfig, TrackContext, TrackMetadata } from "@fishjam-cloud/ts-client"; +import { Variant } from "@fishjam-cloud/ts-client"; import type { BandwidthLimits, Track } from "../types/public"; // In most cases, the track is identified by its remote track ID. @@ -54,22 +55,25 @@ export function setupOnEndedCallback( }); } -const getDisabledEncodings = (activeEncodings: Encoding[] = []) => { - const allEncodings: Encoding[] = ["l", "m", "h"]; +const getDisabledEncodings = (activeEncodings: Variant[] = []) => { + const allEncodings: Variant[] = [Variant.VARIANT_LOW, Variant.VARIANT_MEDIUM, Variant.VARIANT_HIGH]; return allEncodings.filter((encoding) => !activeEncodings.includes(encoding)); }; export const getConfigAndBandwidthFromProps = ( - encodings: Encoding[] | false | undefined, + encodings: Variant[] | false | undefined, bandwidthLimits: BandwidthLimits, ) => { if (!encodings) return [bandwidthLimits.singleStream, undefined] as const; - const config = { + const config: SimulcastConfig = { enabled: true, - activeEncodings: encodings, - disabledEncodings: getDisabledEncodings(encodings), + enabledVariants: encodings, + disabledVariants: getDisabledEncodings(encodings), }; - const bandwidth = new Map(Object.entries(bandwidthLimits.simulcast) as [Encoding, number][]); + + const variantEntries = Object.entries(bandwidthLimits.simulcast) as unknown as [Variant, number][]; + + const bandwidth = new Map(variantEntries); return [bandwidth, config] as const; }; diff --git a/packages/ts-client/package.json b/packages/ts-client/package.json index aed2524b..b8131671 100644 --- a/packages/ts-client/package.json +++ b/packages/ts-client/package.json @@ -1,6 +1,6 @@ { "name": "@fishjam-cloud/ts-client", - "version": "0.9.0", + "version": "0.10.0", "description": "Typescript client library for Fishjam Cloud", "license": "Apache-2.0", "author": "Fishjam Cloud Team", @@ -11,23 +11,19 @@ "webrtc", "fishjam" ], - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", "files": [ "dist/**" ], - "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" - }, - "./protos": { - "import": "./dist/protos/index.js", - "types": "./dist/protos/index.d.ts" - } + "tsup": { + "minify": true, + "format": [ + "esm" + ] }, "scripts": { - "build": "tsc", + "build": "yarn build:check && tsup src/index.ts --dts-resolve", "build:check": "tsc --noEmit", "format": "prettier --write . --ignore-path ./.eslintignore", "format:check": "prettier --check . --ignore-path ./.eslintignore", @@ -38,12 +34,13 @@ }, "dependencies": { "@bufbuild/protobuf": "^2.2.2", - "@fishjam-cloud/webrtc-client": "workspace:*", "events": "^3.3.0", "typed-emitter": "^2.1.0", "uuid": "^11.0.3" }, "devDependencies": { + "@fishjam-cloud/protobufs": "workspace:*", + "@fishjam-cloud/webrtc-client": "workspace:*", "@playwright/test": "^1.49.0", "@types/events": "^3.0.3", "@types/node": "^22.10.0", @@ -52,7 +49,7 @@ "fake-mediastreamtrack": "^1.2.0", "husky": "^9.1.7", "lint-staged": "^15.2.10", - "typed-emitter": "^2.1.0", + "tsup": "^8.3.5", "typedoc": "^0.27.0", "typedoc-plugin-external-resolver": "^1.0.3", "typedoc-plugin-mdn-links": "^4.0.1", diff --git a/packages/ts-client/scripts/protobuf.sh b/packages/ts-client/scripts/protobuf.sh deleted file mode 100755 index c72a9d7a..00000000 --- a/packages/ts-client/scripts/protobuf.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -set -e - -ROOTDIR=$(dirname $(dirname "$(readlink -f $0)")) - -cd $ROOTDIR - -printf "Synchronising submodules... " -git submodule sync --recursive >> /dev/null -git submodule update --recursive --remote --init >> /dev/null -printf "DONE\n" - -file="./protos/fishjam/peer_notifications.proto" - -printf "Compiling file $file... " -protoc --plugin=../../node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./src/ $file -printf "DONE\n" diff --git a/packages/ts-client/src/FishjamClient.ts b/packages/ts-client/src/FishjamClient.ts index ba3819b7..65587f66 100644 --- a/packages/ts-client/src/FishjamClient.ts +++ b/packages/ts-client/src/FishjamClient.ts @@ -1,8 +1,7 @@ import type { BandwidthLimit, - Encoding, + Variant, Endpoint, - SerializedMediaEvent, SimulcastConfig, TrackBandwidthLimit, TrackContext, @@ -10,7 +9,9 @@ import type { import { WebRTCEndpoint } from '@fishjam-cloud/webrtc-client'; import type TypedEmitter from 'typed-emitter'; import { EventEmitter } from 'events'; -import { PeerMessage } from './protos'; +import { PeerMessage } from '@fishjam-cloud/protobufs/fishjamPeer'; +import { MediaEvent as PeerMediaEvent } from '@fishjam-cloud/protobufs/peer'; +import { MediaEvent as ServerMediaEvent } from '@fishjam-cloud/protobufs/server'; import { ReconnectManager } from './reconnection'; import { isAuthError } from './auth'; import { connectEventsHandler } from './connectEventsHandler'; @@ -25,6 +26,7 @@ import type { Peer, TrackMetadata, } from './types'; +import { packageVersion } from './version'; const STATISTICS_INTERVAL = 10_000; @@ -146,7 +148,10 @@ export class FishjamClient { this.emit('socketOpen', event); - const message = PeerMessage.encode({ authRequest: { token } }).finish(); + + const sdkVersion = `web-${packageVersion}`; + const message = PeerMessage.encode({ authRequest: { token, sdkVersion } }).finish(); + this.websocket?.send(message); }; @@ -171,14 +176,15 @@ export class FishjamClient { - const message = PeerMessage.encode({ - mediaEvent: { data: mediaEvent }, - }).finish(); - this.websocket?.send(message); + this.webrtc?.on('sendMediaEvent', (mediaEvent) => { + const peerMediaEvent = PeerMediaEvent.decode(mediaEvent); + this.websocket?.send(PeerMessage.encode({ peerMediaEvent }).finish()); }); this.webrtc?.on('connected', async (peerId: string, endpointsInRoom: Endpoint[]) => { @@ -491,8 +495,8 @@ export class FishjamClient { @@ -578,7 +582,7 @@ export class FishjamClient ): Promise { @@ -636,7 +640,7 @@ export class FishjamClient { readonly metadata?: TrackMetadata; readonly maxBandwidth?: TrackBandwidthLimit; readonly vadStatus: VadStatus; - readonly encoding?: Encoding; + readonly encoding?: Variant; readonly encodingReason?: EncodingReason; } diff --git a/packages/ts-client/src/version.ts b/packages/ts-client/src/version.ts new file mode 100644 index 00000000..85d6e4a2 --- /dev/null +++ b/packages/ts-client/src/version.ts @@ -0,0 +1,3 @@ +import { version } from '../package.json'; + +export const packageVersion = version; diff --git a/packages/ts-client/tsconfig.json b/packages/ts-client/tsconfig.json index 36fadb22..07ba7bba 100644 --- a/packages/ts-client/tsconfig.json +++ b/packages/ts-client/tsconfig.json @@ -7,7 +7,6 @@ "jsx": "react-jsx", "strict": true, - "incremental": true, "skipLibCheck": true, "esModuleInterop": true, "declaration": true, @@ -16,7 +15,12 @@ "noFallthroughCasesInSwitch": true, "noUncheckedIndexedAccess": true, - "outDir": "dist" + "outDir": "dist", + "paths": { + "@fishjam-cloud/protobufs/fishjamPeer": ["../protobufs/fishjam/peer_notifications.ts"], + "@fishjam-cloud/protobufs/fishjamServer": ["../protobufs/fishjam/server_notifications.ts"], + "@fishjam-cloud/protobufs/shared": ["../protobufs/fishjam/media_events/shared.ts"] + } }, "include": ["src/**/*", "tests/**/*"] } diff --git a/packages/webrtc-client/package.json b/packages/webrtc-client/package.json index 2004c0e0..880c0a04 100644 --- a/packages/webrtc-client/package.json +++ b/packages/webrtc-client/package.json @@ -11,20 +11,28 @@ "webrtc", "fishjam" ], - "main": "./dist/src/index.js", - "types": "./dist/src/index.d.ts", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", "files": [ - "dist/src/**" + "dist/**" ], + "tsup": { + "entry": [ + "src/index.ts" + ], + "format": [ + "esm" + ] + }, "scripts": { - "build": "tsc", + "build": "yarn build:check && tsup --dts-resolve", "build:check": "tsc --noEmit", "format": "prettier --write . --ignore-path ./.eslintignore", "format:check": "prettier --check . --ignore-path ./.eslintignore", "lint": "eslint . --ext .ts,.tsx --fix", "lint:check": "eslint . --ext .ts,.tsx", "gen:proto": "sh scripts/protobuf.sh", - "test": "vitest run tests/**", + "test": "vitest", "test:e2e": "NODE_OPTIONS=--dns-result-order=ipv4first playwright test", "test:coverage": "vitest run tests/** --coverage", "prepare": "tsc", @@ -37,6 +45,8 @@ "uuid": "^11.0.3" }, "devDependencies": { + "@faker-js/faker": "^9.2.0", + "@fishjam-cloud/protobufs": "workspace:*", "@playwright/test": "^1.49.0", "@types/events": "^3.0.3", "@types/node": "^22.10.0", @@ -45,6 +55,7 @@ "fake-mediastreamtrack": "^1.2.0", "husky": "^9.1.7", "lint-staged": "^15.2.10", + "tsup": "^8.3.5", "typed-emitter": "^2.1.0", "typedoc": "^0.27.0", "typedoc-plugin-external-resolver": "^1.0.3", diff --git a/packages/webrtc-client/src/ConnectionManager.ts b/packages/webrtc-client/src/ConnectionManager.ts index 339133ef..c9a222e7 100644 --- a/packages/webrtc-client/src/ConnectionManager.ts +++ b/packages/webrtc-client/src/ConnectionManager.ts @@ -1,29 +1,14 @@ +import type { MediaEvent_OfferData_TrackTypes } from '@fishjam-cloud/protobufs/server'; import type { MediaStreamTrackId } from './types'; -export type TurnServer = { - transport: string; - password: string; - serverAddr: string; - serverPort: string; - username: string; -}; - export class ConnectionManager { private readonly connection: RTCPeerConnection; - public readonly isExWebRTC: boolean; - - constructor(turnServers: TurnServer[]) { - this.isExWebRTC = turnServers.length === 0; - - const iceServers = this.isExWebRTC - ? [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun.l.google.com:5349' }] - : this.getIceServers(turnServers); - const iceTransportPolicy = this.isExWebRTC ? 'all' : 'relay'; + constructor(iceServers: RTCIceServer[]) { this.connection = new RTCPeerConnection({ bundlePolicy: 'max-bundle', iceServers: iceServers, - iceTransportPolicy: iceTransportPolicy, + iceTransportPolicy: 'all', }); } @@ -37,46 +22,22 @@ export class ConnectionManager { return isSignalingUnstable && isConnectionNotConnected && isIceNotConnected; }; - /** - * Configures TURN servers for WebRTC connections by adding them to the provided RTCConfiguration object. - */ - private getIceServers = (turnServers: TurnServer[]): RTCIceServer[] => { - return turnServers.map((turnServer: TurnServer) => { - const transport = turnServer.transport === 'tls' ? 'tcp' : turnServer.transport; - const uri = turnServer.transport === 'tls' ? 'turns' : 'turn'; - const address = turnServer.serverAddr; - const port = turnServer.serverPort; - - return { - credential: turnServer.password, - urls: uri.concat(':', address, ':', port, '?transport=', transport), - username: turnServer.username, - }; - }); - }; - public getConnection = (): RTCPeerConnection => { return this.connection; }; - public addTransceiversIfNeeded = (serverTracks: Map) => { + public addTransceiversIfNeeded = (serverTracks: MediaEvent_OfferData_TrackTypes) => { const recvTransceivers = this.connection.getTransceivers().filter((elem) => elem.direction === 'recvonly'); - ['audio', 'video'] - .flatMap((type) => this.getNeededTransceiversTypes(type, recvTransceivers, serverTracks)) - .forEach((kind) => this.connection.addTransceiver(kind, { direction: 'recvonly' })); - }; - - private getNeededTransceiversTypes = ( - type: string, - recvTransceivers: RTCRtpTransceiver[], - serverTracks: Map, - ): string[] => { - const typeNumber = serverTracks.get(type) ?? 0; + const videoTransceiversAmount = recvTransceivers.filter((elem) => elem.receiver.track.kind === 'video').length; + const audioTransceiversAmount = recvTransceivers.filter((elem) => elem.receiver.track.kind === 'audio').length; - const typeTransceiversNumber = recvTransceivers.filter((elem) => elem.receiver.track.kind === type).length; + const videoNeededTypes = Array(serverTracks.video - videoTransceiversAmount).fill('video'); + const audioNeededTypes = Array(serverTracks.audio - audioTransceiversAmount).fill('audio'); - return Array(typeNumber - typeTransceiversNumber).fill(type); + [...videoNeededTypes, ...audioNeededTypes].forEach((kind) => + this.connection.addTransceiver(kind, { direction: 'recvonly' }), + ); }; public addTransceiver = (track: MediaStreamTrack, transceiverConfig: RTCRtpTransceiverInit) => { diff --git a/packages/webrtc-client/src/bitrate.ts b/packages/webrtc-client/src/bitrate.ts index d52f84d0..b01e351b 100644 --- a/packages/webrtc-client/src/bitrate.ts +++ b/packages/webrtc-client/src/bitrate.ts @@ -1,8 +1,8 @@ -import type { Encoding } from './types'; +import { Variant } from '@fishjam-cloud/protobufs/shared'; import { type BandwidthLimit } from './types'; export type Bitrate = number; -export type Bitrates = Record | Bitrate; +export type Bitrates = Record | Bitrate; // The suggested bitrate values are based on our internal tests. export const defaultBitrates = { @@ -14,9 +14,11 @@ export const UNLIMITED_BANDWIDTH: Bitrate = 0 as Bitrate; // The suggested bitrate values are based on our internal tests. export const defaultSimulcastBitrates: { - [key in Encoding]: BandwidthLimit; + [key in Variant]: BandwidthLimit; } = { - h: 2_500_000, - m: 500_000, - l: 150_000, + [Variant.VARIANT_HIGH]: 2_500_000, + [Variant.VARIANT_MEDIUM]: 500_000, + [Variant.VARIANT_LOW]: 150_000, + [Variant.VARIANT_UNSPECIFIED]: 0, + [Variant.UNRECOGNIZED]: 0, }; diff --git a/packages/webrtc-client/src/index.ts b/packages/webrtc-client/src/index.ts index 5396c4cd..7a9f1514 100644 --- a/packages/webrtc-client/src/index.ts +++ b/packages/webrtc-client/src/index.ts @@ -5,14 +5,15 @@ export type { WebRTCEndpointEvents, TrackContextEvents, Endpoint, - SimulcastConfig, TrackContext, - Encoding, VadStatus, EncodingReason, TrackKind, } from './types'; +export { Variant } from '@fishjam-cloud/protobufs/shared'; +export { MediaEvent_Track_SimulcastConfig as SimulcastConfig } from '@fishjam-cloud/protobufs/server'; + export { WebRTCEndpoint } from './webRTCEndpoint'; export type { SerializedMediaEvent, MediaEvent } from './mediaEvent'; diff --git a/packages/webrtc-client/src/internal.ts b/packages/webrtc-client/src/internal.ts index 65457df4..6f6bdb6a 100644 --- a/packages/webrtc-client/src/internal.ts +++ b/packages/webrtc-client/src/internal.ts @@ -3,15 +3,15 @@ import type TypedEmitter from 'typed-emitter'; import type { EncodingReason, Endpoint, - SimulcastConfig, TrackBandwidthLimit, TrackContext, TrackContextEvents, - Encoding, TrackKind, TrackNegotiationStatus, VadStatus, } from './types'; +import type { MediaEvent_Track_SimulcastConfig } from '@fishjam-cloud/protobufs/server'; +import type { Variant } from '@fishjam-cloud/protobufs/shared'; export const isTrackKind = (kind: string): kind is TrackKind => kind === 'audio' || kind === 'video'; @@ -26,9 +26,9 @@ export class TrackContextImpl stream: MediaStream | null = null; metadata?: unknown; metadataParsingError?: any; - simulcastConfig?: SimulcastConfig; + simulcastConfig?: MediaEvent_Track_SimulcastConfig; maxBandwidth: TrackBandwidthLimit = 0; - encoding?: Encoding; + encoding?: Variant; encodingReason?: EncodingReason; vadStatus: VadStatus = 'silence'; negotiationStatus: TrackNegotiationStatus = 'awaiting'; @@ -37,7 +37,12 @@ export class TrackContextImpl // and `updateTrackMetadata` Media Event should be sent after the transition to "done" pendingMetadataUpdate: boolean = false; - constructor(endpoint: Endpoint, trackId: string, metadata: any, simulcastConfig: SimulcastConfig) { + constructor( + endpoint: Endpoint, + trackId: string, + metadata: any, + simulcastConfig: MediaEvent_Track_SimulcastConfig = { enabled: false, enabledVariants: [], disabledVariants: [] }, + ) { super(); this.endpoint = endpoint; this.trackId = trackId; diff --git a/packages/webrtc-client/src/mediaEvent.ts b/packages/webrtc-client/src/mediaEvent.ts index c888434e..758729b9 100644 --- a/packages/webrtc-client/src/mediaEvent.ts +++ b/packages/webrtc-client/src/mediaEvent.ts @@ -1,27 +1,34 @@ -export type SerializedMediaEvent = string; +import { MediaEvent as ServerMediaEvent } from '@fishjam-cloud/protobufs/server'; +import { MediaEvent as PeerMediaEvent } from '@fishjam-cloud/protobufs/peer'; + +export type SerializedMediaEvent = Uint8Array; export interface MediaEvent { - type: string; + type: keyof PeerMediaEvent; key?: string; data?: any; } -export function serializeMediaEvent(mediaEvent: MediaEvent): SerializedMediaEvent { - return JSON.stringify(mediaEvent); +export function serializePeerMediaEvent(mediaEvent: PeerMediaEvent): Uint8Array { + const encodedEvent = PeerMediaEvent.encode(mediaEvent).finish(); + return encodedEvent; +} + +export function serializeServerMediaEvent(mediaEvent: ServerMediaEvent): Uint8Array { + const encodedEvent = ServerMediaEvent.encode(mediaEvent).finish(); + return encodedEvent; } -export function deserializeMediaEvent(serializedMediaEvent: SerializedMediaEvent): MediaEvent { - return JSON.parse(serializedMediaEvent) as MediaEvent; +export function deserializeServerMediaEvent(serializedMediaEvent: SerializedMediaEvent): ServerMediaEvent { + return ServerMediaEvent.decode(serializedMediaEvent); } -export function generateMediaEvent(type: string, data?: any): MediaEvent { - let event: MediaEvent = { type }; - if (data) { - event = { ...event, data }; - } - return event; +export function deserializePeerMediaEvent(serializedMediaEvent: SerializedMediaEvent): PeerMediaEvent { + return PeerMediaEvent.decode(serializedMediaEvent); } -export function generateCustomEvent(data?: any): MediaEvent { - return generateMediaEvent('custom', data); +export function generateMediaEvent(type: T, data?: PeerMediaEvent[T]): MediaEvent { + const mediaEvent: MediaEvent = { type, data }; + + return mediaEvent; } diff --git a/packages/webrtc-client/src/tracks/Local.ts b/packages/webrtc-client/src/tracks/Local.ts index 01499fa2..41ae8d99 100644 --- a/packages/webrtc-client/src/tracks/Local.ts +++ b/packages/webrtc-client/src/tracks/Local.ts @@ -1,23 +1,27 @@ import { LocalTrack } from './LocalTrack'; import type { BandwidthLimit, - Encoding, + LocalTrackId, + MetadataJson, MLineId, RemoteTrackId, - SimulcastConfig, TrackBandwidthLimit, WebRTCEndpointEvents, } from '../types'; import type { EndpointWithTrackContext } from '../internal'; import { isTrackKind, TrackContextImpl } from '../internal'; -import type { MediaEvent } from '../mediaEvent'; -import { generateCustomEvent, generateMediaEvent } from '../mediaEvent'; import type { ConnectionManager } from '../ConnectionManager'; -import type { Bitrates } from '../bitrate'; import type { EndpointId, TrackId } from './TrackCommon'; import type { WebRTCEndpoint } from '../webRTCEndpoint'; - -export type MidToTrackId = Record; +import type { MediaEvent_TrackBitrates, MediaEvent as PeerMediaEvent } from '@fishjam-cloud/protobufs/peer'; +import { + MediaEvent_SdpOffer, + MediaEvent_RenegotiateTracks, + MediaEvent_UpdateEndpointMetadata, + MediaEvent_UpdateTrackMetadata, +} from '@fishjam-cloud/protobufs/peer'; +import type { MediaEvent_Track_SimulcastConfig } from '@fishjam-cloud/protobufs/server'; +import type { Variant } from '@fishjam-cloud/protobufs/shared'; /** * This class encapsulates methods related to handling the list of local tracks and local endpoint. @@ -39,7 +43,7 @@ export class Local { event: E, ...args: Parameters[E]> ) => void; - private readonly sendMediaEvent: (mediaEvent: MediaEvent) => void; + private readonly sendMediaEvent: (mediaEvent: PeerMediaEvent) => void; private connection: ConnectionManager | null = null; @@ -48,7 +52,7 @@ export class Local { event: E, ...args: Parameters[E]> ) => void, - sendMediaEvent: (mediaEvent: MediaEvent) => void, + sendMediaEvent: (mediaEvent: PeerMediaEvent) => void, ) { this.emit = emit; this.sendMediaEvent = sendMediaEvent; @@ -60,12 +64,9 @@ export class Local { }); }; - public updateMLineIds = (midToTrackId: MidToTrackId) => { - Object.entries(midToTrackId).forEach(([mLineId, trackId]) => { - const localTrack = this.localTracks[trackId]; - if (localTrack) { - localTrack.setMLineId(mLineId); - } + public updateMLineIds = (midToTrackIds: Record) => { + Object.entries(midToTrackIds).forEach(([mid, trackId]) => { + this.localTracks[trackId]?.setMLineId(mid); }); }; @@ -77,19 +78,16 @@ export class Local { }); }; - public createSdpOfferEvent = (offer: RTCSessionDescriptionInit): MediaEvent => { - const trackIdToTrackMetadata = this.getTrackIdToMetadata(); - const trackIdToTrackBitrates = this.getTrackIdToTrackBitrates(); + public createSdpOfferEvent = (sdpOffer: RTCSessionDescriptionInit): MediaEvent_SdpOffer => { + const trackIdToMetadataJson = this.getTrackIdToMetadataJson(); + const trackIdToBitrates = this.getTrackIdToTrackBitrates(); const midToTrackId = this.getMidToTrackId(); - return generateCustomEvent({ - type: 'sdpOffer', - data: { - sdpOffer: offer, - trackIdToTrackMetadata, - trackIdToTrackBitrates, - midToTrackId, - }, + return MediaEvent_SdpOffer.create({ + sdp: sdpOffer.sdp, + midToTrackId, + trackIdToBitrates, + trackIdToMetadataJson, }); }; @@ -99,7 +97,7 @@ export class Local { track: MediaStreamTrack, stream: MediaStream, trackMetadata: unknown | undefined, - simulcastConfig: SimulcastConfig, + simulcastConfig: MediaEvent_Track_SimulcastConfig, maxBandwidth: TrackBandwidthLimit, ): LocalTrack => { const trackContext = new TrackContextImpl(this.localEndpoint, trackId, trackMetadata, simulcastConfig); @@ -128,8 +126,8 @@ export class Local { trackManager.removeFromConnection(); - const mediaEvent = generateCustomEvent({ type: 'renegotiateTracks' }); - this.sendMediaEvent(mediaEvent); + const renegotiateTracks = MediaEvent_RenegotiateTracks.create({}); + this.sendMediaEvent({ renegotiateTracks }); this.localEndpoint.tracks.delete(trackId); delete this.localTracks[trackId]; @@ -159,13 +157,14 @@ export class Local { if (!trackManager) throw new Error(`Cannot find ${trackId}`); await trackManager.setTrackBandwidth(bandwidth); - const mediaEvent = trackManager.createTrackVariantBitratesEvent(); - - this.sendMediaEvent(mediaEvent); - this.emit('localTrackBandwidthSet', { - trackId, - bandwidth, - }); + // const mediaEvent = trackManager.createTrackVariantBitratesEvent(); + + // TODO add when simulcast is available + // this.sendMediaEvent(mediaEvent); + // this.emit('localTrackBandwidthSet', { + // trackId, + // bandwidth, + // }); }; public getTrackIdToTrack = (): Map => { @@ -179,20 +178,21 @@ export class Local { this.localEndpoint.id = endpointId; }; - public setEncodingBandwidth = async (trackId: TrackId, rid: Encoding, bandwidth: BandwidthLimit): Promise => { + public setEncodingBandwidth = async (trackId: TrackId, rid: Variant, bandwidth: BandwidthLimit): Promise => { const trackManager = this.localTracks[trackId]; if (!trackManager) throw new Error(`Cannot find ${trackId}`); await trackManager.setEncodingBandwidth(rid, bandwidth); - const mediaEvent = generateCustomEvent({ - type: 'trackVariantBitrates', - data: { - trackId: trackId, - variantBitrates: trackManager.getTrackBitrates(), - }, - }); - this.sendMediaEvent(mediaEvent); + // TODO add when simulcast is available + // const mediaEvent = generateCustomEvent({ + // type: 'trackVariantBitrates', + // data: { + // trackId: trackId, + // variantBitrates: trackManager.getTrackBitrates(), + // }, + // }); + // this.sendMediaEvent(mediaEvent); this.emit('localTrackEncodingBandwidthSet', { trackId, @@ -204,10 +204,11 @@ export class Local { public updateEndpointMetadata = (metadata: unknown) => { this.localEndpoint.metadata = metadata; - const mediaEvent = generateMediaEvent('updateEndpointMetadata', { - metadata: this.localEndpoint.metadata, + const updateEndpointMetadata = MediaEvent_UpdateEndpointMetadata.create({ + metadataJson: JSON.stringify(this.localEndpoint.metadata), }); - this.sendMediaEvent(mediaEvent); + + this.sendMediaEvent({ updateEndpointMetadata }); this.emit('localEndpointMetadataChanged', { metadata: this.localEndpoint.metadata, }); @@ -220,15 +221,14 @@ export class Local { trackManager.updateTrackMetadata(metadata); const trackContext = trackManager.trackContext; - - const mediaEvent = generateMediaEvent('updateTrackMetadata', { + const updateTrackMetadata = MediaEvent_UpdateTrackMetadata.create({ trackId, - trackMetadata: metadata, + metadataJson: metadata ? JSON.stringify(metadata) : undefined, }); switch (trackContext.negotiationStatus) { case 'done': - this.sendMediaEvent(mediaEvent); + this.sendMediaEvent({ updateTrackMetadata }); this.emit('localTrackMetadataChanged', { trackId, @@ -246,70 +246,55 @@ export class Local { } }; - public disableLocalTrackEncoding = async (trackId: string, encoding: Encoding): Promise => { + public disableLocalTrackEncoding = async (trackId: string, encoding: Variant): Promise => { const localTrack = this.localTracks[trackId]; if (!localTrack) throw new Error(`Track ${trackId} not found`); await localTrack.disableTrackEncoding(encoding); - const mediaEvent = generateMediaEvent('disableTrackEncoding', { - trackId: trackId, - encoding: encoding, - }); + // TODO add when simulcast is available + // const mediaEvent = generateMediaEvent('disableTrackEncoding', { + // trackId: trackId, + // encoding: encoding, + // }); - this.sendMediaEvent(mediaEvent); + // this.sendMediaEvent(mediaEvent); this.emit('localTrackEncodingEnabled', { trackId, encoding, }); }; - public enableLocalTrackEncoding = async (trackId: TrackId, encoding: Encoding): Promise => { + public enableLocalTrackEncoding = async (trackId: TrackId, encoding: Variant): Promise => { const trackManager = this.localTracks[trackId]; if (!trackManager) throw new Error(`Cannot find ${trackId}`); await trackManager.enableTrackEncoding(encoding); + // TODO add when simulcast is available + // const mediaEvent = generateMediaEvent('enableTrackEncoding', { + // trackId: trackId, + // encoding: encoding, + // }); - const mediaEvent = generateMediaEvent('enableTrackEncoding', { - trackId: trackId, - encoding: encoding, - }); - - this.sendMediaEvent(mediaEvent); + // this.sendMediaEvent(mediaEvent); this.emit('localTrackEncodingEnabled', { trackId, encoding, }); }; - private getTrackIdToMetadata = (): Record => { - return Object.values(this.localTracks).reduce( - (previousValue, localTrack) => ({ - ...previousValue, - [localTrack.id]: localTrack.trackContext.metadata, - }), - {} as Record, + private getTrackIdToMetadataJson = (): Record => + Object.values(this.localTracks).reduce( + (acc, { id, trackContext }) => ({ ...acc, [id]: JSON.stringify(trackContext.metadata) }), + {}, ); - }; - private getTrackIdToTrackBitrates = (): Record => { - return Object.values(this.localTracks).reduce( - (previousValue, localTrack) => { - const bitrates = localTrack.getTrackBitrates(); - if (bitrates) { - return { - ...previousValue, - [localTrack.id]: bitrates, - }; - } - return previousValue; - }, - {} as Record, - ); - }; + // TODO add bitrates + private getTrackIdToTrackBitrates = (): Record => + Object.values(this.localTracks).reduce((acc, { id }) => ({ ...acc, [id]: { bitrate: 500 } }), {}); - private getMidToTrackId = (): MidToTrackId | null => { - if (!this.connection) return null; + private getMidToTrackId = (): Record => { + if (!this.connection) return {}; // - negotiated unmuted tracks: tracks added in previous negotiation, data is being transmitted // - not yet negotiated tracks: tracks added in this negotiation, data will be transmitted after successful negotiation @@ -317,36 +302,28 @@ export class Local { // - negotiated unmuted tracks: tracks added in previous negotiation, data is being transmitted // - negotiated muted tracks: tracks added in previous negotiation, data is not being transmitted but can be transmitted in the future - const mappingFromLocalNegotiatedTracks = Object.values(this.localTracks).reduce((acc, curr) => { - if (curr.mLineId) { - acc[curr.mLineId] = curr.id; - } - - return acc; - }, {} as MidToTrackId); + const mappingFromLocalNegotiatedTracks = Object.values(this.localTracks) + .filter((track): track is LocalTrack & { mLineId: string } => !!track.mLineId) + .reduce((acc, { id, mLineId }) => ({ ...acc, [mLineId]: id }), {}); return { ...mappingFromTransceivers, ...mappingFromLocalNegotiatedTracks }; }; - private getTransceiverMapping = (): MidToTrackId => { + private getTransceiverMapping = (): Record => { if (!this.connection) return {}; return this.connection .getConnection() .getTransceivers() - .filter((transceiver) => transceiver.sender.track?.id && transceiver.mid) - .reduce((acc, transceiver) => { - const localTrackId = transceiver.sender.track!.id; - const mid = transceiver!.mid!; - - const localTrack = Object.values(this.localTracks).find((track) => track.mediaStreamTrackId === localTrackId); - + .filter((transceiver) => Boolean(transceiver.sender.track?.id && transceiver.mid)) + .reduce((acc, { sender, mid }) => { + const localTrack = Object.values(this.localTracks).find( + (track) => track.mediaStreamTrackId === sender.track!.id, + ); if (!localTrack) throw new Error('Local track not found'); - acc[mid] = localTrack.trackContext.trackId; - - return acc; - }, {} as MidToTrackId); + return { ...acc, [mid!]: localTrack.id }; + }, {}); }; public setLocalTrackStatusToOffered = () => { diff --git a/packages/webrtc-client/src/tracks/LocalTrack.ts b/packages/webrtc-client/src/tracks/LocalTrack.ts index 5be2b531..403463fd 100644 --- a/packages/webrtc-client/src/tracks/LocalTrack.ts +++ b/packages/webrtc-client/src/tracks/LocalTrack.ts @@ -1,14 +1,15 @@ import type { TrackContextImpl } from '../internal'; -import type { BandwidthLimit, Encoding, LocalTrackId, MediaStreamTrackId, MLineId, TrackKind } from '../types'; +import type { BandwidthLimit, LocalTrackId, MediaStreamTrackId, MLineId, TrackKind } from '../types'; import type { TrackCommon, TrackEncodings, TrackId } from './TrackCommon'; -import { generateCustomEvent } from '../mediaEvent'; +// import { generateCustomEvent } from '../mediaEvent'; import type { WebRTCEndpoint } from '../webRTCEndpoint'; import type { Bitrate, Bitrates } from '../bitrate'; import { defaultBitrates, defaultSimulcastBitrates, UNLIMITED_BANDWIDTH } from '../bitrate'; import type { ConnectionManager } from '../ConnectionManager'; -import { getEncodingParameters } from './encodings'; +import { encodingToVariantMap, getEncodingParameters } from './encodings'; import { createTransceiverConfig } from './transceivers'; import { emitMutableEvents, getActionType } from './muteTrackUtils'; +import { Variant } from '@fishjam-cloud/protobufs/shared'; /** * This is a wrapper over `TrackContext` that adds additional properties such as: @@ -45,7 +46,13 @@ export class LocalTrack implements TrackCommon { public mLineId: MLineId | null = null; public readonly trackContext: TrackContextImpl; private sender: RTCRtpSender | null = null; - public readonly encodings: TrackEncodings; + public readonly encodings: TrackEncodings = { + [Variant.UNRECOGNIZED]: false, + [Variant.VARIANT_UNSPECIFIED]: false, + [Variant.VARIANT_LOW]: false, + [Variant.VARIANT_MEDIUM]: false, + [Variant.VARIANT_HIGH]: false, + }; public connection: ConnectionManager | undefined; @@ -55,7 +62,6 @@ export class LocalTrack implements TrackCommon { this.trackContext = trackContext; // todo maybe we could remove this object and use sender.getParameters().encodings.encodingParameter.active instead - this.encodings = { h: true, m: true, l: true }; if (trackContext.track?.id) { this.mediaStreamTrackId = trackContext.track?.id; } @@ -71,13 +77,13 @@ export class LocalTrack implements TrackCommon { this.connection = connection; }; - public disableTrackEncoding = async (encoding: Encoding) => { + public disableTrackEncoding = async (encoding: Variant) => { if (!this.sender) throw new Error(`RTCRtpSender for track ${this.id} not found`); const params = this.sender.getParameters(); const encodings = params.encodings; - const encodingParameter = encodings.find((en) => en.rid == encoding); + const encodingParameter = encodings.find((en) => en.rid && encodingToVariantMap[en.rid] === encoding); if (!encodingParameter) throw new Error(`RTCRtpEncodingParameters for track ${this.id} not found`); @@ -100,12 +106,11 @@ export class LocalTrack implements TrackCommon { }; private updateEncodings = () => { - if (this.trackContext?.track?.kind === 'video' && this.trackContext.simulcastConfig?.activeEncodings) { - const activeEncodings = this.trackContext.simulcastConfig.activeEncodings; - - this.encodings.l = activeEncodings.some((e) => e === 'l'); - this.encodings.m = activeEncodings.some((e) => e === 'm'); - this.encodings.h = activeEncodings.some((e) => e === 'h'); + const enabledVariants = this.trackContext.simulcastConfig?.enabledVariants; + if (this.trackContext?.track?.kind === 'video' && enabledVariants) { + this.encodings[Variant.VARIANT_LOW] = enabledVariants.some((e) => e === Variant.VARIANT_LOW); + this.encodings[Variant.VARIANT_MEDIUM] = enabledVariants.some((e) => e === Variant.VARIANT_MEDIUM); + this.encodings[Variant.VARIANT_HIGH] = enabledVariants.some((e) => e === Variant.VARIANT_HIGH); } }; @@ -164,14 +169,16 @@ export class LocalTrack implements TrackCommon { return this.sender.setParameters(parameters); }; - public setEncodingBandwidth(rid: Encoding, bandwidth: BandwidthLimit): Promise { + public setEncodingBandwidth(variant: Variant, bandwidth: BandwidthLimit): Promise { if (!this.sender) throw new Error(`RTCRtpSender for track ${this.id} not found`); const parameters = this.sender.getParameters(); - const encoding = parameters.encodings.find((encoding) => encoding.rid === rid); + const encoding = parameters.encodings.find( + (encoding) => encoding.rid && encodingToVariantMap[encoding.rid] === variant, + ); if (!encoding) { - return Promise.reject(`Encoding with rid '${rid}' doesn't exist`); + return Promise.reject(`Encoding with Variant '${variant}' doesn't exist`); } else if (bandwidth === 0) { delete encoding.maxBitrate; } else { @@ -185,11 +192,11 @@ export class LocalTrack implements TrackCommon { this.trackContext.metadata = metadata; }; - public enableTrackEncoding = (encoding: Encoding) => { + public enableTrackEncoding = (encoding: Variant) => { if (!this.sender) throw new Error(`RTCRtpSender for track ${this.id} not found`); const params = this.sender.getParameters(); - const encodingParameters = params.encodings.find((en) => en.rid == encoding); + const encodingParameters = params.encodings.find((en) => en.rid && encodingToVariantMap[en.rid] === encoding); if (!encodingParameters) throw new Error(`RTCRtEncodingParameters ${encoding} for track ${this.id} not found`); @@ -232,21 +239,23 @@ export class LocalTrack implements TrackCommon { .filter((encoding) => encoding.rid) .reduce( (acc, encoding) => { - const rid = encoding.rid! as Encoding; - acc[rid] = encoding.maxBitrate || defaultSimulcastBitrates[rid]; + const variant = encodingToVariantMap[encoding.rid!] ?? Variant.VARIANT_UNSPECIFIED; + + acc[variant] = encoding.maxBitrate || defaultSimulcastBitrates[variant]; return acc; }, - {} as Record, + {} as Record, ); }; public createTrackVariantBitratesEvent = () => { - return generateCustomEvent({ - type: 'trackVariantBitrates', - data: { - trackId: this.id, - variantBitrates: this.getTrackBitrates(), - }, - }); + // TODO implement this when simulcast is supported + // return generateCustomEvent({ + // type: 'trackVariantBitrates', + // data: { + // trackId: this.id, + // variantBitrates: this.getTrackBitrates(), + // }, + // }); }; } diff --git a/packages/webrtc-client/src/tracks/LocalTrackManager.ts b/packages/webrtc-client/src/tracks/LocalTrackManager.ts index a5b083ee..e1295947 100644 --- a/packages/webrtc-client/src/tracks/LocalTrackManager.ts +++ b/packages/webrtc-client/src/tracks/LocalTrackManager.ts @@ -1,8 +1,9 @@ -import type { SimulcastConfig, TrackBandwidthLimit } from '../types'; -import { generateCustomEvent, type MediaEvent } from '../mediaEvent'; +import type { TrackBandwidthLimit } from '../types'; import type { ConnectionManager } from '../ConnectionManager'; import type { Local } from './Local'; import type { WebRTCEndpoint } from '../webRTCEndpoint'; +import { MediaEvent_RenegotiateTracks, type MediaEvent as PeerMediaEvent } from '@fishjam-cloud/protobufs/peer'; +import type { SimulcastConfig } from '..'; /** * This class is responsible for handling asynchronous operations related to track management. @@ -28,9 +29,9 @@ export class LocalTrackManager { */ public ongoingRenegotiation: boolean = false; - private readonly sendMediaEvent: (mediaEvent: MediaEvent) => void; + private readonly sendMediaEvent: (mediaEvent: PeerMediaEvent) => void; - constructor(local: Local, sendMediaEvent: (mediaEvent: MediaEvent) => void) { + constructor(local: Local, sendMediaEvent: (mediaEvent: PeerMediaEvent) => void) { this.local = local; this.sendMediaEvent = sendMediaEvent; } @@ -86,8 +87,7 @@ export class LocalTrackManager { trackManager.addTrackToConnection(); } - const mediaEvent = generateCustomEvent({ type: 'renegotiateTracks' }); - this.sendMediaEvent(mediaEvent); + this.sendMediaEvent({ renegotiateTracks: MediaEvent_RenegotiateTracks.create() }); }; public removeTrackHandler = (trackId: string) => { diff --git a/packages/webrtc-client/src/tracks/Remote.ts b/packages/webrtc-client/src/tracks/Remote.ts index e25a380b..6e61df8e 100644 --- a/packages/webrtc-client/src/tracks/Remote.ts +++ b/packages/webrtc-client/src/tracks/Remote.ts @@ -1,15 +1,20 @@ -import type { Encoding, EncodingReason, MLineId, SimulcastConfig, TrackContext, WebRTCEndpointEvents } from '../types'; +import type { + EncodingReason, + MetadataJson, + RemoteTrackId, + TrackContext, + VadStatus, + WebRTCEndpointEvents, +} from '../types'; import { RemoteTrack } from './RemoteTrack'; import type { EndpointWithTrackContext } from '../internal'; import { TrackContextImpl } from '../internal'; -import { isVadStatus } from '../voiceActivityDetection'; -import { generateCustomEvent, type MediaEvent } from '../mediaEvent'; import type { EndpointId, TrackId } from './TrackCommon'; - -type SDPTrack = { - metadata: null; - simulcastConfig: SimulcastConfig; -}; +import type { MediaEvent_Track } from '@fishjam-cloud/protobufs/server'; +import type { MediaEvent as PeerMediaEvent } from '@fishjam-cloud/protobufs/peer'; +import { MediaEvent_EnableTrackVariant } from '@fishjam-cloud/protobufs/peer'; +import { MediaEvent_VadNotification_Status } from '@fishjam-cloud/protobufs/server'; +import type { Variant } from '@fishjam-cloud/protobufs/shared'; export class Remote { private readonly remoteTracks: Record = {}; @@ -19,14 +24,14 @@ export class Remote { event: E, ...args: Parameters[E]> ) => void; - private readonly sendMediaEvent: (mediaEvent: MediaEvent) => void; + private readonly sendMediaEvent: (mediaEvent: PeerMediaEvent) => void; constructor( emit: >( event: E, ...args: Parameters[E]> ) => void, - sendMediaEvent: (mediaEvent: MediaEvent) => void, + sendMediaEvent: (mediaEvent: PeerMediaEvent) => void, ) { this.emit = emit; this.sendMediaEvent = sendMediaEvent; @@ -38,18 +43,16 @@ export class Remote { return remoteTrack; }; - public addTracks = ( - endpointId: EndpointId, - tracks: Record, - trackIdToMetadata: Record, - ) => { + public addTracks = (endpointId: EndpointId, tracks: Record) => { const endpoint: EndpointWithTrackContext | undefined = this.remoteEndpoints[endpointId]; if (!endpoint) throw new Error(`Endpoint ${endpointId} not found`); - Object.entries(tracks || {}) - .map(([trackId, { simulcastConfig }]) => { - const trackContext = new TrackContextImpl(endpoint, trackId, trackIdToMetadata[trackId], simulcastConfig); + Object.entries(tracks) + .map(([trackId, { metadataJson, simulcastConfig }]) => { + const parsedMetadata = metadataJson ? JSON.parse(metadataJson) : undefined; + + const trackContext = new TrackContextImpl(endpoint, trackId, parsedMetadata, simulcastConfig); return new RemoteTrack(trackId, trackContext); }) @@ -79,18 +82,21 @@ export class Remote { this.emit('trackRemoved', remoteTrack.trackContext); }; - public addRemoteEndpoint = (endpoint: any, sendNotification: boolean = true) => { - const newEndpoint: EndpointWithTrackContext = { - id: endpoint.id, - type: endpoint.type, - metadata: undefined, + public addRemoteEndpoint = ( + endpointId: string, + metadataJson?: MetadataJson, + tracks?: Record, + sendNotification: boolean = true, + ) => { + const endpoint = { + id: endpointId, + type: 'exwebrtc', + metadata: metadataJson ? JSON.parse(metadataJson) : undefined, tracks: new Map(), - }; + } satisfies EndpointWithTrackContext; - newEndpoint.metadata = endpoint?.metadata; - - this.addEndpoint(newEndpoint); - this.addTracks(newEndpoint.id, endpoint.tracks, endpoint.trackIdToMetadata); + this.addEndpoint(endpoint); + this.addTracks(endpoint.id, tracks ?? {}); if (sendNotification) { this.emit('endpointAdded', endpoint); @@ -101,11 +107,11 @@ export class Remote { this.remoteEndpoints[endpoint.id] = endpoint; }; - public updateRemoteEndpoint = (data: any) => { - const endpoint: EndpointWithTrackContext | undefined = this.remoteEndpoints[data.id]; - if (!endpoint) throw new Error(`Endpoint ${data.id} not found`); + public updateRemoteEndpoint = (endpointId: string, metadataJson?: MetadataJson) => { + const endpoint: EndpointWithTrackContext | undefined = this.remoteEndpoints[endpointId]; + if (!endpoint) throw new Error(`Endpoint ${endpointId} not found`); - endpoint.metadata = data.metadata; + endpoint.metadata = metadataJson ? JSON.parse(metadataJson) : undefined; this.emit('endpointUpdated', endpoint); }; @@ -123,22 +129,18 @@ export class Remote { this.emit('endpointRemoved', endpoint); }; - public updateRemoteTrack = (data: any) => { - const endpointId = data.endpointId; - const endpoint: EndpointWithTrackContext | undefined = this.remoteEndpoints[endpointId]; - if (!endpoint) throw new Error(`Endpoint ${endpointId} not found`); - - const trackId = data.trackId; + public updateRemoteTrack = (endpointId: string, trackId: string, metadataJson?: MetadataJson) => { + if (!this.remoteEndpoints[endpointId]) throw new Error(`Endpoint ${endpointId} not found`); const remoteTrack = this.remoteTracks[trackId]; if (!remoteTrack) throw new Error(`Track ${trackId} not found`); - remoteTrack.trackContext.metadata = data.metadata; + remoteTrack.trackContext.metadata = metadataJson ? JSON.parse(metadataJson) : undefined; this.emit('trackUpdated', remoteTrack.trackContext); }; - public disableRemoteTrackEncoding = (trackId: TrackId, encoding: Encoding) => { + public disableRemoteTrackEncoding = (trackId: TrackId, encoding: Variant) => { const remoteTrack = this.remoteTracks[trackId]; if (!remoteTrack) throw new Error(`Track ${trackId} not found`); @@ -147,7 +149,7 @@ export class Remote { this.emit('trackEncodingDisabled', remoteTrack.trackContext, encoding); }; - public enableRemoteTrackEncoding = (trackId: TrackId, encoding: Encoding) => { + public enableRemoteTrackEncoding = (trackId: TrackId, encoding: Variant) => { const remoteTrack = this.remoteTracks[trackId]; if (!remoteTrack) throw new Error(`Track ${trackId} not found`); @@ -156,7 +158,7 @@ export class Remote { this.emit('trackEncodingEnabled', remoteTrack.trackContext, encoding); }; - public setRemoteTrackEncoding = (trackId: TrackId, encoding: Encoding, reason: EncodingReason) => { + public setRemoteTrackEncoding = (trackId: TrackId, encoding: Variant, reason?: EncodingReason) => { const remoteTrack = this.remoteTracks[trackId]; if (!remoteTrack) throw new Error(`Track ${trackId} not found`); @@ -166,12 +168,20 @@ export class Remote { remoteTrack.trackContext.emit('encodingChanged', remoteTrack.trackContext); }; - public setRemoteTrackVadStatus = (trackId: TrackId, vadStatus: string) => { + public setRemoteTrackVadStatus = (trackId: TrackId, vadStatus: MediaEvent_VadNotification_Status) => { const remoteTrack = this.remoteTracks[trackId]; if (!remoteTrack) throw new Error(`Track ${trackId} not found`); - if (isVadStatus(vadStatus)) { - remoteTrack.trackContext.vadStatus = vadStatus; + let nextStatus: VadStatus | null = null; + + if (vadStatus === MediaEvent_VadNotification_Status.STATUS_SILENCE) { + nextStatus = 'silence'; + } else if (vadStatus === MediaEvent_VadNotification_Status.STATUS_SPEECH) { + nextStatus = 'speech'; + } + + if (nextStatus) { + remoteTrack.trackContext.vadStatus = nextStatus; remoteTrack.trackContext.emit('voiceActivityChanged', remoteTrack.trackContext); } else { console.warn('Received unknown vad status: ', vadStatus); @@ -195,22 +205,15 @@ export class Remote { ); }; - public setTargetRemoteTrackEncoding = (trackId: TrackId, variant: Encoding) => { + public setTargetRemoteTrackEncoding = (trackId: TrackId, variant: Variant) => { const remoteTrack = this.remoteTracks[trackId]; if (!remoteTrack) throw new Error(`Track ${trackId} not found`); try { remoteTrack.setTargetTrackEncoding(variant); - const mediaEvent = generateCustomEvent({ - type: 'setTargetTrackVariant', - data: { - trackId: trackId, - variant, - }, - }); - - this.sendMediaEvent(mediaEvent); + const enableTrackVariant = MediaEvent_EnableTrackVariant.create({ variant, trackId }); + this.sendMediaEvent({ enableTrackVariant }); this.emit('targetTrackEncodingRequested', { trackId, variant, @@ -220,12 +223,9 @@ export class Remote { } }; - public updateMLineIds = (midToTrackId: Record) => { - Object.entries(midToTrackId).forEach(([mLineId, trackId]) => { - const remoteTrack = this.remoteTracks[trackId]; - if (remoteTrack) { - remoteTrack.setMLineId(mLineId); - } + public updateMLineIds = (midToTrackIds: Record) => { + Object.entries(midToTrackIds).forEach(([mid, trackId]) => { + this.remoteTracks[trackId]?.setMLineId(mid); }); }; } diff --git a/packages/webrtc-client/src/tracks/RemoteTrack.ts b/packages/webrtc-client/src/tracks/RemoteTrack.ts index 2af97483..bd4ad19c 100644 --- a/packages/webrtc-client/src/tracks/RemoteTrack.ts +++ b/packages/webrtc-client/src/tracks/RemoteTrack.ts @@ -1,7 +1,8 @@ -import type { TrackCommon, TrackEncodings, TrackId } from './TrackCommon'; -import type { LocalTrackId, MLineId, Encoding } from '../types'; +import type { TrackCommon, TrackId } from './TrackCommon'; +import type { LocalTrackId, MLineId } from '../types'; import type { TrackContextImpl } from '../internal'; import { isTrackKind } from '../internal'; +import { Variant } from '@fishjam-cloud/protobufs/shared'; /** * This is a wrapper over `TrackContext` that adds additional properties such as: @@ -30,9 +31,15 @@ export class RemoteTrack implements TrackCommon { public id: TrackId; public mLineId: MLineId | null = null; public readonly trackContext: TrackContextImpl; - public readonly encodings: TrackEncodings = { h: false, m: false, l: false }; + public readonly encodings: Record = { + [Variant.UNRECOGNIZED]: false, + [Variant.VARIANT_UNSPECIFIED]: false, + [Variant.VARIANT_LOW]: false, + [Variant.VARIANT_MEDIUM]: false, + [Variant.VARIANT_HIGH]: false, + }; // todo this field is not exposed - private targetEncoding: Encoding | null = null; + private targetEncoding: Variant | null = null; constructor(id: LocalTrackId, trackContext: TrackContextImpl) { this.id = id; @@ -51,15 +58,15 @@ export class RemoteTrack implements TrackCommon { this.trackContext.trackKind = track.kind; }; - public disableTrackEncoding = (encoding: Encoding) => { + public disableTrackEncoding = (encoding: Variant) => { this.encodings[encoding] = false; }; - public enableTrackEncoding = (encoding: Encoding) => { + public enableTrackEncoding = (encoding: Variant) => { this.encodings[encoding] = true; }; - public setTargetTrackEncoding = (variant: Encoding) => { + public setTargetTrackEncoding = (variant: Variant) => { // todo implement validation this.targetEncoding = variant; @@ -69,7 +76,7 @@ export class RemoteTrack implements TrackCommon { throw new Error('The track does not support changing its target variant'); } - const isValidTargetEncoding = !trackContext.simulcastConfig.activeEncodings.includes(variant); + const isValidTargetEncoding = !trackContext.simulcastConfig.enabledVariants.includes(variant); if (isValidTargetEncoding) { throw new Error(`The track does not support variant ${variant}`); diff --git a/packages/webrtc-client/src/tracks/TrackCommon.ts b/packages/webrtc-client/src/tracks/TrackCommon.ts index efd55729..14f3b495 100644 --- a/packages/webrtc-client/src/tracks/TrackCommon.ts +++ b/packages/webrtc-client/src/tracks/TrackCommon.ts @@ -1,9 +1,10 @@ -import type { MLineId, Encoding } from '../types'; +import type { Variant } from '@fishjam-cloud/protobufs/shared'; +import type { MLineId } from '../types'; export type TrackId = string; export type EndpointId = string; -export type TrackEncodings = Record; +export type TrackEncodings = Record; export interface TrackCommon { mLineId: MLineId | null; diff --git a/packages/webrtc-client/src/tracks/encodings.ts b/packages/webrtc-client/src/tracks/encodings.ts index 15ed3d75..e7e2a8ae 100644 --- a/packages/webrtc-client/src/tracks/encodings.ts +++ b/packages/webrtc-client/src/tracks/encodings.ts @@ -1,3 +1,4 @@ +import { Variant } from '@fishjam-cloud/protobufs/shared'; import { splitBandwidth } from './bandwidth'; export const getEncodingParameters = ( @@ -8,3 +9,9 @@ export const getEncodingParameters = ( return parameters.encodings.length === 0 ? unlimitedEncodings : splitBandwidth(parameters.encodings, bandwidth); }; + +export const encodingToVariantMap: Record = { + l: Variant.VARIANT_LOW, + m: Variant.VARIANT_MEDIUM, + h: Variant.VARIANT_HIGH, +}; diff --git a/packages/webrtc-client/src/tracks/muteTrackUtils.ts b/packages/webrtc-client/src/tracks/muteTrackUtils.ts index 1563f833..b4ff84d5 100644 --- a/packages/webrtc-client/src/tracks/muteTrackUtils.ts +++ b/packages/webrtc-client/src/tracks/muteTrackUtils.ts @@ -1,14 +1,13 @@ import type { WebRTCEndpoint } from '../webRTCEndpoint'; -import { generateMediaEvent } from '../mediaEvent'; export function emitMutableEvents(action: 'mute' | 'unmute', webrtc: WebRTCEndpoint, trackId: string) { - const mediaEventType = action === 'mute' ? `muteTrack` : `unmuteTrack`; - const localEventType = action === 'mute' ? `localTrackMuted` : `localTrackUnmuted`; + const localEventType = action === 'mute' ? 'localTrackMuted' : 'localTrackUnmuted'; - const mediaEvent = generateMediaEvent(mediaEventType, { trackId: trackId }); - webrtc.sendMediaEvent(mediaEvent); + // TODO add the mute/unmute event back if they're needed + // const mediaEvent = generateMediaEvent(mediaEventType, { trackId: trackId }); + // webrtc.sendMediaEvent(mediaEvent); - webrtc.emit(localEventType, { trackId: trackId }); + webrtc.emit(localEventType, { trackId }); } export function getActionType( diff --git a/packages/webrtc-client/src/tracks/transceivers.ts b/packages/webrtc-client/src/tracks/transceivers.ts index 082a470d..978555fb 100644 --- a/packages/webrtc-client/src/tracks/transceivers.ts +++ b/packages/webrtc-client/src/tracks/transceivers.ts @@ -1,6 +1,8 @@ -import type { Encoding, SimulcastBandwidthLimit, TrackBandwidthLimit } from '../types'; +import type { SimulcastBandwidthLimit, TrackBandwidthLimit } from '../types'; import type { TrackContextImpl } from '../internal'; import { splitBandwidth } from './bandwidth'; +import { Variant } from '@fishjam-cloud/protobufs/shared'; +import { encodingToVariantMap } from './encodings'; export const createTransceiverConfig = (trackContext: TrackContextImpl): RTCRtpTransceiverInit => { if (!trackContext.track) throw new Error(`Cannot create transceiver config for `); @@ -26,13 +28,13 @@ const createVideoTransceiverConfig = ( if (!trackContext.simulcastConfig) throw new Error(`Simulcast config for track ${trackContext.trackId} not found.`); if (trackContext.simulcastConfig.enabled) { - let simulcastConfig: Map; + let simulcastConfig: Map; if (maxBandwidth === 0) { simulcastConfig = new Map([ - ['l', 0], - ['m', 0], - ['h', 0], + [Variant.VARIANT_LOW, 0], + [Variant.VARIANT_MEDIUM, 0], + [Variant.VARIANT_HIGH, 0], ]); } else if (typeof maxBandwidth === 'number') { throw new Error('Invalid bandwidth limit for simulcast track.'); @@ -67,24 +69,24 @@ const createSimulcastTransceiverConfig = ( ): RTCRtpTransceiverInit => { if (!trackContext.simulcastConfig) throw new Error(`Simulcast config for track ${trackContext.trackId} not found.`); - const activeEncodings = trackContext.simulcastConfig.activeEncodings; + const activeEncodings = trackContext.simulcastConfig.enabledVariants; const encodings: RTCRtpEncodingParameters[] = [ { rid: 'l', - active: activeEncodings.includes('l'), + active: activeEncodings.includes(Variant.VARIANT_LOW), // maxBitrate: 4_000_000, scaleResolutionDownBy: 4.0, // scalabilityMode: "L1T" + TEMPORAL_LAYERS_COUNT, }, { rid: 'm', - active: activeEncodings.includes('m'), + active: activeEncodings.includes(Variant.VARIANT_MEDIUM), scaleResolutionDownBy: 2.0, }, { rid: 'h', - active: activeEncodings.includes('h'), + active: activeEncodings.includes(Variant.VARIANT_HIGH), // maxBitrate: 4_000_000, // scalabilityMode: "L1T" + TEMPORAL_LAYERS_COUNT, }, @@ -103,9 +105,9 @@ const calculateSimulcastEncodings = (encodings: RTCRtpEncodingParameters[], maxB return encodings .filter((encoding) => encoding.rid) .map((encoding) => { - const rid = encoding.rid! as Encoding; + const variant = (!!encoding.rid && encodingToVariantMap[encoding.rid]) || Variant.VARIANT_UNSPECIFIED; - const limit = maxBandwidth.get(rid) || 0; + const limit = maxBandwidth.get(variant) || 0; return { ...encoding, diff --git a/packages/webrtc-client/src/types.ts b/packages/webrtc-client/src/types.ts index 7e2fe837..06aa4e63 100644 --- a/packages/webrtc-client/src/types.ts +++ b/packages/webrtc-client/src/types.ts @@ -1,10 +1,13 @@ import type TypedEmitter from 'typed-emitter'; import type { SerializedMediaEvent } from './mediaEvent'; +import type { MediaEvent_Track_SimulcastConfig } from '@fishjam-cloud/protobufs/server'; +import type { Variant } from '@fishjam-cloud/protobufs/shared'; export type LocalTrackId = string; export type MLineId = string; export type MediaStreamTrackId = string; export type RemoteTrackId = string; +export type MetadataJson = string; export type TrackKind = 'audio' | 'video'; @@ -26,7 +29,7 @@ export type BandwidthLimit = number; * It is a mapping (encoding => BandwidthLimit). * If encoding isn't present in this mapping, it will be assumed that this particular encoding shouldn't have any bandwidth limit */ -export type SimulcastBandwidthLimit = Map; +export type SimulcastBandwidthLimit = Map; /** * Type describing bandwidth limitation of a Track, including simulcast and non-simulcast tracks. @@ -42,35 +45,6 @@ export type TrackBandwidthLimit = BandwidthLimit | SimulcastBandwidthLimit; */ export type EncodingReason = 'other' | 'encodingInactive' | 'lowBandwidth'; -/** - * Simulcast configuration passed to {@link WebRTCEndpoint.addTrack}. - * - * At the moment, simulcast track is initialized in three versions - low, medium and high. - * High resolution is the original track resolution, while medium and low resolutions - * are the original track resolution scaled down by 2 and 4 respectively. - */ -export interface SimulcastConfig { - /** - * Whether to simulcast track or not. - */ - enabled: boolean; - /** - * List of initially active encodings. - * - * Encoding that is not present in this list might still be - * enabled using {@link WebRTCEndpoint.enableTrackEncoding}. - */ - activeEncodings: Encoding[]; - - /** - * List of disabled encodings. - * - * Encoding that is present in this list was - * disabled using {@link WebRTCEndpoint.disableTrackEncoding}. - */ - disabledEncodings: Encoding[]; -} - /** * Track's context i.e. all data that can be useful when operating on track. */ @@ -97,7 +71,7 @@ interface TrackContextFields { * Simulcast configuration. * Only present for local tracks. */ - readonly simulcastConfig?: SimulcastConfig; + readonly simulcastConfig?: MediaEvent_Track_SimulcastConfig; /** * Any info that was passed in {@link WebRTCEndpoint.addTrack}. @@ -112,7 +86,7 @@ interface TrackContextFields { * Encoding that is currently received. * Only present for remote tracks. */ - readonly encoding?: Encoding; + readonly encoding?: Variant; /** * The reason of currently selected encoding. @@ -144,23 +118,6 @@ export interface TrackContext extends TrackContextFields, TypedEmitter trackEncodings.includes(encoding as Encoding); - /** * Events emitted by the {@link WebRTCEndpoint} instance. */ @@ -252,14 +209,14 @@ export interface WebRTCEndpointEvents { /** * Emitted each time track encoding has been disabled. */ - trackEncodingDisabled: (context: TrackContext, encoding: string) => void; + trackEncodingDisabled: (context: TrackContext, encoding: Variant) => void; /** * Emitted each time track encoding has been enabled. */ - trackEncodingEnabled: (context: TrackContext, encoding: string) => void; + trackEncodingEnabled: (context: TrackContext, encoding: Variant) => void; - targetTrackEncodingRequested: (event: { trackId: string; variant: Encoding }) => void; + targetTrackEncodingRequested: (event: { trackId: string; variant: Variant }) => void; disconnectRequested: (event: any) => void; @@ -268,7 +225,7 @@ export interface WebRTCEndpointEvents { track: MediaStreamTrack; stream: MediaStream; trackMetadata?: unknown; - simulcastConfig: SimulcastConfig; + simulcastConfig: MediaEvent_Track_SimulcastConfig; maxBandwidth: TrackBandwidthLimit; }) => void; @@ -282,11 +239,11 @@ export interface WebRTCEndpointEvents { localTrackBandwidthSet: (event: { trackId: string; bandwidth: BandwidthLimit }) => void; - localTrackEncodingBandwidthSet: (event: { trackId: string; rid: string; bandwidth: BandwidthLimit }) => void; + localTrackEncodingBandwidthSet: (event: { trackId: string; rid: Variant; bandwidth: BandwidthLimit }) => void; - localTrackEncodingEnabled: (event: { trackId: string; encoding: Encoding }) => void; + localTrackEncodingEnabled: (event: { trackId: string; encoding: Variant }) => void; - localTrackEncodingDisabled: (event: { trackId: string; encoding: Encoding }) => void; + localTrackEncodingDisabled: (event: { trackId: string; encoding: Variant }) => void; localEndpointMetadataChanged: (event: { metadata: unknown }) => void; diff --git a/packages/webrtc-client/src/voiceActivityDetection.ts b/packages/webrtc-client/src/voiceActivityDetection.ts deleted file mode 100644 index 42be8d9f..00000000 --- a/packages/webrtc-client/src/voiceActivityDetection.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { VadStatus } from './types'; - -const vadStatuses = ['speech', 'silence'] as const; - -export const isVadStatus = (status: string): status is VadStatus => vadStatuses.includes(status as VadStatus); diff --git a/packages/webrtc-client/src/webRTCEndpoint.ts b/packages/webrtc-client/src/webRTCEndpoint.ts index 2700f3c3..908a6bda 100644 --- a/packages/webrtc-client/src/webRTCEndpoint.ts +++ b/packages/webrtc-client/src/webRTCEndpoint.ts @@ -1,25 +1,31 @@ -import type { MediaEvent, SerializedMediaEvent } from './mediaEvent'; -import { deserializeMediaEvent, generateCustomEvent, generateMediaEvent, serializeMediaEvent } from './mediaEvent'; +import type { SerializedMediaEvent } from './mediaEvent'; +import { deserializeServerMediaEvent, serializePeerMediaEvent } from './mediaEvent'; +import type { MediaEvent as PeerMediaEvent } from '@fishjam-cloud/protobufs/peer'; +import { + MediaEvent_Connect, + MediaEvent_Disconnect, + MediaEvent_RenegotiateTracks, + MediaEvent_UpdateTrackMetadata, +} from '@fishjam-cloud/protobufs/peer'; import { v4 as uuidv4 } from 'uuid'; import EventEmitter from 'events'; import type TypedEmitter from 'typed-emitter'; import { Deferred } from './deferred'; -import type { - BandwidthLimit, - Encoding, - SimulcastConfig, - TrackBandwidthLimit, - TrackContext, - WebRTCEndpointEvents, -} from './types'; -import { isEncoding } from './types'; +import type { BandwidthLimit, TrackBandwidthLimit, TrackContext, WebRTCEndpointEvents } from './types'; import type { EndpointWithTrackContext } from './internal'; import { LocalTrackManager } from './tracks/LocalTrackManager'; import { CommandsQueue } from './CommandsQueue'; import { Remote } from './tracks/Remote'; import { Local } from './tracks/Local'; -import type { TurnServer } from './ConnectionManager'; import { ConnectionManager } from './ConnectionManager'; +import type { Variant } from '@fishjam-cloud/protobufs/shared'; +import { Candidate } from '@fishjam-cloud/protobufs/shared'; +import type { + MediaEvent_OfferData, + MediaEvent_SdpAnswer, + MediaEvent_Track_SimulcastConfig, + MediaEvent as ServerMediaEvent, +} from '@fishjam-cloud/protobufs/server'; /** * Main class that is responsible for connecting to the RTC Engine, sending and receiving media. @@ -29,6 +35,7 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter TypedEmitter this.sendMediaEvent(mediaEvent); + const sendEvent = (mediaEvent: PeerMediaEvent) => this.sendMediaEvent(mediaEvent); const emit: >( event: E, @@ -70,10 +77,8 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter { this.local.setEndpointMetadata(metadata); - const mediaEvent = generateMediaEvent('connect', { - metadata: metadata, - }); - this.sendMediaEvent(mediaEvent); + const connect = MediaEvent_Connect.create({ metadataJson: JSON.stringify(metadata) }); + this.sendMediaEvent({ connect }); }; /** @@ -93,28 +98,41 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter { - const deserializedMediaEvent = deserializeMediaEvent(mediaEvent); - switch (deserializedMediaEvent.type) { - case 'connected': { - this.local.setLocalEndpointId(deserializedMediaEvent.data.id); + const deserializedMediaEvent = deserializeServerMediaEvent(mediaEvent); - const endpoints = deserializedMediaEvent.data.otherEndpoints as EndpointWithTrackContext[]; + if (deserializedMediaEvent.connected) { + const connectedEvent = deserializedMediaEvent.connected; - // todo implement track mapping (+ validate metadata) - // todo implement endpoint metadata mapping - endpoints.forEach((endpoint) => { - this.remote.addRemoteEndpoint(endpoint); - }); + this.proposedIceServers = connectedEvent.iceServers; - const remoteEndpoints = Object.values(this.remote.getRemoteEndpoints()); + this.local.setLocalEndpointId(connectedEvent.endpointId); - this.emit('connected', this.local.getEndpoint().id, remoteEndpoints); + const localEndpointMetadataJson = connectedEvent.endpointIdToEndpoint[connectedEvent.endpointId]?.metadataJson; + if (localEndpointMetadataJson) { + this.local.setEndpointMetadata(JSON.parse(localEndpointMetadataJson)); + } - break; + const connectedEndpoint = connectedEvent.endpointIdToEndpoint[connectedEvent.endpointId]; + + if (connectedEndpoint?.metadataJson) { + const parsedMetadata = JSON.parse(connectedEndpoint?.metadataJson); + this.local.setEndpointMetadata(parsedMetadata); } - default: - if (this.getEndpointId() != null) await this.handleMediaEvent(deserializedMediaEvent); + + Object.entries(connectedEvent.endpointIdToEndpoint) + .filter(([endpointId]) => endpointId !== this.local.getEndpoint().id) + .forEach(([endpointId, endpoint]) => { + this.remote.addRemoteEndpoint(endpointId, endpoint.metadataJson, endpoint.trackIdToTrack); + }); + + const remoteEndpoints = Object.values(this.remote.getRemoteEndpoints()); + + this.emit('connected', this.local.getEndpoint().id, remoteEndpoints); + + return; } + + if (this.getEndpointId()) await this.handleMediaEvent(deserializedMediaEvent); }; private getEndpointId = () => this.local.getEndpoint().id; @@ -172,137 +190,83 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter { - switch (deserializedMediaEvent.type) { - case 'offerData': { - await this.onOfferData(deserializedMediaEvent); - break; - } - case 'tracksAdded': { - this.localTrackManager.ongoingRenegotiation = true; - const data = deserializedMediaEvent.data; - - if (this.getEndpointId() === data.endpointId) return; - - this.remote.addTracks(data.endpointId, data.tracks, data.trackIdToMetadata); - break; - } - case 'tracksRemoved': { - this.localTrackManager.ongoingRenegotiation = true; - - const data = deserializedMediaEvent.data; - - const endpointId = data.endpointId; - if (this.getEndpointId() === endpointId) return; - - this.remote.removeTracks(data.trackIds as string[]); - break; - } - - case 'sdpAnswer': - this.localTrackManager.ongoingRenegotiation = false; - await this.onSdpAnswer(deserializedMediaEvent.data); - - this.commandsQueue.processNextCommand(); - break; - - case 'candidate': - await this.onRemoteCandidate(deserializedMediaEvent.data); - break; - - case 'endpointAdded': - const endpoint = deserializedMediaEvent.data; - - if (endpoint.id === this.getEndpointId()) return; - - this.remote.addRemoteEndpoint(endpoint); - break; - - case 'endpointRemoved': - if (deserializedMediaEvent.data.id === this.local.getEndpoint().id) { - this.cleanUp(); - this.emit('disconnected'); - return; - } - - if (this.getEndpointId() === deserializedMediaEvent.data.id) return; - - this.remote.removeRemoteEndpoint(deserializedMediaEvent.data.id); - break; - - case 'endpointUpdated': - if (this.getEndpointId() === deserializedMediaEvent.data.id) return; - - this.remote.updateRemoteEndpoint(deserializedMediaEvent.data); - break; - - case 'trackUpdated': { - if (this.getEndpointId() === deserializedMediaEvent.data.endpointId) return; - - this.remote.updateRemoteTrack(deserializedMediaEvent.data); - break; - } - - case 'trackEncodingDisabled': { - if (this.getEndpointId() === deserializedMediaEvent.data.endpointId) return; - - this.remote.disableRemoteTrackEncoding( - deserializedMediaEvent.data.trackId, - deserializedMediaEvent.data.encoding, - ); - break; - } - - case 'trackEncodingEnabled': { - const data = deserializedMediaEvent.data; - - if (this.getEndpointId() === data.endpointId) return; - - this.remote.enableRemoteTrackEncoding(data.trackId, data.encoding); - break; - } - - case 'encodingSwitched': { - const data = deserializedMediaEvent.data; - - this.remote.setRemoteTrackEncoding(data.trackId, data.encoding, data.reason); - break; - } - case 'custom': - await this.handleMediaEvent(deserializedMediaEvent.data as MediaEvent); - break; - - case 'error': - console.warn('signaling error', { - message: deserializedMediaEvent.data.message, - }); - - this.emit('signalingError', { - message: deserializedMediaEvent.data.message, - }); - - this.disconnect(); - break; - - case 'vadNotification': { - this.remote.setRemoteTrackVadStatus(deserializedMediaEvent.data.trackId, deserializedMediaEvent.data.status); - break; + private handleMediaEvent = async (event: ServerMediaEvent) => { + if (event.offerData) { + await this.onOfferData(event.offerData); + } else if (event.tracksAdded) { + this.localTrackManager.ongoingRenegotiation = true; + + const { trackIdToTrack, endpointId } = event.tracksAdded; + if (this.getEndpointId() === endpointId) return; + + this.remote.addTracks(endpointId, trackIdToTrack); + } else if (event.tracksRemoved) { + this.localTrackManager.ongoingRenegotiation = true; + + const { endpointId, trackIds } = event.tracksRemoved; + + if (this.getEndpointId() === endpointId) return; + + this.remote.removeTracks(trackIds); + } else if (event.sdpAnswer) { + this.localTrackManager.ongoingRenegotiation = false; + await this.onSdpAnswer(event.sdpAnswer); + this.commandsQueue.processNextCommand(); + } else if (event.candidate) { + await this.onRemoteCandidate(event.candidate); + } else if (event.endpointAdded) { + const { endpointId, metadataJson } = event.endpointAdded; + if (endpointId === this.getEndpointId()) return; + + this.remote.addRemoteEndpoint(endpointId, metadataJson); + } else if (event.endpointRemoved) { + const { endpointId } = event.endpointRemoved; + + if (endpointId === this.local.getEndpoint().id) { + this.cleanUp(); + this.emit('disconnected'); + return; } - case 'bandwidthEstimation': { - this.bandwidthEstimation = deserializedMediaEvent.data.estimation; + if (this.getEndpointId() === endpointId) return; + + this.remote.removeRemoteEndpoint(endpointId); + } else if (event.endpointUpdated) { + const { endpointId, metadataJson } = event.endpointUpdated; + if (this.getEndpointId() === endpointId) return; + + this.remote.updateRemoteEndpoint(endpointId, metadataJson); + } else if (event.trackUpdated) { + const { endpointId, trackId, metadataJson } = event.trackUpdated; + if (this.getEndpointId() === endpointId) return; + + this.remote.updateRemoteTrack(endpointId, trackId, metadataJson); + } else if (event.vadNotification) { + const { trackId, status } = event.vadNotification; + this.remote.setRemoteTrackVadStatus(trackId, status); + } else if (event.error) { + console.warn('signaling error', { + message: event.error.message, + }); - this.emit('bandwidthEstimationChanged', this.bandwidthEstimation); - break; - } + this.emit('signalingError', { + message: event.error.message, + }); - default: - console.warn('Received unknown media event: ', deserializedMediaEvent.type); - break; + this.disconnect(); + } else if (event.trackVariantSwitched) { + const { trackId, variant } = event.trackVariantSwitched; + this.remote.setRemoteTrackEncoding(trackId, variant); + } else if (event.trackVariantEnabled) { + const { trackId, variant } = event.trackVariantEnabled; + this.remote.enableRemoteTrackEncoding(trackId, variant); + } else if (event.trackVariantDisabled) { + const { trackId, variant } = event.trackVariantDisabled; + this.remote.disableRemoteTrackEncoding(trackId, variant); } }; - private onSdpAnswer = async (data: any) => { + private onSdpAnswer = async (data: MediaEvent_SdpAnswer) => { this.remote.updateMLineIds(data.midToTrackId); this.local.updateMLineIds(data.midToTrackId); @@ -320,11 +284,11 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter TypedEmitter TypedEmitter { @@ -415,6 +379,7 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter TypedEmitter { - if (!isEncoding(rid)) throw new Error(`Rid is invalid ${rid}`); - + public async setEncodingBandwidth(trackId: string, rid: Variant, bandwidth: BandwidthLimit): Promise { if (!this.connectionManager) throw new Error(`There is no active RTCPeerConnection`); return await this.local.setEncodingBandwidth(trackId, rid, bandwidth); @@ -573,7 +536,7 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter TypedEmitter { + public enableTrackEncoding = async (trackId: string, encoding: Variant) => { await this.local.enableLocalTrackEncoding(trackId, encoding); }; @@ -603,7 +566,7 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter { + public disableTrackEncoding = async (trackId: string, encoding: Variant) => { await this.local.disableLocalTrackEncoding(trackId, encoding); }; @@ -638,8 +601,8 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter { - const mediaEvent = generateMediaEvent('disconnect'); - this.sendMediaEvent(mediaEvent); + const disconnect = MediaEvent_Disconnect.create(); + this.sendMediaEvent({ disconnect }); this.emit('disconnectRequested', {}); this.cleanUp(); }; @@ -663,14 +626,13 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter { - const serializedMediaEvent = serializeMediaEvent(mediaEvent); - this.emit('sendMediaEvent', serializedMediaEvent); + private sendMediaEvent = (mediaEvent: PeerMediaEvent) => { + this.emit('sendMediaEvent', serializePeerMediaEvent(mediaEvent)); }; private async createAndSendOffer() { const connection = this.connectionManager; + if (!connection) return; try { @@ -688,9 +650,9 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter TypedEmitter { - const connection = this.connectionManager; - - if (connection && !connection.isExWebRTC) { - connection.getConnection().restartIce(); - } else if (!connection) { - this.setConnection(offerData.data.integratedTurnServers); + private onOfferData = async (offerData: MediaEvent_OfferData) => { + const connectionManager = this.connectionManager ?? this.createNewConnection(); - const onIceCandidate = (event: RTCPeerConnectionIceEvent) => this.onLocalCandidate(event); - const onIceCandidateError = (event: RTCPeerConnectionIceErrorEvent) => this.onIceCandidateError(event); - const onConnectionStateChange = (event: Event) => this.onConnectionStateChange(event); - const onIceConnectionStateChange = (event: Event) => this.onIceConnectionStateChange(event); + if (offerData.tracksTypes) { + connectionManager.addTransceiversIfNeeded(offerData.tracksTypes); + } - const connection = this.connectionManager; - if (!connection) throw new Error(`There is no active RTCPeerConnection`); + await this.createAndSendOffer(); + }; - this.clearConnectionCallbacks = () => { - connection?.getConnection()?.removeEventListener('icecandidate', onIceCandidate); - connection?.getConnection()?.removeEventListener('icecandidateerror', onIceCandidateError); - connection?.getConnection()?.removeEventListener('connectionstatechange', onConnectionStateChange); - connection?.getConnection()?.removeEventListener('iceconnectionstatechange', onIceConnectionStateChange); - }; + private setupConnectionListeners = (connection: RTCPeerConnection) => { + const onIceCandidate = (event: RTCPeerConnectionIceEvent) => this.onLocalCandidate(event); + const onIceCandidateError = (event: RTCPeerConnectionIceErrorEvent) => this.onIceCandidateError(event); + const onConnectionStateChange = (event: Event) => this.onConnectionStateChange(event); + const onIceConnectionStateChange = (event: Event) => this.onIceConnectionStateChange(event); + + this.clearConnectionCallbacks = () => { + connection.removeEventListener('icecandidate', onIceCandidate); + connection.removeEventListener('icecandidateerror', onIceCandidateError); + connection.removeEventListener('connectionstatechange', onConnectionStateChange); + connection.removeEventListener('iceconnectionstatechange', onIceConnectionStateChange); + }; - connection.getConnection().addEventListener('icecandidate', onIceCandidate); - connection.getConnection().addEventListener('icecandidateerror', onIceCandidateError); - connection.getConnection().addEventListener('connectionstatechange', onConnectionStateChange); - connection.getConnection().addEventListener('iceconnectionstatechange', onIceConnectionStateChange); + connection.addEventListener('icecandidate', onIceCandidate); + connection.addEventListener('icecandidateerror', onIceCandidateError); + connection.addEventListener('connectionstatechange', onConnectionStateChange); + connection.addEventListener('iceconnectionstatechange', onIceConnectionStateChange); + }; - this.commandsQueue.initConnection(connection); + private createNewConnection = () => { + const connectionManager = new ConnectionManager(this.proposedIceServers); - this.local.addAllTracksToConnection(); - } + this.localTrackManager.updateConnection(connectionManager); + this.local.updateConnection(connectionManager); - const tracks = new Map(Object.entries(offerData.data.tracksTypes)); + this.setupConnectionListeners(connectionManager.getConnection()); - this.connectionManager?.addTransceiversIfNeeded(tracks); + this.commandsQueue.initConnection(connectionManager); - await this.createAndSendOffer(); - }; + this.local.addAllTracksToConnection(); - private setConnection = (turnServers: TurnServer[]) => { - this.connectionManager = new ConnectionManager(turnServers); + this.connectionManager = connectionManager; - this.localTrackManager.updateConnection(this.connectionManager); - this.local.updateConnection(this.connectionManager); + return connectionManager; }; - private onRemoteCandidate = async (candidate: RTCIceCandidate) => { + private onRemoteCandidate = async (candidate: Candidate) => { try { const iceCandidate = new RTCIceCandidate(candidate); if (!this.connectionManager) { @@ -758,18 +719,16 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter { - if (event.candidate) { - const mediaEvent = generateCustomEvent({ - type: 'candidate', - data: { - candidate: event.candidate.candidate, - sdpMLineIndex: event.candidate.sdpMLineIndex, - sdpMid: event.candidate.sdpMid, - usernameFragment: event.candidate.usernameFragment, - }, - }); - this.sendMediaEvent(mediaEvent); - } + if (!event.candidate) return; + + const candidate = Candidate.create({ + candidate: event.candidate.candidate, + sdpMLineIndex: event.candidate.sdpMLineIndex ?? undefined, + sdpMid: event.candidate.sdpMid ?? undefined, + usernameFragment: event.candidate.usernameFragment ?? undefined, + }); + + this.sendMediaEvent({ candidate }); }; private onIceCandidateError = (event: RTCPeerConnectionIceErrorEvent) => { @@ -793,7 +752,7 @@ export class WebRTCEndpoint extends (EventEmitter as new () => TypedEmitter - new Promise((done) => { - // Given - const webRTCEndpoint = new WebRTCEndpoint(); +// TODO Add the test back once the bandwidth estimation event is implemented +// it('Change existing track bandwidth estimation', () => +// new Promise((done) => { +// // Given +// const webRTCEndpoint = new WebRTCEndpoint(); - setupRoom(webRTCEndpoint, endpointId, trackId); - const bandwidthEstimationEvent = createBandwidthEstimationEvent(); +// setupRoom(webRTCEndpoint, endpointId, trackId); +// const bandwidthEstimationEvent = createBandwidthEstimationEvent(); - webRTCEndpoint.on('bandwidthEstimationChanged', (estimation) => { - // Then - expect(estimation).toBe(bandwidthEstimationEvent.data.data.estimation); - done(''); - }); +// webRTCEndpoint.on('bandwidthEstimationChanged', (estimation) => { +// // Then +// expect(estimation).toBe(bandwidthEstimationEvent.data.data.estimation); +// done(''); +// }); - // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(bandwidthEstimationEvent)); - })); +// // When +// webRTCEndpoint.receiveMediaEvent(JSON.stringify(bandwidthEstimationEvent)); +// })); +import { describe } from 'vitest'; +describe.skip('Skipping bandwidth estimation, not yet implemented in Fishjam', () => {}); diff --git a/packages/webrtc-client/tests/events/connectedEvent.test.ts b/packages/webrtc-client/tests/events/connectedEvent.test.ts index adb534eb..d6fb7262 100644 --- a/packages/webrtc-client/tests/events/connectedEvent.test.ts +++ b/packages/webrtc-client/tests/events/connectedEvent.test.ts @@ -1,21 +1,23 @@ -import { createConnectedEvent, createEmptyEndpoint, createSimulcastTrack, trackId } from '../fixtures'; +import { faker } from '@faker-js/faker'; +import { createConnectedEvent, createEmptyEndpoint, createTrackWithSimulcast, trackId } from '../fixtures'; import type { Endpoint } from '../../src'; import { WebRTCEndpoint } from '../../src'; import { expect, vi, it } from 'vitest'; +import { serializeServerMediaEvent } from '../../src/mediaEvent'; it('Connecting to empty room produce event', () => new Promise((done) => { const webRTCEndpoint = new WebRTCEndpoint(); - const connectedEvent = createConnectedEvent(); + const connected = createConnectedEvent(); webRTCEndpoint.on('connected', (peerId: string, _peersInRoom: Endpoint[]) => { - expect(connectedEvent.data.id).toBe(peerId); - expect(connectedEvent.data.otherEndpoints.length).toBe(0); + expect(connected.endpointId).toBe(peerId); + expect(Object.keys(connected.endpointIdToEndpoint).length).toBe(1); done(''); }); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected })); })); it('Connecting to empty room set internal state', () => () => { @@ -23,28 +25,31 @@ it('Connecting to empty room set internal state', () => () => { const webRTCEndpoint = new WebRTCEndpoint(); // When - const connectedEvent = createConnectedEvent(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedEvent)); + const connected = createConnectedEvent(); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected })); // Then const localEndpointId = webRTCEndpoint['getEndpointId'](); - expect(localEndpointId).toBe(connectedEvent.data.id); + expect(localEndpointId).toBe(connected.endpointId); }); it('Connecting to room with one peer', () => new Promise((done) => { const webRTCEndpoint = new WebRTCEndpoint(); - const connectedEvent = createConnectedEvent(); - connectedEvent.data.otherEndpoints = [createEmptyEndpoint()]; + const connected = createConnectedEvent(); + connected.endpointIdToEndpoint = { + ...connected.endpointIdToEndpoint, + [faker.string.uuid()]: createEmptyEndpoint(), + }; webRTCEndpoint.on('connected', (peerId: string, _peersInRoom: Endpoint[]) => { - expect(connectedEvent.data.id).toBe(peerId); - expect(connectedEvent.data.otherEndpoints.length).toBe(connectedEvent.data.otherEndpoints.length); + expect(connected.endpointId).toBe(peerId); + expect(Object.keys(connected.endpointIdToEndpoint).length).toBe(2); done(''); }); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected })); })); it('Connecting to room with one peer with one track', () => @@ -54,28 +59,32 @@ it('Connecting to room with one peer with one track', () => const trackAddedCallback = vi.fn((_x) => null); const connectedCallback = vi.fn((_peerId, _peersInRoom) => null); - const connectedEvent = createConnectedEvent(); - connectedEvent.data.otherEndpoints = [createEmptyEndpoint()]; - const endpoint = connectedEvent.data.otherEndpoints[0]!; + const connected = createConnectedEvent(); - endpoint.tracks[trackId] = createSimulcastTrack(); - endpoint.trackIdToMetadata[trackId] = {}; + const otherEndpoint = createEmptyEndpoint(); - webRTCEndpoint.on('connected', (peerId: string, peersInRoom: Endpoint[]) => { - connectedCallback(peerId, peersInRoom); - expect(peerId).toBe(connectedEvent.data.id); - expect(peersInRoom.length).toBe(connectedEvent.data.otherEndpoints.length); + otherEndpoint.trackIdToTrack = { [trackId]: createTrackWithSimulcast() }; + + connected.endpointIdToEndpoint = { + ...connected.endpointIdToEndpoint, + [faker.string.uuid()]: otherEndpoint, + }; + + webRTCEndpoint.on('connected', (peerId: string, remotePeersInRoom: Endpoint[]) => { + connectedCallback(peerId, remotePeersInRoom); + expect(peerId).toBe(connected.endpointId); + expect(remotePeersInRoom.length).toBe(1); }); webRTCEndpoint.on('trackAdded', (ctx) => { trackAddedCallback(ctx); expect(ctx.trackId).toBe(trackId); - expect(ctx.simulcastConfig?.enabled).toBe(endpoint.tracks[trackId]!.simulcastConfig.enabled); + expect(ctx.simulcastConfig?.enabled).toBe(otherEndpoint.trackIdToTrack[trackId]!.simulcastConfig?.enabled); done(''); }); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected })); // Then const remoteTracks = webRTCEndpoint.getRemoteTracks(); diff --git a/packages/webrtc-client/tests/events/encodingSwitchedEvent.test.ts b/packages/webrtc-client/tests/events/encodingSwitchedEvent.test.ts index 3086c3a7..749ca785 100644 --- a/packages/webrtc-client/tests/events/encodingSwitchedEvent.test.ts +++ b/packages/webrtc-client/tests/events/encodingSwitchedEvent.test.ts @@ -1,4 +1,5 @@ -import { WebRTCEndpoint } from '../../src'; +import { Variant, WebRTCEndpoint } from '../../src'; +import { serializeServerMediaEvent } from '../../src/mediaEvent'; import { createEncodingSwitchedEvent, endpointId, @@ -19,12 +20,15 @@ it('Change existing track encoding', () => { expect(initialTrackEncoding).toBe(undefined); // When - const encodingUpdatedEvent = createEncodingSwitchedEvent(endpointId, trackId, 'm'); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(encodingUpdatedEvent)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ + trackVariantSwitched: createEncodingSwitchedEvent(endpointId, trackId, Variant.VARIANT_MEDIUM), + }), + ); // Then const finalTrackEncoding = webRTCEndpoint.getRemoteTracks()[trackId]!.encoding; - expect(finalTrackEncoding).toBe(encodingUpdatedEvent.data.data.encoding); + expect(finalTrackEncoding).toBe(Variant.VARIANT_MEDIUM); }); it('Changing track encoding when endpoint exist but track does not exist', () => { @@ -37,11 +41,13 @@ it('Changing track encoding when endpoint exist but track does not exist', () => expect(initialTrackEncoding).toBe(undefined); // When - const encodingUpdatedEvent = createEncodingSwitchedEvent(endpointId, notExistingTrackId, 'm'); - - expect(() => webRTCEndpoint.receiveMediaEvent(JSON.stringify(encodingUpdatedEvent))).rejects.toThrow( - `Track ${notExistingTrackId} not found`, - ); + expect(() => + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ + trackVariantSwitched: createEncodingSwitchedEvent(endpointId, notExistingTrackId, Variant.VARIANT_MEDIUM), + }), + ), + ).rejects.toThrow(`Track ${notExistingTrackId} not found`); }); it('Changing track encoding when endpoint does not exist but track exist in other endpoint', () => { @@ -54,12 +60,15 @@ it('Changing track encoding when endpoint does not exist but track exist in othe expect(initialTrackEncoding).toBe(undefined); // When - const encodingUpdatedEvent = createEncodingSwitchedEvent(notExistingEndpointId, trackId, 'm'); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(encodingUpdatedEvent)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ + trackVariantSwitched: createEncodingSwitchedEvent(notExistingEndpointId, trackId, Variant.VARIANT_MEDIUM), + }), + ); // Then const finalTrackEncoding = webRTCEndpoint.getRemoteTracks()[trackId]!.encoding; - expect(finalTrackEncoding).toBe(encodingUpdatedEvent.data.data.encoding); + expect(finalTrackEncoding).toBe(Variant.VARIANT_MEDIUM); }); it('Change existing track encoding produces event', () => @@ -72,14 +81,16 @@ it('Change existing track encoding produces event', () => const initialTrackEncoding = webRTCEndpoint.getRemoteTracks()[trackId]!.encoding; expect(initialTrackEncoding).toBe(undefined); - const encodingUpdatedEvent = createEncodingSwitchedEvent(endpointId, trackId, 'm'); - webRTCEndpoint.getRemoteTracks()[trackId]!.on('encodingChanged', (context) => { // Then - expect(context.encoding).toBe(encodingUpdatedEvent.data.data.encoding); + expect(context.encoding).toBe(Variant.VARIANT_MEDIUM); done(''); }); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(encodingUpdatedEvent)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ + trackVariantSwitched: createEncodingSwitchedEvent(endpointId, trackId, Variant.VARIANT_MEDIUM), + }), + ); })); diff --git a/packages/webrtc-client/tests/events/endpointAddedEvent.test.ts b/packages/webrtc-client/tests/events/endpointAddedEvent.test.ts index 900c84af..7adebcd5 100644 --- a/packages/webrtc-client/tests/events/endpointAddedEvent.test.ts +++ b/packages/webrtc-client/tests/events/endpointAddedEvent.test.ts @@ -7,16 +7,17 @@ import { endpointId, } from '../fixtures'; import { expect, it } from 'vitest'; +import { serializeServerMediaEvent } from '../../src/mediaEvent'; it('Add endpoint to empty state', () => { // Given mockRTCPeerConnection(); const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEvent())); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected: createConnectedEvent() })); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createEndpointAdded(endpointId))); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ endpointAdded: createEndpointAdded(endpointId) })); // Then const endpoints = webRTCEndpoint.getRemoteEndpoints(); @@ -28,10 +29,10 @@ it('Add another endpoint', () => { mockRTCPeerConnection(); const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint())); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint() })); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createEndpointAdded(endpointId))); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ endpointAdded: createEndpointAdded(endpointId) })); // Then const endpoints = webRTCEndpoint.getRemoteEndpoints(); @@ -44,19 +45,19 @@ it('Add endpoint produces event', () => mockRTCPeerConnection(); const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint())); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint() })); - const addEndpointEvent = createEndpointAdded(endpointId); + const endpointAdded = createEndpointAdded(endpointId); webRTCEndpoint.on('endpointAdded', (endpoint) => { // Then - expect(endpoint.id).toBe(addEndpointEvent.data.id); - expect(endpoint.metadata).toBe(addEndpointEvent.data.metadata); + expect(endpoint.id).toBe(endpointAdded.endpointId); + expect(endpoint.metadata).toBe(undefined); done(''); }); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(addEndpointEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ endpointAdded })); })); it('Parses the metadata', () => { @@ -64,19 +65,19 @@ it('Parses the metadata', () => { mockRTCPeerConnection(); const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEvent())); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected: createConnectedEvent() })); + + const metadata = { + goodStuff: 'ye', + }; // When webRTCEndpoint.receiveMediaEvent( - JSON.stringify( - createEndpointAdded(endpointId, { - goodStuff: 'ye', - }), - ), + serializeServerMediaEvent({ endpointAdded: createEndpointAdded(endpointId, metadata) }), ); // Then const endpoints: Record = webRTCEndpoint.getRemoteEndpoints(); const addedEndpoint = endpoints[endpointId]; - expect(addedEndpoint.metadata).toEqual({ goodStuff: 'ye' }); + expect(addedEndpoint.metadata).toEqual(metadata); }); diff --git a/packages/webrtc-client/tests/events/endpointRemovedEvent.test.ts b/packages/webrtc-client/tests/events/endpointRemovedEvent.test.ts index cd07d9db..d94bde9f 100644 --- a/packages/webrtc-client/tests/events/endpointRemovedEvent.test.ts +++ b/packages/webrtc-client/tests/events/endpointRemovedEvent.test.ts @@ -9,16 +9,23 @@ import { } from '../fixtures'; import { setupRoom } from '../utils'; import { expect, it } from 'vitest'; +import { serializeServerMediaEvent } from '../../src/mediaEvent'; it('Remove the endpoint that does not exist', () => { // Given mockRTCPeerConnection(); const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint(endpointId))); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint(endpointId) }), + ); // When - expect(() => webRTCEndpoint.receiveMediaEvent(JSON.stringify(createEndpointRemoved(notExistingEndpointId)))) + expect(() => + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ endpointRemoved: createEndpointRemoved(notExistingEndpointId) }), + ), + ) // Then .rejects.toThrow(`Endpoint ${notExistingEndpointId} not found`); }); @@ -30,7 +37,9 @@ it('Remove current peer', () => const webRTCEndpoint = new WebRTCEndpoint(); const currentPeerId = 'currentPeerId'; - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint(endpointId, currentPeerId))); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint(endpointId, currentPeerId) }), + ); webRTCEndpoint.on('disconnected', () => { // Then @@ -38,7 +47,9 @@ it('Remove current peer', () => }); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createEndpointRemoved(currentPeerId))); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ endpointRemoved: createEndpointRemoved(currentPeerId) }), + ); })); it('Remove existing endpoint should remove it from remote endpoints', () => { @@ -46,11 +57,12 @@ it('Remove existing endpoint should remove it from remote endpoints', () => { mockRTCPeerConnection(); const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint(endpointId))); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint(endpointId) }), + ); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createEndpointRemoved(endpointId))); - + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ endpointRemoved: createEndpointRemoved(endpointId) })); // Then const endpoints = webRTCEndpoint.getRemoteEndpoints(); expect(Object.values(endpoints).length).toBe(0); @@ -63,10 +75,8 @@ it('Remove existing endpoint should remove all tracks', () => { setupRoom(webRTCEndpoint, endpointId, trackId); - const addEndpointEvent = createEndpointRemoved(endpointId); - // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(addEndpointEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ endpointRemoved: createEndpointRemoved(endpointId) })); // Then const tracks = webRTCEndpoint.getRemoteTracks(); @@ -88,6 +98,5 @@ it('Remove existing endpoint should emit trackRemoved event', () => }); // When - const addEndpointEvent = createEndpointRemoved(endpointId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(addEndpointEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ endpointRemoved: createEndpointRemoved(endpointId) })); })); diff --git a/packages/webrtc-client/tests/events/endpointUpdatedEvent.test.ts b/packages/webrtc-client/tests/events/endpointUpdatedEvent.test.ts index 99536769..3f0419d9 100644 --- a/packages/webrtc-client/tests/events/endpointUpdatedEvent.test.ts +++ b/packages/webrtc-client/tests/events/endpointUpdatedEvent.test.ts @@ -8,21 +8,24 @@ import { notExistingEndpointId, } from '../fixtures'; import { expect, it } from 'vitest'; +import { serializeServerMediaEvent } from '../../src/mediaEvent'; it('Update existing endpoint metadata', () => { // Given mockRTCPeerConnection(); const webRTCEndpoint = new WebRTCEndpoint(); - const connectedMediaEvent = createConnectedEventWithOneEndpoint(endpointId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedMediaEvent)); + const connected = createConnectedEventWithOneEndpoint(endpointId); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected })); // When const metadata = { newField: 'new field value', }; - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createEndpointUpdatedPeerMetadata(endpointId, metadata))); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ endpointUpdated: createEndpointUpdatedPeerMetadata(endpointId, metadata) }), + ); // Then const endpoint = webRTCEndpoint.getRemoteEndpoints()[endpointId]!; @@ -35,8 +38,9 @@ it('Update existing endpoint produce event', () => mockRTCPeerConnection(); const webRTCEndpoint = new WebRTCEndpoint(); - const connectedMediaEvent = createConnectedEventWithOneEndpoint(endpointId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedMediaEvent)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint(endpointId) }), + ); const metadata = { newField: 'new field value', @@ -49,7 +53,9 @@ it('Update existing endpoint produce event', () => }); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createEndpointUpdatedPeerMetadata(endpointId, metadata))); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ endpointUpdated: createEndpointUpdatedPeerMetadata(endpointId, metadata) }), + ); })); it('Update existing endpoint with undefined metadata', () => { @@ -58,11 +64,13 @@ it('Update existing endpoint with undefined metadata', () => { const webRTCEndpoint = new WebRTCEndpoint(); const connectedMediaEvent = createConnectedEventWithOneEndpoint(endpointId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedMediaEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected: connectedMediaEvent })); // When const metadata = undefined; - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createEndpointUpdatedPeerMetadata(endpointId, metadata))); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ endpointUpdated: createEndpointUpdatedPeerMetadata(endpointId, metadata) }), + ); // Then const endpoint = webRTCEndpoint.getRemoteEndpoints()[endpointId]!; @@ -74,7 +82,7 @@ it('Update endpoint that not exist', () => { mockRTCPeerConnection(); const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEvent())); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected: createConnectedEvent() })); // When const metadata = { @@ -83,7 +91,9 @@ it('Update endpoint that not exist', () => { expect(() => webRTCEndpoint.receiveMediaEvent( - JSON.stringify(createEndpointUpdatedPeerMetadata(notExistingEndpointId, metadata)), + serializeServerMediaEvent({ + endpointUpdated: createEndpointUpdatedPeerMetadata(notExistingEndpointId, metadata), + }), ), ).rejects.toThrow(`Endpoint ${notExistingEndpointId} not found`); }); @@ -94,14 +104,16 @@ it('Parse metadata on endpoint update', () => { const webRTCEndpoint = new WebRTCEndpoint(); const connectedMediaEvent = createConnectedEventWithOneEndpoint(endpointId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedMediaEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected: connectedMediaEvent })); // When const metadata = { goodStuff: 'ye', }; - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createEndpointUpdatedPeerMetadata(endpointId, metadata))); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ endpointUpdated: createEndpointUpdatedPeerMetadata(endpointId, metadata) }), + ); // Then const endpoints = webRTCEndpoint.getRemoteEndpoints(); diff --git a/packages/webrtc-client/tests/events/trackAddedEvent.test.ts b/packages/webrtc-client/tests/events/trackAddedEvent.test.ts index adeb816e..45b1e276 100644 --- a/packages/webrtc-client/tests/events/trackAddedEvent.test.ts +++ b/packages/webrtc-client/tests/events/trackAddedEvent.test.ts @@ -6,9 +6,8 @@ import { createCustomOfferDataEventWithOneVideoTrack, trackId, } from '../fixtures'; -import type { CustomOfferDataEvent, TracksAddedMediaEvent } from '../schema'; import { mockRTCPeerConnection } from '../mocks'; -import { deserializeMediaEvent } from '../../src/mediaEvent'; +import { deserializePeerMediaEvent, serializeServerMediaEvent } from '../../src/mediaEvent'; import { expect, it } from 'vitest'; it('Connect to room with one endpoint then addTrack produce event', () => @@ -16,22 +15,24 @@ it('Connect to room with one endpoint then addTrack produce event', () => // Given const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint())); + const eventWithOneEndpoint = createConnectedEventWithOneEndpoint(); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected: eventWithOneEndpoint })); - const trackAddedEvent: TracksAddedMediaEvent = createAddTrackMediaEvent( - createConnectedEventWithOneEndpoint().data.otherEndpoints[0]!.id, - trackId, - ); + const [otherEndpointId] = Object.entries(eventWithOneEndpoint.endpointIdToEndpoint).find( + ([id]) => id !== eventWithOneEndpoint.endpointId, + )!; + + const tracksAdded = createAddTrackMediaEvent(otherEndpointId, trackId); webRTCEndpoint.on('trackAdded', (ctx) => { expect(ctx.trackId).toBe(trackId); - expect(ctx.endpoint.id).toBe(trackAddedEvent.data.endpointId); - expect(ctx.simulcastConfig?.enabled).toBe(trackAddedEvent.data.tracks[trackId]!.simulcastConfig.enabled); + expect(ctx.endpoint.id).toBe(tracksAdded.endpointId); + expect(ctx.simulcastConfig?.enabled).toBe(tracksAdded.trackIdToTrack[trackId]!.simulcastConfig?.enabled); done(''); }); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(trackAddedEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ tracksAdded })); // Then const remoteTracks: Record = webRTCEndpoint.getRemoteTracks(); @@ -43,13 +44,15 @@ it('Correctly parses track metadata', () => // Given const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint())); + const connected = createConnectedEventWithOneEndpoint(); + + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected })); - const trackAddedEvent: TracksAddedMediaEvent = createAddTrackMediaEvent( - createConnectedEventWithOneEndpoint().data.otherEndpoints[0]!.id, - trackId, - { peer: { goodStuff: 'ye', extraFluff: 'nah' } }, - ); + const otherEndpointId = Object.keys(connected.endpointIdToEndpoint).find((id) => id !== connected.endpointId)!; + + const tracksAdded = createAddTrackMediaEvent(otherEndpointId, trackId, { + peer: { goodStuff: 'ye', extraFluff: 'nah' }, + }); webRTCEndpoint.on('trackAdded', (ctx) => { // Then @@ -58,7 +61,7 @@ it('Correctly parses track metadata', () => }); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(trackAddedEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ tracksAdded })); })); it('tracksAdded -> handle offerData with one video track from server', () => @@ -68,29 +71,26 @@ it('tracksAdded -> handle offerData with one video track from server', () => const webRTCEndpoint = new WebRTCEndpoint(); - const connectedEvent = createConnectedEventWithOneEndpoint(); + const connected = createConnectedEventWithOneEndpoint(); + + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected })); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedEvent)); + const otherEndpointId = Object.keys(connected.endpointIdToEndpoint).find((id) => id !== connected.endpointId)!; - const trackAddedEvent: TracksAddedMediaEvent = createAddTrackMediaEvent( - connectedEvent.data.otherEndpoints[0]!.id, - trackId, - ); + const trackAddedEvent = createAddTrackMediaEvent(otherEndpointId, trackId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(trackAddedEvent)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ tracksAdded: trackAddedEvent })); - const offerData: CustomOfferDataEvent = createCustomOfferDataEventWithOneVideoTrack(); + const offerData = createCustomOfferDataEventWithOneVideoTrack(); webRTCEndpoint.on('sendMediaEvent', (mediaEvent) => { - expect(mediaEvent).toContain('sdpOffer'); - const event = deserializeMediaEvent(mediaEvent); - expect(event.type).toBe('custom'); - expect(event.data.type).toBe('sdpOffer'); + const event = deserializePeerMediaEvent(mediaEvent); + expect(event.sdpOffer).toBeDefined(); done(''); }); // When - webRTCEndpoint.receiveMediaEvent(JSON.stringify(offerData)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ offerData })); // Then @@ -111,16 +111,23 @@ it('tracksAdded -> offerData with one track -> handle sdpAnswer data with one vi const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint())); + const connected = createConnectedEventWithOneEndpoint(); + + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ connected })); + + const otherEndpointId = Object.keys(connected.endpointIdToEndpoint).find((id) => id !== connected.endpointId)!; + + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ tracksAdded: createAddTrackMediaEvent(otherEndpointId, trackId) }), + ); webRTCEndpoint.receiveMediaEvent( - JSON.stringify(createAddTrackMediaEvent(createConnectedEventWithOneEndpoint().data.otherEndpoints[0]!.id, trackId)), + serializeServerMediaEvent({ offerData: createCustomOfferDataEventWithOneVideoTrack() }), ); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createCustomOfferDataEventWithOneVideoTrack())); // When - const answerData = createAnswerData(trackId); + const sdpAnswer = createAnswerData(trackId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(answerData)); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ sdpAnswer })); // Then const midToTrackId = webRTCEndpoint['local']['getMidToTrackId'](); diff --git a/packages/webrtc-client/tests/events/trackRemovedEvent.test.ts b/packages/webrtc-client/tests/events/trackRemovedEvent.test.ts index 89b32b2c..b68584ca 100644 --- a/packages/webrtc-client/tests/events/trackRemovedEvent.test.ts +++ b/packages/webrtc-client/tests/events/trackRemovedEvent.test.ts @@ -1,4 +1,5 @@ import { WebRTCEndpoint } from '../../src'; +import { serializeServerMediaEvent } from '../../src/mediaEvent'; import { createTracksRemovedEvent, endpointId, trackId } from '../fixtures'; import { setupRoomWithMocks } from '../utils'; import { expect, it } from 'vitest'; @@ -17,8 +18,9 @@ it('Remove tracks event should emit event', () => }); // When - const addEndpointEvent = createTracksRemovedEvent(endpointId, [trackId]); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(addEndpointEvent)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ tracksRemoved: createTracksRemovedEvent(endpointId, [trackId]) }), + ); })); it('Remove tracks event should remove from local state', () => { @@ -28,8 +30,9 @@ it('Remove tracks event should remove from local state', () => { setupRoomWithMocks(webRTCEndpoint, endpointId, trackId); // When - const addEndpointEvent = createTracksRemovedEvent(endpointId, [trackId]); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(addEndpointEvent)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ tracksRemoved: createTracksRemovedEvent(endpointId, [trackId]) }), + ); const tracks = webRTCEndpoint.getRemoteTracks(); expect(Object.values(tracks).length).toBe(0); diff --git a/packages/webrtc-client/tests/events/trackUpdatedEvent.test.ts b/packages/webrtc-client/tests/events/trackUpdatedEvent.test.ts index 9d71208c..a0d3629a 100644 --- a/packages/webrtc-client/tests/events/trackUpdatedEvent.test.ts +++ b/packages/webrtc-client/tests/events/trackUpdatedEvent.test.ts @@ -1,4 +1,5 @@ import { WebRTCEndpoint } from '../../src'; +import { serializeServerMediaEvent } from '../../src/mediaEvent'; import { createTrackUpdatedEvent, endpointId, notExistingEndpointId, trackId } from '../fixtures'; import { setupRoom } from '../utils'; import { expect, it } from 'vitest'; @@ -21,8 +22,9 @@ it(`Updating existing track emits events`, () => }; // When - const trackUpdated = createTrackUpdatedEvent(trackId, endpointId, metadata); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(trackUpdated)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ trackUpdated: createTrackUpdatedEvent(trackId, endpointId, metadata) }), + ); })); it(`Updating existing track changes track metadata`, () => { @@ -36,8 +38,9 @@ it(`Updating existing track changes track metadata`, () => { }; // When - const trackUpdated = createTrackUpdatedEvent(trackId, endpointId, metadata); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(trackUpdated)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ trackUpdated: createTrackUpdatedEvent(trackId, endpointId, metadata) }), + ); // Then const track = webRTCEndpoint.getRemoteTracks()[trackId]; @@ -55,8 +58,9 @@ it('Correctly parses track metadata', () => { }; // When - const trackUpdated = createTrackUpdatedEvent(trackId, endpointId, metadata); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(trackUpdated)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ trackUpdated: createTrackUpdatedEvent(trackId, endpointId, metadata) }), + ); // Then const track = webRTCEndpoint.getRemoteTracks()[trackId]!; @@ -74,8 +78,9 @@ it.todo(`Webrtc endpoint skips updating local endpoint metadata`, () => { }; // When - const trackUpdated = createTrackUpdatedEvent(trackId, endpointId, metadata); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(trackUpdated)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ trackUpdated: createTrackUpdatedEvent(trackId, endpointId, metadata) }), + ); // Then // todo How should empty metadata be handled? @@ -97,9 +102,11 @@ it(`Updating track with invalid endpoint id throws error`, () => { }; // When - const trackUpdated = createTrackUpdatedEvent(trackId, notExistingEndpointId, metadata); - - expect(() => webRTCEndpoint.receiveMediaEvent(JSON.stringify(trackUpdated))) + expect(() => + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ trackUpdated: createTrackUpdatedEvent(trackId, notExistingEndpointId, metadata) }), + ), + ) // Then .rejects.toThrow(`Endpoint ${notExistingEndpointId} not found`); }); diff --git a/packages/webrtc-client/tests/events/vadNotificationEvent.test.ts b/packages/webrtc-client/tests/events/vadNotificationEvent.test.ts index 39634b00..d208c4dc 100644 --- a/packages/webrtc-client/tests/events/vadNotificationEvent.test.ts +++ b/packages/webrtc-client/tests/events/vadNotificationEvent.test.ts @@ -1,4 +1,6 @@ +import { MediaEvent_VadNotification_Status } from '@fishjam-cloud/protobufs/server'; import { WebRTCEndpoint } from '../../src'; +import { serializeServerMediaEvent } from '../../src/mediaEvent'; import { createCustomVadNotificationEvent, endpointId, trackId } from '../fixtures'; import { setupRoom } from '../utils'; import { expect, it } from 'vitest'; @@ -10,12 +12,15 @@ it(`Changing VAD notification to "speech" on existing track id`, () => { setupRoom(webRTCEndpoint, endpointId, trackId); // When - const vadNotificationEvent = createCustomVadNotificationEvent(trackId, 'speech'); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(vadNotificationEvent)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ + vadNotification: createCustomVadNotificationEvent(trackId, MediaEvent_VadNotification_Status.STATUS_SPEECH), + }), + ); // Then const track = webRTCEndpoint.getRemoteTracks()[trackId]!; - expect(track.vadStatus).toBe(vadNotificationEvent.data.data.status); + expect(track.vadStatus).toBe('speech'); }); it(`Changing VAD notification to "silence" on existing track id`, () => { @@ -25,12 +30,15 @@ it(`Changing VAD notification to "silence" on existing track id`, () => { setupRoom(webRTCEndpoint, endpointId, trackId); // When - const vadNotificationEvent = createCustomVadNotificationEvent(trackId, 'silence'); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(vadNotificationEvent)); + webRTCEndpoint.receiveMediaEvent( + serializeServerMediaEvent({ + vadNotification: createCustomVadNotificationEvent(trackId, MediaEvent_VadNotification_Status.STATUS_SILENCE), + }), + ); // Then const track = webRTCEndpoint.getRemoteTracks()[trackId]!; - expect(track.vadStatus).toBe(vadNotificationEvent.data.data.status); + expect(track.vadStatus).toBe('silence'); }); it(`Changing VAD notification emits event`, () => @@ -41,11 +49,11 @@ it(`Changing VAD notification emits event`, () => setupRoom(webRTCEndpoint, endpointId, trackId); webRTCEndpoint.getRemoteTracks()[trackId]!.on('voiceActivityChanged', (context) => { - expect(context.vadStatus).toBe(vadNotificationEvent.data.data.status); + expect(context.vadStatus).toBe('speech'); done(''); }); // When - const vadNotificationEvent = createCustomVadNotificationEvent(trackId, 'silence'); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(vadNotificationEvent)); + const vadNotification = createCustomVadNotificationEvent(trackId, MediaEvent_VadNotification_Status.STATUS_SPEECH); + webRTCEndpoint.receiveMediaEvent(serializeServerMediaEvent({ vadNotification })); })); diff --git a/packages/webrtc-client/tests/fixtures.ts b/packages/webrtc-client/tests/fixtures.ts index 7e7fd875..ecd12c81 100644 --- a/packages/webrtc-client/tests/fixtures.ts +++ b/packages/webrtc-client/tests/fixtures.ts @@ -1,36 +1,21 @@ -import type { - ConnectedMediaEvent, - CustomEncodingUpdatedEvent, - CustomOfferDataEvent, - CustomSdpAnswerDataEvent, - Endpoint, - EndpointUpdatedWebrtcEvent, - Track, - TracksAddedMediaEvent, - CustomBandwidthEstimationEvent, - CustomVadNotificationEvent, - TrackUpdatedEvent, - EndpointAddedWebrtcEvent, - EndpointRemovedEvent, - TracksRemovedEvent, -} from './schema'; +import { faker } from '@faker-js/faker'; +import type { MediaEvent_Track, MediaEvent_VadNotification_Status } from '@fishjam-cloud/protobufs/server'; import { - ConnectedMediaEventSchema, - CustomEncodingSwitchedEventSchema, - CustomOfferDataEventSchema, - CustomSdpAnswerDataEventSchema, - EndpointSchema, - EndpointUpdatedWebrtcEventSchema, - TracksAddedMediaEventSchema, - CustomBandwidthEstimationEventSchema, - CustomVadNotificationEventSchema, - TrackUpdatedEventSchema, - EndpointAddedWebrtcEventSchema, - EndpointRemovedEventSchema, - TracksRemovedEventSchema, -} from './schema'; + MediaEvent_Connected, + MediaEvent_Endpoint, + MediaEvent_EndpointAdded, + MediaEvent_EndpointRemoved, + MediaEvent_EndpointUpdated, + MediaEvent_OfferData, + MediaEvent_SdpAnswer, + MediaEvent_TracksAdded, + MediaEvent_TracksRemoved, + MediaEvent_TrackUpdated, + MediaEvent_TrackVariantSwitched, + MediaEvent_VadNotification, +} from '@fishjam-cloud/protobufs/server'; import { FakeMediaStreamTrack } from 'fake-mediastreamtrack'; -import type { Encoding, VadStatus } from '../src'; +import { Variant } from '../src'; import { vi } from 'vitest'; export const endpointId = 'exampleEndpointId'; @@ -43,300 +28,220 @@ export const mockTrack = new FakeMediaStreamTrack({ kind: 'video' }); const MediaStreamMock = vi.fn().mockImplementation(() => {}); export const stream = new MediaStreamMock(); -export const createSimulcastTrack = (metadata: any = undefined): Track => ({ - metadata, +export const createTrackWithSimulcast = (metadata: unknown = undefined): MediaEvent_Track => ({ + metadataJson: JSON.stringify(metadata), simulcastConfig: { enabled: true, - activeEncodings: ['h', 'm', 'l'], + enabledVariants: [Variant.VARIANT_LOW, Variant.VARIANT_MEDIUM, Variant.VARIANT_HIGH], + disabledVariants: [], }, }); -export const createEmptyEndpoint = (endpointId?: string): Endpoint => - EndpointSchema.parse({ - id: endpointId ?? '210fdb82-80d2-4868-8c31-a45f54f6e3c9', - metadata: { peer: undefined, server: undefined }, - trackIdToMetadata: {}, - tracks: {}, - type: 'webrtc', - }); - -export const createConnectedEvent = (localEndpointId?: string): ConnectedMediaEvent => { - const id = localEndpointId ?? '7b789673-8600-4c8b-8f45-476b86cb820d'; +export const createEmptyEndpoint = (): MediaEvent_Endpoint => + MediaEvent_Endpoint.create({ trackIdToTrack: {}, endpointType: 'webrtc' }); - return ConnectedMediaEventSchema.parse({ - type: 'connected', - data: { - id: id, // peerId - otherEndpoints: [], - }, - }); +export const createConnectedEvent = (endpointId = faker.string.uuid()): MediaEvent_Connected => { + return MediaEvent_Connected.create({ endpointId, endpointIdToEndpoint: { [endpointId]: createEmptyEndpoint() } }); }; export const createEncodingSwitchedEvent = ( endpointId: string, trackId: string, - encoding: Encoding, -): CustomEncodingUpdatedEvent => - CustomEncodingSwitchedEventSchema.parse({ - data: { - data: { - encoding: encoding, - endpointId: endpointId, - reason: 'other', - trackId: trackId, - }, - type: 'encodingSwitched', - }, - type: 'custom', + variant: Variant, +): MediaEvent_TrackVariantSwitched => + MediaEvent_TrackVariantSwitched.create({ + variant, + endpointId, + trackId, }); -export const createBandwidthEstimationEvent = (): CustomBandwidthEstimationEvent => - CustomBandwidthEstimationEventSchema.parse({ - data: { - data: { - estimation: 261506.7264961106, - }, - type: 'bandwidthEstimation', - }, - type: 'custom', - }); - -export const createCustomVadNotificationEvent = (trackId: string, vadStatus: VadStatus): CustomVadNotificationEvent => - CustomVadNotificationEventSchema.parse({ - data: { - data: { - status: vadStatus, - trackId: trackId, - }, - type: 'vadNotification', - }, - type: 'custom', +// export const createBandwidthEstimationEvent = (): CustomBandwidthEstimationEvent => +// CustomBandwidthEstimationEventSchema.parse({ +// data: { +// data: { +// estimation: 261506.7264961106, +// }, +// type: 'bandwidthEstimation', +// }, +// type: 'custom', +// }); + +export const createCustomVadNotificationEvent = ( + trackId: string, + status: MediaEvent_VadNotification_Status, +): MediaEvent_VadNotification => + MediaEvent_VadNotification.create({ + trackId, + status, }); -export const createTrackUpdatedEvent = (trackId: string, endpointId: string, metadata: unknown): TrackUpdatedEvent => - TrackUpdatedEventSchema.parse({ - data: { - endpointId: endpointId, - metadata: metadata, - trackId: trackId, - }, - type: 'trackUpdated', +export const createTrackUpdatedEvent = ( + trackId: string, + endpointId: string, + metadata: unknown, +): MediaEvent_TrackUpdated => + MediaEvent_TrackUpdated.create({ + endpointId: endpointId, + metadataJson: JSON.stringify(metadata), + trackId: trackId, }); -export const createEndpointAdded = (endpointId: string, metadata: any = undefined): EndpointAddedWebrtcEvent => - EndpointAddedWebrtcEventSchema.parse({ - data: { - id: endpointId, - metadata, - type: 'webrtc', - }, - type: 'endpointAdded', +export const createEndpointAdded = (endpointId: string, metadata: unknown = undefined): MediaEvent_EndpointAdded => + MediaEvent_EndpointAdded.create({ + endpointId, + metadataJson: JSON.stringify(metadata), }); -export const createEndpointRemoved = (endpointId: string): EndpointRemovedEvent => - EndpointRemovedEventSchema.parse({ - data: { - id: endpointId, - }, - type: 'endpointRemoved', +export const createEndpointRemoved = (endpointId: string): MediaEvent_EndpointRemoved => + MediaEvent_EndpointRemoved.create({ + endpointId, }); export const createConnectedEventWithOneEndpoint = ( - endpointId?: string, + endpointId: string = faker.string.uuid(), localEndpointId?: string, -): ConnectedMediaEvent => { +): MediaEvent_Connected => { const connectedEvent = createConnectedEvent(localEndpointId); - connectedEvent.data.otherEndpoints = [createEmptyEndpoint(endpointId)]; - return ConnectedMediaEventSchema.parse(connectedEvent); + const endpoint = createEmptyEndpoint(); + + connectedEvent.endpointIdToEndpoint = { ...connectedEvent.endpointIdToEndpoint, [endpointId]: endpoint }; + return MediaEvent_Connected.create(connectedEvent); }; export const createConnectedEventWithOneEndpointWithOneTrack = ( remoteEndpointId: string, trackId: string, localEndpointId?: string, -): ConnectedMediaEvent => { +): MediaEvent_Connected => { const connectedEvent = createConnectedEvent(localEndpointId); - connectedEvent.data.otherEndpoints = [createEmptyEndpoint(remoteEndpointId)]; - const endpoint = connectedEvent.data.otherEndpoints[0]!; + const remoteEndpoint = createEmptyEndpoint(); + + remoteEndpoint.trackIdToTrack[trackId] = createTrackWithSimulcast(); - endpoint.tracks[trackId] = createSimulcastTrack(); - endpoint.trackIdToMetadata[trackId] = {}; + connectedEvent.endpointIdToEndpoint = { + ...connectedEvent.endpointIdToEndpoint, + [remoteEndpointId]: remoteEndpoint, + }; - return ConnectedMediaEventSchema.parse(connectedEvent); + return MediaEvent_Connected.create(connectedEvent); }; export const createAddTrackMediaEvent = ( endpointId: string, trackId: string, metadata: any = undefined, -): TracksAddedMediaEvent => - TracksAddedMediaEventSchema.parse({ - type: 'tracksAdded', - data: { - endpointId: endpointId, - tracks: { - [trackId]: createSimulcastTrack(metadata), - }, - trackIdToMetadata: { - [trackId]: metadata, - }, +): MediaEvent_TracksAdded => + MediaEvent_TracksAdded.create({ + endpointId: endpointId, + trackIdToTrack: { + [trackId]: createTrackWithSimulcast(metadata), }, }); -export const createTracksRemovedEvent = (endpointId: string, trackIds: string[]): TracksRemovedEvent => - TracksRemovedEventSchema.parse({ - type: 'tracksRemoved', - data: { - endpointId: endpointId, - trackIds, - }, +export const createTracksRemovedEvent = (endpointId: string, trackIds: string[]): MediaEvent_TracksRemoved => + MediaEvent_TracksRemoved.create({ + endpointId: endpointId, + trackIds, }); -export const createCustomOfferDataEventWithOneVideoTrack = (): CustomOfferDataEvent => - CustomOfferDataEventSchema.parse({ - data: { - data: { - integratedTurnServers: [ - { - password: 'E9ck/2hJCkkuVSmPfFrNg2l1+JA=', - serverAddr: '192.168.1.95', - serverPort: 50018, - transport: 'udp', - username: '1698997572:dedfa04f-b30a-433a-86d5-03336a828caa', - }, - ], - tracksTypes: { - audio: 0, - video: 1, - }, - }, - type: 'offerData', +export const createCustomOfferDataEventWithOneVideoTrack = (): MediaEvent_OfferData => + MediaEvent_OfferData.create({ + tracksTypes: { + audio: 0, + video: 1, }, - type: 'custom', }); -export const createAddLocalTrackSDPOffer = (): CustomOfferDataEvent => - CustomOfferDataEventSchema.parse({ - data: { - data: { - integratedTurnServers: [ - { - password: 'LowwCOr4yR6KhD9LanOMfNbl1J4=', - serverAddr: '192.168.1.100', - serverPort: 50011, - transport: 'udp', - username: '1700768416:1423714f-5a75-4dce-9c99-8ec0dbf940ed', - }, - ], - tracksTypes: { - audio: 0, - video: 0, - }, - }, - type: 'offerData', +export const createAddLocalTrackSDPOffer = (): MediaEvent_OfferData => + MediaEvent_OfferData.create({ + tracksTypes: { + audio: 0, + video: 0, }, - type: 'custom', }); -export const createAnswerData = (trackId: string): CustomSdpAnswerDataEvent => - CustomSdpAnswerDataEventSchema.parse({ - data: { - data: { - midToTrackId: { - '0': '9afe80ce-1964-4958-a386-d7a9e3097ca7:5c74b6b3-cb72-49f1-a76b-0df4895a3d32', - }, - sdp: `v=0\r -o=- 39483584182226872 0 IN IP4 127.0.0.1\r -s=-\r -t=0 0\r -a=group:BUNDLE 0\r -a=extmap-allow-mixed\r -a=ice-lite\r -m=video 9 UDP/TLS/RTP/SAVPF 102 103\r -c=IN IP4 0.0.0.0\r -a=sendonly\r -a=ice-ufrag:fXa4\r -a=ice-pwd:mC2wFgKGsN3cXnxadEhVaa\r -a=ice-options:trickle\r -a=fingerprint:sha-256 50:65:CB:9F:2B:B5:62:7F:20:59:79:C6:7B:49:D8:DF:C2:B5:59:1F:E2:7D:68:F8:C3:07:73:8B:16:70:FB:DD\r -a=setup:passive\r -a=mid:0\r -a=msid:60ff1fb2-6868-42be-8c92-311733034415 ea1339b9-54ce-445b-9cff-2568f9ac504b\r -a=rtcp-mux\r -a=rtpmap:102 H264/90000\r -a=fmtp:102 profile-level-id=42001f;level-asymmetry-allowed=1;packetization-mode=1\r -a=rtpmap:103 rtx/90000\r -a=fmtp:103 apt=102\r -a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r -a=rtcp-fb:102 transport-cc\r -a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r -a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r -a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r -a=rtcp-fb:102 ccm fir\r -a=rtcp-fb:102 nack\r -a=rtcp-fb:102 nack pli\r -a=rtcp-rsize\r -a=ssrc:663086196 cname:${trackId}-video-60ff1fb2-6868-42be-8c92-311733034415\r -`, - type: 'answer', - }, - type: 'sdpAnswer', +export const createAnswerData = (trackId: string): MediaEvent_SdpAnswer => + MediaEvent_SdpAnswer.create({ + sdp: `v=0\r + o=- 39483584182226872 0 IN IP4 127.0.0.1\r + s=-\r + t=0 0\r + a=group:BUNDLE 0\r + a=extmap-allow-mixed\r + a=ice-lite\r + m=video 9 UDP/TLS/RTP/SAVPF 102 103\r + c=IN IP4 0.0.0.0\r + a=sendonly\r + a=ice-ufrag:fXa4\r + a=ice-pwd:mC2wFgKGsN3cXnxadEhVaa\r + a=ice-options:trickle\r + a=fingerprint:sha-256 50:65:CB:9F:2B:B5:62:7F:20:59:79:C6:7B:49:D8:DF:C2:B5:59:1F:E2:7D:68:F8:C3:07:73:8B:16:70:FB:DD\r + a=setup:passive\r + a=mid:0\r + a=msid:60ff1fb2-6868-42be-8c92-311733034415 ea1339b9-54ce-445b-9cff-2568f9ac504b\r + a=rtcp-mux\r + a=rtpmap:102 H264/90000\r + a=fmtp:102 profile-level-id=42001f;level-asymmetry-allowed=1;packetization-mode=1\r + a=rtpmap:103 rtx/90000\r + a=fmtp:103 apt=102\r + a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r + a=rtcp-fb:102 transport-cc\r + a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r + a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r + a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r + a=rtcp-fb:102 ccm fir\r + a=rtcp-fb:102 nack\r + a=rtcp-fb:102 nack pli\r + a=rtcp-rsize\r + a=ssrc:663086196 cname:${trackId}-video-60ff1fb2-6868-42be-8c92-311733034415\r + `, + midToTrackId: { + '0': '9afe80ce-1964-4958-a386-d7a9e3097ca7:5c74b6b3-cb72-49f1-a76b-0df4895a3d32', }, - type: 'custom', }); -export const createAddLocalTrackAnswerData = (trackId: string): CustomSdpAnswerDataEvent => - CustomSdpAnswerDataEventSchema.parse({ - data: { - data: { - midToTrackId: { - '0': trackId, - }, - sdp: `v=0\r -o=- 63903156084304368 0 IN IP4 127.0.0.1\r -s=-\r -t=0 0\r -a=group:BUNDLE 0\r -a=extmap-allow-mixed\r -a=ice-lite\r -m=video 9 UDP/TLS/RTP/SAVPF 106 107\r -c=IN IP4 0.0.0.0\r -a=recvonly\r -a=ice-ufrag:dHiY\r -a=ice-pwd:IAPCE68QAQ8AxSF0OQIEZp\r -a=ice-options:trickle\r -a=fingerprint:sha-256 C1:50:4C:EC:98:1D:62:C8:DA:AE:F8:5B:44:4F:76:BB:4E:FF:5E:51:3E:A7:62:9B:58:38:A5:13:D0:B1:50:67\r -a=setup:passive\r -a=mid:0\r -a=msid:7bf8bef4-be67-456c-8635-ba58339c29e9 ad3deb09-60a6-4bfc-aa14-482ed4f60667\r -a=rtcp-mux\r -a=rtpmap:106 H264/90000\r -a=fmtp:106 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1\r -a=rtpmap:107 rtx/90000\r -a=fmtp:107 apt=106\r -a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r -a=rtcp-fb:106 transport-cc\r -a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r -a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r -a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r -a=rtcp-fb:106 ccm fir\r -a=rtcp-fb:106 nack\r -a=rtcp-fb:106 nack pli\r -a=rtcp-rsize\r -`, - type: 'answer', - }, - type: 'sdpAnswer', +export const createAddLocalTrackAnswerData = (trackId: string): MediaEvent_SdpAnswer => + MediaEvent_SdpAnswer.create({ + midToTrackId: { + '0': trackId, }, - type: 'custom', + sdp: `v=0\r + o=- 63903156084304368 0 IN IP4 127.0.0.1\r + s=-\r + t=0 0\r + a=group:BUNDLE 0\r + a=extmap-allow-mixed\r + a=ice-lite\r + m=video 9 UDP/TLS/RTP/SAVPF 106 107\r + c=IN IP4 0.0.0.0\r + a=recvonly\r + a=ice-ufrag:dHiY\r + a=ice-pwd:IAPCE68QAQ8AxSF0OQIEZp\r + a=ice-options:trickle\r + a=fingerprint:sha-256 C1:50:4C:EC:98:1D:62:C8:DA:AE:F8:5B:44:4F:76:BB:4E:FF:5E:51:3E:A7:62:9B:58:38:A5:13:D0:B1:50:67\r + a=setup:passive\r + a=mid:0\r + a=msid:7bf8bef4-be67-456c-8635-ba58339c29e9 ad3deb09-60a6-4bfc-aa14-482ed4f60667\r + a=rtcp-mux\r + a=rtpmap:106 H264/90000\r + a=fmtp:106 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1\r + a=rtpmap:107 rtx/90000\r + a=fmtp:107 apt=106\r + a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r + a=rtcp-fb:106 transport-cc\r + a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r + a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r + a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r + a=rtcp-fb:106 ccm fir\r + a=rtcp-fb:106 nack\r + a=rtcp-fb:106 nack pli\r + a=rtcp-rsize\r + `, }); -export const createEndpointUpdatedPeerMetadata = (endpointId: string, metadata: any): EndpointUpdatedWebrtcEvent => - EndpointUpdatedWebrtcEventSchema.parse({ - data: { - id: endpointId, - metadata, - }, - type: 'endpointUpdated', +export const createEndpointUpdatedPeerMetadata = (endpointId: string, metadata: unknown): MediaEvent_EndpointUpdated => + MediaEvent_EndpointUpdated.create({ + endpointId, + metadataJson: JSON.stringify(metadata), }); diff --git a/packages/webrtc-client/tests/methods/addTrackMethod.test.ts b/packages/webrtc-client/tests/methods/addTrackMethod.test.ts index 7245fc38..1d029731 100644 --- a/packages/webrtc-client/tests/methods/addTrackMethod.test.ts +++ b/packages/webrtc-client/tests/methods/addTrackMethod.test.ts @@ -1,7 +1,7 @@ import { WebRTCEndpoint } from '../../src'; import { createConnectedEventWithOneEndpoint, mockTrack } from '../fixtures'; import { mockMediaStream, mockRTCPeerConnection } from '../mocks'; -import { deserializeMediaEvent } from '../../src/mediaEvent'; +import { deserializePeerMediaEvent, serializeServerMediaEvent } from '../../src/mediaEvent'; import { expect, it } from 'vitest'; it('Adding track invokes renegotiation', () => @@ -10,14 +10,13 @@ it('Adding track invokes renegotiation', () => const webRTCEndpoint = new WebRTCEndpoint(); mockMediaStream(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint())); + const mediaEvent = serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint() }); + webRTCEndpoint.receiveMediaEvent(mediaEvent); webRTCEndpoint.on('sendMediaEvent', (mediaEvent) => { // Then - expect(mediaEvent).toContain('renegotiateTracks'); - const event = deserializeMediaEvent(mediaEvent); - expect(event.type).toBe('custom'); - expect(event.data.type).toBe('renegotiateTracks'); + const event = deserializePeerMediaEvent(mediaEvent); + expect(event.renegotiateTracks).toBeTruthy(); done(''); // now it's time to create offer and answer @@ -36,7 +35,9 @@ it('Adding track updates internal state', () => { const webRTCEndpoint = new WebRTCEndpoint(); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createConnectedEventWithOneEndpoint())); + const mediaEvent = serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint() }); + + webRTCEndpoint.receiveMediaEvent(mediaEvent); // When webRTCEndpoint.addTrack(mockTrack); diff --git a/packages/webrtc-client/tests/methods/connectMethod.test.ts b/packages/webrtc-client/tests/methods/connectMethod.test.ts index 13f39b61..dcb1e8dc 100644 --- a/packages/webrtc-client/tests/methods/connectMethod.test.ts +++ b/packages/webrtc-client/tests/methods/connectMethod.test.ts @@ -1,5 +1,5 @@ import { WebRTCEndpoint } from '../../src'; -import { deserializeMediaEvent } from '../../src/mediaEvent'; +import { deserializePeerMediaEvent } from '../../src/mediaEvent'; import { expect, it } from 'vitest'; it('Method connect sends mediaEvent to backend', () => @@ -11,8 +11,8 @@ it('Method connect sends mediaEvent to backend', () => webRTCEndpoint.on('sendMediaEvent', (mediaEvent) => { // Then - const event = deserializeMediaEvent(mediaEvent); - expect(event.type).toBe('connect'); + const event = deserializePeerMediaEvent(mediaEvent); + expect(event.connect).toBeTruthy(); done(''); }); @@ -29,8 +29,10 @@ it("Method 'connect' sends metadata in event", () => webRTCEndpoint.on('sendMediaEvent', (mediaEvent) => { // Then - const event: any = deserializeMediaEvent(mediaEvent); - expect(event.data.metadata).toMatchObject(peerMetadata); + const event = deserializePeerMediaEvent(mediaEvent); + const metadataJson = event.connect?.metadataJson; + expect(metadataJson).toBeDefined(); + expect(JSON.parse(metadataJson!)).toMatchObject(peerMetadata); done(''); }); diff --git a/packages/webrtc-client/tests/methods/disconnectMethod.test.ts b/packages/webrtc-client/tests/methods/disconnectMethod.test.ts index 1c0d8756..577daffa 100644 --- a/packages/webrtc-client/tests/methods/disconnectMethod.test.ts +++ b/packages/webrtc-client/tests/methods/disconnectMethod.test.ts @@ -1,7 +1,7 @@ import { WebRTCEndpoint } from '../../src'; import { endpointId, trackId } from '../fixtures'; import { setupRoomWithMocks } from '../utils'; -import { deserializeMediaEvent } from '../../src/mediaEvent'; +import { deserializePeerMediaEvent } from '../../src/mediaEvent'; import { expect, it } from 'vitest'; it('Disconnect sets connection to undefined', async () => { @@ -27,8 +27,8 @@ it('Disconnect invokes disconnected event', () => await setupRoomWithMocks(webRTCEndpoint, endpointId, trackId); webRTCEndpoint.on('sendMediaEvent', (mediaEvent) => { - const event = deserializeMediaEvent(mediaEvent); - expect(event.type).toBe('disconnect'); + const event = deserializePeerMediaEvent(mediaEvent); + expect(event.disconnect).toBeTruthy(); done(''); }); diff --git a/packages/webrtc-client/tests/schema.ts b/packages/webrtc-client/tests/schema.ts deleted file mode 100644 index dc31b711..00000000 --- a/packages/webrtc-client/tests/schema.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { z } from 'zod'; - -export const TrackIdToMetadataSchema = z.record(z.any()); - -export type TrackIdToMetadata = z.infer; - -export const TrackSchema = z.object({ - metadata: z.any(), - simulcastConfig: z.object({ - activeEncodings: z.array(z.union([z.literal('h'), z.literal('m'), z.literal('l')])), - enabled: z.boolean(), - }), -}); - -export type Track = z.infer; - -export const EndpointSchema = z.object({ - id: z.string().min(1), // peer / component id - metadata: z.union([z.null(), z.unknown()]), // null or object - trackIdToMetadata: TrackIdToMetadataSchema, - tracks: z.record(TrackSchema), - type: z.string(), // fix 'webrtc' etc. -}); - -export type Endpoint = z.infer; - -export const ConnectedMediaEventSchema = z.object({ - data: z.object({ - id: z.string().min(1), // uuid room id - otherEndpoints: z.array(EndpointSchema), - }), - type: z.literal('connected'), -}); - -export type ConnectedMediaEvent = z.infer; - -export const TracksAddedMediaEventSchema = z.object({ - data: z.object({ - endpointId: z.string().min(1), // uuid room id - trackIdToMetadata: TrackIdToMetadataSchema, // todo fix, - tracks: z.record(TrackSchema), - }), - type: z.literal('tracksAdded'), -}); - -export type TracksAddedMediaEvent = z.infer; - -export const TracksRemovedEventSchema = z.object({ - data: z.object({ - endpointId: z.string().min(1), - trackIds: z.array(z.string().min(1)), - }), - type: z.literal('tracksRemoved'), -}); - -export type TracksRemovedEvent = z.infer; - -export const CustomOfferDataEventSchema = z.object({ - data: z.object({ - data: z.object({ - integratedTurnServers: z.array( - z.object({ - password: z.string().min(1), - serverAddr: z.string().min(1), - serverPort: z.number(), - transport: z.string().min(1), - username: z.string().min(1), - }), - ), - tracksTypes: z.object({ - audio: z.number(), // .min(0).max(infinity) - number of video tracks - video: z.number(), // .min(0).max(infinity) - number of audio tracks - }), - }), - type: z.literal('offerData'), - }), - type: z.literal('custom'), -}); - -export type CustomOfferDataEvent = z.infer; - -export const CustomSdpAnswerDataEventSchema = z.object({ - data: z.object({ - data: z.object({ - midToTrackId: z.record(z.string().min(1)), // key is number.toString() eg. "0", "1"... - sdp: z.string().min(1), - type: z.literal('answer'), - }), - type: z.literal('sdpAnswer'), - }), - type: z.literal('custom'), -}); - -export type CustomSdpAnswerDataEvent = z.infer; - -export const EndpointAddedWebrtcEventSchema = z.object({ - data: z.object({ - id: z.string().min(1), - metadata: z.any(), - type: z.union([z.literal('webrtc'), z.literal('hls'), z.literal('rtsp')]), - }), - type: z.literal('endpointAdded'), -}); - -export type EndpointAddedWebrtcEvent = z.infer; - -export const EndpointRemovedEventSchema = z.object({ - data: z.object({ - id: z.string().min(1), - }), - type: z.literal('endpointRemoved'), -}); - -export type EndpointRemovedEvent = z.infer; - -export const EndpointUpdatedWebrtcEventSchema = z.object({ - data: z.object({ - id: z.string().min(1), - metadata: z.any(), - }), - type: z.literal('endpointUpdated'), -}); - -export type EndpointUpdatedWebrtcEvent = z.infer; - -export const CustomEncodingSwitchedEventSchema = z.object({ - data: z.object({ - data: z.object({ - encoding: z.union([z.literal('l'), z.literal('m'), z.literal('h')]), - endpointId: z.string().min(1), - reason: z.string(), - trackId: z.string().min(1), - }), - type: z.literal('encodingSwitched'), - }), - type: z.literal('custom'), -}); - -export type CustomEncodingUpdatedEvent = z.infer; - -export const CustomBandwidthEstimationEventSchema = z.object({ - data: z.object({ - data: z.object({ - estimation: z.number(), - }), - type: z.literal('bandwidthEstimation'), - }), - type: z.literal('custom'), -}); - -export type CustomBandwidthEstimationEvent = z.infer; - -export const CustomVadNotificationEventSchema = z.object({ - data: z.object({ - data: z.object({ - status: z.union([z.literal('speech'), z.literal('silence')]), - trackId: z.string().min(1), - }), - type: z.literal('vadNotification'), - }), - type: z.literal('custom'), -}); - -export type CustomVadNotificationEvent = z.infer; - -export const TrackUpdatedEventSchema = z.object({ - data: z.object({ - endpointId: z.string().min(1), - metadata: z.union([z.null(), z.unknown()]), - trackId: z.string().min(1), - }), - type: z.literal('trackUpdated'), -}); - -export type TrackUpdatedEvent = z.infer; - -export type MediaEvent = TracksAddedMediaEvent | ConnectedMediaEvent; -export type CustomEvent = CustomOfferDataEvent | CustomSdpAnswerDataEvent; diff --git a/packages/webrtc-client/tests/utils.ts b/packages/webrtc-client/tests/utils.ts index 36512a6f..6b447434 100644 --- a/packages/webrtc-client/tests/utils.ts +++ b/packages/webrtc-client/tests/utils.ts @@ -7,16 +7,19 @@ import { } from './fixtures'; import type { WebRTCEndpoint } from '../src'; import { mockRTCPeerConnection } from './mocks'; +import { MediaEvent } from '@fishjam-cloud/protobufs/server'; export const setupRoom = (webRTCEndpoint: WebRTCEndpoint, endpointId: string, trackId: string): void => { - const connectedEvent = createConnectedEventWithOneEndpointWithOneTrack(endpointId, trackId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(connectedEvent)); + const connected = createConnectedEventWithOneEndpointWithOneTrack(endpointId, trackId); + const connectedEvent = MediaEvent.encode({ connected }).finish(); + webRTCEndpoint.receiveMediaEvent(connectedEvent); // Right now info about tracks from connectedEvent is ignored // We need to fix it but not in this commit // We need more tests (e2e) to introduce this change - const addTrackEvent = createAddTrackMediaEvent(endpointId, trackId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(addTrackEvent)); + const tracksAdded = createAddTrackMediaEvent(endpointId, trackId); + const tracksAddedEvent = MediaEvent.encode({ tracksAdded }).finish(); + webRTCEndpoint.receiveMediaEvent(tracksAddedEvent); }; // Fix proposal: @@ -49,8 +52,11 @@ export const setupRoomWithMocks = async ( setupRoom(webRTCEndpoint, endpointId, trackId); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createAddLocalTrackSDPOffer())); - webRTCEndpoint.receiveMediaEvent(JSON.stringify(createAddLocalTrackAnswerData(trackId))); + const offerData = createAddLocalTrackSDPOffer(); + webRTCEndpoint.receiveMediaEvent(MediaEvent.encode({ offerData }).finish()); + + const sdpAnswer = createAddLocalTrackAnswerData(trackId); + webRTCEndpoint.receiveMediaEvent(MediaEvent.encode({ sdpAnswer }).finish()); const connection = webRTCEndpoint['connectionManager']!; const transciever = new RTCRtpTransceiver(); diff --git a/packages/webrtc-client/tsconfig.json b/packages/webrtc-client/tsconfig.json index 36fadb22..2c7f2635 100644 --- a/packages/webrtc-client/tsconfig.json +++ b/packages/webrtc-client/tsconfig.json @@ -7,7 +7,6 @@ "jsx": "react-jsx", "strict": true, - "incremental": true, "skipLibCheck": true, "esModuleInterop": true, "declaration": true, @@ -15,8 +14,12 @@ "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "noUncheckedIndexedAccess": true, - - "outDir": "dist" + "outDir": "dist", + "paths": { + "@fishjam-cloud/protobufs/peer": ["../protobufs/fishjam/media_events/peer/peer.ts"], + "@fishjam-cloud/protobufs/server": ["../protobufs/fishjam/media_events/server/server.ts"], + "@fishjam-cloud/protobufs/shared": ["../protobufs/fishjam/media_events/shared.ts"] + } }, "include": ["src/**/*", "tests/**/*"] } diff --git a/yarn.lock b/yarn.lock index 89867fb3..86c079dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,17 +22,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.12.13": - version: 7.24.7 - resolution: "@babel/code-frame@npm:7.24.7" - dependencies: - "@babel/highlight": "npm:^7.24.7" - picocolors: "npm:^1.0.0" - checksum: 10c0/ab0af539473a9f5aeaac7047e377cb4f4edd255a81d84a76058595f8540784cc3fbe8acf73f1e073981104562490aabfb23008cd66dc677a456a4ed5390fdde6 - languageName: node - linkType: hard - -"@babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0": +"@babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0": version: 7.26.2 resolution: "@babel/code-frame@npm:7.26.2" dependencies: @@ -129,13 +119,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/helper-string-parser@npm:7.24.8" - checksum: 10c0/6361f72076c17fabf305e252bf6d580106429014b3ab3c1f5c4eb3e6d465536ea6b670cc0e9a637a77a9ad40454d3e41361a2909e70e305116a23d68ce094c08 - languageName: node - linkType: hard - "@babel/helper-string-parser@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-string-parser@npm:7.25.9" @@ -143,13 +126,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-validator-identifier@npm:7.24.7" - checksum: 10c0/87ad608694c9477814093ed5b5c080c2e06d44cb1924ae8320474a74415241223cc2a725eea2640dd783ff1e3390e5f95eede978bc540e870053152e58f1d651 - languageName: node - linkType: hard - "@babel/helper-validator-identifier@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-validator-identifier@npm:7.25.9" @@ -174,30 +150,7 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/highlight@npm:7.24.7" - dependencies: - "@babel/helper-validator-identifier": "npm:^7.24.7" - chalk: "npm:^2.4.2" - js-tokens: "npm:^4.0.0" - picocolors: "npm:^1.0.0" - checksum: 10c0/674334c571d2bb9d1c89bdd87566383f59231e16bcdcf5bb7835babdf03c9ae585ca0887a7b25bdf78f303984af028df52831c7989fecebb5101cc132da9393a - languageName: node - linkType: hard - -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.25.4": - version: 7.25.4 - resolution: "@babel/parser@npm:7.25.4" - dependencies: - "@babel/types": "npm:^7.25.4" - bin: - parser: ./bin/babel-parser.js - checksum: 10c0/bdada5662f15d1df11a7266ec3bc9bb769bf3637ecf3d051eafcfc8f576dcf5a3ac1007c5e059db4a1e1387db9ae9caad239fc4f79e4c2200930ed610e779993 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.0, @babel/parser@npm:^7.26.2": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.0, @babel/parser@npm:^7.26.2": version: 7.26.2 resolution: "@babel/parser@npm:7.26.2" dependencies: @@ -256,18 +209,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.4": - version: 7.25.4 - resolution: "@babel/types@npm:7.25.4" - dependencies: - "@babel/helper-string-parser": "npm:^7.24.8" - "@babel/helper-validator-identifier": "npm:^7.24.7" - to-fast-properties: "npm:^2.0.0" - checksum: 10c0/9aa25dfcd89cc4e4dde3188091c34398a005a49e2c2b069d0367b41e1122c91e80fd92998c52a90f2fb500f7e897b6090ec8be263d9cb53d0d75c756f44419f2 - languageName: node - linkType: hard - -"@babel/types@npm:^7.25.9, @babel/types@npm:^7.26.0": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.4, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.0": version: 7.26.0 resolution: "@babel/types@npm:7.26.0" dependencies: @@ -291,7 +233,7 @@ __metadata: languageName: node linkType: hard -"@bufbuild/protobuf@npm:^2.2.2": +"@bufbuild/protobuf@npm:^2.0.0, @bufbuild/protobuf@npm:^2.2.2": version: 2.2.2 resolution: "@bufbuild/protobuf@npm:2.2.2" checksum: 10c0/17687c36c85b2e489c7ffd676479c54e12437270eb5a724775cb880b09948cad205b79e5822a4d10cefde0eef433b781350d73bb649d8de9e84d7745871ee719 @@ -467,20 +409,20 @@ __metadata: linkType: hard "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": - version: 4.4.0 - resolution: "@eslint-community/eslint-utils@npm:4.4.0" + version: 4.4.1 + resolution: "@eslint-community/eslint-utils@npm:4.4.1" dependencies: - eslint-visitor-keys: "npm:^3.3.0" + eslint-visitor-keys: "npm:^3.4.3" peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - checksum: 10c0/7e559c4ce59cd3a06b1b5a517b593912e680a7f981ae7affab0d01d709e99cd5647019be8fafa38c350305bc32f1f7d42c7073edde2ab536c745e365f37b607e + checksum: 10c0/2aa0ac2fc50ff3f234408b10900ed4f1a0b19352f21346ad4cc3d83a1271481bdda11097baa45d484dd564c895e0762a27a8240be7a256b3ad47129e96528252 languageName: node linkType: hard "@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.6.1": - version: 4.11.0 - resolution: "@eslint-community/regexpp@npm:4.11.0" - checksum: 10c0/0f6328869b2741e2794da4ad80beac55cba7de2d3b44f796a60955b0586212ec75e6b0253291fd4aad2100ad471d1480d8895f2b54f1605439ba4c875e05e523 + version: 4.12.1 + resolution: "@eslint-community/regexpp@npm:4.12.1" + checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6 languageName: node linkType: hard @@ -508,6 +450,13 @@ __metadata: languageName: node linkType: hard +"@faker-js/faker@npm:^9.2.0": + version: 9.2.0 + resolution: "@faker-js/faker@npm:9.2.0" + checksum: 10c0/d711a5d206558f90e3ce9ecafe366e236fbe190b4df9d3968b512ccb87ec625843c919d16050beade88b790ed3df6332f6a837e41fba6de33e7a2f8daa67f08d + languageName: node + linkType: hard + "@fastify/busboy@npm:^2.0.0": version: 2.1.1 resolution: "@fastify/busboy@npm:2.1.1" @@ -515,6 +464,15 @@ __metadata: languageName: node linkType: hard +"@fishjam-cloud/protobufs@workspace:*, @fishjam-cloud/protobufs@workspace:^, @fishjam-cloud/protobufs@workspace:packages/protobufs": + version: 0.0.0-use.local + resolution: "@fishjam-cloud/protobufs@workspace:packages/protobufs" + dependencies: + ts-proto: "npm:^2.2.7" + tsup: "npm:^8.3.5" + languageName: unknown + linkType: soft + "@fishjam-cloud/react-client@workspace:*, @fishjam-cloud/react-client@workspace:packages/react-client": version: 0.0.0-use.local resolution: "@fishjam-cloud/react-client@workspace:packages/react-client" @@ -540,6 +498,7 @@ __metadata: resolution: "@fishjam-cloud/ts-client@workspace:packages/ts-client" dependencies: "@bufbuild/protobuf": "npm:^2.2.2" + "@fishjam-cloud/protobufs": "workspace:*" "@fishjam-cloud/webrtc-client": "workspace:*" "@playwright/test": "npm:^1.49.0" "@types/events": "npm:^3.0.3" @@ -550,6 +509,7 @@ __metadata: fake-mediastreamtrack: "npm:^1.2.0" husky: "npm:^9.1.7" lint-staged: "npm:^15.2.10" + tsup: "npm:^8.3.5" typed-emitter: "npm:^2.1.0" typedoc: "npm:^0.27.0" typedoc-plugin-external-resolver: "npm:^1.0.3" @@ -566,6 +526,8 @@ __metadata: resolution: "@fishjam-cloud/webrtc-client@workspace:packages/webrtc-client" dependencies: "@bufbuild/protobuf": "npm:^2.2.2" + "@faker-js/faker": "npm:^9.2.0" + "@fishjam-cloud/protobufs": "workspace:*" "@playwright/test": "npm:^1.49.0" "@types/events": "npm:^3.0.3" "@types/node": "npm:^22.10.0" @@ -575,6 +537,7 @@ __metadata: fake-mediastreamtrack: "npm:^1.2.0" husky: "npm:^9.1.7" lint-staged: "npm:^15.2.10" + tsup: "npm:^8.3.5" typed-emitter: "npm:^2.1.0" typedoc: "npm:^0.27.0" typedoc-plugin-external-resolver: "npm:^1.0.3" @@ -599,9 +562,9 @@ __metadata: languageName: unknown linkType: soft -"@fishjam-e2e/ts-client-e2e@workspace:e2e-tests/ts-client/app": +"@fishjam-e2e/webrtc-client-e2e@workspace:e2e-tests/webrtc-client/app": version: 0.0.0-use.local - resolution: "@fishjam-e2e/ts-client-e2e@workspace:e2e-tests/ts-client/app" + resolution: "@fishjam-e2e/webrtc-client-e2e@workspace:e2e-tests/webrtc-client/app" dependencies: "@fishjam-cloud/ts-client": "workspace:*" "@playwright/test": "npm:^1.49.0" @@ -739,12 +702,12 @@ __metadata: linkType: hard "@floating-ui/dom@npm:^1.0.0": - version: 1.6.11 - resolution: "@floating-ui/dom@npm:1.6.11" + version: 1.6.12 + resolution: "@floating-ui/dom@npm:1.6.12" dependencies: "@floating-ui/core": "npm:^1.6.0" "@floating-ui/utils": "npm:^0.2.8" - checksum: 10c0/02ef34a75a515543c772880338eea7b66724997bd5ec7cd58d26b50325709d46d480a306b84e7d5509d734434411a4bcf23af5680c2e461e6e6a8bf45d751df8 + checksum: 10c0/c67b39862175b175c6ac299ea970f17a22c7482cfdf3b1bc79313407bf0880188b022b878953fa69d3ce166ff2bd9ae57c86043e5dd800c262b470d877591b7d languageName: node linkType: hard @@ -1721,92 +1684,92 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-darwin-arm64@npm:1.7.28" +"@swc/core-darwin-arm64@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-darwin-arm64@npm:1.9.3" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-darwin-x64@npm:1.7.28" +"@swc/core-darwin-x64@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-darwin-x64@npm:1.9.3" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.28" +"@swc/core-linux-arm-gnueabihf@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.9.3" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-linux-arm64-gnu@npm:1.7.28" +"@swc/core-linux-arm64-gnu@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-arm64-gnu@npm:1.9.3" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-linux-arm64-musl@npm:1.7.28" +"@swc/core-linux-arm64-musl@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-arm64-musl@npm:1.9.3" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-linux-x64-gnu@npm:1.7.28" +"@swc/core-linux-x64-gnu@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-x64-gnu@npm:1.9.3" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-linux-x64-musl@npm:1.7.28" +"@swc/core-linux-x64-musl@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-x64-musl@npm:1.9.3" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-win32-arm64-msvc@npm:1.7.28" +"@swc/core-win32-arm64-msvc@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-win32-arm64-msvc@npm:1.9.3" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-win32-ia32-msvc@npm:1.7.28" +"@swc/core-win32-ia32-msvc@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-win32-ia32-msvc@npm:1.9.3" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.7.28": - version: 1.7.28 - resolution: "@swc/core-win32-x64-msvc@npm:1.7.28" +"@swc/core-win32-x64-msvc@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-win32-x64-msvc@npm:1.9.3" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.7.26": - version: 1.7.28 - resolution: "@swc/core@npm:1.7.28" - dependencies: - "@swc/core-darwin-arm64": "npm:1.7.28" - "@swc/core-darwin-x64": "npm:1.7.28" - "@swc/core-linux-arm-gnueabihf": "npm:1.7.28" - "@swc/core-linux-arm64-gnu": "npm:1.7.28" - "@swc/core-linux-arm64-musl": "npm:1.7.28" - "@swc/core-linux-x64-gnu": "npm:1.7.28" - "@swc/core-linux-x64-musl": "npm:1.7.28" - "@swc/core-win32-arm64-msvc": "npm:1.7.28" - "@swc/core-win32-ia32-msvc": "npm:1.7.28" - "@swc/core-win32-x64-msvc": "npm:1.7.28" + version: 1.9.3 + resolution: "@swc/core@npm:1.9.3" + dependencies: + "@swc/core-darwin-arm64": "npm:1.9.3" + "@swc/core-darwin-x64": "npm:1.9.3" + "@swc/core-linux-arm-gnueabihf": "npm:1.9.3" + "@swc/core-linux-arm64-gnu": "npm:1.9.3" + "@swc/core-linux-arm64-musl": "npm:1.9.3" + "@swc/core-linux-x64-gnu": "npm:1.9.3" + "@swc/core-linux-x64-musl": "npm:1.9.3" + "@swc/core-win32-arm64-msvc": "npm:1.9.3" + "@swc/core-win32-ia32-msvc": "npm:1.9.3" + "@swc/core-win32-x64-msvc": "npm:1.9.3" "@swc/counter": "npm:^0.1.3" - "@swc/types": "npm:^0.1.12" + "@swc/types": "npm:^0.1.17" peerDependencies: "@swc/helpers": "*" dependenciesMeta: @@ -1833,7 +1796,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10c0/e7a5764fe9c476b606b3c68e9bfcb1945a2a1576458176dbd93edd06bd57e3e0837a119c21162904fa2b0c3f5c3a7049d77b52c4f793e0d4f4f345ba959c5456 + checksum: 10c0/a9507a5be580518d51cf7f41821a89e1044be6f72930efbdf3877366c27e9ff1dbca3e1a7f18698679f8c345b6698f43cd80d7dfa24ba30dcab493de9b7a336e languageName: node linkType: hard @@ -1844,12 +1807,12 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.12": - version: 0.1.12 - resolution: "@swc/types@npm:0.1.12" +"@swc/types@npm:^0.1.17": + version: 0.1.17 + resolution: "@swc/types@npm:0.1.17" dependencies: "@swc/counter": "npm:^0.1.3" - checksum: 10c0/f95fea7dee8fc07f8389afbb9578f3d0cd84b429b1d0dbff7fd99b2ef06ed88e96bc33631f36c3bc0505d5a783bee1374acd84b8fc2593001219b6c2caba241b + checksum: 10c0/29f5c8933a16042956f1adb7383e836ed7646cbf679826e78b53fdd0c08e8572cb42152e527b6b530a9bd1052d33d0972f90f589761ccd252c12652c9b7a72fc languageName: node linkType: hard @@ -1919,13 +1882,13 @@ __metadata: linkType: hard "@types/dockerode@npm:^3.3.29": - version: 3.3.31 - resolution: "@types/dockerode@npm:3.3.31" + version: 3.3.32 + resolution: "@types/dockerode@npm:3.3.32" dependencies: "@types/docker-modem": "npm:*" "@types/node": "npm:*" "@types/ssh2": "npm:*" - checksum: 10c0/e0b85edcb7065c24d6d8140b90ea3be128451d4ef25d44fe07ef653e931f3ff4ce86ba0fbb0d7037f51296eb5055d17d8d3ac373028c9e13861b3baf249578d8 + checksum: 10c0/89b45da46ffd0eee36b469a6e61ddcbf101835ee2212ac26b84ee01556c5a99155469e5e345eadb677fd3dd80e7f238d3f265c15b3f99f94f4c53cad9b35023c languageName: node linkType: hard @@ -1969,43 +1932,34 @@ __metadata: linkType: hard "@types/lodash@npm:*": - version: 4.17.7 - resolution: "@types/lodash@npm:4.17.7" - checksum: 10c0/40c965b5ffdcf7ff5c9105307ee08b782da228c01b5c0529122c554c64f6b7168fc8f11dc79aa7bae4e67e17efafaba685dc3a47e294dbf52a65ed2b67100561 + version: 4.17.13 + resolution: "@types/lodash@npm:4.17.13" + checksum: 10c0/c3d0b7efe7933ac0369b99f2f7bff9240d960680fdb74b41ed4bd1b3ca60cca1e31fe4046d9abbde778f941a41bc2a75eb629abf8659fa6c27b66efbbb0802a9 languageName: node linkType: hard -"@types/node@npm:*": - version: 22.5.0 - resolution: "@types/node@npm:22.5.0" +"@types/node@npm:*, @types/node@npm:^22.10.0": + version: 22.10.0 + resolution: "@types/node@npm:22.10.0" dependencies: - undici-types: "npm:~6.19.2" - checksum: 10c0/45aa75c5e71645fac42dced4eff7f197c3fdfff6e8a9fdacd0eb2e748ff21ee70ffb73982f068a58e8d73b2c088a63613142c125236cdcf3c072ea97eada1559 + undici-types: "npm:~6.20.0" + checksum: 10c0/efb3783b6fe74b4300c5bdd4f245f1025887d9b1d0950edae584af58a30d95cc058c10b4b3428f8300e4318468b605240c2ede8fcfb6ead2e0f05bca31e54c1b languageName: node linkType: hard "@types/node@npm:^18.11.18": - version: 18.19.45 - resolution: "@types/node@npm:18.19.45" + version: 18.19.66 + resolution: "@types/node@npm:18.19.66" dependencies: undici-types: "npm:~5.26.4" - checksum: 10c0/79c324176411dcfa92f76b0ffc0673aa4bd8da82d003b44633e927c9493cdc46c35f04c0873b096b23b12bab090a6bbdea21242b3bbb2ea5dc1d9bf72adaa04f - languageName: node - linkType: hard - -"@types/node@npm:^22.10.0": - version: 22.10.0 - resolution: "@types/node@npm:22.10.0" - dependencies: - undici-types: "npm:~6.20.0" - checksum: 10c0/efb3783b6fe74b4300c5bdd4f245f1025887d9b1d0950edae584af58a30d95cc058c10b4b3428f8300e4318468b605240c2ede8fcfb6ead2e0f05bca31e54c1b + checksum: 10c0/d6a31a3f03c4e2c62cdd319ab7f6ff66038d7408158304cfe0a852c9b0247893f5173bafffa0a090bcc279a6015a7c8399a51bf8e44f781edd4d41c43f274edb languageName: node linkType: hard "@types/prop-types@npm:*": - version: 15.7.12 - resolution: "@types/prop-types@npm:15.7.12" - checksum: 10c0/1babcc7db6a1177779f8fde0ccc78d64d459906e6ef69a4ed4dd6339c920c2e05b074ee5a92120fe4e9d9f1a01c952f843ebd550bee2332fc2ef81d1706878f8 + version: 15.7.13 + resolution: "@types/prop-types@npm:15.7.13" + checksum: 10c0/1b20fc67281902c6743379960247bc161f3f0406ffc0df8e7058745a85ea1538612109db0406290512947f9632fe9e10e7337bf0ce6338a91d6c948df16a7c61 languageName: node linkType: hard @@ -2018,17 +1972,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*": - version: 18.3.4 - resolution: "@types/react@npm:18.3.4" - dependencies: - "@types/prop-types": "npm:*" - csstype: "npm:^3.0.2" - checksum: 10c0/5c52e1e6f540cff21e3c2a5212066d02e005f6fb21e4a536a29097fae878db9f407cd7a4b43778f51359349c5f692e08bc77ddb5f5cecbfca9ca4d4e3c91a48e - languageName: node - linkType: hard - -"@types/react@npm:^18.3.12": +"@types/react@npm:*, @types/react@npm:^18.3.12": version: 18.3.12 resolution: "@types/react@npm:18.3.12" dependencies: @@ -2367,11 +2311,11 @@ __metadata: linkType: hard "acorn@npm:^8.9.0": - version: 8.12.1 - resolution: "acorn@npm:8.12.1" + version: 8.14.0 + resolution: "acorn@npm:8.14.0" bin: acorn: bin/acorn - checksum: 10c0/51fb26cd678f914e13287e886da2d7021f8c2bc0ccc95e03d3e0447ee278dd3b40b9c57dc222acd5881adcf26f3edc40901a4953403232129e3876793cd17386 + checksum: 10c0/6d4ee461a7734b2f48836ee0fbb752903606e576cc100eb49340295129ca0b452f3ba91ddd4424a1d4406a98adfb2ebb6bd0ff4c49d7a0930c10e462719bbfd7 languageName: node linkType: hard @@ -2432,18 +2376,9 @@ __metadata: linkType: hard "ansi-regex@npm:^6.0.1": - version: 6.0.1 - resolution: "ansi-regex@npm:6.0.1" - checksum: 10c0/cbe16dbd2c6b2735d1df7976a7070dd277326434f0212f43abf6d87674095d247968209babdaad31bb00882fa68807256ba9be340eec2f1004de14ca75f52a08 - languageName: node - linkType: hard - -"ansi-styles@npm:^3.2.1": - version: 3.2.1 - resolution: "ansi-styles@npm:3.2.1" - dependencies: - color-convert: "npm:^1.9.0" - checksum: 10c0/ece5a8ef069fcc5298f67e3f4771a663129abd174ea2dfa87923a2be2abf6cd367ef72ac87942da00ce85bd1d651d4cd8595aebdb1b385889b89b205860e977b + version: 6.1.0 + resolution: "ansi-regex@npm:6.1.0" + checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc languageName: node linkType: hard @@ -2600,9 +2535,9 @@ __metadata: linkType: hard "b4a@npm:^1.6.4": - version: 1.6.6 - resolution: "b4a@npm:1.6.6" - checksum: 10c0/56f30277666cb511a15829e38d369b114df7dc8cec4cedc09cc5d685bc0f27cb63c7bcfb58e09a19a1b3c4f2541069ab078b5328542e85d74a39620327709a38 + version: 1.6.7 + resolution: "b4a@npm:1.6.7" + checksum: 10c0/ec2f004d1daae04be8c5a1f8aeb7fea213c34025e279db4958eb0b82c1729ee25f7c6e89f92a5f65c8a9cf2d017ce27e3dda912403341d1781bd74528a4849d4 languageName: node linkType: hard @@ -2614,27 +2549,27 @@ __metadata: linkType: hard "bare-events@npm:^2.0.0, bare-events@npm:^2.2.0": - version: 2.4.2 - resolution: "bare-events@npm:2.4.2" - checksum: 10c0/09fa923061f31f815e83504e2ed4a8ba87732a01db40a7fae703dbb7eef7f05d99264b5e186074cbe9698213990d1af564c62cca07a5ff88baea8099ad9a6303 + version: 2.5.0 + resolution: "bare-events@npm:2.5.0" + checksum: 10c0/afbeec4e8be4d93fb4a3be65c3b4a891a2205aae30b5a38fafd42976cc76cf30dad348963fe330a0d70186e15dc507c11af42c89af5dddab2a54e5aff02e2896 languageName: node linkType: hard "bare-fs@npm:^2.1.1": - version: 2.3.1 - resolution: "bare-fs@npm:2.3.1" + version: 2.3.5 + resolution: "bare-fs@npm:2.3.5" dependencies: bare-events: "npm:^2.0.0" bare-path: "npm:^2.0.0" bare-stream: "npm:^2.0.0" - checksum: 10c0/820979ad3dd8693076ba08af842e41b5119fcca63f4324b8f28d96b96050cd260085dffd1169dc644f20746fadb4cf4368b317f2fa2db4e40890921ceb557581 + checksum: 10c0/ff18cc9be7c557c38e0342681ba3672ae4b01e5696b567d4035e5995255dc6bc7d4df88ed210fa4d3eb940eb29512e924ebb42814c87fc59a2bee8cf83b7c2f9 languageName: node linkType: hard "bare-os@npm:^2.1.0": - version: 2.4.0 - resolution: "bare-os@npm:2.4.0" - checksum: 10c0/85615522fd8309d3815d3bef227623f008fac34e037459294a7e24bb2b51ea125597274b8aa7e7038f82de89c15e2148fef299eece40ec3ea33797a357c4f2bb + version: 2.4.4 + resolution: "bare-os@npm:2.4.4" + checksum: 10c0/e7d1a7b2100c05da8d25b60d0d48cf850c6f57064577a3f2f51cf18d417fbcfd6967ed2d8314320914ed69e0f2ebcf54eb1b36092dd172d8e8f969cf8cccf041 languageName: node linkType: hard @@ -2648,11 +2583,11 @@ __metadata: linkType: hard "bare-stream@npm:^2.0.0": - version: 2.1.3 - resolution: "bare-stream@npm:2.1.3" + version: 2.4.2 + resolution: "bare-stream@npm:2.4.2" dependencies: - streamx: "npm:^2.18.0" - checksum: 10c0/8703b1d80318496ea560483943d5f425a160ded8d3d75659571842caf5f374f52668809bc1e39b032af14df7210973995efaf273f8c35986bef697380ef4674a + streamx: "npm:^2.20.0" + checksum: 10c0/5e64d96dc32d901c317399f14fd1057882b2bd68d1f8ab54710f0e640b0d1f3a4bf4f9c238bb4c81051ef4b687cf2223e5e05dda9f6ce08bc0cc2ac98f3b52e0 languageName: node linkType: hard @@ -2718,21 +2653,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.23.3": - version: 4.23.3 - resolution: "browserslist@npm:4.23.3" - dependencies: - caniuse-lite: "npm:^1.0.30001646" - electron-to-chromium: "npm:^1.5.4" - node-releases: "npm:^2.0.18" - update-browserslist-db: "npm:^1.1.0" - bin: - browserslist: cli.js - checksum: 10c0/3063bfdf812815346447f4796c8f04601bf5d62003374305fd323c2a463e42776475bcc5309264e39bcf9a8605851e53560695991a623be988138b3ff8c66642 - languageName: node - linkType: hard - -"browserslist@npm:^4.24.0": +"browserslist@npm:^4.23.3, browserslist@npm:^4.24.0": version: 4.24.2 resolution: "browserslist@npm:4.24.2" dependencies: @@ -2780,6 +2701,17 @@ __metadata: languageName: node linkType: hard +"bundle-require@npm:^5.0.0": + version: 5.0.0 + resolution: "bundle-require@npm:5.0.0" + dependencies: + load-tsconfig: "npm:^0.2.3" + peerDependencies: + esbuild: ">=0.18" + checksum: 10c0/92c46df02586e0ebd66ee4831c9b5775adb3c32a43fe2b2aaf7bc675135c141f751de6a9a26b146d64c607c5b40f9eef5f10dce3c364f602d4bed268444c32c6 + languageName: node + linkType: hard + "byline@npm:^5.0.0": version: 5.0.0 resolution: "byline@npm:5.0.0" @@ -2828,20 +2760,20 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001646": - version: 1.0.30001651 - resolution: "caniuse-lite@npm:1.0.30001651" - checksum: 10c0/7821278952a6dbd17358e5d08083d258f092e2a530f5bc1840657cb140fbbc5ec44293bc888258c44a18a9570cde149ed05819ac8320b9710cf22f699891e6ad - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001669": +"caniuse-lite@npm:^1.0.30001646, caniuse-lite@npm:^1.0.30001669": version: 1.0.30001684 resolution: "caniuse-lite@npm:1.0.30001684" checksum: 10c0/446485ca3d9caf408a339a44636a86a2b119ec247492393ae661cd93dccd6668401dd2dfec1e149be4e44563cd1e23351b44453a52fa2c2f19e2bf3287c865f6 languageName: node linkType: hard +"case-anything@npm:^2.1.13": + version: 2.1.13 + resolution: "case-anything@npm:2.1.13" + checksum: 10c0/b02ffa51d7d58b9a32df7b40973836e16afad131eae7d343e64cb3ca7be57a936bf3d6c9d57a7aa242cf2f545d9a33990b755e93bcac2517761d77773a4a6a30 + languageName: node + linkType: hard + "chai@npm:^5.1.2": version: 5.1.2 resolution: "chai@npm:5.1.2" @@ -2855,17 +2787,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^2.4.2": - version: 2.4.2 - resolution: "chalk@npm:2.4.2" - dependencies: - ansi-styles: "npm:^3.2.1" - escape-string-regexp: "npm:^1.0.5" - supports-color: "npm:^5.3.0" - checksum: 10c0/e6543f02ec877732e3a2d1c3c3323ddb4d39fbab687c23f526e25bd4c6a9bf3b83a696e8c769d078e04e5754921648f7821b2a2acfd16c550435fd630026e073 - languageName: node - linkType: hard - "chalk@npm:^4.0.0, chalk@npm:^4.1.1": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -2909,6 +2830,15 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^4.0.1": + version: 4.0.1 + resolution: "chokidar@npm:4.0.1" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/4bb7a3adc304059810bb6c420c43261a15bb44f610d77c35547addc84faa0374265c3adc67f25d06f363d9a4571962b02679268c40de07676d260de1986efea9 + languageName: node + linkType: hard + "chownr@npm:^1.1.1": version: 1.1.4 resolution: "chownr@npm:1.1.4" @@ -2924,11 +2854,11 @@ __metadata: linkType: hard "class-variance-authority@npm:^0.7.0": - version: 0.7.0 - resolution: "class-variance-authority@npm:0.7.0" + version: 0.7.1 + resolution: "class-variance-authority@npm:0.7.1" dependencies: - clsx: "npm:2.0.0" - checksum: 10c0/e11c57edf4bf50ef1c97bae41d68885afbaaedba26c48b7cc5dfb033390fed7012147e9532168d8c4f3497fce4dff15e20e6e60b8c9c9a4b0fe26b0e804513db + clsx: "npm:^2.1.1" + checksum: 10c0/0f438cea22131808b99272de0fa933c2532d5659773bfec0c583de7b3f038378996d3350683426b8e9c74a6286699382106d71fbec52f0dd5fbb191792cccb5b languageName: node linkType: hard @@ -2958,13 +2888,6 @@ __metadata: languageName: node linkType: hard -"clsx@npm:2.0.0": - version: 2.0.0 - resolution: "clsx@npm:2.0.0" - checksum: 10c0/c09f43b3144a0b7826b6b11b6a111b2c7440831004eecc02d333533c5e58ef0aa5f2dce071d3b25fbb8c8ea97b45df96c74bcc1d51c8c2027eb981931107b0cd - languageName: node - linkType: hard - "clsx@npm:^2.1.1": version: 2.1.1 resolution: "clsx@npm:2.1.1" @@ -2972,15 +2895,6 @@ __metadata: languageName: node linkType: hard -"color-convert@npm:^1.9.0": - version: 1.9.3 - resolution: "color-convert@npm:1.9.3" - dependencies: - color-name: "npm:1.1.3" - checksum: 10c0/5ad3c534949a8c68fca8fbc6f09068f435f0ad290ab8b2f76841b9e6af7e0bb57b98cb05b0e19fe33f5d91e5a8611ad457e5f69e0a484caad1f7487fd0e8253c - languageName: node - linkType: hard - "color-convert@npm:^2.0.1": version: 2.0.1 resolution: "color-convert@npm:2.0.1" @@ -2990,13 +2904,6 @@ __metadata: languageName: node linkType: hard -"color-name@npm:1.1.3": - version: 1.1.3 - resolution: "color-name@npm:1.1.3" - checksum: 10c0/566a3d42cca25b9b3cd5528cd7754b8e89c0eb646b7f214e8e2eaddb69994ac5f0557d9c175eb5d8f0ad73531140d9c47525085ee752a91a2ab15ab459caf6d6 - languageName: node - linkType: hard - "color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" @@ -3061,6 +2968,13 @@ __metadata: languageName: node linkType: hard +"consola@npm:^3.2.3": + version: 3.2.3 + resolution: "consola@npm:3.2.3" + checksum: 10c0/c606220524ec88a05bb1baf557e9e0e04a0c08a9c35d7a08652d99de195c4ddcb6572040a7df57a18ff38bbc13ce9880ad032d56630cef27bef72768ef0ac078 + languageName: node + linkType: hard + "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -3075,7 +2989,7 @@ __metadata: languageName: node linkType: hard -"cpu-features@npm:~0.0.9": +"cpu-features@npm:~0.0.10": version: 0.0.10 resolution: "cpu-features@npm:0.0.10" dependencies: @@ -3206,6 +3120,15 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^1.0.3": + version: 1.0.3 + resolution: "detect-libc@npm:1.0.3" + bin: + detect-libc: ./bin/detect-libc.js + checksum: 10c0/4da0deae9f69e13bc37a0902d78bf7169480004b1fed3c19722d56cff578d16f0e11633b7fbf5fb6249181236c72e90024cbd68f0b9558ae06e281f47326d50d + languageName: node + linkType: hard + "detect-node-es@npm:^1.1.0": version: 1.1.0 resolution: "detect-node-es@npm:1.1.0" @@ -3268,6 +3191,15 @@ __metadata: languageName: node linkType: hard +"dprint-node@npm:^1.0.8": + version: 1.0.8 + resolution: "dprint-node@npm:1.0.8" + dependencies: + detect-libc: "npm:^1.0.3" + checksum: 10c0/39c1f8511833226cde773129afc5862dfd05babe062375e6b1f5824e221a5743a4d9c48626f32f7c2080113566270fe80521a50acb9029a20a2e80a3cd5e4106 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -3275,13 +3207,6 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.5.4": - version: 1.5.13 - resolution: "electron-to-chromium@npm:1.5.13" - checksum: 10c0/1d88ac39447e1d718c4296f92fe89836df4688daf2d362d6c49108136795f05a56dd9c950f1c6715e0395fa037c3b5f5ea686c543fdc90e6d74a005877c45022 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.5.41": version: 1.5.65 resolution: "electron-to-chromium@npm:1.5.65" @@ -3290,9 +3215,9 @@ __metadata: linkType: hard "emoji-regex@npm:^10.3.0": - version: 10.3.0 - resolution: "emoji-regex@npm:10.3.0" - checksum: 10c0/b4838e8dcdceb44cf47f59abe352c25ff4fe7857acaf5fb51097c427f6f75b44d052eb907a7a3b86f86bc4eae3a93f5c2b7460abe79c407307e6212d65c91163 + version: 10.4.0 + resolution: "emoji-regex@npm:10.4.0" + checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d languageName: node linkType: hard @@ -3446,13 +3371,6 @@ __metadata: languageName: node linkType: hard -"escalade@npm:^3.1.2": - version: 3.1.2 - resolution: "escalade@npm:3.1.2" - checksum: 10c0/6b4adafecd0682f3aa1cd1106b8fff30e492c7015b178bc81b2d2f75106dabea6c6d6e8508fc491bd58e597c74abb0e8e2368f943ecb9393d4162e3c2f3cf287 - languageName: node - linkType: hard - "escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -3460,13 +3378,6 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:^1.0.5": - version: 1.0.5 - resolution: "escape-string-regexp@npm:1.0.5" - checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371 - languageName: node - linkType: hard - "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -3513,7 +3424,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": +"eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 @@ -3753,6 +3664,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.4.2": + version: 6.4.2 + resolution: "fdir@npm:6.4.2" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/34829886f34a3ca4170eca7c7180ec4de51a3abb4d380344063c0ae2e289b11d2ba8b724afee974598c83027fea363ff598caf2b51bc4e6b1e0d8b80cc530573 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -3785,6 +3708,7 @@ __metadata: version: 0.0.0-use.local resolution: "fishjam-web-sdk@workspace:." dependencies: + "@fishjam-cloud/protobufs": "workspace:^" "@typescript-eslint/eslint-plugin": "npm:^8.16.0" "@typescript-eslint/parser": "npm:^8.16.0" eslint: "npm:^8.57.1" @@ -3811,9 +3735,9 @@ __metadata: linkType: hard "flatted@npm:^3.2.9": - version: 3.3.1 - resolution: "flatted@npm:3.3.1" - checksum: 10c0/324166b125ee07d4ca9bcf3a5f98d915d5db4f39d711fba640a3178b959919aae1f7cfd8aabcfef5826ed8aa8a2aa14cc85b2d7d18ff638ddf4ae3df39573eaf + version: 3.3.2 + resolution: "flatted@npm:3.3.2" + checksum: 10c0/24cc735e74d593b6c767fe04f2ef369abe15b62f6906158079b9874bdb3ee5ae7110bb75042e70cd3f99d409d766f357caf78d5ecee9780206f5fdc5edbad334 languageName: node linkType: hard @@ -3951,16 +3875,9 @@ __metadata: linkType: hard "get-east-asian-width@npm:^1.0.0": - version: 1.2.0 - resolution: "get-east-asian-width@npm:1.2.0" - checksum: 10c0/914b1e217cf38436c24b4c60b4c45289e39a45bf9e65ef9fd343c2815a1a02b8a0215aeec8bf9c07c516089004b6e3826332481f40a09529fcadbf6e579f286b - languageName: node - linkType: hard - -"get-func-name@npm:^2.0.1": - version: 2.0.2 - resolution: "get-func-name@npm:2.0.2" - checksum: 10c0/89830fd07623fa73429a711b9daecdb304386d237c71268007f788f113505ef1d4cc2d0b9680e072c5082490aec9df5d7758bf5ac6f1c37062855e8e3dc0b9df + version: 1.3.0 + resolution: "get-east-asian-width@npm:1.3.0" + checksum: 10c0/1a049ba697e0f9a4d5514c4623781c5246982bdb61082da6b5ae6c33d838e52ce6726407df285cdbb27ec1908b333cf2820989bd3e986e37bb20979437fdf34b languageName: node linkType: hard @@ -4063,13 +3980,6 @@ __metadata: languageName: node linkType: hard -"has-flag@npm:^3.0.0": - version: 3.0.0 - resolution: "has-flag@npm:3.0.0" - checksum: 10c0/1c6c83b14b8b1b3c25b0727b8ba3e3b647f99e9e6e13eb7322107261de07a4c1be56fc0d45678fc376e09772a3a1642ccdaf8fc69bdf123b6c086598397ce473 - languageName: node - linkType: hard - "has-flag@npm:^4.0.0": version: 4.0.0 resolution: "has-flag@npm:4.0.0" @@ -4408,6 +4318,13 @@ __metadata: languageName: node linkType: hard +"joycon@npm:^3.1.1": + version: 3.1.1 + resolution: "joycon@npm:3.1.1" + checksum: 10c0/131fb1e98c9065d067fd49b6e685487ac4ad4d254191d7aa2c9e3b90f4e9ca70430c43cad001602bdbdabcf58717d3b5c5b7461c1bd8e39478c8de706b3fe6ae + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -4520,7 +4437,7 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:^3.0.0, lilconfig@npm:~3.1.2": +"lilconfig@npm:^3.0.0, lilconfig@npm:^3.1.1, lilconfig@npm:~3.1.2": version: 3.1.2 resolution: "lilconfig@npm:3.1.2" checksum: 10c0/f059630b1a9bddaeba83059db00c672b64dc14074e9f232adce32b38ca1b5686ab737eb665c5ba3c32f147f0002b4bee7311ad0386a9b98547b5623e87071fbe @@ -4564,8 +4481,8 @@ __metadata: linkType: hard "listr2@npm:~8.2.4": - version: 8.2.4 - resolution: "listr2@npm:8.2.4" + version: 8.2.5 + resolution: "listr2@npm:8.2.5" dependencies: cli-truncate: "npm:^4.0.0" colorette: "npm:^2.0.20" @@ -4573,7 +4490,14 @@ __metadata: log-update: "npm:^6.1.0" rfdc: "npm:^1.4.1" wrap-ansi: "npm:^9.0.0" - checksum: 10c0/df5b129e9767de1997973cec6103cd4bd6fc3b3367685b7c23048d12b61d5b7e44fecd8a3d3534c0e1c963bd5ac43ca501d14712f46fa101050037be323a5c16 + checksum: 10c0/f5a9599514b00c27d7eb32d1117c83c61394b2a985ec20e542c798bf91cf42b19340215701522736f5b7b42f557e544afeadec47866e35e5d4f268f552729671 + languageName: node + linkType: hard + +"load-tsconfig@npm:^0.2.3": + version: 0.2.5 + resolution: "load-tsconfig@npm:0.2.5" + checksum: 10c0/bf2823dd26389d3497b6567f07435c5a7a58d9df82e879b0b3892f87d8db26900f84c85bc329ef41c0540c0d6a448d1c23ddc64a80f3ff6838b940f3915a3fcb languageName: node linkType: hard @@ -4614,6 +4538,13 @@ __metadata: languageName: node linkType: hard +"lodash.sortby@npm:^4.7.0": + version: 4.7.0 + resolution: "lodash.sortby@npm:4.7.0" + checksum: 10c0/fc48fb54ff7669f33bb32997cab9460757ee99fafaf72400b261c3e10fde21538e47d8cfcbe6a25a31bcb5b7b727c27d52626386fc2de24eb059a6d64a89cdf5 + languageName: node + linkType: hard + "lodash@npm:^4.17.15": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -4645,16 +4576,7 @@ __metadata: languageName: node linkType: hard -"loupe@npm:^3.1.0": - version: 3.1.1 - resolution: "loupe@npm:3.1.1" - dependencies: - get-func-name: "npm:^2.0.1" - checksum: 10c0/99f88badc47e894016df0c403de846fedfea61154aadabbf776c8428dd59e8d8378007135d385d737de32ae47980af07d22ba7bec5ef7beebd721de9baa0a0af - languageName: node - linkType: hard - -"loupe@npm:^3.1.2": +"loupe@npm:^3.1.0, loupe@npm:^3.1.2": version: 3.1.2 resolution: "loupe@npm:3.1.2" checksum: 10c0/b13c02e3ddd6a9d5f8bf84133b3242de556512d824dddeea71cce2dbd6579c8f4d672381c4e742d45cf4423d0701765b4a6e5fbc24701def16bc2b40f8daa96a @@ -4694,11 +4616,11 @@ __metadata: linkType: hard "magic-string@npm:^0.30.12": - version: 0.30.12 - resolution: "magic-string@npm:0.30.12" + version: 0.30.14 + resolution: "magic-string@npm:0.30.14" dependencies: "@jridgewell/sourcemap-codec": "npm:^1.5.0" - checksum: 10c0/469f457d18af37dfcca8617086ea8a65bcd8b60ba8a1182cb024ce43e470ace3c9d1cb6bee58d3b311768fb16bc27bd50bdeebcaa63dadd0fd46cac4d2e11d5f + checksum: 10c0/c52c2a6e699dfa8a840e13154da35464a40cd8b07049b695a8b282883b0426c0811af1e36ac26860b4267289340b42772c156a5608e87be97b63d510e617e87a languageName: node linkType: hard @@ -4971,21 +4893,21 @@ __metadata: languageName: node linkType: hard -"nan@npm:^2.18.0, nan@npm:^2.19.0": - version: 2.20.0 - resolution: "nan@npm:2.20.0" +"nan@npm:^2.19.0, nan@npm:^2.20.0": + version: 2.22.0 + resolution: "nan@npm:2.22.0" dependencies: node-gyp: "npm:latest" - checksum: 10c0/75775309a21ad179a55250d62ce47322c33ca03d8ddb5ad4c555bd820dd72484b3c59253dd9f41cc68dd63453ef04017407fbd081a549bc030d977079bb798b7 + checksum: 10c0/d5d31aefdb218deba308d44867c5f432b4d3aabeb57c70a2b236d62652e9fee7044e5d5afd380d9fef022fe7ebb2f2d6c85ca3cbcac5031aaca3592c844526bb languageName: node linkType: hard "nanoid@npm:^3.3.7": - version: 3.3.7 - resolution: "nanoid@npm:3.3.7" + version: 3.3.8 + resolution: "nanoid@npm:3.3.8" bin: nanoid: bin/nanoid.cjs - checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 + checksum: 10c0/4b1bb29f6cfebf3be3bc4ad1f1296fb0a10a3043a79f34fbffe75d1621b4318319211cd420549459018ea3592f0d2f159247a6f874911d6d26eaaadda2478120 languageName: node linkType: hard @@ -4997,9 +4919,9 @@ __metadata: linkType: hard "negotiator@npm:^0.6.3": - version: 0.6.3 - resolution: "negotiator@npm:0.6.3" - checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2 + version: 0.6.4 + resolution: "negotiator@npm:0.6.4" + checksum: 10c0/3e677139c7fb7628a6f36335bf11a885a62c21d5390204590a1a214a5631fcbe5ea74ef6a610b60afe84b4d975cbe0566a23f20ee17c77c73e74b80032108dea languageName: node linkType: hard @@ -5156,9 +5078,9 @@ __metadata: linkType: hard "package-json-from-dist@npm:^1.0.0": - version: 1.0.0 - resolution: "package-json-from-dist@npm:1.0.0" - checksum: 10c0/e3ffaf6ac1040ab6082a658230c041ad14e72fabe99076a2081bb1d5d41210f11872403fc09082daf4387fc0baa6577f96c9c0e94c90c394fd57794b66aa4033 + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b languageName: node linkType: hard @@ -5230,14 +5152,7 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1, picocolors@npm:^1.0.0, picocolors@npm:^1.0.1": - version: 1.0.1 - resolution: "picocolors@npm:1.0.1" - checksum: 10c0/c63cdad2bf812ef0d66c8db29583802355d4ca67b9285d846f390cc15c2f6ccb94e8cb7eb6a6e97fc5990a6d3ad4ae42d86c84d3146e667c739a4234ed50d400 - languageName: node - linkType: hard - -"picocolors@npm:^1.1.0, picocolors@npm:^1.1.1": +"picocolors@npm:^1, picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.0, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 @@ -5251,6 +5166,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.2": + version: 4.0.2 + resolution: "picomatch@npm:4.0.2" + checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc + languageName: node + linkType: hard + "pidtree@npm:~0.6.0": version: 0.6.0 resolution: "pidtree@npm:0.6.0" @@ -5340,6 +5262,29 @@ __metadata: languageName: node linkType: hard +"postcss-load-config@npm:^6.0.1": + version: 6.0.1 + resolution: "postcss-load-config@npm:6.0.1" + dependencies: + lilconfig: "npm:^3.1.1" + peerDependencies: + jiti: ">=1.21.0" + postcss: ">=8.0.9" + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + checksum: 10c0/74173a58816dac84e44853f7afbd283f4ef13ca0b6baeba27701214beec33f9e309b128f8102e2b173e8d45ecba45d279a9be94b46bf48d219626aa9b5730848 + languageName: node + linkType: hard + "postcss-nested@npm:^6.2.0": version: 6.2.0 resolution: "postcss-nested@npm:6.2.0" @@ -5522,12 +5467,12 @@ __metadata: linkType: hard "pump@npm:^3.0.0": - version: 3.0.0 - resolution: "pump@npm:3.0.0" + version: 3.0.2 + resolution: "pump@npm:3.0.2" dependencies: end-of-stream: "npm:^1.1.0" once: "npm:^1.3.1" - checksum: 10c0/bbdeda4f747cdf47db97428f3a135728669e56a0ae5f354a9ac5b74556556f5446a46f720a8f14ca2ece5be9b4d5d23c346db02b555f46739934cc6c093a5478 + checksum: 10c0/5ad655cb2a7738b4bcf6406b24ad0970d680649d996b55ad20d1be8e0c02394034e4c45ff7cd105d87f1e9b96a0e3d06fd28e11fae8875da26e7f7a8e2c9726f languageName: node linkType: hard @@ -5705,6 +5650,13 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^4.0.1": + version: 4.0.2 + resolution: "readdirp@npm:4.0.2" + checksum: 10c0/a16ecd8ef3286dcd90648c3b103e3826db2b766cdb4a988752c43a83f683d01c7059158d623cbcd8bdfb39e65d302d285be2d208e7d9f34d022d912b929217dd + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -5721,6 +5673,13 @@ __metadata: languageName: node linkType: hard +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + "resolve@npm:^1.1.7, resolve@npm:^1.22.8": version: 1.22.8 resolution: "resolve@npm:1.22.8" @@ -5789,7 +5748,7 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^4.23.0": +"rollup@npm:^4.23.0, rollup@npm:^4.24.0": version: 4.27.4 resolution: "rollup@npm:4.27.4" dependencies: @@ -6009,20 +5968,22 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.2.0": - version: 1.2.0 - resolution: "source-map-js@npm:1.2.0" - checksum: 10c0/7e5f896ac10a3a50fe2898e5009c58ff0dc102dcb056ed27a354623a0ece8954d4b2649e1a1b2b52ef2e161d26f8859c7710350930751640e71e374fe2d321a4 - languageName: node - linkType: hard - -"source-map-js@npm:^1.2.1": +"source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf languageName: node linkType: hard +"source-map@npm:0.8.0-beta.0": + version: 0.8.0-beta.0 + resolution: "source-map@npm:0.8.0-beta.0" + dependencies: + whatwg-url: "npm:^7.0.0" + checksum: 10c0/fb4d9bde9a9fdb2c29b10e5eae6c71d10e09ef467e1afb75fdec2eb7e11fa5b343a2af553f74f18b695dbc0b81f9da2e9fa3d7a317d5985e9939499ec6087835 + languageName: node + linkType: hard + "split-ca@npm:^1.0.1": version: 1.0.1 resolution: "split-ca@npm:1.0.1" @@ -6048,19 +6009,19 @@ __metadata: linkType: hard "ssh2@npm:^1.11.0, ssh2@npm:^1.4.0": - version: 1.15.0 - resolution: "ssh2@npm:1.15.0" + version: 1.16.0 + resolution: "ssh2@npm:1.16.0" dependencies: asn1: "npm:^0.2.6" bcrypt-pbkdf: "npm:^1.0.2" - cpu-features: "npm:~0.0.9" - nan: "npm:^2.18.0" + cpu-features: "npm:~0.0.10" + nan: "npm:^2.20.0" dependenciesMeta: cpu-features: optional: true nan: optional: true - checksum: 10c0/7c76888fbfa1c15660cf51086a6e5699b3c1caad516e29adb1d2a00fc1ef6b48946ca7ec811b4bb50456984967c4346115c7ddd3dbf981a1193bd1f40fa4529a + checksum: 10c0/d336a85d87501c64ba230b6c1a2901a9b0e376fe7f7a1640a7f8dbdafe674b2e1a5dc6236ffd1329969dc0cf03cd57759b28743075e61229a984065ee1d56bed languageName: node linkType: hard @@ -6087,9 +6048,9 @@ __metadata: languageName: node linkType: hard -"streamx@npm:^2.15.0, streamx@npm:^2.18.0": - version: 2.19.0 - resolution: "streamx@npm:2.19.0" +"streamx@npm:^2.15.0, streamx@npm:^2.20.0": + version: 2.20.2 + resolution: "streamx@npm:2.20.2" dependencies: bare-events: "npm:^2.2.0" fast-fifo: "npm:^1.3.2" @@ -6098,7 +6059,7 @@ __metadata: dependenciesMeta: bare-events: optional: true - checksum: 10c0/5833a2c1226488a015e8efde08c6cd4983d7d20098b2210d09594b23f598a8b028c083d542621e2e91ddcb33a266233c3524c60152203be40f1dd816b9ede9da + checksum: 10c0/2ad68b9426e0211c1198b5b5dd7280088793c6792e1f8e2a8fbd2487d483f35ee13b0b46edfa247daad2132d6b0abc21af4eaa4a4c099ff4cd11fcff83e6ce3e languageName: node linkType: hard @@ -6210,15 +6171,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^5.3.0": - version: 5.5.0 - resolution: "supports-color@npm:5.5.0" - dependencies: - has-flag: "npm:^3.0.0" - checksum: 10c0/6ae5ff319bfbb021f8a86da8ea1f8db52fac8bd4d499492e30ec17095b58af11f0c55f8577390a749b1c4dde691b6a0315dab78f5f54c9b3d83f8fb5905c1c05 - languageName: node - linkType: hard - "supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" @@ -6386,11 +6338,9 @@ __metadata: linkType: hard "text-decoder@npm:^1.1.0": - version: 1.1.1 - resolution: "text-decoder@npm:1.1.1" - dependencies: - b4a: "npm:^1.6.4" - checksum: 10c0/e527d05454b59c0fa77456495de68c88e560a122de3dd28b3ebdbf81828aabeaa7e9bb8054b9eb52bc5029ccb5899ad04f466cbba3c53b2685270599d1710cee + version: 1.2.1 + resolution: "text-decoder@npm:1.2.1" + checksum: 10c0/deea9e3f4bde3b8990439e59cd52b2e917a416e29fbaf607052c89117c7148f1831562c099e9dd49abea0839cffdeb75a3c8f1f137f1686afd2808322f8e3f00 languageName: node linkType: hard @@ -6440,10 +6390,20 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.9": + version: 0.2.10 + resolution: "tinyglobby@npm:0.2.10" + dependencies: + fdir: "npm:^6.4.2" + picomatch: "npm:^4.0.2" + checksum: 10c0/ce946135d39b8c0e394e488ad59f4092e8c4ecd675ef1bcd4585c47de1b325e61ec6adfbfbe20c3c2bfa6fd674c5b06de2a2e65c433f752ae170aff11793e5ef + languageName: node + linkType: hard + "tinypool@npm:^1.0.1": - version: 1.0.1 - resolution: "tinypool@npm:1.0.1" - checksum: 10c0/90939d6a03f1519c61007bf416632dc1f0b9c1a9dd673c179ccd9e36a408437384f984fc86555a5d040d45b595abc299c3bb39d354439e98a090766b5952e73d + version: 1.0.2 + resolution: "tinypool@npm:1.0.2" + checksum: 10c0/31ac184c0ff1cf9a074741254fe9ea6de95026749eb2b8ec6fd2b9d8ca94abdccda731f8e102e7f32e72ed3b36d32c6975fd5f5523df3f1b6de6c3d8dfd95e63 languageName: node linkType: hard @@ -6468,13 +6428,6 @@ __metadata: languageName: node linkType: hard -"to-fast-properties@npm:^2.0.0": - version: 2.0.0 - resolution: "to-fast-properties@npm:2.0.0" - checksum: 10c0/b214d21dbfb4bce3452b6244b336806ffea9c05297148d32ebb428d5c43ce7545bdfc65a1ceb58c9ef4376a65c0cb2854d645f33961658b3e3b4f84910ddcdd7 - languageName: node - linkType: hard - "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -6484,12 +6437,30 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^1.0.1": + version: 1.0.1 + resolution: "tr46@npm:1.0.1" + dependencies: + punycode: "npm:^2.1.0" + checksum: 10c0/41525c2ccce86e3ef30af6fa5e1464e6d8bb4286a58ea8db09228f598889581ef62347153f6636cd41553dc41685bdfad0a9d032ef58df9fbb0792b3447d0f04 + languageName: node + linkType: hard + +"tree-kill@npm:^1.2.2": + version: 1.2.2 + resolution: "tree-kill@npm:1.2.2" + bin: + tree-kill: cli.js + checksum: 10c0/7b1b7c7f17608a8f8d20a162e7957ac1ef6cd1636db1aba92f4e072dc31818c2ff0efac1e3d91064ede67ed5dc57c565420531a8134090a12ac10cf792ab14d2 + languageName: node + linkType: hard + "ts-api-utils@npm:^1.3.0": - version: 1.3.0 - resolution: "ts-api-utils@npm:1.3.0" + version: 1.4.2 + resolution: "ts-api-utils@npm:1.4.2" peerDependencies: typescript: ">=4.2.0" - checksum: 10c0/f54a0ba9ed56ce66baea90a3fa087a484002e807f28a8ccb2d070c75e76bde64bd0f6dce98b3802834156306050871b67eec325cb4e918015a360a3f0868c77c + checksum: 10c0/b9d82922af42cefa14650397f5ff42a1ff8c8a1b4fac3590fa3e2daeeb3666fbe260a324f55dc748d9653dce30c2a21a148fba928511b2022bedda66423695bf languageName: node linkType: hard @@ -6500,10 +6471,42 @@ __metadata: languageName: node linkType: hard +"ts-poet@npm:^6.7.0": + version: 6.9.0 + resolution: "ts-poet@npm:6.9.0" + dependencies: + dprint-node: "npm:^1.0.8" + checksum: 10c0/c1e3ee6048f075407c2076c8b884b405a3402ae6c013c0af9d0f6b8a6d939c71751a954882d2bd33a606c013d4a57519f5305e0bb1fd6aa0d23edda93e6007bf + languageName: node + linkType: hard + +"ts-proto-descriptors@npm:2.0.0": + version: 2.0.0 + resolution: "ts-proto-descriptors@npm:2.0.0" + dependencies: + "@bufbuild/protobuf": "npm:^2.0.0" + checksum: 10c0/a4f47a6db7de6b328a5b22bb0bed2a0dac929c28002567613db7980e0a8392b9148530e432a602a8c6b2cfda9909be9568a2e5c545f5624bb5d5eba52031c059 + languageName: node + linkType: hard + +"ts-proto@npm:^2.2.7": + version: 2.4.0 + resolution: "ts-proto@npm:2.4.0" + dependencies: + "@bufbuild/protobuf": "npm:^2.0.0" + case-anything: "npm:^2.1.13" + ts-poet: "npm:^6.7.0" + ts-proto-descriptors: "npm:2.0.0" + bin: + protoc-gen-ts_proto: protoc-gen-ts_proto + checksum: 10c0/59cd37d33c2d1afa8b1fe009dfdd736c55506dc8841b8dca7d162ddd2fe359679f0dcc75bd887f2511dde06cd3068794ae1c8d95afd7bcd67ecc1f6493945b9e + languageName: node + linkType: hard + "tslib@npm:^2.0.0": - version: 2.7.0 - resolution: "tslib@npm:2.7.0" - checksum: 10c0/469e1d5bf1af585742128827000711efa61010b699cb040ab1800bcd3ccdd37f63ec30642c9e07c4439c1db6e46345582614275daca3e0f4abae29b0083f04a6 + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 languageName: node linkType: hard @@ -6514,6 +6517,47 @@ __metadata: languageName: node linkType: hard +"tsup@npm:^8.3.5": + version: 8.3.5 + resolution: "tsup@npm:8.3.5" + dependencies: + bundle-require: "npm:^5.0.0" + cac: "npm:^6.7.14" + chokidar: "npm:^4.0.1" + consola: "npm:^3.2.3" + debug: "npm:^4.3.7" + esbuild: "npm:^0.24.0" + joycon: "npm:^3.1.1" + picocolors: "npm:^1.1.1" + postcss-load-config: "npm:^6.0.1" + resolve-from: "npm:^5.0.0" + rollup: "npm:^4.24.0" + source-map: "npm:0.8.0-beta.0" + sucrase: "npm:^3.35.0" + tinyexec: "npm:^0.3.1" + tinyglobby: "npm:^0.2.9" + tree-kill: "npm:^1.2.2" + peerDependencies: + "@microsoft/api-extractor": ^7.36.0 + "@swc/core": ^1 + postcss: ^8.4.12 + typescript: ">=4.5.0" + peerDependenciesMeta: + "@microsoft/api-extractor": + optional: true + "@swc/core": + optional: true + postcss: + optional: true + typescript: + optional: true + bin: + tsup: dist/cli-default.js + tsup-node: dist/cli-node.js + checksum: 10c0/7794953cbc784b7c8f14c4898d36a293b815b528d3098c2416aeaa2b4775dc477132cd12f75f6d32737dfd15ba10139c73f7039045352f2ba1ea7e5fe6fe3773 + languageName: node + linkType: hard + "tweetnacl@npm:^0.14.3": version: 0.14.5 resolution: "tweetnacl@npm:0.14.5" @@ -6637,13 +6681,6 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.19.2": - version: 6.19.8 - resolution: "undici-types@npm:6.19.8" - checksum: 10c0/078afa5990fba110f6824823ace86073b4638f1d5112ee26e790155f481f2a868cc3e0615505b6f4282bdf74a3d8caad715fd809e870c2bb0704e3ea6082f344 - languageName: node - linkType: hard - "undici-types@npm:~6.20.0": version: 6.20.0 resolution: "undici-types@npm:6.20.0" @@ -6685,20 +6722,6 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.1.0": - version: 1.1.0 - resolution: "update-browserslist-db@npm:1.1.0" - dependencies: - escalade: "npm:^3.1.2" - picocolors: "npm:^1.0.1" - peerDependencies: - browserslist: ">= 4.21.0" - bin: - update-browserslist-db: cli.js - checksum: 10c0/a7452de47785842736fb71547651c5bbe5b4dc1e3722ccf48a704b7b34e4dcf633991eaa8e4a6a517ffb738b3252eede3773bef673ef9021baa26b056d63a5b9 - languageName: node - linkType: hard - "update-browserslist-db@npm:^1.1.1": version: 1.1.1 resolution: "update-browserslist-db@npm:1.1.1" @@ -7007,6 +7030,24 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^4.0.2": + version: 4.0.2 + resolution: "webidl-conversions@npm:4.0.2" + checksum: 10c0/def5c5ac3479286dffcb604547628b2e6b46c5c5b8a8cfaa8c71dc3bafc85859bde5fbe89467ff861f571ab38987cf6ab3d6e7c80b39b999e50e803c12f3164f + languageName: node + linkType: hard + +"whatwg-url@npm:^7.0.0": + version: 7.1.0 + resolution: "whatwg-url@npm:7.1.0" + dependencies: + lodash.sortby: "npm:^4.7.0" + tr46: "npm:^1.0.1" + webidl-conversions: "npm:^4.0.2" + checksum: 10c0/2785fe4647690e5a0225a79509ba5e21fdf4a71f9de3eabdba1192483fe006fc79961198e0b99f82751557309f17fc5a07d4d83c251aa5b2f85ba71e674cbee9 + languageName: node + linkType: hard + "which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -7102,7 +7143,7 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.2.2, yaml@npm:^2.3.4, yaml@npm:~2.5.0": +"yaml@npm:^2.2.2, yaml@npm:^2.3.4": version: 2.5.0 resolution: "yaml@npm:2.5.0" bin: @@ -7120,6 +7161,15 @@ __metadata: languageName: node linkType: hard +"yaml@npm:~2.5.0": + version: 2.5.1 + resolution: "yaml@npm:2.5.1" + bin: + yaml: bin.mjs + checksum: 10c0/40fba5682898dbeeb3319e358a968fe886509fab6f58725732a15f8dda3abac509f91e76817c708c9959a15f786f38ff863c1b88062d7c1162c5334a7d09cb4a + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"