Add setVideoMaxDuration() API (#172)

* Add setVideoMaxDuration() API

* Better tests
pull/173/head
Mattia Iavarone 7 years ago committed by GitHub
parent 9b8cc16d96
commit c1973b0d71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 53
      README.md
  2. 14
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  3. 23
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java
  4. 5
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java
  5. 36
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
  6. 5
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java
  7. 22
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
  8. 97
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  9. 2
      cameraview/src/main/res/values/attrs.xml

@ -139,8 +139,8 @@ ensure lower quality output.
### Capturing Video ### Capturing Video
To capture video just call `CameraView.startRecordingVideo(file)` to start, and To capture video just call `CameraView.startCapturingVideo(file)` to start, and
`CameraView.stopRecordingVideo()` to finish. Make sure you setup a `CameraListener` to handle `CameraView.stopCapturingVideo()` to finish. Make sure you setup a `CameraListener` to handle
the video callback. the video callback.
```java ```java
@ -154,20 +154,16 @@ camera.addCameraListener(new CameraListener() {
// Select output file. Make sure you have write permissions. // Select output file. Make sure you have write permissions.
File file = ...; File file = ...;
camera.startCapturingVideo(file);
// Record a 2500 ms video: // Later... stop recording. This will trigger onVideoTaken().
camera.startRecordingVideo(file, 2500); camera.stopCapturingVideo();
// Full version
camera.startRecordingVideo(file);
camera.postDelayed(new Runnable() {
@Override
public void run() {
// This will trigger onVideoTaken().
camera.stopRecordingVideo();
}
}, 2500);
// You can also use one of the video constraints:
// videoMaxSize and videoMaxDuration will automatically stop recording when satisfied.
camera.setVideoMaxSize(100000);
camera.setVideoMaxDuration(5000);
camera.startCapturingVideo(file);
``` ```
### Other camera events ### Other camera events
@ -406,7 +402,9 @@ Most camera parameters can be controlled through XML attributes or linked method
app:cameraWhiteBalance="auto" app:cameraWhiteBalance="auto"
app:cameraHdr="off" app:cameraHdr="off"
app:cameraAudio="on" app:cameraAudio="on"
app:cameraPlaySounds="true"/> app:cameraPlaySounds="true"
app:cameraVideoMaxSize="0"
app:cameraVideoMaxDuration="0"/>
``` ```
|XML Attribute|Method|Values|Default Value| |XML Attribute|Method|Values|Default Value|
@ -422,6 +420,8 @@ Most camera parameters can be controlled through XML attributes or linked method
|[`cameraHdr`](#camerahdr)|`setHdr()`|`off` `on`|`off`| |[`cameraHdr`](#camerahdr)|`setHdr()`|`off` `on`|`off`|
|[`cameraAudio`](#cameraaudio)|`setAudio()`|`off` `on`|`on`| |[`cameraAudio`](#cameraaudio)|`setAudio()`|`off` `on`|`on`|
|[`cameraPlaySounds`](#cameraplaysounds)|`setPlaySounds()`|`true` `false`|`true`| |[`cameraPlaySounds`](#cameraplaysounds)|`setPlaySounds()`|`true` `false`|`true`|
|[`cameraVideoMaxSize`](#cameravideomaxsize)|`setVideoMaxSize()`|number|`0`|
|[`cameraVideoMaxDuration`](#cameravideomaxduration)|`setVideoMaxDuration()`|number|`0`|
#### cameraSessionType #### cameraSessionType
@ -547,6 +547,28 @@ cameraView.setPlaySounds(true);
cameraView.setPlaySounds(false); cameraView.setPlaySounds(false);
``` ```
#### cameraVideoMaxSize
Defines the maximum size in bytes for recorded video files.
Once this size is reached, the recording will automatically stop.
Defaults to unlimited size. Use 0 or negatives to disable.
```java
cameraView.setVideoMaxSize(100000);
cameraView.setVideoMaxSize(0); // Disable
```
#### cameraVideoMaxDuration
Defines the maximum duration in milliseconds for video recordings.
Once this duration is reached, the recording will automatically stop.
Defaults to unlimited duration. Use 0 or negatives to disable.
```java
cameraView.setVideoMaxDuration(100000);
cameraView.setVideoMaxDuration(0); // Disable
```
## Frame Processing ## Frame Processing
We support frame processors that will receive data from the camera preview stream: We support frame processors that will receive data from the camera preview stream:
@ -606,7 +628,6 @@ Other APIs not mentioned above are provided, and are well documented and comment
|`getPreviewSize()`|Returns the size of the preview surface. If CameraView was not constrained in its layout phase (e.g. it was `wrap_content`), this will return the same aspect ratio of CameraView.| |`getPreviewSize()`|Returns the size of the preview surface. If CameraView was not constrained in its layout phase (e.g. it was `wrap_content`), this will return the same aspect ratio of CameraView.|
|`getSnapshotSize()`|Returns `getPreviewSize()`, since a snapshot is a preview frame.| |`getSnapshotSize()`|Returns `getPreviewSize()`, since a snapshot is a preview frame.|
|`getPictureSize()`|Returns the size of the output picture. The aspect ratio is consistent with `getPreviewSize()`.| |`getPictureSize()`|Returns the size of the output picture. The aspect ratio is consistent with `getPreviewSize()`.|
|`setVideoMaxSize(long)`|Set a max file size (in bytes) for a video recording. There is no file size limit by default unless set by the user.|
Take also a look at public methods in `CameraUtils`, `CameraOptions`, `ExtraProperties`. Take also a look at public methods in `CameraUtils`, `CameraOptions`, `ExtraProperties`.

@ -96,6 +96,8 @@ public class CameraViewTest extends BaseTest {
assertEquals(cameraView.getLocation(), null); assertEquals(cameraView.getLocation(), null);
assertEquals(cameraView.getExposureCorrection(), 0f, 0f); assertEquals(cameraView.getExposureCorrection(), 0f, 0f);
assertEquals(cameraView.getZoom(), 0f, 0f); assertEquals(cameraView.getZoom(), 0f, 0f);
assertEquals(cameraView.getVideoMaxDuration(), 0, 0);
assertEquals(cameraView.getVideoMaxSize(), 0, 0);
// Self managed // Self managed
assertEquals(cameraView.getPlaySounds(), CameraView.DEFAULT_PLAY_SOUNDS); assertEquals(cameraView.getPlaySounds(), CameraView.DEFAULT_PLAY_SOUNDS);
@ -567,6 +569,18 @@ public class CameraViewTest extends BaseTest {
assertEquals(result, source); assertEquals(result, source);
} }
@Test
public void testVideoMaxSize() {
cameraView.setVideoMaxSize(5000);
assertEquals(cameraView.getVideoMaxSize(), 5000);
}
@Test
public void testVideoMaxDuration() {
cameraView.setVideoMaxDuration(5000);
assertEquals(cameraView.getVideoMaxDuration(), 5000);
}
//endregion //endregion
//region Lists of listeners and processors //region Lists of listeners and processors

@ -5,6 +5,7 @@ import android.graphics.Bitmap;
import android.graphics.PointF; import android.graphics.PointF;
import android.hardware.Camera; import android.hardware.Camera;
import android.os.Build; import android.os.Build;
import android.os.Handler;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule; import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
@ -129,9 +130,9 @@ public class IntegrationTest extends BaseTest {
doEndTask(video, true).when(listener).onVideoTaken(any(File.class)); doEndTask(video, true).when(listener).onVideoTaken(any(File.class));
Boolean result = video.await(8000); Boolean result = video.await(8000);
if (expectSuccess) { if (expectSuccess) {
assertNotNull("Can take video", result); assertNotNull("Should end video", result);
} else { } else {
assertNull("Should not take video", result); assertNull("Should not end video", result);
} }
} }
@ -463,6 +464,24 @@ public class IntegrationTest extends BaseTest {
waitForVideoEnd(false); waitForVideoEnd(false);
} }
@Test
public void testEndVideo_withMaxSize() {
camera.setSessionType(SessionType.VIDEO);
camera.setVideoMaxSize(500*1000); // 0.5 mb
waitForOpen(true);
waitForVideoStart();
waitForVideoEnd(true);
}
@Test
public void testEndVideo_withMaxDuration() {
camera.setSessionType(SessionType.VIDEO);
camera.setVideoMaxDuration(4000);
waitForOpen(true);
waitForVideoStart();
waitForVideoEnd(true);
}
//endregion //endregion
//region startAutoFocus //region startAutoFocus

@ -125,11 +125,6 @@ public class MockCameraController extends CameraController {
public void onBufferAvailable(byte[] buffer) { public void onBufferAvailable(byte[] buffer) {
} }
@Override
void setVideoMaxSize(long videoMaxSizeInBytes) {
}
@Override @Override
void setPlaySounds(boolean playSounds) { void setPlaySounds(boolean playSounds) {

@ -696,31 +696,28 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
} }
if (mLocation != null) { if (mLocation != null) {
mMediaRecorder.setLocation((float) mLocation.getLatitude(), mMediaRecorder.setLocation(
(float) mLocation.getLatitude(),
(float) mLocation.getLongitude()); (float) mLocation.getLongitude());
} }
mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath()); mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath());
mMediaRecorder.setOrientationHint(computeSensorToOutputOffset()); mMediaRecorder.setOrientationHint(computeSensorToOutputOffset());
//If the user sets a max file size, set it to the max file size mMediaRecorder.setMaxFileSize(mVideoMaxSize);
if (mVideoMaxSizeInBytes > 0) { mMediaRecorder.setMaxDuration(mVideoMaxDuration);
mMediaRecorder.setMaxFileSize(mVideoMaxSizeInBytes);
//Attach a listener to the media recorder to listen for file size notifications
mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
@Override
public void onInfo(MediaRecorder mediaRecorder, int i, int i1) {
switch (i) {
case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED: {
endVideoImmediately();
break;
}
}
mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
@Override
public void onInfo(MediaRecorder mediaRecorder, int what, int extra) {
switch (what) {
case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
endVideoImmediately();
break;
} }
}); }
} });
// Not needed. mMediaRecorder.setPreviewDisplay(mPreview.getSurface()); // Not needed. mMediaRecorder.setPreviewDisplay(mPreview.getSurface());
} }
@ -879,11 +876,6 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
return result; return result;
} }
@Override
void setVideoMaxSize(long videoMaxSizeInBytes) {
mVideoMaxSizeInBytes = videoMaxSizeInBytes;
}
@Override @Override
void setPlaySounds(boolean playSounds) { void setPlaySounds(boolean playSounds) {
final boolean old = mPlaySounds; final boolean old = mPlaySounds;

@ -115,11 +115,6 @@ class Camera2 extends CameraController {
} }
@Override
void setVideoMaxSize(long videoMaxSizeInBytes) {
}
@Override @Override
void setPlaySounds(boolean playSounds) { void setPlaySounds(boolean playSounds) {

@ -55,6 +55,8 @@ abstract class CameraController implements
protected SizeSelector mPictureSizeSelector; protected SizeSelector mPictureSizeSelector;
protected MediaRecorder mMediaRecorder; protected MediaRecorder mMediaRecorder;
protected File mVideoFile; protected File mVideoFile;
protected long mVideoMaxSize;
protected int mVideoMaxDuration;
protected Size mPictureSize; protected Size mPictureSize;
protected Size mPreviewSize; protected Size mPreviewSize;
protected int mPreviewFormat; protected int mPreviewFormat;
@ -67,7 +69,6 @@ abstract class CameraController implements
protected boolean mIsCapturingVideo = false; protected boolean mIsCapturingVideo = false;
protected int mState = STATE_STOPPED; protected int mState = STATE_STOPPED;
protected long mVideoMaxSizeInBytes = 0;
// Used for testing. // Used for testing.
Task<Void> mZoomTask = new Task<>(); Task<Void> mZoomTask = new Task<>();
@ -276,6 +277,15 @@ abstract class CameraController implements
mPictureSizeSelector = selector; mPictureSizeSelector = selector;
} }
final void setVideoMaxSize(long videoMaxSizeBytes) {
mVideoMaxSize = videoMaxSizeBytes;
}
final void setVideoMaxDuration(int videoMaxDurationMillis) {
mVideoMaxDuration = videoMaxDurationMillis;
}
//endregion //endregion
//region Abstract setters and APIs //region Abstract setters and APIs
@ -320,8 +330,6 @@ abstract class CameraController implements
abstract void startAutoFocus(@Nullable Gesture gesture, PointF point); abstract void startAutoFocus(@Nullable Gesture gesture, PointF point);
abstract void setVideoMaxSize(long videoMaxSizeInBytes);
abstract void setPlaySounds(boolean playSounds); abstract void setPlaySounds(boolean playSounds);
//endregion //endregion
@ -354,6 +362,14 @@ abstract class CameraController implements
return mVideoQuality; return mVideoQuality;
} }
final long getVideoMaxSize() {
return mVideoMaxSize;
}
final int getVideoMaxDuration() {
return mVideoMaxDuration;
}
final SessionType getSessionType() { final SessionType getSessionType() {
return mSessionType; return mSessionType;
} }

