client-sdk-js
client-sdk-js copied to clipboard
AudioPlaybackStatus gets fired multiple times even after invoking startAudio()
Describe the bug
Im expecting audioPlaybackStatusChanged
to not fire after calling room.startAudio()
on user click. But audioPlaybackStatusChanged
keeps firing on each new participant joined.
Reproduction
Happens in MS Edge Mobile on https://example.livekit.io/ whenever a new participant joins
Logs
chunk-vendors.d8055e62.js:316 room event {event: 'participantConnected', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'trackPublished', args: Array(2)}
chunk-vendors.d8055e62.js:316 room event {event: 'connectionQualityChanged', args: Array(2)}
chunk-vendors.d8055e62.js:316 received server offer {RTCSdpType: 'offer', signalingState: 'stable'}
chunk-vendors.d8055e62.js:316 room event {event: 'trackSubscriptionPermissionChanged', args: Array(3)}
chunk-vendors.d8055e62.js:316 room event {event: 'trackSubscribed', args: Array(3)}
app.a2ae327e.js:1 trackSubscribed Ba {_events: {…}, _eventsCount: 12, _maxListeners: undefined, audioLevel: 0, isSpeaking: false, …} Ha {_events: {…}, _eventsCount: 7, _maxListeners: undefined, metadataMuted: false, handleMuted: ƒ, …}
chunk-vendors.d8055e62.js:316 could not playback audio DOMException: play() can only be initiated by a user gesture.
Ns.handleAudioPlaybackFailed @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Promise.catch (async)
attach @ chunk-vendors.d8055e62.js:316
attach @ chunk-vendors.d8055e62.js:316
mounted @ app.a2ae327e.js:1
In @ chunk-vendors.d8055e62.js:63
Ni @ chunk-vendors.d8055e62.js:63
insert @ chunk-vendors.d8055e62.js:63
O @ chunk-vendors.d8055e62.js:63
(anonymous) @ chunk-vendors.d8055e62.js:63
Oi.e._update @ chunk-vendors.d8055e62.js:63
i @ chunk-vendors.d8055e62.js:63
e.get @ chunk-vendors.d8055e62.js:63
e.run @ chunk-vendors.d8055e62.js:63
Gi @ chunk-vendors.d8055e62.js:63
(anonymous) @ chunk-vendors.d8055e62.js:63
Vn @ chunk-vendors.d8055e62.js:63
Promise.then (async)
Hn @ chunk-vendors.d8055e62.js:63
Kn @ chunk-vendors.d8055e62.js:63
Xi @ chunk-vendors.d8055e62.js:63
e.update @ chunk-vendors.d8055e62.js:63
e.notify @ chunk-vendors.d8055e62.js:63
set @ chunk-vendors.d8055e62.js:63
(anonymous) @ app.a2ae327e.js:1
u @ chunk-vendors.d8055e62.js:216
(anonymous) @ chunk-vendors.d8055e62.js:216
(anonymous) @ chunk-vendors.d8055e62.js:216
a @ chunk-vendors.d8055e62.js:79
s @ chunk-vendors.d8055e62.js:79
(anonymous) @ chunk-vendors.d8055e62.js:79
t @ chunk-vendors.d8055e62.js:121
(anonymous) @ chunk-vendors.d8055e62.js:79
t @ app.a2ae327e.js:1
(anonymous) @ app.a2ae327e.js:1
r.emitEvent @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
emit @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
emit @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
setTrack @ chunk-vendors.d8055e62.js:316
addSubscribedMediaTrack @ chunk-vendors.d8055e62.js:316
onTrackAdded @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
cr.subscriber.pc.ontrack @ chunk-vendors.d8055e62.js:316
a @ chunk-vendors.d8055e62.js:316
a @ chunk-vendors.d8055e62.js:316
Show 23 more frames
chunk-vendors.d8055e62.js:316 room event {event: 'audioPlaybackChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 sending answer
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'audioPlaybackChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'trackMuted', args: Array(2)}
chunk-vendors.d8055e62.js:316 room event {event: 'participantConnected', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'trackPublished', args: Array(2)}
chunk-vendors.d8055e62.js:316 room event {event: 'connectionQualityChanged', args: Array(2)}
chunk-vendors.d8055e62.js:316 received server offer {RTCSdpType: 'offer', signalingState: 'stable'}
chunk-vendors.d8055e62.js:316 room event {event: 'trackSubscriptionPermissionChanged', args: Array(3)}
chunk-vendors.d8055e62.js:316 room event {event: 'trackSubscribed', args: Array(3)}
app.a2ae327e.js:1 trackSubscribed Ba {_events: {…}, _eventsCount: 12, _maxListeners: undefined, audioLevel: 0, isSpeaking: false, …} Ha {_events: {…}, _eventsCount: 7, _maxListeners: undefined, metadataMuted: false, handleMuted: ƒ, …}
chunk-vendors.d8055e62.js:316 could not playback audio DOMException: play() can only be initiated by a user gesture.
Ns.handleAudioPlaybackFailed @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Promise.catch (async)
attach @ chunk-vendors.d8055e62.js:316
attach @ chunk-vendors.d8055e62.js:316
mounted @ app.a2ae327e.js:1
In @ chunk-vendors.d8055e62.js:63
Ni @ chunk-vendors.d8055e62.js:63
insert @ chunk-vendors.d8055e62.js:63
O @ chunk-vendors.d8055e62.js:63
(anonymous) @ chunk-vendors.d8055e62.js:63
Oi.e._update @ chunk-vendors.d8055e62.js:63
i @ chunk-vendors.d8055e62.js:63
e.get @ chunk-vendors.d8055e62.js:63
e.run @ chunk-vendors.d8055e62.js:63
Gi @ chunk-vendors.d8055e62.js:63
(anonymous) @ chunk-vendors.d8055e62.js:63
Vn @ chunk-vendors.d8055e62.js:63
Promise.then (async)
Hn @ chunk-vendors.d8055e62.js:63
Kn @ chunk-vendors.d8055e62.js:63
Xi @ chunk-vendors.d8055e62.js:63
e.update @ chunk-vendors.d8055e62.js:63
e.notify @ chunk-vendors.d8055e62.js:63
set @ chunk-vendors.d8055e62.js:63
(anonymous) @ app.a2ae327e.js:1
u @ chunk-vendors.d8055e62.js:216
(anonymous) @ chunk-vendors.d8055e62.js:216
(anonymous) @ chunk-vendors.d8055e62.js:216
a @ chunk-vendors.d8055e62.js:79
s @ chunk-vendors.d8055e62.js:79
(anonymous) @ chunk-vendors.d8055e62.js:79
t @ chunk-vendors.d8055e62.js:121
(anonymous) @ chunk-vendors.d8055e62.js:79
t @ app.a2ae327e.js:1
(anonymous) @ app.a2ae327e.js:1
r.emitEvent @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
emit @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
emit @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
setTrack @ chunk-vendors.d8055e62.js:316
addSubscribedMediaTrack @ chunk-vendors.d8055e62.js:316
onTrackAdded @ chunk-vendors.d8055e62.js:316
(anonymous) @ chunk-vendors.d8055e62.js:316
Or.emit @ chunk-vendors.d8055e62.js:316
cr.subscriber.pc.ontrack @ chunk-vendors.d8055e62.js:316
a @ chunk-vendors.d8055e62.js:316
a @ chunk-vendors.d8055e62.js:316
Show 23 more frames
chunk-vendors.d8055e62.js:316 room event {event: 'audioPlaybackChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 sending answer
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'audioPlaybackChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
chunk-vendors.d8055e62.js:316 room event {event: 'activeSpeakersChanged', args: Array(1)}
System Info
Android 12, Microsoft edge
Severity
blocking all usage of LiveKit
Additional Information
No response
MS Edge on mobile seem to have strict policy about when an audio element can be autoplayed. From what I can gather, any new audio element that's created after the user has given autoplay consent will need to be granted explicitly again.
The following case does not require reconsent:
- after approval, existing user leaves and rejoins
- after approval, existing user leaves and another joins
These cases work because we recycle audio elements for this reason.
The following case does require reconsent:
- after approval, an additional user joins (this involves a new audio element to be created)
Since this is an Edge security feature, I'm unsure if there's anything we can do about it.
maybe the work in https://github.com/livekit/client-sdk-js/pull/446 could serve as a workaround for this issue on edge, although not sure if that makes a difference at all if the autoplay is blocked on the element level.
that's a good idea! @karthikSirsikar Do you want to give this a try?
- run against pre-release main
- pass this option in when creating the room
expWebAudioMix: true
Yes, this seems to work on MS Edge android but not on IOS Safari
on which iOS version do you see this failing? (and is there any error being thrown?)
15.4.1 on IPad
This is the error: NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission
this sounds like you didn't allow the website to use microphone/camera!
make sure to test it on a secure host, it probably won't work for iOS if you use http
instead of https
@karthikSirsikar does it work for you ? just tried to reproduce the issue you mentioned (before deleting) when not publishing, but couldn't reproduce it
Sorry, that was misinterpreted. I had disabled expWebAudioMix for IOS and had only enabled it for android devices.
But now the issue is on android, the audio is going into loop. Should i skip attaching track to audio tag when expWebAudioMix is enabled
hm. it shouldn't create a feedback loop when calling attach
. does the feedback loop also happen when running the sample ? (the sample also uses attach
)
i meant the one in this repo, started with yarn sample
Yes feedback loop is happening. I enabled expWebAudioMix in the sample and tried
Coming back to this: Could it be that what you were seeing was the fact that echoCancellation
doesn't work with webAudio?
Or worded differently: does the loopback also happen when using headphones on the phone?
for reference, this is the chromium bug (the issue mentions Android explicitly as well) https://bugs.chromium.org/p/chromium/issues/detail?id=687574#c60 sounds like the fix will be enabled by default soon in chrome!
expWebAudioMix
does seem to solve the original issue for edge, feel free to open a new issue for the feedback loop in case this is still happening.