|
|
|
@ -16,6 +16,7 @@ import com.otaliastudios.cameraview.CameraException; |
|
|
|
|
import com.otaliastudios.cameraview.CameraLogger; |
|
|
|
|
import com.otaliastudios.cameraview.PictureResult; |
|
|
|
|
import com.otaliastudios.cameraview.internal.utils.ExifHelper; |
|
|
|
|
import com.otaliastudios.cameraview.internal.utils.WorkerHandler; |
|
|
|
|
|
|
|
|
|
import java.io.ByteArrayInputStream; |
|
|
|
|
import java.io.IOException; |
|
|
|
@ -30,7 +31,7 @@ import androidx.exifinterface.media.ExifInterface; |
|
|
|
|
* A {@link PictureResult} that uses standard APIs. |
|
|
|
|
*/ |
|
|
|
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) |
|
|
|
|
public class Full2PictureRecorder extends PictureRecorder { |
|
|
|
|
public class Full2PictureRecorder extends PictureRecorder implements ImageReader.OnImageAvailableListener { |
|
|
|
|
|
|
|
|
|
private static final String TAG = Full2PictureRecorder.class.getSimpleName(); |
|
|
|
|
private static final CameraLogger LOG = CameraLogger.create(TAG); |
|
|
|
@ -42,11 +43,14 @@ public class Full2PictureRecorder extends PictureRecorder { |
|
|
|
|
private static final int STATE_WAITING_CAPTURE = 4; |
|
|
|
|
private static final int STATE_WAITING_IMAGE = 5; |
|
|
|
|
|
|
|
|
|
private static final int REQUEST_TAG = CameraDevice.TEMPLATE_STILL_CAPTURE; |
|
|
|
|
|
|
|
|
|
private CameraCaptureSession mSession; |
|
|
|
|
private CaptureRequest.Builder mBuilder; |
|
|
|
|
private CameraCaptureSession.CaptureCallback mCallback; |
|
|
|
|
private ImageReader mPictureReader; |
|
|
|
|
private CaptureRequest.Builder mPictureBuilder; |
|
|
|
|
private boolean mStopPreviewBeforeCapture; |
|
|
|
|
private int mState = STATE_IDLE; |
|
|
|
|
|
|
|
|
|
public Full2PictureRecorder(@NonNull PictureResult.Stub stub, |
|
|
|
@ -55,42 +59,16 @@ public class Full2PictureRecorder extends PictureRecorder { |
|
|
|
|
@NonNull CaptureRequest.Builder builder, |
|
|
|
|
@NonNull CameraCaptureSession.CaptureCallback callback, |
|
|
|
|
@NonNull CaptureRequest.Builder pictureBuilder, |
|
|
|
|
@NonNull ImageReader pictureReader) { |
|
|
|
|
@NonNull ImageReader pictureReader, |
|
|
|
|
boolean stopPreviewBeforeCapture) { |
|
|
|
|
super(stub, listener); |
|
|
|
|
mSession = session; |
|
|
|
|
mBuilder = builder; |
|
|
|
|
mCallback = callback; |
|
|
|
|
mPictureBuilder = pictureBuilder; |
|
|
|
|
mStopPreviewBeforeCapture = stopPreviewBeforeCapture; |
|
|
|
|
mPictureReader = pictureReader; |
|
|
|
|
mPictureReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { |
|
|
|
|
@Override |
|
|
|
|
public void onImageAvailable(ImageReader reader) { |
|
|
|
|
mState = STATE_IDLE; |
|
|
|
|
|
|
|
|
|
// Read the JPEG.
|
|
|
|
|
try { |
|
|
|
|
Image image = reader.acquireLatestImage(); |
|
|
|
|
ByteBuffer buffer = image.getPlanes()[0].getBuffer(); |
|
|
|
|
byte[] bytes = new byte[buffer.remaining()]; |
|
|
|
|
buffer.get(bytes); |
|
|
|
|
mResult.data = bytes; |
|
|
|
|
mResult.format = PictureResult.FORMAT_JPEG; |
|
|
|
|
} catch (Exception e) { |
|
|
|
|
mResult = null; |
|
|
|
|
mError = e; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Before leaving, unlock focus.
|
|
|
|
|
try { |
|
|
|
|
mBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, |
|
|
|
|
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); |
|
|
|
|
mSession.capture(mBuilder.build(), mCallback, null); |
|
|
|
|
} catch (CameraAccessException ignore) { } |
|
|
|
|
|
|
|
|
|
// Leave.
|
|
|
|
|
dispatchResult(); |
|
|
|
|
} |
|
|
|
|
}, null); |
|
|
|
|
mPictureReader.setOnImageAvailableListener(this, WorkerHandler.get().getHandler()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@ -126,11 +104,18 @@ public class Full2PictureRecorder extends PictureRecorder { |
|
|
|
|
|
|
|
|
|
private void runCapture() { |
|
|
|
|
try { |
|
|
|
|
mPictureBuilder.setTag(CameraDevice.TEMPLATE_STILL_CAPTURE); |
|
|
|
|
mPictureBuilder.setTag(REQUEST_TAG); |
|
|
|
|
mPictureBuilder.addTarget(mPictureReader.getSurface()); |
|
|
|
|
mPictureBuilder.set(CaptureRequest.JPEG_ORIENTATION, mResult.rotation); |
|
|
|
|
// mCaptureSession.stopRepeating();
|
|
|
|
|
mSession.abortCaptures(); |
|
|
|
|
if (mStopPreviewBeforeCapture) { |
|
|
|
|
// These two are present in official samples and are probably meant to speed things up?
|
|
|
|
|
// But from my tests, they actually make everything slower. So this is disabled by default
|
|
|
|
|
// with a boolean coming from the engine. Maybe in the future we can make this configurable
|
|
|
|
|
// as some people might want to stop the preview while picture is being taken even if it
|
|
|
|
|
// increases the latency.
|
|
|
|
|
mSession.stopRepeating(); |
|
|
|
|
mSession.abortCaptures(); |
|
|
|
|
} |
|
|
|
|
mSession.capture(mPictureBuilder.build(), mCallback, null); |
|
|
|
|
} catch (CameraAccessException e) { |
|
|
|
|
mResult = null; |
|
|
|
@ -139,7 +124,21 @@ public class Full2PictureRecorder extends PictureRecorder { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void process(@NonNull CaptureResult result) { |
|
|
|
|
public void onCaptureStarted(@NonNull CaptureRequest request) { |
|
|
|
|
if (request.getTag() == (Integer) REQUEST_TAG) { |
|
|
|
|
dispatchOnShutter(false); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void onCaptureProgressed(@NonNull CaptureResult result) { |
|
|
|
|
process(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void onCaptureCompleted(@NonNull CaptureResult result) { |
|
|
|
|
process(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void process(@NonNull CaptureResult result) { |
|
|
|
|
switch (mState) { |
|
|
|
|
case STATE_IDLE: break; |
|
|
|
|
case STATE_WAITING_FOCUS_LOCK: { |
|
|
|
@ -179,7 +178,7 @@ public class Full2PictureRecorder extends PictureRecorder { |
|
|
|
|
} |
|
|
|
|
case STATE_WAITING_CAPTURE: { |
|
|
|
|
if (result instanceof TotalCaptureResult |
|
|
|
|
&& result.getRequest().getTag() == (Integer) CameraDevice.TEMPLATE_STILL_CAPTURE) { |
|
|
|
|
&& result.getRequest().getTag() == (Integer) REQUEST_TAG) { |
|
|
|
|
mState = STATE_WAITING_IMAGE; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
@ -187,6 +186,50 @@ public class Full2PictureRecorder extends PictureRecorder { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void onImageAvailable(ImageReader reader) { |
|
|
|
|
mState = STATE_IDLE; |
|
|
|
|
|
|
|
|
|
// Read the JPEG.
|
|
|
|
|
Image image = null; |
|
|
|
|
//noinspection TryFinallyCanBeTryWithResources
|
|
|
|
|
try { |
|
|
|
|
image = reader.acquireNextImage(); |
|
|
|
|
ByteBuffer buffer = image.getPlanes()[0].getBuffer(); |
|
|
|
|
byte[] bytes = new byte[buffer.remaining()]; |
|
|
|
|
buffer.get(bytes); |
|
|
|
|
mResult.data = bytes; |
|
|
|
|
} catch (Exception e) { |
|
|
|
|
mResult = null; |
|
|
|
|
mError = e; |
|
|
|
|
dispatchResult(); |
|
|
|
|
return; |
|
|
|
|
} finally { |
|
|
|
|
if (image != null) image.close(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Just like Camera1, unfortunately, the camera might rotate the image
|
|
|
|
|
// and put EXIF=0 instead of respecting our EXIF and leave the image unaltered.
|
|
|
|
|
mResult.format = PictureResult.FORMAT_JPEG; |
|
|
|
|
mResult.rotation = 0; |
|
|
|
|
try { |
|
|
|
|
ExifInterface exif = new ExifInterface(new ByteArrayInputStream(mResult.data)); |
|
|
|
|
int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); |
|
|
|
|
mResult.rotation = ExifHelper.readExifOrientation(exifOrientation); |
|
|
|
|
} catch (IOException ignore) { } |
|
|
|
|
|
|
|
|
|
// Before leaving, unlock focus.
|
|
|
|
|
try { |
|
|
|
|
mBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, |
|
|
|
|
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); |
|
|
|
|
mSession.capture(mBuilder.build(), mCallback, null); |
|
|
|
|
} catch (CameraAccessException ignore) { } |
|
|
|
|
|
|
|
|
|
// Leave.
|
|
|
|
|
dispatchResult(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
protected void dispatchResult() { |
|
|
|
|
mState = STATE_IDLE; |
|
|
|
|