FileDocCategorySizeDatePackage
LegacyCameraDevice.javaAPI DocAndroid 5.1 API27719Thu Mar 12 22:22:10 GMT 2015android.hardware.camera2.legacy

LegacyCameraDevice

public class LegacyCameraDevice extends Object implements AutoCloseable
This class emulates the functionality of a Camera2 device using a the old Camera class.

There are two main components that are used to implement this: - A state machine containing valid Camera2 device states ({@link CameraDeviceState}). - A message-queue based pipeline that manages an old Camera class, and executes capture and configuration requests.

Fields Summary
public static final String
DEBUG_PROP
private final String
TAG
private static final boolean
DEBUG
private final int
mCameraId
private final android.hardware.camera2.CameraCharacteristics
mStaticCharacteristics
private final android.hardware.camera2.ICameraDeviceCallbacks
mDeviceCallbacks
private final CameraDeviceState
mDeviceState
private List
mConfiguredSurfaces
private boolean
mClosed
private final android.os.ConditionVariable
mIdle
private final android.os.HandlerThread
mResultThread
private final android.os.HandlerThread
mCallbackHandlerThread
private final android.os.Handler
mCallbackHandler
private final android.os.Handler
mResultHandler
private static final int
ILLEGAL_VALUE
private static final int
GRALLOC_USAGE_RENDERSCRIPT
private static final int
GRALLOC_USAGE_SW_READ_OFTEN
private static final int
GRALLOC_USAGE_HW_TEXTURE
private static final int
GRALLOC_USAGE_HW_COMPOSER
private static final int
GRALLOC_USAGE_HW_VIDEO_ENCODER
public static final int
MAX_DIMEN_FOR_ROUNDING
private final CameraDeviceState.CameraDeviceStateListener
mStateListener
Listener for the camera device state machine. Calls the appropriate {@link ICameraDeviceCallbacks} for each state transition.
private final RequestThreadManager
mRequestThreadManager
Constructors Summary
public LegacyCameraDevice(int cameraId, android.hardware.Camera camera, android.hardware.camera2.CameraCharacteristics characteristics, android.hardware.camera2.ICameraDeviceCallbacks callbacks)
Create a new emulated camera device from a given Camera 1 API camera.

The {@link Camera} provided to this constructor must already have been successfully opened, and ownership of the provided camera is passed to this object. No further calls to the camera methods should be made following this constructor.

param
cameraId the id of the camera.
param
camera an open {@link Camera} device.
param
characteristics the static camera characteristics for this camera device
param
callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.

        mCameraId = cameraId;
        mDeviceCallbacks = callbacks;
        TAG = String.format("CameraDevice-%d-LE", mCameraId);

        mResultThread.start();
        mResultHandler = new Handler(mResultThread.getLooper());
        mCallbackHandlerThread.start();
        mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
        mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
        mStaticCharacteristics = characteristics;
        mRequestThreadManager =
                new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
        mRequestThreadManager.start();
    
Methods Summary
public longcancelRequest(int requestId)
Cancel the repeating request with the given request id.

param
requestId the request id of the request to cancel.
return
the last frame number to be returned from the HAL for the given repeating request, or {@code INVALID_FRAME} if none exists.

        return mRequestThreadManager.cancelRepeating(requestId);
    
public voidclose()

        mRequestThreadManager.quit();
        mCallbackHandlerThread.quitSafely();
        mResultThread.quitSafely();

        try {
            mCallbackHandlerThread.join();
        } catch (InterruptedException e) {
            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
                    mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
        }

        try {
            mResultThread.join();
        } catch (InterruptedException e) {
            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
                    mResultThread.getName(), mResultThread.getId()));
        }

        mClosed = true;
    
public intconfigureOutputs(java.util.List outputs)
Configure the device with a set of output surfaces.

Using empty or {@code null} {@code outputs} is the same as unconfiguring.

Every surface in {@code outputs} must be non-{@code null}.

