|
|
|
@ -35,7 +35,8 @@ import com.otaliastudios.cameraview.internal.utils.CropHelper; |
|
|
|
|
import com.otaliastudios.cameraview.internal.utils.Op; |
|
|
|
|
import com.otaliastudios.cameraview.picture.FullPictureRecorder; |
|
|
|
|
import com.otaliastudios.cameraview.picture.PictureRecorder; |
|
|
|
|
import com.otaliastudios.cameraview.picture.SnapshotPictureRecorder; |
|
|
|
|
import com.otaliastudios.cameraview.picture.Snapshot1PictureRecorder; |
|
|
|
|
import com.otaliastudios.cameraview.picture.SnapshotGlPictureRecorder; |
|
|
|
|
import com.otaliastudios.cameraview.preview.GlCameraPreview; |
|
|
|
|
import com.otaliastudios.cameraview.size.AspectRatio; |
|
|
|
|
import com.otaliastudios.cameraview.size.Size; |
|
|
|
@ -50,9 +51,7 @@ import java.util.List; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("deprecation") |
|
|
|
|
public class Camera1Engine extends CameraEngine implements Camera.PreviewCallback, Camera.ErrorCallback, |
|
|
|
|
VideoRecorder.VideoResultListener, |
|
|
|
|
PictureRecorder.PictureResultListener { |
|
|
|
|
public class Camera1Engine extends CameraEngine implements Camera.PreviewCallback, Camera.ErrorCallback { |
|
|
|
|
|
|
|
|
|
private static final String TAG = Camera1Engine.class.getSimpleName(); |
|
|
|
|
private static final CameraLogger LOG = CameraLogger.create(TAG); |
|
|
|
@ -87,7 +86,7 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void schedule(@Nullable final Op<Void> op, final boolean ensureAvailable, final Runnable action) { |
|
|
|
|
mHandler.post(new Runnable() { |
|
|
|
|
mHandler.run(new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (ensureAvailable && !isCameraAvailable()) { |
|
|
|
@ -104,34 +103,29 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
@WorkerThread |
|
|
|
|
@Override |
|
|
|
|
protected Task<Void> onStartEngine() { |
|
|
|
|
if (collectCameraId()) { |
|
|
|
|
try { |
|
|
|
|
mCamera = Camera.open(mCameraId); |
|
|
|
|
} catch (Exception e) { |
|
|
|
|
LOG.e("onStartEngine:", "Failed to connect. Maybe in use by another app?"); |
|
|
|
|
throw new CameraException(e, CameraException.REASON_FAILED_TO_CONNECT); |
|
|
|
|
} |
|
|
|
|
mCamera.setErrorCallback(this); |
|
|
|
|
|
|
|
|
|
// Set parameters that might have been set before the camera was opened.
|
|
|
|
|
LOG.i("onStartEngine:", "Applying default parameters."); |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
mCameraOptions = new CameraOptions(params, flip(REF_SENSOR, REF_VIEW)); |
|
|
|
|
applyDefaultFocus(params); |
|
|
|
|
applyFlash(params, Flash.OFF); |
|
|
|
|
applyLocation(params, null); |
|
|
|
|
applyWhiteBalance(params, WhiteBalance.AUTO); |
|
|
|
|
applyHdr(params, Hdr.OFF); |
|
|
|
|
applyPlaySounds(mPlaySounds); |
|
|
|
|
params.setRecordingHint(mMode == Mode.VIDEO); |
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
mCamera.setDisplayOrientation(offset(REF_SENSOR, REF_VIEW)); // <- not allowed during preview
|
|
|
|
|
LOG.i("onStartEngine:", "Ended"); |
|
|
|
|
return Tasks.forResult(null); |
|
|
|
|
} else { |
|
|
|
|
LOG.e("onStartEngine:", "No camera available for facing", mFacing); |
|
|
|
|
throw new CameraException(CameraException.REASON_NO_CAMERA); |
|
|
|
|
try { |
|
|
|
|
mCamera = Camera.open(mCameraId); |
|
|
|
|
} catch (Exception e) { |
|
|
|
|
LOG.e("onStartEngine:", "Failed to connect. Maybe in use by another app?"); |
|
|
|
|
throw new CameraException(e, CameraException.REASON_FAILED_TO_CONNECT); |
|
|
|
|
} |
|
|
|
|
mCamera.setErrorCallback(this); |
|
|
|
|
|
|
|
|
|
// Set parameters that might have been set before the camera was opened.
|
|
|
|
|
LOG.i("onStartEngine:", "Applying default parameters."); |
|
|
|
|
Camera.Parameters params = mCamera.getParameters(); |
|
|
|
|
mCameraOptions = new CameraOptions(params, flip(REF_SENSOR, REF_VIEW)); |
|
|
|
|
applyDefaultFocus(params); |
|
|
|
|
applyFlash(params, Flash.OFF); |
|
|
|
|
applyLocation(params, null); |
|
|
|
|
applyWhiteBalance(params, WhiteBalance.AUTO); |
|
|
|
|
applyHdr(params, Hdr.OFF); |
|
|
|
|
applyPlaySounds(mPlaySounds); |
|
|
|
|
params.setRecordingHint(mMode == Mode.VIDEO); |
|
|
|
|
mCamera.setParameters(params); |
|
|
|
|
mCamera.setDisplayOrientation(offset(REF_SENSOR, REF_VIEW)); // <- not allowed during preview
|
|
|
|
|
LOG.i("onStartEngine:", "Ended"); |
|
|
|
|
return Tasks.forResult(null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@NonNull |
|
|
|
@ -269,9 +263,10 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
restartPreview(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean collectCameraId() { |
|
|
|
|
int internalFacing = mMapper.map(mFacing); |
|
|
|
|
LOG.i("collectCameraId", "Facing:", mFacing, "Internal:", internalFacing, "Cameras:", Camera.getNumberOfCameras()); |
|
|
|
|
@Override |
|
|
|
|
protected boolean collectCameraInfo(@NonNull Facing facing) { |
|
|
|
|
int internalFacing = mMapper.map(facing); |
|
|
|
|
LOG.i("collectCameraInfo", "Facing:", facing, "Internal:", internalFacing, "Cameras:", Camera.getNumberOfCameras()); |
|
|
|
|
Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); |
|
|
|
|
for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) { |
|
|
|
|
Camera.getCameraInfo(i, cameraInfo); |
|
|
|
@ -284,6 +279,7 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void onBufferAvailable(@NonNull byte[] buffer) { |
|
|
|
|
// TODO: sync with handler?
|
|
|
|
@ -350,24 +346,6 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void setFacing(@NonNull Facing facing) { |
|
|
|
|
final Facing old = mFacing; |
|
|
|
|
if (facing != old) { |
|
|
|
|
mFacing = facing; |
|
|
|
|
schedule(null, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (collectCameraId()) { |
|
|
|
|
restart(); |
|
|
|
|
} else { |
|
|
|
|
mFacing = old; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void setWhiteBalance(@NonNull WhiteBalance whiteBalance) { |
|
|
|
|
final WhiteBalance old = mWhiteBalance; |
|
|
|
@ -434,18 +412,6 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void setAudio(@NonNull Audio audio) { |
|
|
|
|
if (mAudio != audio) { |
|
|
|
|
if (isTakingVideo()) { |
|
|
|
|
LOG.w("Audio setting was changed while recording. " + |
|
|
|
|
"Changes will take place starting from next video"); |
|
|
|
|
} |
|
|
|
|
mAudio = audio; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void setFlash(@NonNull Flash flash) { |
|
|
|
|
final Flash old = mFlash; |
|
|
|
@ -500,24 +466,6 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
// -----------------
|
|
|
|
|
// Picture recording stuff.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void onPictureShutter(boolean didPlaySound) { |
|
|
|
|
mCallback.onShutter(!didPlaySound); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void onPictureResult(@Nullable PictureResult.Stub result) { |
|
|
|
|
mPictureRecorder = null; |
|
|
|
|
if (result != null) { |
|
|
|
|
mCallback.dispatchOnPictureTaken(result); |
|
|
|
|
} else { |
|
|
|
|
// Something went wrong.
|
|
|
|
|
mCallback.dispatchError(new CameraException(CameraException.REASON_PICTURE_FAILED)); |
|
|
|
|
LOG.e("onPictureResult", "result is null: something went wrong."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void takePicture(final @NonNull PictureResult.Stub stub) { |
|
|
|
|
LOG.v("takePicture: scheduling"); |
|
|
|
@ -543,34 +491,19 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Just a note about the snapshot size - it is the PreviewStreamSize, cropped with the view ratio. |
|
|
|
|
* @param viewAspectRatio the view aspect ratio |
|
|
|
|
*/ |
|
|
|
|
@WorkerThread |
|
|
|
|
@Override |
|
|
|
|
public void takePictureSnapshot(final @NonNull PictureResult.Stub stub, @NonNull final AspectRatio viewAspectRatio) { |
|
|
|
|
LOG.v("takePictureSnapshot: scheduling"); |
|
|
|
|
schedule(null, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
LOG.v("takePictureSnapshot: performing.", isTakingPicture()); |
|
|
|
|
if (isTakingPicture()) return; |
|
|
|
|
|
|
|
|
|
stub.location = mLocation; |
|
|
|
|
stub.isSnapshot = true; |
|
|
|
|
stub.facing = mFacing; |
|
|
|
|
stub.size = getUncroppedSnapshotSize(REF_OUTPUT); // Not the real size: it will be cropped to match the view ratio
|
|
|
|
|
stub.rotation = offset(REF_SENSOR, REF_OUTPUT); // Actually it will be rotated and set to 0.
|
|
|
|
|
AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; |
|
|
|
|
protected void onTakePictureSnapshot(@NonNull PictureResult.Stub stub, @NonNull AspectRatio viewAspectRatio) { |
|
|
|
|
stub.size = getUncroppedSnapshotSize(REF_OUTPUT); // Not the real size: it will be cropped to match the view ratio
|
|
|
|
|
stub.rotation = offset(REF_SENSOR, REF_OUTPUT); // Actually it will be rotated and set to 0.
|
|
|
|
|
AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; |
|
|
|
|
|
|
|
|
|
LOG.v("Rotations", "SV", offset(REF_SENSOR, REF_VIEW), "VS", offset(REF_VIEW, REF_SENSOR)); |
|
|
|
|
LOG.v("Rotations", "SO", offset(REF_SENSOR, REF_OUTPUT), "OS", offset(REF_OUTPUT, REF_SENSOR)); |
|
|
|
|
LOG.v("Rotations", "VO", offset(REF_VIEW, REF_OUTPUT), "OV", offset(REF_OUTPUT, REF_VIEW)); |
|
|
|
|
|
|
|
|
|
mPictureRecorder = new SnapshotPictureRecorder(stub, Camera1Engine.this, mPreview, mCamera, outputRatio); |
|
|
|
|
mPictureRecorder.take(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
if (mPreview instanceof GlCameraPreview && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { |
|
|
|
|
mPictureRecorder = new SnapshotGlPictureRecorder(stub, this, (GlCameraPreview) mPreview, outputRatio); |
|
|
|
|
} else { |
|
|
|
|
mPictureRecorder = new Snapshot1PictureRecorder(stub, this, mCamera, outputRatio); |
|
|
|
|
} |
|
|
|
|
mPictureRecorder.take(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@ -588,12 +521,8 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void onVideoResult(@Nullable VideoResult.Stub result, @Nullable Exception exception) { |
|
|
|
|
mVideoRecorder = null; |
|
|
|
|
if (result != null) { |
|
|
|
|
mCallback.dispatchOnVideoTaken(result); |
|
|
|
|
} else { |
|
|
|
|
if (result == null) { |
|
|
|
|
// Something went wrong, lock the camera again.
|
|
|
|
|
mCallback.dispatchError(new CameraException(exception, CameraException.REASON_VIDEO_FAILED)); |
|
|
|
|
mCamera.lock(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -639,103 +568,67 @@ public class Camera1Engine extends CameraEngine implements Camera.PreviewCallbac |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param file the output file |
|
|
|
|
* @param viewAspectRatio the view aspect ratio |
|
|
|
|
*/ |
|
|
|
|
@SuppressLint("NewApi") |
|
|
|
|
@WorkerThread |
|
|
|
|
@Override |
|
|
|
|
public void takeVideoSnapshot(final @NonNull VideoResult.Stub stub, @NonNull final File file, @NonNull final AspectRatio viewAspectRatio) { |
|
|
|
|
protected void onTakeVideoSnapshot(@NonNull VideoResult.Stub stub, @NonNull File file, @NonNull AspectRatio viewAspectRatio) { |
|
|
|
|
if (!(mPreview instanceof GlCameraPreview)) { |
|
|
|
|
throw new IllegalStateException("Video snapshots are only supported with GlCameraPreview."); |
|
|
|
|
} |
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { |
|
|
|
|
throw new IllegalStateException("Video snapshots are only supported starting from API 18."); |
|
|
|
|
} |
|
|
|
|
schedule(mStartVideoOp, true, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
if (isTakingVideo()) return; |
|
|
|
|
|
|
|
|
|
// Create the video result stub
|
|
|
|
|
stub.file = file; |
|
|
|
|
stub.isSnapshot = true; |
|
|
|
|
stub.videoCodec = mVideoCodec; |
|
|
|
|
stub.location = mLocation; |
|
|
|
|
stub.facing = mFacing; |
|
|
|
|
stub.videoBitRate = mVideoBitRate; |
|
|
|
|
stub.audioBitRate = mAudioBitRate; |
|
|
|
|
stub.audio = mAudio; |
|
|
|
|
stub.maxSize = mVideoMaxSize; |
|
|
|
|
stub.maxDuration = mVideoMaxDuration; |
|
|
|
|
|
|
|
|
|
// Size and rotation turned out to be extremely tricky. In case of SnapshotPictureRecorder
|
|
|
|
|
// we use the preview size in REF_OUTPUT (cropped) and offset(REF_SENSOR, REF_OUTPUT) as rotation.
|
|
|
|
|
// These values mean that we expect input to be in the REF_SENSOR system.
|
|
|
|
|
|
|
|
|
|
// Here everything seems different. We would expect a difference because the two snapshot
|
|
|
|
|
// recorders have different mechanics (the picture one uses a SurfaceTexture with setBufferSize,
|
|
|
|
|
// the video one here uses the MediaCodec input surface which we can't control).
|
|
|
|
|
|
|
|
|
|
// The strangest thing is the fact that the correct angle seems to be the same for FRONT and
|
|
|
|
|
// BACK sensor, which means that our sensor correction actually screws things up. For this reason
|
|
|
|
|
// facing value is temporarily set to BACK.
|
|
|
|
|
Facing realFacing = mFacing; |
|
|
|
|
mFacing = Facing.BACK; |
|
|
|
|
|
|
|
|
|
// These are the angles that make it work on a Nexus5X, compared to the offset() results.
|
|
|
|
|
// For instance, SV means offset(REF_SENSOR, REF_VIEW). The rest should be clear.
|
|
|
|
|
// CONFIG | WANTED | SV | VS | VO | OV | SO | OS |
|
|
|
|
|
// ------------|--------|--------|--------|--------|--------|--------|--------|
|
|
|
|
|
// Vertical | 0 | 270 | 90 | 0 | 0 | 270 | 90 |
|
|
|
|
|
// Left | 270 | 270 | 90 | 270 | 90 | 180 | 180 |
|
|
|
|
|
// Right | 90 | 270 | 90 | 90 | 270 | 0 | 0 |
|
|
|
|
|
// Upside down | 180 | 270 | 90 | 180 | 180 | 90 | 270 |
|
|
|
|
|
|
|
|
|
|
// The VO is the only correct value. Things change when using FRONT camera, in which case,
|
|
|
|
|
// no value is actually correct, and the needed values are the same of BACK!
|
|
|
|
|
// CONFIG | WANTED | SV | VS | VO | OV | SO | OS |
|
|
|
|
|
// ------------|--------|--------|--------|--------|--------|--------|--------|
|
|
|
|
|
// Vertical | 0 | 90 | 270 | 180 | 180 | 270 | 90 |
|
|
|
|
|
// Left | 270 | 90 | 270 | 270 | 90 | 0 | 0 |
|
|
|
|
|
// Right | 90 | 90 | 270 | 90 | 270 | 180 | 180 |
|
|
|
|
|
// Upside down | 180 | 90 | 270 | 0 | 0 | 90 | 270 |
|
|
|
|
|
|
|
|
|
|
// Based on this we will use VO for everything. See if we get issues about distortion
|
|
|
|
|
// and maybe we can improve. The reason why this happen is beyond my understanding.
|
|
|
|
|
|
|
|
|
|
Size outputSize = getUncroppedSnapshotSize(REF_OUTPUT); |
|
|
|
|
if (outputSize == null) { |
|
|
|
|
throw new IllegalStateException("outputSize should not be null."); |
|
|
|
|
} |
|
|
|
|
AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; |
|
|
|
|
Rect outputCrop = CropHelper.computeCrop(outputSize, outputRatio); |
|
|
|
|
outputSize = new Size(outputCrop.width(), outputCrop.height()); |
|
|
|
|
stub.size = outputSize; |
|
|
|
|
stub.rotation = offset(REF_VIEW, REF_OUTPUT); |
|
|
|
|
|
|
|
|
|
// Reset facing and start.
|
|
|
|
|
mFacing = realFacing; |
|
|
|
|
GlCameraPreview cameraPreview = (GlCameraPreview) mPreview; |
|
|
|
|
mVideoRecorder = new SnapshotVideoRecorder(stub, |
|
|
|
|
Camera1Engine.this, Camera1Engine.this, cameraPreview); |
|
|
|
|
mVideoRecorder.start(); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void stopVideo() { |
|
|
|
|
schedule(null, false, new Runnable() { |
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
LOG.i("stopVideo", "mVideoRecorder is null?", mVideoRecorder == null); |
|
|
|
|
if (mVideoRecorder != null) { |
|
|
|
|
mVideoRecorder.stop(); |
|
|
|
|
mVideoRecorder = null; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
GlCameraPreview glPreview = (GlCameraPreview) mPreview; |
|
|
|
|
|
|
|
|
|
// Size and rotation turned out to be extremely tricky. In case of Snapshot1PictureRecorder
|
|
|
|
|
// we use the preview size in REF_OUTPUT (cropped) and offset(REF_SENSOR, REF_OUTPUT) as rotation.
|
|
|
|
|
// These values mean that we expect input to be in the REF_SENSOR system.
|
|
|
|
|
|
|
|
|
|
// Here everything seems different. We would expect a difference because the two snapshot
|
|
|
|
|
// recorders have different mechanics (the picture one uses a SurfaceTexture with setBufferSize,
|
|
|
|
|
// the video one here uses the MediaCodec input surface which we can't control).
|
|
|
|
|
|
|
|
|
|
// The strangest thing is the fact that the correct angle seems to be the same for FRONT and
|
|
|
|
|
// BACK sensor, which means that our sensor correction actually screws things up. For this reason
|
|
|
|
|
// facing value is temporarily set to BACK.
|
|
|
|
|
Facing realFacing = mFacing; |
|
|
|
|
mFacing = Facing.BACK; |
|
|
|
|
|
|
|
|
|
// These are the angles that make it work on a Nexus5X, compared to the offset() results.
|
|
|
|
|
// For instance, SV means offset(REF_SENSOR, REF_VIEW). The rest should be clear.
|
|
|
|
|
// CONFIG | WANTED | SV | VS | VO | OV | SO | OS |
|
|
|
|
|
// ------------|--------|--------|--------|--------|--------|--------|--------|
|
|
|
|
|
// Vertical | 0 | 270 | 90 | 0 | 0 | 270 | 90 |
|
|
|
|
|
// Left | 270 | 270 | 90 | 270 | 90 | 180 | 180 |
|
|
|
|
|
// Right | 90 | 270 | 90 | 90 | 270 | 0 | 0 |
|
|
|
|
|
// Upside down | 180 | 270 | 90 | 180 | 180 | 90 | 270 |
|
|
|
|
|
|
|
|
|
|
// The VO is the only correct value. Things change when using FRONT camera, in which case,
|
|
|
|
|
// no value is actually correct, and the needed values are the same of BACK!
|
|
|
|
|
// CONFIG | WANTED | SV | VS | VO | OV | SO | OS |
|
|
|
|
|
// ------------|--------|--------|--------|--------|--------|--------|--------|
|
|
|
|
|
// Vertical | 0 | 90 | 270 | 180 | 180 | 270 | 90 |
|
|
|
|
|
// Left | 270 | 90 | 270 | 270 | 90 | 0 | 0 |
|
|
|
|
|
// Right | 90 | 90 | 270 | 90 | 270 | 180 | 180 |
|
|
|
|
|
// Upside down | 180 | 90 | 270 | 0 | 0 | 90 | 270 |
|
|
|
|
|
|
|
|
|
|
// Based on this we will use VO for everything. See if we get issues about distortion
|
|
|
|
|
// and maybe we can improve. The reason why this happen is beyond my understanding.
|
|
|
|
|
|
|
|
|
|
Size outputSize = getUncroppedSnapshotSize(REF_OUTPUT); |
|
|
|
|
if (outputSize == null) { |
|
|
|
|
throw new IllegalStateException("outputSize should not be null."); |
|
|
|
|
} |
|
|
|
|
AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; |
|
|
|
|
Rect outputCrop = CropHelper.computeCrop(outputSize, outputRatio); |
|
|
|
|
outputSize = new Size(outputCrop.width(), outputCrop.height()); |
|
|
|
|
stub.size = outputSize; |
|
|
|
|
stub.rotation = offset(REF_VIEW, REF_OUTPUT); |
|
|
|
|
|
|
|
|
|
// Reset facing and start.
|
|
|
|
|
mFacing = realFacing; |
|
|
|
|
mVideoRecorder = new SnapshotVideoRecorder(stub, Camera1Engine.this, glPreview); |
|
|
|
|
mVideoRecorder.start(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// -----------------
|
|
|
|
|