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
204
205 public ListenableFuture<SessionDescription> createOffer() {
206 return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
207 final SettableFuture<SessionDescription> future = SettableFuture.create();
208 peerConnection.createOffer(new CreateSdpObserver() {
209 @Override
210 public void onCreateSuccess(SessionDescription sessionDescription) {
211 future.set(sessionDescription);
212 }
213
214 @Override
215 public void onCreateFailure(String s) {
216 future.setException(new IllegalStateException("Unable to create offer: " + s));
217 }
218 }, new MediaConstraints());
219 return future;
220 }, MoreExecutors.directExecutor());
221 }
222
223 public ListenableFuture<SessionDescription> createAnswer() {
224 return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
225 final SettableFuture<SessionDescription> future = SettableFuture.create();
226 peerConnection.createAnswer(new CreateSdpObserver() {
227 @Override
228 public void onCreateSuccess(SessionDescription sessionDescription) {
229 future.set(sessionDescription);
230 }
231
232 @Override
233 public void onCreateFailure(String s) {
234 future.setException(new IllegalStateException("Unable to create answer: " + s));
235 }
236 }, new MediaConstraints());
237 return future;
238 }, MoreExecutors.directExecutor());
239 }
240
241 public ListenableFuture<Void> setLocalDescription(final SessionDescription sessionDescription) {
242 return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
243 final SettableFuture<Void> future = SettableFuture.create();
244 peerConnection.setLocalDescription(new SetSdpObserver() {
245 @Override
246 public void onSetSuccess() {
247 future.set(null);
248 }
249
250 @Override
251 public void onSetFailure(String s) {
252 future.setException(new IllegalArgumentException("unable to set local session description: " + s));
253
254 }
255 }, sessionDescription);
256 return future;
257 }, MoreExecutors.directExecutor());
258 }
259
260 public ListenableFuture<Void> setRemoteDescription(final SessionDescription sessionDescription) {
261 return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
262 final SettableFuture<Void> future = SettableFuture.create();
263 peerConnection.setRemoteDescription(new SetSdpObserver() {
264 @Override
265 public void onSetSuccess() {
266 future.set(null);
267 }
268
269 @Override
270 public void onSetFailure(String s) {
271 future.setException(new IllegalArgumentException("unable to set remote session description: " + s));
272
273 }
274 }, sessionDescription);
275 return future;
276 }, MoreExecutors.directExecutor());
277 }
278
279 @Nonnull
280 private ListenableFuture<PeerConnection> getPeerConnectionFuture() {
281 final PeerConnection peerConnection = this.peerConnection;
282 if (peerConnection == null) {
283 return Futures.immediateFailedFuture(new IllegalStateException("initialize PeerConnection first"));
284 } else {
285 return Futures.immediateFuture(peerConnection);
286 }
287 }
288
289 public void addIceCandidate(IceCandidate iceCandidate) {
290 final PeerConnection peerConnection = this.peerConnection;
291 if (peerConnection == null) {
292 throw new IllegalStateException("initialize PeerConnection first");
293 }
294 peerConnection.addIceCandidate(iceCandidate);
295 }
296
297 public PeerConnection.PeerConnectionState getState() {
298 return this.peerConnection.connectionState();
299 }
300
301 private static abstract class SetSdpObserver implements SdpObserver {
302
303 @Override
304 public void onCreateSuccess(org.webrtc.SessionDescription sessionDescription) {
305 throw new IllegalStateException("Not able to use SetSdpObserver");
306 }
307
308 @Override
309 public void onCreateFailure(String s) {
310 throw new IllegalStateException("Not able to use SetSdpObserver");
311 }
312
313 }
314
315 private static abstract class CreateSdpObserver implements SdpObserver {
316
317
318 @Override
319 public void onSetSuccess() {
320 throw new IllegalStateException("Not able to use CreateSdpObserver");
321 }
322
323
324 @Override
325 public void onSetFailure(String s) {
326 throw new IllegalStateException("Not able to use CreateSdpObserver");
327 }
328 }
329
330 public interface EventCallback {
331 void onIceCandidate(IceCandidate iceCandidate);
332 void onConnectionChange(PeerConnection.PeerConnectionState newState);
333 }
334}