* update(chat): add option to disable private chat
- Adds `public.chat.private` to settings.yml, defaults to true
- Adds `privateChat` to disabledFeatures
* backend validation for disabledFeatures=privateChat
* refactor(settings): remove chat.private from settings.yml
Will be kept only in disabledFeatures
---------
Co-authored-by: Gustavo Trott <gustavo@trott.com.br>
I/O device IDs are logged in some specific logCodes, but they aren't too
useful on their own without the rest of the MediaDeviceInfo object. We
need that extra data (label, group) to be able to better investigate
incorrect device issues and NotFoundError occurrences.
Register full I/O device info whenever the client fetches them and add
those, unfiltered, to the following logCodes:
- audiomanager_error_getting_device
- audiomanager_error_device_not_found
- audiomanager_error_unknown
- audio_joined
- audio_ended
- audio_failure
- audiomanager_input_live_device_change_failure
- audiomanager_output_device_change_failure
Stored input device IDs are not cleaned up whenever getUserMedia fails
with `NotFoundError` (i.e.: device not found). This causes a couple of
issues whenever skipEchoIfPreviousDevice is enabled:
- An unnecessary error screen if the user retries audio from scratch
(it should just go straight to the audio settings modal)
- A retry loop if no other devices are present in the system
Guarantee that stored input device IDs are cleared up whenever
getUserMedia fails with error name `NotFoundError`. This both the retry
loop and the unnecessary error screen.
`getUserMedia` is called by each audio bridge if it hasn't been
triggered during pre-flight screens. This ties gUM to the bridge's
negotiation timers and audio-manager's activation tracking, leading to
two issues:
- A gUM prompt left unanswered for over 30 seconds can cause an
incorrect 1010 (negotiation timeout) audio error.
- If `retryThroughRelay: true`, and a joinAudio timeout occurs due to an
unanswered gUM prompt, but the user responds while the system retries
the connection, it can create overlapping audio sessions, resulting in
mute state inconsistencies when `muteOnStart: true`.
This commit addresses these issues by moving gUM handling to the
audio-manager before any bridge action. This removes gUM from the
negotiation timeout trackers, ensuring that gUM errors are treated as
browser API errors (as expected).
Additionally, audio activation tracking in audio-manager has been updated
to exclude gUM times. Initially, gUM was included to catch unintended
browser-related gUM timeouts (e.g., Chrome bugs), but this skewed the
metric intended for tracking *negotiation times*. With this change,
`secondsToActivateAudio` will now focus solely on negotiation.
The metadata is comming from server with { name: metaname, value: metavalue }, which is not compatible with
getFromMeetingSettings, so it's not reading corretly.
Convert it before storing.
* feat(layout/observer): Add user preference for dark theme in `LayoutObserver` component.
* feat(docs/administration): Add support for default dark theme preference parameter
* refactor(settings): Remove dark theme setting from LayoutObserver and move it to Settings class.
Commit 26815f4679 was seemingly lost
during a merge in the 3.0.x-release branch. Nothing breaks, but we're
missing the log info originally added via that commit.
Restore the changes in 26815f4679:
- Add secondsToActivateAudio, inputDeviceId, outputDeviceId and isListenOnly
to audio_joined.extraInfo
- Add inputDeviceId, outputDeviceId and isListenOnly to
audio_failure.extraInfo
- Add a try-catch to the device enforcement procedure triggered by
onAudioJoin - it may throw and block the modal.
We currently use full renegotiation for audio, video, and screen sharing
reconnections, which involves re-creating transports and signaling channels
from scratch. While effective in some scenarios, this approach is slow and,
especially with outbound cameras and screen sharing, prone to failures.
To counter that, WebRTC provides a mechanism to restart ICE without needing
to re-create the peer connection. This allows us to avoid full renegotiation
and bypass some server-side signaling limitations. Implementing ICE restart
should make outbound camera/screen sharing reconnections more reliable and
faster.
This commit implements the ICE restart procedure for all WebRTC components'
*outbound* peers. It is based on bbb-webrtc-sfu >= v2.15.0-beta.0, which
added support for ICE restart requests. This feature is *off by default*.
To enable it, adjust the following flags:
- `/etc/bigbluebutton/bbb-webrtc-sfu/production.yml`: `allowIceRestart: true`
- `/etc/bigbluebutton/bbb-html5.yml`: `public.kurento.restartIce`
* Refer to the inline documentation; this can be enabled on the client side
per media type.
* Note: The default max retries for audio is lower than for cameras/screen
sharing (1 vs 3). This is because the full renegotiation process for audio
is more reliable, so ICE restart is attempted first, followed by full
renegotiation if necessary. This approach is less suitable for cameras/
screen sharing, where longer retry periods for ICE restart make sense
since full renegotation there is... iffy.
Endpoints that are inbound/`recvonly` only (client's perspective) do *not*
support ICE restart yet. There are two main reasons:
- Server-side changes are required to support `recvonly` endpoints,
particularly the proper handling of the server’s `setup` role in the
its SDPs during an ICE restart. These changes are too broad for now,
so they are deferred to future releases (SFU@v2.16).
- Full reconnections for `recvonly` endpoints are currently reliable,
unlike for `send*` endpoints. ICE restarts could still provide benefits
for `recvonly` endpoints, but we need the server updates first.
When `muteOnStart=true`, the initial local mute state in AudioManager is
desynced from the server. This issue stems from two recent changes:
- Decoupling voice activity updates from the main user_voice subscription,
which introduced an implicit muted state placeholder value
of true instead of false. See user_voice_activity's DB schema
propagation rules.
- Introduction of dialplan-level muteOnStart, muting channels on creation
rather than after.
Without properly updating AudioManager's `isMuted` placeholder, no
user_voice_activity update triggers *when joining audio* with
muteOnStart=true, causing two issues:
- Sender tracks are not locally muted on audio join.
- Opening the audio settings modal while muted will cause the
microphone to be incorrectly *unmuted* once it's closed (first try only).
This fix sets AudioManager's `isMuted` placeholder to true, matching the
server. Additionally:
- Enforce the local mute state before joining audio to ensure the desired
sender track state. Should make this a bit more future proof.
- Track `user_voice_activity` before joining audio (rather than after)
to avoid race conditions.
- Clean up `AudioManager.init` (loadBridges no longer returns a promise etc).
* feat(screenshare): add support for troubleshooting links
Adds setting option to specify troubleshooting links to each error code
of screenshare. When a troubleshooting link for the given error exists,
the toast notification about the error is displayed with a 'Learn more'
button that when clicked leads the user to the external link. When there
is no link set for the specific error code, the button is not displayed.
* fix(screenshare): change toast type for error code 1136
Changed toast type from 'error' to 'warning' for error code 1136 when
sharing screen. This adjustment was made because error code 1136 is also
returned when the user cancels screen sharing during the tab selection
process. Displaying an error toast in this situation could cause
unnecessary alarm for users, as they were simply canceling an operation.
* fix(notification): help link button element
Uses the button element instead of a div to display the 'Learn more'
help link button.
---------
Co-authored-by: Carlos Henrique <carloshsc1998@gmail.com>
Safari may enter a microphone permission check loop due to buggy behavior
in the Permissions API. When permission isn't permanently denied, gUM
requests fail with a NotAllowedError for a few seconds. During this time,
the permission state remains 'prompt' instead of transitioning to 'denied'
and back to 'prompt' after the timeout.
This leads to an issue where, on retrying while in 'prompt' + blocked,
the client loops through gUM checks via: 1) checking permission in the API,
2) receiving 'prompt', so trying gUM, 3) gUM fails, 4) returning to the
modal and checking permission again because the API still says 'prompt'.
Additionally, the `isUsingAudio` flag incorrectly counts the local echo
test/audio settings modal as "using audio," which toggles the flag on/off,
triggering the useEffect that causes the loop more frequently.
To fix this, remove the unnecessary AudioModal permission check that
causes the loop. Also, exclude "isEchoTest" from the `isUsingAudio` flag.
When listen only mode is deactivated and an user joins audio, an incorrect
remount of AudioSettings can trigger a spurious mute toggle. This happens
because AudioManager clears the `isConnecting` flag before setting the
`isConnected` flag. This creates a brief period where audio is flagged as
"disconnected," leading to a remount and unmount cycle that causes unwanted
mute/unmute actions due to AudioSettings' logic of muting/unmuting
active devices.
Ensure the `isConnected` flag is set before clearing the
`isConnecting` flag, preventing audio from being incorrectly flagged as
disconnected.
When going from "no mic" -> mic via the unmute action, the client isn't
unmuting itself after confirming the change. This is caused by not
waiting the liveChangeInputDevice method (which is a Promise) to be
fully executed before unmounting the AudioSettings modal -- the one
responsible for triggering the unmute. Since it unmounts before the
device is changed, the unmute action will be ignored because the device
is still "listen-only" (no mic).
Properly unmute audio when transitioning from "no mic" -> "mic" via the
unmute trigger by waiting for liveChangeInputDevice to resolve.
Additionally, some general improvements to UI/UX:
- Display the AudioSettings modal title when gUM is on prompt mode
- Add specific subtitles to the AudioSettings modal to 1) warn that no
mic is selected 2) Give a hint that the user can test their devices
- Always honor settings.yml's "initialHearingState" state (whether
local echo feedback should be played by default in AudioSettings)
This is a rework of the audio join procedure whithout the explict listen
only separation in mind. It's supposed to be used in conjunction with
the transparent listen only feature so that the distinction between
modes is seamless with minimal server-side impact. An abridged list of
changes:
- Let the user pick no input device when joining microphone while
allowing them to set an input device on the fly later on
- Give the user the option to join audio with no input device whenever
we fail to obtain input devices, with the option to try re-enabling
them on the fly later on
- Add the option to open the audio settings modal (echo test et al)
via the in-call device selection chevron
- Rework the SFU audio bridge and its services to support
adding/removing tracks on the fly without renegotiation
- Rework the SFU audio bridge and its services to support a new peer
role called "passive-sendrecv". That role is used by dupled peers
that have no active input source on start, but might have one later
on.
- Remove stale PermissionsOverlay component from the audio modal
- Rework how permission errors are detected using the Permissions API
- Rework the local echo test so that it uses a separate media tag
rather than the remote
- Add new, separate dialplans that mute/hold FreeSWITCH channels on
hold based on UA strings. This is orchestrated server-side via
webrtc-sfu and akka-apps. The basic difference here is that channels
now join in their desired state rather than waiting for client side
observers to sync the state up. It also mitigates transparent listen
only performance edge cases on multiple audio channels joining at
the same time.
The old, decoupled listen only mode is still present in code while we
validate this new approach. To test this, transparentListenOnly
must be enabled and listen only mode must be disable on audio join so
that the user skips straight through microphone join.
* Refactor: Make bundle using webpack
* Fix: restore after install codes and a few settings
* Fix: build script folder permission
* Refactor: Remove support to async import on audio bridges
* Upgrade npm using nvm
* Avoid questions on npm ci execution
* Let npm ci install dev dependencies (as we need the build tools here)
* Fix: enconding
* Fix: old lock files
* Remove: bbb-config dependency to bbb-html5 service, bbb-html5 isn't a service anymore
* Fix: TS errors
* Fix: eslint
* Fix: chat styles
* npm install with "lockfileVersion": 3 (newer npm)
* build: allow nodejs 22
* node 22; drop meteor from CI and bbb-conf
* TEMP: use bbb-install without mongo but with node 22 and newer image
* build: relax nodejs condition to not trip 22.6
* build: ensure dir /usr/share/bigbluebutton/nginx exists
* init sites-available/bbb; drop disable-transparent-
* nginx complaining of missing file and ;
* TMP: print status of services
* WIP: tweak nginx location to debug
* Fix: webcam widgets alignments
* akka-apps -- update location of settings.yml
* build: add locales path for nginx
* docs and config changes for removal of meteor
* Fix: build encoding and locales enpoint folder path
* build: set wss url for media
* Add: Enable minimizer and modify to Terser
* Fix: TS errors
---------
Co-authored-by: Tiago Jacobs <tiago.jacobs@gmail.com>
Co-authored-by: Anton Georgiev <anto.georgiev@gmail.com>
Co-authored-by: Anton Georgiev <antobinary@users.noreply.github.com>
* feat(webcams): skip video preview if valid input devices stored
Additionally:
- refactor: re-use the existing VirtualBackground_* storage info instead
of creating a new one
- fix: store background choices per deviceId instead of globally
- fix: guarantee background restore attempts are *critical* when
video-preview is supposed to be skipped. We want the preview to be
shown if the previous background could not be restored to preserver
the user's privacy choice
- fix: cameras could not be shared if no previous device info was in
the user's session
- fix: uploaded background images were not correctly restored
- fix: do not spin up virtual bg workers for brightness if it has not
been altered by the user
- refactor: remove old video-provider background restore routine,
centralize it in video-preview
* fix(skip-video-preview): correct storage check and add playwright test and docs
---------
Co-authored-by: prlanzarin <4529051+prlanzarin@users.noreply.github.com>
* Refactor: Remove the old user status (reaction/emoji)
* Update settings.yml
* test: remove old user status testing code related
---------
Co-authored-by: Anton B <antonbsa.bck@gmail.com>
* feat(html5): initial implementation of Gladia transcriptions to BBB 3.0
* fix(transcription): Add missing locales and fix invalid cc menu key
* fix(bbb-transcription-controller): Bump transcription controller to fix some bugs
* fix: adjust yq syntax for setting fs esl password in transctiption-controller
* fix(transcription): Use newer useSettings format from transcription options
* fix(captions): Correctly use captions settings
---------
Co-authored-by: João Victor <joaovictornunes973@gmail.com>
Co-authored-by: Anton Georgiev <anto.georgiev@gmail.com>
Co-authored-by: Ramón Souza <contato@ramonsouza.com>
* refactor(storage): replace Tracker.Dependency with observer hook
* fix(storage): set initial value
* refactor(storage): stop using Meteor's Session singleton