The solution advertised by @hiroshihorie of subscribing to an audio track via RemoteAudioTrack.add(audioRenderer:) doesn't work as expected as the wrapped object AudioRendererAdapter gets released immediately due to the memory semantics of WebRTC.
Specifically, RemoteAudioTrack internally calls audioTrack.add(AudioRendererAdapter(target: audioRenderer)), which wraps the supplied audio renderer in an adapter and sends it to the LKRTCAudioTrack object.
This adapter, however, is not retained by anyone and thus released immediately. See the code documentation Does not retain. in RTCAudioTrack.h.
As a fix, I would suggest either of the 2 solutions:
Return a type-erased object from add(audioRenderer:) which the caller is responsible for keeping a reference to. This would mimic the memory semantics of WebRTC, but adds complexity on the integrator's side.
Store references to the created adapter objects inside of RemoteAudioTrack until remove(audioRenderer:) is called. This comes with the added complexity of removing adapters when their internal target is deallocated (i.e. the renderer which the caller supplied).
As a side note to No 2, reading through the RemoteAudioTrack.remove(audioRenderer:) implementation, I highly suspect this doesn't work because it creates a new adapter and tries to remove that from audioTrack. I haven't tested this myself, but I think what should happen here is the following:
Look up the adapter associated with the audioRenderer object passed into remove(audioRenderer:)
Call audioTrack.remove() with that adapter (which was previously created in add(audioRenderer:))
SDK Version
2.0.4
iOS/macOS Version
iOS
Steps to Reproduce
Implement a type that conforms to the AudioRenderer protocol
Create an instance of that type and keep a reference to it for the lifecycle of your session/the room
Grab any incoming audio tracks via the room delegate's room(_ room:participant:didSubscribeTrack:) callback
Call add(audioRenderer:) on the participant's audio track to add the previously created audio renderer
Expected behaviorrender(sampleBuffer:) of my AudioRenderer implementation should be called.
Describe the bug This is a follow-up to https://github.com/livekit/client-sdk-swift/issues/188.
The solution advertised by @hiroshihorie of subscribing to an audio track via
RemoteAudioTrack.add(audioRenderer:)
doesn't work as expected as the wrapped objectAudioRendererAdapter
gets released immediately due to the memory semantics of WebRTC.Specifically,
RemoteAudioTrack
internally callsaudioTrack.add(AudioRendererAdapter(target: audioRenderer))
, which wraps the supplied audio renderer in an adapter and sends it to theLKRTCAudioTrack
object. This adapter, however, is not retained by anyone and thus released immediately. See the code documentationDoes not retain.
inRTCAudioTrack.h
.As a fix, I would suggest either of the 2 solutions:
Return a type-erased object from
add(audioRenderer:)
which the caller is responsible for keeping a reference to. This would mimic the memory semantics of WebRTC, but adds complexity on the integrator's side.Store references to the created adapter objects inside of
RemoteAudioTrack
untilremove(audioRenderer:)
is called. This comes with the added complexity of removing adapters when their internaltarget
is deallocated (i.e. the renderer which the caller supplied).As a side note to No 2, reading through the
RemoteAudioTrack.remove(audioRenderer:)
implementation, I highly suspect this doesn't work because it creates a new adapter and tries to remove that fromaudioTrack
. I haven't tested this myself, but I think what should happen here is the following:audioRenderer
object passed intoremove(audioRenderer:)
audioTrack.remove()
with that adapter (which was previously created inadd(audioRenderer:)
)SDK Version 2.0.4
iOS/macOS Version iOS
Steps to Reproduce
AudioRenderer
protocolroom(_ room:participant:didSubscribeTrack:)
callbackadd(audioRenderer:)
on theparticipant
's audio track to add the previously created audio rendererExpected behavior
render(sampleBuffer:)
of myAudioRenderer
implementation should be called.Actual behavior Nothing happens.