@ -107,6 +107,8 @@ public class CameraView extends FrameLayout {
SessionType sessionType = SessionType.fromValue(a.getInteger(R.styleable.CameraView_cameraSessionType, SessionType.DEFAULT.value())); SessionType sessionType = SessionType.fromValue(a.getInteger(R.styleable.CameraView_cameraSessionType, SessionType.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()));
long videoMaxSize = (long) a.getFloat(R.styleable.CameraView_cameraVideoMaxSize, 0);
int videoMaxDuration = a.getInteger(R.styleable.CameraView_cameraVideoMaxDuration, 0);
// Size selectors // Size selectors
List<SizeSelector> constraints = new ArrayList<>(3); List<SizeSelector> constraints = new ArrayList<>(3);
@ -145,9 +147,6 @@ public class CameraView extends FrameLayout {
GestureAction scrollHorizontalGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureScrollHorizontal, GestureAction.DEFAULT_SCROLL_HORIZONTAL.value())); GestureAction scrollHorizontalGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureScrollHorizontal, GestureAction.DEFAULT_SCROLL_HORIZONTAL.value()));
GestureAction scrollVerticalGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureScrollVertical, GestureAction.DEFAULT_SCROLL_VERTICAL.value())); GestureAction scrollVerticalGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureScrollVertical, GestureAction.DEFAULT_SCROLL_VERTICAL.value()));
//Get max size
float cameraVideoMaxSize = a.getFloat(R.styleable.CameraView_cameraVideoMaxSize, -1);
a.recycle(); a.recycle();
// Components // Components
@ -182,6 +181,8 @@ public class CameraView extends FrameLayout {
setHdr(hdr); setHdr(hdr);
setAudio(audio); setAudio(audio);
setPictureSize(selector); setPictureSize(selector);
setVideoMaxSize(videoMaxSize);
setVideoMaxDuration(videoMaxDuration);
// Apply gestures // Apply gestures
mapGesture(Gesture.TAP, tapGesture); mapGesture(Gesture.TAP, tapGesture);
@ -190,11 +191,6 @@ public class CameraView extends FrameLayout {
mapGesture(Gesture.SCROLL_HORIZONTAL, scrollHorizontalGesture); mapGesture(Gesture.SCROLL_HORIZONTAL, scrollHorizontalGesture);
mapGesture(Gesture.SCROLL_VERTICAL, scrollVerticalGesture); mapGesture(Gesture.SCROLL_VERTICAL, scrollVerticalGesture);
//Set camera video maxSize
if(cameraVideoMaxSize > 0) {
setVideoMaxSize((long)cameraVideoMaxSize);
}
if (!isInEditMode()) { if (!isInEditMode()) {
mOrientationHelper = new OrientationHelper(context, mCameraCallbacks); mOrientationHelper = new OrientationHelper(context, mCameraCallbacks);
} }
@ -1212,8 +1208,7 @@ public class CameraView extends FrameLayout {
/** /**
* Starts recording a video with selected options, in a file called * Starts recording a video, in a file called "video.mp4" in the default folder.
* "video.mp4" in the default folder.
* This is discouraged, please use {@link #startCapturingVideo(File)} instead. * This is discouraged, please use {@link #startCapturingVideo(File)} instead.
* *
* @deprecated see {@link #startCapturingVideo(File)} * @deprecated see {@link #startCapturingVideo(File)}
@ -1225,7 +1220,7 @@ public class CameraView extends FrameLayout {
/** /**
* Starts recording a video with selected options. Video will be written to the given file, * Starts recording a video. Video will be written to the given file,
* so callers should ensure they have appropriate permissions to write to the file. * so callers should ensure they have appropriate permissions to write to the file.
* *
* @param file a file where the video will be saved * @param file a file where the video will be saved
@ -1246,27 +1241,29 @@ public class CameraView extends FrameLayout {
/** /**
* Starts recording a video with selected options. Video will be written to the given file, * Starts recording a video. Video will be written to the given file,
* so callers should ensure they have appropriate permissions to write to the file. * so callers should ensure they have appropriate permissions to write to the file.
* Recording will be automatically stopped after durationMillis, unless * Recording will be automatically stopped after the given duration, overriding
* {@link #stopCapturingVideo()} is not called meanwhile. * temporarily any duration limit set by {@link #setVideoMaxDuration(int)}.
* *
* @param file a file where the video will be saved * @param file a file where the video will be saved
* @param durationMillis video max duration * @param durationMillis recording max duration
* *
* @throws IllegalArgumentException if durationMillis is less than 500 milliseconds * @deprecated use {@link #setVideoMaxDuration(int)} instead.
*/ */
@Deprecated
public void startCapturingVideo(File file, long durationMillis) { public void startCapturingVideo(File file, long durationMillis) {
if (durationMillis < 500) { // TODO: v2: change signature to int, or remove (better).
throw new IllegalArgumentException("Video duration can't be < 500 milliseconds"); final int old = getVideoMaxDuration();
} addCameraListener(new CameraListener() {
startCapturingVideo(file);
mUiHandler.postDelayed(new Runnable() {
@Override @Override
public void run() { public void onVideoTaken(File video) {
stopCapturingVideo(); setVideoMaxDuration(old);
removeCameraListener(this);
} }
}, durationMillis); });
setVideoMaxDuration((int) durationMillis);
startCapturingVideo(file);
} }
@ -1343,9 +1340,6 @@ public class CameraView extends FrameLayout {
} }
} }
//endregion
//region Sounds
@SuppressLint("NewApi") @SuppressLint("NewApi")
private void playSound(int soundType) { private void playSound(int soundType) {
@ -1355,6 +1349,7 @@ public class CameraView extends FrameLayout {
} }
} }
/** /**
* Controls whether CameraView should play sound effects on certain * Controls whether CameraView should play sound effects on certain
* events (picture taken, focus complete). Note that: * events (picture taken, focus complete). Note that:
@ -1368,6 +1363,7 @@ public class CameraView extends FrameLayout {
mCameraController.setPlaySounds(playSounds); mCameraController.setPlaySounds(playSounds);
} }
/** /**
* Gets the current sound effect behavior. * Gets the current sound effect behavior.
* *
@ -1378,16 +1374,55 @@ public class CameraView extends FrameLayout {
return mPlaySounds; return mPlaySounds;
} }
/** /**
* Set a max file size (in bytes) for a video recording. There is no file size limit by default * Sets the maximum size in bytes for recorded video files.
* unless set by the user. * Once this size is reached, the recording will automatically stop.
* Defaults to unlimited size. Use 0 or negatives to disable.
* *
* @param videoMaxSizeInBytes The maximum size of videos in bytes * @param videoMaxSizeInBytes The maximum video size in bytes
*/ */
public void setVideoMaxSize(long videoMaxSizeInBytes){ public void setVideoMaxSize(long videoMaxSizeInBytes) {
mCameraController.setVideoMaxSize(videoMaxSizeInBytes); mCameraController.setVideoMaxSize(videoMaxSizeInBytes);
} }
/**
* Returns the maximum size in bytes for recorded video files, or 0
* if no size was set.
*
* @see #setVideoMaxSize(long)
* @return the maximum size in bytes
*/
public long getVideoMaxSize() {
return mCameraController.getVideoMaxSize();
}
/**
* Sets the maximum duration in milliseconds for video recordings.
* Once this duration is reached, the recording will automatically stop.
* Defaults to unlimited duration. Use 0 or negatives to disable.
*
* @param videoMaxDurationMillis The maximum video duration in milliseconds
*/
public void setVideoMaxDuration(int videoMaxDurationMillis) {
mCameraController.setVideoMaxDuration(videoMaxDurationMillis);
}
/**
* Returns the maximum duration in milliseconds for video recordings, or 0
* if no limit was set.
*
* @see #setVideoMaxDuration(int)
* @return the maximum duration in milliseconds
*/
public int getVideoMaxDuration() {
return mCameraController.getVideoMaxDuration();
}
/** /**
* Returns true if the camera is currently recording a video * Returns true if the camera is currently recording a video
* @return boolean indicating if the camera is recording a video * @return boolean indicating if the camera is recording a video

@ -112,6 +112,8 @@
<attr name="cameraVideoMaxSize" format="float" /> <attr name="cameraVideoMaxSize" format="float" />
<attr name="cameraVideoMaxDuration" format="integer" />
<!-- deprecated attr name="cameraZoomMode" format="enum"> <!-- deprecated attr name="cameraZoomMode" format="enum">
<enum name="off" value="0" /> <enum name="off" value="0" />
<enum name="pinch" value="1" /> <enum name="pinch" value="1" />

Loading…
Cancel
Save