param
outputs a list of surfaces to set.
return
an error code for this binder operation, or {@link NO_ERROR} on success.

        List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
        if (outputs != null) {
            for (Surface output : outputs) {
                if (output == null) {
                    Log.e(TAG, "configureOutputs - null outputs are not allowed");
                    return BAD_VALUE;
                }
                StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
                        get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

                // Validate surface size and format.
                try {
                    Size s = getSurfaceSize(output);
                    int surfaceType = detectSurfaceType(output);

                    boolean flexibleConsumer = isFlexibleConsumer(output);

                    Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
                    if (sizes == null) {
                        // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
                        if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
                            surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {

                            // YUV_420_888 is always present in LEGACY for all
                            // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
                            // API (i.e. {@code #getOutputSizes} works here).
                            sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
                        } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
                            sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
                        }
                    }

                    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." :
                                    ("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 not valid, %s", s.getWidth(), s.getHeight(),
                                    surfaceType, reason));
                            return BAD_VALUE;
                        }
                    } else {
                        sizedSurfaces.add(new Pair<>(output, s));
                    }
                } catch (BufferQueueAbandonedException e) {
                    Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
                    return BAD_VALUE;
                }

            }
        }

        boolean success = false;
        if (mDeviceState.setConfiguring()) {
            mRequestThreadManager.configure(sizedSurfaces);
            success = mDeviceState.setIdle();
        }

        if (success) {
            mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
        } else {
            return CameraBinderDecorator.INVALID_OPERATION;
        }
        return CameraBinderDecorator.NO_ERROR;
    
static voidconfigureSurface(android.view.Surface surface, int width, int height, int pixelFormat)

        checkNotNull(surface);
        checkArgumentPositive(width, "width must be positive.");
        checkArgumentPositive(height, "height must be positive.");

        LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height,
                pixelFormat));
    
static booleancontainsSurfaceId(android.view.Surface s, java.util.Collection ids)

        long id = getSurfaceId(s);
        return ids.contains(id);
    
public static intdetectSurfaceType(android.view.Surface surface)
Query the surface for its currently configured format

        checkNotNull(surface);
        return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
    
static intdetectSurfaceUsageFlags(android.view.Surface surface)
Query the surface for its currently configured usage flags

        checkNotNull(surface);
        return nativeDetectSurfaceUsageFlags(surface);
    
protected voidfinalize()

        try {
            close();
        } catch (CameraRuntimeException e) {
            Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
        } finally {
            super.finalize();
        }
    
static android.util.SizefindClosestSize(android.util.Size size, android.util.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;
    
static longfindEuclidDistSquare(android.util.Size a, android.util.Size b)

        long d0 = a.getWidth() - b.getWidth();
        long d1 = a.getHeight() - b.getHeight();
        return d0 * d0 + d1 * d1;
    
public longflush()
Flush any pending requests.

return
the last frame number.

        long lastFrame = mRequestThreadManager.flush();
        waitUntilIdle();
        return lastFrame;
    
private android.hardware.camera2.impl.CaptureResultExtrasgetExtrasFromRequest(RequestHolder holder)

 // maximum allowed width for rounding

        
        if (holder == null) {
            return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
                    ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
        }
        return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
                /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
                /*partialResultCount*/1);
    
static longgetSurfaceId(android.view.Surface surface)

        checkNotNull(surface);
        return nativeGetSurfaceId(surface);
    
static java.util.ListgetSurfaceIds(java.util.Collection surfaces)

        if (surfaces == null) {
            throw new NullPointerException("Null argument surfaces");
        }
        List<Long> surfaceIds = new ArrayList<>();
        for (Surface s : surfaces) {
            long id = getSurfaceId(s);
            if (id == 0) {
                throw new IllegalStateException(
                        "Configured surface had null native GraphicBufferProducer pointer!");
            }
            surfaceIds.add(id);
        }
        return surfaceIds;
    
public static android.util.SizegetSurfaceSize(android.view.Surface surface)
Query the surface for its currently configured default buffer size.

param
surface a non-{@code null} {@code Surface}
return
the width and height of the surface
throws
NullPointerException if the {@code surface} was {@code null}
throws
IllegalStateException if the {@code surface} was invalid

        checkNotNull(surface);

        int[] dimens = new int[2];
        LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));

        return new Size(dimens[0], dimens[1]);
    
static android.util.SizegetTextureSize(android.graphics.SurfaceTexture surfaceTexture)

        checkNotNull(surfaceTexture);

        int[] dimens = new int[2];
        LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
                /*out*/dimens));

        return new Size(dimens[0], dimens[1]);
    
public booleanisClosed()
Return {@code true} if the device has been closed.

        return mClosed;
    
