diff --git a/build.gradle b/build.gradle index 05f2c8cb..b7dff698 100644 --- a/build.gradle +++ b/build.gradle @@ -2,10 +2,10 @@ buildscript { repositories { - jcenter() + } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:2.3.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -14,7 +14,7 @@ buildscript { allprojects { repositories { - jcenter() + flatDir{ dirs "../test-libs" } } } diff --git a/demo/src/main/java/com/google/android/cameraview/demo/MainActivity.java b/demo/src/main/java/com/google/android/cameraview/demo/MainActivity.java index 99119475..e13dfa5a 100644 --- a/demo/src/main/java/com/google/android/cameraview/demo/MainActivity.java +++ b/demo/src/main/java/com/google/android/cameraview/demo/MainActivity.java @@ -20,6 +20,8 @@ import android.app.Dialog; import android.content.DialogInterface; import android.content.pm.PackageManager; +import android.graphics.ImageFormat; +import android.media.Image; import android.os.Build; import android.os.Bundle; import android.os.Environment; @@ -235,7 +237,11 @@ private Handler getBackgroundHandler() { } private CameraView.Callback mCallback - = new CameraView.Callback() { + = new CameraView.Callback() + { + @Override + public void onPreviewFrame(CameraView cameraView, byte[] data, int width, int height, int format) { + } @Override public void onCameraOpened(CameraView cameraView) { diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml index 2acca63e..292bd192 100644 --- a/demo/src/main/res/layout/activity_main.xml +++ b/demo/src/main/res/layout/activity_main.xml @@ -29,7 +29,9 @@ android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:adjustViewBounds="true" - android:background="@android:color/black"/> + android:background="@android:color/black" + app:preferredPreviewFormat="NV21" + /> FLASH_MODES = new SparseArrayCompat<>(); + private static final String TAG = "Camera1"; static { FLASH_MODES.put(Constants.FLASH_OFF, Camera.Parameters.FLASH_MODE_OFF); @@ -70,15 +73,37 @@ class Camera1 extends CameraViewImpl { private int mFlash; private int mDisplayOrientation; + private int mPreviewFormat; - Camera1(Callback callback, PreviewImpl preview) { + Camera.PreviewCallback mPreviewCallback; + + + Camera1(final Callback callback, PreviewImpl preview) { super(callback, preview); + + mPreviewCallback = new Camera.PreviewCallback() { + @Override public void onPreviewFrame(byte[] data, Camera camera) + { + callback.onPreviewFrame( data, + mCameraParameters.getPreviewSize().width, + mCameraParameters.getPreviewSize().height, + mCameraParameters.getPreviewFormat()); + } + }; + + preview.setCallback(new PreviewImpl.Callback() { @Override public void onSurfaceChanged() { if (mCamera != null) { setUpPreview(); adjustCameraParameters(); + + mCamera.setPreviewCallback(mPreviewCallback); + } + else + { + Log.d(TAG, "Cannot set preview callback because mCamera == null"); } } }); @@ -99,16 +124,21 @@ boolean start() { @Override void stop() { if (mCamera != null) { + mCamera.setPreviewCallback(null); mCamera.stopPreview(); } + mShowingPreview = false; releaseCamera(); } + + // Suppresses Camera#setPreviewTexture @SuppressLint("NewApi") void setUpPreview() { try { + if (mPreview.getOutputClass() == SurfaceHolder.class) { final boolean needsToStopPreview = mShowingPreview && Build.VERSION.SDK_INT < 14; if (needsToStopPreview) { @@ -131,6 +161,24 @@ boolean isCameraOpened() { return mCamera != null; } + + @Override + void setPreferredPreviewFormat(int imageFormat) { + if (mPreviewFormat == imageFormat) { + return; + } + mPreviewFormat = imageFormat; + if (isCameraOpened()) { + stop(); + start(); + } + } + + @Override + public int getPreferredPreviewFormat() { + return mPreviewFormat; + } + @Override void setFacing(int facing) { if (mFacing == facing) { @@ -334,6 +382,10 @@ void adjustCameraParameters() { if (mShowingPreview) { mCamera.stopPreview(); } + + if(mCameraParameters.getSupportedPreviewFormats().contains(getPreferredPreviewFormat())) { + mCameraParameters.setPreviewFormat(getPreferredPreviewFormat()); + } mCameraParameters.setPreviewSize(size.getWidth(), size.getHeight()); mCameraParameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight()); mCameraParameters.setRotation(calcCameraRotation(mDisplayOrientation)); diff --git a/library/src/main/api21/com/google/android/cameraview/Camera2.java b/library/src/main/api21/com/google/android/cameraview/Camera2.java index 4835f89e..c279ec99 100644 --- a/library/src/main/api21/com/google/android/cameraview/Camera2.java +++ b/library/src/main/api21/com/google/android/cameraview/Camera2.java @@ -35,7 +35,9 @@ import android.util.SparseIntArray; import android.view.Surface; +import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; import java.util.Set; import java.util.SortedSet; @@ -151,10 +153,44 @@ public void onReady() { }; - private final ImageReader.OnImageAvailableListener mOnImageAvailableListener + private final ImageReader.OnImageAvailableListener mOnPreviewAvailableListener = new ImageReader.OnImageAvailableListener() { @Override + public void onImageAvailable(ImageReader reader) { + Image image = reader.acquireLatestImage(); + if (image != null) { + try { + ArrayList buffers = new ArrayList<>(); + Image.Plane[] planes = image.getPlanes(); + if(planes.length == 0) + {return;} + + //concatenate planes into a single byte array to be compatible with Camera1 + for (int i = 0; i < planes.length; i++) { + ByteBuffer buffer = planes[i].getBuffer(); + byte[] data = new byte[buffer.remaining()]; + buffer.get(data); + buffers.add(data); + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + for (int i = 0; i < buffers.size(); i++) { + outputStream.write(buffers.get(i), 0, buffers.get(i).length); + } + + mCallback.onPreviewFrame( + outputStream.toByteArray(), image.getWidth(), image.getHeight(), image.getFormat() + ); + } finally { + image.close(); + } + } + } + }; + + private final ImageReader.OnImageAvailableListener mOnImageAvailableListener + = new ImageReader.OnImageAvailableListener() { + @Override public void onImageAvailable(ImageReader reader) { try (Image image = reader.acquireNextImage()) { Image.Plane[] planes = image.getPlanes(); @@ -182,6 +218,8 @@ public void onImageAvailable(ImageReader reader) { private ImageReader mImageReader; + private ImageReader mPreviewReader; + private final SizeMap mPreviewSizes = new SizeMap(); private final SizeMap mPictureSizes = new SizeMap(); @@ -196,6 +234,9 @@ public void onImageAvailable(ImageReader reader) { private int mDisplayOrientation; + private int mPreviewFormat = ImageFormat.YUV_420_888; + private int[] mOutputFormats; + Camera2(Callback callback, PreviewImpl preview, Context context) { super(callback, preview); mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); @@ -232,6 +273,12 @@ void stop() { mImageReader.close(); mImageReader = null; } + + if(mPreviewReader != null) + { + mPreviewReader.close(); + mPreviewReader = null; + } } @Override @@ -256,6 +303,24 @@ int getFacing() { return mFacing; } + @Override + void setPreferredPreviewFormat(int imageFormat) { + if (mPreviewFormat == imageFormat) { + return; + } + + mPreviewFormat = imageFormat; + if (isCameraOpened()) { + stop(); + start(); + } + } + + @Override + public int getPreferredPreviewFormat() { + return mPreviewFormat; + } + @Override Set getSupportedAspectRatios() { return mPreviewSizes.ratios(); @@ -382,8 +447,7 @@ private boolean chooseCameraIdByFacing() { mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId); Integer level = mCameraCharacteristics.get( CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); - if (level == null || - level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) { + if (level == null ) { return false; } Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING); @@ -435,6 +499,8 @@ private void collectCameraInfo() { if (!mPreviewSizes.ratios().contains(mAspectRatio)) { mAspectRatio = mPreviewSizes.ratios().iterator().next(); } + + mOutputFormats = map.getOutputFormats(); } protected void collectPictureSizes(SizeMap sizes, StreamConfigurationMap map) { @@ -451,6 +517,29 @@ private void prepareImageReader() { mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, /* maxImages */ 2); mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null); + + + if (mPreviewReader != null) { + mPreviewReader.close(); + } + Size previewSize = mPreviewSizes.sizes(mAspectRatio).last(); + + int fmt = ImageFormat.YUV_420_888; + if (mOutputFormats != null) { + for (int format : mOutputFormats) { + if (format == getPreferredPreviewFormat()) { + fmt = format; + break; + } + } + } + + mPreviewReader = ImageReader.newInstance( previewSize.getWidth(), + previewSize.getHeight(), + fmt, + 2); + + mPreviewReader.setOnImageAvailableListener( mOnPreviewAvailableListener, null ); } /** @@ -478,9 +567,11 @@ void startCaptureSession() { mPreview.setBufferSize(previewSize.getWidth(), previewSize.getHeight()); Surface surface = mPreview.getSurface(); try { - mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); mPreviewRequestBuilder.addTarget(surface); - mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), + mPreviewRequestBuilder.addTarget(mPreviewReader.getSurface()); + + mCamera.createCaptureSession( Arrays.asList(surface, mImageReader.getSurface(), mPreviewReader.getSurface()), mSessionCallback, null); } catch (CameraAccessException e) { throw new RuntimeException("Failed to start camera session"); diff --git a/library/src/main/base/com/google/android/cameraview/CameraViewImpl.java b/library/src/main/base/com/google/android/cameraview/CameraViewImpl.java index 31dab0c4..6040b52e 100644 --- a/library/src/main/base/com/google/android/cameraview/CameraViewImpl.java +++ b/library/src/main/base/com/google/android/cameraview/CameraViewImpl.java @@ -16,6 +16,7 @@ package com.google.android.cameraview; +import android.media.Image; import android.view.View; import java.util.Set; @@ -46,6 +47,9 @@ View getView() { abstract void setFacing(int facing); + abstract void setPreferredPreviewFormat(int imageFormat); + abstract int getPreferredPreviewFormat(); + abstract int getFacing(); abstract Set getSupportedAspectRatios(); @@ -77,6 +81,7 @@ interface Callback { void onPictureTaken(byte[] data); + void onPreviewFrame(byte[] data, int width, int height, int format); } } diff --git a/library/src/main/java/com/google/android/cameraview/CameraView.java b/library/src/main/java/com/google/android/cameraview/CameraView.java index 04826d84..25863c60 100644 --- a/library/src/main/java/com/google/android/cameraview/CameraView.java +++ b/library/src/main/java/com/google/android/cameraview/CameraView.java @@ -19,6 +19,7 @@ import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.ImageFormat; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -109,6 +110,11 @@ public CameraView(Context context, AttributeSet attrs, int defStyleAttr) { R.style.Widget_CameraView); mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, false); setFacing(a.getInt(R.styleable.CameraView_facing, FACING_BACK)); + + setPreviewFormat(a.getInt(R.styleable.CameraView_preferredPreviewFormat, + Build.VERSION.SDK_INT < 21 ? ImageFormat.NV21 + : ImageFormat.YUV_420_888)); + String aspectRatio = a.getString(R.styleable.CameraView_aspectRatio); if (aspectRatio != null) { setAspectRatio(AspectRatio.parse(aspectRatio)); @@ -127,6 +133,11 @@ public void onDisplayOrientationChanged(int displayOrientation) { }; } + private void setPreviewFormat(int value) { + mImpl.setPreferredPreviewFormat(value); + } + + @NonNull private PreviewImpl createPreviewImpl(Context context) { PreviewImpl preview; @@ -259,6 +270,7 @@ public void start() { */ public void stop() { mImpl.stop(); + mCallbacks.clear(); } /** @@ -424,6 +436,11 @@ public void remove(Callback callback) { mCallbacks.remove(callback); } + public void clear() + { + mCallbacks.clear(); + } + @Override public void onCameraOpened() { if (mRequestLayoutOnOpen) { @@ -449,6 +466,13 @@ public void onPictureTaken(byte[] data) { } } + @Override + public void onPreviewFrame(byte[] data, int width, int height, int format) { + for (Callback callback : mCallbacks) { + callback.onPreviewFrame( CameraView.this, data, width, height, format); + } + } + public void reserveRequestLayoutOnOpen() { mRequestLayoutOnOpen = true; } @@ -535,6 +559,20 @@ public void onCameraClosed(CameraView cameraView) { */ public void onPictureTaken(CameraView cameraView, byte[] data) { } + + /** + * called when a new preview image is ready + * + * @param cameraView the CameraView object + * @param data the bytes of the image. If the image has multiple planes, each + * plane will be concatenated. Use the format, width, and height to + * calculate the size of each plane. + * @param width width, in pixels, of the image + * @param height height, in pixels, of the image + * @param format a value from android.media.ImageFormat indicating the type of image + */ + public void onPreviewFrame(CameraView cameraView, byte[] data, int width, int height, int format ) { + } } } diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 7ac245d8..0a18d245 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -25,6 +25,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-libs/hamcrest-core-1.3-javadoc.jar b/test-libs/hamcrest-core-1.3-javadoc.jar new file mode 100644 index 00000000..eb935e03 Binary files /dev/null and b/test-libs/hamcrest-core-1.3-javadoc.jar differ diff --git a/test-libs/hamcrest-core-1.3-sources.jar b/test-libs/hamcrest-core-1.3-sources.jar new file mode 100644 index 00000000..c3c110b4 Binary files /dev/null and b/test-libs/hamcrest-core-1.3-sources.jar differ diff --git a/test-libs/hamcrest-core-1.3.jar b/test-libs/hamcrest-core-1.3.jar new file mode 100644 index 00000000..9d5fe16e Binary files /dev/null and b/test-libs/hamcrest-core-1.3.jar differ diff --git a/test-libs/hamcrest-integration-1.3-javadoc.jar b/test-libs/hamcrest-integration-1.3-javadoc.jar new file mode 100644 index 00000000..1f7a3706 Binary files /dev/null and b/test-libs/hamcrest-integration-1.3-javadoc.jar differ diff --git a/test-libs/hamcrest-integration-1.3-sources.jar b/test-libs/hamcrest-integration-1.3-sources.jar new file mode 100644 index 00000000..29e19f57 Binary files /dev/null and b/test-libs/hamcrest-integration-1.3-sources.jar differ diff --git a/test-libs/hamcrest-integration-1.3.jar b/test-libs/hamcrest-integration-1.3.jar new file mode 100644 index 00000000..e07447a0 Binary files /dev/null and b/test-libs/hamcrest-integration-1.3.jar differ diff --git a/test-libs/hamcrest-library-1.3-javadoc.jar b/test-libs/hamcrest-library-1.3-javadoc.jar new file mode 100644 index 00000000..7b54f496 Binary files /dev/null and b/test-libs/hamcrest-library-1.3-javadoc.jar differ diff --git a/test-libs/hamcrest-library-1.3-sources.jar b/test-libs/hamcrest-library-1.3-sources.jar new file mode 100644 index 00000000..3007bf3a Binary files /dev/null and b/test-libs/hamcrest-library-1.3-sources.jar differ diff --git a/test-libs/hamcrest-library-1.3.jar b/test-libs/hamcrest-library-1.3.jar new file mode 100644 index 00000000..9eac80d7 Binary files /dev/null and b/test-libs/hamcrest-library-1.3.jar differ diff --git a/test-libs/javawriter-2.1.1-javadoc.jar b/test-libs/javawriter-2.1.1-javadoc.jar new file mode 100644 index 00000000..3072ed9e Binary files /dev/null and b/test-libs/javawriter-2.1.1-javadoc.jar differ diff --git a/test-libs/javawriter-2.1.1-sources.jar b/test-libs/javawriter-2.1.1-sources.jar new file mode 100644 index 00000000..6ee07665 Binary files /dev/null and b/test-libs/javawriter-2.1.1-sources.jar differ diff --git a/test-libs/javawriter-2.1.1.jar b/test-libs/javawriter-2.1.1.jar new file mode 100644 index 00000000..62ad1d80 Binary files /dev/null and b/test-libs/javawriter-2.1.1.jar differ diff --git a/test-libs/javax.annotation-api-1.2-javadoc.jar b/test-libs/javax.annotation-api-1.2-javadoc.jar new file mode 100644 index 00000000..84d6831d Binary files /dev/null and b/test-libs/javax.annotation-api-1.2-javadoc.jar differ diff --git a/test-libs/javax.annotation-api-1.2-sources.jar b/test-libs/javax.annotation-api-1.2-sources.jar new file mode 100644 index 00000000..d2ff519d Binary files /dev/null and b/test-libs/javax.annotation-api-1.2-sources.jar differ diff --git a/test-libs/javax.annotation-api-1.2.jar b/test-libs/javax.annotation-api-1.2.jar new file mode 100644 index 00000000..9ab39ffa Binary files /dev/null and b/test-libs/javax.annotation-api-1.2.jar differ diff --git a/test-libs/javax.inject-1-javadoc.jar b/test-libs/javax.inject-1-javadoc.jar new file mode 100644 index 00000000..69421bd9 Binary files /dev/null and b/test-libs/javax.inject-1-javadoc.jar differ diff --git a/test-libs/javax.inject-1-sources.jar b/test-libs/javax.inject-1-sources.jar new file mode 100644 index 00000000..0c980535 Binary files /dev/null and b/test-libs/javax.inject-1-sources.jar differ diff --git a/test-libs/javax.inject-1.jar b/test-libs/javax.inject-1.jar new file mode 100644 index 00000000..b2a9d0bf Binary files /dev/null and b/test-libs/javax.inject-1.jar differ diff --git a/test-libs/jsr305-2.0.1.jar b/test-libs/jsr305-2.0.1.jar new file mode 100644 index 00000000..43807b02 Binary files /dev/null and b/test-libs/jsr305-2.0.1.jar differ diff --git a/test-libs/junit-4.12-javadoc.jar b/test-libs/junit-4.12-javadoc.jar new file mode 100644 index 00000000..f7bdb82a Binary files /dev/null and b/test-libs/junit-4.12-javadoc.jar differ diff --git a/test-libs/junit-4.12-sources.jar b/test-libs/junit-4.12-sources.jar new file mode 100644 index 00000000..884f92f5 Binary files /dev/null and b/test-libs/junit-4.12-sources.jar differ diff --git a/test-libs/junit-4.12.jar b/test-libs/junit-4.12.jar new file mode 100644 index 00000000..3a7fc266 Binary files /dev/null and b/test-libs/junit-4.12.jar differ