Merge "Camera2: Allow rendering to arbitrary surface sizes in LEGACY mode." into lmp-mr1-dev
This commit is contained in:
@@ -22,6 +22,8 @@ import android.os.ConditionVariable;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.util.Size;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -57,11 +59,11 @@ public class GLThreadManager {
|
|||||||
*/
|
*/
|
||||||
private static class ConfigureHolder {
|
private static class ConfigureHolder {
|
||||||
public final ConditionVariable condition;
|
public final ConditionVariable condition;
|
||||||
public final Collection<Surface> surfaces;
|
public final Collection<Pair<Surface, Size>> surfaces;
|
||||||
public final CaptureCollector collector;
|
public final CaptureCollector collector;
|
||||||
|
|
||||||
public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces,
|
public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface,
|
||||||
CaptureCollector collector) {
|
Size>> surfaces, CaptureCollector collector) {
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.surfaces = surfaces;
|
this.surfaces = surfaces;
|
||||||
this.collector = collector;
|
this.collector = collector;
|
||||||
@@ -202,10 +204,12 @@ public class GLThreadManager {
|
|||||||
* Configure the GL renderer for the given set of output surfaces, and block until
|
* Configure the GL renderer for the given set of output surfaces, and block until
|
||||||
* this configuration has been applied.
|
* this configuration has been applied.
|
||||||
*
|
*
|
||||||
* @param surfaces a collection of {@link android.view.Surface}s to configure.
|
* @param surfaces a collection of pairs of {@link android.view.Surface}s and their
|
||||||
|
* corresponding sizes to configure.
|
||||||
* @param collector a {@link CaptureCollector} to retrieve requests from.
|
* @param collector a {@link CaptureCollector} to retrieve requests from.
|
||||||
*/
|
*/
|
||||||
public void setConfigurationAndWait(Collection<Surface> surfaces, CaptureCollector collector) {
|
public void setConfigurationAndWait(Collection<Pair<Surface, Size>> surfaces,
|
||||||
|
CaptureCollector collector) {
|
||||||
checkNotNull(collector, "collector must not be null");
|
checkNotNull(collector, "collector must not be null");
|
||||||
Handler handler = mGLHandlerThread.getHandler();
|
Handler handler = mGLHandlerThread.getHandler();
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import android.hardware.camera2.CaptureRequest;
|
|||||||
import android.hardware.camera2.impl.CameraDeviceImpl;
|
import android.hardware.camera2.impl.CameraDeviceImpl;
|
||||||
import android.hardware.camera2.impl.CaptureResultExtras;
|
import android.hardware.camera2.impl.CaptureResultExtras;
|
||||||
import android.hardware.camera2.ICameraDeviceCallbacks;
|
import android.hardware.camera2.ICameraDeviceCallbacks;
|
||||||
import android.hardware.camera2.params.StreamConfiguration;
|
|
||||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||||
import android.hardware.camera2.utils.ArrayUtils;
|
import android.hardware.camera2.utils.ArrayUtils;
|
||||||
import android.hardware.camera2.utils.CameraBinderDecorator;
|
import android.hardware.camera2.utils.CameraBinderDecorator;
|
||||||
@@ -36,6 +35,7 @@ import android.os.Handler;
|
|||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
@@ -78,6 +78,15 @@ public class LegacyCameraDevice implements AutoCloseable {
|
|||||||
private final Handler mResultHandler;
|
private final Handler mResultHandler;
|
||||||
private static final int ILLEGAL_VALUE = -1;
|
private static final int ILLEGAL_VALUE = -1;
|
||||||
|
|
||||||
|
// Keep up to date with values in hardware/libhardware/include/hardware/gralloc.h
|
||||||
|
private static final int GRALLOC_USAGE_RENDERSCRIPT = 0x00100000;
|
||||||
|
private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003;
|
||||||
|
private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100;
|
||||||
|
private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800;
|
||||||
|
private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
|
||||||
|
|
||||||
|
private static final int MAX_DIMEN_FOR_ROUNDING = 1080; // maximum allowed width for rounding
|
||||||
|
|
||||||
private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
|
private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
|
||||||
if (holder == null) {
|
if (holder == null) {
|
||||||
return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
|
return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
|
||||||
@@ -276,6 +285,7 @@ public class LegacyCameraDevice implements AutoCloseable {
|
|||||||
* on success.
|
* on success.
|
||||||
*/
|
*/
|
||||||
public int configureOutputs(List<Surface> outputs) {
|
public int configureOutputs(List<Surface> outputs) {
|
||||||
|
List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
|
||||||
if (outputs != null) {
|
if (outputs != null) {
|
||||||
for (Surface output : outputs) {
|
for (Surface output : outputs) {
|
||||||
if (output == null) {
|
if (output == null) {
|
||||||
@@ -289,16 +299,25 @@ public class LegacyCameraDevice implements AutoCloseable {
|
|||||||
try {
|
try {
|
||||||
Size s = getSurfaceSize(output);
|
Size s = getSurfaceSize(output);
|
||||||
int surfaceType = detectSurfaceType(output);
|
int surfaceType = detectSurfaceType(output);
|
||||||
Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
|
int usageFlags = detectSurfaceUsageFlags(output);
|
||||||
|
|
||||||
|
// Keep up to date with allowed consumer types in
|
||||||
|
// frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
|
||||||
|
int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
|
||||||
|
int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
|
||||||
|
GRALLOC_USAGE_HW_COMPOSER;
|
||||||
|
boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
|
||||||
|
(usageFlags & allowedFlags) != 0);
|
||||||
|
|
||||||
|
Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
|
||||||
if (sizes == null) {
|
if (sizes == null) {
|
||||||
// WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
|
// WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
|
||||||
if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
|
if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
|
||||||
surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
|
surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
|
||||||
|
|
||||||
// YUV_420_888 is always present in LEGACY for all IMPLEMENTATION_DEFINED
|
// YUV_420_888 is always present in LEGACY for all
|
||||||
// output sizes, and is publicly visible in the API (i.e.
|
// IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
|
||||||
// {@code #getOutputSizes} works here).
|
// API (i.e. {@code #getOutputSizes} works here).
|
||||||
sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
|
sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
|
||||||
} else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
|
} else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
|
||||||
sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
|
sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
|
||||||
@@ -306,12 +325,18 @@ public class LegacyCameraDevice implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ArrayUtils.contains(sizes, s)) {
|
if (!ArrayUtils.contains(sizes, s)) {
|
||||||
String reason = (sizes == null) ? "format is invalid." :
|
if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
|
||||||
("size not in valid set: " + Arrays.toString(sizes));
|
sizedSurfaces.add(new Pair<>(output, s));
|
||||||
Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format 0x%x is"
|
} else {
|
||||||
+ " not valid, %s", s.getWidth(), s.getHeight(), surfaceType,
|
String reason = (sizes == null) ? "format is invalid." :
|
||||||
reason));
|
("size not in valid set: " + Arrays.toString(sizes));
|
||||||
return BAD_VALUE;
|
Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
|
||||||
|
"0x%x is not valid, %s", s.getWidth(), s.getHeight(),
|
||||||
|
surfaceType, reason));
|
||||||
|
return BAD_VALUE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sizedSurfaces.add(new Pair<>(output, s));
|
||||||
}
|
}
|
||||||
} catch (BufferQueueAbandonedException e) {
|
} catch (BufferQueueAbandonedException e) {
|
||||||
Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
|
Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
|
||||||
@@ -323,7 +348,7 @@ public class LegacyCameraDevice implements AutoCloseable {
|
|||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
if (mDeviceState.setConfiguring()) {
|
if (mDeviceState.setConfiguring()) {
|
||||||
mRequestThreadManager.configure(outputs);
|
mRequestThreadManager.configure(sizedSurfaces);
|
||||||
success = mDeviceState.setIdle();
|
success = mDeviceState.setIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,6 +498,31 @@ public class LegacyCameraDevice implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long findEuclidDistSquare(Size a, Size b) {
|
||||||
|
long d0 = a.getWidth() - b.getWidth();
|
||||||
|
long d1 = a.getHeight() - b.getHeight();
|
||||||
|
return d0 * d0 + d1 * d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep up to date with rounding behavior in
|
||||||
|
// frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
|
||||||
|
static Size findClosestSize(Size size, Size[] supportedSizes) {
|
||||||
|
if (size == null || supportedSizes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Size bestSize = null;
|
||||||
|
for (Size s : supportedSizes) {
|
||||||
|
if (s.equals(size)) {
|
||||||
|
return size;
|
||||||
|
} else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
|
||||||
|
LegacyCameraDevice.findEuclidDistSquare(size, s) <
|
||||||
|
LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
|
||||||
|
bestSize = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestSize;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the surface for its currently configured default buffer size.
|
* Query the surface for its currently configured default buffer size.
|
||||||
* @param surface a non-{@code null} {@code Surface}
|
* @param surface a non-{@code null} {@code Surface}
|
||||||
@@ -490,6 +540,11 @@ public class LegacyCameraDevice implements AutoCloseable {
|
|||||||
return new Size(dimens[0], dimens[1]);
|
return new Size(dimens[0], dimens[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int detectSurfaceUsageFlags(Surface surface) {
|
||||||
|
checkNotNull(surface);
|
||||||
|
return nativeDetectSurfaceUsageFlags(surface);
|
||||||
|
}
|
||||||
|
|
||||||
static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
|
static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
|
||||||
checkNotNull(surface);
|
checkNotNull(surface);
|
||||||
return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
|
return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
|
||||||
@@ -608,5 +663,7 @@ public class LegacyCameraDevice implements AutoCloseable {
|
|||||||
|
|
||||||
private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
|
private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
|
||||||
|
|
||||||
|
private static native int nativeDetectSurfaceUsageFlags(Surface surface);
|
||||||
|
|
||||||
static native int nativeGetJpegFooterSize();
|
static native int nativeGetJpegFooterSize();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -116,9 +117,10 @@ public class RequestThreadManager {
|
|||||||
*/
|
*/
|
||||||
private static class ConfigureHolder {
|
private static class ConfigureHolder {
|
||||||
public final ConditionVariable condition;
|
public final ConditionVariable condition;
|
||||||
public final Collection<Surface> surfaces;
|
public final Collection<Pair<Surface, Size>> surfaces;
|
||||||
|
|
||||||
public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces) {
|
public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface,
|
||||||
|
Size>> surfaces) {
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.surfaces = surfaces;
|
this.surfaces = surfaces;
|
||||||
}
|
}
|
||||||
@@ -317,7 +319,7 @@ public class RequestThreadManager {
|
|||||||
startPreview();
|
startPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureOutputs(Collection<Surface> outputs) {
|
private void configureOutputs(Collection<Pair<Surface, Size>> outputs) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
|
String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
|
||||||
Log.d(TAG, "configureOutputs with " + outputsStr);
|
Log.d(TAG, "configureOutputs with " + outputsStr);
|
||||||
@@ -346,10 +348,15 @@ public class RequestThreadManager {
|
|||||||
mJpegSurfaceIds.clear();
|
mJpegSurfaceIds.clear();
|
||||||
mPreviewTexture = null;
|
mPreviewTexture = null;
|
||||||
|
|
||||||
|
List<Size> previewOutputSizes = new ArrayList<>();
|
||||||
|
List<Size> callbackOutputSizes = new ArrayList<>();
|
||||||
|
|
||||||
int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
|
int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
|
||||||
int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
|
int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
|
||||||
if (outputs != null) {
|
if (outputs != null) {
|
||||||
for (Surface s : outputs) {
|
for (Pair<Surface, Size> outPair : outputs) {
|
||||||
|
Surface s = outPair.first;
|
||||||
|
Size outSize = outPair.second;
|
||||||
try {
|
try {
|
||||||
int format = LegacyCameraDevice.detectSurfaceType(s);
|
int format = LegacyCameraDevice.detectSurfaceType(s);
|
||||||
LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
|
LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
|
||||||
@@ -362,9 +369,11 @@ public class RequestThreadManager {
|
|||||||
}
|
}
|
||||||
mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
|
mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
|
||||||
mCallbackOutputs.add(s);
|
mCallbackOutputs.add(s);
|
||||||
|
callbackOutputSizes.add(outSize);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mPreviewOutputs.add(s);
|
mPreviewOutputs.add(s);
|
||||||
|
previewOutputSizes.add(outSize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||||
@@ -391,18 +400,9 @@ public class RequestThreadManager {
|
|||||||
mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
|
mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
|
||||||
bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
|
bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
|
||||||
|
|
||||||
if (mPreviewOutputs.size() > 0) {
|
if (previewOutputSizes.size() > 0) {
|
||||||
List<Size> outputSizes = new ArrayList<>(outputs.size());
|
|
||||||
for (Surface s : mPreviewOutputs) {
|
|
||||||
try {
|
|
||||||
Size size = LegacyCameraDevice.getSurfaceSize(s);
|
|
||||||
outputSizes.add(size);
|
|
||||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
|
||||||
Log.w(TAG, "Surface abandoned, skipping...", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Size largestOutput = SizeAreaComparator.findLargestByArea(outputSizes);
|
Size largestOutput = SizeAreaComparator.findLargestByArea(previewOutputSizes);
|
||||||
|
|
||||||
// Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
|
// Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
|
||||||
Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
|
Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
|
||||||
@@ -439,7 +439,8 @@ public class RequestThreadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs, mParams);
|
Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs,
|
||||||
|
callbackOutputSizes, mParams);
|
||||||
if (smallestSupportedJpegSize != null) {
|
if (smallestSupportedJpegSize != null) {
|
||||||
/*
|
/*
|
||||||
* Set takePicture size to the smallest supported JPEG size large enough
|
* Set takePicture size to the smallest supported JPEG size large enough
|
||||||
@@ -457,7 +458,12 @@ public class RequestThreadManager {
|
|||||||
mGLThreadManager.start();
|
mGLThreadManager.start();
|
||||||
}
|
}
|
||||||
mGLThreadManager.waitUntilStarted();
|
mGLThreadManager.waitUntilStarted();
|
||||||
mGLThreadManager.setConfigurationAndWait(mPreviewOutputs, mCaptureCollector);
|
List<Pair<Surface, Size>> previews = new ArrayList<>();
|
||||||
|
Iterator<Size> previewSizeIter = previewOutputSizes.iterator();
|
||||||
|
for (Surface p : mPreviewOutputs) {
|
||||||
|
previews.add(new Pair<>(p, previewSizeIter.next()));
|
||||||
|
}
|
||||||
|
mGLThreadManager.setConfigurationAndWait(previews, mCaptureCollector);
|
||||||
mGLThreadManager.allowNewFrames();
|
mGLThreadManager.allowNewFrames();
|
||||||
mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
|
mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
|
||||||
if (mPreviewTexture != null) {
|
if (mPreviewTexture != null) {
|
||||||
@@ -499,26 +505,25 @@ public class RequestThreadManager {
|
|||||||
* {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
|
* {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
|
||||||
* surfaces.
|
* surfaces.
|
||||||
*/
|
*/
|
||||||
private Size calculatePictureSize(
|
private Size calculatePictureSize( List<Surface> callbackOutputs,
|
||||||
Collection<Surface> callbackOutputs, Camera.Parameters params) {
|
List<Size> callbackSizes, Camera.Parameters params) {
|
||||||
/*
|
/*
|
||||||
* Find the largest JPEG size (if any), from the configured outputs:
|
* Find the largest JPEG size (if any), from the configured outputs:
|
||||||
* - the api1 picture size should be set to the smallest legal size that's at least as large
|
* - the api1 picture size should be set to the smallest legal size that's at least as large
|
||||||
* as the largest configured JPEG size
|
* as the largest configured JPEG size
|
||||||
*/
|
*/
|
||||||
List<Size> configuredJpegSizes = new ArrayList<Size>();
|
if (callbackOutputs.size() != callbackSizes.size()) {
|
||||||
|
throw new IllegalStateException("Input collections must be same length");
|
||||||
|
}
|
||||||
|
List<Size> configuredJpegSizes = new ArrayList<>();
|
||||||
|
Iterator<Size> sizeIterator = callbackSizes.iterator();
|
||||||
for (Surface callbackSurface : callbackOutputs) {
|
for (Surface callbackSurface : callbackOutputs) {
|
||||||
try {
|
Size jpegSize = sizeIterator.next();
|
||||||
|
|
||||||
if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
|
if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
|
||||||
continue; // Ignore non-JPEG callback formats
|
continue; // Ignore non-JPEG callback formats
|
||||||
}
|
}
|
||||||
|
|
||||||
Size jpegSize = LegacyCameraDevice.getSurfaceSize(callbackSurface);
|
|
||||||
configuredJpegSizes.add(jpegSize);
|
configuredJpegSizes.add(jpegSize);
|
||||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
|
||||||
Log.w(TAG, "Surface abandoned, skipping...", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!configuredJpegSizes.isEmpty()) {
|
if (!configuredJpegSizes.isEmpty()) {
|
||||||
/*
|
/*
|
||||||
@@ -994,7 +999,7 @@ public class RequestThreadManager {
|
|||||||
*
|
*
|
||||||
* @param outputs a {@link java.util.Collection} of outputs to configure.
|
* @param outputs a {@link java.util.Collection} of outputs to configure.
|
||||||
*/
|
*/
|
||||||
public void configure(Collection<Surface> outputs) {
|
public void configure(Collection<Pair<Surface, Size>> outputs) {
|
||||||
Handler handler = mRequestThread.waitAndGetHandler();
|
Handler handler = mRequestThread.waitAndGetHandler();
|
||||||
final ConditionVariable condition = new ConditionVariable(/*closed*/false);
|
final ConditionVariable condition = new ConditionVariable(/*closed*/false);
|
||||||
ConfigureHolder holder = new ConfigureHolder(condition, outputs);
|
ConfigureHolder holder = new ConfigureHolder(condition, outputs);
|
||||||
|
|||||||
@@ -397,16 +397,9 @@ public class SurfaceTextureRenderer {
|
|||||||
EGL14.EGL_NONE
|
EGL14.EGL_NONE
|
||||||
};
|
};
|
||||||
for (EGLSurfaceHolder holder : surfaces) {
|
for (EGLSurfaceHolder holder : surfaces) {
|
||||||
try {
|
holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs,
|
||||||
Size size = LegacyCameraDevice.getSurfaceSize(holder.surface);
|
holder.surface, surfaceAttribs, /*offset*/ 0);
|
||||||
holder.width = size.getWidth();
|
checkEglError("eglCreateWindowSurface");
|
||||||
holder.height = size.getHeight();
|
|
||||||
holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs,
|
|
||||||
holder.surface, surfaceAttribs, /*offset*/ 0);
|
|
||||||
checkEglError("eglCreateWindowSurface");
|
|
||||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
|
||||||
Log.w(TAG, "Surface abandoned, skipping...", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,24 +410,17 @@ public class SurfaceTextureRenderer {
|
|||||||
|
|
||||||
int maxLength = 0;
|
int maxLength = 0;
|
||||||
for (EGLSurfaceHolder holder : surfaces) {
|
for (EGLSurfaceHolder holder : surfaces) {
|
||||||
try {
|
int length = holder.width * holder.height;
|
||||||
Size size = LegacyCameraDevice.getSurfaceSize(holder.surface);
|
// Find max surface size, ensure PBuffer can hold this many pixels
|
||||||
int length = size.getWidth() * size.getHeight();
|
maxLength = (length > maxLength) ? length : maxLength;
|
||||||
// Find max surface size, ensure PBuffer can hold this many pixels
|
int[] surfaceAttribs = {
|
||||||
maxLength = (length > maxLength) ? length : maxLength;
|
EGL14.EGL_WIDTH, holder.width,
|
||||||
int[] surfaceAttribs = {
|
EGL14.EGL_HEIGHT, holder.height,
|
||||||
EGL14.EGL_WIDTH, size.getWidth(),
|
EGL14.EGL_NONE
|
||||||
EGL14.EGL_HEIGHT, size.getHeight(),
|
};
|
||||||
EGL14.EGL_NONE
|
holder.eglSurface =
|
||||||
};
|
EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0);
|
||||||
holder.width = size.getWidth();
|
checkEglError("eglCreatePbufferSurface");
|
||||||
holder.height = size.getHeight();
|
|
||||||
holder.eglSurface =
|
|
||||||
EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0);
|
|
||||||
checkEglError("eglCreatePbufferSurface");
|
|
||||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
|
||||||
Log.w(TAG, "Surface abandoned, skipping...", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mPBufferPixels = ByteBuffer.allocateDirect(maxLength * PBUFFER_PIXEL_BYTES)
|
mPBufferPixels = ByteBuffer.allocateDirect(maxLength * PBUFFER_PIXEL_BYTES)
|
||||||
.order(ByteOrder.nativeOrder());
|
.order(ByteOrder.nativeOrder());
|
||||||
@@ -569,7 +555,7 @@ public class SurfaceTextureRenderer {
|
|||||||
*
|
*
|
||||||
* @param surfaces a {@link Collection} of surfaces.
|
* @param surfaces a {@link Collection} of surfaces.
|
||||||
*/
|
*/
|
||||||
public void configureSurfaces(Collection<Surface> surfaces) {
|
public void configureSurfaces(Collection<Pair<Surface, Size>> surfaces) {
|
||||||
releaseEGLContext();
|
releaseEGLContext();
|
||||||
|
|
||||||
if (surfaces == null || surfaces.size() == 0) {
|
if (surfaces == null || surfaces.size() == 0) {
|
||||||
@@ -577,18 +563,20 @@ public class SurfaceTextureRenderer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Surface s : surfaces) {
|
for (Pair<Surface, Size> p : surfaces) {
|
||||||
|
Surface s = p.first;
|
||||||
|
Size surfaceSize = p.second;
|
||||||
// If pixel conversions aren't handled by egl, use a pbuffer
|
// If pixel conversions aren't handled by egl, use a pbuffer
|
||||||
try {
|
try {
|
||||||
|
EGLSurfaceHolder holder = new EGLSurfaceHolder();
|
||||||
|
holder.surface = s;
|
||||||
|
holder.width = surfaceSize.getWidth();
|
||||||
|
holder.height = surfaceSize.getHeight();
|
||||||
if (LegacyCameraDevice.needsConversion(s)) {
|
if (LegacyCameraDevice.needsConversion(s)) {
|
||||||
// Always override to YV12 output for YUV surface formats.
|
// Always override to YV12 output for YUV surface formats.
|
||||||
LegacyCameraDevice.setSurfaceFormat(s, ImageFormat.YV12);
|
LegacyCameraDevice.setSurfaceFormat(s, ImageFormat.YV12);
|
||||||
EGLSurfaceHolder holder = new EGLSurfaceHolder();
|
|
||||||
holder.surface = s;
|
|
||||||
mConversionSurfaces.add(holder);
|
mConversionSurfaces.add(holder);
|
||||||
} else {
|
} else {
|
||||||
EGLSurfaceHolder holder = new EGLSurfaceHolder();
|
|
||||||
holder.surface = s;
|
|
||||||
mSurfaces.add(holder);
|
mSurfaces.add(holder);
|
||||||
}
|
}
|
||||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||||
@@ -672,10 +660,11 @@ public class SurfaceTextureRenderer {
|
|||||||
List<Long> targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
|
List<Long> targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
|
||||||
for (EGLSurfaceHolder holder : mSurfaces) {
|
for (EGLSurfaceHolder holder : mSurfaces) {
|
||||||
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
|
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
|
||||||
makeCurrent(holder.eglSurface);
|
try{
|
||||||
try {
|
|
||||||
LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
|
LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
|
||||||
holder.height);
|
holder.height);
|
||||||
|
makeCurrent(holder.eglSurface);
|
||||||
|
|
||||||
LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
|
LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
|
||||||
drawFrame(mSurfaceTexture, holder.width, holder.height);
|
drawFrame(mSurfaceTexture, holder.width, holder.height);
|
||||||
swapBuffers(holder.eglSurface);
|
swapBuffers(holder.eglSurface);
|
||||||
@@ -695,10 +684,11 @@ public class SurfaceTextureRenderer {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
int format = LegacyCameraDevice.detectSurfaceType(holder.surface);
|
int format = LegacyCameraDevice.detectSurfaceType(holder.surface);
|
||||||
|
LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
|
||||||
|
holder.height);
|
||||||
LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
|
LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
|
||||||
LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(),
|
LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(),
|
||||||
holder.width, holder.height, format);
|
holder.width, holder.height, format);
|
||||||
swapBuffers(holder.eglSurface);
|
|
||||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||||
Log.w(TAG, "Surface abandoned, dropping frame. ", e);
|
Log.w(TAG, "Surface abandoned, dropping frame. ", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,6 +470,26 @@ static jint LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject th
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jint LegacyCameraDevice_nativeDetectSurfaceUsageFlags(JNIEnv* env, jobject thiz,
|
||||||
|
jobject surface) {
|
||||||
|
ALOGV("nativeDetectSurfaceUsageFlags");
|
||||||
|
|
||||||
|
sp<ANativeWindow> anw;
|
||||||
|
if ((anw = getNativeWindow(env, surface)) == NULL) {
|
||||||
|
jniThrowException(env, "Ljava/lang/UnsupportedOperationException;",
|
||||||
|
"Could not retrieve native window from surface.");
|
||||||
|
return BAD_VALUE;
|
||||||
|
}
|
||||||
|
int32_t usage = 0;
|
||||||
|
status_t err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage);
|
||||||
|
if(err != NO_ERROR) {
|
||||||
|
jniThrowException(env, "Ljava/lang/UnsupportedOperationException;",
|
||||||
|
"Error while querying surface usage bits");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return usage;
|
||||||
|
}
|
||||||
|
|
||||||
static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz,
|
static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz,
|
||||||
jobject surfaceTexture, jintArray dimens) {
|
jobject surfaceTexture, jintArray dimens) {
|
||||||
ALOGV("nativeDetectTextureDimens");
|
ALOGV("nativeDetectTextureDimens");
|
||||||
@@ -713,6 +733,9 @@ static JNINativeMethod gCameraDeviceMethods[] = {
|
|||||||
{ "nativeGetJpegFooterSize",
|
{ "nativeGetJpegFooterSize",
|
||||||
"()I",
|
"()I",
|
||||||
(void *)LegacyCameraDevice_nativeGetJpegFooterSize },
|
(void *)LegacyCameraDevice_nativeGetJpegFooterSize },
|
||||||
|
{ "nativeDetectSurfaceUsageFlags",
|
||||||
|
"(Landroid/view/Surface;)I",
|
||||||
|
(void *)LegacyCameraDevice_nativeDetectSurfaceUsageFlags },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get all the required offsets in java class and register native functions
|
// Get all the required offsets in java class and register native functions
|
||||||
|
|||||||
@@ -578,7 +578,11 @@ public class ImageReader implements AutoCloseable {
|
|||||||
@Override
|
@Override
|
||||||
public int getWidth() {
|
public int getWidth() {
|
||||||
if (mIsImageValid) {
|
if (mIsImageValid) {
|
||||||
return ImageReader.this.mWidth;
|
if (mWidth == -1) {
|
||||||
|
mWidth = (getFormat() == ImageFormat.JPEG) ? ImageReader.this.getWidth() :
|
||||||
|
nativeGetWidth();
|
||||||
|
}
|
||||||
|
return mWidth;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Image is already released");
|
throw new IllegalStateException("Image is already released");
|
||||||
}
|
}
|
||||||
@@ -587,7 +591,11 @@ public class ImageReader implements AutoCloseable {
|
|||||||
@Override
|
@Override
|
||||||
public int getHeight() {
|
public int getHeight() {
|
||||||
if (mIsImageValid) {
|
if (mIsImageValid) {
|
||||||
return ImageReader.this.mHeight;
|
if (mHeight == -1) {
|
||||||
|
mHeight = (getFormat() == ImageFormat.JPEG) ? ImageReader.this.getHeight() :
|
||||||
|
nativeGetHeight();
|
||||||
|
}
|
||||||
|
return mHeight;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Image is already released");
|
throw new IllegalStateException("Image is already released");
|
||||||
}
|
}
|
||||||
@@ -721,9 +729,13 @@ public class ImageReader implements AutoCloseable {
|
|||||||
|
|
||||||
private SurfacePlane[] mPlanes;
|
private SurfacePlane[] mPlanes;
|
||||||
private boolean mIsImageValid;
|
private boolean mIsImageValid;
|
||||||
|
private int mHeight = -1;
|
||||||
|
private int mWidth = -1;
|
||||||
|
|
||||||
private synchronized native ByteBuffer nativeImageGetBuffer(int idx, int readerFormat);
|
private synchronized native ByteBuffer nativeImageGetBuffer(int idx, int readerFormat);
|
||||||
private synchronized native SurfacePlane nativeCreatePlane(int idx, int readerFormat);
|
private synchronized native SurfacePlane nativeCreatePlane(int idx, int readerFormat);
|
||||||
|
private synchronized native int nativeGetWidth();
|
||||||
|
private synchronized native int nativeGetHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized native void nativeInit(Object weakSelf, int w, int h,
|
private synchronized native void nativeInit(Object weakSelf, int w, int h,
|
||||||
|
|||||||
@@ -615,6 +615,24 @@ static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buff
|
|||||||
return rowStride;
|
return rowStride;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int Image_getBufferWidth(CpuConsumer::LockedBuffer* buffer) {
|
||||||
|
if (buffer == NULL) return -1;
|
||||||
|
|
||||||
|
if (!buffer->crop.isEmpty()) {
|
||||||
|
return buffer->crop.getWidth();
|
||||||
|
}
|
||||||
|
return buffer->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Image_getBufferHeight(CpuConsumer::LockedBuffer* buffer) {
|
||||||
|
if (buffer == NULL) return -1;
|
||||||
|
|
||||||
|
if (!buffer->crop.isEmpty()) {
|
||||||
|
return buffer->crop.getHeight();
|
||||||
|
}
|
||||||
|
return buffer->height;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
static void ImageReader_classInit(JNIEnv* env, jclass clazz)
|
static void ImageReader_classInit(JNIEnv* env, jclass clazz)
|
||||||
@@ -795,33 +813,16 @@ static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the producer buffer configurations match what ImageReader configured.
|
// Check if the producer buffer configurations match what ImageReader configured.
|
||||||
// We want to fail for the very first image because this case is too bad.
|
int outputWidth = Image_getBufferWidth(buffer);
|
||||||
int outputWidth = buffer->width;
|
int outputHeight = Image_getBufferHeight(buffer);
|
||||||
int outputHeight = buffer->height;
|
|
||||||
|
|
||||||
// Correct width/height when crop is set.
|
|
||||||
if (!buffer->crop.isEmpty()) {
|
|
||||||
outputWidth = buffer->crop.getWidth();
|
|
||||||
outputHeight = buffer->crop.getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
int imgReaderFmt = ctx->getBufferFormat();
|
int imgReaderFmt = ctx->getBufferFormat();
|
||||||
int imageReaderWidth = ctx->getBufferWidth();
|
int imageReaderWidth = ctx->getBufferWidth();
|
||||||
int imageReaderHeight = ctx->getBufferHeight();
|
int imageReaderHeight = ctx->getBufferHeight();
|
||||||
if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) && (imgReaderFmt != HAL_PIXEL_FORMAT_BLOB) &&
|
if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) && (imgReaderFmt != HAL_PIXEL_FORMAT_BLOB) &&
|
||||||
(imageReaderWidth != outputWidth || imageReaderHeight > outputHeight)) {
|
(imageReaderWidth != outputWidth || imageReaderHeight != outputHeight)) {
|
||||||
/**
|
ALOGV("%s: Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d",
|
||||||
* For video decoder, the buffer height is actually the vertical stride,
|
__FUNCTION__, outputWidth, outputHeight, imageReaderWidth, imageReaderHeight);
|
||||||
* which is always >= actual image height. For future, decoder need provide
|
|
||||||
* right crop rectangle to CpuConsumer to indicate the actual image height,
|
|
||||||
* see bug 9563986. After this bug is fixed, we can enforce the height equal
|
|
||||||
* check. Right now, only make sure buffer height is no less than ImageReader
|
|
||||||
* height.
|
|
||||||
*/
|
|
||||||
jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
|
|
||||||
"Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d",
|
|
||||||
outputWidth, outputHeight, imageReaderWidth, imageReaderHeight);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int bufFmt = buffer->format;
|
int bufFmt = buffer->format;
|
||||||
@@ -934,6 +935,19 @@ static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx, int reade
|
|||||||
return byteBuffer;
|
return byteBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jint Image_getWidth(JNIEnv* env, jobject thiz)
|
||||||
|
{
|
||||||
|
CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
|
||||||
|
return Image_getBufferWidth(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jint Image_getHeight(JNIEnv* env, jobject thiz)
|
||||||
|
{
|
||||||
|
CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
|
||||||
|
return Image_getBufferHeight(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -943,14 +957,16 @@ static JNINativeMethod gImageReaderMethods[] = {
|
|||||||
{"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init },
|
{"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init },
|
||||||
{"nativeClose", "()V", (void*)ImageReader_close },
|
{"nativeClose", "()V", (void*)ImageReader_close },
|
||||||
{"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
|
{"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
|
||||||
{"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup },
|
{"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup },
|
||||||
{"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
|
{"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
|
||||||
};
|
};
|
||||||
|
|
||||||
static JNINativeMethod gImageMethods[] = {
|
static JNINativeMethod gImageMethods[] = {
|
||||||
{"nativeImageGetBuffer", "(II)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer },
|
{"nativeImageGetBuffer", "(II)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer },
|
||||||
{"nativeCreatePlane", "(II)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
|
{"nativeCreatePlane", "(II)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
|
||||||
(void*)Image_createSurfacePlane },
|
(void*)Image_createSurfacePlane },
|
||||||
|
{"nativeGetWidth", "()I", (void*)Image_getWidth },
|
||||||
|
{"nativeGetHeight", "()I", (void*)Image_getHeight },
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_android_media_ImageReader(JNIEnv *env) {
|
int register_android_media_ImageReader(JNIEnv *env) {
|
||||||
|
|||||||
Reference in New Issue
Block a user