diff --git a/MIGRATION.md b/MIGRATION.md index b721e534..9332220c 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -22,4 +22,13 @@ - CameraOptions.isVideoSnapshotSupported(): removed, this would be ambiguous now. While in video mode, you can only use takePictureSnapshot(), not takePicture(). - takePicture(): will now throw an exception if called when Mode == Mode.VIDEO. You can only take snapshots. -- VideoQuality: this has been removed. \ No newline at end of file +- VideoQuality: this has been removed. +- CameraOptions: methods returning a Set now return a Collection. +- CameraOptions: in addition to getSupportedPictureSizes and getSupportedPictureAspectRatio, + now there are video mode equivalents too. +- getPictureSize(): now it returns null when mode == Mode.VIDEO. +- getVideoSize(): added. Returns the size of the capture in video mode. Returns null when + mode == Mode.PICTURE. +- VideoSizeSelector: added. It is needed to choose the capture size in VIDEO mode. + Defaults to SizeSelectors.biggest(), but you can choose by aspect ratio or whatever. + \ No newline at end of file diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java index 1d14d8bd..19a588b4 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java @@ -58,7 +58,7 @@ public class CameraOptions1Test extends BaseTest { Camera.Parameters params = mock(Camera.Parameters.class); when(params.getSupportedPictureSizes()).thenReturn(sizes); CameraOptions o = new CameraOptions(params, false); - Set supportedSizes = o.getSupportedPictureSizes(); + Collection supportedSizes = o.getSupportedPictureSizes(); assertEquals(supportedSizes.size(), sizes.size()); for (Camera.Size size : sizes) { Size internalSize = new Size(size.width, size.height); @@ -77,7 +77,7 @@ public class CameraOptions1Test extends BaseTest { Camera.Parameters params = mock(Camera.Parameters.class); when(params.getSupportedPictureSizes()).thenReturn(sizes); CameraOptions o = new CameraOptions(params, true); - Set supportedSizes = o.getSupportedPictureSizes(); + Collection supportedSizes = o.getSupportedPictureSizes(); assertEquals(supportedSizes.size(), sizes.size()); for (Camera.Size size : sizes) { Size internalSize = new Size(size.width, size.height).flip(); @@ -102,7 +102,70 @@ public class CameraOptions1Test extends BaseTest { Camera.Parameters params = mock(Camera.Parameters.class); when(params.getSupportedPictureSizes()).thenReturn(sizes); CameraOptions o = new CameraOptions(params, false); - Set supportedRatios = o.getSupportedPictureAspectRatios(); + Collection supportedRatios = o.getSupportedPictureAspectRatios(); + assertEquals(supportedRatios.size(), expected.size()); + for (AspectRatio ratio : expected) { + assertTrue(supportedRatios.contains(ratio)); + } + } + + + @Test + public void testVideoSizes() { + List sizes = Arrays.asList( + mockCameraSize(100, 200), + mockCameraSize(50, 50), + mockCameraSize(1600, 900), + mockCameraSize(1000, 2000) + ); + Camera.Parameters params = mock(Camera.Parameters.class); + when(params.getSupportedVideoSizes()).thenReturn(sizes); + CameraOptions o = new CameraOptions(params, false); + Collection supportedSizes = o.getSupportedVideoSizes(); + assertEquals(supportedSizes.size(), sizes.size()); + for (Camera.Size size : sizes) { + Size internalSize = new Size(size.width, size.height); + assertTrue(supportedSizes.contains(internalSize)); + } + } + + @Test + public void testVideoSizesFlip() { + List sizes = Arrays.asList( + mockCameraSize(100, 200), + mockCameraSize(50, 50), + mockCameraSize(1600, 900), + mockCameraSize(1000, 2000) + ); + Camera.Parameters params = mock(Camera.Parameters.class); + when(params.getSupportedVideoSizes()).thenReturn(sizes); + CameraOptions o = new CameraOptions(params, true); + Collection supportedSizes = o.getSupportedVideoSizes(); + assertEquals(supportedSizes.size(), sizes.size()); + for (Camera.Size size : sizes) { + Size internalSize = new Size(size.width, size.height).flip(); + assertTrue(supportedSizes.contains(internalSize)); + } + } + + @Test + public void testVideoAspectRatio() { + List sizes = Arrays.asList( + mockCameraSize(100, 200), + mockCameraSize(50, 50), + mockCameraSize(1600, 900), + mockCameraSize(1000, 2000) + ); + + Set expected = new HashSet<>(); + expected.add(AspectRatio.of(1, 2)); + expected.add(AspectRatio.of(1, 1)); + expected.add(AspectRatio.of(16, 9)); + + Camera.Parameters params = mock(Camera.Parameters.class); + when(params.getSupportedVideoSizes()).thenReturn(sizes); + CameraOptions o = new CameraOptions(params, false); + Collection supportedRatios = o.getSupportedVideoAspectRatios(); assertEquals(supportedRatios.size(), expected.size()); for (AspectRatio ratio : expected) { assertTrue(supportedRatios.contains(ratio)); diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java index 169346f1..08ee413e 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java @@ -76,6 +76,7 @@ public class CameraViewTest extends BaseTest { assertNull(cameraView.getCameraOptions()); assertNull(cameraView.getSnapshotSize()); assertNull(cameraView.getPictureSize()); + assertNull(cameraView.getVideoSize()); } @Test @@ -542,6 +543,15 @@ public class CameraViewTest extends BaseTest { assertEquals(result, source); } + @Test + public void testVideoSizeSelector() { + SizeSelector source = SizeSelectors.minHeight(50); + cameraView.setVideoSize(source); + SizeSelector result = mockController.getVideoSizeSelector(); + assertNotNull(result); + assertEquals(result, source); + } + @Test public void testVideoMaxSize() { cameraView.setVideoMaxSize(5000); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java index dd92c353..c654d430 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java @@ -127,7 +127,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera throw new CameraException(e, CameraException.REASON_FAILED_TO_START_PREVIEW); } - mPictureSize = computePictureSize(); + mCaptureSize = computeCaptureSize(); mPreviewSize = computePreviewSize(sizesFromList(mCamera.getParameters().getSupportedPreviewSizes())); applySizesAndStartPreview("bindToSurface:"); mIsBound = true; @@ -144,7 +144,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera Camera.Parameters params = mCamera.getParameters(); mPreviewFormat = params.getPreviewFormat(); params.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); // <- not allowed during preview - params.setPictureSize(mPictureSize.getWidth(), mPictureSize.getHeight()); // <- allowed + params.setPictureSize(mCaptureSize.getWidth(), mCaptureSize.getHeight()); // <- allowed mCamera.setParameters(params); mCamera.setPreviewCallbackWithBuffer(null); // Release anything left @@ -228,7 +228,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera mCameraOptions = null; mCamera = null; mPreviewSize = null; - mPictureSize = null; + mCaptureSize = null; mIsBound = false; mIsCapturingImage = false; mIsTakingVideo = false; @@ -632,8 +632,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera mIsTakingVideo = true; // Create the video result - CamcorderProfile profile = getCamcorderProfile(); - Size videoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight); + final Size videoSize = mCaptureSize; mVideoResult = new VideoResult(); mVideoResult.file = videoFile; mVideoResult.isSnapshot = false; @@ -651,9 +650,12 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera // Must be called before setOutputFormat. mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); } + + // TODO: should get a profile of a quality compatible with the chosen size. + final CamcorderProfile profile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH); mMediaRecorder.setOutputFormat(profile.fileFormat); mMediaRecorder.setVideoFrameRate(profile.videoFrameRate); - mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight); + mMediaRecorder.setVideoSize(videoSize.getWidth(), videoSize.getHeight()); mMediaRecorder.setVideoEncoder(mMapper.map(mVideoCodec)); mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate); if (mAudio == Audio.ON) { diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java index 752e506c..03be98b6 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java @@ -6,7 +6,6 @@ import android.location.Location; import android.media.CamcorderProfile; import android.media.MediaRecorder; -import android.os.Build; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; @@ -15,6 +14,7 @@ import android.support.annotation.WorkerThread; import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.List; abstract class CameraController implements @@ -56,11 +56,12 @@ abstract class CameraController implements protected Mapper mMapper; protected FrameManager mFrameManager; protected SizeSelector mPictureSizeSelector; + protected SizeSelector mVideoSizeSelector; protected MediaRecorder mMediaRecorder; protected VideoResult mVideoResult; protected long mVideoMaxSize; protected int mVideoMaxDuration; - protected Size mPictureSize; + protected Size mCaptureSize; protected Size mPreviewSize; protected int mPreviewFormat; @@ -280,6 +281,10 @@ abstract class CameraController implements mPictureSizeSelector = selector; } + final void setVideoSizeSelector(SizeSelector selector) { + mVideoSizeSelector = selector; + } + final void setVideoMaxSize(long videoMaxSizeBytes) { mVideoMaxSize = videoMaxSizeBytes; } @@ -385,10 +390,14 @@ abstract class CameraController implements return mAudio; } - final SizeSelector getPictureSizeSelector() { + /* for tests */ final SizeSelector getPictureSizeSelector() { return mPictureSizeSelector; } + /* for tests */ final SizeSelector getVideoSizeSelector() { + return mVideoSizeSelector; + } + final float getZoomValue() { return mZoomValue; } @@ -444,8 +453,13 @@ abstract class CameraController implements } final Size getPictureSize(int reference) { - if (mPictureSize == null) return null; - return flip(REF_SENSOR, reference) ? mPictureSize.flip() : mPictureSize; + if (mCaptureSize == null || mMode == Mode.VIDEO) return null; + return flip(REF_SENSOR, reference) ? mCaptureSize.flip() : mCaptureSize; + } + + final Size getVideoSize(int reference) { + if (mCaptureSize == null || mMode == Mode.PICTURE) return null; + return flip(REF_SENSOR, reference) ? mCaptureSize.flip() : mCaptureSize; } final Size getPreviewSize(int reference) { @@ -465,43 +479,33 @@ abstract class CameraController implements * But when it does, the {@link CameraPreview.SurfaceCallback} should be called, * and this should be refreshed. */ - protected final Size computePictureSize() { - // The external selector is expecting stuff in the view world, not in the sensor world. - // Use the list in the camera options, then flip the result if needed. + protected final Size computeCaptureSize() { + // We want to pass stuff into the REF_VIEW reference, not the sensor one. + // This is already managed by CameraOptions, so we just flip again at the end. boolean flip = flip(REF_SENSOR, REF_VIEW); SizeSelector selector; - + Collection sizes; if (mMode == Mode.PICTURE) { - selector = SizeSelectors.or(mPictureSizeSelector, SizeSelectors.biggest()); + selector = mPictureSizeSelector; + sizes = mCameraOptions.getSupportedPictureSizes(); } else { - // The Camcorder internally checks for cameraParameters.getSupportedVideoSizes() etc. - // And we want the picture size to be the biggest picture consistent with the video aspect ratio. - // -> Use the external picture selector, but enforce the ratio constraint. - CamcorderProfile profile = getCamcorderProfile(); - AspectRatio targetRatio = AspectRatio.of(profile.videoFrameWidth, profile.videoFrameHeight); - if (flip) targetRatio = targetRatio.inverse(); - LOG.i("size:", "computeCaptureSize:", "targetRatio:", targetRatio); - SizeSelector matchRatio = SizeSelectors.aspectRatio(targetRatio, 0); - selector = SizeSelectors.or( - SizeSelectors.and(matchRatio, mPictureSizeSelector), - SizeSelectors.and(matchRatio), - mPictureSizeSelector - ); + selector = mVideoSizeSelector; + sizes = mCameraOptions.getSupportedVideoSizes(); } - - List list = new ArrayList<>(mCameraOptions.getSupportedPictureSizes()); + selector = SizeSelectors.or(selector, SizeSelectors.biggest()); + List list = new ArrayList<>(sizes); Size result = selector.select(list).get(0); - LOG.i("computePictureSize:", "result:", result, "flip:", flip); - if (flip) result = result.flip(); + LOG.i("computeCaptureSize:", "result:", result, "flip:", flip); + if (flip) result = result.flip(); // Go back to REF_SENSOR return result; } protected final Size computePreviewSize(List previewSizes) { - // instead of flipping everything to the view world, we can just flip the - // surface size to the sensor world - AspectRatio targetRatio = AspectRatio.of(mPictureSize.getWidth(), mPictureSize.getHeight()); + // instead of flipping everything to REF_VIEW, we can just flip the + // surface size from REF_VIEW to REF_SENSOR, and reflip at the end. + AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight()); Size targetMinSize = mPreview.getSurfaceSize(); - boolean flip = flip(REF_SENSOR, REF_VIEW); + boolean flip = flip(REF_VIEW, REF_SENSOR); if (flip) targetMinSize = targetMinSize.flip(); LOG.i("size:", "computePreviewSize:", "targetRatio:", targetRatio, "targetMinSize:", targetMinSize); SizeSelector matchRatio = SizeSelectors.and( // Match this aspect ratio and sort by biggest @@ -522,10 +526,5 @@ abstract class CameraController implements return result; } - @NonNull - protected final CamcorderProfile getCamcorderProfile() { - return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH); - } - //endregion } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java index 2485893c..6a65ff59 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java @@ -23,10 +23,11 @@ public class CameraOptions { private Set supportedFlash = new HashSet<>(4); private Set supportedHdr = new HashSet<>(2); private Set supportedPictureSizes = new HashSet<>(15); + private Set supportedVideoSizes = new HashSet<>(5); private Set supportedPictureAspectRatio = new HashSet<>(4); + private Set supportedVideoAspectRatio = new HashSet<>(3); private boolean zoomSupported; - private boolean videoSnapshotSupported; private boolean exposureCorrectionSupported; private float exposureCorrectionMinValue; private float exposureCorrectionMaxValue; @@ -75,7 +76,6 @@ public class CameraOptions { } zoomSupported = params.isZoomSupported(); - videoSnapshotSupported = params.isVideoSnapshotSupported(); autoFocusSupported = params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO); // Exposure correction @@ -93,6 +93,15 @@ public class CameraOptions { supportedPictureSizes.add(new Size(width, height)); supportedPictureAspectRatio.add(AspectRatio.of(width, height)); } + List vsizes = params.getSupportedVideoSizes(); + if (vsizes != null) { + for (Camera.Size size : vsizes) { + int width = flipSizes ? size.height : size.width; + int height = flipSizes ? size.width : size.height; + supportedVideoSizes.add(new Size(width, height)); + supportedVideoAspectRatio.add(AspectRatio.of(width, height)); + } + } } @@ -163,11 +172,10 @@ public class CameraOptions { /** * Set of supported picture sizes for the currently opened camera. * - * @return a set of supported values. + * @return a collection of supported values. */ @NonNull - public Set getSupportedPictureSizes() { - // TODO v2: return a Collection + public Collection getSupportedPictureSizes() { return Collections.unmodifiableSet(supportedPictureSizes); } @@ -175,25 +183,45 @@ public class CameraOptions { /** * Set of supported picture aspect ratios for the currently opened camera. * - * @return a set of supported values. + * @return a collection of supported values. */ @NonNull - public Set getSupportedPictureAspectRatios() { - // TODO v2: return a Collection + public Collection getSupportedPictureAspectRatios() { return Collections.unmodifiableSet(supportedPictureAspectRatio); } + /** + * Set of supported video sizes for the currently opened camera. + * + * @return a collection of supported values. + */ + @NonNull + public Collection getSupportedVideoSizes() { + return Collections.unmodifiableSet(supportedVideoSizes); + } + + + /** + * Set of supported picture aspect ratios for the currently opened camera. + * + * @return a set of supported values. + */ + @NonNull + public Collection getSupportedVideoAspectRatios() { + return Collections.unmodifiableSet(supportedVideoAspectRatio); + } + + /** * Set of supported facing values. * * @see Facing#BACK * @see Facing#FRONT - * @return a set of supported values. + * @return a collection of supported values. */ @NonNull - public Set getSupportedFacing() { - // TODO v2: return a Collection + public Collection getSupportedFacing() { return Collections.unmodifiableSet(supportedFacing); } @@ -205,11 +233,10 @@ public class CameraOptions { * @see Flash#OFF * @see Flash#ON * @see Flash#TORCH - * @return a set of supported values. + * @return a collection of supported values. */ @NonNull - public Set getSupportedFlash() { - // TODO v2: return a Collection + public Collection getSupportedFlash() { return Collections.unmodifiableSet(supportedFlash); } @@ -222,11 +249,10 @@ public class CameraOptions { * @see WhiteBalance#FLUORESCENT * @see WhiteBalance#DAYLIGHT * @see WhiteBalance#CLOUDY - * @return a set of supported values. + * @return a collection of supported values. */ @NonNull - public Set getSupportedWhiteBalance() { - // TODO v2: return a Collection + public Collection getSupportedWhiteBalance() { return Collections.unmodifiableSet(supportedWhiteBalance); } @@ -236,11 +262,10 @@ public class CameraOptions { * * @see Hdr#OFF * @see Hdr#ON - * @return a set of supported values. + * @return a collection of supported values. */ @NonNull - public Set getSupportedHdr() { - // TODO v2: return a Collection + public Collection getSupportedHdr() { return Collections.unmodifiableSet(supportedHdr); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 4bc1d4f0..7d6899c7 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -107,34 +107,65 @@ public class CameraView extends FrameLayout implements LifecycleObserver { long videoMaxSize = (long) a.getFloat(R.styleable.CameraView_cameraVideoMaxSize, 0); int videoMaxDuration = a.getInteger(R.styleable.CameraView_cameraVideoMaxDuration, 0); - // Size selectors - List constraints = new ArrayList<>(3); + // Picture size selector + List pictureConstraints = new ArrayList<>(3); if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMinWidth)) { - constraints.add(SizeSelectors.minWidth(a.getInteger(R.styleable.CameraView_cameraPictureSizeMinWidth, 0))); + pictureConstraints.add(SizeSelectors.minWidth(a.getInteger(R.styleable.CameraView_cameraPictureSizeMinWidth, 0))); } if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMaxWidth)) { - constraints.add(SizeSelectors.maxWidth(a.getInteger(R.styleable.CameraView_cameraPictureSizeMaxWidth, 0))); + pictureConstraints.add(SizeSelectors.maxWidth(a.getInteger(R.styleable.CameraView_cameraPictureSizeMaxWidth, 0))); } if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMinHeight)) { - constraints.add(SizeSelectors.minHeight(a.getInteger(R.styleable.CameraView_cameraPictureSizeMinHeight, 0))); + pictureConstraints.add(SizeSelectors.minHeight(a.getInteger(R.styleable.CameraView_cameraPictureSizeMinHeight, 0))); } if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMaxHeight)) { - constraints.add(SizeSelectors.maxHeight(a.getInteger(R.styleable.CameraView_cameraPictureSizeMaxHeight, 0))); + pictureConstraints.add(SizeSelectors.maxHeight(a.getInteger(R.styleable.CameraView_cameraPictureSizeMaxHeight, 0))); } if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMinArea)) { - constraints.add(SizeSelectors.minArea(a.getInteger(R.styleable.CameraView_cameraPictureSizeMinArea, 0))); + pictureConstraints.add(SizeSelectors.minArea(a.getInteger(R.styleable.CameraView_cameraPictureSizeMinArea, 0))); } if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMaxArea)) { - constraints.add(SizeSelectors.maxArea(a.getInteger(R.styleable.CameraView_cameraPictureSizeMaxArea, 0))); + pictureConstraints.add(SizeSelectors.maxArea(a.getInteger(R.styleable.CameraView_cameraPictureSizeMaxArea, 0))); } if (a.hasValue(R.styleable.CameraView_cameraPictureSizeAspectRatio)) { //noinspection ConstantConditions - constraints.add(SizeSelectors.aspectRatio(AspectRatio.parse(a.getString(R.styleable.CameraView_cameraPictureSizeAspectRatio)), 0)); + pictureConstraints.add(SizeSelectors.aspectRatio(AspectRatio.parse(a.getString(R.styleable.CameraView_cameraPictureSizeAspectRatio)), 0)); } - if (a.getBoolean(R.styleable.CameraView_cameraPictureSizeSmallest, false)) constraints.add(SizeSelectors.smallest()); - if (a.getBoolean(R.styleable.CameraView_cameraPictureSizeBiggest, false)) constraints.add(SizeSelectors.biggest()); - SizeSelector selector = !constraints.isEmpty() ? - SizeSelectors.and(constraints.toArray(new SizeSelector[0])) : + + if (a.getBoolean(R.styleable.CameraView_cameraPictureSizeSmallest, false)) pictureConstraints.add(SizeSelectors.smallest()); + if (a.getBoolean(R.styleable.CameraView_cameraPictureSizeBiggest, false)) pictureConstraints.add(SizeSelectors.biggest()); + SizeSelector pictureSelector = !pictureConstraints.isEmpty() ? + SizeSelectors.and(pictureConstraints.toArray(new SizeSelector[0])) : + SizeSelectors.biggest(); + + // Video size selector + List videoConstraints = new ArrayList<>(3); + if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMinWidth)) { + videoConstraints.add(SizeSelectors.minWidth(a.getInteger(R.styleable.CameraView_cameraVideoSizeMinWidth, 0))); + } + if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMaxWidth)) { + videoConstraints.add(SizeSelectors.maxWidth(a.getInteger(R.styleable.CameraView_cameraVideoSizeMaxWidth, 0))); + } + if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMinHeight)) { + videoConstraints.add(SizeSelectors.minHeight(a.getInteger(R.styleable.CameraView_cameraVideoSizeMinHeight, 0))); + } + if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMaxHeight)) { + videoConstraints.add(SizeSelectors.maxHeight(a.getInteger(R.styleable.CameraView_cameraVideoSizeMaxHeight, 0))); + } + if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMinArea)) { + videoConstraints.add(SizeSelectors.minArea(a.getInteger(R.styleable.CameraView_cameraVideoSizeMinArea, 0))); + } + if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMaxArea)) { + videoConstraints.add(SizeSelectors.maxArea(a.getInteger(R.styleable.CameraView_cameraVideoSizeMaxArea, 0))); + } + if (a.hasValue(R.styleable.CameraView_cameraVideoSizeAspectRatio)) { + //noinspection ConstantConditions + videoConstraints.add(SizeSelectors.aspectRatio(AspectRatio.parse(a.getString(R.styleable.CameraView_cameraVideoSizeAspectRatio)), 0)); + } + if (a.getBoolean(R.styleable.CameraView_cameraVideoSizeSmallest, false)) videoConstraints.add(SizeSelectors.smallest()); + if (a.getBoolean(R.styleable.CameraView_cameraVideoSizeBiggest, false)) videoConstraints.add(SizeSelectors.biggest()); + SizeSelector videoSelector = !videoConstraints.isEmpty() ? + SizeSelectors.and(videoConstraints.toArray(new SizeSelector[0])) : SizeSelectors.biggest(); // Gestures @@ -173,7 +204,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver { setGrid(grid); setHdr(hdr); setAudio(audio); - setPictureSize(selector); + setPictureSize(pictureSelector); + setVideoSize(videoSelector); setVideoCodec(codec); setVideoMaxSize(videoMaxSize); setVideoMaxDuration(videoMaxDuration); @@ -1003,8 +1035,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver { /** - * Sets picture capture size for picture mode. - * The {@link SizeSelector} will be invoked with the list of available size, and the first + * Sets a capture size selector for picture mode. + * The {@link SizeSelector} will be invoked with the list of available sizes, and the first * acceptable size will be accepted and passed to the internal engine. * See the {@link SizeSelectors} class for handy utilities for creating selectors. * @@ -1015,6 +1047,19 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } + /** + * Sets a capture size selector for video mode. + * The {@link SizeSelector} will be invoked with the list of available sizes, and the first + * acceptable size will be accepted and passed to the internal engine. + * See the {@link SizeSelectors} class for handy utilities for creating selectors. + * + * @param selector a size selector + */ + public void setVideoSize(@NonNull SizeSelector selector) { + mCameraController.setVideoSizeSelector(selector); + } + + /** * Adds a {@link CameraListener} instance to be notified of all * interesting events that happen during the camera lifecycle. @@ -1204,7 +1249,9 @@ public class CameraView extends FrameLayout implements LifecycleObserver { /** * Returns the size used for pictures taken with {@link #takePicture()}, - * or null if it hasn't been computed (for example if the surface is not ready). + * or null if it hasn't been computed (for example if the surface is not ready), + * or null if we are in video mode. + * * The size is rotated to match the output orientation. * * @return the size of pictures @@ -1215,6 +1262,21 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } + /** + * Returns the size used for videos taken with {@link #takeVideo(File)}, + * or null if it hasn't been computed (for example if the surface is not ready), + * or null if we are in picture mode. + * + * The size is rotated to match the output orientation. + * + * @return the size of videos + */ + @Nullable + public Size getVideoSize() { + return mCameraController.getVideoSize(CameraController.REF_OUTPUT); + } + + // If we end up here, we're in M. @TargetApi(Build.VERSION_CODES.M) private void requestPermissions(boolean requestCamera, boolean requestAudio) { diff --git a/cameraview/src/main/res/values/attrs.xml b/cameraview/src/main/res/values/attrs.xml index 8e2a9827..5c217374 100644 --- a/cameraview/src/main/res/values/attrs.xml +++ b/cameraview/src/main/res/values/attrs.xml @@ -3,23 +3,25 @@ - - - - - - - - + + + + + + + + + + @@ -116,27 +118,5 @@ - - - - - - - - \ No newline at end of file diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java index db84789f..b0d50074 100644 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java @@ -147,6 +147,10 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis } private void capturePicture() { + if (camera.getMode() == Mode.VIDEO) { + message("Can't take HQ pictures while in VIDEO mode.", false); + return; + } if (mCapturingPicture) return; mCapturingPicture = true; mCaptureTime = System.currentTimeMillis(); @@ -163,8 +167,8 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis } private void captureVideo() { - if (camera.getMode() != Mode.VIDEO) { - message("Can't record video while session type is 'picture'.", false); + if (camera.getMode() == Mode.PICTURE) { + message("Can't record HQ videos while in PICTURE mode.", false); return; } if (mCapturingPicture || mCapturingVideo) return;