From 27d8da2ab4e927a4b81c266a2501ed666eaedd4b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 19 Nov 2022 13:03:34 +0100 Subject: [PATCH] refactor WebRTCWrapper to allow for track adds --- .../xmpp/jingle/VideoSourceWrapper.java | 12 +-- .../xmpp/jingle/WebRTCWrapper.java | 102 +++++++++++++----- 2 files changed, 80 insertions(+), 34 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java index 5e83f2ba9..b837131e8 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java @@ -3,7 +3,6 @@ package eu.siacs.conversations.xmpp.jingle; import android.content.Context; import android.util.Log; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListenableFuture; @@ -127,7 +126,7 @@ class VideoSourceWrapper { this.context = context; } - public Optional create() { + public VideoSourceWrapper create() { final CameraEnumerator enumerator = new Camera2Enumerator(context); final Set deviceNames = ImmutableSet.copyOf(enumerator.getDeviceNames()); for (final String deviceName : deviceNames) { @@ -135,17 +134,16 @@ class VideoSourceWrapper { final VideoSourceWrapper videoSourceWrapper = of(enumerator, deviceName, deviceNames); if (videoSourceWrapper == null) { - return Optional.absent(); + return null; } videoSourceWrapper.isFrontCamera = true; - return Optional.of(videoSourceWrapper); + return videoSourceWrapper; } } if (deviceNames.size() == 0) { - return Optional.absent(); + return null; } else { - return Optional.fromNullable( - of(enumerator, Iterables.get(deviceNames, 0), deviceNames)); + return of(enumerator, Iterables.get(deviceNames, 0), deviceNames); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java index f71799bdf..4ce9c1478 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java @@ -196,6 +196,7 @@ public class WebRTCWrapper { + ")"); } }; + @Nullable private PeerConnectionFactory peerConnectionFactory = null; @Nullable private PeerConnection peerConnection = null; private AppRTCAudioManager appRTCAudioManager = null; private ToneManager toneManager = null; @@ -260,7 +261,7 @@ public class WebRTCWrapper { String.format( "setUseHardwareAcousticEchoCanceler(%s) model=%s", setUseHardwareAcousticEchoCanceler, Build.MODEL)); - PeerConnectionFactory peerConnectionFactory = + this.peerConnectionFactory = PeerConnectionFactory.builder() .setVideoDecoderFactory( new DefaultVideoDecoderFactory(eglBase.getEglBaseContext())) @@ -268,7 +269,7 @@ public class WebRTCWrapper { new DefaultVideoEncoderFactory( eglBase.getEglBaseContext(), true, true)) .setAudioDeviceModule( - JavaAudioDeviceModule.builder(context) + JavaAudioDeviceModule.builder(requireContext()) .setUseHardwareAcousticEchoCanceler( setUseHardwareAcousticEchoCanceler) .createAudioDeviceModule()) @@ -276,36 +277,18 @@ public class WebRTCWrapper { final PeerConnection.RTCConfiguration rtcConfig = buildConfiguration(iceServers); final PeerConnection peerConnection = - peerConnectionFactory.createPeerConnection(rtcConfig, peerConnectionObserver); + requirePeerConnectionFactory() + .createPeerConnection(rtcConfig, peerConnectionObserver); if (peerConnection == null) { throw new InitializationException("Unable to create PeerConnection"); } - final Optional optionalVideoSourceWrapper = - media.contains(Media.VIDEO) - ? new VideoSourceWrapper.Factory(requireContext()).create() - : Optional.absent(); - - if (optionalVideoSourceWrapper.isPresent()) { - this.videoSourceWrapper = optionalVideoSourceWrapper.get(); - this.videoSourceWrapper.initialize( - peerConnectionFactory, context, eglBase.getEglBaseContext()); - this.videoSourceWrapper.startCapture(); - - final VideoTrack videoTrack = - peerConnectionFactory.createVideoTrack( - "my-video-track", this.videoSourceWrapper.getVideoSource()); - - this.localVideoTrack = TrackWrapper.addTrack(peerConnection, videoTrack); + if (media.contains(Media.VIDEO)) { + addVideoTrack(peerConnection); } if (media.contains(Media.AUDIO)) { - // set up audio track - final AudioSource audioSource = - peerConnectionFactory.createAudioSource(new MediaConstraints()); - final AudioTrack audioTrack = - peerConnectionFactory.createAudioTrack("my-audio-track", audioSource); - this.localAudioTrack = TrackWrapper.addTrack(peerConnection, audioTrack); + addAudioTrack(peerConnection); } peerConnection.setAudioPlayout(true); peerConnection.setAudioRecording(true); @@ -313,6 +296,58 @@ public class WebRTCWrapper { this.peerConnection = peerConnection; } + private VideoSourceWrapper initializeVideoSourceWrapper() { + final VideoSourceWrapper existingVideoSourceWrapper = this.videoSourceWrapper; + if (existingVideoSourceWrapper != null) { + existingVideoSourceWrapper.startCapture(); + return existingVideoSourceWrapper; + } + final VideoSourceWrapper videoSourceWrapper = + new VideoSourceWrapper.Factory(requireContext()).create(); + if (videoSourceWrapper == null) { + throw new IllegalStateException("Could not instantiate VideoSourceWrapper"); + } + videoSourceWrapper.initialize( + requirePeerConnectionFactory(), requireContext(), eglBase.getEglBaseContext()); + videoSourceWrapper.startCapture(); + return videoSourceWrapper; + } + + public synchronized boolean addTrack(final Media media) { + if (media == Media.VIDEO) { + return addVideoTrack(requirePeerConnection()); + } else if (media == Media.AUDIO) { + return addAudioTrack(requirePeerConnection()); + } + throw new IllegalStateException(String.format("Could not add track for %s", media)); + } + + private boolean addAudioTrack(final PeerConnection peerConnection) { + final AudioSource audioSource = + requirePeerConnectionFactory().createAudioSource(new MediaConstraints()); + final AudioTrack audioTrack = + requirePeerConnectionFactory().createAudioTrack("my-audio-track", audioSource); + this.localAudioTrack = TrackWrapper.addTrack(peerConnection, audioTrack); + return true; + } + + private boolean addVideoTrack(final PeerConnection peerConnection) { + Preconditions.checkState( + this.localVideoTrack == null, "A local video track already exists"); + final VideoSourceWrapper videoSourceWrapper; + try { + videoSourceWrapper = initializeVideoSourceWrapper(); + } catch (final IllegalStateException e) { + Log.d(Config.LOGTAG, "could not add video track", e); + return false; + } + final VideoTrack videoTrack = + requirePeerConnectionFactory() + .createVideoTrack("my-video-track", videoSourceWrapper.getVideoSource()); + this.localVideoTrack = TrackWrapper.addTrack(peerConnection, videoTrack); + return true; + } + private static PeerConnection.RTCConfiguration buildConfiguration( final List iceServers) { final PeerConnection.RTCConfiguration rtcConfig = @@ -344,6 +379,7 @@ public class WebRTCWrapper { synchronized void close() { final PeerConnection peerConnection = this.peerConnection; + final PeerConnectionFactory peerConnectionFactory = this.peerConnectionFactory; final VideoSourceWrapper videoSourceWrapper = this.videoSourceWrapper; final AppRTCAudioManager audioManager = this.appRTCAudioManager; final EglBase eglBase = this.eglBase; @@ -363,12 +399,15 @@ public class WebRTCWrapper { } catch (final InterruptedException e) { Log.e(Config.LOGTAG, "unable to stop capturing"); } - // TODO call dispose + videoSourceWrapper.dispose(); } if (eglBase != null) { eglBase.release(); this.eglBase = null; } + if (peerConnectionFactory != null) { + peerConnectionFactory.dispose(); + } } synchronized void verifyClosed() { @@ -530,6 +569,7 @@ public class WebRTCWrapper { } } + @Nonnull private PeerConnection requirePeerConnection() { final PeerConnection peerConnection = this.peerConnection; if (peerConnection == null) { @@ -538,6 +578,15 @@ public class WebRTCWrapper { return peerConnection; } + @Nonnull + private PeerConnectionFactory requirePeerConnectionFactory() { + final PeerConnectionFactory peerConnectionFactory = this.peerConnectionFactory; + if (peerConnectionFactory == null) { + throw new IllegalStateException("Make sure PeerConnectionFactory is initialized"); + } + return peerConnectionFactory; + } + void addIceCandidate(IceCandidate iceCandidate) { requirePeerConnection().addIceCandidate(iceCandidate); } @@ -626,5 +675,4 @@ public class WebRTCWrapper { super(message); } } - }