|
|
|
@ -38,7 +38,6 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
private Runnable mPostFocusResetRunnable = new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
if (!isCameraAvailable()) return; |
|
|
|
|
mCamera.cancelAutoFocus(); |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
@ -47,89 +46,95 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
applyDefaultFocus(params); // Revert to internal focus.
|
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
private Mapper mMapper = new Mapper.Mapper1(); |
|
|
|
|
private boolean mIsSetup = false; |
|
|
|
|
private ScheduleRunnable mScheduleRunnable; |
|
|
|
|
private Mapper mMapper; |
|
|
|
|
private boolean mIsBound = false; |
|
|
|
|
private boolean mIsCapturingImage = false; |
|
|
|
|
private boolean mIsCapturingVideo = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Camera1(CameraView.CameraCallbacks callback) { |
|
|
|
|
super(callback); |
|
|
|
|
mScheduleRunnable = new ScheduleRunnable(); |
|
|
|
|
mMapper = new Mapper.Mapper1(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Preview surface is now available. If camera is open, set up. |
|
|
|
|
*/ |
|
|
|
|
private class ScheduleRunnable implements Runnable { |
|
|
|
|
private Task<Void> mTask; |
|
|
|
|
private Runnable mAction; |
|
|
|
|
private boolean mEnsureAvailable; |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (mEnsureAvailable && !isCameraAvailable()) { |
|
|
|
|
if (mTask != null) mTask.end(null); |
|
|
|
|
} else { |
|
|
|
|
mAction.run(); |
|
|
|
|
if (mTask != null) mTask.end(null); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void schedule(@Nullable Task<Void> task, boolean ensureAvailable, Runnable action) { |
|
|
|
|
mScheduleRunnable.mEnsureAvailable = ensureAvailable; |
|
|
|
|
mScheduleRunnable.mAction = action; |
|
|
|
|
mScheduleRunnable.mTask = task; |
|
|
|
|
mHandler.post(mScheduleRunnable); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Preview surface is now available. If camera is open, set up.
|
|
|
|
|
@Override |
|
|
|
|
public void onSurfaceAvailable() { |
|
|
|
|
LOG.i("onSurfaceAvailable:", "Size is", mPreview.getSurfaceSize()); |
|
|
|
|
if (!shouldSetup()) return; |
|
|
|
|
mHandler.post(new Runnable() { |
|
|
|
|
schedule(null, false, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (!shouldSetup()) return; |
|
|
|
|
if (shouldBindToSurface()) { |
|
|
|
|
LOG.i("onSurfaceAvailable:", "Inside handler. About to bind."); |
|
|
|
|
try { |
|
|
|
|
setup(); |
|
|
|
|
bindToSurface(); |
|
|
|
|
} catch (Exception e) { |
|
|
|
|
LOG.w("onSurfaceAvailable:", "Exception while binding camera to preview.", e); |
|
|
|
|
LOG.e("onSurfaceAvailable:", "Exception while binding camera to preview.", e); |
|
|
|
|
throw new RuntimeException(e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Preview surface did change its size. Compute a new preview size. |
|
|
|
|
* This requires stopping and restarting the preview. |
|
|
|
|
*/ |
|
|
|
|
// Preview surface did change its size. Compute a new preview size.
|
|
|
|
|
// This requires stopping and restarting the preview.
|
|
|
|
|
@Override |
|
|
|
|
public void onSurfaceChanged() { |
|
|
|
|
LOG.i("onSurfaceChanged, size is", mPreview.getSurfaceSize()); |
|
|
|
|
if (mIsSetup && isCameraAvailable()) { |
|
|
|
|
schedule(null, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (!mIsBound) return; |
|
|
|
|
|
|
|
|
|
// Compute a new camera preview size.
|
|
|
|
|
Size newSize = computePreviewSize(); |
|
|
|
|
if (!newSize.equals(mPreviewSize)) { |
|
|
|
|
LOG.i("onSurfaceChanged:", "Computed a new preview size. Dispatching."); |
|
|
|
|
if (newSize.equals(mPreviewSize)) return; |
|
|
|
|
|
|
|
|
|
// Apply.
|
|
|
|
|
LOG.i("onSurfaceChanged:", "Computed a new preview size. Going on."); |
|
|
|
|
mPreviewSize = newSize; |
|
|
|
|
mCameraCallbacks.onCameraPreviewSizeChanged(); |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
LOG.i("onSurfaceChanged:", "Stopping preview."); |
|
|
|
|
mCamera.stopPreview(); |
|
|
|
|
LOG.i("onSurfaceChanged:", "Stopped preview."); |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
params.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); |
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
boolean invertPreviewSizes = shouldFlipSizes(); |
|
|
|
|
mPreview.setDesiredSize( |
|
|
|
|
invertPreviewSizes ? mPreviewSize.getHeight() : mPreviewSize.getWidth(), |
|
|
|
|
invertPreviewSizes ? mPreviewSize.getWidth() : mPreviewSize.getHeight() |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
mCamera.setPreviewCallbackWithBuffer(null); // This clears the buffers
|
|
|
|
|
mCamera.setPreviewCallbackWithBuffer(this); // Reset
|
|
|
|
|
mFrameManager.allocate(ImageFormat.getBitsPerPixel(mPreviewFormat), mPreviewSize); |
|
|
|
|
|
|
|
|
|
LOG.i("onSurfaceChanged:", "Restarting preview."); |
|
|
|
|
mCamera.startPreview(); |
|
|
|
|
LOG.i("onSurfaceChanged:", "Restarted preview."); |
|
|
|
|
} |
|
|
|
|
applySizesAndStartPreview("onSurfaceChanged:"); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean shouldSetup() { |
|
|
|
|
return isCameraAvailable() && mPreview != null && mPreview.isReady() && !mIsSetup; |
|
|
|
|
private boolean shouldBindToSurface() { |
|
|
|
|
return isCameraAvailable() && mPreview != null && mPreview.isReady() && !mIsBound; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// The act of binding an "open" camera to a "ready" preview.
|
|
|
|
|
// These can happen at different times but we want to end up here.
|
|
|
|
|
@WorkerThread |
|
|
|
|
private void setup() throws Exception { |
|
|
|
|
LOG.i("setup:", "Started"); |
|
|
|
|
private void bindToSurface() throws Exception { |
|
|
|
|
LOG.i("bindToSurface:", "Started"); |
|
|
|
|
Object output = mPreview.getOutput(); |
|
|
|
|
if (mPreview.getOutputClass() == SurfaceHolder.class) { |
|
|
|
|
mCamera.setPreviewDisplay((SurfaceHolder) output); |
|
|
|
@ -137,34 +142,38 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
mCamera.setPreviewTexture((SurfaceTexture) output); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
boolean invertPreviewSizes = shouldFlipSizes(); |
|
|
|
|
mCaptureSize = computeCaptureSize(); |
|
|
|
|
mPreviewSize = computePreviewSize(); |
|
|
|
|
LOG.i("setup:", "Dispatching onCameraPreviewSizeChanged."); |
|
|
|
|
applySizesAndStartPreview("bindToSurface:"); |
|
|
|
|
mIsBound = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// To be called when the preview size is setup or changed.
|
|
|
|
|
private void applySizesAndStartPreview(String log) { |
|
|
|
|
LOG.i(log, "Dispatching onCameraPreviewSizeChanged."); |
|
|
|
|
mCameraCallbacks.onCameraPreviewSizeChanged(); |
|
|
|
|
|
|
|
|
|
boolean invertPreviewSizes = shouldFlipSizes(); |
|
|
|
|
mPreview.setDesiredSize( |
|
|
|
|
invertPreviewSizes ? mPreviewSize.getHeight() : mPreviewSize.getWidth(), |
|
|
|
|
invertPreviewSizes ? mPreviewSize.getWidth() : mPreviewSize.getHeight() |
|
|
|
|
); |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
|
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
mPreviewFormat = params.getPreviewFormat(); |
|
|
|
|
params.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); // <- not allowed during preview
|
|
|
|
|
params.setPictureSize(mCaptureSize.getWidth(), mCaptureSize.getHeight()); // <- allowed
|
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mCamera.setPreviewCallbackWithBuffer(null); // Release anything left
|
|
|
|
|
mCamera.setPreviewCallbackWithBuffer(this); // Add ourselves
|
|
|
|
|
mFrameManager.allocate(ImageFormat.getBitsPerPixel(mPreviewFormat), mPreviewSize); |
|
|
|
|
|
|
|
|
|
LOG.i("setup:", "Starting preview with startPreview()."); |
|
|
|
|
LOG.i(log, "Starting preview with startPreview()."); |
|
|
|
|
mCamera.startPreview(); |
|
|
|
|
LOG.i("setup:", "Started preview with startPreview()."); |
|
|
|
|
mIsSetup = true; |
|
|
|
|
LOG.i(log, "Started preview."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@WorkerThread |
|
|
|
|
@Override |
|
|
|
|
void onStart() throws Exception { |
|
|
|
@ -176,7 +185,6 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
mCamera = Camera.open(mCameraId); |
|
|
|
|
|
|
|
|
|
// Set parameters that might have been set before the camera was opened.
|
|
|
|
|
synchronized (mLock) { |
|
|
|
|
LOG.i("onStart:", "Applying default parameters."); |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
mExtraProperties = new ExtraProperties(params); |
|
|
|
@ -185,13 +193,13 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
mergeFlash(params, Flash.DEFAULT); |
|
|
|
|
mergeLocation(params, null); |
|
|
|
|
mergeWhiteBalance(params, WhiteBalance.DEFAULT); |
|
|
|
|
mergeHdr(params, Hdr.DEFAULT); |
|
|
|
|
params.setRecordingHint(mSessionType == SessionType.VIDEO); |
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try starting preview.
|
|
|
|
|
mCamera.setDisplayOrientation(computeSensorToDisplayOffset()); // <- not allowed during preview
|
|
|
|
|
if (shouldSetup()) setup(); |
|
|
|
|
if (shouldBindToSurface()) bindToSurface(); |
|
|
|
|
LOG.i("onStart:", "Ended"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -205,7 +213,6 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
mFrameManager.release(); |
|
|
|
|
|
|
|
|
|
if (mCamera != null) { |
|
|
|
|
|
|
|
|
|
LOG.i("onStop:", "Clean up.", "Ending video?", mIsCapturingVideo); |
|
|
|
|
if (mIsCapturingVideo) endVideo(); |
|
|
|
|
|
|
|
|
@ -233,7 +240,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
mCamera = null; |
|
|
|
|
mPreviewSize = null; |
|
|
|
|
mCaptureSize = null; |
|
|
|
|
mIsSetup = false; |
|
|
|
|
mIsBound = false; |
|
|
|
|
|
|
|
|
|
if (error != null) throw error; |
|
|
|
|
} |
|
|
|
@ -254,6 +261,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void onBufferAvailable(byte[] buffer) { |
|
|
|
|
// TODO: sync with handler?
|
|
|
|
|
if (isCameraAvailable()) { |
|
|
|
|
mCamera.addCallbackBuffer(buffer); |
|
|
|
|
} |
|
|
|
@ -263,22 +271,26 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
void setSessionType(SessionType sessionType) { |
|
|
|
|
if (sessionType != mSessionType) { |
|
|
|
|
mSessionType = sessionType; |
|
|
|
|
if (isCameraAvailable()) { |
|
|
|
|
schedule(null, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
restart(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void setLocation(Location location) { |
|
|
|
|
Location oldLocation = mLocation; |
|
|
|
|
final Location oldLocation = mLocation; |
|
|
|
|
mLocation = location; |
|
|
|
|
if (isCameraAvailable()) { |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
schedule(mLocationTask, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
if (mergeLocation(params, oldLocation)) mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean mergeLocation(Camera.Parameters params, Location oldLocation) { |
|
|
|
@ -301,22 +313,28 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
void setFacing(Facing facing) { |
|
|
|
|
if (facing != mFacing) { |
|
|
|
|
mFacing = facing; |
|
|
|
|
if (collectCameraId() && isCameraAvailable()) { |
|
|
|
|
schedule(null, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (collectCameraId()) { |
|
|
|
|
restart(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void setWhiteBalance(WhiteBalance whiteBalance) { |
|
|
|
|
WhiteBalance old = mWhiteBalance; |
|
|
|
|
final WhiteBalance old = mWhiteBalance; |
|
|
|
|
mWhiteBalance = whiteBalance; |
|
|
|
|
if (isCameraAvailable()) { |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
schedule(mWhiteBalanceTask, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
if (mergeWhiteBalance(params, old)) mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean mergeWhiteBalance(Camera.Parameters params, WhiteBalance oldWhiteBalance) { |
|
|
|
@ -330,14 +348,15 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void setHdr(Hdr hdr) { |
|
|
|
|
Hdr old = mHdr; |
|
|
|
|
final Hdr old = mHdr; |
|
|
|
|
mHdr = hdr; |
|
|
|
|
if (isCameraAvailable()) { |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
schedule(mHdrTask, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
if (mergeHdr(params, old)) mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean mergeHdr(Camera.Parameters params, Hdr oldHdr) { |
|
|
|
@ -354,7 +373,8 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
void setAudio(Audio audio) { |
|
|
|
|
if (mAudio != audio) { |
|
|
|
|
if (mIsCapturingVideo) { |
|
|
|
|
LOG.w("Changing audio mode while recording. Changes will take place starting from next video"); |
|
|
|
|
LOG.w("Audio setting was changed while recording. " + |
|
|
|
|
"Changes will take place starting from next video"); |
|
|
|
|
} |
|
|
|
|
mAudio = audio; |
|
|
|
|
} |
|
|
|
@ -362,14 +382,15 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void setFlash(Flash flash) { |
|
|
|
|
Flash old = mFlash; |
|
|
|
|
final Flash old = mFlash; |
|
|
|
|
mFlash = flash; |
|
|
|
|
if (isCameraAvailable()) { |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
schedule(mFlashTask, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
if (mergeFlash(params, old)) mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -412,36 +433,43 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void setVideoQuality(VideoQuality videoQuality) { |
|
|
|
|
final VideoQuality old = mVideoQuality; |
|
|
|
|
mVideoQuality = videoQuality; |
|
|
|
|
schedule(mVideoQualityTask, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (mIsCapturingVideo) { |
|
|
|
|
// TODO: actually any call to getParameters() could fail while recording a video.
|
|
|
|
|
// See. https://stackoverflow.com/questions/14941625/correct-handling-of-exception-getparameters-failed-empty-parameters
|
|
|
|
|
// See. https://stackoverflow.com/questions/14941625/
|
|
|
|
|
mVideoQuality = old; |
|
|
|
|
throw new IllegalStateException("Can't change video quality while recording a video."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mVideoQuality = videoQuality; |
|
|
|
|
if (isCameraAvailable() && mSessionType == SessionType.VIDEO) { |
|
|
|
|
if (mSessionType == SessionType.VIDEO) { |
|
|
|
|
// Change capture size to a size that fits the video aspect ratio.
|
|
|
|
|
Size oldSize = mCaptureSize; |
|
|
|
|
mCaptureSize = computeCaptureSize(); |
|
|
|
|
if (!mCaptureSize.equals(oldSize)) { |
|
|
|
|
// New video quality triggers a new aspect ratio.
|
|
|
|
|
// Go on and see if preview size should change also.
|
|
|
|
|
synchronized (mLock) { |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
params.setPictureSize(mCaptureSize.getWidth(), mCaptureSize.getHeight()); |
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
onSurfaceChanged(); |
|
|
|
|
} |
|
|
|
|
LOG.i("setVideoQuality:", "captureSize:", mCaptureSize); |
|
|
|
|
LOG.i("setVideoQuality:", "previewSize:", mPreviewSize); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void capturePicture() { |
|
|
|
|
schedule(null, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (mIsCapturingImage) return; |
|
|
|
|
if (!isCameraAvailable()) return; |
|
|
|
|
if (mSessionType == SessionType.VIDEO && mIsCapturingVideo) { |
|
|
|
|
if (!mOptions.isVideoSnapshotSupported()) return; |
|
|
|
|
} |
|
|
|
@ -451,11 +479,10 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
final int exifRotation = computeExifRotation(); |
|
|
|
|
final boolean exifFlip = computeExifFlip(); |
|
|
|
|
final int sensorToDisplay = computeSensorToDisplayOffset(); |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
params.setRotation(exifRotation); |
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Is the final picture (decoded respecting EXIF) consistent with CameraView orientation?
|
|
|
|
|
// We must consider exifOrientation to bring back the picture in the sensor world.
|
|
|
|
|
// Then use sensorToDisplay to move to the display world, where CameraView lives.
|
|
|
|
@ -473,14 +500,11 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
@Override |
|
|
|
|
public void onPictureTaken(byte[] data, final Camera camera) { |
|
|
|
|
mIsCapturingImage = false; |
|
|
|
|
mHandler.post(new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
// This is needed, read somewhere in the docs.
|
|
|
|
|
camera.startPreview(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
mCameraCallbacks.processImage(data, consistentWithView, exifFlip); |
|
|
|
|
camera.startPreview(); // This is needed, read somewhere in the docs.
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
@ -488,7 +512,9 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void captureSnapshot() { |
|
|
|
|
if (!isCameraAvailable()) return; |
|
|
|
|
schedule(null, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (mIsCapturingImage) return; |
|
|
|
|
// This won't work while capturing a video.
|
|
|
|
|
// Switch to capturePicture.
|
|
|
|
@ -533,6 +559,8 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void onPreviewFrame(byte[] data, Camera camera) { |
|
|
|
@ -559,12 +587,11 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
// If we are going to be closed, don't act on camera.
|
|
|
|
|
// Even if mCamera != null, it might have been released.
|
|
|
|
|
case STATE_STOPPING: return false; |
|
|
|
|
// If we are started, act as long as there is no stop/restart scheduled.
|
|
|
|
|
// At this point mCamera should never be null.
|
|
|
|
|
case STATE_STARTED: return !mScheduledForStop && !mScheduledForRestart; |
|
|
|
|
// If we are started, mCamera should never be null.
|
|
|
|
|
case STATE_STARTED: return true; |
|
|
|
|
// If we are starting, theoretically we could act.
|
|
|
|
|
// Just check that camera is available.
|
|
|
|
|
case STATE_STARTING: return mCamera != null && !mScheduledForStop && !mScheduledForRestart; |
|
|
|
|
case STATE_STARTING: return mCamera != null; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
@ -628,7 +655,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
// The Camcorder internally checks for cameraParameters.getSupportedVideoSizes() etc.
|
|
|
|
|
// We want the picture size to be the max picture consistent with the video aspect ratio.
|
|
|
|
|
List<Size> captureSizes = sizesFromList(params.getSupportedPictureSizes()); |
|
|
|
|
CamcorderProfile profile = getCamcorderProfile(mVideoQuality); |
|
|
|
|
CamcorderProfile profile = getCamcorderProfile(mCameraId, mVideoQuality); |
|
|
|
|
AspectRatio targetRatio = AspectRatio.of(profile.videoFrameWidth, profile.videoFrameHeight); |
|
|
|
|
LOG.i("size:", "computeCaptureSize:", "videoQuality:", mVideoQuality, "targetRatio:", targetRatio); |
|
|
|
|
return matchSize(captureSizes, targetRatio, new Size(0, 0), true); |
|
|
|
@ -650,9 +677,11 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void startVideo(@NonNull File videoFile) { |
|
|
|
|
void startVideo(@NonNull final File videoFile) { |
|
|
|
|
schedule(null, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (mIsCapturingVideo) return; |
|
|
|
|
if (!isCameraAvailable()) return; |
|
|
|
|
if (mSessionType == SessionType.VIDEO) { |
|
|
|
|
mVideoFile = videoFile; |
|
|
|
|
mIsCapturingVideo = true; |
|
|
|
@ -664,15 +693,27 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
LOG.e("Error while starting MediaRecorder. Swallowing.", e); |
|
|
|
|
mVideoFile = null; |
|
|
|
|
mCamera.lock(); |
|
|
|
|
endVideo(); |
|
|
|
|
endVideoNow(); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
throw new IllegalStateException("Can't record video while session type is picture"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void endVideo() { |
|
|
|
|
schedule(null, false, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
endVideoNow(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@WorkerThread |
|
|
|
|
private void endVideoNow() { |
|
|
|
|
if (mIsCapturingVideo) { |
|
|
|
|
mIsCapturingVideo = false; |
|
|
|
|
if (mMediaRecorder != null) { |
|
|
|
@ -693,7 +734,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@WorkerThread |
|
|
|
|
private void initMediaRecorder() { |
|
|
|
|
mMediaRecorder = new MediaRecorder(); |
|
|
|
|
mCamera.unlock(); |
|
|
|
@ -701,7 +742,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
|
|
|
|
|
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); |
|
|
|
|
|
|
|
|
|
CamcorderProfile profile = getCamcorderProfile(mVideoQuality); |
|
|
|
|
CamcorderProfile profile = getCamcorderProfile(mCameraId, mVideoQuality); |
|
|
|
|
if (mAudio == Audio.ON) { |
|
|
|
|
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); |
|
|
|
|
mMediaRecorder.setProfile(profile); |
|
|
|
@ -724,48 +765,47 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
// Not needed. mMediaRecorder.setPreviewDisplay(mPreview.getSurface());
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
private CamcorderProfile getCamcorderProfile(VideoQuality videoQuality) { |
|
|
|
|
private static CamcorderProfile getCamcorderProfile(int cameraId, VideoQuality videoQuality) { |
|
|
|
|
switch (videoQuality) { |
|
|
|
|
case HIGHEST: |
|
|
|
|
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH); |
|
|
|
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH); |
|
|
|
|
|
|
|
|
|
case MAX_2160P: |
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && |
|
|
|
|
CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P)) { |
|
|
|
|
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_2160P); |
|
|
|
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_2160P); |
|
|
|
|
} |
|
|
|
|
// Don't break.
|
|
|
|
|
|
|
|
|
|
case MAX_1080P: |
|
|
|
|
if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P)) { |
|
|
|
|
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_1080P); |
|
|
|
|
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_1080P)) { |
|
|
|
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_1080P); |
|
|
|
|
} |
|
|
|
|
// Don't break.
|
|
|
|
|
|
|
|
|
|
case MAX_720P: |
|
|
|
|
if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P)) { |
|
|
|
|
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_720P); |
|
|
|
|
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_720P)) { |
|
|
|
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_720P); |
|
|
|
|
} |
|
|
|
|
// Don't break.
|
|
|
|
|
|
|
|
|
|
case MAX_480P: |
|
|
|
|
if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P)) { |
|
|
|
|
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_480P); |
|
|
|
|
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_480P)) { |
|
|
|
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_480P); |
|
|
|
|
} |
|
|
|
|
// Don't break.
|
|
|
|
|
|
|
|
|
|
case MAX_QVGA: |
|
|
|
|
if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA)) { |
|
|
|
|
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_QVGA); |
|
|
|
|
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_QVGA)) { |
|
|
|
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_QVGA); |
|
|
|
|
} |
|
|
|
|
// Don't break.
|
|
|
|
|
|
|
|
|
|
case LOWEST: |
|
|
|
|
default: |
|
|
|
|
// Fallback to lowest.
|
|
|
|
|
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_LOW); |
|
|
|
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -774,56 +814,74 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void setZoom(float zoom, PointF[] points, boolean notify) { |
|
|
|
|
if (!isCameraAvailable()) return; |
|
|
|
|
void setZoom(final float zoom, final PointF[] points, final boolean notify) { |
|
|
|
|
schedule(mZoomTask, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (!mOptions.isZoomSupported()) return; |
|
|
|
|
|
|
|
|
|
mZoomValue = zoom; |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
float max = params.getMaxZoom(); |
|
|
|
|
params.setZoom((int) (zoom * max)); |
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (notify) { |
|
|
|
|
mCameraCallbacks.dispatchOnZoomChanged(zoom, points); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void setExposureCorrection(float EVvalue, float[] bounds, PointF[] points, boolean notify) { |
|
|
|
|
if (!isCameraAvailable()) return; |
|
|
|
|
void setExposureCorrection(final float EVvalue, final float[] bounds, |
|
|
|
|
final PointF[] points, final boolean notify) { |
|
|
|
|
schedule(mExposureCorrectionTask, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (!mOptions.isExposureCorrectionSupported()) return; |
|
|
|
|
|
|
|
|
|
float value = EVvalue; |
|
|
|
|
float max = mOptions.getExposureCorrectionMaxValue(); |
|
|
|
|
float min = mOptions.getExposureCorrectionMinValue(); |
|
|
|
|
EVvalue = EVvalue < min ? min : EVvalue > max ? max : EVvalue; // cap
|
|
|
|
|
mExposureCorrectionValue = EVvalue; |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
value = value < min ? min : value > max ? max : value; // cap
|
|
|
|
|
mExposureCorrectionValue = value; |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
int indexValue = (int) (EVvalue / params.getExposureCompensationStep()); |
|
|
|
|
int indexValue = (int) (value / params.getExposureCompensationStep()); |
|
|
|
|
params.setExposureCompensation(indexValue); |
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (notify) { |
|
|
|
|
mCameraCallbacks.dispatchOnExposureCorrectionChanged(EVvalue, bounds, points); |
|
|
|
|
mCameraCallbacks.dispatchOnExposureCorrectionChanged(value, bounds, points); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// -----------------
|
|
|
|
|
// Tap to focus stuff.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
void startAutoFocus(@Nullable final Gesture gesture, PointF point) { |
|
|
|
|
if (!isCameraAvailable()) return; |
|
|
|
|
void startAutoFocus(@Nullable final Gesture gesture, final PointF point) { |
|
|
|
|
// Must get width and height from the UI thread.
|
|
|
|
|
int viewWidth = 0, viewHeight = 0; |
|
|
|
|
if (mPreview != null && mPreview.isReady()) { |
|
|
|
|
viewWidth = mPreview.getView().getWidth(); |
|
|
|
|
viewHeight = mPreview.getView().getHeight(); |
|
|
|
|
} |
|
|
|
|
final int viewWidthF = viewWidth; |
|
|
|
|
final int viewHeightF = viewHeight; |
|
|
|
|
// Schedule.
|
|
|
|
|
schedule(null, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (!mOptions.isAutoFocusSupported()) return; |
|
|
|
|
final PointF p = new PointF(point.x, point.y); // copy.
|
|
|
|
|
List<Camera.Area> meteringAreas2 = computeMeteringAreas(p.x, p.y); |
|
|
|
|
List<Camera.Area> meteringAreas2 = computeMeteringAreas(p.x, p.y, |
|
|
|
|
viewWidthF, viewHeightF, computeSensorToDisplayOffset()); |
|
|
|
|
List<Camera.Area> meteringAreas1 = meteringAreas2.subList(0, 1); |
|
|
|
|
synchronized (mLock) { |
|
|
|
|
|
|
|
|
|
// At this point we are sure that camera supports auto focus... right? Look at CameraView.onTouchEvent().
|
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
int maxAF = params.getMaxNumFocusAreas(); |
|
|
|
@ -844,17 +902,19 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private List<Camera.Area> computeMeteringAreas(double viewClickX, double viewClickY) { |
|
|
|
|
@WorkerThread |
|
|
|
|
private static List<Camera.Area> computeMeteringAreas(double viewClickX, double viewClickY, |
|
|
|
|
int viewWidth, int viewHeight, |
|
|
|
|
int sensorToDisplay) { |
|
|
|
|
// Event came in view coordinates. We must rotate to sensor coordinates.
|
|
|
|
|
// First, rescale to the -1000 ... 1000 range.
|
|
|
|
|
int displayToSensor = -computeSensorToDisplayOffset(); |
|
|
|
|
double viewWidth = mPreview.getView().getWidth(); |
|
|
|
|
double viewHeight = mPreview.getView().getHeight(); |
|
|
|
|
viewClickX = -1000d + (viewClickX / viewWidth) * 2000d; |
|
|
|
|
viewClickY = -1000d + (viewClickY / viewHeight) * 2000d; |
|
|
|
|
int displayToSensor = -sensorToDisplay; |
|
|
|
|
viewClickX = -1000d + (viewClickX / (double) viewWidth) * 2000d; |
|
|
|
|
viewClickY = -1000d + (viewClickY / (double) viewHeight) * 2000d; |
|
|
|
|
|
|
|
|
|
// Apply rotation to this point.
|
|
|
|
|
// https://academo.org/demos/rotation-about-point/
|
|
|
|
@ -877,7 +937,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Rect computeMeteringArea(double centerX, double centerY, double size) { |
|
|
|
|
private static Rect computeMeteringArea(double centerX, double centerY, double size) { |
|
|
|
|
double delta = size / 2d; |
|
|
|
|
int top = (int) Math.max(centerY - delta, -1000); |
|
|
|
|
int bottom = (int) Math.min(centerY + delta, 1000); |
|
|
|
|