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