Rename SessionType to Mode. takePicture fails when Mode == VIDEO

v2
Mattia Iavarone 7 years ago
parent 348cda70ad
commit 7884f486e5
  1. 5
      MIGRATION.md
  2. 14
      README.md
  3. 6
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java
  4. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewCallbacksTest.java
  5. 14
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  6. 36
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java
  7. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java
  8. 161
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
  9. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java
  10. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
  11. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java
  12. 55
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  13. 54
      cameraview/src/main/options/com/otaliastudios/cameraview/Mode.java
  14. 56
      cameraview/src/main/options/com/otaliastudios/cameraview/SessionType.java
  15. 2
      cameraview/src/main/res/values/attrs.xml
  16. 4
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
  17. 11
      demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java
  18. 2
      demo/src/main/res/layout/activity_camera.xml

@ -17,3 +17,8 @@
- onPictureTaken(): now passing a PictureResult. Use PictureResult.getJpeg() to access the jpeg stream. - onPictureTaken(): now passing a PictureResult. Use PictureResult.getJpeg() to access the jpeg stream.
- CameraUtils.BitmapCallback: has been moved in a separate BitmapCallback class. - CameraUtils.BitmapCallback: has been moved in a separate BitmapCallback class.
- isCapturingVideo(): renamed to isTakingVideo(). - isCapturingVideo(): renamed to isTakingVideo().
- SessionType: renamed to Mode. This means that setSessionType() and cameraSessionType are renamed to
setMode() and cameraMode.
- 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.

