Upgrade CameraX to 1.3.0-rc01

This commit is contained in:
Greyson Parrelli
2023-09-06 15:52:21 -04:00
committed by Alex Hart
parent f959543c19
commit b19aedd17c
7 changed files with 118 additions and 36 deletions

View File

@@ -3,12 +3,9 @@ package org.thoughtcrime.securesms.mediasend;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.camera.view.video.ExperimentalVideo;
import androidx.fragment.app.Fragment;
import org.thoughtcrime.securesms.R;

View File

@@ -30,10 +30,12 @@ import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.ImageProxy;
import androidx.camera.video.FallbackStrategy;
import androidx.camera.video.Quality;
import androidx.camera.video.QualitySelector;
import androidx.camera.view.CameraController;
import androidx.camera.view.LifecycleCameraController;
import androidx.camera.view.PreviewView;
import androidx.camera.view.video.ExperimentalVideo;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.content.ContextCompat;
@@ -71,7 +73,6 @@ import io.reactivex.rxjava3.disposables.Disposable;
* Camera captured implemented using the CameraX SDK, which uses Camera2 under the hood. Should be
* preferred whenever possible.
*/
@ExperimentalVideo
public class CameraXFragment extends LoggingFragment implements CameraFragment {
private static final String TAG = Log.tag(CameraXFragment.class);
@@ -239,7 +240,7 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
CameraController.OutputSize outputSize = new CameraController.OutputSize(size);
cameraController.setImageCaptureTargetSize(outputSize);
cameraController.setVideoCaptureTargetSize(new CameraController.OutputSize(VideoUtil.getVideoRecordingSize()));
cameraController.setVideoCaptureQualitySelector(QualitySelector.from(Quality.HD, FallbackStrategy.lowerQualityThan(Quality.HD)));
controlsContainer.removeAllViews();
controlsContainer.addView(LayoutInflater.from(getContext()).inflate(layout, controlsContainer, false));

View File

@@ -14,12 +14,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.core.ZoomState;
import androidx.camera.video.FileDescriptorOutputOptions;
import androidx.camera.video.Recording;
import androidx.camera.video.VideoRecordEvent;
import androidx.camera.view.CameraController;
import androidx.camera.view.PreviewView;
import androidx.camera.view.video.ExperimentalVideo;
import androidx.camera.view.video.OnVideoSavedCallback;
import androidx.camera.view.video.OutputFileOptions;
import androidx.camera.view.video.OutputFileResults;
import androidx.camera.view.video.AudioConfig;
import androidx.core.util.Consumer;
import androidx.fragment.app.Fragment;
import com.bumptech.glide.util.Executors;
@@ -39,7 +40,6 @@ import java.util.Objects;
import java.util.concurrent.TimeUnit;
@RequiresApi(26)
@ExperimentalVideo
class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener {
private static final String TAG = CameraXVideoCaptureHelper.class.getName();
@@ -57,25 +57,31 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
private ValueAnimator cameraMetricsAnimator;
private final OnVideoSavedCallback videoSavedListener = new OnVideoSavedCallback() {
@SuppressLint("RestrictedApi")
@Override
public void onVideoSaved(@NonNull OutputFileResults outputFileResults) {
try {
debouncer.clear();
cameraController.setZoomRatio(Objects.requireNonNull(cameraController.getZoomState().getValue()).getMinZoomRatio());
memoryFileDescriptor.seek(0);
callback.onVideoSaved(memoryFileDescriptor.getFileDescriptor());
} catch (IOException e) {
callback.onVideoError(e);
}
}
private @Nullable Recording activeRecording = null;
@SuppressLint("RestrictedApi")
private final Consumer<VideoRecordEvent> videoSavedListener = new Consumer<>() {
@Override
public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
debouncer.clear();
callback.onVideoError(cause);
public void accept(VideoRecordEvent videoRecordEvent) {
Log.d(TAG, "Received recording event: " + videoRecordEvent.getClass().getSimpleName());
if (videoRecordEvent instanceof VideoRecordEvent.Finalize) {
VideoRecordEvent.Finalize event = (VideoRecordEvent.Finalize) videoRecordEvent;
if (event.hasError()) {
Log.w(TAG, "Hit an error while recording! Error code: " + event.getError(), event.getCause());
debouncer.clear();
callback.onVideoError(event.getCause());
} else {
try {
debouncer.clear();
cameraController.setZoomRatio(Objects.requireNonNull(cameraController.getZoomState().getValue()).getMinZoomRatio());
memoryFileDescriptor.seek(0);
callback.onVideoSaved(memoryFileDescriptor.getFileDescriptor());
} catch (IOException e) {
callback.onVideoError(e);
}
}
}
}
};
@@ -140,9 +146,11 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
callback.onVideoRecordStarted();
shrinkCaptureArea();
OutputFileOptions options = OutputFileOptions.builder(memoryFileDescriptor.getParcelFileDescriptor()).build();
FileDescriptorOutputOptions outputOptions = new FileDescriptorOutputOptions.Builder(memoryFileDescriptor.getParcelFileDescriptor()).build();
AudioConfig audioConfig = AudioConfig.create(true);
activeRecording = cameraController.startRecording(outputOptions, audioConfig, Executors.mainThreadExecutor(), videoSavedListener);
cameraController.startRecording(options, Executors.mainThreadExecutor(), videoSavedListener);
updateProgressAnimator.start();
debouncer.publish(this::onVideoCaptureComplete);
}
@@ -198,10 +206,19 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
@Override
public void onVideoCaptureComplete() {
if (!canRecordAudio()) return;
if (!canRecordAudio()) {
Log.w(TAG, "Can't record audio!");
return;
}
if (activeRecording == null) {
Log.w(TAG, "No active recording!");
return;
}
Log.d(TAG, "onVideoCaptureComplete");
cameraController.stopRecording();
activeRecording.close();
activeRecording = null;
if (cameraMetricsAnimator != null && cameraMetricsAnimator.isRunning()) {
cameraMetricsAnimator.reverse();
@@ -219,6 +236,16 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
cameraController.setZoomRatio((range * increment) + zoomState.getMinZoomRatio());
}
@Override
protected void finalize() throws Throwable {
if (activeRecording != null) {
Log.w(TAG, "Dangling recording left open in finalize()! Attempting to close.");
activeRecording.close();
}
super.finalize();
}
static MemoryFileDescriptor createFileDescriptor(@NonNull Context context) throws MemoryFileDescriptor.MemoryFileException {
return MemoryFileDescriptor.newMemoryFileDescriptor(
context,

View File

@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.mediasend.camerax
import android.content.Context
import android.os.Build
import androidx.camera.view.CameraController
import androidx.camera.view.video.ExperimentalVideo
import org.signal.core.util.asListContains
import org.thoughtcrime.securesms.mms.MediaConstraints
import org.thoughtcrime.securesms.util.FeatureFlags
@@ -12,7 +11,6 @@ import org.thoughtcrime.securesms.video.VideoUtil
/**
* Describes device capabilities
*/
@ExperimentalVideo
sealed class CameraXModePolicy {
abstract val isVideoSupported: Boolean

View File

@@ -7,7 +7,7 @@ import java.util.Set;
/**
* A set of {@link android.os.Build#MODEL} that are known to both benefit from
* {@link androidx.camera.core.ImageCapture.CaptureMode#MAX_QUALITY} and execute it quickly.
* {@link androidx.camera.core.ImageCapture.CaptureMode#CAPTURE_MODE_MAXIMIZE_QUALITY} and execute it quickly.
*
*/
public class FastCameraModels {
@@ -19,6 +19,7 @@ public class FastCameraModels {
add("Pixel 3 XL");
add("Pixel 3a");
add("Pixel 3a XL");
add("SM-S911U1");
}};
/**