diff options
author | Shuzhen Wang <shuzhenwang@google.com> | 2019-02-06 13:30:25 -0800 |
---|---|---|
committer | Shuzhen Wang <shuzhenwang@google.com> | 2019-02-11 10:43:34 -0800 |
commit | cf2d36dc9f0c949e298f9c4116ed7634bea4f302 (patch) | |
tree | 64c4ec1837dada5e62e9a064f43dbe657fbda4e9 | |
parent | 6831f5bddffd8916cd176aaadc799866d88c0410 (diff) | |
download | pdk-cf2d36dc9f0c949e298f9c4116ed7634bea4f302.tar.gz |
TestingCamera2: Add HEIC image capture support
Test: Take a HEIC picture using the app
Bug: 124071759
Change-Id: I0965037f9cddd40bb471effe0234a2d4aca230b0
4 files changed, 112 insertions, 38 deletions
diff --git a/apps/TestingCamera2/res/layout/main.xml b/apps/TestingCamera2/res/layout/main.xml index 488265d..acc8594 100644 --- a/apps/TestingCamera2/res/layout/main.xml +++ b/apps/TestingCamera2/res/layout/main.xml @@ -66,7 +66,13 @@ android:spinnerMode="dropdown" android:prompt="@string/camera_id_spinner_prompt" /> - + <Spinner + android:id="@+id/still_format_spinner" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:spinnerMode="dropdown" + android:prompt="@string/still_format_spinner_prompt" + /> <Button android:id="@+id/info_button" android:text="@string/info_button_label" diff --git a/apps/TestingCamera2/res/values/strings.xml b/apps/TestingCamera2/res/values/strings.xml index c3bc711..71c88dc 100644 --- a/apps/TestingCamera2/res/values/strings.xml +++ b/apps/TestingCamera2/res/values/strings.xml @@ -87,7 +87,8 @@ <string name="v1_app_name">TestingCam2</string> <string name="camera_id_spinner_prompt">Camera Id</string> - <string name="info_button_label">JPEG!</string> + <string name="still_format_spinner_prompt">Still Capture Format</string> + <string name="info_button_label">Capture!</string> <string name="flush_button_label">Flush device</string> <string name="focus_lock_button_label">Focus Lock</string> <string name="focus_unlock_button_label">Focus Unlock</string> diff --git a/apps/TestingCamera2/src/com/android/testingcamera2/v1/CameraOps.java b/apps/TestingCamera2/src/com/android/testingcamera2/v1/CameraOps.java index 753fa00..486c245 100644 --- a/apps/TestingCamera2/src/com/android/testingcamera2/v1/CameraOps.java +++ b/apps/TestingCamera2/src/com/android/testingcamera2/v1/CameraOps.java @@ -31,6 +31,7 @@ import android.util.Size; import android.media.Image; import android.media.ImageReader; import android.media.MediaCodec; +import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; @@ -84,8 +85,8 @@ public class CameraOps { List<Surface> mOutputSurfaces = new ArrayList<Surface>(2); private Surface mPreviewSurface; private Surface mPreviewSurface2; - // How many JPEG buffers do we want to hold on to at once - private static final int MAX_CONCURRENT_JPEGS = 2; + // How many still capture buffers do we want to hold on to at once + private static final int MAX_CONCURRENT_STILL_CAPTURES = 2; private static final int STATUS_ERROR = 0; private static final int STATUS_UNINITIALIZED = 1; @@ -408,33 +409,65 @@ public class CameraOps { } } - public void minimalJpegCapture(final CaptureCallback listener, CaptureResultListener l, - Handler h, CameraControls cameraControl) throws ApiFailureException { + private static class SimpleImageListener implements ImageReader.OnImageAvailableListener { + private final ConditionVariable imageAvailable = new ConditionVariable(); + private final CaptureCallback mListener; + + SimpleImageListener(final CaptureCallback listener) { + mListener = listener; + } + + @Override + public void onImageAvailable(ImageReader reader) { + Image i = null; + try { + i = reader.acquireNextImage(); + mListener.onCaptureAvailable(i); + } finally { + if (i != null) { + i.close(); + } + imageAvailable.open(); + } + } + + public void waitForImageAvailable(long timeout) { + if (imageAvailable.block(timeout)) { + imageAvailable.close(); + } else { + Log.e(TAG, "wait for image available timed out after " + timeout + "ms"); + } + } + } + + public void minimalStillCapture(final CaptureCallback listener, CaptureResultListener l, + Handler h, CameraControls cameraControl, int format) throws ApiFailureException { minimalOpenCamera(); try { CameraCharacteristics properties = mCameraManager.getCameraCharacteristics(mCamera.getId()); - Size[] jpegSizes = null; + Size[] stillSizes = null; if (properties != null) { - jpegSizes = properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP). - getOutputSizes(ImageFormat.JPEG); + stillSizes = properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP). + getOutputSizes(format); } int width = 640; int height = 480; - if (jpegSizes != null && jpegSizes.length > 0) { - width = jpegSizes[0].getWidth(); - height = jpegSizes[0].getHeight(); + if (stillSizes != null && stillSizes.length > 0) { + width = stillSizes[0].getWidth(); + height = stillSizes[0].getHeight(); } if (mCaptureReader == null || mCaptureReader.getWidth() != width || - mCaptureReader.getHeight() != height) { + mCaptureReader.getHeight() != height || + mCaptureReader.getImageFormat() != format) { if (mCaptureReader != null) { mCaptureReader.close(); } mCaptureReader = ImageReader.newInstance(width, height, - ImageFormat.JPEG, MAX_CONCURRENT_JPEGS); + format, MAX_CONCURRENT_STILL_CAPTURES); } List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1); @@ -450,26 +483,15 @@ public class CameraOps { updateCaptureRequest(captureBuilder, cameraControl); - ImageReader.OnImageAvailableListener readerListener = - new ImageReader.OnImageAvailableListener() { - @Override - public void onImageAvailable(ImageReader reader) { - Image i = null; - try { - i = reader.acquireNextImage(); - listener.onCaptureAvailable(i); - } finally { - if (i != null) { - i.close(); - } - } - } - }; + SimpleImageListener readerListener = new SimpleImageListener(listener); + mCaptureReader.setOnImageAvailableListener(readerListener, h); mSession.capture(captureBuilder.build(), l, mOpsHandler); + + readerListener.waitForImageAvailable(1000L/*timeout*/); } catch (CameraAccessException e) { - throw new ApiFailureException("Error in minimal JPEG capture", e); + throw new ApiFailureException("Error in minimal still capture", e); } } diff --git a/apps/TestingCamera2/src/com/android/testingcamera2/v1/TestingCamera2.java b/apps/TestingCamera2/src/com/android/testingcamera2/v1/TestingCamera2.java index 7a4a9b5..c5e9ae5 100644 --- a/apps/TestingCamera2/src/com/android/testingcamera2/v1/TestingCamera2.java +++ b/apps/TestingCamera2/src/com/android/testingcamera2/v1/TestingCamera2.java @@ -29,6 +29,7 @@ import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.TotalCaptureResult; import android.media.Image; import android.media.MediaMuxer; @@ -73,6 +74,7 @@ public class TestingCamera2 extends Activity implements SurfaceHolder.Callback { private CameraOps mCameraOps; private String[] mAvailableCameraIds; private String mCameraId; + private int mCaptureFormat = ImageFormat.JPEG; private static final int mSeekBarMax = 100; private static final long MAX_EXPOSURE = 200000000L; // 200ms private static final long MIN_EXPOSURE = 100000L; // 100us @@ -94,7 +96,9 @@ public class TestingCamera2 extends Activity implements SurfaceHolder.Callback { private SurfaceHolder mCurrentPreviewHolder2 = null; private Spinner mCameraIdSpinner; + private Spinner mCaptureFormatSpinner; private OnItemSelectedListener mCameraIdSelectionListener; + private OnItemSelectedListener mCaptureFormatSelectionListener; private Button mInfoButton; private Button mFlushButton; private ToggleButton mFocusLockToggle; @@ -139,6 +143,7 @@ public class TestingCamera2 extends Activity implements SurfaceHolder.Callback { mStillView = (ImageView) findViewById(R.id.still_view); mCameraIdSpinner = (Spinner) findViewById(R.id.camera_id_spinner); + mCaptureFormatSpinner = (Spinner) findViewById(R.id.still_format_spinner); mInfoButton = (Button) findViewById(R.id.info_button); mInfoButton.setOnClickListener(mInfoButtonListener); mFlushButton = (Button) findViewById(R.id.flush_button); @@ -402,14 +407,14 @@ public class TestingCamera2 extends Activity implements SurfaceHolder.Callback { @Override public void run() { try { - mCameraOps.minimalJpegCapture(mCaptureCallback, mCaptureResultListener, - uiHandler, mCameraControl); + mCameraOps.minimalStillCapture(mCaptureCallback, mCaptureResultListener, + uiHandler, mCameraControl, mCaptureFormat); if (mCurrentPreviewHolder != null && mCurrentPreviewHolder2 != null) { mCameraOps.minimalPreview(mCurrentPreviewHolder, mCurrentPreviewHolder2, mCameraControl); } } catch (ApiFailureException e) { - logException("Can't take a JPEG! ", e); + logException("Can't take a still capture! ", e); } } }); @@ -448,15 +453,16 @@ public class TestingCamera2 extends Activity implements SurfaceHolder.Callback { private final CameraOps.CaptureCallback mCaptureCallback = new CameraOps.CaptureCallback() { @Override public void onCaptureAvailable(Image capture) { - if (capture.getFormat() != ImageFormat.JPEG) { + if (capture.getFormat() != ImageFormat.JPEG && + capture.getFormat() != ImageFormat.HEIC) { Log.e(TAG, "Unexpected format: " + capture.getFormat()); return; } - ByteBuffer jpegBuffer = capture.getPlanes()[0].getBuffer(); - byte[] jpegData = new byte[jpegBuffer.capacity()]; - jpegBuffer.get(jpegData); + ByteBuffer encodedBuffer = capture.getPlanes()[0].getBuffer(); + byte[] encodedData = new byte[encodedBuffer.capacity()]; + encodedBuffer.get(encodedData); - Bitmap b = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); + Bitmap b = BitmapFactory.decodeByteArray(encodedData, 0, encodedData.length); mStillView.setImageBitmap(b); } }; @@ -822,6 +828,45 @@ public class TestingCamera2 extends Activity implements SurfaceHolder.Callback { // Do nothing } }); + + // Map available still capture formats -> capture format spinner dropdown list of + // strings + StreamConfigurationMap streamConfigMap = + characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + final List<String> captureFormatList = new ArrayList<>(); + + captureFormatList.add("JPEG"); + if (streamConfigMap.isOutputSupportedFor(ImageFormat.HEIC)) { + captureFormatList.add("HEIC"); + } + + dataAdapter = new ArrayAdapter<>(TestingCamera2.this, + android.R.layout.simple_spinner_item, captureFormatList); + dataAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + mCaptureFormatSpinner.setAdapter(dataAdapter); + + /* + * Change the capture format and update preview when format spinner's selected item changes + */ + mCaptureFormatSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, + long id) { + int format = (position == 0 ? ImageFormat.JPEG : ImageFormat.HEIC); + + Log.i(TAG, "Change image capture format to " + captureFormatList.get(position) + + " " + format); + + mCaptureFormat = format; + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + // Do nothing + } + }); } }; } |