@ -410,8 +410,8 @@ Most camera parameters can be controlled through XML attributes or linked method
app:cameraFacing="back" app:cameraFacing="back"
app:cameraFlash="off" app:cameraFlash="off"
app:cameraGrid="off" app:cameraGrid="off"
app:cameraSessionType="picture"
app:cameraVideoQuality="max480p" app:cameraVideoQuality="max480p"
app:cameraMode="picture"
app:cameraVideoCodec="deviceDefault" app:cameraVideoCodec="deviceDefault"
app:cameraWhiteBalance="auto" app:cameraWhiteBalance="auto"
app:cameraHdr="off" app:cameraHdr="off"
@ -423,7 +423,7 @@ Most camera parameters can be controlled through XML attributes or linked method
|XML Attribute|Method|Values|Default Value| |XML Attribute|Method|Values|Default Value|
|-------------|------|------|-------------| |-------------|------|------|-------------|
|[`cameraSessionType`](#camerasessiontype)|`setSessionType()`|`picture` `video`|`picture`| |[`cameraMode`](#cameramode)|`setMode()`|`picture` `video`|`picture`|
|[`cameraFacing`](#camerafacing)|`setFacing()`|`back` `front`|`back`| |[`cameraFacing`](#camerafacing)|`setFacing()`|`back` `front`|`back`|
|[`cameraFlash`](#cameraflash)|`setFlash()`|`off` `on` `auto` `torch`|`off`| |[`cameraFlash`](#cameraflash)|`setFlash()`|`off` `on` `auto` `torch`|`off`|
|[`cameraGrid`](#cameragrid)|`setGrid()`|`off` `draw3x3` `draw4x4` `drawPhi`|`off`| |[`cameraGrid`](#cameragrid)|`setGrid()`|`off` `draw3x3` `draw4x4` `drawPhi`|`off`|
@ -436,21 +436,21 @@ Most camera parameters can be controlled through XML attributes or linked method
|[`cameraVideoMaxSize`](#cameravideomaxsize)|`setVideoMaxSize()`|number|`0`| |[`cameraVideoMaxSize`](#cameravideomaxsize)|`setVideoMaxSize()`|number|`0`|
|[`cameraVideoMaxDuration`](#cameravideomaxduration)|`setVideoMaxDuration()`|number|`0`| |[`cameraVideoMaxDuration`](#cameravideomaxduration)|`setVideoMaxDuration()`|number|`0`|
#### cameraSessionType #### cameraMode
What to capture - either picture or video. This has a couple of consequences: What to capture - either picture or video. This has a couple of consequences:
- Sizing: picture and preview size are chosen among the available picture or video sizes, - Sizing: picture and preview size are chosen among the available picture or video sizes,
depending on the flag. The picture size is chosen according to the given [size selector](#picture-size). depending on the flag. The picture size is chosen according to the given [size selector](#picture-size).
When `video`, in addition, we try to match the `videoQuality` aspect ratio. When `video`, in addition, we try to match the `videoQuality` aspect ratio.
- Picture capturing: while recording a video, image capturing might work, but it is not guaranteed - Capturing: while in picture mode, `takeVideo` will throw an exception.
(it's device dependent) - Capturing: while in video mode, `takePicture` will throw an exception, but picture snapshots are supported.
- Permission behavior: when requesting a `video` session, the record audio permission will be requested. - Permission behavior: when requesting a `video` session, the record audio permission will be requested.
If this is needed, the audio permission should be added to your manifest or the app will crash. If this is needed, the audio permission should be added to your manifest or the app will crash.
```java ```java
cameraView.setSessionType(SessionType.PICTURE); cameraView.setMode(Mode.PICTURE);
cameraView.setSessionType(SessionType.VIDEO); cameraView.setMode(Mode.VIDEO);
``` ```
#### cameraFacing #### cameraFacing

@ -35,7 +35,6 @@ public class CameraOptions1Test extends BaseTest {
assertTrue(o.getSupportedHdr().isEmpty()); assertTrue(o.getSupportedHdr().isEmpty());
assertFalse(o.isAutoFocusSupported()); assertFalse(o.isAutoFocusSupported());
assertFalse(o.isExposureCorrectionSupported()); assertFalse(o.isExposureCorrectionSupported());
assertFalse(o.isVideoSnapshotSupported());
assertFalse(o.isZoomSupported()); assertFalse(o.isZoomSupported());
assertEquals(o.getExposureCorrectionMaxValue(), 0f, 0); assertEquals(o.getExposureCorrectionMaxValue(), 0f, 0);
assertEquals(o.getExposureCorrectionMinValue(), 0f, 0); assertEquals(o.getExposureCorrectionMinValue(), 0f, 0);
@ -135,11 +134,11 @@ public class CameraOptions1Test extends BaseTest {
Collection<Grid> grids = o.getSupportedControls(Grid.class); Collection<Grid> grids = o.getSupportedControls(Grid.class);
Collection<VideoQuality> video = o.getSupportedControls(VideoQuality.class); Collection<VideoQuality> video = o.getSupportedControls(VideoQuality.class);
Collection<SessionType> sessions = o.getSupportedControls(SessionType.class); Collection<Mode> sessions = o.getSupportedControls(Mode.class);
Collection<Audio> audio = o.getSupportedControls(Audio.class); Collection<Audio> audio = o.getSupportedControls(Audio.class);
assertEquals(grids.size(), Grid.values().length); assertEquals(grids.size(), Grid.values().length);
assertEquals(video.size(), VideoQuality.values().length); assertEquals(video.size(), VideoQuality.values().length);
assertEquals(sessions.size(), SessionType.values().length); assertEquals(sessions.size(), Mode.values().length);
assertEquals(audio.size(), Audio.values().length); assertEquals(audio.size(), Audio.values().length);
} }
@ -223,7 +222,6 @@ public class CameraOptions1Test extends BaseTest {
when(params.isZoomSupported()).thenReturn(true); when(params.isZoomSupported()).thenReturn(true);
when(params.getSupportedFocusModes()).thenReturn(Arrays.asList(Camera.Parameters.FOCUS_MODE_AUTO)); when(params.getSupportedFocusModes()).thenReturn(Arrays.asList(Camera.Parameters.FOCUS_MODE_AUTO));
CameraOptions o = new CameraOptions(params, false); CameraOptions o = new CameraOptions(params, false);
assertTrue(o.isVideoSnapshotSupported());
assertTrue(o.isZoomSupported()); assertTrue(o.isZoomSupported());
assertTrue(o.isAutoFocusSupported()); assertTrue(o.isAutoFocusSupported());
} }

@ -2,8 +2,6 @@ package com.otaliastudios.cameraview;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PointF; import android.graphics.PointF;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
@ -63,7 +61,7 @@ public class CameraViewCallbacksTest extends BaseTest {
} }
@Override @Override
protected boolean checkPermissions(SessionType sessionType, Audio audio) { protected boolean checkPermissions(Mode mode, Audio audio) {
return true; return true;
} }
}; };

@ -50,7 +50,7 @@ public class CameraViewTest extends BaseTest {
} }
@Override @Override
protected boolean checkPermissions(SessionType sessionType, Audio audio) { protected boolean checkPermissions(Mode mode, Audio audio) {
return hasPermissions; return hasPermissions;
} }
}; };
@ -85,7 +85,7 @@ public class CameraViewTest extends BaseTest {
assertEquals(cameraView.getFacing(), Facing.DEFAULT); assertEquals(cameraView.getFacing(), Facing.DEFAULT);
assertEquals(cameraView.getGrid(), Grid.DEFAULT); assertEquals(cameraView.getGrid(), Grid.DEFAULT);
assertEquals(cameraView.getWhiteBalance(), WhiteBalance.DEFAULT); assertEquals(cameraView.getWhiteBalance(), WhiteBalance.DEFAULT);
assertEquals(cameraView.getSessionType(), SessionType.DEFAULT); assertEquals(cameraView.getMode(), Mode.DEFAULT);
assertEquals(cameraView.getHdr(), Hdr.DEFAULT); assertEquals(cameraView.getHdr(), Hdr.DEFAULT);
assertEquals(cameraView.getAudio(), Audio.DEFAULT); assertEquals(cameraView.getAudio(), Audio.DEFAULT);
assertEquals(cameraView.getVideoQuality(), VideoQuality.DEFAULT); assertEquals(cameraView.getVideoQuality(), VideoQuality.DEFAULT);
@ -503,11 +503,11 @@ public class CameraViewTest extends BaseTest {
} }
@Test @Test
public void testSessionType() { public void testMode() {
cameraView.set(SessionType.VIDEO); cameraView.set(Mode.VIDEO);
assertEquals(cameraView.getSessionType(), SessionType.VIDEO); assertEquals(cameraView.getMode(), Mode.VIDEO);
cameraView.set(SessionType.PICTURE); cameraView.set(Mode.PICTURE);
assertEquals(cameraView.getSessionType(), SessionType.PICTURE); assertEquals(cameraView.getMode(), Mode.PICTURE);
} }
@Test @Test

@ -37,7 +37,7 @@ import static org.mockito.Mockito.mock;
*/ */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@MediumTest @MediumTest
@Ignore // @Ignore
public class IntegrationTest extends BaseTest { public class IntegrationTest extends BaseTest {
@Rule @Rule
@ -231,8 +231,8 @@ public class IntegrationTest extends BaseTest {
} }
@Test @Test
public void testSetSessionType() throws Exception { public void testSetMode() throws Exception {
camera.setSessionType(SessionType.PICTURE); camera.setMode(Mode.PICTURE);
waitForOpen(true); waitForOpen(true);
// set session type should call stop and start again. // set session type should call stop and start again.
@ -240,11 +240,11 @@ public class IntegrationTest extends BaseTest {
doCountDown(latch).when(listener).onCameraOpened(any(CameraOptions.class)); doCountDown(latch).when(listener).onCameraOpened(any(CameraOptions.class));
doCountDown(latch).when(listener).onCameraClosed(); doCountDown(latch).when(listener).onCameraClosed();
camera.setSessionType(SessionType.VIDEO); camera.setMode(Mode.VIDEO);
boolean did = latch.await(2, TimeUnit.SECONDS); boolean did = latch.await(2, TimeUnit.SECONDS);
assertTrue("Handles setSessionType while active", did); assertTrue("Handles setMode while active", did);
assertEquals(camera.getSessionType(), SessionType.VIDEO); assertEquals(camera.getMode(), Mode.VIDEO);
} }
//endregion //endregion
@ -391,7 +391,7 @@ public class IntegrationTest extends BaseTest {
public void testSetVideoQuality_whileRecording() throws Throwable { public void testSetVideoQuality_whileRecording() throws Throwable {
// Can't run on Travis, MediaRecorder not supported. // Can't run on Travis, MediaRecorder not supported.
// Error while starting MediaRecorder. java.lang.RuntimeException: start failed. // Error while starting MediaRecorder. java.lang.RuntimeException: start failed.
camera.setSessionType(SessionType.VIDEO); camera.setMode(Mode.VIDEO);
waitForVideoQuality(VideoQuality.HIGHEST); waitForVideoQuality(VideoQuality.HIGHEST);
waitForOpen(true); waitForOpen(true);
waitForVideoStart(); waitForVideoStart();
@ -400,8 +400,8 @@ public class IntegrationTest extends BaseTest {
} }
@Test @Test
public void testSetVideoQuality_whileInPictureSessionType() { public void testSetVideoQuality_whileInPictureMode() {
camera.setSessionType(SessionType.PICTURE); camera.setMode(Mode.PICTURE);
waitForVideoQuality(VideoQuality.HIGHEST); waitForVideoQuality(VideoQuality.HIGHEST);
waitForOpen(true); waitForOpen(true);
waitForVideoQuality(VideoQuality.LOWEST); waitForVideoQuality(VideoQuality.LOWEST);
@ -435,7 +435,7 @@ public class IntegrationTest extends BaseTest {
// Fails on Travis. Some emulators can't deal with MediaRecorder // Fails on Travis. Some emulators can't deal with MediaRecorder
// Error while starting MediaRecorder. java.lang.RuntimeException: start failed. // Error while starting MediaRecorder. java.lang.RuntimeException: start failed.
// as documented. This works locally though. // as documented. This works locally though.
camera.setSessionType(SessionType.PICTURE); camera.setMode(Mode.PICTURE);
waitForOpen(true); waitForOpen(true);
waitForVideoStart(); waitForVideoStart();
waitForUiException(); waitForUiException();
@ -446,7 +446,7 @@ public class IntegrationTest extends BaseTest {
// Fails on Travis. Some emulators can't deal with MediaRecorder, // Fails on Travis. Some emulators can't deal with MediaRecorder,
// Error while starting MediaRecorder. java.lang.RuntimeException: start failed. // Error while starting MediaRecorder. java.lang.RuntimeException: start failed.
// as documented. This works locally though. // as documented. This works locally though.
camera.setSessionType(SessionType.VIDEO); camera.setMode(Mode.VIDEO);
waitForOpen(true); waitForOpen(true);
camera.takeVideo(null, 4000); camera.takeVideo(null, 4000);
waitForVideoEnd(true); waitForVideoEnd(true);
@ -454,7 +454,7 @@ public class IntegrationTest extends BaseTest {
@Test @Test
public void testEndVideo_withoutStarting() { public void testEndVideo_withoutStarting() {
camera.setSessionType(SessionType.VIDEO); camera.setMode(Mode.VIDEO);
waitForOpen(true); waitForOpen(true);
camera.stopVideo(); camera.stopVideo();
waitForVideoEnd(false); waitForVideoEnd(false);
@ -462,7 +462,7 @@ public class IntegrationTest extends BaseTest {
@Test @Test
public void testEndVideo_withMaxSize() { public void testEndVideo_withMaxSize() {
camera.setSessionType(SessionType.VIDEO); camera.setMode(Mode.VIDEO);
camera.setVideoMaxSize(500*1000); // 0.5 mb camera.setVideoMaxSize(500*1000); // 0.5 mb
waitForOpen(true); waitForOpen(true);
waitForVideoStart(); waitForVideoStart();
@ -471,7 +471,7 @@ public class IntegrationTest extends BaseTest {
@Test @Test
public void testEndVideo_withMaxDuration() { public void testEndVideo_withMaxDuration() {
camera.setSessionType(SessionType.VIDEO); camera.setMode(Mode.VIDEO);
camera.setVideoMaxDuration(4000); camera.setVideoMaxDuration(4000);
waitForOpen(true); waitForOpen(true);
waitForVideoStart(); waitForVideoStart();
@ -540,6 +540,14 @@ public class IntegrationTest extends BaseTest {
assertFalse(result.isSnapshot()); assertFalse(result.isSnapshot());
} }
@Test(expected = RuntimeException.class)
public void testCapturePicture_whileInVideoMode() throws Throwable {
camera.setMode(Mode.VIDEO);
waitForOpen(true);
camera.takePicture();
waitForUiException();
}
@Test @Test
public void testCaptureSnapshot_beforeStarted() { public void testCaptureSnapshot_beforeStarted() {
camera.takePictureSnapshot(); camera.takePictureSnapshot();

@ -72,8 +72,8 @@ public class MockCameraController extends CameraController {
} }
@Override @Override
void setSessionType(SessionType sessionType) { void setMode(Mode mode) {
mSessionType = sessionType; mMode = mode;
} }
@Override @Override

@ -187,7 +187,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
mergeWhiteBalance(params, WhiteBalance.DEFAULT); mergeWhiteBalance(params, WhiteBalance.DEFAULT);
mergeHdr(params, Hdr.DEFAULT); mergeHdr(params, Hdr.DEFAULT);
mergePlaySound(mPlaySounds); mergePlaySound(mPlaySounds);
params.setRecordingHint(mSessionType == SessionType.VIDEO); params.setRecordingHint(mMode == Mode.VIDEO);
mCamera.setParameters(params); mCamera.setParameters(params);
// Try starting preview. // Try starting preview.
@ -284,9 +284,9 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
} }
@Override @Override
void setSessionType(SessionType sessionType) { void setMode(Mode mode) {
if (sessionType != mSessionType) { if (mode != mMode) {
mSessionType = sessionType; mMode = mode;
schedule(null, true, new Runnable() { schedule(null, true, new Runnable() {
@Override @Override
public void run() { public void run() {
@ -441,7 +441,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
private void applyDefaultFocus(Camera.Parameters params) { private void applyDefaultFocus(Camera.Parameters params) {
List<String> modes = params.getSupportedFocusModes(); List<String> modes = params.getSupportedFocusModes();
if (mSessionType == SessionType.VIDEO && if (mMode == Mode.VIDEO &&
modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
return; return;
@ -478,7 +478,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
throw new IllegalStateException("Can't change video quality while recording a video."); throw new IllegalStateException("Can't change video quality while recording a video.");
} }
if (mSessionType == SessionType.VIDEO) { if (mMode == Mode.VIDEO) {
// Change capture size to a size that fits the video aspect ratio. // Change capture size to a size that fits the video aspect ratio.
Size oldSize = mPictureSize; Size oldSize = mPictureSize;
mPictureSize = computePictureSize(); mPictureSize = computePictureSize();
@ -503,11 +503,14 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
schedule(null, true, new Runnable() { schedule(null, true, new Runnable() {
@Override @Override
public void run() { public void run() {
if (mMode == Mode.VIDEO) {
throw new IllegalStateException("Can't take hq pictures while in VIDEO mode");
}
LOG.v("takePicture: performing.", mIsCapturingImage); LOG.v("takePicture: performing.", mIsCapturingImage);
if (mIsCapturingImage) return; if (mIsCapturingImage) return;
if (mIsTakingVideo && !mCameraOptions.isVideoSnapshotSupported()) return;
mIsCapturingImage = true; mIsCapturingImage = true;
final int sensorToOutput = offset(REF_SENSOR, REF_OUTPUT); final int sensorToOutput = offset(REF_SENSOR, REF_OUTPUT);
final Size outputSize = getPictureSize(REF_OUTPUT); final Size outputSize = getPictureSize(REF_OUTPUT);
Camera.Parameters params = mCamera.getParameters(); Camera.Parameters params = mCamera.getParameters();
@ -556,15 +559,15 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
schedule(null, true, new Runnable() { schedule(null, true, new Runnable() {
@Override @Override
public void run() { public void run() {
LOG.v("takePictureSnapshot: performing.", mIsCapturingImage);
if (mIsCapturingImage) return;
// This won't work while capturing a video.
// Switch to takePicture.
// TODO v2: what to do here?
if (mIsTakingVideo) { if (mIsTakingVideo) {
takePicture(); // TODO v2: what to do here?
// This won't work while capturing a video.
// But we want it to work.
return; return;
} }
LOG.v("takePictureSnapshot: performing.", mIsCapturingImage);
if (mIsCapturingImage) return;
mIsCapturingImage = true; mIsCapturingImage = true;
mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() { mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
@Override @Override
@ -654,74 +657,74 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
schedule(mStartVideoTask, true, new Runnable() { schedule(mStartVideoTask, true, new Runnable() {
@Override @Override
public void run() { public void run() {
if (mMode == Mode.PICTURE) {
throw new IllegalStateException("Can't record video while in PICTURE mode");
}
if (mIsTakingVideo) return; if (mIsTakingVideo) return;
if (mSessionType == SessionType.VIDEO) { mIsTakingVideo = true;
mIsTakingVideo = true;
// Create the video result
// Create the video result CamcorderProfile profile = getCamcorderProfile();
CamcorderProfile profile = getCamcorderProfile(); Size videoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
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; mVideoResult.codec = mVideoCodec;
mVideoResult.codec = mVideoCodec; mVideoResult.location = mLocation;
mVideoResult.location = mLocation; mVideoResult.rotation = offset(REF_SENSOR, REF_OUTPUT);
mVideoResult.rotation = offset(REF_SENSOR, REF_OUTPUT); mVideoResult.size = flip(REF_SENSOR, REF_OUTPUT) ? videoSize.flip() : videoSize;
mVideoResult.size = flip(REF_SENSOR, REF_OUTPUT) ? videoSize.flip() : videoSize;
// Initialize the media recorder
// Initialize the media recorder mCamera.unlock();
mCamera.unlock(); mMediaRecorder = new MediaRecorder();
mMediaRecorder = new MediaRecorder(); mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setCamera(mCamera); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); if (mAudio == Audio.ON) {
if (mAudio == Audio.ON) { // Must be called before setOutputFormat.
// Must be called before setOutputFormat. mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); }
} mMediaRecorder.setOutputFormat(profile.fileFormat);
mMediaRecorder.setOutputFormat(profile.fileFormat); mMediaRecorder.setVideoFrameRate(profile.videoFrameRate);
mMediaRecorder.setVideoFrameRate(profile.videoFrameRate); mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight); 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) { mMediaRecorder.setAudioChannels(profile.audioChannels);
mMediaRecorder.setAudioChannels(profile.audioChannels); mMediaRecorder.setAudioSamplingRate(profile.audioSampleRate);
mMediaRecorder.setAudioSamplingRate(profile.audioSampleRate); mMediaRecorder.setAudioEncoder(profile.audioCodec);
mMediaRecorder.setAudioEncoder(profile.audioCodec); mMediaRecorder.setAudioEncodingBitRate(profile.audioBitRate);
mMediaRecorder.setAudioEncodingBitRate(profile.audioBitRate); }
} if (mLocation != null) {
if (mLocation != null) { mMediaRecorder.setLocation(
mMediaRecorder.setLocation( (float) mLocation.getLatitude(),
(float) mLocation.getLatitude(), (float) mLocation.getLongitude());
(float) mLocation.getLongitude()); }
} mMediaRecorder.setOutputFile(mVideoResult.getFile().getAbsolutePath());
mMediaRecorder.setOutputFile(mVideoResult.getFile().getAbsolutePath()); mMediaRecorder.setOrientationHint(mVideoResult.getRotation());
mMediaRecorder.setOrientationHint(mVideoResult.getRotation()); mMediaRecorder.setMaxFileSize(mVideoMaxSize);
mMediaRecorder.setMaxFileSize(mVideoMaxSize); mMediaRecorder.setMaxDuration(mVideoMaxDuration);
mMediaRecorder.setMaxDuration(mVideoMaxDuration); mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() { @Override
@Override public void onInfo(MediaRecorder mediaRecorder, int what, int extra) {
public void onInfo(MediaRecorder mediaRecorder, int what, int extra) { switch (what) {
switch (what) { case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED: stopVideoImmediately();
stopVideoImmediately(); break;
break;
}
} }
});
// Not needed. mMediaRecorder.setPreviewDisplay(mPreview.getSurface());
try {
mMediaRecorder.prepare();
mMediaRecorder.start();
} catch (Exception e) {
LOG.e("Error while starting MediaRecorder. Swallowing.", e);
mVideoResult = null;
mCamera.lock();
stopVideoImmediately();
} }
} else { });
throw new IllegalStateException("Can't record video while session type is picture"); // Not needed. mMediaRecorder.setPreviewDisplay(mPreview.getSurface());
try {
mMediaRecorder.prepare();
mMediaRecorder.start();
} catch (Exception e) {
LOG.e("Error while starting MediaRecorder. Swallowing.", e);
mVideoResult = null;
mCamera.lock();
stopVideoImmediately();
} }
} }
}); });

@ -36,7 +36,7 @@ class Camera2 extends CameraController {
} }
@Override @Override
void setSessionType(SessionType sessionType) { void setMode(Mode mode) {
} }

@ -44,7 +44,7 @@ abstract class CameraController implements
protected WhiteBalance mWhiteBalance; protected WhiteBalance mWhiteBalance;
protected VideoQuality mVideoQuality; protected VideoQuality mVideoQuality;
protected VideoCodec mVideoCodec; protected VideoCodec mVideoCodec;
protected SessionType mSessionType; protected Mode mMode;
protected Hdr mHdr; protected Hdr mHdr;
protected Location mLocation; protected Location mLocation;
protected Audio mAudio; protected Audio mAudio;
@ -299,7 +299,7 @@ abstract class CameraController implements
//region Abstract setters and APIs //region Abstract setters and APIs
// Should restart the session if active. // Should restart the session if active.
abstract void setSessionType(SessionType sessionType); abstract void setMode(Mode mode);
// Should restart the session if active. // Should restart the session if active.
abstract void setFacing(Facing facing); abstract void setFacing(Facing facing);
@ -377,8 +377,8 @@ abstract class CameraController implements
return mVideoMaxDuration; return mVideoMaxDuration;
} }
final SessionType getSessionType() { final Mode getMode() {
return mSessionType; return mMode;
} }
final Hdr getHdr() { final Hdr getHdr() {
@ -479,7 +479,7 @@ abstract class CameraController implements
boolean flip = flip(REF_SENSOR, REF_VIEW); boolean flip = flip(REF_SENSOR, REF_VIEW);
SizeSelector selector; SizeSelector selector;
if (mSessionType == SessionType.PICTURE) { if (mMode == Mode.PICTURE) {
selector = SizeSelectors.or(mPictureSizeSelector, SizeSelectors.biggest()); selector = SizeSelectors.or(mPictureSizeSelector, SizeSelectors.biggest());
} else { } else {
// The Camcorder internally checks for cameraParameters.getSupportedVideoSizes() etc. // The Camcorder internally checks for cameraParameters.getSupportedVideoSizes() etc.

@ -148,8 +148,8 @@ public class CameraOptions {
return (Collection<T>) Arrays.asList(Grid.values()); return (Collection<T>) Arrays.asList(Grid.values());
} else if (controlClass.equals(Hdr.class)) { } else if (controlClass.equals(Hdr.class)) {
return (Collection<T>) getSupportedHdr(); return (Collection<T>) getSupportedHdr();
} else if (controlClass.equals(SessionType.class)) { } else if (controlClass.equals(Mode.class)) {
return (Collection<T>) Arrays.asList(SessionType.values()); return (Collection<T>) Arrays.asList(Mode.values());
} else if (controlClass.equals(VideoQuality.class)) { } else if (controlClass.equals(VideoQuality.class)) {
return (Collection<T>) Arrays.asList(VideoQuality.values()); return (Collection<T>) Arrays.asList(VideoQuality.values());
} else if (controlClass.equals(WhiteBalance.class)) { } else if (controlClass.equals(WhiteBalance.class)) {
@ -255,17 +255,6 @@ public class CameraOptions {
} }
/**
* Whether video snapshots are supported. If this is false, taking pictures
* while recording a video will have no effect.
*
* @return whether video snapshot is supported.
*/
public boolean isVideoSnapshotSupported() {
return videoSnapshotSupported;
}
/** /**
* Whether auto focus is supported. This means you can map gestures to * Whether auto focus is supported. This means you can map gestures to
* {@link GestureAction#FOCUS} or {@link GestureAction#FOCUS_WITH_MARKER} * {@link GestureAction#FOCUS} or {@link GestureAction#FOCUS_WITH_MARKER}

@ -101,7 +101,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
Grid grid = Grid.fromValue(a.getInteger(R.styleable.CameraView_cameraGrid, Grid.DEFAULT.value())); Grid grid = Grid.fromValue(a.getInteger(R.styleable.CameraView_cameraGrid, Grid.DEFAULT.value()));
WhiteBalance whiteBalance = WhiteBalance.fromValue(a.getInteger(R.styleable.CameraView_cameraWhiteBalance, WhiteBalance.DEFAULT.value())); WhiteBalance whiteBalance = WhiteBalance.fromValue(a.getInteger(R.styleable.CameraView_cameraWhiteBalance, WhiteBalance.DEFAULT.value()));
VideoQuality videoQuality = VideoQuality.fromValue(a.getInteger(R.styleable.CameraView_cameraVideoQuality, VideoQuality.DEFAULT.value())); VideoQuality videoQuality = VideoQuality.fromValue(a.getInteger(R.styleable.CameraView_cameraVideoQuality, VideoQuality.DEFAULT.value()));
SessionType sessionType = SessionType.fromValue(a.getInteger(R.styleable.CameraView_cameraSessionType, SessionType.DEFAULT.value())); Mode mode = Mode.fromValue(a.getInteger(R.styleable.CameraView_cameraMode, Mode.DEFAULT.value()));
Hdr hdr = Hdr.fromValue(a.getInteger(R.styleable.CameraView_cameraHdr, Hdr.DEFAULT.value())); Hdr hdr = Hdr.fromValue(a.getInteger(R.styleable.CameraView_cameraHdr, Hdr.DEFAULT.value()));
Audio audio = Audio.fromValue(a.getInteger(R.styleable.CameraView_cameraAudio, Audio.DEFAULT.value())); Audio audio = Audio.fromValue(a.getInteger(R.styleable.CameraView_cameraAudio, Audio.DEFAULT.value()));
VideoCodec codec = VideoCodec.fromValue(a.getInteger(R.styleable.CameraView_cameraVideoCodec, VideoCodec.DEFAULT.value())); VideoCodec codec = VideoCodec.fromValue(a.getInteger(R.styleable.CameraView_cameraVideoCodec, VideoCodec.DEFAULT.value()));
@ -169,7 +169,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// Apply camera controller params // Apply camera controller params
setFacing(facing); setFacing(facing);
setFlash(flash); setFlash(flash);
setSessionType(sessionType); setMode(mode);
setVideoQuality(videoQuality); setVideoQuality(videoQuality);
setWhiteBalance(whiteBalance); setWhiteBalance(whiteBalance);
setGrid(grid); setGrid(grid);
@ -551,7 +551,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
public void start() { public void start() {
if (!isEnabled()) return; if (!isEnabled()) return;
if (checkPermissions(getSessionType(), getAudio())) { if (checkPermissions(getMode(), getAudio())) {
// Update display orientation for current CameraController // Update display orientation for current CameraController
mOrientationHelper.enable(getContext()); mOrientationHelper.enable(getContext());
mCameraController.setDisplayOffset(mOrientationHelper.getDisplayOffset()); mCameraController.setDisplayOffset(mOrientationHelper.getDisplayOffset());
@ -563,19 +563,19 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
/** /**
* Checks that we have appropriate permissions for this session type. * Checks that we have appropriate permissions for this session type.
* Throws if session = audio and manifest did not add the microphone permissions. * Throws if session = audio and manifest did not add the microphone permissions.
* @param sessionType the sessionType to be checked * @param mode the sessionType to be checked
* @param audio the audio setting to be checked * @param audio the audio setting to be checked
* @return true if we can go on, false otherwise. * @return true if we can go on, false otherwise.
*/ */
@SuppressLint("NewApi") @SuppressLint("NewApi")
protected boolean checkPermissions(SessionType sessionType, Audio audio) { protected boolean checkPermissions(Mode mode, Audio audio) {
checkPermissionsManifestOrThrow(sessionType, audio); checkPermissionsManifestOrThrow(mode, audio);
// Manifest is OK at this point. Let's check runtime permissions. // Manifest is OK at this point. Let's check runtime permissions.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;
Context c = getContext(); Context c = getContext();
boolean needsCamera = true; boolean needsCamera = true;
boolean needsAudio = sessionType == SessionType.VIDEO && audio == Audio.ON; boolean needsAudio = mode == Mode.VIDEO && audio == Audio.ON;
needsCamera = needsCamera && c.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED; needsCamera = needsCamera && c.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED;
needsAudio = needsAudio && c.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED; needsAudio = needsAudio && c.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED;
@ -593,8 +593,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
* If the developer did not add this to its manifest, throw and fire warnings. * If the developer did not add this to its manifest, throw and fire warnings.
* (Hoping this is not caught elsewhere... we should test). * (Hoping this is not caught elsewhere... we should test).
*/ */
private void checkPermissionsManifestOrThrow(SessionType sessionType, Audio audio) { private void checkPermissionsManifestOrThrow(Mode mode, Audio audio) {
if (sessionType == SessionType.VIDEO && audio == Audio.ON) { if (mode == Mode.VIDEO && audio == Audio.ON) {
try { try {
PackageManager manager = getContext().getPackageManager(); PackageManager manager = getContext().getPackageManager();
PackageInfo info = manager.getPackageInfo(getContext().getPackageName(), PackageManager.GET_PERMISSIONS); PackageInfo info = manager.getPackageInfo(getContext().getPackageName(), PackageManager.GET_PERMISSIONS);
@ -656,8 +656,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setGrid((Grid) control); setGrid((Grid) control);
} else if (control instanceof Hdr) { } else if (control instanceof Hdr) {
setHdr((Hdr) control); setHdr((Hdr) control);
} else if (control instanceof SessionType) { } else if (control instanceof Mode) {
setSessionType((SessionType) control); setMode((Mode) control);
} else if (control instanceof VideoQuality) { } else if (control instanceof VideoQuality) {
setVideoQuality((VideoQuality) control); setVideoQuality((VideoQuality) control);
} else if (control instanceof WhiteBalance) { } else if (control instanceof WhiteBalance) {
@ -932,7 +932,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// Check did took place, or will happen on start(). // Check did took place, or will happen on start().
mCameraController.setAudio(audio); mCameraController.setAudio(audio);
} else if (checkPermissions(getSessionType(), audio)) { } else if (checkPermissions(getMode(), audio)) {
// Camera is running. Pass. // Camera is running. Pass.
mCameraController.setAudio(audio); mCameraController.setAudio(audio);
@ -971,24 +971,21 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
/** /**
* Set the current session type to either picture or video. * Set the current session type to either picture or video.
* When sessionType is video,
* - {@link #takeVideo(File)} will not throw any exception
* - {@link #takePicture()} might fallback to {@link #takePictureSnapshot()} or might not work
* *
* @see SessionType#PICTURE * @see Mode#PICTURE
* @see SessionType#VIDEO * @see Mode#VIDEO
* *
* @param sessionType desired session type. * @param mode desired session type.
*/ */
public void setSessionType(SessionType sessionType) { public void setMode(Mode mode) {
if (sessionType == getSessionType() || isStopped()) { if (mode == getMode() || isStopped()) {
// Check did took place, or will happen on start(). // Check did took place, or will happen on start().
mCameraController.setSessionType(sessionType); mCameraController.setMode(mode);
} else if (checkPermissions(sessionType, getAudio())) { } else if (checkPermissions(mode, getAudio())) {
// Camera is running. CameraImpl setSessionType will do the trick. // Camera is running. CameraImpl setMode will do the trick.
mCameraController.setSessionType(sessionType); mCameraController.setMode(mode);
} else { } else {
// This means that the audio permission is being asked. // This means that the audio permission is being asked.
@ -1001,11 +998,11 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
/** /**
* Gets the current session type. * Gets the current mode.
* @return the current session type * @return the current mode
*/ */
public SessionType getSessionType() { public Mode getMode() {
return mCameraController.getSessionType(); return mCameraController.getMode();
} }
@ -1123,7 +1120,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
* This will trigger {@link CameraListener#onPictureTaken(PictureResult)} if a listener * This will trigger {@link CameraListener#onPictureTaken(PictureResult)} if a listener
* was registered. * was registered.
* *
* Note that if sessionType is {@link SessionType#VIDEO}, this * Note that if sessionType is {@link Mode#VIDEO}, this
* might fall back to {@link #takePictureSnapshot()} (that is, we might capture a preview frame). * might fall back to {@link #takePictureSnapshot()} (that is, we might capture a preview frame).
* *
* @see #takePictureSnapshot() * @see #takePictureSnapshot()

@ -0,0 +1,54 @@
package com.otaliastudios.cameraview;
import java.io.File;
/**
* Type of the session to be opened or to move to.
* Session modes have influence over the capture and preview size, ability to shoot pictures,
* focus modes, runtime permissions needed.
*
* @see CameraView#setMode(Mode)
*/
public enum Mode implements Control {
/**
* Session used to capture pictures.
*
* - {@link CameraView#takeVideo(File)} will throw an exception
* - Only the camera permission is requested
* - Capture size is chosen according to the current picture size selector
*/
PICTURE(0),
/**
* Session used to capture videos.
*
* - {@link CameraView#takePicture()} will throw an exception
* - Camera and audio record permissions are requested
* - Capture size is chosen according to the current video size selector
*/
VIDEO(1);
static final Mode DEFAULT = PICTURE;
private int value;
Mode(int value) {
this.value = value;
}
int value() {
return value;
}
static Mode fromValue(int value) {
Mode[] list = Mode.values();
for (Mode action : list) {
if (action.value() == value) {
return action;
}
}
return null;
}
}

@ -1,56 +0,0 @@
package com.otaliastudios.cameraview;
/**
* Type of the session to be opened or to move to.
* Session types have influence over the capture and preview size, ability to shoot pictures,
* focus modes, runtime permissions needed.
*
* @see CameraView#setSessionType(SessionType)
*/
public enum SessionType implements Control {
/**
* Session optimized to capture pictures.
*
* - Trying to take videos in this session will throw an exception
* - Only the camera permission is requested
* - Capture size is chosen according to the current picture size selector
*/
PICTURE(0),
/**
* Session optimized to capture videos.
*
* - Trying to take pictures in this session will work, though with lower quality
* - Trying to take pictures while recording a video will work if supported
* - Camera and audio record permissions are requested
* - Capture size is chosen trying to respect the {@link VideoQuality} aspect ratio
*
* @see CameraOptions#isVideoSnapshotSupported()
*/
VIDEO(1);
static final SessionType DEFAULT = PICTURE;
private int value;
SessionType(int value) {
this.value = value;
}
int value() {
return value;
}
static SessionType fromValue(int value) {
SessionType[] list = SessionType.values();
for (SessionType action : list) {
if (action.value() == value) {
return action;
}
}
return null;
}
}

@ -84,7 +84,7 @@
<enum name="drawPhi" value="3" /> <enum name="drawPhi" value="3" />
</attr> </attr>
<attr name="cameraSessionType" format="enum"> <attr name="cameraMode" format="enum">
<enum name="picture" value="0" /> <enum name="picture" value="0" />
<enum name="video" value="1" /> <enum name="video" value="1" />
</attr> </attr>

@ -18,7 +18,7 @@ import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.CameraOptions; import com.otaliastudios.cameraview.CameraOptions;
import com.otaliastudios.cameraview.CameraView; import com.otaliastudios.cameraview.CameraView;
import com.otaliastudios.cameraview.PictureResult; import com.otaliastudios.cameraview.PictureResult;
import com.otaliastudios.cameraview.SessionType; import com.otaliastudios.cameraview.Mode;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
import java.io.File; import java.io.File;
@ -163,7 +163,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
} }
private void captureVideo() { private void captureVideo() {
if (camera.getSessionType() != SessionType.VIDEO) { if (camera.getMode() != Mode.VIDEO) {
message("Can't record video while session type is 'picture'.", false); message("Can't record video while session type is 'picture'.", false);
return; return;
} }

@ -11,12 +11,11 @@ import com.otaliastudios.cameraview.Gesture;
import com.otaliastudios.cameraview.GestureAction; import com.otaliastudios.cameraview.GestureAction;
import com.otaliastudios.cameraview.Grid; import com.otaliastudios.cameraview.Grid;
import com.otaliastudios.cameraview.Hdr; import com.otaliastudios.cameraview.Hdr;
import com.otaliastudios.cameraview.SessionType; import com.otaliastudios.cameraview.Mode;
import com.otaliastudios.cameraview.VideoQuality; import com.otaliastudios.cameraview.VideoQuality;
import com.otaliastudios.cameraview.WhiteBalance; import com.otaliastudios.cameraview.WhiteBalance;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -27,7 +26,7 @@ public enum Control {
WIDTH("Width", false), WIDTH("Width", false),
HEIGHT("Height", true), HEIGHT("Height", true),
SESSION("Session type", false), MODE("Mode", false),
FLASH("Flash", false), FLASH("Flash", false),
WHITE_BALANCE("White balance", false), WHITE_BALANCE("White balance", false),
GRID("Grid", true), GRID("Grid", true),
@ -72,7 +71,7 @@ public enum Control {
list.add(i); list.add(i);
} }
return list; return list;
case SESSION: return options.getSupportedControls(SessionType.class); case MODE: return options.getSupportedControls(Mode.class);
case FLASH: return options.getSupportedControls(Flash.class); case FLASH: return options.getSupportedControls(Flash.class);
case WHITE_BALANCE: return options.getSupportedControls(WhiteBalance.class); case WHITE_BALANCE: return options.getSupportedControls(WhiteBalance.class);
case HDR: return options.getSupportedControls(Hdr.class); case HDR: return options.getSupportedControls(Hdr.class);
@ -108,7 +107,7 @@ public enum Control {
switch (this) { switch (this) {
case WIDTH: return view.getLayoutParams().width; case WIDTH: return view.getLayoutParams().width;
case HEIGHT: return view.getLayoutParams().height; case HEIGHT: return view.getLayoutParams().height;
case SESSION: return view.getSessionType(); case MODE: return view.getMode();
case FLASH: return view.getFlash(); case FLASH: return view.getFlash();
case WHITE_BALANCE: return view.getWhiteBalance(); case WHITE_BALANCE: return view.getWhiteBalance();
case GRID: return view.getGrid(); case GRID: return view.getGrid();
@ -134,7 +133,7 @@ public enum Control {
camera.getLayoutParams().height = (int) value; camera.getLayoutParams().height = (int) value;
camera.setLayoutParams(camera.getLayoutParams()); camera.setLayoutParams(camera.getLayoutParams());
break; break;
case SESSION: case MODE:
case FLASH: case FLASH:
case WHITE_BALANCE: case WHITE_BALANCE:
case GRID: case GRID:

@ -25,7 +25,7 @@
app:cameraGesturePinch="zoom" app:cameraGesturePinch="zoom"
app:cameraGestureScrollHorizontal="exposureCorrection" app:cameraGestureScrollHorizontal="exposureCorrection"
app:cameraGestureScrollVertical="none" app:cameraGestureScrollVertical="none"
app:cameraSessionType="picture" /> app:cameraMode="picture" />
<ImageButton <ImageButton

Loading…
Cancel
Save