|  |  |  | @ -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; | 
			
		
	
	
		
			
				
					|  |  |  | 
 |