Add CameraView.setFrameProcessingFormat(int), tests, docs

pull/704/head
Mattia Iavarone 6 years ago
parent 2df7f826eb
commit ec6e799ade
  1. 2
      .github/workflows/build.yml
  2. 1
      README.md
  3. 28
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  4. 32
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  5. 1
      cameraview/src/main/res/values/attrs.xml
  6. 38
      docs/_posts/2018-12-20-frame-processing.md

@ -64,7 +64,7 @@ jobs:
with:
java-version: 1.8
- name: Execute emulator tests
timeout-minutes: 20
timeout-minutes: 30
uses: reactivecircus/android-emulator-runner@v2.2.0
with:
api-level: ${{ matrix.EMULATOR_API }}

@ -116,6 +116,7 @@ Using CameraView is extremely simple:
app:cameraSnapshotMaxHeight="@integer/snapshot_max_height"
app:cameraFrameProcessingMaxWidth="@integer/processing_max_width"
app:cameraFrameProcessingMaxHeight="@integer/processing_max_height"
app:cameraFrameProcessingFormat="@integer/processing_format"
app:cameraVideoBitRate="@integer/video_bit_rate"
app:cameraAudioBitRate="@integer/audio_bit_rate"
app:cameraGestureTap="none|autoFocus|takePicture"

@ -3,6 +3,7 @@ package com.otaliastudios.cameraview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.ImageFormat;
import android.graphics.PointF;
import android.location.Location;
import androidx.annotation.NonNull;
@ -168,10 +169,13 @@ public class CameraViewTest extends BaseTest {
assertEquals(cameraView.getLocation(), null);
assertEquals(cameraView.getExposureCorrection(), 0f, 0f);
assertEquals(cameraView.getZoom(), 0f, 0f);
assertEquals(cameraView.getVideoMaxDuration(), 0, 0);
assertEquals(cameraView.getVideoMaxSize(), 0, 0);
assertEquals(cameraView.getSnapshotMaxWidth(), 0, 0);
assertEquals(cameraView.getSnapshotMaxHeight(), 0, 0);
assertEquals(cameraView.getVideoMaxDuration(), 0);
assertEquals(cameraView.getVideoMaxSize(), 0);
assertEquals(cameraView.getSnapshotMaxWidth(), 0);
assertEquals(cameraView.getSnapshotMaxHeight(), 0);
assertEquals(cameraView.getFrameProcessingMaxWidth(), 0);
assertEquals(cameraView.getFrameProcessingMaxHeight(), 0);
assertEquals(cameraView.getFrameProcessingFormat(), 0);
// Self managed
GestureParser gestures = new GestureParser(empty);
@ -806,17 +810,25 @@ public class CameraViewTest extends BaseTest {
@Test
public void testSnapshotMaxSize() {
cameraView.setSnapshotMaxWidth(500);
assertEquals(cameraView.getSnapshotMaxWidth(), 500);
assertEquals(500, cameraView.getSnapshotMaxWidth());
cameraView.setSnapshotMaxHeight(700);
assertEquals(cameraView.getSnapshotMaxHeight(), 700);
assertEquals(700, cameraView.getSnapshotMaxHeight());
}
@Test
public void testFrameProcessingMaxSize() {
cameraView.setFrameProcessingMaxWidth(500);
assertEquals(cameraView.getFrameProcessingMaxWidth(), 500);
assertEquals(500, cameraView.getFrameProcessingMaxWidth());
cameraView.setFrameProcessingMaxHeight(700);
assertEquals(cameraView.getFrameProcessingMaxHeight(), 700);
assertEquals(700, cameraView.getFrameProcessingMaxHeight());
}
@Test
public void testFrameProcessingFormat() {
cameraView.setFrameProcessingFormat(ImageFormat.YUV_420_888);
assertEquals(ImageFormat.YUV_420_888, cameraView.getFrameProcessingFormat());
cameraView.setFrameProcessingFormat(ImageFormat.YUV_422_888);
assertEquals(ImageFormat.YUV_422_888, cameraView.getFrameProcessingFormat());
}
//endregion

@ -203,6 +203,9 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
DEFAULT_PICTURE_SNAPSHOT_METERING);
int snapshotMaxWidth = a.getInteger(R.styleable.CameraView_cameraSnapshotMaxWidth, 0);
int snapshotMaxHeight = a.getInteger(R.styleable.CameraView_cameraSnapshotMaxHeight, 0);
int frameMaxWidth = a.getInteger(R.styleable.CameraView_cameraFrameProcessingMaxWidth, 0);
int frameMaxHeight = a.getInteger(R.styleable.CameraView_cameraFrameProcessingMaxHeight, 0);
int frameFormat = a.getInteger(R.styleable.CameraView_cameraFrameProcessingFormat, 0);
// Size selectors and gestures
SizeSelectorParser sizeSelectors = new SizeSelectorParser(a);
@ -261,6 +264,9 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setPreviewFrameRate(videoFrameRate);
setSnapshotMaxWidth(snapshotMaxWidth);
setSnapshotMaxHeight(snapshotMaxHeight);
setFrameProcessingMaxWidth(frameMaxWidth);
setFrameProcessingMaxHeight(frameMaxHeight);
setFrameProcessingFormat(frameFormat);
// Apply gestures
mapGesture(Gesture.TAP, gestures.getTapAction());
@ -972,6 +978,9 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setPreviewFrameRate(oldEngine.getPreviewFrameRate());
setSnapshotMaxWidth(oldEngine.getSnapshotMaxWidth());
setSnapshotMaxHeight(oldEngine.getSnapshotMaxHeight());
setFrameProcessingMaxWidth(oldEngine.getFrameProcessingMaxWidth());
setFrameProcessingMaxHeight(oldEngine.getFrameProcessingMaxHeight());
setFrameProcessingFormat(oldEngine.getFrameProcessingFormat());
}
/**
@ -2303,7 +2312,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
/**
* Sets the max width for frame processing {@link Frame}s.
* This option is only supported by {@link Camera2Engine} and will have no effect
* This option is only supported by {@link Engine#CAMERA2} and will have no effect
* on other engines.
*
* @param maxWidth max width for frames
@ -2314,7 +2323,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
/**
* Sets the max height for frame processing {@link Frame}s.
* This option is only supported by {@link Camera2Engine} and will have no effect
* This option is only supported by {@link Engine#CAMERA2} and will have no effect
* on other engines.
*
* @param maxHeight max height for frames
@ -2341,6 +2350,25 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return mCameraEngine.getFrameProcessingMaxHeight();
}
/**
* Sets the {@link android.graphics.ImageFormat} for frame processing.
* Before applying you should check {@link CameraOptions#getSupportedFrameProcessingFormats()}.
*
* @param format image format
*/
public void setFrameProcessingFormat(int format) {
mCameraEngine.setFrameProcessingFormat(format);
}
/**
* Returns the current frame processing format.
* @see #setFrameProcessingFormat(int)
* @return image format
*/
public int getFrameProcessingFormat() {
return mCameraEngine.getFrameProcessingFormat();
}
//endregion
//region Overlays

