Add setVideoSize, video size selectors and available video sizes in CameraOptions

pull/360/head
Mattia Iavarone 7 years ago
parent acf1b4cdde
commit d409bb2449
  1. 9
      MIGRATION.md
  2. 69
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java
  3. 10
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  4. 14
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
  5. 71
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
  6. 65
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java
  7. 96
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  8. 40
      cameraview/src/main/res/values/attrs.xml
  9. 8
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java

@ -23,3 +23,12 @@
mode, you can only use takePictureSnapshot(), not takePicture(). 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. - takePicture(): will now throw an exception if called when Mode == Mode.VIDEO. You can only take snapshots.
- VideoQuality: this has been removed. - 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.

@ -58,7 +58,7 @@ public class CameraOptions1Test extends BaseTest {
Camera.Parameters params = mock(Camera.Parameters.class); Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedPictureSizes()).thenReturn(sizes); when(params.getSupportedPictureSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, false); CameraOptions o = new CameraOptions(params, false);
Set<Size> supportedSizes = o.getSupportedPictureSizes(); Collection<Size> supportedSizes = o.getSupportedPictureSizes();
assertEquals(supportedSizes.size(), sizes.size()); assertEquals(supportedSizes.size(), sizes.size());
for (Camera.Size size : sizes) { for (Camera.Size size : sizes) {
Size internalSize = new Size(size.width, size.height); Size internalSize = new Size(size.width, size.height);
@ -77,7 +77,7 @@ public class CameraOptions1Test extends BaseTest {
Camera.Parameters params = mock(Camera.Parameters.class); Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedPictureSizes()).thenReturn(sizes); when(params.getSupportedPictureSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, true); CameraOptions o = new CameraOptions(params, true);
Set<Size> supportedSizes = o.getSupportedPictureSizes(); Collection<Size> supportedSizes = o.getSupportedPictureSizes();
assertEquals(supportedSizes.size(), sizes.size()); assertEquals(supportedSizes.size(), sizes.size());
for (Camera.Size size : sizes) { for (Camera.Size size : sizes) {
Size internalSize = new Size(size.width, size.height).flip(); 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); Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedPictureSizes()).thenReturn(sizes); when(params.getSupportedPictureSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, false); CameraOptions o = new CameraOptions(params, false);
Set<AspectRatio> supportedRatios = o.getSupportedPictureAspectRatios(); Collection<AspectRatio> supportedRatios = o.getSupportedPictureAspectRatios();
assertEquals(supportedRatios.size(), expected.size());
for (AspectRatio ratio : expected) {
assertTrue(supportedRatios.contains(ratio));
}
}
@Test
public void testVideoSizes() {
List<Camera.Size> 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<Size> 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<Camera.Size> 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<Size> 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<Camera.Size> sizes = Arrays.asList(
mockCameraSize(100, 200),
mockCameraSize(50, 50),
mockCameraSize(1600, 900),
mockCameraSize(1000, 2000)
);
Set<AspectRatio> 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<AspectRatio> supportedRatios = o.getSupportedVideoAspectRatios();
assertEquals(supportedRatios.size(), expected.size()); assertEquals(supportedRatios.size(), expected.size());
for (AspectRatio ratio : expected) { for (AspectRatio ratio : expected) {
assertTrue(supportedRatios.contains(ratio)); assertTrue(supportedRatios.contains(ratio));

@ -76,6 +76,7 @@ public class CameraViewTest extends BaseTest {
assertNull(cameraView.getCameraOptions()); assertNull(cameraView.getCameraOptions());
assertNull(cameraView.getSnapshotSize()); assertNull(cameraView.getSnapshotSize());
assertNull(cameraView.getPictureSize()); assertNull(cameraView.getPictureSize());
assertNull(cameraView.getVideoSize());
} }
@Test @Test
@ -542,6 +543,15 @@ public class CameraViewTest extends BaseTest {
assertEquals(result, source); 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 @Test
public void testVideoMaxSize() { public void testVideoMaxSize() {
cameraView.setVideoMaxSize(5000); cameraView.setVideoMaxSize(5000);

@ -127,7 +127,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
throw new CameraException(e, CameraException.REASON_FAILED_TO_START_PREVIEW); throw new CameraException(e, CameraException.REASON_FAILED_TO_START_PREVIEW);
} }
mPictureSize = computePictureSize(); mCaptureSize = computeCaptureSize();
mPreviewSize = computePreviewSize(sizesFromList(mCamera.getParameters().getSupportedPreviewSizes())); mPreviewSize = computePreviewSize(sizesFromList(mCamera.getParameters().getSupportedPreviewSizes()));
applySizesAndStartPreview("bindToSurface:"); applySizesAndStartPreview("bindToSurface:");
mIsBound = true; mIsBound = true;
@ -144,7 +144,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
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(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.setParameters(params);
mCamera.setPreviewCallbackWithBuffer(null); // Release anything left mCamera.setPreviewCallbackWithBuffer(null); // Release anything left
@ -228,7 +228,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
mCameraOptions = null; mCameraOptions = null;
mCamera = null; mCamera = null;
mPreviewSize = null; mPreviewSize = null;
mPictureSize = null; mCaptureSize = null;
mIsBound = false; mIsBound = false;
mIsCapturingImage = false; mIsCapturingImage = false;
mIsTakingVideo = false; mIsTakingVideo = false;
@ -632,8 +632,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
mIsTakingVideo = true; mIsTakingVideo = true;
// Create the video result // Create the video result
CamcorderProfile profile = getCamcorderProfile(); final Size videoSize = mCaptureSize;
Size videoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
mVideoResult = new VideoResult(); mVideoResult = new VideoResult();
mVideoResult.file = videoFile; mVideoResult.file = videoFile;
mVideoResult.isSnapshot = false; mVideoResult.isSnapshot = false;
@ -651,9 +650,12 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
// Must be called before setOutputFormat. // Must be called before setOutputFormat.
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); 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.setOutputFormat(profile.fileFormat);
mMediaRecorder.setVideoFrameRate(profile.videoFrameRate); mMediaRecorder.setVideoFrameRate(profile.videoFrameRate);
mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight); mMediaRecorder.setVideoSize(videoSize.getWidth(), videoSize.getHeight());
mMediaRecorder.setVideoEncoder(mMapper.map(mVideoCodec)); mMediaRecorder.setVideoEncoder(mMapper.map(mVideoCodec));
mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate); mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate);
if (mAudio == Audio.ON) { if (mAudio == Audio.ON) {

@ -6,7 +6,6 @@ import android.location.Location;
import android.media.CamcorderProfile; import android.media.CamcorderProfile;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -15,6 +14,7 @@ import android.support.annotation.WorkerThread;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
abstract class CameraController implements abstract class CameraController implements
@ -56,11 +56,12 @@ abstract class CameraController implements
protected Mapper mMapper; protected Mapper mMapper;
protected FrameManager mFrameManager; protected FrameManager mFrameManager;
protected SizeSelector mPictureSizeSelector; protected SizeSelector mPictureSizeSelector;
protected SizeSelector mVideoSizeSelector;
protected MediaRecorder mMediaRecorder; protected MediaRecorder mMediaRecorder;
protected VideoResult mVideoResult; protected VideoResult mVideoResult;
protected long mVideoMaxSize; protected long mVideoMaxSize;
protected int mVideoMaxDuration; protected int mVideoMaxDuration;
protected Size mPictureSize; protected Size mCaptureSize;
protected Size mPreviewSize; protected Size mPreviewSize;
protected int mPreviewFormat; protected int mPreviewFormat;
@ -280,6 +281,10 @@ abstract class CameraController implements
mPictureSizeSelector = selector; mPictureSizeSelector = selector;
} }
final void setVideoSizeSelector(SizeSelector selector) {
mVideoSizeSelector = selector;
}
final void setVideoMaxSize(long videoMaxSizeBytes) { final void setVideoMaxSize(long videoMaxSizeBytes) {
mVideoMaxSize = videoMaxSizeBytes; mVideoMaxSize = videoMaxSizeBytes;
} }
@ -385,10 +390,14 @@ abstract class CameraController implements
return mAudio; return mAudio;
} }
final SizeSelector getPictureSizeSelector() { /* for tests */ final SizeSelector getPictureSizeSelector() {
return mPictureSizeSelector; return mPictureSizeSelector;
} }
/* for tests */ final SizeSelector getVideoSizeSelector() {
return mVideoSizeSelector;
}
final float getZoomValue() { final float getZoomValue() {
return mZoomValue; return mZoomValue;
} }
@ -444,8 +453,13 @@ abstract class CameraController implements
} }
final Size getPictureSize(int reference) { final Size getPictureSize(int reference) {
if (mPictureSize == null) return null; if (mCaptureSize == null || mMode == Mode.VIDEO) return null;
return flip(REF_SENSOR, reference) ? mPictureSize.flip() : mPictureSize; 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) { final Size getPreviewSize(int reference) {
@ -465,43 +479,33 @@ abstract class CameraController implements
* But when it does, the {@link CameraPreview.SurfaceCallback} should be called, * But when it does, the {@link CameraPreview.SurfaceCallback} should be called,
* and this should be refreshed. * and this should be refreshed.
*/ */
protected final Size computePictureSize() { protected final Size computeCaptureSize() {
// The external selector is expecting stuff in the view world, not in the sensor world. // We want to pass stuff into the REF_VIEW reference, not the sensor one.
// Use the list in the camera options, then flip the result if needed. // This is already managed by CameraOptions, so we just flip again at the end.
boolean flip = flip(REF_SENSOR, REF_VIEW); boolean flip = flip(REF_SENSOR, REF_VIEW);
SizeSelector selector; SizeSelector selector;
Collection<Size> sizes;
if (mMode == Mode.PICTURE) { if (mMode == Mode.PICTURE) {
selector = SizeSelectors.or(mPictureSizeSelector, SizeSelectors.biggest()); selector = mPictureSizeSelector;
sizes = mCameraOptions.getSupportedPictureSizes();
} else { } else {
// The Camcorder internally checks for cameraParameters.getSupportedVideoSizes() etc. selector = mVideoSizeSelector;
// And we want the picture size to be the biggest picture consistent with the video aspect ratio. sizes = mCameraOptions.getSupportedVideoSizes();
// -> 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 = SizeSelectors.or(selector, SizeSelectors.biggest());
List<Size> list = new ArrayList<>(mCameraOptions.getSupportedPictureSizes()); List<Size> list = new ArrayList<>(sizes);
Size result = selector.select(list).get(0); Size result = selector.select(list).get(0);
LOG.i("computePictureSize:", "result:", result, "flip:", flip); LOG.i("computeCaptureSize:", "result:", result, "flip:", flip);
if (flip) result = result.flip(); if (flip) result = result.flip(); // Go back to REF_SENSOR
return result; return result;
} }
protected final Size computePreviewSize(List<Size> previewSizes) { protected final Size computePreviewSize(List<Size> previewSizes) {
// instead of flipping everything to the view world, we can just flip the // instead of flipping everything to REF_VIEW, we can just flip the
// surface size to the sensor world // surface size from REF_VIEW to REF_SENSOR, and reflip at the end.
AspectRatio targetRatio = AspectRatio.of(mPictureSize.getWidth(), mPictureSize.getHeight()); AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight());
Size targetMinSize = mPreview.getSurfaceSize(); Size targetMinSize = mPreview.getSurfaceSize();
boolean flip = flip(REF_SENSOR, REF_VIEW); boolean flip = flip(REF_VIEW, REF_SENSOR);
if (flip) targetMinSize = targetMinSize.flip(); if (flip) targetMinSize = targetMinSize.flip();
LOG.i("size:", "computePreviewSize:", "targetRatio:", targetRatio, "targetMinSize:", targetMinSize); LOG.i("size:", "computePreviewSize:", "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
@ -522,10 +526,5 @@ abstract class CameraController implements
return result; return result;
} }
@NonNull
protected final CamcorderProfile getCamcorderProfile() {
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH);
}
//endregion //endregion
} }

@ -23,10 +23,11 @@ public class CameraOptions {
private Set<Flash> supportedFlash = new HashSet<>(4); private Set<Flash> supportedFlash = new HashSet<>(4);
private Set<Hdr> supportedHdr = new HashSet<>(2); private Set<Hdr> supportedHdr = new HashSet<>(2);
private Set<Size> supportedPictureSizes = new HashSet<>(15); private Set<Size> supportedPictureSizes = new HashSet<>(15);
private Set<Size> supportedVideoSizes = new HashSet<>(5);
private Set<AspectRatio> supportedPictureAspectRatio = new HashSet<>(4); private Set<AspectRatio> supportedPictureAspectRatio = new HashSet<>(4);
private Set<AspectRatio> supportedVideoAspectRatio = new HashSet<>(3);
private boolean zoomSupported; private boolean zoomSupported;
private boolean videoSnapshotSupported;
private boolean exposureCorrectionSupported; private boolean exposureCorrectionSupported;
private float exposureCorrectionMinValue; private float exposureCorrectionMinValue;
private float exposureCorrectionMaxValue; private float exposureCorrectionMaxValue;
@ -75,7 +76,6 @@ public class CameraOptions {
} }
zoomSupported = params.isZoomSupported(); zoomSupported = params.isZoomSupported();
videoSnapshotSupported = params.isVideoSnapshotSupported();
autoFocusSupported = params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO); autoFocusSupported = params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO);
// Exposure correction // Exposure correction
@ -93,6 +93,15 @@ public class CameraOptions {
supportedPictureSizes.add(new Size(width, height)); supportedPictureSizes.add(new Size(width, height));
supportedPictureAspectRatio.add(AspectRatio.of(width, height)); supportedPictureAspectRatio.add(AspectRatio.of(width, height));
} }
List<Camera.Size> 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. * Set of supported picture sizes for the currently opened camera.
* *
* @return a set of supported values. * @return a collection of supported values.
*/ */
@NonNull @NonNull
public Set<Size> getSupportedPictureSizes() { public Collection<Size> getSupportedPictureSizes() {
// TODO v2: return a Collection
return Collections.unmodifiableSet(supportedPictureSizes); return Collections.unmodifiableSet(supportedPictureSizes);
} }
@ -175,25 +183,45 @@ public class CameraOptions {
/** /**
* Set of supported picture aspect ratios for the currently opened camera. * Set of supported picture aspect ratios for the currently opened camera.
* *
* @return a set of supported values. * @return a collection of supported values.
*/ */
@NonNull @NonNull
public Set<AspectRatio> getSupportedPictureAspectRatios() { public Collection<AspectRatio> getSupportedPictureAspectRatios() {
// TODO v2: return a Collection
return Collections.unmodifiableSet(supportedPictureAspectRatio); return Collections.unmodifiableSet(supportedPictureAspectRatio);
} }
/**
* Set of supported video sizes for the currently opened camera.
*
* @return a collection of supported values.
*/
@NonNull
public Collection<Size> 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<AspectRatio> getSupportedVideoAspectRatios() {
return Collections.unmodifiableSet(supportedVideoAspectRatio);
}
/** /**
* Set of supported facing values. * Set of supported facing values.
* *
* @see Facing#BACK * @see Facing#BACK
* @see Facing#FRONT * @see Facing#FRONT
* @return a set of supported values. * @return a collection of supported values.
*/ */
@NonNull @NonNull
public Set<Facing> getSupportedFacing() { public Collection<Facing> getSupportedFacing() {
// TODO v2: return a Collection
return Collections.unmodifiableSet(supportedFacing); return Collections.unmodifiableSet(supportedFacing);
} }
@ -205,11 +233,10 @@ public class CameraOptions {
* @see Flash#OFF * @see Flash#OFF
* @see Flash#ON * @see Flash#ON
* @see Flash#TORCH * @see Flash#TORCH
* @return a set of supported values. * @return a collection of supported values.
*/ */
@NonNull @NonNull
public Set<Flash> getSupportedFlash() { public Collection<Flash> getSupportedFlash() {
// TODO v2: return a Collection
return Collections.unmodifiableSet(supportedFlash); return Collections.unmodifiableSet(supportedFlash);
} }
@ -222,11 +249,10 @@ public class CameraOptions {
* @see WhiteBalance#FLUORESCENT * @see WhiteBalance#FLUORESCENT
* @see WhiteBalance#DAYLIGHT * @see WhiteBalance#DAYLIGHT
* @see WhiteBalance#CLOUDY * @see WhiteBalance#CLOUDY
* @return a set of supported values. * @return a collection of supported values.
*/ */
@NonNull @NonNull
public Set<WhiteBalance> getSupportedWhiteBalance() { public Collection<WhiteBalance> getSupportedWhiteBalance() {
// TODO v2: return a Collection
return Collections.unmodifiableSet(supportedWhiteBalance); return Collections.unmodifiableSet(supportedWhiteBalance);
} }
@ -236,11 +262,10 @@ public class CameraOptions {
* *
* @see Hdr#OFF * @see Hdr#OFF
* @see Hdr#ON * @see Hdr#ON
* @return a set of supported values. * @return a collection of supported values.
*/ */
@NonNull @NonNull
public Set<Hdr> getSupportedHdr() { public Collection<Hdr> getSupportedHdr() {
// TODO v2: return a Collection
return Collections.unmodifiableSet(supportedHdr); return Collections.unmodifiableSet(supportedHdr);
} }

@ -107,34 +107,65 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
long videoMaxSize = (long) a.getFloat(R.styleable.CameraView_cameraVideoMaxSize, 0); long videoMaxSize = (long) a.getFloat(R.styleable.CameraView_cameraVideoMaxSize, 0);
int videoMaxDuration = a.getInteger(R.styleable.CameraView_cameraVideoMaxDuration, 0); int videoMaxDuration = a.getInteger(R.styleable.CameraView_cameraVideoMaxDuration, 0);
// Size selectors // Picture size selector
List<SizeSelector> constraints = new ArrayList<>(3); List<SizeSelector> pictureConstraints = new ArrayList<>(3);
if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMinWidth)) { 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)) { 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)) { 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)) { 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)) { 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)) { 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)) { if (a.hasValue(R.styleable.CameraView_cameraPictureSizeAspectRatio)) {
//noinspection ConstantConditions //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()); if (a.getBoolean(R.styleable.CameraView_cameraPictureSizeSmallest, false)) pictureConstraints.add(SizeSelectors.smallest());
SizeSelector selector = !constraints.isEmpty() ? if (a.getBoolean(R.styleable.CameraView_cameraPictureSizeBiggest, false)) pictureConstraints.add(SizeSelectors.biggest());
SizeSelectors.and(constraints.toArray(new SizeSelector[0])) : SizeSelector pictureSelector = !pictureConstraints.isEmpty() ?
SizeSelectors.and(pictureConstraints.toArray(new SizeSelector[0])) :
SizeSelectors.biggest();
// Video size selector
List<SizeSelector> 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(); SizeSelectors.biggest();
// Gestures // Gestures
@ -173,7 +204,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setGrid(grid); setGrid(grid);
setHdr(hdr); setHdr(hdr);
setAudio(audio); setAudio(audio);
setPictureSize(selector); setPictureSize(pictureSelector);
setVideoSize(videoSelector);
setVideoCodec(codec); setVideoCodec(codec);
setVideoMaxSize(videoMaxSize); setVideoMaxSize(videoMaxSize);
setVideoMaxDuration(videoMaxDuration); setVideoMaxDuration(videoMaxDuration);
@ -1003,8 +1035,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
/** /**
* Sets picture capture size for picture mode. * Sets a capture size selector for picture mode.
* The {@link SizeSelector} will be invoked with the list of available size, and the first * 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. * acceptable size will be accepted and passed to the internal engine.
* See the {@link SizeSelectors} class for handy utilities for creating selectors. * 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 * Adds a {@link CameraListener} instance to be notified of all
* interesting events that happen during the camera lifecycle. * 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()}, * 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. * The size is rotated to match the output orientation.
* *
* @return the size of pictures * @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. // If we end up here, we're in M.
@TargetApi(Build.VERSION_CODES.M) @TargetApi(Build.VERSION_CODES.M)
private void requestPermissions(boolean requestCamera, boolean requestAudio) { private void requestPermissions(boolean requestCamera, boolean requestAudio) {

@ -3,23 +3,25 @@
<declare-styleable name="CameraView"> <declare-styleable name="CameraView">
<attr name="cameraPictureSizeMinWidth" format="integer|reference"/> <attr name="cameraPictureSizeMinWidth" format="integer|reference"/>
<attr name="cameraPictureSizeMaxWidth" format="integer|reference"/> <attr name="cameraPictureSizeMaxWidth" format="integer|reference"/>
<attr name="cameraPictureSizeMinHeight" format="integer|reference"/> <attr name="cameraPictureSizeMinHeight" format="integer|reference"/>
<attr name="cameraPictureSizeMaxHeight" format="integer|reference"/> <attr name="cameraPictureSizeMaxHeight" format="integer|reference"/>
<attr name="cameraPictureSizeMinArea" format="integer|reference" /> <attr name="cameraPictureSizeMinArea" format="integer|reference" />
<attr name="cameraPictureSizeMaxArea" format="integer|reference" /> <attr name="cameraPictureSizeMaxArea" format="integer|reference" />
<attr name="cameraPictureSizeSmallest" format="boolean"/> <attr name="cameraPictureSizeSmallest" format="boolean"/>
<attr name="cameraPictureSizeBiggest" format="boolean"/> <attr name="cameraPictureSizeBiggest" format="boolean"/>
<attr name="cameraPictureSizeAspectRatio" format="string|reference"/> <attr name="cameraPictureSizeAspectRatio" format="string|reference"/>
<attr name="cameraVideoSizeMinWidth" format="integer|reference"/>
<attr name="cameraVideoSizeMaxWidth" format="integer|reference"/>
<attr name="cameraVideoSizeMinHeight" format="integer|reference"/>
<attr name="cameraVideoSizeMaxHeight" format="integer|reference"/>
<attr name="cameraVideoSizeMinArea" format="integer|reference" />
<attr name="cameraVideoSizeMaxArea" format="integer|reference" />
<attr name="cameraVideoSizeSmallest" format="boolean"/>
<attr name="cameraVideoSizeBiggest" format="boolean"/>
<attr name="cameraVideoSizeAspectRatio" format="string|reference"/>
<attr name="cameraGestureTap" format="enum"> <attr name="cameraGestureTap" format="enum">
<enum name="none" value="0" /> <enum name="none" value="0" />
<enum name="focus" value="1" /> <enum name="focus" value="1" />
@ -116,27 +118,5 @@
<enum name="h264" value="2" /> <enum name="h264" value="2" />
</attr> </attr>
<!-- deprecated attr name="cameraZoomMode" format="enum">
<enum name="off" value="0" />
<enum name="pinch" value="1" />
</attr -->
<!-- deprecated attr name="cameraCaptureMethod" format="enum">
<enum name="standard" value="0" />
<enum name="frame" value="1" />
</attr -->
<!-- deprecated attr name="cameraPermissionPolicy" format="enum">
<enum name="video" value="0" />
<enum name="picture" value="1" />
</attr -->
<!-- deprecated attr name="cameraFocus" format="enum">
<enum name="fixed" value="0" />
<enum name="continuous" value="1" />
<enum name="tap" value="2" />
<enum name="tapWithMarker" value="3" />
</attr -->
</declare-styleable> </declare-styleable>
</resources> </resources>

@ -147,6 +147,10 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
} }
private void capturePicture() { private void capturePicture() {
if (camera.getMode() == Mode.VIDEO) {
message("Can't take HQ pictures while in VIDEO mode.", false);
return;
}
if (mCapturingPicture) return; if (mCapturingPicture) return;
mCapturingPicture = true; mCapturingPicture = true;
mCaptureTime = System.currentTimeMillis(); mCaptureTime = System.currentTimeMillis();
@ -163,8 +167,8 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
} }
private void captureVideo() { private void captureVideo() {
if (camera.getMode() != Mode.VIDEO) { if (camera.getMode() == Mode.PICTURE) {
message("Can't record video while session type is 'picture'.", false); message("Can't record HQ videos while in PICTURE mode.", false);
return; return;
} }
if (mCapturingPicture || mCapturingVideo) return; if (mCapturingPicture || mCapturingVideo) return;

Loading…
Cancel
Save