Scale down based on encoder requirements

pull/545/head
Mattia Iavarone 6 years ago
parent 15618d9d82
commit d69a5e286a
  1. 34
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/DeviceEncodersTest.java
  2. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  3. 21
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/DeviceEncoders.java
  4. 11
      docs/_posts/2019-02-24-snapshot-size.md

@ -11,6 +11,7 @@ import androidx.test.rule.ActivityTestRule;
import com.otaliastudios.cameraview.BaseTest; import com.otaliastudios.cameraview.BaseTest;
import com.otaliastudios.cameraview.TestActivity; import com.otaliastudios.cameraview.TestActivity;
import com.otaliastudios.cameraview.controls.Grid; import com.otaliastudios.cameraview.controls.Grid;
import com.otaliastudios.cameraview.size.AspectRatio;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
import org.junit.After; import org.junit.After;
@ -122,30 +123,27 @@ public class DeviceEncodersTest extends BaseTest {
assertSame(input, output); assertSame(input, output);
} }
@Test(expected = RuntimeException.class) @Test
public void testGetSupportedVideoSize_hugeWidth() { public void testGetSupportedVideoSize_scalesDown() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
Size input = new Size(Integer.MAX_VALUE, GUARANTEED_SIZE.getHeight());
deviceEncoders.getSupportedVideoSize(input);
} else {
throw new RuntimeException("Test should pass.");
}
}
@Test(expected = RuntimeException.class)
public void testGetSupportedVideoSize_hugeHeight() {
DeviceEncoders deviceEncoders = create(); DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) { if (DeviceEncoders.ENABLED) {
Size input = new Size(GUARANTEED_SIZE.getWidth(), Integer.MAX_VALUE); Size input = new Size(
deviceEncoders.getSupportedVideoSize(input); GUARANTEED_SIZE.getWidth() * 1000,
} else { GUARANTEED_SIZE.getHeight() * 1000);
throw new RuntimeException("Test should pass."); try {
Size output = deviceEncoders.getSupportedVideoSize(input);
assertTrue(AspectRatio.of(input).matches(output, 0.01F));
} catch (RuntimeException e) {
// The scaled down size happens to be not supported.
// I see no way of testing this easily if we're not sure of supported ranges.
// This depends highly on the alignment since scaling down, while keeping AR,
// can change the alignment and require width / height changes.
}
} }
} }
@Test @Test
public void testGetSupportedVideoSize_alignsSize() { public void testGetSupportedVideoSize_aligns() {
DeviceEncoders deviceEncoders = create(); DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) { if (DeviceEncoders.ENABLED) {
Size input = new Size(GUARANTEED_SIZE.getWidth() + 1, Size input = new Size(GUARANTEED_SIZE.getWidth() + 1,

@ -1664,6 +1664,12 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
* (for example if the surface is not ready). This is the preview size, rotated to match * (for example if the surface is not ready). This is the preview size, rotated to match
* the output orientation, and cropped to the visible part. * the output orientation, and cropped to the visible part.
* *
* This also includes the {@link #setSnapshotMaxWidth(int)} and
* {@link #setSnapshotMaxHeight(int)} constraints.
*
* This does NOT include any constraints specific to video encoding, which are
* device specific and depend on the capabilities of the device codec.
*
* @return the size of snapshots * @return the size of snapshots
*/ */
@Nullable @Nullable

@ -138,8 +138,25 @@ public class DeviceEncoders {
if (!ENABLED) return size; if (!ENABLED) return size;
int width = size.getWidth(); int width = size.getWidth();
int height = size.getHeight(); int height = size.getHeight();
double aspect = (double) width / height;
// If width is too large, scale down, but keep aspect ratio.
if (mVideoCapabilities.getSupportedWidths().getUpper() < width) {
width = mVideoCapabilities.getSupportedWidths().getUpper();
height = (int) Math.round(width / aspect);
}
// If height is too large, scale down, but keep aspect ratio.
if (mVideoCapabilities.getSupportedHeights().getUpper() < height) {
height = mVideoCapabilities.getSupportedHeights().getUpper();
width = (int) Math.round(aspect * height);
}
// Adjust the alignment.
while (width % mVideoCapabilities.getWidthAlignment() != 0) width--; while (width % mVideoCapabilities.getWidthAlignment() != 0) width--;
while (height % mVideoCapabilities.getHeightAlignment() != 0) height--; while (height % mVideoCapabilities.getHeightAlignment() != 0) height--;
// It's still possible that we're BELOW the lower.
if (!mVideoCapabilities.getSupportedWidths().contains(width)) { if (!mVideoCapabilities.getSupportedWidths().contains(width)) {
throw new RuntimeException("Width not supported after adjustment." + throw new RuntimeException("Width not supported after adjustment." +
" Desired:" + width + " Desired:" + width +
@ -150,10 +167,12 @@ public class DeviceEncoders {
" Desired:" + height + " Desired:" + height +
" Range:" + mVideoCapabilities.getSupportedHeights()); " Range:" + mVideoCapabilities.getSupportedHeights());
} }
// It's still possible that we're unsupported for other reasons.
if (!mVideoCapabilities.isSizeSupported(width, height)) { if (!mVideoCapabilities.isSizeSupported(width, height)) {
throw new RuntimeException("Size not supported for unknown reason." + throw new RuntimeException("Size not supported for unknown reason." +
" Might be an aspect ratio issue." + " Might be an aspect ratio issue." +
" Desired size:" + size); " Desired size:" + new Size(width, height));
} }
Size adjusted = new Size(width, height); Size adjusted = new Size(width, height);
LOG.i("getSupportedVideoSize -", "inputSize:", size, "adjustedSize:", adjusted); LOG.i("getSupportedVideoSize -", "inputSize:", size, "adjustedSize:", adjusted);

@ -42,6 +42,17 @@ This is very useful as it decouples the snapshot size logic from the preview. By
you can have a pleasant, good looking preview stream, while still capturing fast, low-res snapshots you can have a pleasant, good looking preview stream, while still capturing fast, low-res snapshots
with no issues. with no issues.
### Video Codec requirements
When taking video snapshots, the video codec that the device provides might require extra constraints,
like
- width / height alignment
- maximum width or height
CameraView will try to read these requirements and apply them, which can result in video snapshots
that are smaller than you would expect, or with a **very slightly** different aspect ratio.
### XML Attributes ### XML Attributes
```xml ```xml

Loading…
Cancel
Save