@ -27,6 +27,7 @@
<attr name="cameraFrameProcessingMaxWidth" format="integer|reference" />
<attr name="cameraFrameProcessingMaxHeight" format="integer|reference" />
<attr name="cameraFrameProcessingFormat" format="integer|reference" />
<attr name="cameraVideoBitRate" format="integer|reference" />
<attr name="cameraAudioBitRate" format="integer|reference" />

@ -19,12 +19,17 @@ cameraView.addFrameProcessor(new FrameProcessor() {
@Override
@WorkerThread
public void process(@NonNull Frame frame) {
byte[] data = frame.getData();
int rotation = frame.getRotation();
long time = frame.getTime();
Size size = frame.getSize();
int format = frame.getFormat();
// Process...
if (frame.getDataClass() == byte[].class) {
byte[] data = frame.getData();
// Process byte array...
} else if (frame.getDataClass() == Image.class) {
Image data = frame.getData();
// Process android.media.Image...
}
}
}
```
@ -79,6 +84,15 @@ public void process(@NonNull Frame frame) {
}
```
### Frame Data
Starting from `v2.5.0`, the type of data offered by `frame.getData()` depends on the camera engine
that created this frame:
- The Camera1 engine will offer `byte[]` arrays
- The Camera2 engine will offer `android.media.Image` objects
You can check this at runtime by inspecting the data class using `frame.getDataClass()`.
### Frame Size
The Camera2 engine offers the option to set size constraints for the incoming frames.
@ -90,12 +104,25 @@ cameraView.setFrameProcessingMaxHeight(maxWidth);
With other engines, these API have no effect.
### Frame Format
The Camera2 engine offers the option to set the frame format as one of the ImageFormat
constants. The default is `ImageFormat.YUV_420_888`.
```java
cameraView.setFrameProcessingFormat(ImageFormat.YUV_420_888);
cameraView.setFrameProcessingFormat(ImageFormat.YUV_422_888);
```
With the Camera1 engine, the incoming format will always be `ImageFormat.NV21`.
### XML Attributes
```xml
<com.otaliastudios.cameraview.CameraView
app:cameraFrameProcessingMaxWidth="640"
app:cameraFrameProcessingMaxHeight="640"/>
app:cameraFrameProcessingMaxHeight="640"
app:cameraFrameProcessingFormat="0x23"/>
```
### Related APIs
@ -109,7 +136,10 @@ With other engines, these API have no effect.
|`camera.setFrameProcessingMaxHeight(int)`|`-`|Sets the max height for incoming frames.|
|`camera.getFrameProcessingMaxWidth()`|`int`|Gets the max width for incoming frames.|
|`camera.getFrameProcessingMaxHeight()`|`int`|Gets the max height for incoming frames.|
|`frame.getData()`|`byte[]`|The current preview frame, in its original orientation.|
|`camera.setFrameProcessingFormat(int)`|`-`|Sets the desired format for incoming frames. Should be one of the ImageFormat constants.|
|`camera.getFrameProcessingFormat()`|`-`|Gets the format for incoming frames. One of the ImageFormat constants.|
|`frame.getDataClass()`|`Class<T>`|The class of the data returned by `getData()`. Either `byte[]` or `android.media.Image`.|
|`frame.getData()`|`T`|The current preview frame, in its original orientation.|
|`frame.getTime()`|`long`|The preview timestamp, in `System.currentTimeMillis()` reference.|
|`frame.getRotation()`|`int`|The rotation that should be applied to the byte array in order to see what the user sees.|
|`frame.getSize()`|`Size`|The frame size, before any rotation is applied, to access data.|

Loading…
Cancel
Save