diff --git a/packages/core/src/observables/participant.ts b/packages/core/src/observables/participant.ts index 6b3c1351b..aec21df59 100644 --- a/packages/core/src/observables/participant.ts +++ b/packages/core/src/observables/participant.ts @@ -46,14 +46,14 @@ export function observeParticipantMedia(participant: T) { participant, ParticipantEvent.TrackMuted, ParticipantEvent.TrackUnmuted, - ParticipantEvent.ParticipantPermissionsChanged, // ParticipantEvent.IsSpeakingChanged, ParticipantEvent.TrackPublished, ParticipantEvent.TrackUnpublished, - ParticipantEvent.TrackSubscribed, - ParticipantEvent.TrackUnsubscribed, ParticipantEvent.LocalTrackPublished, ParticipantEvent.LocalTrackUnpublished, + ParticipantEvent.TrackSubscriptionStatusChanged, + ParticipantEvent.TrackStreamStateChanged, + ParticipantEvent.TrackSubscriptionPermissionChanged, // ParticipantEvent.ConnectionQualityChanged, ).pipe( map((p) => { diff --git a/packages/core/src/observables/track.ts b/packages/core/src/observables/track.ts index 511edbced..64968f415 100644 --- a/packages/core/src/observables/track.ts +++ b/packages/core/src/observables/track.ts @@ -18,8 +18,9 @@ export function trackObservable(track: TrackPublication) { track, TrackEvent.Muted, TrackEvent.Unmuted, - TrackEvent.Subscribed, - TrackEvent.Unsubscribed, + TrackEvent.SubscriptionStatusChanged, + TrackEvent.SubscriptionPermissionChanged, + TrackEvent.Restarted, ); return trackObserver; diff --git a/packages/react/package.json b/packages/react/package.json index 2c6091614..bbbe4508d 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -47,7 +47,8 @@ "dependencies": { "@livekit/components-core": "0.6.1", "@react-hook/latest": "^1.0.3", - "clsx": "^1.2.1" + "clsx": "^1.2.1", + "react-json-tree": "^0.18.0" }, "peerDependencies": { "livekit-client": "^1.7.0", diff --git a/packages/react/src/components/TrackLoop.tsx b/packages/react/src/components/TrackLoop.tsx index 9dc7148ed..e48b3a365 100644 --- a/packages/react/src/components/TrackLoop.tsx +++ b/packages/react/src/components/TrackLoop.tsx @@ -40,7 +40,7 @@ export const TrackLoop = ({ tracks, ...props }: React.PropsWithChildren + )} ); diff --git a/packages/react/src/components/participant/DebugView.tsx b/packages/react/src/components/participant/DebugView.tsx new file mode 100644 index 000000000..300e16597 --- /dev/null +++ b/packages/react/src/components/participant/DebugView.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { JSONTree } from 'react-json-tree'; +import type { DebugTrackInfo } from './MediaTrack'; + +export const DebugView = ({ debugInfo }: { debugInfo?: DebugTrackInfo }) => { + // React.useEffect(() => { + // import('react-json-tree').then((value) => setJSONTree(value.JSONTree)); + // }, []); + + console.log(debugInfo); + + return ( +
+ {typeof JSONTree !== 'undefined' && debugInfo && } +
+ ); +}; diff --git a/packages/react/src/components/participant/MediaTrack.tsx b/packages/react/src/components/participant/MediaTrack.tsx index 39a20b79a..bc2a5a64a 100644 --- a/packages/react/src/components/participant/MediaTrack.tsx +++ b/packages/react/src/components/participant/MediaTrack.tsx @@ -1,9 +1,16 @@ import type { Participant, TrackPublication } from 'livekit-client'; +import { + LocalAudioTrack, + LocalVideoTrack, + RemoteAudioTrack, + RemoteVideoTrack, +} from 'livekit-client'; import { Track } from 'livekit-client'; import * as React from 'react'; import { useMediaTrackBySourceOrName } from '../../hooks/useMediaTrackBySourceOrName'; import type { ParticipantClickEvent } from '@livekit/components-core'; import { useEnsureParticipant } from '../../context'; +import type { TrackInfo } from 'livekit-client/dist/src/proto/livekit_models'; /** * @deprecated Use `AudioTrack` or `VideoTrack` instead @@ -16,8 +23,14 @@ export type MediaTrackProps = publication?: TrackPublication; onTrackClick?: (evt: ParticipantClickEvent) => void; onSubscriptionStatusChanged?: (subscribed: boolean) => void; + onDebugInfo?: (stats: DebugTrackInfo) => void; }; +export type DebugTrackInfo = { + info?: TrackInfo; + stats?: Map>; +}; + /** * @deprecated This component will be removed in the next version. Use `AudioTrack` or `VideoTrack` instead */ @@ -29,6 +42,7 @@ export function MediaTrack({ name, participant, publication, + onDebugInfo, ...props }: MediaTrackProps) { const mediaEl = React.useRef(null); @@ -53,6 +67,48 @@ export function MediaTrack({ onSubscriptionStatusChanged?.(!!isSubscribed); }, [isSubscribed, onSubscriptionStatusChanged]); + React.useEffect(() => { + const updateStats = async () => { + if (onDebugInfo && pub) { + const track = pub.track; + let stats: RTCStatsReport | undefined; + const readableStats: Map> = new Map(); + + if (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) { + stats = await track.sender?.getStats(); + console.log( + 'local stats', + stats?.forEach((val) => { + if (val.type === 'outbound-rtp') { + readableStats.set(val['rid'], val); + } + }), + ); + } else if (track instanceof RemoteVideoTrack || track instanceof RemoteAudioTrack) { + stats = await track.receiver?.getStats(); + console.log( + 'local stats', + stats?.forEach((val) => { + if (val.type === 'inbound-rtp') { + readableStats.set(val['trackIdentifier'], val); + } + }), + ); + } + const debugInfo = { + stats: readableStats, + info: pub.trackInfo, + } satisfies DebugTrackInfo; + onDebugInfo(debugInfo); + } + }; + updateStats(); + const statsInterval = setInterval(() => updateStats(), 2000); + return () => { + clearInterval(statsInterval); + }; + }, [onDebugInfo, pub]); + const clickHandler = (evt: React.MouseEvent) => { onClick?.(evt); onTrackClick?.({ participant: p, track: pub }); diff --git a/packages/react/src/components/participant/ParticipantTile.tsx b/packages/react/src/components/participant/ParticipantTile.tsx index 9d303264f..0349ac3f0 100644 --- a/packages/react/src/components/participant/ParticipantTile.tsx +++ b/packages/react/src/components/participant/ParticipantTile.tsx @@ -4,6 +4,7 @@ import { Track } from 'livekit-client'; import type { ParticipantClickEvent, TrackReferenceOrPlaceholder } from '@livekit/components-core'; import { isParticipantSourcePinned, setupParticipantTile } from '@livekit/components-core'; import { ConnectionQualityIndicator } from './ConnectionQualityIndicator'; +import type { DebugTrackInfo } from './MediaTrack'; import { MediaTrack } from './MediaTrack'; import { ParticipantName } from './ParticipantName'; import { TrackMutedIndicator } from './TrackMutedIndicator'; @@ -18,6 +19,7 @@ import { mergeProps } from '../../utils'; import { FocusToggle } from '../controls/FocusToggle'; import { ParticipantPlaceholder } from '../../assets/images'; import { ScreenShareIcon } from '../../assets/icons'; +import { DebugView } from './DebugView'; export type ParticipantTileProps = React.HTMLAttributes & { disableSpeakingIndicator?: boolean; @@ -25,6 +27,7 @@ export type ParticipantTileProps = React.HTMLAttributes & { source?: Track.Source; publication?: TrackPublication; onParticipantClick?: (event: ParticipantClickEvent) => void; + showDebugOverlay?: boolean; }; export type UseParticipantTileProps> = @@ -109,9 +112,12 @@ export const ParticipantTile = ({ onParticipantClick, publication, disableSpeakingIndicator, + showDebugOverlay, ...htmlProps }: ParticipantTileProps) => { const p = useEnsureParticipant(participant); + const [debugInfo, setDebugInfo] = React.useState(undefined); + const [showDebugPopup, setShowDebugPopup] = React.useState(false); const { elementProps } = useParticipantTile({ participant: p, @@ -150,6 +156,7 @@ export const ParticipantTile = ({ publication={publication} participant={participant} onSubscriptionStatusChanged={handleSubscribe} + onDebugInfo={showDebugOverlay ? setDebugInfo : undefined} />
@@ -171,12 +178,22 @@ export const ParticipantTile = ({ )}
+ {showDebugOverlay && debugInfo && ( +
setShowDebugPopup((val) => !val)} + > + debug +
+ )} + )} + {showDebugPopup && debugInfo && } ); }; diff --git a/yarn.lock b/yarn.lock index b8cb23e3b..8d8ab0392 100644 --- a/yarn.lock +++ b/yarn.lock @@ -971,7 +971,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.20.1", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.8", "@babel/runtime@^7.20.1", "@babel/runtime@^7.20.6", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== @@ -2780,6 +2780,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/base16@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/base16/-/base16-1.0.2.tgz#eb3a07db52309bfefb9ba010dfdb3c0784971f65" + integrity sha512-oYO/U4VD1DavwrKuCSQWdLG+5K22SLPem2OQaHmFcQuwHoVeGC+JGVRji2MUqZUAIQZHEonOeVfAX09hYiLsdg== + "@types/body-parser@*": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" @@ -2936,7 +2941,7 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/lodash@^4.14.167": +"@types/lodash@^4.14.167", "@types/lodash@^4.14.178", "@types/lodash@^4.14.191": version "4.14.192" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.192.tgz#5790406361a2852d332d41635d927f1600811285" integrity sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A== @@ -3791,6 +3796,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base16@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" + integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -4254,7 +4264,7 @@ clsx@^1.2.1: resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== -color-convert@^1.9.0: +color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -4273,16 +4283,32 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + color-support@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== +color@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + colorette@^2.0.19: version "2.0.19" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" @@ -4522,7 +4548,7 @@ cssstyle@^3.0.0: dependencies: rrweb-cssom "^0.6.0" -csstype@^3.0.2: +csstype@^3.0.10, csstype@^3.0.2: version "3.1.2" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== @@ -6548,6 +6574,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -7265,6 +7296,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.curry@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" + integrity sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -8631,6 +8667,19 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +react-base16-styling@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.9.1.tgz#4906b4c0a51636f2dca2cea8b682175aa8bd0c92" + integrity sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw== + dependencies: + "@babel/runtime" "^7.16.7" + "@types/base16" "^1.0.2" + "@types/lodash" "^4.14.178" + base16 "^1.0.0" + color "^3.2.1" + csstype "^3.0.10" + lodash.curry "^4.1.1" + react-colorful@^5.1.2: version "5.6.1" resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.6.1.tgz#7dc2aed2d7c72fac89694e834d179e32f3da563b" @@ -8694,6 +8743,15 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-json-tree@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/react-json-tree/-/react-json-tree-0.18.0.tgz#3c4bec7b091f50dcc9c09652d89c8f4373ebf3ea" + integrity sha512-Qe6HKSXrr++n9Y31nkRJ3XvQMATISpqigH1vEKhLwB56+nk5thTP0ITThpjxY6ZG/ubpVq/aEHIcyLP/OPHxeA== + dependencies: + "@babel/runtime" "^7.20.6" + "@types/lodash" "^4.14.191" + react-base16-styling "^0.9.1" + react-refresh@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" @@ -9232,6 +9290,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + simple-update-notifier@^1.0.0, simple-update-notifier@^1.0.7: version "1.1.0" resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82"