public static booleanisFlexibleConsumer(android.view.Surface output)

        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);
        return flexibleConsumer;
    
private static native intnativeConfigureSurface(android.view.Surface surface, int width, int height, int pixelFormat)

private static native intnativeDetectSurfaceDimens(android.view.Surface surface, int[] dimens)

private static native intnativeDetectSurfaceType(android.view.Surface surface)

private static native intnativeDetectSurfaceUsageFlags(android.view.Surface surface)

private static native intnativeDetectTextureDimens(android.graphics.SurfaceTexture surfaceTexture, int[] dimens)

static native intnativeGetJpegFooterSize()

private static native longnativeGetSurfaceId(android.view.Surface surface)

private static native intnativeProduceFrame(android.view.Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)

private static native intnativeSetNextTimestamp(android.view.Surface surface, long timestamp)

private static native intnativeSetSurfaceDimens(android.view.Surface surface, int width, int height)

private static native intnativeSetSurfaceFormat(android.view.Surface surface, int pixelFormat)

private static native intnativeSetSurfaceOrientation(android.view.Surface surface, int facing, int sensorOrientation)

static booleanneedsConversion(android.view.Surface s)
Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily converted to this; YV12 and NV21 are the two currently supported formats.

param
s the surface to check.
return
{@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible format.


                                                               
          
        int nativeType = detectSurfaceType(s);
        return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
                nativeType == ImageFormat.NV21;
    
static voidproduceFrame(android.view.Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)

        checkNotNull(surface);
        checkNotNull(pixelBuffer);
        checkArgumentPositive(width, "width must be positive.");
        checkArgumentPositive(height, "height must be positive.");

        LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
                pixelFormat));
    
static voidsetNextTimestamp(android.view.Surface surface, long timestamp)

        checkNotNull(surface);
        LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
    
static voidsetSurfaceDimens(android.view.Surface surface, int width, int height)

        checkNotNull(surface);
        checkArgumentPositive(width, "width must be positive.");
        checkArgumentPositive(height, "height must be positive.");

        LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
    
static voidsetSurfaceFormat(android.view.Surface surface, int pixelFormat)

        checkNotNull(surface);

        LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
    
static voidsetSurfaceOrientation(android.view.Surface surface, int facing, int sensorOrientation)

        checkNotNull(surface);
        LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
                sensorOrientation));
    
public intsubmitRequest(android.hardware.camera2.CaptureRequest request, boolean repeating, android.hardware.camera2.utils.LongParcelable frameNumber)
Submit a single capture request.

param
request the capture request to execute.
param
repeating {@code true} if this request is repeating.
param
frameNumber an output argument that contains either the frame number of the last frame that will be returned for this request, or the frame number of the last frame that will be returned for the current repeating request if this request is set to be repeating.
return
the request id.

        ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
        requestList.add(request);
        return submitRequestList(requestList, repeating, frameNumber);
    
public intsubmitRequestList(java.util.List requestList, boolean repeating, android.hardware.camera2.utils.LongParcelable frameNumber)
Submit a burst of capture requests.

param
requestList a list of capture requests to execute.
param
repeating {@code true} if this burst is repeating.
param
frameNumber an output argument that contains either the frame number of the last frame that will be returned for this request, or the frame number of the last frame that will be returned for the current repeating request if this burst is set to be repeating.
return
the request id.

        if (requestList == null || requestList.isEmpty()) {
            Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
            return BAD_VALUE;
        }

        List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
                getSurfaceIds(mConfiguredSurfaces);

        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
        for (CaptureRequest request : requestList) {
            if (request.getTargets().isEmpty()) {
                Log.e(TAG, "submitRequestList - "
                        + "Each request must have at least one Surface target");
                return BAD_VALUE;
            }

            for (Surface surface : request.getTargets()) {
                if (surface == null) {
                    Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
                    return BAD_VALUE;
                } else if (mConfiguredSurfaces == null) {
                    Log.e(TAG, "submitRequestList - must configure " +
                            " device with valid surfaces before submitting requests");
                    return INVALID_OPERATION;
                } else if (!containsSurfaceId(surface, surfaceIds)) {
                    Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
                    return BAD_VALUE;
                }
            }
        }

        // TODO: further validation of request here
        mIdle.close();
        return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
                frameNumber);
    
public voidwaitUntilIdle()
Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.

        mIdle.block();