From 430d3fb06765bb578e0d2edfa1d8a04b3bed87ea Mon Sep 17 00:00:00 2001 From: streamer45 Date: Mon, 24 Jun 2024 16:32:18 +0200 Subject: [PATCH 1/4] AV1 support --- e2e/config.ts | 7 ++ e2e/tests/media.spec.ts | 145 +++++++++++++++++++++++++++++++++++ go.mod | 19 ++--- go.sum | 40 +++++----- plugin.json | 9 ++- server/cluster_message.go | 15 ++-- server/configuration.go | 10 +++ server/plugin.go | 8 +- server/websocket.go | 33 +++++--- standalone/package-lock.json | 6 +- standalone/package.json | 2 +- standalone/src/init.ts | 4 +- webapp/package-lock.json | 6 +- webapp/package.json | 2 +- webapp/src/client.ts | 28 ++++++- webapp/src/index.tsx | 1 + webapp/src/types/types.ts | 13 +--- 17 files changed, 274 insertions(+), 74 deletions(-) diff --git a/e2e/config.ts b/e2e/config.ts index 304512492..123f33403 100644 --- a/e2e/config.ts +++ b/e2e/config.ts @@ -5,6 +5,7 @@ import {adminState, baseURL, pluginID} from './constants'; type CallsConfig = { enabletranscriptions?: boolean; enablelivecaptions?: boolean; + enableav1?: boolean; }; export const apiPatchConfig = async (cfg: CallsConfig) => { @@ -40,3 +41,9 @@ export const apiSetEnableLiveCaptions = async (enabled: boolean) => { enablelivecaptions: enabled, }); }; + +export const apiSetEnableAV1 = async (enabled: boolean) => { + return apiPatchConfig({ + enableav1: enabled, + }); +}; diff --git a/e2e/tests/media.spec.ts b/e2e/tests/media.spec.ts index 142d1f1e7..d0f1ab1b1 100644 --- a/e2e/tests/media.spec.ts +++ b/e2e/tests/media.spec.ts @@ -1,5 +1,6 @@ import {expect, test} from '@playwright/test'; +import {apiSetEnableAV1} from '../config'; import PlaywrightDevPage from '../page'; import {getUserStoragesForTest, startCall} from '../utils'; @@ -123,6 +124,150 @@ test.describe('screen sharing', () => { await devPage.leaveCall(); await userPage.leaveCall(); }); + + test('av1', async ({page}) => { + // Enabling AV1 + await apiSetEnableAV1(true); + + const receiverPage = await startCall(userStorages[1]); + + const senderPage = new PlaywrightDevPage(page); + await senderPage.joinCall(); + + await page.locator('#calls-widget-toggle-menu-button').click(); + await page.locator('#calls-widget-menu-screenshare').click(); + + await expect(page.locator('#screen-player')).toBeVisible(); + await expect(receiverPage.page.locator('#screen-player')).toBeVisible(); + + let screenStreamID = await (await receiverPage.page.waitForFunction(() => { + return window.callsClient.getRemoteScreenStream()?.getVideoTracks()[0]?.id; + })).evaluate(() => { + return window.callsClient.getRemoteScreenStream()?.getVideoTracks()[0]?.id; + }); + expect(screenStreamID).toContain('screen_'); + + // Give it a couple of seconds for encoders/decoders stats to be available. + await senderPage.wait(2000); + + type rtcCodecStats = { + type: string, + mimeType: string, + }; + + type rtcCodecs = { + vp8?: rtcCodecStats, + av1?: rtcCodecStats, + }; + + let rxCodecs = await receiverPage.page.evaluate(async () => { + const stats = await window.callsClient.peer.pc.getStats(); + const codecs: rtcCodecs = {}; + stats.forEach((report: rtcCodecStats) => { + if (report.type === 'codec') { + if (report.mimeType === 'video/VP8') { + codecs.vp8 = report; + } else if (report.mimeType === 'video/AV1') { + codecs.av1 = report; + } + } + }); + return codecs; + }); + expect(rxCodecs.av1).toBeDefined(); + + let txCodecs = await senderPage.page.evaluate(async () => { + const stats = await window.callsClient.peer.pc.getStats(); + const codecs: rtcCodecs = {}; + stats.forEach((report: rtcCodecStats) => { + if (report.type === 'codec') { + if (report.mimeType === 'video/VP8') { + codecs.vp8 = report; + } else if (report.mimeType === 'video/AV1') { + codecs.av1 = report; + } + } + }); + return codecs; + }); + expect(txCodecs.vp8).toBeDefined(); + expect(txCodecs.av1).toBeDefined(); + + await page.getByTestId('calls-widget-stop-screenshare').click(); + + await expect(page.locator('#screen-player')).toBeHidden(); + await expect(receiverPage.page.locator('#screen-player')).toBeHidden(); + + await senderPage.leaveCall(); + await receiverPage.leaveCall(); + + // Disabling AV1 + await apiSetEnableAV1(false); + + // need to refresh for the updated config to be loaded + await Promise.all([senderPage.page.reload(), receiverPage.page.reload()]); + + await receiverPage.startCall(); + await senderPage.joinCall(); + + await page.locator('#calls-widget-toggle-menu-button').click(); + await page.locator('#calls-widget-menu-screenshare').click(); + + await expect(page.locator('#screen-player')).toBeVisible(); + await expect(receiverPage.page.locator('#screen-player')).toBeVisible(); + + screenStreamID = await (await receiverPage.page.waitForFunction(() => { + return window.callsClient.getRemoteScreenStream()?.getVideoTracks()[0]?.id; + })).evaluate(() => { + return window.callsClient.getRemoteScreenStream()?.getVideoTracks()[0]?.id; + }); + expect(screenStreamID).toContain('screen_'); + + // Give it a couple of seconds for encoders/decoders stats to be available. + await senderPage.wait(2000); + + rxCodecs = await receiverPage.page.evaluate(async () => { + const stats = await window.callsClient.peer.pc.getStats(); + const codecs: rtcCodecs = {}; + stats.forEach((report: rtcCodecStats) => { + if (report.type === 'codec') { + if (report.mimeType === 'video/VP8') { + codecs.vp8 = report; + } else if (report.mimeType === 'video/AV1') { + codecs.av1 = report; + } + } + }); + return codecs; + }); + expect(rxCodecs.vp8).toBeDefined(); + expect(rxCodecs.av1).not.toBeDefined(); + + txCodecs = await senderPage.page.evaluate(async () => { + const stats = await window.callsClient.peer.pc.getStats(); + const codecs: rtcCodecs = {}; + stats.forEach((report: rtcCodecStats) => { + if (report.type === 'codec') { + if (report.mimeType === 'video/VP8') { + codecs.vp8 = report; + } else if (report.mimeType === 'video/AV1') { + codecs.av1 = report; + } + } + }); + return codecs; + }); + expect(txCodecs.vp8).toBeDefined(); + expect(txCodecs.av1).not.toBeDefined(); + + await page.getByTestId('calls-widget-stop-screenshare').click(); + + await expect(page.locator('#screen-player')).toBeHidden(); + await expect(receiverPage.page.locator('#screen-player')).toBeHidden(); + + await senderPage.leaveCall(); + await receiverPage.leaveCall(); + }); }); test.describe('sending voice', () => { diff --git a/go.mod b/go.mod index 5d9ef91ea..f75f28cf4 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/mattermost/mattermost-plugin-calls go 1.21.9 require ( - github.com/pion/ice/v2 v2.3.24 // indirect + github.com/pion/ice/v2 v2.3.25 // indirect github.com/pion/rtcp v1.2.14 // indirect - github.com/pion/webrtc/v3 v3.2.40 // indirect + github.com/pion/webrtc/v3 v3.2.42 // indirect github.com/prometheus/client_golang v1.16.0 github.com/stretchr/testify v1.9.0 ) @@ -24,7 +24,7 @@ require ( github.com/mattermost/mattermost-plugin-calls/server/public v0.0.3 github.com/mattermost/mattermost/server/public v0.1.5-0.20240613070149-4b0ae20ef7b4 github.com/mattermost/morph v1.1.0 - github.com/mattermost/rtcd v0.16.0 + github.com/mattermost/rtcd v0.16.2-0.20240624142925-2c28bfbdc0fe github.com/mattermost/squirrel v0.2.0 github.com/pkg/errors v0.9.1 github.com/rudderlabs/analytics-go v3.3.3+incompatible @@ -138,13 +138,14 @@ require ( go.opentelemetry.io/otel v1.19.0 // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.0 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.24.0 // indirect golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect - golang.org/x/mod v0.16.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.18.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.62.0 // indirect google.golang.org/protobuf v1.32.0 // indirect diff --git a/go.sum b/go.sum index 2f6c797a2..d7e81a961 100644 --- a/go.sum +++ b/go.sum @@ -377,8 +377,8 @@ github.com/mattermost/mattermost/server/public v0.1.5-0.20240613070149-4b0ae20ef github.com/mattermost/mattermost/server/public v0.1.5-0.20240613070149-4b0ae20ef7b4/go.mod h1:PDPb/iqzJJ5ZvK/m70oDF55AXN/cOvVFj96Yu4e6j+Q= github.com/mattermost/morph v1.1.0 h1:Q9vrJbeM3s2jfweGheq12EFIzdNp9a/6IovcbvOQ6Cw= github.com/mattermost/morph v1.1.0/go.mod h1:gD+EaqX2UMyyuzmF4PFh4r33XneQ8Nzi+0E8nXjMa3A= -github.com/mattermost/rtcd v0.16.0 h1:oAYUbIYdbXDB7kMpEEKZVrgIvsCKYNAsbL3gNecADls= -github.com/mattermost/rtcd v0.16.0/go.mod h1:YhKWxm9FXKq+vGiiBFtSh43uGuWdWmEJgzQHHAgtHFI= +github.com/mattermost/rtcd v0.16.2-0.20240624142925-2c28bfbdc0fe h1:1Rumg/R6lfKutgFIABq5vfSzxBLjtMDh6ZFx5z1fmpI= +github.com/mattermost/rtcd v0.16.2-0.20240624142925-2c28bfbdc0fe/go.mod h1:HPC9PKE9SkoJzOlEWcyaqRDgdowrYixisVFIh3TSUA4= github.com/mattermost/squirrel v0.2.0 h1:8ZWeyf+MWQ2cL7hu9REZgLtz2IJi51qqZEovI3T3TT8= github.com/mattermost/squirrel v0.2.0/go.mod h1:NPPtk+CdpWre4GxMGoOpzEVFVc0ZoEFyJBZGCtn9nSU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -450,8 +450,8 @@ github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNI github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= -github.com/pion/ice/v2 v2.3.24 h1:RYgzhH/u5lH0XO+ABatVKCtRd+4U1GEaCXSMjNr13tI= -github.com/pion/ice/v2 v2.3.24/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw= +github.com/pion/ice/v2 v2.3.25 h1:M5rJA07dqhi3nobJIg+uPtcVjFECTrhcR3n0ns8kDZs= +github.com/pion/ice/v2 v2.3.25/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8= @@ -485,8 +485,8 @@ github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37 github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc= github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= -github.com/pion/webrtc/v3 v3.2.40 h1:Wtfi6AZMQg+624cvCXUuSmrKWepSB7zfgYDOYqsSOVU= -github.com/pion/webrtc/v3 v3.2.40/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY= +github.com/pion/webrtc/v3 v3.2.42 h1:WN/ZuMjtpQOoGRCZUg/zFG+JHEvYLVyDKOxU6H1qWlE= +github.com/pion/webrtc/v3 v3.2.42/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -712,8 +712,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -755,8 +755,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -810,8 +810,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -840,8 +840,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -915,8 +915,8 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -940,8 +940,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1005,8 +1005,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/plugin.json b/plugin.json index ad21522c5..6b60e434b 100644 --- a/plugin.json +++ b/plugin.json @@ -285,11 +285,18 @@ "type": "bool", "default": false, "help_text": "When set to true, ringing functionality is enabled: participants in DM and GM channels will receive a desktop alert and a ringing notification when a call is started. Changing this setting requires a plugin restart." + }, + { + "key": "EnableAV1", + "display_name": "Enable AV1 codec for screen sharing (Experimental)", + "type": "bool", + "default": false, + "help_text": "When set to true it enables using the AV1 codec to encode screen sharing tracks. This can result in improved screen sharing quality for clients that support it.\nNote: this setting won't apply when EnableSimulcast is true." } ] }, "props": { - "min_rtcd_version": "v0.12.0", + "min_rtcd_version": "v0.17.0", "min_offloader_version": "v0.8.0", "calls_recorder_version": "v0.7.2", "calls_transcriber_version": "v0.2.2" diff --git a/server/cluster_message.go b/server/cluster_message.go index df108dfd8..54868b9f2 100644 --- a/server/cluster_message.go +++ b/server/cluster_message.go @@ -7,16 +7,19 @@ import ( "encoding/json" "fmt" + "github.com/mattermost/rtcd/service/rtc" + "github.com/mattermost/mattermost/server/public/model" ) type clusterMessage struct { - ConnID string `json:"conn_id,omitempty"` - UserID string `json:"user_id,omitempty"` - ChannelID string `json:"channel_id,omitempty"` - CallID string `json:"call_id,omitempty"` - SenderID string `json:"sender_id,omitempty"` - ClientMessage clientMessage `json:"client_message,omitempty"` + ConnID string `json:"conn_id,omitempty"` + UserID string `json:"user_id,omitempty"` + ChannelID string `json:"channel_id,omitempty"` + CallID string `json:"call_id,omitempty"` + SenderID string `json:"sender_id,omitempty"` + SessionProps rtc.SessionProps `json:"session_props,omitempty"` + ClientMessage clientMessage `json:"client_message,omitempty"` } type clusterMessageType string diff --git a/server/configuration.go b/server/configuration.go index 8d890846b..b88339417 100644 --- a/server/configuration.go +++ b/server/configuration.go @@ -123,6 +123,8 @@ type clientConfig struct { SkuShortName string `json:"sku_short_name"` // Let the server determine whether or not host controls are allowed (through license checks or otherwise) HostControlsAllowed bool + // When set to true it enables using the AV1 codec to encode screen sharing tracks. + EnableAV1 *bool } const ( @@ -238,6 +240,9 @@ func (c *configuration) SetDefaults() { if c.LiveCaptionsLanguage == "" { c.LiveCaptionsLanguage = transcriber.LiveCaptionsLanguageDefault } + if c.EnableAV1 == nil { + c.EnableAV1 = model.NewBool(false) + } } func (c *configuration) IsValid() error { @@ -415,6 +420,10 @@ func (c *configuration) Clone() *configuration { cfg.LiveCaptionsNumThreadsPerTranscriber = model.NewInt(*c.LiveCaptionsNumThreadsPerTranscriber) } + if c.EnableAV1 != nil { + cfg.EnableAV1 = model.NewBool(*c.EnableAV1) + } + return &cfg } @@ -479,6 +488,7 @@ func (p *Plugin) getClientConfig() clientConfig { EnableRinging: c.EnableRinging, SkuShortName: skuShortName, HostControlsAllowed: p.licenseChecker.HostControlsAllowed(), + EnableAV1: c.EnableAV1, } } diff --git a/server/plugin.go b/server/plugin.go index e9909975e..9142bc7c7 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -80,15 +80,13 @@ type Plugin struct { removeSessionsBatchers map[string]*batching.Batcher } -func (p *Plugin) startSession(us *session, senderID string) { +func (p *Plugin) startSession(us *session, senderID string, props rtc.SessionProps) { cfg := rtc.SessionConfig{ GroupID: "default", CallID: us.callID, UserID: us.userID, SessionID: us.connID, - Props: map[string]any{ - "channelID": us.channelID, - }, + Props: props, } if err := p.rtcServer.InitSession(cfg, func() error { p.LogDebug("rtc session close cb", "sessionID", us.connID) @@ -162,7 +160,7 @@ func (p *Plugin) handleEvent(ev model.PluginClusterEvent) error { } us = newUserSession(msg.UserID, msg.ChannelID, msg.ConnID, msg.CallID, true) p.sessions[msg.ConnID] = us - go p.startSession(us, msg.SenderID) + go p.startSession(us, msg.SenderID, msg.SessionProps) return nil case clusterMessageTypeReconnect: p.LogDebug("reconnect event", "UserID", msg.UserID, "ConnID", msg.ConnID) diff --git a/server/websocket.go b/server/websocket.go index 9252ef47a..18538bbd3 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -84,6 +84,8 @@ type CallsClientJoinData struct { Title string ThreadID string + AV1Support bool + // JobID is the id of the job tight to the bot connection to // a call (e.g. recording, transcription). It's a parameter reserved to the // Calls bot only. @@ -766,11 +768,12 @@ func (p *Plugin) handleJoin(userID, connID, authSessionID string, joinData calls if p.rtcdManager != nil { msg := rtcd.ClientMessage{ Type: rtcd.ClientMessageJoin, - Data: map[string]string{ - "callID": us.callID, - "userID": userID, - "sessionID": connID, - "channelID": channelID, + Data: map[string]any{ + "callID": us.callID, + "userID": userID, + "sessionID": connID, + "channelID": channelID, + "av1Support": joinData.AV1Support, }, } if err := p.rtcdManager.Send(msg, state.Call.Props.RTCDHost); err != nil { @@ -789,8 +792,9 @@ func (p *Plugin) handleJoin(userID, connID, authSessionID string, joinData calls CallID: us.callID, UserID: userID, SessionID: connID, - Props: map[string]any{ - "channelID": us.channelID, + Props: rtc.SessionProps{ + "channelID": channelID, + "av1Support": joinData.AV1Support, }, } p.LogDebug("initializing RTC session", "userID", userID, "connID", connID, "channelID", channelID, "callID", us.callID) @@ -816,6 +820,10 @@ func (p *Plugin) handleJoin(userID, connID, authSessionID string, joinData calls ChannelID: channelID, CallID: us.callID, SenderID: p.nodeID, + SessionProps: rtc.SessionProps{ + "channelID": channelID, + "av1Support": joinData.AV1Support, + }, }, clusterMessageTypeConnect, handlerID); err != nil { p.LogError("failed to send connect message", "err", err.Error()) go func() { @@ -1143,15 +1151,18 @@ func (p *Plugin) WebSocketMessageHasBeenPosted(connID, userID string, req *model // it will be an empty string. jobID, _ := req.Data["jobID"].(string) + av1Support, _ := req.Data["av1Support"].(bool) + remoteAddr, _ := req.Data[model.WebSocketRemoteAddr].(string) xff, _ := req.Data[model.WebSocketXForwardedFor].(string) joinData := callsJoinData{ CallsClientJoinData{ - channelID, - title, - threadID, - jobID, + ChannelID: channelID, + Title: title, + ThreadID: threadID, + AV1Support: av1Support, + JobID: jobID, }, remoteAddr, xff, diff --git a/standalone/package-lock.json b/standalone/package-lock.json index 94602ab8c..bc6a9fb11 100644 --- a/standalone/package-lock.json +++ b/standalone/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "dependencies": { - "@mattermost/calls-common": "0.27.2", + "@mattermost/calls-common": "github:mattermost/calls-common#3fbbae7156dbcaa9675db9308ad88518e5f00b17", "@mattermost/compass-icons": "0.1.31", "@msgpack/msgpack": "2.7.1", "bootstrap": "3.4.1", @@ -3089,8 +3089,8 @@ }, "node_modules/@mattermost/calls-common": { "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@mattermost/calls-common/-/calls-common-0.27.2.tgz", - "integrity": "sha512-8L7VdFP+QU1AzLKi7cQ/wmSBxJHnEPYWICDkyXqreiEN/vAef7B5pWiJgdFuYX5iXNKQ+EEvrDKMCb0EPQC3Tw==" + "resolved": "git+ssh://git@github.com/mattermost/calls-common.git#3fbbae7156dbcaa9675db9308ad88518e5f00b17", + "integrity": "sha512-xx5L5TzPWJpvGTTiqycl4gGncEqS3kR/a/NWA8W1flPOYZrKP1ELLd5L5e/NN59TvOr9tbBqfrskKz4jGjB3Xw==" }, "node_modules/@mattermost/client": { "resolved": "../webapp/mattermost-webapp/webapp/platform/client", diff --git a/standalone/package.json b/standalone/package.json index ae372f35b..3db2dd0f3 100644 --- a/standalone/package.json +++ b/standalone/package.json @@ -18,7 +18,7 @@ "extract": "formatjs extract 'src/**/*.{ts,tsx}' --ignore 'src/**/*.d.ts' --out-file i18n/temp.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' && formatjs compile 'i18n/temp.json' --out-file i18n/en.json && rm i18n/temp.json" }, "dependencies": { - "@mattermost/calls-common": "0.27.2", + "@mattermost/calls-common": "github:mattermost/calls-common#3fbbae7156dbcaa9675db9308ad88518e5f00b17", "@mattermost/compass-icons": "0.1.31", "@msgpack/msgpack": "2.7.1", "bootstrap": "3.4.1", diff --git a/standalone/src/init.ts b/standalone/src/init.ts index ccc81f0ea..ae0c59ebb 100644 --- a/standalone/src/init.ts +++ b/standalone/src/init.ts @@ -10,6 +10,7 @@ import '@mattermost/compass-icons/css/compass-icons.css'; import { CallHostChangedData, CallJobStateData, + CallsClientJoinData, CallStartData, CallStateData, EmptyData, @@ -76,7 +77,7 @@ import { handleUserVoiceOn, } from 'plugin/websocket_handlers'; import {Reducer} from 'redux'; -import {CallActions, CallsClientConfig, CallsClientJoinData, CurrentCallData, CurrentCallDataDefault} from 'src/types/types'; +import {CallActions, CallsClientConfig, CurrentCallData, CurrentCallDataDefault} from 'src/types/types'; import { getCallID, @@ -227,6 +228,7 @@ export default async function init(cfg: InitConfig) { iceServers: iceConfigs, authToken: getToken(), simulcast: callsConfig(store.getState()).EnableSimulcast, + enableAV1: callsConfig(store.getState()).EnableAV1, }; connectCall(joinData, clientConfig, (ev) => { diff --git a/webapp/package-lock.json b/webapp/package-lock.json index a4e38d90f..1a70b3964 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -7,7 +7,7 @@ "hasInstallScript": true, "dependencies": { "@floating-ui/react": "0.26.12", - "@mattermost/calls-common": "0.27.2", + "@mattermost/calls-common": "github:mattermost/calls-common#3fbbae7156dbcaa9675db9308ad88518e5f00b17", "@msgpack/msgpack": "2.7.1", "@redux-devtools/extension": "3.2.3", "core-js": "3.26.1", @@ -8239,8 +8239,8 @@ }, "node_modules/@mattermost/calls-common": { "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@mattermost/calls-common/-/calls-common-0.27.2.tgz", - "integrity": "sha512-8L7VdFP+QU1AzLKi7cQ/wmSBxJHnEPYWICDkyXqreiEN/vAef7B5pWiJgdFuYX5iXNKQ+EEvrDKMCb0EPQC3Tw==" + "resolved": "git+ssh://git@github.com/mattermost/calls-common.git#3fbbae7156dbcaa9675db9308ad88518e5f00b17", + "integrity": "sha512-xx5L5TzPWJpvGTTiqycl4gGncEqS3kR/a/NWA8W1flPOYZrKP1ELLd5L5e/NN59TvOr9tbBqfrskKz4jGjB3Xw==" }, "node_modules/@mattermost/client": { "resolved": "mattermost-webapp/webapp/platform/client", diff --git a/webapp/package.json b/webapp/package.json index 6c0a26c56..cb6b41f44 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@floating-ui/react": "0.26.12", - "@mattermost/calls-common": "0.27.2", + "@mattermost/calls-common": "github:mattermost/calls-common#3fbbae7156dbcaa9675db9308ad88518e5f00b17", "@msgpack/msgpack": "2.7.1", "@redux-devtools/extension": "3.2.3", "core-js": "3.26.1", diff --git a/webapp/src/client.ts b/webapp/src/client.ts index e5e0a6272..d1aeddbb6 100644 --- a/webapp/src/client.ts +++ b/webapp/src/client.ts @@ -1,12 +1,13 @@ /* eslint-disable max-lines */ // eslint-disable-next-line simple-import-sort/imports import {parseRTCStats, RTCMonitor, RTCPeer} from '@mattermost/calls-common'; -import {EmojiData} from '@mattermost/calls-common/lib/types'; +import type {EmojiData, CallsClientJoinData} from '@mattermost/calls-common/lib/types'; + import {EventEmitter} from 'events'; // @ts-ignore import {deflate} from 'pako/lib/deflate'; -import {AudioDevices, CallsClientConfig, CallsClientJoinData, CallsClientStats, TrackInfo} from 'src/types/types'; +import {AudioDevices, CallsClientConfig, CallsClientStats, TrackInfo} from 'src/types/types'; import {logDebug, logErr, logInfo, logWarn} from './log'; import {getScreenStream} from './utils'; @@ -44,6 +45,7 @@ export default class CallsClient extends EventEmitter { private connected = false; public initTime = Date.now(); private rtcMonitor: RTCMonitor | null = null; + private av1Codec: RTCRtpCodecCapability | null = null; constructor(config: CallsClientConfig) { super(); @@ -158,6 +160,14 @@ export default class CallsClient extends EventEmitter { public async init(joinData: CallsClientJoinData) { this.channelID = joinData.channelID; + if (this.config.enableAV1) { + this.av1Codec = await RTCPeer.getVideoCodec('video/AV1'); + if (this.av1Codec) { + logDebug('client has AV1 support'); + joinData.av1Support = true; + } + } + if (!window.isSecureContext) { throw insecureContextErr; } @@ -531,6 +541,7 @@ export default class CallsClient extends EventEmitter { this.localScreenTrack = screenTrack; const screenAudioTrack = screenStream.getAudioTracks()[0]; + if (screenAudioTrack) { screenStream = new MediaStream([screenTrack, screenAudioTrack]); } else { @@ -555,8 +566,21 @@ export default class CallsClient extends EventEmitter { }; logDebug('adding stream to peer', screenStream.id); + + // Always send a fallback track (VP8 encoded) for receivers that don't yet support AV1. this.peer.addStream(screenStream); + if (this.config.enableAV1 && this.av1Codec) { + if (this.config.simulcast) { + logWarn('both simulcast and av1 support are enabled'); + } else { + logDebug('AV1 supported, sending track', this.av1Codec); + this.peer.addStream(screenStream, [{ + codec: this.av1Codec, + }]); + } + } + this.ws.send('screen_on', { data: JSON.stringify({ screenStreamID: screenStream.id, diff --git a/webapp/src/index.tsx b/webapp/src/index.tsx index 751d64699..7a6d789e9 100644 --- a/webapp/src/index.tsx +++ b/webapp/src/index.tsx @@ -514,6 +514,7 @@ export default class Plugin { wsURL: getWSConnectionURL(getConfig(state)), iceServers: iceConfigs, simulcast: callsConfig(state).EnableSimulcast, + enableAV1: callsConfig(state).EnableAV1, }); window.currentCallData = CurrentCallDataDefault; diff --git a/webapp/src/types/types.ts b/webapp/src/types/types.ts index 42fa487d3..135a1ce01 100644 --- a/webapp/src/types/types.ts +++ b/webapp/src/types/types.ts @@ -19,6 +19,7 @@ export const CallsConfigDefault: CallsConfig = { EnableTranscriptions: false, EnableLiveCaptions: false, HostControlsAllowed: false, + EnableAV1: false, }; export type ChannelState = { @@ -26,22 +27,12 @@ export type ChannelState = { enabled?: boolean; } -export type CallsClientJoinData = { - channelID: string; - title?: string; - threadID?: string; - - // Calls bot only - // jobID is the id of the job tight to the bot connection to - // a call (e.g. recording, transcription). - jobID?: string; -} - export type CallsClientConfig = { wsURL: string; authToken?: string; iceServers: RTCIceServer[]; simulcast?: boolean; + enableAV1: boolean; } export type AudioDevices = { From df9309ea0d53c74ac9754ef4271100fdcdd6cf21 Mon Sep 17 00:00:00 2001 From: streamer45 Date: Wed, 26 Jun 2024 14:42:11 +0200 Subject: [PATCH 2/4] Guard functionality behind config flag --- webapp/src/client.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/webapp/src/client.ts b/webapp/src/client.ts index d1aeddbb6..ee21c8852 100644 --- a/webapp/src/client.ts +++ b/webapp/src/client.ts @@ -160,12 +160,14 @@ export default class CallsClient extends EventEmitter { public async init(joinData: CallsClientJoinData) { this.channelID = joinData.channelID; - if (this.config.enableAV1) { + if (this.config.enableAV1 && !this.config.simulcast) { this.av1Codec = await RTCPeer.getVideoCodec('video/AV1'); if (this.av1Codec) { logDebug('client has AV1 support'); joinData.av1Support = true; } + } else if (this.config.enableAV1 && this.config.simulcast) { + logWarn('both simulcast and av1 support are enabled'); } if (!window.isSecureContext) { @@ -571,14 +573,10 @@ export default class CallsClient extends EventEmitter { this.peer.addStream(screenStream); if (this.config.enableAV1 && this.av1Codec) { - if (this.config.simulcast) { - logWarn('both simulcast and av1 support are enabled'); - } else { - logDebug('AV1 supported, sending track', this.av1Codec); - this.peer.addStream(screenStream, [{ - codec: this.av1Codec, - }]); - } + logDebug('AV1 supported, sending track', this.av1Codec); + this.peer.addStream(screenStream, [{ + codec: this.av1Codec, + }]); } this.ws.send('screen_on', { From 47581b17fe405fbeaff6aadcff7fa802e3054a65 Mon Sep 17 00:00:00 2001 From: streamer45 Date: Fri, 28 Jun 2024 08:56:59 +0200 Subject: [PATCH 3/4] Fix lint --- webapp/src/types/types.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/webapp/src/types/types.ts b/webapp/src/types/types.ts index c298fdeab..83b8bd497 100644 --- a/webapp/src/types/types.ts +++ b/webapp/src/types/types.ts @@ -20,9 +20,7 @@ export const CallsConfigDefault: CallsConfig = { EnableLiveCaptions: false, HostControlsAllowed: false, EnableAV1: false, - - // Admin only - TranscribeAPI: TranscribeAPI.WhisperCPP, + TranscribeAPI: TranscribeAPI.WhisperCPP, // Admin only }; export type ChannelState = { From 4d6969ee809accc3c8e8d6f799420237f0745163 Mon Sep 17 00:00:00 2001 From: streamer45 Date: Fri, 26 Jul 2024 10:25:31 +0200 Subject: [PATCH 4/4] Bump rtcd --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8e958bf52..bd19f4539 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/mattermost/mattermost-plugin-calls/server/public v0.0.3 github.com/mattermost/mattermost/server/public v0.1.5-0.20240613070149-4b0ae20ef7b4 github.com/mattermost/morph v1.1.0 - github.com/mattermost/rtcd v0.16.2-0.20240624142925-2c28bfbdc0fe + github.com/mattermost/rtcd v0.16.3-0.20240726081546-8b783d9cd2d4 github.com/mattermost/squirrel v0.2.0 github.com/pkg/errors v0.9.1 github.com/rudderlabs/analytics-go v3.3.3+incompatible diff --git a/go.sum b/go.sum index 1e7b64aff..fa91acb6d 100644 --- a/go.sum +++ b/go.sum @@ -377,8 +377,8 @@ github.com/mattermost/mattermost/server/public v0.1.5-0.20240613070149-4b0ae20ef github.com/mattermost/mattermost/server/public v0.1.5-0.20240613070149-4b0ae20ef7b4/go.mod h1:PDPb/iqzJJ5ZvK/m70oDF55AXN/cOvVFj96Yu4e6j+Q= github.com/mattermost/morph v1.1.0 h1:Q9vrJbeM3s2jfweGheq12EFIzdNp9a/6IovcbvOQ6Cw= github.com/mattermost/morph v1.1.0/go.mod h1:gD+EaqX2UMyyuzmF4PFh4r33XneQ8Nzi+0E8nXjMa3A= -github.com/mattermost/rtcd v0.16.2-0.20240624142925-2c28bfbdc0fe h1:1Rumg/R6lfKutgFIABq5vfSzxBLjtMDh6ZFx5z1fmpI= -github.com/mattermost/rtcd v0.16.2-0.20240624142925-2c28bfbdc0fe/go.mod h1:HPC9PKE9SkoJzOlEWcyaqRDgdowrYixisVFIh3TSUA4= +github.com/mattermost/rtcd v0.16.3-0.20240726081546-8b783d9cd2d4 h1:wva3gPvesIiOtUO4/CQyVfVq/kzSFr7EQcV7Lh6ebsY= +github.com/mattermost/rtcd v0.16.3-0.20240726081546-8b783d9cd2d4/go.mod h1:bpGyHeb+JDkDzbrQheenpbrqfPhphW0jr09Jh+rpmpY= github.com/mattermost/squirrel v0.2.0 h1:8ZWeyf+MWQ2cL7hu9REZgLtz2IJi51qqZEovI3T3TT8= github.com/mattermost/squirrel v0.2.0/go.mod h1:NPPtk+CdpWre4GxMGoOpzEVFVc0ZoEFyJBZGCtn9nSU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=