Merge "Camera2: Allow rendering to arbitrary surface sizes in LEGACY mode." into lmp-mr1-dev

This commit is contained in:
Ruben Brunk
2014-12-09 23:09:50 +00:00
committed by Android (Google) Code Review
7 changed files with 214 additions and 107 deletions

View File

@@ -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();

View File

@@ -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,13 +325,19 @@ public class LegacyCameraDevice implements AutoCloseable {
} }
if (!ArrayUtils.contains(sizes, s)) { if (!ArrayUtils.contains(sizes, s)) {
if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
sizedSurfaces.add(new Pair<>(output, s));
} else {
String reason = (sizes == null) ? "format is invalid." : String reason = (sizes == null) ? "format is invalid." :
("size not in valid set: " + Arrays.toString(sizes)); ("size not in valid set: " + Arrays.toString(sizes));
Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format 0x%x is" Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
+ " not valid, %s", s.getWidth(), s.getHeight(), surfaceType, "0x%x is not valid, %s", s.getWidth(), s.getHeight(),
reason)); surfaceType, reason));
return BAD_VALUE; 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);
return BAD_VALUE; return BAD_VALUE;
@@ -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();
} }

View File

@@ -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);

View File

@@ -397,16 +397,9 @@ public class SurfaceTextureRenderer {
EGL14.EGL_NONE EGL14.EGL_NONE
}; };
for (EGLSurfaceHolder holder : surfaces) { for (EGLSurfaceHolder holder : surfaces) {
try {
Size size = LegacyCameraDevice.getSurfaceSize(holder.surface);
holder.width = size.getWidth();
holder.height = size.getHeight();
holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs, holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs,
holder.surface, surfaceAttribs, /*offset*/ 0); holder.surface, surfaceAttribs, /*offset*/ 0);
checkEglError("eglCreateWindowSurface"); 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);
int length = size.getWidth() * size.getHeight();
// Find max surface size, ensure PBuffer can hold this many pixels // Find max surface size, ensure PBuffer can hold this many pixels
maxLength = (length > maxLength) ? length : maxLength; maxLength = (length > maxLength) ? length : maxLength;
int[] surfaceAttribs = { int[] surfaceAttribs = {
EGL14.EGL_WIDTH, size.getWidth(), EGL14.EGL_WIDTH, holder.width,
EGL14.EGL_HEIGHT, size.getHeight(), EGL14.EGL_HEIGHT, holder.height,
EGL14.EGL_NONE EGL14.EGL_NONE
}; };
holder.width = size.getWidth();
holder.height = size.getHeight();
holder.eglSurface = holder.eglSurface =
EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0); EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0);
checkEglError("eglCreatePbufferSurface"); 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);
} }

View File

@@ -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

View File

@@ -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,

View File

@@ -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"
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -951,6 +965,8 @@ 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) {