WebRTCWrapper.java

  1package eu.siacs.conversations.xmpp.jingle;
  2
  3import android.content.Context;
  4import android.util.Log;
  5
  6import com.google.common.collect.ImmutableList;
  7import com.google.common.util.concurrent.Futures;
  8import com.google.common.util.concurrent.ListenableFuture;
  9import com.google.common.util.concurrent.MoreExecutors;
 10import com.google.common.util.concurrent.SettableFuture;
 11
 12import org.webrtc.AudioSource;
 13import org.webrtc.AudioTrack;
 14import org.webrtc.Camera1Capturer;
 15import org.webrtc.Camera1Enumerator;
 16import org.webrtc.CameraVideoCapturer;
 17import org.webrtc.CandidatePairChangeEvent;
 18import org.webrtc.DataChannel;
 19import org.webrtc.IceCandidate;
 20import org.webrtc.MediaConstraints;
 21import org.webrtc.MediaStream;
 22import org.webrtc.PeerConnection;
 23import org.webrtc.PeerConnectionFactory;
 24import org.webrtc.RtpReceiver;
 25import org.webrtc.SdpObserver;
 26import org.webrtc.SessionDescription;
 27import org.webrtc.VideoCapturer;
 28import org.webrtc.VideoSource;
 29import org.webrtc.VideoTrack;
 30
 31import java.util.List;
 32
 33import javax.annotation.Nonnull;
 34import javax.annotation.Nullable;
 35
 36import eu.siacs.conversations.Config;
 37
 38public class WebRTCWrapper {
 39
 40    private VideoTrack localVideoTrack = null;
 41    private VideoTrack remoteVideoTrack = null;
 42
 43    private final EventCallback eventCallback;
 44
 45    private final PeerConnection.Observer peerConnectionObserver = new PeerConnection.Observer() {
 46        @Override
 47        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
 48            Log.d(Config.LOGTAG, "onSignalingChange(" + signalingState + ")");
 49
 50        }
 51
 52        @Override
 53        public void onConnectionChange(PeerConnection.PeerConnectionState newState) {
 54            eventCallback.onConnectionChange(newState);
 55        }
 56
 57        @Override
 58        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
 59
 60        }
 61
 62        @Override
 63        public void onSelectedCandidatePairChanged(CandidatePairChangeEvent event) {
 64            Log.d(Config.LOGTAG, "remote candidate selected: " + event.remote);
 65            Log.d(Config.LOGTAG, "local candidate selected: " + event.local);
 66        }
 67
 68        @Override
 69        public void onIceConnectionReceivingChange(boolean b) {
 70
 71        }
 72
 73        @Override
 74        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
 75
 76        }
 77
 78        @Override
 79        public void onIceCandidate(IceCandidate iceCandidate) {
 80            eventCallback.onIceCandidate(iceCandidate);
 81        }
 82
 83        @Override
 84        public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
 85
 86        }
 87
 88        @Override
 89        public void onAddStream(MediaStream mediaStream) {
 90            Log.d(Config.LOGTAG, "onAddStream");
 91            for (AudioTrack audioTrack : mediaStream.audioTracks) {
 92                Log.d(Config.LOGTAG, "remote? - audioTrack enabled:" + audioTrack.enabled() + " state=" + audioTrack.state());
 93            }
 94            final List<VideoTrack> videoTracks = mediaStream.videoTracks;
 95            if (videoTracks.size() > 0) {
 96                Log.d(Config.LOGTAG, "more than zero remote video tracks found. using first");
 97                remoteVideoTrack = videoTracks.get(0);
 98            }
 99        }
