Add onVideoRecordingEnd

pull/506/head
Mattia Iavarone 6 years ago
parent c7f9b92896
commit 6f703cdbdc
  1. 9
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewCallbacksTest.java
  2. 8
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java
  3. 5
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java
  4. 7
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/video/VideoRecorderTest.java
  5. 21
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java
  6. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  7. 5
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
  8. 1
      cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java
  9. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java
  10. 19
      cameraview/src/main/java/com/otaliastudios/cameraview/video/VideoRecorder.java
  11. 1
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/AudioMediaEncoder.java
  12. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/MediaEncoderEngine.java
  13. 21
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
  14. 2
      docs/_posts/2018-12-20-camera-events.md
  15. 31
      docs/_posts/2018-12-20-capturing-media.md
  16. 2
      docs/_posts/2018-12-20-changelog.md
  17. 40
      docs/_posts/2018-12-20-controls.md
  18. 32
      docs/_posts/2018-12-20-more-features.md

@ -164,6 +164,15 @@ public class CameraViewCallbacksTest extends BaseTest {
verify(listener, times(1)).onVideoRecordingStart();
}
@Test
public void testDispatchOnVideoRecordingEnd() {
completeTask().when(listener).onVideoRecordingEnd();
camera.mCameraCallbacks.dispatchOnVideoRecordingEnd();
assertNotNull(op.await(500));
verify(listener, times(1)).onVideoRecordingEnd();
}
@Test
public void testDispatchOnVideoTaken() {
VideoResult.Stub stub = new VideoResult.Stub();

@ -167,6 +167,11 @@ public abstract class CameraIntegrationTest extends BaseTest {
@SuppressWarnings("UnusedReturnValue")
@Nullable
private VideoResult waitForVideoResult(boolean expectSuccess) {
// CountDownLatch for onVideoRecordingEnd.
CountDownLatch onVideoRecordingEnd = new CountDownLatch(1);
doCountDown(onVideoRecordingEnd).when(listener).onVideoRecordingEnd();
// Op for onVideoTaken.
final Op<VideoResult> video = new Op<>(true);
doEndTask(video, 0).when(listener).onVideoTaken(any(VideoResult.class));
doEndTask(video, null).when(listener).onCameraError(argThat(new ArgumentMatcher<CameraException>() {
@ -175,8 +180,11 @@ public abstract class CameraIntegrationTest extends BaseTest {
return argument.getReason() == CameraException.REASON_VIDEO_FAILED;
}
}));
// Wait for onVideoTaken and check.
VideoResult result = video.await(VIDEO_DELAY);
if (expectSuccess) {
assertEquals("Should call onVideoRecordingEnd", 0, onVideoRecordingEnd.getCount());
assertNotNull("Should end video", result);
} else {
assertNull("Should not end video", result);

@ -177,9 +177,4 @@ public class MockCameraEngine extends CameraEngine {
protected boolean collectCameraInfo(@NonNull Facing facing) {
return true;
}
@Override
public void onVideoRecordingStart() {
}
}

@ -27,10 +27,13 @@ public class VideoRecorderTest extends BaseTest {
VideoRecorder.VideoResultListener listener = Mockito.mock(VideoRecorder.VideoResultListener.class);
VideoRecorder recorder = new VideoRecorder(listener) {
@Override
protected void onStart() { dispatchVideoRecordingStart(); }
protected void onStart() {
dispatchVideoRecordingStart();
}
@Override
protected void onStop() {
dispatchVideoRecordingEnd();
dispatchResult();
}
};
@ -38,6 +41,8 @@ public class VideoRecorderTest extends BaseTest {
Mockito.verify(listener,Mockito.times(1) )
.onVideoRecordingStart();
recorder.stop();
Mockito.verify(listener, Mockito.times(1))
.onVideoRecordingEnd();
Mockito.verify(listener, Mockito.times(1))
.onVideoResult(result, null);
}

@ -129,13 +129,30 @@ public abstract class CameraListener {
/**
* Notifies that the actual video recording has started
* Notifies that the actual video recording has started.
* This is the time when actual frames recording starts.
* This can be used to show some indicator while the actual video recording.
*
* This can be used to show some UI indicator for video recording or counting time.
*
* @see #onVideoRecordingEnd()
*/
@UiThread
public void onVideoRecordingStart() {
}
/**
* Notifies that the actual video recording has ended.
* At this point recording has ended, though the file might still be processed.
* The {@link #onVideoTaken(VideoResult)} callback will be called soon.
*
* This can be used to remove UI indicators for video recording.
*
* @see #onVideoRecordingStart()
*/
@UiThread
public void onVideoRecordingEnd() {
}
}

@ -2080,7 +2080,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@Override
public void dispatchOnVideoRecordingStart() {
mLogger.i("dispatchOnVideoRecordingStart", "dispatchOnVideoRecordingStart");
mLogger.i("dispatchOnVideoRecordingStart");
mUiHandler.post(new Runnable() {
@Override
public void run() {
@ -2090,6 +2090,19 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
}
});
}
@Override
public void dispatchOnVideoRecordingEnd() {
mLogger.i("dispatchOnVideoRecordingEnd");
mUiHandler.post(new Runnable() {
@Override
public void run() {
for (CameraListener listener : mListeners) {
listener.onVideoRecordingEnd();
}
}
});
}
}
//endregion

@ -136,6 +136,7 @@ public abstract class CameraEngine implements
void dispatchFrame(Frame frame);
void dispatchError(CameraException exception);
void dispatchOnVideoRecordingStart();
void dispatchOnVideoRecordingEnd();
}
private static final String TAG = CameraEngine.class.getSimpleName();
@ -1210,6 +1211,10 @@ public abstract class CameraEngine implements
mCallback.dispatchOnVideoRecordingStart();
}
@Override
public void onVideoRecordingEnd() {
mCallback.dispatchOnVideoRecordingEnd();
}
@WorkerThread
protected abstract void onTakePicture(@NonNull PictureResult.Stub stub);

@ -148,6 +148,7 @@ public abstract class FullVideoRecorder extends VideoRecorder {
@Override
protected void onStop() {
if (mMediaRecorder != null) {
dispatchVideoRecordingEnd();
try {
mMediaRecorder.stop();
} catch (Exception e) {

@ -81,7 +81,6 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
protected void onStart() {
mPreview.addRendererFrameCallback(this);
mDesiredState = STATE_RECORDING;
dispatchVideoRecordingStart();
}
@Override
@ -210,7 +209,12 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
@Override
public void onEncodingStart() {
// Do nothing.
dispatchVideoRecordingStart();
}
@Override
public void onEncodingStop() {
dispatchVideoRecordingEnd();
}
@EncoderThread

@ -29,10 +29,16 @@ public abstract class VideoRecorder {
* The callback for the actual video recording starting.
*/
void onVideoRecordingStart();
/**
* Video recording has ended. We will finish processing the file
* and soon {@link #onVideoResult(VideoResult.Stub, Exception)} will be called.
*/
void onVideoRecordingEnd();
}
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) VideoResult.Stub mResult;
@VisibleForTesting final VideoResultListener mListener;
private final VideoResultListener mListener;
@SuppressWarnings("WeakerAccess")
protected Exception mError;
private boolean mIsRecording;
@ -101,4 +107,15 @@ public abstract class VideoRecorder {
mListener.onVideoRecordingStart();
}
}
/**
* Subclasses can call this to notify that the video recording has ended,
* although the video result might still be processed.
*/
@CallSuper
protected void dispatchVideoRecordingEnd() {
if (mListener != null) {
mListener.onVideoRecordingEnd();
}
}
}

@ -22,7 +22,6 @@ import java.util.concurrent.LinkedBlockingQueue;
/**
* Default implementation for audio encoding.
*/
// TODO create onVideoRecordingEnd callback
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class AudioMediaEncoder extends MediaEncoder {

@ -68,7 +68,17 @@ public class MediaEncoderEngine {
void onEncodingStart();
/**
* Called when encoding stopped for some reason.
* Called when encoding stopped. At this point the mxuer might still be processing,
* but we have stopped receiving input (recording video and audio frames).
*
* The {@link #onEncodingEnd(int, Exception)} callback will soon be called
* with the results.
*/
@EncoderThread
void onEncodingStop();
/**
* Called when encoding ended for some reason.
* If there's an exception, it failed.
* @param reason the reason
* @param e the error, if present
@ -353,6 +363,9 @@ public class MediaEncoderEngine {
LOG.w("notifyStopped:", "Called for track", track);
if (++mReleasedEncodersCount == mEncoders.size()) {
LOG.w("requestStop:", "All encoders have been released. Stopping the muxer.");
if (mListener != null) {
mListener.onEncodingStop();
}
end();
}
}

@ -35,6 +35,8 @@ import java.util.List;
public class CameraActivity extends AppCompatActivity implements View.OnClickListener, OptionView.Callback {
private final static CameraLogger LOG = CameraLogger.create("DemoApp");
private CameraView camera;
private ViewGroup controlPanel;
private long mCaptureTime;
@ -134,9 +136,14 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
animator.start();
}
private void message(String content, boolean important) {
int length = important ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT;
Toast.makeText(this, content, length).show();
private void message(@NonNull String content, boolean important) {
if (important) {
LOG.w(content);
Toast.makeText(this, content, Toast.LENGTH_LONG).show();
} else {
LOG.i(content);
Toast.makeText(this, content, Toast.LENGTH_SHORT).show();
}
}
private class Listener extends CameraListener {
@ -170,7 +177,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
PicturePreviewActivity.setPictureResult(result);
Intent intent = new Intent(CameraActivity.this, PicturePreviewActivity.class);
intent.putExtra("delay", callbackTime - mCaptureTime);
Log.e("CameraActivity", "Picture delay: " + (callbackTime - mCaptureTime));
LOG.w("Picture delay:", callbackTime - mCaptureTime);
startActivity(intent);
mCaptureTime = 0;
}
@ -182,6 +189,12 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
Intent intent = new Intent(CameraActivity.this, VideoPreviewActivity.class);
startActivity(intent);
}
@Override
public void onVideoRecordingStart() {
super.onVideoRecordingStart();
LOG.w("onVideoRecordingStart!");
}
}
@Override

@ -39,6 +39,8 @@ camera.addCameraListener(new CameraListener() {
public void onExposureCorrectionChanged(float newValue, float[] bounds, PointF[] fingers) {}
public void onVideoRecordingStart() {}
public void onVideoRecordingEnd() {}
});
```

@ -59,7 +59,7 @@ Please note that the video snaphot features requires:
This is allowed at the following conditions:
- `takePictureSnapshot()` is used (no HQ pictures)
- the OpenGL preview is used (see [previews](previews.html))
- the `GL_SURFACE` preview is used (see [previews](previews.html))
### Related XML attributes
@ -68,6 +68,35 @@ This is allowed at the following conditions:
app:cameraMode="picture|video"/>
```
### Related callbacks
```java
camera.addCameraListener(new CameraListener() {
@Override
public void onPictureTaken(@NonNull PictureResult result) {
// A Picture was taken!
}
@Override
public void onVideoTaken(@NonNull VideoResult result) {
// A Video was taken!
}
@Override
public void onVideoRecordingStart() {
// Notifies that the actual video recording has started.
// Can be used to show some UI indicator for video recording or counting time.
}
@Override
public void onVideoRecordingEnd() {
// Notifies that the actual video recording has ended.
// Can be used to remove UI indicators added in onVideoRecordingStart.
}
})
```
### Related APIs
|Method|Description|

@ -27,7 +27,7 @@ New versions are released through GitHub, so the reference page is the [GitHub R
If you were using `focus`, just switch to `autoFocus`.
If you were using `focusWithMarker`, you can [add back the old marker](../docs/more-features.html#cameraautofocusmarker).
If you were using `focusWithMarker`, you can [add back the old marker](../docs/controls.html#cameraautofocusmarker).
### v2.0.0-beta05

@ -143,7 +143,7 @@ cameraView.setVideoBitRate(0);
cameraView.setVideoBitRate(4000000);
```
### Manual Focus
### Auto Focus
There are many ways to focus a CameraView engine:
@ -175,6 +175,44 @@ cameraView.addCameraListener(new CameraListener() {
Auto focus is not guaranteed to be supported: check the `CameraOptions` to be sure.
```xml
<com.otaliastudios.cameraview.CameraView
app:cameraAutoFocusMarker="@string/cameraview_default_autofocus_marker"
app:cameraAutoFocusResetDelay="3000"/>
```
##### cameraAutoFocusMarker
Lets you set a marker for drawing on screen in response to auto focus events.
In XML, you should pass the qualified class name of your marker.
```java
cameraView.setAutoFocusMarker(null);
cameraView.setAutoFocusMarker(marker);
```
We offer a default marker (similar to the old `focusWithMarker` attribute in v1),
which you can set in XML using the `@string/cameraview_default_autofocus_marker` resource,
or programmatically:
```java
cameraView.setAutoFocusMarker(new DefaultAutoFocusMarker());
```
##### cameraAutoFocusResetDelay
Lets you control how an auto-focus operation is reset after completed.
Setting a value <= 0 or == Long.MAX_VALUE will not reset the auto-focus.
This is useful for low end devices that have slow auto-focus capabilities.
Defaults to 3 seconds.
```java
cameraView.setCameraAutoFocusResetDelay(1000); // 1 second
cameraView.setCameraAutoFocusResetDelay(0); // NO reset
cameraView.setCameraAutoFocusResetDelay(-1); // NO reset
cameraView.setCameraAutoFocusResetDelay(Long.MAX_VALUE); // NO reset
```
### Zoom
There are two ways to control the zoom value:

@ -58,38 +58,6 @@ cameraView.setGridColor(Color.WHITE);
cameraView.setGridColor(Color.BLACK);
```
##### cameraAutoFocusMarker
Lets you set a marker for drawing on screen in response to auto focus events.
In XML, you should pass the qualified class name of your marker.
```java
cameraView.setAutoFocusMarker(null);
cameraView.setAutoFocusMarker(marker);
```
We offer a default marker (similar to the old `focusWithMarker` attribute in v1),
which you can set in XML using the `@string/cameraview_default_autofocus_marker` resource,
or programmatically:
```java
cameraView.setAutoFocusMarker(new DefaultAutoFocusMarker());
```
##### cameraAutoFocusResetDelay
Lets you control how an auto-focus operation is reset after completed.
Setting a value <= 0 or == Long.MAX_VALUE will not reset the auto-focus.
This is useful for low end devices that have slow auto-focus capabilities.
Defaults to 3 seconds.
```java
cameraView.setCameraAutoFocusResetDelay(1000); // 1 second
cameraView.setCameraAutoFocusResetDelay(0); // NO reset
cameraView.setCameraAutoFocusResetDelay(-1); // NO reset
cameraView.setCameraAutoFocusResetDelay(Long.MAX_VALUE); // NO reset
```
##### cameraUseDeviceOrientation
Controls whether we should consider the device orientation for picture and video outputs.

Loading…
Cancel
Save