That fuck shit the fascists are using
1package org.tm.archive.ringrtc;
2
3import android.content.Context;
4import android.hardware.camera2.CameraAccessException;
5import android.hardware.camera2.CameraCharacteristics;
6import android.hardware.camera2.CameraManager;
7import android.hardware.camera2.CameraMetadata;
8
9import androidx.annotation.NonNull;
10import androidx.annotation.Nullable;
11
12import com.annimon.stream.Stream;
13
14import org.signal.core.util.logging.Log;
15import org.signal.ringrtc.CameraControl;
16import org.tm.archive.components.webrtc.EglBaseWrapper;
17import org.webrtc.Camera1Enumerator;
18import org.webrtc.Camera2Capturer;
19import org.webrtc.Camera2Enumerator;
20import org.webrtc.CameraEnumerator;
21import org.webrtc.CameraVideoCapturer;
22import org.webrtc.CapturerObserver;
23import org.webrtc.SurfaceTextureHelper;
24import org.webrtc.VideoFrame;
25
26import java.util.LinkedList;
27import java.util.List;
28
29import static org.tm.archive.ringrtc.CameraState.Direction.BACK;
30import static org.tm.archive.ringrtc.CameraState.Direction.FRONT;
31import static org.tm.archive.ringrtc.CameraState.Direction.NONE;
32import static org.tm.archive.ringrtc.CameraState.Direction.PENDING;
33
34/**
35 * Encapsulate the camera functionality needed for video calling.
36 */
37public class Camera implements CameraControl, CameraVideoCapturer.CameraSwitchHandler {
38
39 private static final String TAG = Log.tag(Camera.class);
40
41 @NonNull private final Context context;
42 @Nullable private final CameraVideoCapturer capturer;
43 @Nullable private CameraEventListener cameraEventListener;
44 @NonNull private final EglBaseWrapper eglBase;
45 private final int cameraCount;
46 @NonNull private CameraState.Direction activeDirection;
47 private boolean enabled;
48 private boolean isInitialized;
49 private int orientation;
50
51 public Camera(@NonNull Context context,
52 @NonNull CameraEventListener cameraEventListener,
53 @NonNull EglBaseWrapper eglBase,
54 @NonNull CameraState.Direction desiredCameraDirection)
55 {
56 this.context = context;
57 this.cameraEventListener = cameraEventListener;
58 this.eglBase = eglBase;
59 CameraEnumerator enumerator = getCameraEnumerator(context);
60 cameraCount = enumerator.getDeviceNames().length;
61
62 CameraState.Direction firstChoice = desiredCameraDirection.isUsable() ? desiredCameraDirection : FRONT;
63
64 CameraVideoCapturer capturerCandidate = createVideoCapturer(enumerator, firstChoice);
65 if (capturerCandidate != null) {
66 activeDirection = firstChoice;
67 } else {
68 CameraState.Direction secondChoice = firstChoice.switchDirection();
69 capturerCandidate = createVideoCapturer(enumerator, secondChoice);
70 if (capturerCandidate != null) {
71 activeDirection = secondChoice;
72 } else {
73 activeDirection = NONE;
74 }
75 }
76 capturer = capturerCandidate;
77 }
78
79 @Override
80 public void initCapturer(@NonNull CapturerObserver observer) {
81 if (capturer != null) {
82 eglBase.performWithValidEglBase(base -> {
83 capturer.initialize(SurfaceTextureHelper.create("WebRTC-SurfaceTextureHelper", base.getEglBaseContext()),
84 context,
85 new CameraCapturerWrapper(observer));
86 capturer.setOrientation(orientation);
87 isInitialized = true;
88 });
89 }
90 }
91
92 @Override
93 public boolean hasCapturer() {
94 return capturer != null;
95 }
96
97 @Override
98 public void flip() {
99 if (capturer == null || cameraCount < 2) {
100 throw new AssertionError("Tried to flip the camera, but we only have " + cameraCount + " of them.");
101 }
102 activeDirection = PENDING;
103 capturer.switchCamera(this);
104 }
105
106 @Override
107 public void setOrientation(@Nullable Integer orientation) {
108 this.orientation = orientation;
109
110 if (isInitialized && capturer != null) {
111 capturer.setOrientation(orientation);
112 }
113 }
114
115 @Override
116 public void setEnabled(boolean enabled) {
117 Log.i(TAG, "setEnabled(): " + enabled);
118
119 this.enabled = enabled;
120
121 if (capturer == null) {
122 return;
123 }
124
125 try {
126 if (enabled) {
127 Log.i(TAG, "setEnabled(): starting capture");
128 capturer.startCapture(1280, 720, 30);
129 } else {
130 Log.i(TAG, "setEnabled(): stopping capture");
131 capturer.stopCapture();
132 }
133 } catch (InterruptedException e) {
134 Log.w(TAG, "Got interrupted while trying to stop video capture", e);
135 }
136 }
137
138 public void setCameraEventListener(@Nullable CameraEventListener cameraEventListener) {
139 this.cameraEventListener = cameraEventListener;
140 }
141
142 public void dispose() {
143 if (capturer != null) {
144 capturer.dispose();
145 isInitialized = false;
146 }
147 }
148
149 int getCount() {
150 return cameraCount;
151 }
152
153 @NonNull CameraState.Direction getActiveDirection() {
154 return enabled ? activeDirection : NONE;
155 }
156
157 @NonNull public CameraState getCameraState() {
158 return new CameraState(getActiveDirection(), getCount());
159 }
160
161 @Nullable CameraVideoCapturer getCapturer() {
162 return capturer;
163 }
164
165 public boolean isInitialized() {
166 return isInitialized;
167 }
168
169 private @Nullable CameraVideoCapturer createVideoCapturer(@NonNull CameraEnumerator enumerator,
170 @NonNull CameraState.Direction direction)
171 {
172 String[] deviceNames = enumerator.getDeviceNames();
173 for (String deviceName : deviceNames) {
174 if ((direction == FRONT && enumerator.isFrontFacing(deviceName)) ||
175 (direction == BACK && enumerator.isBackFacing(deviceName)))
176 {
177 return enumerator.createCapturer(deviceName, null);
178 }
179 }
180
181 return null;
182 }
183
184 private @NonNull CameraEnumerator getCameraEnumerator(@NonNull Context context) {
185 boolean camera2EnumeratorIsSupported = false;
186 try {
187 camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(context);
188 } catch (final Throwable throwable) {
189 Log.w(TAG, "Camera2Enumator.isSupport() threw.", throwable);
190 }
191
192 Log.i(TAG, "Camera2 enumerator supported: " + camera2EnumeratorIsSupported);
193
194 return camera2EnumeratorIsSupported ? new FilteredCamera2Enumerator(context)
195 : new Camera1Enumerator(true);
196 }
197
198 @Override
199 public void onCameraSwitchDone(boolean isFrontFacing) {
200 activeDirection = isFrontFacing ? FRONT : BACK;
201 if (cameraEventListener != null) cameraEventListener.onCameraSwitchCompleted(new CameraState(getActiveDirection(), getCount()));
202 }
203
204 @Override
205 public void onCameraSwitchError(String errorMessage) {
206 Log.e(TAG, "onCameraSwitchError: " + errorMessage);
207 if (cameraEventListener != null) cameraEventListener.onCameraSwitchCompleted(new CameraState(getActiveDirection(), getCount()));
208 }
209
210 private static class FilteredCamera2Enumerator extends Camera2Enumerator {
211
212 private static final String TAG = Log.tag(Camera2Enumerator.class);
213
214 @NonNull private final Context context;
215 @Nullable private final CameraManager cameraManager;
216 @Nullable private String[] deviceNames;
217
218 FilteredCamera2Enumerator(@NonNull Context context) {
219 super(context);
220
221 this.context = context;
222 this.cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
223 this.deviceNames = null;
224 }
225
226 private static boolean isMonochrome(@NonNull String deviceName, @NonNull CameraManager cameraManager) {
227 try {
228 CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(deviceName);
229 int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
230
231 if (capabilities != null) {
232 for (int capability : capabilities) {
233 if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) {
234 return true;
235 }
236 }
237 }
238 } catch (CameraAccessException e) {
239 return false;
240 }
241
242 return false;
243 }
244
245 private static boolean isLensFacing(@NonNull String deviceName, @NonNull CameraManager cameraManager, @NonNull Integer facing) {
246 try {
247 CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(deviceName);
248 Integer lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
249
250 return facing.equals(lensFacing);
251 } catch (CameraAccessException e) {
252 return false;
253 }
254 }
255
256 @Override
257 public @NonNull String[] getDeviceNames() {
258 if (deviceNames != null) {
259 return deviceNames;
260 }
261
262 try {
263 List<String> cameraList = new LinkedList<>();
264
265 if (cameraManager != null) {
266 List<String> devices = Stream.of(cameraManager.getCameraIdList())
267 .filterNot(id -> isMonochrome(id, cameraManager))
268 .toList();
269
270 String frontCamera = Stream.of(devices)
271 .filter(id -> isLensFacing(id, cameraManager, CameraMetadata.LENS_FACING_FRONT))
272 .findFirst()
273 .orElse(null);
274
275 if (frontCamera != null) {
276 cameraList.add(frontCamera);
277 }
278
279 String backCamera = Stream.of(devices)
280 .filter(id -> isLensFacing(id, cameraManager, CameraMetadata.LENS_FACING_BACK))
281 .findFirst()
282 .orElse(null);
283
284 if (backCamera != null) {
285 cameraList.add(backCamera);
286 }
287 }
288
289 this.deviceNames = cameraList.toArray(new String[0]);
290 } catch (CameraAccessException e) {
291 Log.e(TAG, "Camera access exception: " + e);
292 this.deviceNames = new String[] {};
293 }
294
295 return deviceNames;
296 }
297
298 @Override
299 public @NonNull CameraVideoCapturer createCapturer(@Nullable String deviceName,
300 @Nullable CameraVideoCapturer.CameraEventsHandler eventsHandler)
301 {
302 return new Camera2Capturer(context, deviceName, eventsHandler, new FilteredCamera2Enumerator(context));
303 }
304 }
305
306 private class CameraCapturerWrapper implements CapturerObserver {
307 private final CapturerObserver observer;
308
309 public CameraCapturerWrapper(@NonNull CapturerObserver observer) {
310 this.observer = observer;
311 }
312
313 @Override
314 public void onCapturerStarted(boolean success) {
315 observer.onCapturerStarted(success);
316 if (success && cameraEventListener != null) {
317 cameraEventListener.onFullyInitialized();
318 }
319 }
320
321 @Override
322 public void onCapturerStopped() {
323 observer.onCapturerStopped();
324 if (cameraEventListener != null) cameraEventListener.onCameraStopped();
325 }
326
327 @Override
328 public void onFrameCaptured(VideoFrame videoFrame) {
329 observer.onFrameCaptured(videoFrame);
330 }
331 }
332}