am 8acfdc7b: Merge "camera2: Hide JPEGs in RGBA gralloc buffers." into lmp-dev
* commit '8acfdc7bf6b8ee5250351723ab2eef28f2b71e51': camera2: Hide JPEGs in RGBA gralloc buffers.
This commit is contained in:
@@ -22,6 +22,7 @@ import android.util.Log;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -38,14 +39,16 @@ public class BurstHolder {
|
||||
*
|
||||
* @param requestId id of the burst request.
|
||||
* @param repeating true if this burst is repeating.
|
||||
* @param requests a {@link java.util.List} of {@link CaptureRequest}s in this burst.
|
||||
* @param requests a {@link List} of {@link CaptureRequest}s in this burst.
|
||||
* @param jpegSurfaceIds a {@link Collection} of IDs for the surfaces that have jpeg outputs.
|
||||
*/
|
||||
public BurstHolder(int requestId, boolean repeating, List<CaptureRequest> requests) {
|
||||
mRequestBuilders = new ArrayList<RequestHolder.Builder>();
|
||||
public BurstHolder(int requestId, boolean repeating, List<CaptureRequest> requests,
|
||||
Collection<Long> jpegSurfaceIds) {
|
||||
mRequestBuilders = new ArrayList<>();
|
||||
int i = 0;
|
||||
for (CaptureRequest r : requests) {
|
||||
mRequestBuilders.add(new RequestHolder.Builder(requestId, /*subsequenceId*/i,
|
||||
/*request*/r, repeating));
|
||||
/*request*/r, repeating, jpegSurfaceIds));
|
||||
++i;
|
||||
}
|
||||
mRepeating = repeating;
|
||||
|
||||
@@ -522,7 +522,7 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
return surfaceIds;
|
||||
}
|
||||
|
||||
static boolean containsSurfaceId(Surface s, List<Long> ids) {
|
||||
static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
|
||||
long id = getSurfaceId(s);
|
||||
return ids.contains(id);
|
||||
}
|
||||
|
||||
@@ -42,71 +42,6 @@ public class RequestHolder {
|
||||
private final int mNumPreviewTargets;
|
||||
private volatile boolean mFailed = false;
|
||||
|
||||
/**
|
||||
* Returns true if the given surface requires jpeg buffers.
|
||||
*
|
||||
* @param s a {@link android.view.Surface} to check.
|
||||
* @return true if the surface requires a jpeg buffer.
|
||||
*/
|
||||
public static boolean jpegType(Surface s)
|
||||
throws LegacyExceptionUtils.BufferQueueAbandonedException {
|
||||
return LegacyCameraDevice.detectSurfaceType(s) ==
|
||||
CameraMetadataNative.NATIVE_JPEG_FORMAT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given surface requires non-jpeg buffer types.
|
||||
*
|
||||
* <p>
|
||||
* "Jpeg buffer" refers to the buffers returned in the jpeg
|
||||
* {@link android.hardware.Camera.PictureCallback}. Non-jpeg buffers are created using a tee
|
||||
* of the preview stream drawn to the surface
|
||||
* set via {@link android.hardware.Camera#setPreviewDisplay(android.view.SurfaceHolder)} or
|
||||
* equivalent methods.
|
||||
* </p>
|
||||
* @param s a {@link android.view.Surface} to check.
|
||||
* @return true if the surface requires a non-jpeg buffer type.
|
||||
*/
|
||||
public static boolean previewType(Surface s)
|
||||
throws LegacyExceptionUtils.BufferQueueAbandonedException {
|
||||
return LegacyCameraDevice.detectSurfaceType(s) !=
|
||||
CameraMetadataNative.NATIVE_JPEG_FORMAT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of surfaces targeted by the request that require jpeg buffers.
|
||||
*/
|
||||
private static int numJpegTargets(CaptureRequest request) {
|
||||
int count = 0;
|
||||
for (Surface s : request.getTargets()) {
|
||||
try {
|
||||
if (jpegType(s)) {
|
||||
++count;
|
||||
}
|
||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||
Log.w(TAG, "Surface abandoned, skipping...", e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of surfaces targeted by the request that require non-jpeg buffers.
|
||||
*/
|
||||
private static int numPreviewTargets(CaptureRequest request) {
|
||||
int count = 0;
|
||||
for (Surface s : request.getTargets()) {
|
||||
try {
|
||||
if (previewType(s)) {
|
||||
++count;
|
||||
}
|
||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||
Log.w(TAG, "Surface abandoned, skipping...", e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder class for {@link RequestHolder} objects.
|
||||
*
|
||||
@@ -121,6 +56,7 @@ public class RequestHolder {
|
||||
private final boolean mRepeating;
|
||||
private final int mNumJpegTargets;
|
||||
private final int mNumPreviewTargets;
|
||||
private final Collection<Long> mJpegSurfaceIds;
|
||||
|
||||
/**
|
||||
* Construct a new {@link Builder} to generate {@link RequestHolder} objects.
|
||||
@@ -132,16 +68,80 @@ public class RequestHolder {
|
||||
* @param repeating {@code true} if the request is repeating.
|
||||
*/
|
||||
public Builder(int requestId, int subsequenceId, CaptureRequest request,
|
||||
boolean repeating) {
|
||||
boolean repeating, Collection<Long> jpegSurfaceIds) {
|
||||
checkNotNull(request, "request must not be null");
|
||||
mRequestId = requestId;
|
||||
mSubsequenceId = subsequenceId;
|
||||
mRequest = request;
|
||||
mRepeating = repeating;
|
||||
mJpegSurfaceIds = jpegSurfaceIds;
|
||||
mNumJpegTargets = numJpegTargets(mRequest);
|
||||
mNumPreviewTargets = numPreviewTargets(mRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given surface requires jpeg buffers.
|
||||
*
|
||||
* @param s a {@link android.view.Surface} to check.
|
||||
* @return true if the surface requires a jpeg buffer.
|
||||
*/
|
||||
private boolean jpegType(Surface s)
|
||||
throws LegacyExceptionUtils.BufferQueueAbandonedException {
|
||||
return LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given surface requires non-jpeg buffer types.
|
||||
*
|
||||
* <p>
|
||||
* "Jpeg buffer" refers to the buffers returned in the jpeg
|
||||
* {@link android.hardware.Camera.PictureCallback}. Non-jpeg buffers are created using a tee
|
||||
* of the preview stream drawn to the surface
|
||||
* set via {@link android.hardware.Camera#setPreviewDisplay(android.view.SurfaceHolder)} or
|
||||
* equivalent methods.
|
||||
* </p>
|
||||
* @param s a {@link android.view.Surface} to check.
|
||||
* @return true if the surface requires a non-jpeg buffer type.
|
||||
*/
|
||||
private boolean previewType(Surface s)
|
||||
throws LegacyExceptionUtils.BufferQueueAbandonedException {
|
||||
return !jpegType(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of surfaces targeted by the request that require jpeg buffers.
|
||||
*/
|
||||
private int numJpegTargets(CaptureRequest request) {
|
||||
int count = 0;
|
||||
for (Surface s : request.getTargets()) {
|
||||
try {
|
||||
if (jpegType(s)) {
|
||||
++count;
|
||||
}
|
||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||
Log.d(TAG, "Surface abandoned, skipping...", e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of surfaces targeted by the request that require non-jpeg buffers.
|
||||
*/
|
||||
private int numPreviewTargets(CaptureRequest request) {
|
||||
int count = 0;
|
||||
for (Surface s : request.getTargets()) {
|
||||
try {
|
||||
if (previewType(s)) {
|
||||
++count;
|
||||
}
|
||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||
Log.d(TAG, "Surface abandoned, skipping...", e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new {@link RequestHolder} using with parameters generated from this
|
||||
* {@link Builder}.
|
||||
|
||||
@@ -39,8 +39,11 @@ public class RequestQueue {
|
||||
private long mCurrentFrameNumber = 0;
|
||||
private long mCurrentRepeatingFrameNumber = INVALID_FRAME;
|
||||
private int mCurrentRequestId = 0;
|
||||
private final List<Long> mJpegSurfaceIds;
|
||||
|
||||
public RequestQueue() {}
|
||||
public RequestQueue(List<Long> jpegSurfaceIds) {
|
||||
mJpegSurfaceIds = jpegSurfaceIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return and remove the next burst on the queue.
|
||||
@@ -117,7 +120,7 @@ public class RequestQueue {
|
||||
public synchronized int submit(List<CaptureRequest> requests, boolean repeating,
|
||||
/*out*/LongParcelable frameNumber) {
|
||||
int requestId = mCurrentRequestId++;
|
||||
BurstHolder burst = new BurstHolder(requestId, repeating, requests);
|
||||
BurstHolder burst = new BurstHolder(requestId, repeating, requests, mJpegSurfaceIds);
|
||||
long ret = INVALID_FRAME;
|
||||
if (burst.isRepeating()) {
|
||||
Log.i(TAG, "Repeating capture request set.");
|
||||
|
||||
@@ -91,9 +91,11 @@ public class RequestThreadManager {
|
||||
private SurfaceTexture mPreviewTexture;
|
||||
private Camera.Parameters mParams;
|
||||
|
||||
private final List<Long> mJpegSurfaceIds = new ArrayList<>();
|
||||
|
||||
private Size mIntermediateBufferSize;
|
||||
|
||||
private final RequestQueue mRequestQueue = new RequestQueue();
|
||||
private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds);
|
||||
private LegacyRequest mLastRequest = null;
|
||||
private SurfaceTexture mDummyTexture;
|
||||
private Surface mDummySurface;
|
||||
@@ -102,6 +104,10 @@ public class RequestThreadManager {
|
||||
private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
|
||||
private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
|
||||
|
||||
// Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write
|
||||
// limitations for (b/17379185).
|
||||
private static final boolean USE_BLOB_FORMAT_OVERRIDE = true;
|
||||
|
||||
/**
|
||||
* Container object for Configure messages.
|
||||
*/
|
||||
@@ -197,10 +203,13 @@ public class RequestThreadManager {
|
||||
}
|
||||
for (Surface s : holder.getHolderTargets()) {
|
||||
try {
|
||||
if (RequestHolder.jpegType(s)) {
|
||||
if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
|
||||
Log.i(TAG, "Producing jpeg buffer...");
|
||||
LegacyCameraDevice.setSurfaceDimens(s, data.length +
|
||||
LegacyCameraDevice.nativeGetJpegFooterSize(), /*height*/1);
|
||||
|
||||
int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
|
||||
totalSize += ((totalSize - 1) & ~0x3) + 4; // align to next octonibble
|
||||
|
||||
LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1);
|
||||
LegacyCameraDevice.setNextTimestamp(s, timestamp);
|
||||
LegacyCameraDevice.produceFrame(s, data, data.length, /*height*/1,
|
||||
CameraMetadataNative.NATIVE_JPEG_FORMAT);
|
||||
@@ -316,6 +325,7 @@ public class RequestThreadManager {
|
||||
mGLThreadManager.ignoreNewFrames();
|
||||
mGLThreadManager.waitUntilIdle();
|
||||
}
|
||||
resetJpegSurfaceFormats(mCallbackOutputs);
|
||||
mPreviewOutputs.clear();
|
||||
mCallbackOutputs.clear();
|
||||
mPreviewTexture = null;
|
||||
@@ -329,6 +339,12 @@ public class RequestThreadManager {
|
||||
LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
|
||||
switch (format) {
|
||||
case CameraMetadataNative.NATIVE_JPEG_FORMAT:
|
||||
if (USE_BLOB_FORMAT_OVERRIDE) {
|
||||
// Override to RGBA_8888 format.
|
||||
LegacyCameraDevice.setSurfaceFormat(s,
|
||||
LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
|
||||
}
|
||||
mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
|
||||
mCallbackOutputs.add(s);
|
||||
break;
|
||||
default:
|
||||
@@ -426,8 +442,19 @@ public class RequestThreadManager {
|
||||
}
|
||||
|
||||
mCamera.setParameters(mParams);
|
||||
// TODO: configure the JPEG surface with some arbitrary size
|
||||
// using LegacyCameraDevice.nativeConfigureSurface
|
||||
}
|
||||
|
||||
private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
|
||||
if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
|
||||
return;
|
||||
}
|
||||
for(Surface s : surfaces) {
|
||||
try {
|
||||
LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
|
||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||
Log.w(TAG, "Surface abandoned, skipping...", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,9 +486,8 @@ public class RequestThreadManager {
|
||||
List<Size> configuredJpegSizes = new ArrayList<Size>();
|
||||
for (Surface callbackSurface : callbackOutputs) {
|
||||
try {
|
||||
int format = LegacyCameraDevice.detectSurfaceType(callbackSurface);
|
||||
|
||||
if (format != CameraMetadataNative.NATIVE_JPEG_FORMAT) {
|
||||
if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
|
||||
continue; // Ignore non-JPEG callback formats
|
||||
}
|
||||
|
||||
@@ -821,6 +847,7 @@ public class RequestThreadManager {
|
||||
if (mCamera != null) {
|
||||
mCamera.release();
|
||||
}
|
||||
resetJpegSurfaceFormats(mCallbackOutputs);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unhandled message " + msg.what +
|
||||
|
||||
@@ -581,6 +581,7 @@ public class SurfaceTextureRenderer {
|
||||
// If pixel conversions aren't handled by egl, use a pbuffer
|
||||
try {
|
||||
if (LegacyCameraDevice.needsConversion(s)) {
|
||||
// Always override to YV12 output for YUV surface formats.
|
||||
LegacyCameraDevice.setSurfaceFormat(s, ImageFormat.YV12);
|
||||
EGLSurfaceHolder holder = new EGLSurfaceHolder();
|
||||
holder.surface = s;
|
||||
|
||||
Reference in New Issue
Block a user