|
|
@ -1,11 +1,15 @@ |
|
|
|
package com.otaliastudios.cameraview.picture; |
|
|
|
package com.otaliastudios.cameraview.picture; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import android.graphics.SurfaceTexture; |
|
|
|
import android.hardware.camera2.CameraAccessException; |
|
|
|
import android.hardware.camera2.CameraAccessException; |
|
|
|
import android.hardware.camera2.CameraCaptureSession; |
|
|
|
import android.hardware.camera2.CameraCaptureSession; |
|
|
|
import android.hardware.camera2.CaptureRequest; |
|
|
|
import android.hardware.camera2.CaptureRequest; |
|
|
|
import android.hardware.camera2.CaptureResult; |
|
|
|
import android.hardware.camera2.CaptureResult; |
|
|
|
import android.hardware.camera2.TotalCaptureResult; |
|
|
|
import android.hardware.camera2.TotalCaptureResult; |
|
|
|
|
|
|
|
import android.opengl.EGL14; |
|
|
|
|
|
|
|
import android.opengl.EGLContext; |
|
|
|
import android.os.Build; |
|
|
|
import android.os.Build; |
|
|
|
|
|
|
|
import android.util.Log; |
|
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull; |
|
|
|
import androidx.annotation.NonNull; |
|
|
|
import androidx.annotation.Nullable; |
|
|
|
import androidx.annotation.Nullable; |
|
|
@ -14,22 +18,24 @@ import androidx.annotation.RequiresApi; |
|
|
|
import com.otaliastudios.cameraview.CameraLogger; |
|
|
|
import com.otaliastudios.cameraview.CameraLogger; |
|
|
|
import com.otaliastudios.cameraview.PictureResult; |
|
|
|
import com.otaliastudios.cameraview.PictureResult; |
|
|
|
import com.otaliastudios.cameraview.engine.Camera2Engine; |
|
|
|
import com.otaliastudios.cameraview.engine.Camera2Engine; |
|
|
|
import com.otaliastudios.cameraview.engine.CameraEngine; |
|
|
|
import com.otaliastudios.cameraview.filter.Filter; |
|
|
|
import com.otaliastudios.cameraview.overlay.Overlay; |
|
|
|
|
|
|
|
import com.otaliastudios.cameraview.preview.GlCameraPreview; |
|
|
|
import com.otaliastudios.cameraview.preview.GlCameraPreview; |
|
|
|
|
|
|
|
import com.otaliastudios.cameraview.preview.RendererFrameCallback; |
|
|
|
import com.otaliastudios.cameraview.size.AspectRatio; |
|
|
|
import com.otaliastudios.cameraview.size.AspectRatio; |
|
|
|
|
|
|
|
|
|
|
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) |
|
|
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) |
|
|
|
public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder { |
|
|
|
public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder implements RendererFrameCallback { |
|
|
|
|
|
|
|
|
|
|
|
private final static String TAG = Snapshot2PictureRecorder.class.getSimpleName(); |
|
|
|
private final static String TAG = Snapshot2PictureRecorder.class.getSimpleName(); |
|
|
|
private final static CameraLogger LOG = CameraLogger.create(TAG); |
|
|
|
private final static CameraLogger LOG = CameraLogger.create(TAG); |
|
|
|
|
|
|
|
|
|
|
|
private final static int STATE_IDLE = 0; |
|
|
|
private final static int STATE_IDLE = 0; |
|
|
|
private final static int STATE_WAITING_CAPTURE = 1; |
|
|
|
private final static int STATE_WAITING_CAPTURE = 1; |
|
|
|
private final static int STATE_WAITING_IMAGE = 2; |
|
|
|
private final static int STATE_WAITING_CORRECT_FRAME = 2; |
|
|
|
|
|
|
|
private final static int STATE_WAITING_IMAGE = 3; |
|
|
|
|
|
|
|
|
|
|
|
private final Camera2Engine mEngine; |
|
|
|
private final Camera2Engine mEngine; |
|
|
|
|
|
|
|
private final GlCameraPreview mPreview; |
|
|
|
private final CameraCaptureSession mSession; |
|
|
|
private final CameraCaptureSession mSession; |
|
|
|
private final CameraCaptureSession.CaptureCallback mCallback; |
|
|
|
private final CameraCaptureSession.CaptureCallback mCallback; |
|
|
|
private final CaptureRequest.Builder mBuilder; |
|
|
|
private final CaptureRequest.Builder mBuilder; |
|
|
@ -37,6 +43,12 @@ public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder { |
|
|
|
private Integer mOldCaptureIntent; |
|
|
|
private Integer mOldCaptureIntent; |
|
|
|
private int mSequenceId; |
|
|
|
private int mSequenceId; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private SurfaceTexture mLastFrameSurfaceTexture; |
|
|
|
|
|
|
|
private float mLastFrameScaleX; |
|
|
|
|
|
|
|
private float mLastFrameScaleY; |
|
|
|
|
|
|
|
private EGLContext mLastFrameScaleEGLContext; |
|
|
|
|
|
|
|
private Long mDesiredTimestamp = null; |
|
|
|
|
|
|
|
|
|
|
|
public Snapshot2PictureRecorder(@NonNull PictureResult.Stub stub, |
|
|
|
public Snapshot2PictureRecorder(@NonNull PictureResult.Stub stub, |
|
|
|
@NonNull Camera2Engine engine, |
|
|
|
@NonNull Camera2Engine engine, |
|
|
|
@NonNull GlCameraPreview preview, |
|
|
|
@NonNull GlCameraPreview preview, |
|
|
@ -44,8 +56,9 @@ public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder { |
|
|
|
@NonNull CameraCaptureSession session, |
|
|
|
@NonNull CameraCaptureSession session, |
|
|
|
@NonNull CameraCaptureSession.CaptureCallback callback, |
|
|
|
@NonNull CameraCaptureSession.CaptureCallback callback, |
|
|
|
@NonNull CaptureRequest.Builder builder) { |
|
|
|
@NonNull CaptureRequest.Builder builder) { |
|
|
|
super(stub, engine, preview, outputRatio, engine.getOverlay()); |
|
|
|
super(stub, engine, preview, outputRatio); |
|
|
|
mEngine = engine; |
|
|
|
mEngine = engine; |
|
|
|
|
|
|
|
mPreview = preview; |
|
|
|
mSession = session; |
|
|
|
mSession = session; |
|
|
|
mCallback = callback; |
|
|
|
mCallback = callback; |
|
|
|
mBuilder = builder; |
|
|
|
mBuilder = builder; |
|
|
@ -53,10 +66,15 @@ public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void take() { |
|
|
|
public void take() { |
|
|
|
if (mEngine.getPictureSnapshotMetering()) { |
|
|
|
if (!mEngine.getPictureSnapshotMetering()) { |
|
|
|
try { |
|
|
|
super.take(); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
LOG.i("take:", "Engine does metering, adding our CONTROL_CAPTURE_INTENT."); |
|
|
|
LOG.i("take:", "Engine does metering, adding our CONTROL_CAPTURE_INTENT."); |
|
|
|
|
|
|
|
mPreview.addRendererFrameCallback(this); |
|
|
|
mState = STATE_WAITING_CAPTURE; |
|
|
|
mState = STATE_WAITING_CAPTURE; |
|
|
|
|
|
|
|
try { |
|
|
|
mOldCaptureIntent = mBuilder.get(CaptureRequest.CONTROL_CAPTURE_INTENT); |
|
|
|
mOldCaptureIntent = mBuilder.get(CaptureRequest.CONTROL_CAPTURE_INTENT); |
|
|
|
mBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE); |
|
|
|
mBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE); |
|
|
|
mSequenceId = mSession.setRepeatingRequest(mBuilder.build(), mCallback, null); |
|
|
|
mSequenceId = mSession.setRepeatingRequest(mBuilder.build(), mCallback, null); |
|
|
@ -66,9 +84,25 @@ public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder { |
|
|
|
mError = e; |
|
|
|
mError = e; |
|
|
|
dispatchResult(); |
|
|
|
dispatchResult(); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
|
|
|
|
super.take(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void onRendererTextureCreated(int textureId) { |
|
|
|
|
|
|
|
super.onRendererTextureCreated(textureId); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void onRendererFilterChanged(@NonNull Filter filter) { |
|
|
|
|
|
|
|
super.onRendererFilterChanged(filter); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void onRendererFrame(@NonNull SurfaceTexture surfaceTexture, float scaleX, float scaleY) { |
|
|
|
|
|
|
|
mLastFrameSurfaceTexture = surfaceTexture; |
|
|
|
|
|
|
|
mLastFrameScaleX = scaleX; |
|
|
|
|
|
|
|
mLastFrameScaleY = scaleY; |
|
|
|
|
|
|
|
mLastFrameScaleEGLContext = EGL14.eglGetCurrentContext(); |
|
|
|
|
|
|
|
maybeTakeFrame(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void onCaptureCompleted(@NonNull TotalCaptureResult result) { |
|
|
|
public void onCaptureCompleted(@NonNull TotalCaptureResult result) { |
|
|
@ -81,13 +115,39 @@ public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder { |
|
|
|
LOG.w("onCaptureCompleted:", |
|
|
|
LOG.w("onCaptureCompleted:", |
|
|
|
"aeState:", result.get(CaptureResult.CONTROL_AE_STATE), |
|
|
|
"aeState:", result.get(CaptureResult.CONTROL_AE_STATE), |
|
|
|
"flashState:", result.get(CaptureResult.FLASH_STATE)); |
|
|
|
"flashState:", result.get(CaptureResult.FLASH_STATE)); |
|
|
|
mState = STATE_WAITING_IMAGE; |
|
|
|
mState = STATE_WAITING_CORRECT_FRAME; |
|
|
|
LOG.i("onCaptureCompleted:", "Got first result! Calling the GL recorder."); |
|
|
|
mDesiredTimestamp = result.get(CaptureResult.SENSOR_TIMESTAMP); |
|
|
|
super.take(); |
|
|
|
if (mDesiredTimestamp == null) mDesiredTimestamp = 0L; |
|
|
|
|
|
|
|
LOG.i("onCaptureCompleted:", "Got timestamp:", mDesiredTimestamp); |
|
|
|
|
|
|
|
maybeTakeFrame(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void maybeTakeFrame() { |
|
|
|
|
|
|
|
if (mState != STATE_WAITING_CORRECT_FRAME) { |
|
|
|
|
|
|
|
LOG.w("maybeTakeFrame:", "we're not waiting for a frame. Ignoring.", mState); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (mDesiredTimestamp == null || mLastFrameSurfaceTexture == null) { |
|
|
|
|
|
|
|
LOG.w("maybeTakeFrame:", "either timestamp or surfaceTexture are null.", mDesiredTimestamp); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long currentTimestamp = mLastFrameSurfaceTexture.getTimestamp(); |
|
|
|
|
|
|
|
if (currentTimestamp == mDesiredTimestamp) { |
|
|
|
|
|
|
|
LOG.i("maybeTakeFrame:", "taking frame with exact timestamp:", currentTimestamp); |
|
|
|
|
|
|
|
mState = STATE_WAITING_IMAGE; |
|
|
|
|
|
|
|
takeFrame(mLastFrameSurfaceTexture, mLastFrameScaleX, mLastFrameScaleY, mLastFrameScaleEGLContext); |
|
|
|
|
|
|
|
} else if (currentTimestamp > mDesiredTimestamp) { |
|
|
|
|
|
|
|
LOG.w("maybeTakeFrame:", "taking frame with some delay. Flash might not be respected."); |
|
|
|
|
|
|
|
mState = STATE_WAITING_IMAGE; |
|
|
|
|
|
|
|
takeFrame(mLastFrameSurfaceTexture, mLastFrameScaleX, mLastFrameScaleY, mLastFrameScaleEGLContext); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
LOG.i("maybeTakeFrame:", "Waiting...", mDesiredTimestamp - currentTimestamp); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
protected void dispatchResult() { |
|
|
|
protected void dispatchResult() { |
|
|
|
if (mState == STATE_WAITING_IMAGE) { |
|
|
|
if (mState == STATE_WAITING_IMAGE) { |
|
|
@ -98,6 +158,7 @@ public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder { |
|
|
|
mSession.setRepeatingRequest(mBuilder.build(), mCallback, null); |
|
|
|
mSession.setRepeatingRequest(mBuilder.build(), mCallback, null); |
|
|
|
} catch (CameraAccessException ignore) {} |
|
|
|
} catch (CameraAccessException ignore) {} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
mPreview.removeRendererFrameCallback(this); |
|
|
|
mState = STATE_IDLE; |
|
|
|
mState = STATE_IDLE; |
|
|
|
super.dispatchResult(); |
|
|
|
super.dispatchResult(); |
|
|
|
} |
|
|
|
} |
|
|
|