diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java index 56d404f4..5292fc57 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java @@ -558,6 +558,15 @@ public class CameraViewTest extends BaseTest { assertEquals(cameraView.getVideoCodec(), VideoCodec.H_264); } + @Test + public void testPreviewSizeSelector() { + SizeSelector source = SizeSelectors.minHeight(50); + cameraView.setPreviewSize(source); + SizeSelector result = mockController.getPreviewSizeSelector(); + assertNotNull(result); + assertEquals(result, source); + } + @Test public void testPictureSizeSelector() { SizeSelector source = SizeSelectors.minHeight(50); diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java index 2eac6e8a..fa066551 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java @@ -109,7 +109,7 @@ public class MockCameraController extends CameraController { } @Override - void startAutoFocus(@Nullable Gesture gesture, PointF point) { + void startAutoFocus(@Nullable Gesture gesture, @NonNull PointF point) { mFocusStarted = true; } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java index d6bb6870..33c14b99 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java @@ -49,13 +49,15 @@ abstract class CameraController implements protected float mExposureCorrectionValue; protected boolean mPlaySounds; + @Nullable private SizeSelector mPreviewSizeSelector; + private SizeSelector mPictureSizeSelector; + private SizeSelector mVideoSizeSelector; + protected int mCameraId; protected CameraOptions mCameraOptions; protected Mapper mMapper; protected FrameManager mFrameManager; - protected SizeSelector mPictureSizeSelector; protected PictureRecorder mPictureRecorder; - protected SizeSelector mVideoSizeSelector; protected VideoRecorder mVideoRecorder; protected long mVideoMaxSize; protected int mVideoMaxDuration; @@ -221,6 +223,7 @@ abstract class CameraController implements } // Forces a restart. + @SuppressWarnings("WeakerAccess") protected final void restart() { LOG.i("Restart:", "posting runnable"); mHandler.post(new Runnable() { @@ -274,6 +277,10 @@ abstract class CameraController implements mDeviceOrientation = deviceOrientation; } + final void setPreviewSizeSelector(@Nullable SizeSelector selector) { + mPreviewSizeSelector = selector; + } + final void setPictureSizeSelector(@NonNull SizeSelector selector) { mPictureSizeSelector = selector; } @@ -343,7 +350,7 @@ abstract class CameraController implements abstract void stopVideo(); - abstract void startAutoFocus(@Nullable Gesture gesture, PointF point); + abstract void startAutoFocus(@Nullable Gesture gesture, @NonNull PointF point); abstract void setPlaySounds(boolean playSounds); @@ -411,6 +418,11 @@ abstract class CameraController implements return mAudioBitRate; } + @Nullable + /* for tests */ final SizeSelector getPreviewSizeSelector() { + return mPreviewSizeSelector; + } + @NonNull /* for tests */ final SizeSelector getPictureSizeSelector() { return mPictureSizeSelector; @@ -532,12 +544,19 @@ abstract class CameraController implements @NonNull @SuppressWarnings("WeakerAccess") protected final Size computePreviewSize(@NonNull List previewSizes) { - // 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()); + // These sizes come in REF_SENSOR. Since there is an external selector involved, + // we must convert all of them to REF_VIEW, then flip back when returning. + boolean flip = flip(REF_SENSOR, REF_VIEW); + List sizes = new ArrayList<>(previewSizes.size()); + for (Size size : previewSizes) { + sizes.add(flip ? size.flip() : size); + } + + // Create our own default selector, which will be used if the external mPreviewSizeSelector + // is null, or if it fails in finding a size. Size targetMinSize = mPreview.getOutputSurfaceSize(); - boolean flip = flip(REF_VIEW, REF_SENSOR); - if (flip) targetMinSize = targetMinSize.flip(); + AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight()); + if (flip) targetRatio = targetRatio.inverse(); LOG.i("size:", "computePreviewSize:", "targetRatio:", targetRatio, "targetMinSize:", targetMinSize); SizeSelector matchRatio = SizeSelectors.and( // Match this aspect ratio and sort by biggest SizeSelectors.aspectRatio(targetRatio, 0), @@ -552,7 +571,17 @@ abstract class CameraController implements matchRatio, // If couldn't respect size, at least match aspect ratio SizeSelectors.biggest() // If couldn't match any, take the biggest. ); - Size result = matchAll.select(previewSizes).get(0); + + // Apply the external selector with this as a fallback, + // and return a size in REF_SENSOR reference. + SizeSelector selector; + if (mPreviewSizeSelector != null) { + selector = SizeSelectors.or(mPreviewSizeSelector, matchAll); + } else { + selector = matchAll; + } + Size result = selector.select(sizes).get(0); + if (flip) result = result.flip(); LOG.i("computePreviewSize:", "result:", result, "flip:", flip); return result; } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 70bb4b78..fcf82ca0 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -1062,6 +1062,28 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } + /** + * ADVANCED FEATURE - sets a size selector for the preview stream. + * 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 & surface. + * + * This is typically NOT NEEDED. The default size selector is already smart enough to respect + * the picture/video output aspect ratio, and be bigger than the surface so that there is no + * upscaling. If all you want is set an aspect ratio, use {@link #setPictureSize(SizeSelector)} + * and {@link #setVideoSize(SizeSelector)}. + * + * When size changes, the {@link CameraView} is remeasured so any WRAP_CONTENT dimension + * is recomputed accordingly. + * + * See the {@link SizeSelectors} class for handy utilities for creating selectors. + * + * @param selector a size selector + */ + public void setPreviewSize(@NonNull SizeSelector selector) { + mCameraController.setPreviewSizeSelector(selector); + } + + /** * Set the current session type to either picture or video. * diff --git a/docs/_posts/2018-12-20-preview-size.md b/docs/_posts/2018-12-20-preview-size.md index ccc30499..6fd20c1b 100644 --- a/docs/_posts/2018-12-20-preview-size.md +++ b/docs/_posts/2018-12-20-preview-size.md @@ -20,7 +20,7 @@ This means that your visible preview can be of any size, not just the presets. Whatever you do, the preview will never be distorted - it can only be cropped if needed. -### Examples +## Examples ##### Center Inside @@ -50,4 +50,37 @@ of the internal preview surface, the surface will be cropped to fill the view. ``` This means that part of the preview might be hidden, and the output might contain parts of the scene -that were not visible during the capture, **unless it is taken as a snapshot, since snapshots account for cropping**. \ No newline at end of file +that were not visible during the capture, **unless it is taken as a snapshot, since snapshots account for cropping**. + + +## Advanced feature: Preview Size Selection + +**Only do this if you know what you are doing. This is typically not needed - prefer picture/video size selectors, +as they will drive the preview size selection and, eventually, the view size. If what you want is just +choose an aspect ratio, do so with [Capture Size](capture-size.html) selection.** + +As said, `WRAP_CONTENT` adapts the view boundaries to the preview size. The preview size must be determined +based on the sizes that the device sensor & hardware actually support. This operation is done automatically +by the engine. The default selector will do the following: + +- Constraint 1: match the picture/video output aspect ratio (so you get what you see) +- Constraint 2: match sizes a bit bigger than the View (so there is no upscaling) +- Try to match both, or just one, or fallback to the biggest available size + +There are not so many reason why you would replace this, other than control the frame processor size +or, indirectly, the snapshot size. You can, however, hook into the process using `setPreviewSize(SizeSelector)`: + +```java +cameraView.setPreviewSize(new SizeSelector() { + @Override + public List select(List source) { + // Receives a list of available sizes. + // Must return a list of acceptable sizes. + } +}); +``` + +After the preview size is determined, if it has changed since list time, the `CameraView` will receive +another call to `onMeasure` so the `WRAP_CONTENT` magic can take place. + +To understand how SizeSelectors work and the available utilities, please read the [Capture Size](capture-size.html) document. diff --git a/docs/_posts/2018-12-20-v1-migration-guide.md b/docs/_posts/2018-12-20-v1-migration-guide.md index 22ae6516..856a9d0b 100644 --- a/docs/_posts/2018-12-20-v1-migration-guide.md +++ b/docs/_posts/2018-12-20-v1-migration-guide.md @@ -1,7 +1,7 @@ --- layout: page title: "v1 Migration Guide" -subtitle: "Breaking Changes & concepts" +subtitle: "Breaking Changes & new concepts" category: extra date: 2018-12-20 19:01:55 order: 1 @@ -120,6 +120,21 @@ The GL surface, as an extra benefit, has a much more efficient way of capturing that avoids OOM errors, rotating the image on the fly, reading EXIF, and other horrible things belonging to v1. These picture snapshots will also work while taking videos. +### Advanced feature: Preview Sizing + +We finally introduced a `setPreviewSize()` method which accepts a `SizeSelector`. The use of this method +is discouraged if you don't know exactly what you are doing. The default preview size selector is already +smart enough to + +- respect the picture/video aspect ratio +- be a bit bigger than the view so that there is no upscaling + +There are not so many reason why you would use this method, other than, for example, control the frame +processor size or, indirectly, the snapshots size. If what you are doing is just assigning an aspect ratio, +for instance, please do so using `setPictureSize()` and `setVideoSize()`. + +**Note**: `getPreviewSize()` was removed as it has no useful meaning. + ### CameraListener The listener interface brings two breaking signature changes: @@ -143,4 +158,3 @@ Might be used in the future to speed up development. TODO: opencollective? TODO: improve the focus marker drawing, move out of XML (accept a drawable?) -TODO: do we want getPreviewSize() / setPreviewSize() ? probably not the getter. diff --git a/docs/index.md b/docs/index.md index 76f2b5d5..a8f893b1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,10 +27,11 @@ addressing most of the common issues and needs, and still leaving you with flexi ### Get started Get started with [install info](about/install.html), [quick setup](about/getting-started.html), or -read the in-depth [documentation](). +read the in-depth [documentation](docs/camera-events.html). ### Older versions This website contains documentation and informations about version 2.X.X of the library. For older versions, please take a look at the v1 branch in the [project page](https://github.com/natario1/CameraView). +For migration guide, take a look at the [migration page](extra/v1-migration-guide.html).