[go: nahoru, domu]

blob: 5477b9cacbd6a5a61ae2c7aa5a7348be51829b27 [file] [log] [blame]
Xi Zhangc1e26922022-05-19 15:30:26 -07001/*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.camera.core.processing;
18
Xi Zhang88f070a2023-03-21 13:55:34 -070019import static androidx.camera.core.ImageProcessingUtil.writeJpegBytesToSurface;
Xi Zhang227b50c2023-02-14 15:33:00 -080020import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
Xi Zhanga9836902023-04-26 10:16:07 -070021import static androidx.camera.core.impl.utils.TransformUtils.rotateSize;
Xi Zhang227b50c2023-02-14 15:33:00 -080022import static androidx.core.util.Preconditions.checkState;
23
Xi Zhang059ba092023-04-05 15:31:53 -070024import static java.util.Objects.requireNonNull;
25
Xi Zhang88f070a2023-03-21 13:55:34 -070026import android.graphics.Bitmap;
Xi Zhang227b50c2023-02-14 15:33:00 -080027import android.graphics.ImageFormat;
Xi Zhangc1e26922022-05-19 15:30:26 -070028import android.graphics.SurfaceTexture;
29import android.os.Handler;
leo huang53adb2c2022-06-06 19:50:19 +080030import android.os.HandlerThread;
Xi Zhang88f070a2023-03-21 13:55:34 -070031import android.util.Size;
Xi Zhangc1e26922022-05-19 15:30:26 -070032import android.view.Surface;
33
Xi Zhang059ba092023-04-05 15:31:53 -070034import androidx.annotation.IntRange;
Xi Zhangc1e26922022-05-19 15:30:26 -070035import androidx.annotation.NonNull;
Xi Zhang88f070a2023-03-21 13:55:34 -070036import androidx.annotation.Nullable;
Xi Zhangc1e26922022-05-19 15:30:26 -070037import androidx.annotation.RequiresApi;
leo huang53adb2c2022-06-06 19:50:19 +080038import androidx.annotation.VisibleForTesting;
39import androidx.annotation.WorkerThread;
mingdatsai8b6debe2023-05-30 21:47:01 +080040import androidx.arch.core.util.Function;
41import androidx.camera.core.DynamicRange;
leo huange69cb3c2023-03-17 09:39:37 +080042import androidx.camera.core.Logger;
Xi Zhangc1e26922022-05-19 15:30:26 -070043import androidx.camera.core.SurfaceOutput;
Xi Zhangc531e7a2022-09-21 11:11:18 -070044import androidx.camera.core.SurfaceProcessor;
Xi Zhangc1e26922022-05-19 15:30:26 -070045import androidx.camera.core.SurfaceRequest;
Xi Zhanga9836902023-04-26 10:16:07 -070046import androidx.camera.core.impl.utils.MatrixExt;
leo huang53adb2c2022-06-06 19:50:19 +080047import androidx.camera.core.impl.utils.executor.CameraXExecutors;
Xi Zhang88f070a2023-03-21 13:55:34 -070048import androidx.camera.core.impl.utils.futures.Futures;
leo huang40ac97e2022-07-05 15:08:07 +080049import androidx.concurrent.futures.CallbackToFutureAdapter;
50
Xi Zhanga9836902023-04-26 10:16:07 -070051import com.google.auto.value.AutoValue;
leo huang40ac97e2022-07-05 15:08:07 +080052import com.google.common.util.concurrent.ListenableFuture;
Xi Zhangc1e26922022-05-19 15:30:26 -070053
Xi Zhang88f070a2023-03-21 13:55:34 -070054import kotlin.Triple;
55
56import java.io.ByteArrayOutputStream;
Xi Zhanga9836902023-04-26 10:16:07 -070057import java.io.IOException;
Xi Zhang88f070a2023-03-21 13:55:34 -070058import java.util.ArrayList;
Xi Zhanga9836902023-04-26 10:16:07 -070059import java.util.Iterator;
leo huang53adb2c2022-06-06 19:50:19 +080060import java.util.LinkedHashMap;
Xi Zhang88f070a2023-03-21 13:55:34 -070061import java.util.List;
Xi Zhangc1e26922022-05-19 15:30:26 -070062import java.util.Map;
leo huang40ac97e2022-07-05 15:08:07 +080063import java.util.concurrent.ExecutionException;
Xi Zhangc1e26922022-05-19 15:30:26 -070064import java.util.concurrent.Executor;
leo huange69cb3c2023-03-17 09:39:37 +080065import java.util.concurrent.RejectedExecutionException;
leo huang53adb2c2022-06-06 19:50:19 +080066import java.util.concurrent.atomic.AtomicBoolean;
Xi Zhangc1e26922022-05-19 15:30:26 -070067
68/**
Xi Zhangc531e7a2022-09-21 11:11:18 -070069 * A default implementation of {@link SurfaceProcessor}.
Xi Zhangc1e26922022-05-19 15:30:26 -070070 *
71 * <p> This implementation simply copies the frame from the source to the destination with the
72 * transformation defined in {@link SurfaceOutput#updateTransformMatrix}.
73 */
74@RequiresApi(21)
Xi Zhangc531e7a2022-09-21 11:11:18 -070075public class DefaultSurfaceProcessor implements SurfaceProcessorInternal,
Xi Zhangc1e26922022-05-19 15:30:26 -070076 SurfaceTexture.OnFrameAvailableListener {
leo huange69cb3c2023-03-17 09:39:37 +080077 private static final String TAG = "DefaultSurfaceProcessor";
Xi Zhang88f070a2023-03-21 13:55:34 -070078
leo huang53adb2c2022-06-06 19:50:19 +080079 private final OpenGlRenderer mGlRenderer;
80 @VisibleForTesting
81 final HandlerThread mGlThread;
82 private final Executor mGlExecutor;
83 @VisibleForTesting
84 final Handler mGlHandler;
leo huange69cb3c2023-03-17 09:39:37 +080085 private final AtomicBoolean mIsReleaseRequested = new AtomicBoolean(false);
leo huang53adb2c2022-06-06 19:50:19 +080086 private final float[] mTextureMatrix = new float[16];
87 private final float[] mSurfaceOutputMatrix = new float[16];
Xi Zhangc1e26922022-05-19 15:30:26 -070088 // Map of current set of available outputs. Only access this on GL thread.
leo huang53adb2c2022-06-06 19:50:19 +080089 @SuppressWarnings("WeakerAccess") /* synthetic access */
90 final Map<SurfaceOutput, Surface> mOutputSurfaces = new LinkedHashMap<>();
91
92 // Only access this on GL thread.
93 private int mInputSurfaceCount = 0;
leo huange69cb3c2023-03-17 09:39:37 +080094 // Only access this on GL thread.
95 private boolean mIsReleased = false;
Xi Zhang88f070a2023-03-21 13:55:34 -070096 // Only access this on GL thread.
Xi Zhanga9836902023-04-26 10:16:07 -070097 private final List<PendingSnapshot> mPendingSnapshots = new ArrayList<>();
leo huang53adb2c2022-06-06 19:50:19 +080098
Xi Zhangc531e7a2022-09-21 11:11:18 -070099 /** Constructs {@link DefaultSurfaceProcessor} with default shaders. */
mingdatsai8b6debe2023-05-30 21:47:01 +0800100 DefaultSurfaceProcessor(@NonNull DynamicRange dynamicRange) {
101 this(dynamicRange, ShaderProvider.DEFAULT);
leo huangb598b072022-09-12 17:56:47 +0800102 }
103
leo huang40ac97e2022-07-05 15:08:07 +0800104 /**
Xi Zhangc531e7a2022-09-21 11:11:18 -0700105 * Constructs {@link DefaultSurfaceProcessor} with custom shaders.
leo huang40ac97e2022-07-05 15:08:07 +0800106 *
107 * @param shaderProvider custom shader provider for OpenGL rendering.
108 * @throws IllegalArgumentException if the shaderProvider provides invalid shader.
109 */
mingdatsai8b6debe2023-05-30 21:47:01 +0800110 DefaultSurfaceProcessor(@NonNull DynamicRange dynamicRange,
111 @NonNull ShaderProvider shaderProvider) {
leo huang53adb2c2022-06-06 19:50:19 +0800112 mGlThread = new HandlerThread("GL Thread");
113 mGlThread.start();
114 mGlHandler = new Handler(mGlThread.getLooper());
115 mGlExecutor = CameraXExecutors.newHandlerExecutor(mGlHandler);
116 mGlRenderer = new OpenGlRenderer();
leo huang40ac97e2022-07-05 15:08:07 +0800117 try {
mingdatsai8b6debe2023-05-30 21:47:01 +0800118 initGlRenderer(dynamicRange, shaderProvider);
leo huang40ac97e2022-07-05 15:08:07 +0800119 } catch (RuntimeException e) {
120 release();
121 throw e;
122 }
leo huang53adb2c2022-06-06 19:50:19 +0800123 }
Xi Zhangc1e26922022-05-19 15:30:26 -0700124
125 /**
126 * {@inheritDoc}
127 */
128 @Override
129 public void onInputSurface(@NonNull SurfaceRequest surfaceRequest) {
leo huange69cb3c2023-03-17 09:39:37 +0800130 if (mIsReleaseRequested.get()) {
leo huang53adb2c2022-06-06 19:50:19 +0800131 surfaceRequest.willNotProvideSurface();
132 return;
133 }
leo huange69cb3c2023-03-17 09:39:37 +0800134 executeSafely(() -> {
leo huang53adb2c2022-06-06 19:50:19 +0800135 mInputSurfaceCount++;
136 SurfaceTexture surfaceTexture = new SurfaceTexture(mGlRenderer.getTextureName());
137 surfaceTexture.setDefaultBufferSize(surfaceRequest.getResolution().getWidth(),
138 surfaceRequest.getResolution().getHeight());
139 Surface surface = new Surface(surfaceTexture);
140 surfaceRequest.provideSurface(surface, mGlExecutor, result -> {
141 surfaceTexture.setOnFrameAvailableListener(null);
142 surfaceTexture.release();
143 surface.release();
144 mInputSurfaceCount--;
145 checkReadyToRelease();
146 });
147 surfaceTexture.setOnFrameAvailableListener(this, mGlHandler);
leo huange69cb3c2023-03-17 09:39:37 +0800148 }, surfaceRequest::willNotProvideSurface);
Xi Zhangc1e26922022-05-19 15:30:26 -0700149 }
150
151 /**
152 * {@inheritDoc}
153 */
154 @Override
155 public void onOutputSurface(@NonNull SurfaceOutput surfaceOutput) {
leo huange69cb3c2023-03-17 09:39:37 +0800156 if (mIsReleaseRequested.get()) {
Xi Zhangc1e26922022-05-19 15:30:26 -0700157 surfaceOutput.close();
leo huang53adb2c2022-06-06 19:50:19 +0800158 return;
159 }
leo huange69cb3c2023-03-17 09:39:37 +0800160 executeSafely(() -> {
mingdatsai28670282022-10-03 15:32:01 +0800161 Surface surface = surfaceOutput.getSurface(mGlExecutor, event -> {
162 surfaceOutput.close();
163 Surface removedSurface = mOutputSurfaces.remove(surfaceOutput);
164 if (removedSurface != null) {
165 mGlRenderer.unregisterOutputSurface(removedSurface);
166 }
167 });
168 mGlRenderer.registerOutputSurface(surface);
169 mOutputSurfaces.put(surfaceOutput, surface);
leo huange69cb3c2023-03-17 09:39:37 +0800170 }, surfaceOutput::close);
leo huang53adb2c2022-06-06 19:50:19 +0800171 }
172
173 /**
Xi Zhangc531e7a2022-09-21 11:11:18 -0700174 * Release the {@link DefaultSurfaceProcessor}.
leo huang53adb2c2022-06-06 19:50:19 +0800175 */
Xi Zhang4fd5e4c2022-06-29 06:55:54 -0700176 @Override
leo huang53adb2c2022-06-06 19:50:19 +0800177 public void release() {
leo huange69cb3c2023-03-17 09:39:37 +0800178 if (mIsReleaseRequested.getAndSet(true)) {
leo huang53adb2c2022-06-06 19:50:19 +0800179 return;
180 }
leo huange69cb3c2023-03-17 09:39:37 +0800181 executeSafely(() -> {
182 mIsReleased = true;
183 checkReadyToRelease();
184 });
Xi Zhangc1e26922022-05-19 15:30:26 -0700185 }
186
Xi Zhang4b7cc122023-03-28 15:24:58 -0700187 @Override
Xi Zhanga9836902023-04-26 10:16:07 -0700188 @NonNull
189 public ListenableFuture<Void> snapshot(
190 @IntRange(from = 0, to = 100) int jpegQuality,
191 @IntRange(from = 0, to = 359) int rotationDegrees) {
Xi Zhang88f070a2023-03-21 13:55:34 -0700192 return Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(
193 completer -> {
Xi Zhanga9836902023-04-26 10:16:07 -0700194 PendingSnapshot pendingSnapshot = PendingSnapshot.of(jpegQuality,
195 rotationDegrees, completer);
Xi Zhang059ba092023-04-05 15:31:53 -0700196 executeSafely(
197 () -> mPendingSnapshots.add(pendingSnapshot),
198 () -> completer.setException(new Exception(
199 "Failed to snapshot: OpenGLRenderer not ready.")));
Xi Zhang88f070a2023-03-21 13:55:34 -0700200 return "DefaultSurfaceProcessor#snapshot";
201 }));
Xi Zhang4b7cc122023-03-28 15:24:58 -0700202 }
203
Xi Zhangc1e26922022-05-19 15:30:26 -0700204 /**
205 * {@inheritDoc}
206 */
207 @Override
leo huang53adb2c2022-06-06 19:50:19 +0800208 public void onFrameAvailable(@NonNull SurfaceTexture surfaceTexture) {
leo huange69cb3c2023-03-17 09:39:37 +0800209 if (mIsReleaseRequested.get()) {
leo huang53adb2c2022-06-06 19:50:19 +0800210 // Ignore frame update if released.
211 return;
212 }
leo huang53adb2c2022-06-06 19:50:19 +0800213 surfaceTexture.updateTexImage();
214 surfaceTexture.getTransformMatrix(mTextureMatrix);
Xi Zhang88f070a2023-03-21 13:55:34 -0700215 // Surface, size and transform matrix for JPEG Surface if exists
216 Triple<Surface, Size, float[]> jpegOutput = null;
leo huang53adb2c2022-06-06 19:50:19 +0800217
218 for (Map.Entry<SurfaceOutput, Surface> entry : mOutputSurfaces.entrySet()) {
219 Surface surface = entry.getValue();
220 SurfaceOutput surfaceOutput = entry.getKey();
Xi Zhang88f070a2023-03-21 13:55:34 -0700221 surfaceOutput.updateTransformMatrix(mSurfaceOutputMatrix, mTextureMatrix);
Xi Zhang227b50c2023-02-14 15:33:00 -0800222 if (surfaceOutput.getFormat() == INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
223 // Render GPU output directly.
Xi Zhangff6e8352023-08-09 14:03:28 +0000224 try {
225 mGlRenderer.render(surfaceTexture.getTimestamp(), mSurfaceOutputMatrix,
226 surface);
227 } catch (RuntimeException e) {
228 // This should not happen. However, when it happens, we catch the exception
229 // to prevent the crash.
230 Logger.e(TAG, "Failed to render with OpenGL.", e);
231 }
Xi Zhang227b50c2023-02-14 15:33:00 -0800232 } else {
233 checkState(surfaceOutput.getFormat() == ImageFormat.JPEG,
234 "Unsupported format: " + surfaceOutput.getFormat());
Xi Zhang88f070a2023-03-21 13:55:34 -0700235 checkState(jpegOutput == null, "Only one JPEG output is supported.");
236 jpegOutput = new Triple<>(surface, surfaceOutput.getSize(),
237 mSurfaceOutputMatrix.clone());
Xi Zhang227b50c2023-02-14 15:33:00 -0800238 }
leo huang53adb2c2022-06-06 19:50:19 +0800239 }
Xi Zhang88f070a2023-03-21 13:55:34 -0700240
241 // Execute all pending snapshots.
Xi Zhang059ba092023-04-05 15:31:53 -0700242 try {
243 takeSnapshotAndDrawJpeg(jpegOutput);
244 } catch (RuntimeException e) {
245 // Propagates error back to the app if failed to take snapshot.
246 failAllPendingSnapshots(e);
247 }
Xi Zhang88f070a2023-03-21 13:55:34 -0700248 }
249
250 /**
251 * Takes a snapshot of the current frame and draws it to given JPEG surface.
252 *
253 * @param jpegOutput The <Surface, Surface size, transform matrix> tuple for drawing.
254 */
Xi Zhanga9836902023-04-26 10:16:07 -0700255 @WorkerThread
Xi Zhang88f070a2023-03-21 13:55:34 -0700256 private void takeSnapshotAndDrawJpeg(@Nullable Triple<Surface, Size, float[]> jpegOutput) {
257 if (mPendingSnapshots.isEmpty()) {
258 // No pending snapshot requests, do nothing.
259 return;
260 }
261
262 // No JPEG Surface, fail all snapshot requests.
263 if (jpegOutput == null) {
Xi Zhang059ba092023-04-05 15:31:53 -0700264 failAllPendingSnapshots(new Exception("Failed to snapshot: no JPEG Surface."));
Xi Zhang88f070a2023-03-21 13:55:34 -0700265 return;
266 }
267
Xi Zhang88f070a2023-03-21 13:55:34 -0700268 // Write to JPEG surface, once for each snapshot request.
Xi Zhanga9836902023-04-26 10:16:07 -0700269 try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
270 byte[] jpegBytes = null;
271 int jpegQuality = -1;
272 int rotationDegrees = -1;
273 Bitmap bitmap = null;
274 Iterator<PendingSnapshot> iterator = mPendingSnapshots.iterator();
275 while (iterator.hasNext()) {
276 PendingSnapshot pendingSnapshot = iterator.next();
277 // Take a new snapshot if the rotation is different.
278 if (rotationDegrees != pendingSnapshot.getRotationDegrees() || bitmap == null) {
279 rotationDegrees = pendingSnapshot.getRotationDegrees();
280 // Recycle the previous bitmap to free up memory.
281 if (bitmap != null) {
282 bitmap.recycle();
283 }
284 bitmap = getBitmap(jpegOutput.getSecond(), jpegOutput.getThird(),
285 rotationDegrees);
286 // Clear JPEG quality to force re-encoding.
287 jpegQuality = -1;
288 }
289 // Re-encode the bitmap if the quality is different.
290 if (jpegQuality != pendingSnapshot.getJpegQuality()) {
291 outputStream.reset();
292 jpegQuality = pendingSnapshot.getJpegQuality();
293 bitmap.compress(Bitmap.CompressFormat.JPEG, jpegQuality, outputStream);
294 jpegBytes = outputStream.toByteArray();
295 }
296 writeJpegBytesToSurface(jpegOutput.getFirst(), requireNonNull(jpegBytes));
297 pendingSnapshot.getCompleter().set(null);
298 iterator.remove();
Xi Zhang059ba092023-04-05 15:31:53 -0700299 }
Xi Zhanga9836902023-04-26 10:16:07 -0700300 } catch (IOException e) {
301 failAllPendingSnapshots(e);
Xi Zhang059ba092023-04-05 15:31:53 -0700302 }
Xi Zhang059ba092023-04-05 15:31:53 -0700303 }
304
305 private void failAllPendingSnapshots(@NonNull Throwable throwable) {
Xi Zhanga9836902023-04-26 10:16:07 -0700306 for (PendingSnapshot pendingSnapshot : mPendingSnapshots) {
307 pendingSnapshot.getCompleter().setException(throwable);
Xi Zhang88f070a2023-03-21 13:55:34 -0700308 }
309 mPendingSnapshots.clear();
310 }
311
312 @NonNull
Xi Zhanga9836902023-04-26 10:16:07 -0700313 private Bitmap getBitmap(@NonNull Size size,
314 @NonNull float[] textureTransform,
315 int rotationDegrees) {
Xi Zhangcb1e9142023-11-22 07:21:01 -0800316 float[] snapshotTransform = textureTransform.clone();
Xi Zhanga9836902023-04-26 10:16:07 -0700317
318 // Rotate the output if requested.
319 MatrixExt.preRotate(snapshotTransform, rotationDegrees, 0.5f, 0.5f);
320
Xi Zhangcb1e9142023-11-22 07:21:01 -0800321 // Flip the snapshot. This is for reverting the GL transform added in SurfaceOutputImpl.
322 MatrixExt.preVerticalFlip(snapshotTransform, 0.5f);
Xi Zhanga9836902023-04-26 10:16:07 -0700323
324 // Update the size based on the rotation degrees.
325 size = rotateSize(size, rotationDegrees);
326
Xi Zhang88f070a2023-03-21 13:55:34 -0700327 // Take a snapshot Bitmap and compress it to JPEG.
Xi Zhang059ba092023-04-05 15:31:53 -0700328 return mGlRenderer.snapshot(size, snapshotTransform);
leo huang53adb2c2022-06-06 19:50:19 +0800329 }
330
331 @WorkerThread
332 private void checkReadyToRelease() {
leo huange69cb3c2023-03-17 09:39:37 +0800333 if (mIsReleased && mInputSurfaceCount == 0) {
leo huang53adb2c2022-06-06 19:50:19 +0800334 // Once release is called, we can stop sending frame to output surfaces.
335 for (SurfaceOutput surfaceOutput : mOutputSurfaces.keySet()) {
336 surfaceOutput.close();
337 }
Xi Zhanga9836902023-04-26 10:16:07 -0700338 for (PendingSnapshot pendingSnapshot : mPendingSnapshots) {
339 pendingSnapshot.getCompleter().setException(
Xi Zhang88f070a2023-03-21 13:55:34 -0700340 new Exception("Failed to snapshot: DefaultSurfaceProcessor is released."));
341 }
leo huang53adb2c2022-06-06 19:50:19 +0800342 mOutputSurfaces.clear();
343 mGlRenderer.release();
344 mGlThread.quit();
345 }
Xi Zhangc1e26922022-05-19 15:30:26 -0700346 }
leo huang40ac97e2022-07-05 15:08:07 +0800347
mingdatsai8b6debe2023-05-30 21:47:01 +0800348 private void initGlRenderer(@NonNull DynamicRange dynamicRange,
349 @NonNull ShaderProvider shaderProvider) {
leo huang40ac97e2022-07-05 15:08:07 +0800350 ListenableFuture<Void> initFuture = CallbackToFutureAdapter.getFuture(completer -> {
leo huange69cb3c2023-03-17 09:39:37 +0800351 executeSafely(() -> {
leo huang40ac97e2022-07-05 15:08:07 +0800352 try {
mingdatsai8b6debe2023-05-30 21:47:01 +0800353 mGlRenderer.init(dynamicRange, shaderProvider);
leo huang40ac97e2022-07-05 15:08:07 +0800354 completer.set(null);
355 } catch (RuntimeException e) {
356 completer.setException(e);
357 }
358 });
359 return "Init GlRenderer";
360 });
361 try {
362 initFuture.get();
363 } catch (ExecutionException | InterruptedException e) {
364 // If the cause is a runtime exception, throw it directly. Otherwise convert to runtime
365 // exception and throw.
366 Throwable cause = e instanceof ExecutionException ? e.getCause() : e;
367 if (cause instanceof RuntimeException) {
368 throw (RuntimeException) cause;
369 } else {
Xi Zhangc531e7a2022-09-21 11:11:18 -0700370 throw new IllegalStateException("Failed to create DefaultSurfaceProcessor", cause);
leo huang40ac97e2022-07-05 15:08:07 +0800371 }
372 }
373 }
Xi Zhangf421d18c2023-01-25 11:48:19 -0800374
leo huange69cb3c2023-03-17 09:39:37 +0800375 private void executeSafely(@NonNull Runnable runnable) {
376 executeSafely(runnable, () -> {
377 // Do nothing.
378 });
379 }
380
381 private void executeSafely(@NonNull Runnable runnable, @NonNull Runnable onFailure) {
382 try {
383 mGlExecutor.execute(() -> {
384 if (mIsReleased) {
385 onFailure.run();
386 } else {
387 runnable.run();
388 }
389 });
390 } catch (RejectedExecutionException e) {
391 Logger.w(TAG, "Unable to executor runnable", e);
392 onFailure.run();
393 }
394 }
395
Xi Zhangf421d18c2023-01-25 11:48:19 -0800396 /**
Xi Zhanga9836902023-04-26 10:16:07 -0700397 * A pending snapshot request to be executed on the next frame available.
398 */
399 @AutoValue
400 abstract static class PendingSnapshot {
401
402 @IntRange(from = 0, to = 100)
403 abstract int getJpegQuality();
404
405 @IntRange(from = 0, to = 359)
406 abstract int getRotationDegrees();
407
408 @NonNull
409 abstract CallbackToFutureAdapter.Completer<Void> getCompleter();
410
411 @NonNull
412 static AutoValue_DefaultSurfaceProcessor_PendingSnapshot of(
413 @IntRange(from = 0, to = 100) int jpegQuality,
414 @IntRange(from = 0, to = 359) int rotationDegrees,
415 @NonNull CallbackToFutureAdapter.Completer<Void> completer) {
416 return new AutoValue_DefaultSurfaceProcessor_PendingSnapshot(
417 jpegQuality, rotationDegrees, completer);
418 }
419 }
420
421 /**
Xi Zhangf421d18c2023-01-25 11:48:19 -0800422 * Factory class that produces {@link DefaultSurfaceProcessor}.
423 *
424 * <p> This is for working around the limit that OpenGL cannot be initialized in unit tests.
425 */
426 public static class Factory {
427 private Factory() {
428 }
429
mingdatsai8b6debe2023-05-30 21:47:01 +0800430 private static Function<DynamicRange, SurfaceProcessorInternal> sSupplier =
431 DefaultSurfaceProcessor::new;
Xi Zhangf421d18c2023-01-25 11:48:19 -0800432
433 /**
434 * Creates a new {@link DefaultSurfaceProcessor} with no-op shader.
435 */
436 @NonNull
mingdatsai8b6debe2023-05-30 21:47:01 +0800437 public static SurfaceProcessorInternal newInstance(@NonNull DynamicRange dynamicRange) {
438 return sSupplier.apply(dynamicRange);
Xi Zhangf421d18c2023-01-25 11:48:19 -0800439 }
440
441 /**
442 * Overrides the {@link DefaultSurfaceProcessor} supplier for testing.
443 */
444 @VisibleForTesting
mingdatsai8b6debe2023-05-30 21:47:01 +0800445 public static void setSupplier(
446 @NonNull Function<DynamicRange, SurfaceProcessorInternal> supplier) {
Xi Zhangf421d18c2023-01-25 11:48:19 -0800447 sSupplier = supplier;
448 }
449 }
Xi Zhangc1e26922022-05-19 15:30:26 -0700450}