refactor WebRTCWrapper to allow for track adds

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xmpp/jingle/VideoSourceWrapper.java |  12 
src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java      | 102 
2 files changed, 80 insertions(+), 34 deletions(-)

Detailed changes

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<VideoSourceWrapper> create() {
+        public VideoSourceWrapper create() {
             final CameraEnumerator enumerator = new Camera2Enumerator(context);
             final Set<String> 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);
             }
         }
 

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<VideoSourceWrapper> 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<PeerConnection.IceServer> 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);
         }
     }
-
 }