Rename setPreviewSize and all internals to setPreviewStreamSize

pull/393/head
Mattia Iavarone 7 years ago
parent e5adf4d0c4
commit 632cc0f09a
  1. 22
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java
  2. 20
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  3. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java
  4. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/TextureCameraPreviewTest.java
  5. 66
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
  6. 32
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
  7. 16
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  8. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java
  9. 25
      cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java
  10. 8
      cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java
  11. 6
      cameraview/src/main/views/com/otaliastudios/cameraview/SurfaceCameraPreview.java
  12. 10
      cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java
  13. 12
      docs/_posts/2018-12-20-preview-size.md

@ -102,17 +102,17 @@ public abstract class CameraPreviewTest extends BaseTest {
@Test @Test
public void testDesiredSize() { public void testDesiredSize() {
preview.setInputStreamSize(160, 90, false); preview.setStreamSize(160, 90, false);
assertEquals(160, preview.getInputStreamSize().getWidth()); assertEquals(160, preview.getStreamSize().getWidth());
assertEquals(90, preview.getInputStreamSize().getHeight()); assertEquals(90, preview.getStreamSize().getHeight());
} }
@Test @Test
public void testSurfaceAvailable() { public void testSurfaceAvailable() {
ensureAvailable(); ensureAvailable();
verify(callback, times(1)).onSurfaceAvailable(); verify(callback, times(1)).onSurfaceAvailable();
assertEquals(surfaceSize.getWidth(), preview.getOutputSurfaceSize().getWidth()); assertEquals(surfaceSize.getWidth(), preview.getSurfaceSize().getWidth());
assertEquals(surfaceSize.getHeight(), preview.getOutputSurfaceSize().getHeight()); assertEquals(surfaceSize.getHeight(), preview.getSurfaceSize().getHeight());
} }
@Test @Test
@ -121,8 +121,8 @@ public abstract class CameraPreviewTest extends BaseTest {
ensureDestroyed(); ensureDestroyed();
// This might be called twice in Texture because it overrides ensureDestroyed method // This might be called twice in Texture because it overrides ensureDestroyed method
verify(callback, atLeastOnce()).onSurfaceDestroyed(); verify(callback, atLeastOnce()).onSurfaceDestroyed();
assertEquals(0, preview.getOutputSurfaceSize().getWidth()); assertEquals(0, preview.getSurfaceSize().getWidth());
assertEquals(0, preview.getOutputSurfaceSize().getHeight()); assertEquals(0, preview.getSurfaceSize().getHeight());
} }
@Test @Test
@ -146,7 +146,7 @@ public abstract class CameraPreviewTest extends BaseTest {
// Since desired is 'desired', let's fake a new view size that is consistent with it. // Since desired is 'desired', let's fake a new view size that is consistent with it.
// Ensure crop is not happening anymore. // Ensure crop is not happening anymore.
preview.mCropTask.listen(); preview.mCropTask.listen();
preview.dispatchOnOutputSurfaceSizeChanged((int) (50f * desired), 50); // Wait... preview.dispatchOnSurfaceSizeChanged((int) (50f * desired), 50); // Wait...
preview.mCropTask.await(); preview.mCropTask.await();
assertEquals(desired, getViewAspectRatioWithScale(), 0.01f); assertEquals(desired, getViewAspectRatioWithScale(), 0.01f);
assertFalse(preview.isCropping()); assertFalse(preview.isCropping());
@ -154,19 +154,19 @@ public abstract class CameraPreviewTest extends BaseTest {
private void setDesiredAspectRatio(float desiredAspectRatio) { private void setDesiredAspectRatio(float desiredAspectRatio) {
preview.mCropTask.listen(); preview.mCropTask.listen();
preview.setInputStreamSize((int) (10f * desiredAspectRatio), 10, false); // Wait... preview.setStreamSize((int) (10f * desiredAspectRatio), 10, false); // Wait...
preview.mCropTask.await(); preview.mCropTask.await();
assertEquals(desiredAspectRatio, getViewAspectRatioWithScale(), 0.01f); assertEquals(desiredAspectRatio, getViewAspectRatioWithScale(), 0.01f);
} }
private float getViewAspectRatio() { private float getViewAspectRatio() {
Size size = preview.getOutputSurfaceSize(); Size size = preview.getSurfaceSize();
return AspectRatio.of(size.getWidth(), size.getHeight()).toFloat(); return AspectRatio.of(size.getWidth(), size.getHeight()).toFloat();
} }
private float getViewAspectRatioWithScale() { private float getViewAspectRatioWithScale() {
Size size = preview.getOutputSurfaceSize(); Size size = preview.getSurfaceSize();
int newWidth = (int) (((float) size.getWidth()) * getCropScaleX()); int newWidth = (int) (((float) size.getWidth()) * getCropScaleX());
int newHeight = (int) (((float) size.getHeight()) * getCropScaleY()); int newHeight = (int) (((float) size.getHeight()) * getCropScaleY());
return AspectRatio.of(newWidth, newHeight).toFloat(); return AspectRatio.of(newWidth, newHeight).toFloat();

@ -288,14 +288,14 @@ public class CameraViewTest extends BaseTest {
//region testMeasure //region testMeasure
private void mockPreviewSize() { private void mockPreviewStreamSize() {
Size size = new Size(900, 1600); Size size = new Size(900, 1600);
mockController.setMockPreviewSize(size); mockController.setMockPreviewStreamSize(size);
} }
@Test @Test
public void testMeasure_early() { public void testMeasure_early() {
mockController.setMockPreviewSize(null); mockController.setMockPreviewStreamSize(null);
cameraView.measure( cameraView.measure(
makeMeasureSpec(500, EXACTLY), makeMeasureSpec(500, EXACTLY),
makeMeasureSpec(500, EXACTLY)); makeMeasureSpec(500, EXACTLY));
@ -305,7 +305,7 @@ public class CameraViewTest extends BaseTest {
@Test @Test
public void testMeasure_matchParentBoth() { public void testMeasure_matchParentBoth() {
mockPreviewSize(); mockPreviewStreamSize();
// Respect parent/layout constraints on both dimensions. // Respect parent/layout constraints on both dimensions.
cameraView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); cameraView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
@ -331,7 +331,7 @@ public class CameraViewTest extends BaseTest {
@Test @Test
public void testMeasure_wrapContentBoth() { public void testMeasure_wrapContentBoth() {
mockPreviewSize(); mockPreviewStreamSize();
// Respect parent constraints, but fit aspect ratio. // Respect parent constraints, but fit aspect ratio.
// Fit into a 160x160 parent so we espect final width to be 90. // Fit into a 160x160 parent so we espect final width to be 90.
@ -345,7 +345,7 @@ public class CameraViewTest extends BaseTest {
@Test @Test
public void testMeasure_wrapContentSingle() { public void testMeasure_wrapContentSingle() {
mockPreviewSize(); mockPreviewStreamSize();
// Respect MATCH_PARENT on height, change width to fit the aspect ratio. // Respect MATCH_PARENT on height, change width to fit the aspect ratio.
cameraView.setLayoutParams(new ViewGroup.LayoutParams(WRAP_CONTENT, MATCH_PARENT)); cameraView.setLayoutParams(new ViewGroup.LayoutParams(WRAP_CONTENT, MATCH_PARENT));
@ -366,7 +366,7 @@ public class CameraViewTest extends BaseTest {
@Test @Test
public void testMeasure_scrollableContainer() { public void testMeasure_scrollableContainer() {
mockPreviewSize(); mockPreviewStreamSize();
// Assume a vertical scroll view. It will pass UNSPECIFIED as height. // Assume a vertical scroll view. It will pass UNSPECIFIED as height.
// We respect MATCH_PARENT on width (160), and enlarge height to match the aspect ratio. // We respect MATCH_PARENT on width (160), and enlarge height to match the aspect ratio.
@ -559,10 +559,10 @@ public class CameraViewTest extends BaseTest {
} }
@Test @Test
public void testPreviewSizeSelector() { public void testPreviewStreamSizeSelector() {
SizeSelector source = SizeSelectors.minHeight(50); SizeSelector source = SizeSelectors.minHeight(50);
cameraView.setPreviewSize(source); cameraView.setPreviewStreamSize(source);
SizeSelector result = mockController.getPreviewSizeSelector(); SizeSelector result = mockController.getPreviewStreamSizeSelector();
assertNotNull(result); assertNotNull(result);
assertEquals(result, source); assertEquals(result, source);
} }

@ -23,8 +23,8 @@ public class MockCameraController extends CameraController {
mCameraOptions = options; mCameraOptions = options;
} }
void setMockPreviewSize(Size size) { void setMockPreviewStreamSize(Size size) {
mPreviewSize = size; mPreviewStreamSize = size;
} }
void mockStarted(boolean started) { void mockStarted(boolean started) {

@ -23,7 +23,7 @@ public class TextureCameraPreviewTest extends CameraPreviewTest {
if (isHardwareAccelerated()) { if (isHardwareAccelerated()) {
super.ensureAvailable(); super.ensureAvailable();
} else { } else {
preview.dispatchOnOutputSurfaceAvailable( preview.dispatchOnSurfaceAvailable(
surfaceSize.getWidth(), surfaceSize.getWidth(),
surfaceSize.getHeight()); surfaceSize.getHeight());
} }
@ -34,7 +34,7 @@ public class TextureCameraPreviewTest extends CameraPreviewTest {
super.ensureDestroyed(); super.ensureDestroyed();
if (!isHardwareAccelerated()) { if (!isHardwareAccelerated()) {
// Ensure it is called. // Ensure it is called.
preview.dispatchOnOutputSurfaceDestroyed(); preview.dispatchOnSurfaceDestroyed();
} }
} }

@ -97,12 +97,12 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
if (!mIsBound) return; if (!mIsBound) return;
// Compute a new camera preview size. // Compute a new camera preview size.
Size newSize = computePreviewSize(sizesFromList(mCamera.getParameters().getSupportedPreviewSizes())); Size newSize = computePreviewStreamSize(sizesFromList(mCamera.getParameters().getSupportedPreviewSizes()));
if (newSize.equals(mPreviewSize)) return; if (newSize.equals(mPreviewStreamSize)) return;
// Apply. // Apply.
LOG.i("onSurfaceChanged:", "Computed a new preview size. Going on."); LOG.i("onSurfaceChanged:", "Computed a new preview size. Going on.");
mPreviewSize = newSize; mPreviewStreamSize = newSize;
stopPreview(); stopPreview();
startPreview("onSurfaceChanged:"); startPreview("onSurfaceChanged:");
} }
@ -135,10 +135,12 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
LOG.i("bindToSurface:", "Started"); LOG.i("bindToSurface:", "Started");
Object output = mPreview.getOutput(); Object output = mPreview.getOutput();
try { try {
if (mPreview.getOutputClass() == SurfaceHolder.class) { if (output instanceof SurfaceHolder) {
mCamera.setPreviewDisplay((SurfaceHolder) output); mCamera.setPreviewDisplay((SurfaceHolder) output);
} else { } else if (output instanceof SurfaceTexture) {
mCamera.setPreviewTexture((SurfaceTexture) output); mCamera.setPreviewTexture((SurfaceTexture) output);
} else {
throw new RuntimeException("Unknown CameraPreview output class.");
} }
} catch (IOException e) { } catch (IOException e) {
LOG.e("bindToSurface:", "Failed to bind.", e); LOG.e("bindToSurface:", "Failed to bind.", e);
@ -146,20 +148,22 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
} }
mCaptureSize = computeCaptureSize(); mCaptureSize = computeCaptureSize();
mPreviewSize = computePreviewSize(sizesFromList(mCamera.getParameters().getSupportedPreviewSizes())); mPreviewStreamSize = computePreviewStreamSize(sizesFromList(mCamera.getParameters().getSupportedPreviewSizes()));
mIsBound = true; mIsBound = true;
} }
@WorkerThread @WorkerThread
private void unbindFromSurface() { private void unbindFromSurface() {
mIsBound = false; mIsBound = false;
mPreviewSize = null; mPreviewStreamSize = null;
mCaptureSize = null; mCaptureSize = null;
try { try {
if (mPreview.getOutputClass() == SurfaceHolder.class) { if (mPreview.getOutputClass() == SurfaceHolder.class) {
mCamera.setPreviewDisplay(null); mCamera.setPreviewDisplay(null);
} else { } else if (mPreview.getOutputClass() == SurfaceTexture.class) {
mCamera.setPreviewTexture(null); mCamera.setPreviewTexture(null);
} else {
throw new RuntimeException("Unknown CameraPreview output class.");
} }
} catch (IOException e) { } catch (IOException e) {
LOG.e("unbindFromSurface", "Could not release surface", e); LOG.e("unbindFromSurface", "Could not release surface", e);
@ -172,16 +176,16 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
// To be called when the preview size is setup or changed. // To be called when the preview size is setup or changed.
private void startPreview(String log) { private void startPreview(String log) {
LOG.i(log, "Dispatching onCameraPreviewSizeChanged."); LOG.i(log, "Dispatching onCameraPreviewStreamSizeChanged.");
mCameraCallbacks.onCameraPreviewSizeChanged(); mCameraCallbacks.onCameraPreviewStreamSizeChanged();
Size previewSize = getPreviewSize(REF_VIEW); Size previewSize = getPreviewStreamSize(REF_VIEW);
boolean wasFlipped = flip(REF_SENSOR, REF_VIEW); boolean wasFlipped = flip(REF_SENSOR, REF_VIEW);
mPreview.setInputStreamSize(previewSize.getWidth(), previewSize.getHeight(), wasFlipped); mPreview.setStreamSize(previewSize.getWidth(), previewSize.getHeight(), wasFlipped);
Camera.Parameters params = mCamera.getParameters(); Camera.Parameters params = mCamera.getParameters();
mPreviewFormat = params.getPreviewFormat(); mPreviewFormat = params.getPreviewFormat();
params.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); // <- not allowed during preview params.setPreviewSize(mPreviewStreamSize.getWidth(), mPreviewStreamSize.getHeight()); // <- not allowed during preview
if (mMode == Mode.PICTURE) { if (mMode == Mode.PICTURE) {
params.setPictureSize(mCaptureSize.getWidth(), mCaptureSize.getHeight()); // <- allowed params.setPictureSize(mCaptureSize.getWidth(), mCaptureSize.getHeight()); // <- allowed
} else { } else {
@ -196,7 +200,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
mCamera.setPreviewCallbackWithBuffer(null); // Release anything left mCamera.setPreviewCallbackWithBuffer(null); // Release anything left
mCamera.setPreviewCallbackWithBuffer(this); // Add ourselves mCamera.setPreviewCallbackWithBuffer(this); // Add ourselves
mFrameManager.allocate(ImageFormat.getBitsPerPixel(mPreviewFormat), mPreviewSize); mFrameManager.allocate(ImageFormat.getBitsPerPixel(mPreviewFormat), mPreviewStreamSize);
LOG.i(log, "Starting preview with startPreview()."); LOG.i(log, "Starting preview with startPreview().");
try { try {
@ -289,7 +293,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
} }
mCameraOptions = null; mCameraOptions = null;
mCamera = null; mCamera = null;
mPreviewSize = null; mPreviewStreamSize = null;
mCaptureSize = null; mCaptureSize = null;
mIsBound = false; mIsBound = false;
LOG.w("onStop:", "Clean up.", "Returning."); LOG.w("onStop:", "Clean up.", "Returning.");
@ -574,7 +578,22 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
}); });
} }
/**
* Just a note about the snapshot size - it is the PreviewStreamSize, cropped with the view ratio.
* One might be tempted to say that it is the SurfaceSize (which already matches the view ratio),
* but it's not.
*
* The camera sensor will capture preview frames with PreviewStreamSize and that's it. Then they
* are hardware-scaled by the preview surface, but this does not affect the snapshot, as the
* snapshot recorder simply creates another surface.
*
* Done tests to ensure that this is true, by using
* 1. small SurfaceSize and biggest() PreviewStreamSize: output is not low quality
* 2. big SurfaceSize and smallest() PreviewStreamSize: output is low quality
* In both cases the result.size here was set to the biggest of the two.
*
* @param viewAspectRatio the view aspect ratio
*/
@Override @Override
void takePictureSnapshot(@NonNull final AspectRatio viewAspectRatio) { void takePictureSnapshot(@NonNull final AspectRatio viewAspectRatio) {
LOG.v("takePictureSnapshot: scheduling"); LOG.v("takePictureSnapshot: scheduling");
@ -588,7 +607,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
result.location = mLocation; result.location = mLocation;
result.isSnapshot = true; result.isSnapshot = true;
result.facing = mFacing; result.facing = mFacing;
result.size = getPreviewSize(REF_OUTPUT); // Not the real size: it will be cropped to match the view ratio result.size = getPreviewStreamSize(REF_OUTPUT); // Not the real size: it will be cropped to match the view ratio
result.rotation = offset(REF_SENSOR, REF_OUTPUT); // Actually it will be rotated and set to 0. result.rotation = offset(REF_SENSOR, REF_OUTPUT); // Actually it will be rotated and set to 0.
AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.inverse() : viewAspectRatio; AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.inverse() : viewAspectRatio;
// LOG.e("ROTBUG_pic", "aspectRatio (REF_VIEW):", viewAspectRatio); // LOG.e("ROTBUG_pic", "aspectRatio (REF_VIEW):", viewAspectRatio);
@ -611,7 +630,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
Frame frame = mFrameManager.getFrame(data, Frame frame = mFrameManager.getFrame(data,
System.currentTimeMillis(), System.currentTimeMillis(),
offset(REF_SENSOR, REF_OUTPUT), offset(REF_SENSOR, REF_OUTPUT),
mPreviewSize, mPreviewStreamSize,
mPreviewFormat); mPreviewFormat);
mCameraCallbacks.dispatchFrame(frame); mCameraCallbacks.dispatchFrame(frame);
} }
@ -693,6 +712,15 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
}); });
} }
/**
* Is output size the SurfaceSize or the PreviewStreamSize ?
* I could not find evidence by testing, but I think that comments in {@link #takePictureSnapshot(AspectRatio)}
* would still apply here, despite the capturing mechanism being different.
* So we should use PreviewStreamSize as output size, cropped by the view aspect ratio.
*
* @param file the output file
* @param viewAspectRatio the view aspect ratio
*/
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Override @Override
void takeVideoSnapshot(@NonNull final File file, @NonNull final AspectRatio viewAspectRatio) { void takeVideoSnapshot(@NonNull final File file, @NonNull final AspectRatio viewAspectRatio) {
@ -755,7 +783,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
// Based on this we will use VO for everything. See if we get issues about distortion // 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. // and maybe we can improve. The reason why this happen is beyond my understanding.
Size outputSize = getPreviewSize(REF_OUTPUT); Size outputSize = getPreviewStreamSize(REF_OUTPUT);
AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.inverse() : viewAspectRatio; AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.inverse() : viewAspectRatio;
Rect outputCrop = CropHelper.computeCrop(outputSize, outputRatio); Rect outputCrop = CropHelper.computeCrop(outputSize, outputRatio);
outputSize = new Size(outputCrop.width(), outputCrop.height()); outputSize = new Size(outputCrop.width(), outputCrop.height());

@ -49,7 +49,7 @@ abstract class CameraController implements
protected float mExposureCorrectionValue; protected float mExposureCorrectionValue;
protected boolean mPlaySounds; protected boolean mPlaySounds;
@Nullable private SizeSelector mPreviewSizeSelector; @Nullable private SizeSelector mPreviewStreamSizeSelector;
private SizeSelector mPictureSizeSelector; private SizeSelector mPictureSizeSelector;
private SizeSelector mVideoSizeSelector; private SizeSelector mVideoSizeSelector;
@ -64,7 +64,7 @@ abstract class CameraController implements
protected int mVideoBitRate; protected int mVideoBitRate;
protected int mAudioBitRate; protected int mAudioBitRate;
protected Size mCaptureSize; protected Size mCaptureSize;
protected Size mPreviewSize; protected Size mPreviewStreamSize;
protected int mPreviewFormat; protected int mPreviewFormat;
protected int mSensorOffset; protected int mSensorOffset;
@ -279,8 +279,8 @@ abstract class CameraController implements
mDeviceOrientation = deviceOrientation; mDeviceOrientation = deviceOrientation;
} }
final void setPreviewSizeSelector(@Nullable SizeSelector selector) { final void setPreviewStreamSizeSelector(@Nullable SizeSelector selector) {
mPreviewSizeSelector = selector; mPreviewStreamSizeSelector = selector;
} }
final void setPictureSizeSelector(@NonNull SizeSelector selector) { final void setPictureSizeSelector(@NonNull SizeSelector selector) {
@ -421,8 +421,8 @@ abstract class CameraController implements
} }
@Nullable @Nullable
/* for tests */ final SizeSelector getPreviewSizeSelector() { /* for tests */ final SizeSelector getPreviewStreamSizeSelector() {
return mPreviewSizeSelector; return mPreviewStreamSizeSelector;
} }
@NonNull @NonNull
@ -506,15 +506,15 @@ abstract class CameraController implements
} }
@Nullable @Nullable
final Size getPreviewSize(int reference) { final Size getPreviewStreamSize(int reference) {
if (mPreviewSize == null) return null; if (mPreviewStreamSize == null) return null;
return flip(REF_SENSOR, reference) ? mPreviewSize.flip() : mPreviewSize; return flip(REF_SENSOR, reference) ? mPreviewStreamSize.flip() : mPreviewStreamSize;
} }
@Nullable @Nullable
final Size getSurfaceSize(int reference) { final Size getSurfaceSize(int reference) {
if (mPreview == null) return null; if (mPreview == null) return null;
return flip(REF_VIEW, reference) ? mPreview.getOutputSurfaceSize().flip() : mPreview.getOutputSurfaceSize(); return flip(REF_VIEW, reference) ? mPreview.getSurfaceSize().flip() : mPreview.getSurfaceSize();
} }
@ -559,7 +559,7 @@ abstract class CameraController implements
@NonNull @NonNull
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected final Size computePreviewSize(@NonNull List<Size> previewSizes) { protected final Size computePreviewStreamSize(@NonNull List<Size> previewSizes) {
// These sizes come in REF_SENSOR. Since there is an external selector involved, // 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. // we must convert all of them to REF_VIEW, then flip back when returning.
boolean flip = flip(REF_SENSOR, REF_VIEW); boolean flip = flip(REF_SENSOR, REF_VIEW);
@ -568,12 +568,12 @@ abstract class CameraController implements
sizes.add(flip ? size.flip() : size); sizes.add(flip ? size.flip() : size);
} }
// Create our own default selector, which will be used if the external mPreviewSizeSelector // Create our own default selector, which will be used if the external mPreviewStreamSizeSelector
// is null, or if it fails in finding a size. // is null, or if it fails in finding a size.
Size targetMinSize = getSurfaceSize(REF_VIEW); Size targetMinSize = getSurfaceSize(REF_VIEW);
AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight()); AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight());
if (flip) targetRatio = targetRatio.inverse(); if (flip) targetRatio = targetRatio.inverse();
LOG.i("size:", "computePreviewSize:", "targetRatio:", targetRatio, "targetMinSize:", targetMinSize); LOG.i("size:", "computePreviewStreamSize:", "targetRatio:", targetRatio, "targetMinSize:", targetMinSize);
SizeSelector matchRatio = SizeSelectors.and( // Match this aspect ratio and sort by biggest SizeSelector matchRatio = SizeSelectors.and( // Match this aspect ratio and sort by biggest
SizeSelectors.aspectRatio(targetRatio, 0), SizeSelectors.aspectRatio(targetRatio, 0),
SizeSelectors.biggest()); SizeSelectors.biggest());
@ -591,14 +591,14 @@ abstract class CameraController implements
// Apply the external selector with this as a fallback, // Apply the external selector with this as a fallback,
// and return a size in REF_SENSOR reference. // and return a size in REF_SENSOR reference.
SizeSelector selector; SizeSelector selector;
if (mPreviewSizeSelector != null) { if (mPreviewStreamSizeSelector != null) {
selector = SizeSelectors.or(mPreviewSizeSelector, matchAll); selector = SizeSelectors.or(mPreviewStreamSizeSelector, matchAll);
} else { } else {
selector = matchAll; selector = matchAll;
} }
Size result = selector.select(sizes).get(0); Size result = selector.select(sizes).get(0);
if (flip) result = result.flip(); if (flip) result = result.flip();
LOG.i("computePreviewSize:", "result:", result, "flip:", flip); LOG.i("computePreviewStreamSize:", "result:", result, "flip:", flip);
return result; return result;
} }

@ -311,7 +311,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
*/ */
@Override @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Size previewSize = mCameraController.getPreviewSize(CameraController.REF_VIEW); Size previewSize = mCameraController.getPreviewStreamSize(CameraController.REF_VIEW);
if (previewSize == null) { if (previewSize == null) {
LOG.w("onMeasure:", "surface is not ready. Calling default behavior."); LOG.w("onMeasure:", "surface is not ready. Calling default behavior.");
super.onMeasure(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@ -1073,15 +1073,15 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
* upscaling. If all you want is set an aspect ratio, use {@link #setPictureSize(SizeSelector)} * upscaling. If all you want is set an aspect ratio, use {@link #setPictureSize(SizeSelector)}
* and {@link #setVideoSize(SizeSelector)}. * and {@link #setVideoSize(SizeSelector)}.
* *
* When size changes, the {@link CameraView} is remeasured so any WRAP_CONTENT dimension * When stream size changes, the {@link CameraView} is remeasured so any WRAP_CONTENT dimension
* is recomputed accordingly. * is recomputed accordingly.
* *
* See the {@link SizeSelectors} class for handy utilities for creating selectors. * See the {@link SizeSelectors} class for handy utilities for creating selectors.
* *
* @param selector a size selector * @param selector a size selector
*/ */
public void setPreviewSize(@NonNull SizeSelector selector) { public void setPreviewStreamSize(@NonNull SizeSelector selector) {
mCameraController.setPreviewSizeSelector(selector); mCameraController.setPreviewStreamSizeSelector(selector);
} }
@ -1402,7 +1402,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// Get the preview size and crop according to the current view size. // Get the preview size and crop according to the current view size.
// It's better to do calculations in the REF_VIEW reference, and then flip if needed. // It's better to do calculations in the REF_VIEW reference, and then flip if needed.
Size preview = mCameraController.getPreviewSize(CameraController.REF_VIEW); Size preview = mCameraController.getPreviewStreamSize(CameraController.REF_VIEW);
AspectRatio viewRatio = AspectRatio.of(getWidth(), getHeight()); AspectRatio viewRatio = AspectRatio.of(getWidth(), getHeight());
Rect crop = CropHelper.computeCrop(preview, viewRatio); Rect crop = CropHelper.computeCrop(preview, viewRatio);
Size cropSize = new Size(crop.width(), crop.height()); Size cropSize = new Size(crop.width(), crop.height());
@ -1597,7 +1597,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
interface CameraCallbacks extends OrientationHelper.Callback { interface CameraCallbacks extends OrientationHelper.Callback {
void dispatchOnCameraOpened(CameraOptions options); void dispatchOnCameraOpened(CameraOptions options);
void dispatchOnCameraClosed(); void dispatchOnCameraClosed();
void onCameraPreviewSizeChanged(); void onCameraPreviewStreamSizeChanged();
void onShutter(boolean shouldPlaySound); void onShutter(boolean shouldPlaySound);
void dispatchOnVideoTaken(VideoResult result); void dispatchOnVideoTaken(VideoResult result);
void dispatchOnPictureTaken(PictureResult result); void dispatchOnPictureTaken(PictureResult result);
@ -1642,8 +1642,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
@Override @Override
public void onCameraPreviewSizeChanged() { public void onCameraPreviewStreamSizeChanged() {
mLogger.i("onCameraPreviewSizeChanged"); mLogger.i("onCameraPreviewStreamSizeChanged");
// Camera preview size has changed. // Camera preview size has changed.
// Request a layout pass for onMeasure() to do its stuff. // Request a layout pass for onMeasure() to do its stuff.
// Potentially this will change CameraView size, which changes Surface size, // Potentially this will change CameraView size, which changes Surface size,

@ -37,7 +37,7 @@ class SnapshotPictureRecorder extends PictureRecorder {
mCamera = camera; mCamera = camera;
mOutputRatio = outputRatio; mOutputRatio = outputRatio;
mFormat = mController.mPreviewFormat; mFormat = mController.mPreviewFormat;
mSensorPreviewSize = mController.mPreviewSize; mSensorPreviewSize = mController.mPreviewStreamSize;
} }
@Override @Override
@ -189,7 +189,7 @@ class SnapshotPictureRecorder extends PictureRecorder {
// It seems that the buffers are already cleared here, so we need to allocate again. // It seems that the buffers are already cleared here, so we need to allocate again.
camera.setPreviewCallbackWithBuffer(null); // Release anything left camera.setPreviewCallbackWithBuffer(null); // Release anything left
camera.setPreviewCallbackWithBuffer(mController); // Add ourselves camera.setPreviewCallbackWithBuffer(mController); // Add ourselves
mController.mFrameManager.allocate(ImageFormat.getBitsPerPixel(mFormat), mController.mPreviewSize); mController.mFrameManager.allocate(ImageFormat.getBitsPerPixel(mFormat), mController.mPreviewStreamSize);
} }
}); });
} }

@ -6,6 +6,13 @@ import androidx.annotation.Nullable;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
/**
* A CameraPreview takes in input stream from the {@link CameraController}, and streams it
* into an output surface that belongs to the view hierarchy.
*
* @param <T> the type of view which hosts the content surface
* @param <Output> the type of output, either {@link android.view.SurfaceHolder} or {@link android.graphics.SurfaceTexture}
*/
abstract class CameraPreview<T extends View, Output> { abstract class CameraPreview<T extends View, Output> {
protected final static CameraLogger LOG = CameraLogger.create(CameraPreview.class.getSimpleName()); protected final static CameraLogger LOG = CameraLogger.create(CameraPreview.class.getSimpleName());
@ -60,8 +67,8 @@ abstract class CameraPreview<T extends View, Output> {
// As far as I can see, these are the actual preview dimensions, as set in CameraParameters. // As far as I can see, these are the actual preview dimensions, as set in CameraParameters.
// This is called by the CameraImpl. // This is called by the CameraImpl.
// These must be alredy rotated, if needed, to be consistent with surface/view sizes. // These must be alredy rotated, if needed, to be consistent with surface/view sizes.
void setInputStreamSize(int width, int height, boolean wasFlipped) { void setStreamSize(int width, int height, boolean wasFlipped) {
LOG.i("setInputStreamSize:", "desiredW=", width, "desiredH=", height); LOG.i("setStreamSize:", "desiredW=", width, "desiredH=", height);
mInputStreamWidth = width; mInputStreamWidth = width;
mInputStreamHeight = height; mInputStreamHeight = height;
mInputFlipped = wasFlipped; mInputFlipped = wasFlipped;
@ -71,12 +78,12 @@ abstract class CameraPreview<T extends View, Output> {
} }
@NonNull @NonNull
final Size getInputStreamSize() { final Size getStreamSize() {
return new Size(mInputStreamWidth, mInputStreamHeight); return new Size(mInputStreamWidth, mInputStreamHeight);
} }
@NonNull @NonNull
final Size getOutputSurfaceSize() { final Size getSurfaceSize() {
return new Size(mOutputSurfaceWidth, mOutputSurfaceHeight); return new Size(mOutputSurfaceWidth, mOutputSurfaceHeight);
} }
@ -90,8 +97,8 @@ abstract class CameraPreview<T extends View, Output> {
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected final void dispatchOnOutputSurfaceAvailable(int width, int height) { protected final void dispatchOnSurfaceAvailable(int width, int height) {
LOG.i("dispatchOnOutputSurfaceAvailable:", "w=", width, "h=", height); LOG.i("dispatchOnSurfaceAvailable:", "w=", width, "h=", height);
mOutputSurfaceWidth = width; mOutputSurfaceWidth = width;
mOutputSurfaceHeight = height; mOutputSurfaceHeight = height;
if (mOutputSurfaceWidth > 0 && mOutputSurfaceHeight > 0) { if (mOutputSurfaceWidth > 0 && mOutputSurfaceHeight > 0) {
@ -104,8 +111,8 @@ abstract class CameraPreview<T extends View, Output> {
// As far as I can see, these are the view/surface dimensions. // As far as I can see, these are the view/surface dimensions.
// This is called by subclasses. // This is called by subclasses.
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected final void dispatchOnOutputSurfaceSizeChanged(int width, int height) { protected final void dispatchOnSurfaceSizeChanged(int width, int height) {
LOG.i("dispatchOnOutputSurfaceSizeChanged:", "w=", width, "h=", height); LOG.i("dispatchOnSurfaceSizeChanged:", "w=", width, "h=", height);
if (width != mOutputSurfaceWidth || height != mOutputSurfaceHeight) { if (width != mOutputSurfaceWidth || height != mOutputSurfaceHeight) {
mOutputSurfaceWidth = width; mOutputSurfaceWidth = width;
mOutputSurfaceHeight = height; mOutputSurfaceHeight = height;
@ -117,7 +124,7 @@ abstract class CameraPreview<T extends View, Output> {
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected final void dispatchOnOutputSurfaceDestroyed() { protected final void dispatchOnSurfaceDestroyed() {
mOutputSurfaceWidth = 0; mOutputSurfaceWidth = 0;
mOutputSurfaceHeight = 0; mOutputSurfaceHeight = 0;
mSurfaceCallback.onSurfaceDestroyed(); mSurfaceCallback.onSurfaceDestroyed();

@ -85,7 +85,7 @@ class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
@Override @Override
public void surfaceDestroyed(SurfaceHolder holder) { public void surfaceDestroyed(SurfaceHolder holder) {
dispatchOnOutputSurfaceDestroyed(); dispatchOnSurfaceDestroyed();
mDispatched = false; mDispatched = false;
} }
}); });
@ -159,7 +159,7 @@ class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
@Override @Override
public void onSurfaceChanged(GL10 gl, final int width, final int height) { public void onSurfaceChanged(GL10 gl, final int width, final int height) {
if (!mDispatched) { if (!mDispatched) {
dispatchOnOutputSurfaceAvailable(width, height); dispatchOnSurfaceAvailable(width, height);
mDispatched = true; mDispatched = true;
} else if (mOutputSurfaceWidth == width && mOutputSurfaceHeight == height) { } else if (mOutputSurfaceWidth == width && mOutputSurfaceHeight == height) {
// I was experimenting and this was happening. // I was experimenting and this was happening.
@ -168,13 +168,13 @@ class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
// With other CameraPreview implementation we could just dispatch the 'size changed' event // With other CameraPreview implementation we could just dispatch the 'size changed' event
// to the controller and everything would go straight. In case of GL, apparently we have to // to the controller and everything would go straight. In case of GL, apparently we have to
// force recreate the EGLContext by calling onPause and onResume in the UI thread. // force recreate the EGLContext by calling onPause and onResume in the UI thread.
dispatchOnOutputSurfaceDestroyed(); dispatchOnSurfaceDestroyed();
getView().post(new Runnable() { getView().post(new Runnable() {
@Override @Override
public void run() { public void run() {
getView().onPause(); getView().onPause();
getView().onResume(); getView().onResume();
dispatchOnOutputSurfaceAvailable(width, height); dispatchOnSurfaceAvailable(width, height);
} }
}); });
} }

@ -44,17 +44,17 @@ class SurfaceCameraPreview extends CameraPreview<SurfaceView, SurfaceHolder> {
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
LOG.i("callback:", "surfaceChanged", "w:", width, "h:", height, "dispatched:", mDispatched); LOG.i("callback:", "surfaceChanged", "w:", width, "h:", height, "dispatched:", mDispatched);
if (!mDispatched) { if (!mDispatched) {
dispatchOnOutputSurfaceAvailable(width, height); dispatchOnSurfaceAvailable(width, height);
mDispatched = true; mDispatched = true;
} else { } else {
dispatchOnOutputSurfaceSizeChanged(width, height); dispatchOnSurfaceSizeChanged(width, height);
} }
} }
@Override @Override
public void surfaceDestroyed(SurfaceHolder holder) { public void surfaceDestroyed(SurfaceHolder holder) {
LOG.i("callback:", "surfaceDestroyed"); LOG.i("callback:", "surfaceDestroyed");
dispatchOnOutputSurfaceDestroyed(); dispatchOnSurfaceDestroyed();
mDispatched = false; mDispatched = false;
} }
}); });

@ -28,17 +28,17 @@ class TextureCameraPreview extends CameraPreview<TextureView, SurfaceTexture> {
@Override @Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
dispatchOnOutputSurfaceAvailable(width, height); dispatchOnSurfaceAvailable(width, height);
} }
@Override @Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
dispatchOnOutputSurfaceSizeChanged(width, height); dispatchOnSurfaceSizeChanged(width, height);
} }
@Override @Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
dispatchOnOutputSurfaceDestroyed(); dispatchOnSurfaceDestroyed();
return true; return true;
} }
@ -70,8 +70,8 @@ class TextureCameraPreview extends CameraPreview<TextureView, SurfaceTexture> {
@TargetApi(15) @TargetApi(15)
@Override @Override
void setInputStreamSize(int width, int height, boolean wasFlipped) { void setStreamSize(int width, int height, boolean wasFlipped) {
super.setInputStreamSize(width, height, wasFlipped); super.setStreamSize(width, height, wasFlipped);
if (getView().getSurfaceTexture() != null) { if (getView().getSurfaceTexture() != null) {
getView().getSurfaceTexture().setDefaultBufferSize(width, height); getView().getSurfaceTexture().setDefaultBufferSize(width, height);
} }

@ -55,13 +55,13 @@ This means that part of the preview might be hidden, and the output might contai
that were not visible during the capture, **unless it is taken as a snapshot, since snapshots account for cropping**. that were not visible during the capture, **unless it is taken as a snapshot, since snapshots account for cropping**.
## Advanced feature: Preview Size Selection ## Advanced feature: Preview Stream Size Selection
**Only do this if you know what you are doing. This is typically not needed - prefer picture/video size selectors, **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 as they will drive the preview stream 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.** 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 As said, `WRAP_CONTENT` adapts the view boundaries to the preview stream size. The preview stream size must be determined
based on the sizes that the device sensor & hardware actually support. This operation is done automatically 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: by the engine. The default selector will do the following:
@ -70,10 +70,10 @@ by the engine. The default selector will do the following:
- Try to match both, or just one, or fallback to the biggest available size - 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 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)`: or, indirectly, the snapshot size. You can, however, hook into the process using `setPreviewStreamSize(SizeSelector)`:
```java ```java
cameraView.setPreviewSize(new SizeSelector() { cameraView.setPreviewStreamSize(new SizeSelector() {
@Override @Override
public List<Size> select(List<Size> source) { public List<Size> select(List<Size> source) {
// Receives a list of available sizes. // Receives a list of available sizes.
@ -82,7 +82,7 @@ cameraView.setPreviewSize(new SizeSelector() {
}); });
``` ```
After the preview size is determined, if it has changed since list time, the `CameraView` will receive After the preview stream 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. 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. To understand how SizeSelectors work and the available utilities, please read the [Capture Size](capture-size.html) document.

Loading…
Cancel
Save