100
101        @Override
102        public void onRemoveStream(MediaStream mediaStream) {
103
104        }
105
106        @Override
107        public void onDataChannel(DataChannel dataChannel) {
108
109        }
110
111        @Override
112        public void onRenegotiationNeeded() {
113
114        }
115
116        @Override
117        public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
118            Log.d(Config.LOGTAG, "onAddTrack()");
119
120        }
121    };
122    @Nullable
123    private PeerConnection peerConnection = null;
124
125    public WebRTCWrapper(final EventCallback eventCallback) {
126        this.eventCallback = eventCallback;
127    }
128
129    public void setup(final Context context) {
130        PeerConnectionFactory.initialize(
131                PeerConnectionFactory.InitializationOptions.builder(context).createInitializationOptions()
132        );
133    }
134
135    public void initializePeerConnection() {
136        PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory();
137
138        CameraVideoCapturer capturer = null;
139        Camera1Enumerator camera1Enumerator = new Camera1Enumerator();
140        for (String deviceName : camera1Enumerator.getDeviceNames()) {
141            Log.d(Config.LOGTAG, "camera device name: " + deviceName);
142            if (camera1Enumerator.isFrontFacing(deviceName)) {
143                capturer = camera1Enumerator.createCapturer(deviceName, new CameraVideoCapturer.CameraEventsHandler() {
144                    @Override
145                    public void onCameraError(String s) {
146
147                    }
148
149                    @Override
150                    public void onCameraDisconnected() {
151
152                    }
153
154                    @Override
155                    public void onCameraFreezed(String s) {
156
157                    }
158
159                    @Override
160                    public void onCameraOpening(String s) {
161                        Log.d(Config.LOGTAG, "onCameraOpening");
162                    }
163
164                    @Override
165                    public void onFirstFrameAvailable() {
166                        Log.d(Config.LOGTAG, "onFirstFrameAvailable");
167                    }
168
169                    @Override
170                    public void onCameraClosed() {
171
172                    }
173                });
174            }
175        }
176
177        /*if (capturer != null) {
178            capturer.initialize();
179            Log.d(Config.LOGTAG,"start capturing");
180            capturer.startCapture(800,600,30);
181        }*/
182
183        final VideoSource videoSource = peerConnectionFactory.createVideoSource(false);
184        final VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("my-video-track", videoSource);
185
186        final AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
187
188        final AudioTrack audioTrack = peerConnectionFactory.createAudioTrack("my-audio-track", audioSource);
189        Log.d(Config.LOGTAG, "audioTrack enabled:" + audioTrack.enabled() + " state=" + audioTrack.state());
190        final MediaStream stream = peerConnectionFactory.createLocalMediaStream("my-media-stream");
191        stream.addTrack(audioTrack);
192        //stream.addTrack(videoTrack);
193
194        this.localVideoTrack = videoTrack;
195
196
197        final List<PeerConnection.IceServer> iceServers = ImmutableList.of(
198                PeerConnection.IceServer.builder("stun:xmpp.conversations.im:3478").createIceServer()
199        );
200        final PeerConnection peerConnection = peerConnectionFactory.createPeerConnection(iceServers, peerConnectionObserver);
201        if (peerConnection == null) {
202            throw new IllegalStateException("Unable to create PeerConnection");
203        }
204        peerConnection.addStream(stream);
205        peerConnection.setAudioPlayout(true);
206        peerConnection.setAudioRecording(true);
207        this.peerConnection = peerConnection;
208    }
209
210    public void closeOrThrow() {
211        requirePeerConnection().close();
212    }
213
214    public void close() {
215        final PeerConnection peerConnection = this.peerConnection;
216        if (peerConnection != null) {
217            peerConnection.close();
218        }
219    }
220
221
222    public ListenableFuture<SessionDescription> createOffer() {
223        return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
224            final SettableFuture<SessionDescription> future = SettableFuture.create();
225            peerConnection.createOffer(new CreateSdpObserver() {
226                @Override
227                public void onCreateSuccess(SessionDescription sessionDescription) {
228                    future.set(sessionDescription);
229                }
230
231                @Override
232                public void onCreateFailure(String s) {
233                    future.setException(new IllegalStateException("Unable to create offer: " + s));
234                }
235            }, new MediaConstraints());
236            return future;
237        }, MoreExecutors.directExecutor());
238    }
239
240    public ListenableFuture<SessionDescription> createAnswer() {
241        return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
242            final SettableFuture<SessionDescription> future = SettableFuture.create();
243            peerConnection.createAnswer(new CreateSdpObserver() {
244                @Override
245                public void onCreateSuccess(SessionDescription sessionDescription) {
246                    future.set(sessionDescription);
247                }
248
249                @Override
250                public void onCreateFailure(String s) {
251                    future.setException(new IllegalStateException("Unable to create answer: " + s));
252                }
253            }, new MediaConstraints());
254            return future;
255        }, MoreExecutors.directExecutor());
256    }
257
258    public ListenableFuture<Void> setLocalDescription(final SessionDescription sessionDescription) {
259        return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
260            final SettableFuture<Void> future = SettableFuture.create();
261            peerConnection.setLocalDescription(new SetSdpObserver() {
262                @Override
263                public void onSetSuccess() {
264                    future.set(null);
265                }
266
267                @Override
268                public void onSetFailure(String s) {
269                    future.setException(new IllegalArgumentException("unable to set local session description: " + s));
270
271                }
272            }, sessionDescription);
273            return future;
274        }, MoreExecutors.directExecutor());
275    }
276
277    public ListenableFuture<Void> setRemoteDescription(final SessionDescription sessionDescription) {
278        return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
279            final SettableFuture<Void> future = SettableFuture.create();
280            peerConnection.setRemoteDescription(new SetSdpObserver() {
281                @Override
282                public void onSetSuccess() {
283                    future.set(null);
284                }
285
286                @Override
287                public void onSetFailure(String s) {
288                    future.setException(new IllegalArgumentException("unable to set remote session description: " + s));
289
290                }
291            }, sessionDescription);
292            return future;
293        }, MoreExecutors.directExecutor());
294    }
295
296    @Nonnull
297    private ListenableFuture<PeerConnection> getPeerConnectionFuture() {
298        final PeerConnection peerConnection = this.peerConnection;
299        if (peerConnection == null) {
300            return Futures.immediateFailedFuture(new IllegalStateException("initialize PeerConnection first"));
301        } else {
302            return Futures.immediateFuture(peerConnection);
303        }
304    }
305
306    public void addIceCandidate(IceCandidate iceCandidate) {
307        requirePeerConnection().addIceCandidate(iceCandidate);
308    }
309
310    public PeerConnection.PeerConnectionState getState() {
311        return requirePeerConnection().connectionState();
312    }
313
314    private PeerConnection requirePeerConnection() {
315        final PeerConnection peerConnection = this.peerConnection;
316        if (peerConnection == null) {
317            throw new IllegalStateException("initialize PeerConnection first");
318        }
319        return peerConnection;
320    }
321
322    private static abstract class SetSdpObserver implements SdpObserver {
323
324        @Override
325        public void onCreateSuccess(org.webrtc.SessionDescription sessionDescription) {
326            throw new IllegalStateException("Not able to use SetSdpObserver");
327        }
328
329        @Override
330        public void onCreateFailure(String s) {
331            throw new IllegalStateException("Not able to use SetSdpObserver");
332        }
333
334    }
335
336    private static abstract class CreateSdpObserver implements SdpObserver {
337
338
339        @Override
340        public void onSetSuccess() {
341            throw new IllegalStateException("Not able to use CreateSdpObserver");
342        }
343
344
345        @Override
346        public void onSetFailure(String s) {
347            throw new IllegalStateException("Not able to use CreateSdpObserver");
348        }
349    }
350
351    public interface EventCallback {
352        void onIceCandidate(IceCandidate iceCandidate);
353
354        void onConnectionChange(PeerConnection.PeerConnectionState newState);
355    }
356}