[go: nahoru, domu]

blob: 0aeb6418188af84f2c00c66b970d657d840e16f7 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.integration.extensions;
import static androidx.camera.testing.SurfaceTextureProvider.createSurfaceTextureProvider;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue;
import android.app.Instrumentation;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.experimental.UseExperimental;
import androidx.camera.camera2.Camera2Config;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraX;
import androidx.camera.core.CameraXConfig;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.Preview;
import androidx.camera.core.impl.CaptureProcessor;
import androidx.camera.core.impl.PreviewConfig;
import androidx.camera.core.internal.CameraUseCaseAdapter;
import androidx.camera.extensions.AutoImageCaptureExtender;
import androidx.camera.extensions.AutoPreviewExtender;
import androidx.camera.extensions.BeautyImageCaptureExtender;
import androidx.camera.extensions.BeautyPreviewExtender;
import androidx.camera.extensions.BokehImageCaptureExtender;
import androidx.camera.extensions.BokehPreviewExtender;
import androidx.camera.extensions.ExtensionsManager;
import androidx.camera.extensions.HdrImageCaptureExtender;
import androidx.camera.extensions.HdrPreviewExtender;
import androidx.camera.extensions.ImageCaptureExtender;
import androidx.camera.extensions.NightImageCaptureExtender;
import androidx.camera.extensions.NightPreviewExtender;
import androidx.camera.extensions.PreviewExtender;
import androidx.camera.testing.CameraUtil;
import androidx.camera.testing.GLUtil;
import androidx.camera.testing.SurfaceTextureProvider;
import androidx.camera.testing.TimestampCaptureProcessor;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.common.util.concurrent.ListenableFuture;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Tests that the {@link androidx.camera.extensions.impl.PreviewImageProcessorImpl} properly
* populates the timestamp for the {@link SurfaceTexture}.
*/
@MediumTest
@RunWith(Parameterized.class)
public class PreviewProcessorTimestampTest {
@Rule
public TestRule mUseCamera = CameraUtil.grantCameraPermissionAndPreTest();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final Context mContext = ApplicationProvider.getApplicationContext();
private ExtensionsManager.EffectMode mEffectMode;
@CameraSelector.LensFacing
private int mLensFacing;
private CountDownLatch mInputTimestampsLatch;
private CountDownLatch mOutputTimestampsLatch;
private CountDownLatch mSurfaceTextureLatch;
private Set<Long> mInputTimestamps = new HashSet<>();
private Set<Long> mOutputTimestamps = new HashSet<>();
TimestampCaptureProcessor.TimestampListener mTimestampListener;
SurfaceTexture.OnFrameAvailableListener mOnFrameAvailableListener;
private ImageCapture.Builder mImageCaptureBuilder;
private Preview.Builder mPreviewBuilder;
@Parameterized.Parameters(name = "effect = {0}, facing = {1}")
public static Collection<Object[]> getParameters() {
return Arrays.asList(new Object[][]{
{ExtensionsManager.EffectMode.BOKEH, CameraSelector.LENS_FACING_FRONT},
{ExtensionsManager.EffectMode.BOKEH, CameraSelector.LENS_FACING_BACK},
{ExtensionsManager.EffectMode.HDR, CameraSelector.LENS_FACING_FRONT},
{ExtensionsManager.EffectMode.HDR, CameraSelector.LENS_FACING_BACK},
{ExtensionsManager.EffectMode.BEAUTY, CameraSelector.LENS_FACING_FRONT},
{ExtensionsManager.EffectMode.BEAUTY, CameraSelector.LENS_FACING_BACK},
{ExtensionsManager.EffectMode.NIGHT, CameraSelector.LENS_FACING_FRONT},
{ExtensionsManager.EffectMode.NIGHT, CameraSelector.LENS_FACING_BACK},
{ExtensionsManager.EffectMode.AUTO, CameraSelector.LENS_FACING_FRONT},
{ExtensionsManager.EffectMode.AUTO, CameraSelector.LENS_FACING_BACK}
});
}
public PreviewProcessorTimestampTest(ExtensionsManager.EffectMode effectMode,
@CameraSelector.LensFacing int lensFacing) {
mEffectMode = effectMode;
mLensFacing = lensFacing;
}
@Before
@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
public void setUp() throws Exception {
mProcessingHandlerThread =
new HandlerThread("Processing");
mProcessingHandlerThread.start();
mProcessingHandler = new Handler(mProcessingHandlerThread.getLooper());
assumeTrue(androidx.camera.testing.CameraUtil.deviceHasCamera());
CameraXConfig config = Camera2Config.defaultConfig();
CameraX.initialize(mContext, config);
ListenableFuture<ExtensionsManager.ExtensionsAvailability> availability =
ExtensionsManager.init(mContext);
ExtensionsManager.ExtensionsAvailability extensionsAvailability = availability.get(1,
TimeUnit.SECONDS);
assumeTrue(extensionsAvailability
== ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE);
mImageCaptureBuilder = new ImageCapture.Builder();
mPreviewBuilder = new Preview.Builder();
mInputTimestampsLatch = new CountDownLatch(1);
mTimestampListener = new TimestampCaptureProcessor.TimestampListener() {
boolean mComplete = false;
@Override
public void onTimestampAvailable(long timestamp) {
if (mComplete) {
return;
}
mInputTimestamps.add(timestamp);
if (mInputTimestamps.size() > 10) {
mInputTimestampsLatch.countDown();
mComplete = true;
}
}
};
mOutputTimestampsLatch = new CountDownLatch(1);
mOnFrameAvailableListener = new SurfaceTexture.OnFrameAvailableListener() {
boolean mComplete = false;
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
if (mComplete) {
return;
}
synchronized (mIsSurfaceTextureReleasedLock) {
if (!mIsSurfaceTextureReleased) {
mInstrumentation.runOnMainSync(() -> {
surfaceTexture.updateTexImage();
});
}
}
mOutputTimestamps.add(surfaceTexture.getTimestamp());
if (mOutputTimestamps.size() > 10) {
mOutputTimestampsLatch.countDown();
mComplete = true;
}
}
};
mSurfaceTextureLatch = new CountDownLatch(1);
}
@After
public void cleanUp() throws InterruptedException, ExecutionException, TimeoutException {
CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
ExtensionsManager.deinit().get();
}
private HandlerThread mProcessingHandlerThread;
private Handler mProcessingHandler;
private boolean mIsSurfaceTextureReleased = false;
private Object mIsSurfaceTextureReleasedLock = new Object();
@Test
public void timestampIsCorrect() throws InterruptedException {
assumeTrue(androidx.camera.testing.CameraUtil.hasCameraWithLensFacing(mLensFacing));
assumeTrue(ExtensionsManager.isExtensionAvailable(mEffectMode, mLensFacing));
enableExtension(mEffectMode, mLensFacing);
// To test bind/unbind and take picture.
ImageCapture imageCapture = mImageCaptureBuilder.build();
PreviewConfig previewConfig = mPreviewBuilder.getUseCaseConfig();
CaptureProcessor previewCaptureProcessor = previewConfig.getCaptureProcessor(null);
assumeNotNull(previewCaptureProcessor);
mPreviewBuilder.setCaptureProcessor(
new TimestampCaptureProcessor(previewCaptureProcessor, mTimestampListener));
Preview preview = mPreviewBuilder.build();
CameraSelector cameraSelector =
new CameraSelector.Builder().requireLensFacing(mLensFacing).build();
mInstrumentation.runOnMainSync(() -> {
// To set the update listener and Preview will change to active state.
preview.setSurfaceProvider(createSurfaceTextureProvider(
new SurfaceTextureProvider.SurfaceTextureCallback() {
@Override
public void onSurfaceTextureReady(@NonNull SurfaceTexture surfaceTexture,
@NonNull Size resolution) {
surfaceTexture.attachToGLContext(GLUtil.getTexIdFromGLContext());
surfaceTexture.setOnFrameAvailableListener(
mOnFrameAvailableListener, mProcessingHandler);
mSurfaceTextureLatch.countDown();
}
@Override
public void onSafeToRelease(@NonNull SurfaceTexture surfaceTexture) {
synchronized (mIsSurfaceTextureReleasedLock) {
mIsSurfaceTextureReleased = true;
surfaceTexture.release();
}
}
}));
CameraUseCaseAdapter cameraUseCaseAdapter =
CameraUtil.createCameraUseCaseAdapter(mContext, cameraSelector);
try {
cameraUseCaseAdapter.addUseCases(Arrays.asList(preview, imageCapture));
} catch (CameraUseCaseAdapter.CameraException e) {
e.printStackTrace();
}
});
assertTrue(mSurfaceTextureLatch.await(1000, TimeUnit.MILLISECONDS));
assertTrue(mInputTimestampsLatch.await(1000, TimeUnit.MILLISECONDS));
assertTrue(mOutputTimestampsLatch.await(1000, TimeUnit.MILLISECONDS));
assertEquals(mInputTimestamps, mOutputTimestamps);
}
/**
* To invoke the enableExtension() method for different effect.
*/
private void enableExtension(ExtensionsManager.EffectMode effectMode,
@CameraSelector.LensFacing int lensFacing) {
CameraSelector cameraSelector =
new CameraSelector.Builder().requireLensFacing(lensFacing).build();
ImageCaptureExtender imageCaptureExtender = null;
PreviewExtender previewExtender = null;
switch (effectMode) {
case HDR:
imageCaptureExtender = HdrImageCaptureExtender.create(mImageCaptureBuilder);
previewExtender = HdrPreviewExtender.create(mPreviewBuilder);
break;
case BOKEH:
imageCaptureExtender = BokehImageCaptureExtender.create(mImageCaptureBuilder);
previewExtender = BokehPreviewExtender.create(mPreviewBuilder);
break;
case BEAUTY:
imageCaptureExtender = BeautyImageCaptureExtender.create(mImageCaptureBuilder);
previewExtender = BeautyPreviewExtender.create(mPreviewBuilder);
break;
case NIGHT:
imageCaptureExtender = NightImageCaptureExtender.create(mImageCaptureBuilder);
previewExtender = NightPreviewExtender.create(mPreviewBuilder);
break;
case AUTO:
imageCaptureExtender = AutoImageCaptureExtender.create(mImageCaptureBuilder);
previewExtender = AutoPreviewExtender.create(mPreviewBuilder);
break;
}
assertNotNull(imageCaptureExtender);
assertNotNull(previewExtender);
assertTrue(previewExtender.isExtensionAvailable(cameraSelector));
previewExtender.enableExtension(cameraSelector);
assertTrue(imageCaptureExtender.isExtensionAvailable(cameraSelector));
imageCaptureExtender.enableExtension(cameraSelector);
}
}