The aim of this library is to let you integrate camera features to your app, in case using Intent
to launch default Camera cannot fulfill your requirements, such as overlaying images to your Camera preview.
This is a fork from Google's CameraView.
Based on the original one, the following has been done (As per Version 0.9.4):
byte[]
to Bitmap
, with rotation degreesSurprising, I found that there are not much CameraView library out there.
I think it is because in most use-cases, we just need to launch the default camera by Intent
and get the returned URI.
As far as I know, unless you want to customize the layout of the "Camera Activity" (e.g. overlaying a photoframe on the preview), it is rare that you need to implement your own CameraView.
For those libraries I found out there, such as CameraView and Fotoapparat, although they are really good libraries, up to date and with a lot of revisions, I still encountered various minor problems.
For example, distorted aspect ratios in some devices, low FPS in returned preview frame, wrong orientation of returned preview frame, not using new Camera 2 API (Which produces a sound when taking pictures, less smooth zooming), insufficient documentations, etc...
Although this library does not have as much functionalities and flexibilities the above 2 libraries provided, but if what you want is simply a CameraView that is able to show a correct preview, take a correct picture in a correct orientation, then you are at the right place (I hope).
Recently I used it to create a real-time face detection app, overlaying images onto those faces and take a picture with those overlays, with correct orientations no matter how you hold the device. This might be more difficult than you imagined!
byte[]
with width, height and rotation degreesView
is responsible for this)This library only supports, and tested, in a portrait activity.
User can hold the device in any orientations he/she wants, the output image has correct orientation, but your activity configuration should never be rotated to landscape.
So, in your AndroidManifest.xml:
<application
...>
...
<activity android:name=".CameraActivity"
android:screenOrientation="portrait"/>
</application>
Add to application's build.gradle:
implementation 'com.asksira.android:cameraviewplus:0.9.5'
<com.google.android.cameraview.CameraView
android:id="@+id/camera_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:keepScreenOn="true"
android:adjustViewBounds="true"
app:autoFocus="true"
app:cameraAspectRatio="4:3"
app:facing="back"
app:flash="off"
app:maximumWidth="4000"
app:maximumPreviewWidth="1280"
app:useHighResPicture="false"
app:enableZoom="true"/>
Attribute Name | Default | Allowed Values |
---|---|---|
autoFocus | true | true / false |
cameraAspectRatio | 4:3 | String (But I suggest only 4:3 or 16:9) |
facing | back | back / front |
flash | auto | auto / on / off / redEye / torch |
maximumWidth | 0 | integers |
maximumPreviewWidth | 0 | integers |
useHighResPicture | true | See below |
enableZoom | true | true/ false |
cameraAspectRatio
is width/height in LANDSCAPE mode. (Thus width is the LONGER side)
maximumWidth
and maximumPreviewWidth
will limit the longer length of the resolution. For example, if you set aspectRatio="16:9"
and maximumWidth="2000"
, 3200*1800
will not be used, but 1920*1080
will be used.
If useHighResPicture
is set to true && your device supports it, it will override maximumWidth
and will use only super high resolution.
IMPORTANT: Please use your own ViewGroup to contain CameraView. Otherwise the preview might over-expand to out of what you may expect. I did not spend time on trying to fix this.
This library does not help you to check runtime permission!
Remember to check it by yourself before calling cameraView.start()
.
@Override
protected void onResume() {
super.onResume();
if (isStoragePermissionGranted() && isCameraPermissionGranted()) {
cameraView.start();
} else {
if (!isCameraPermissionGranted()) {
checkCameraPermission();
} else {
checkStoragePermission();
}
}
}
@Override
protected void onPause() {
cameraView.stop();
super.onPause();
}
Warm reminder: onReumse()
will be triggered again after user has granted or denied a permission.
IMPORTANT: Add these callbacks after cameraView.start(). Otherwise no effect will take place if the device has API level >= 21 but using LEGACY camera.
I have anchored 4 places where CameraView will callback your activity to let you do something. They are:
(1) When camera focus has locked (Play shutter animation or play some sounds, up to you)
cameraView.setOnFocusLockedListener(new CameraViewImpl.OnFocusLockedListener() {
@Override
public void onFocusLocked() {
playShutterAnimation();
}
});
(2) When camera has successfully taken a picture
cameraView.setOnPictureTakenListener(new CameraViewImpl.OnPictureTakenListener() {
@Override
public void onPictureTaken(Bitmap bitmap, int rotationDegrees) {
startSavingPhoto(bitmap, int rotationDegrees);
}
});
Before saving the Bitmap
into a File
, rotate it to a correct orientation first:
Matrix matrix = new Matrix();
matrix.postRotate(-rotationDegrees);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
Warm Reminder: Do this in a background thread!
(3) When camera catched an exception when trying to turn to front camera
cameraView.setOnTurnCameraFailListener(new CameraViewImpl.OnTurnCameraFailListener() {
@Override
public void onTurnCameraFail(Exception e) {
Toast.makeText(CameraActivity.this, "Switch Camera Failed. Does you device has a front camera?",
Toast.LENGTH_SHORT).show();
}
});
(4) When camera encounters any other sorts of error (An exception is returned to you)
cameraView.setOnCameraErrorListener(new CameraViewImpl.OnCameraErrorListener() {
@Override
public void onCameraError(Exception e) {
Toast.makeText(CameraActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
cameraView.takePicture();
cameraView.switchCamera();
cameraView.setPixelsPerOneZoomLevel(100) //Default value is 80
The number of pixels represents the distance (in pixels) between your fingers that need to change, in order for zoom level to increase or decrease by 1x.
(With that said, Camera1 API does not expose such precise zoom ratio. So, if the device uses Camera1 API, the zoom level difference is discrete and hard to tell the exact ratio change.)
This is useful if you need to process each frame, e.g. Perform some face detections.
cameraView.setOnFrameListener(new CameraViewImpl.OnFrameListener() {
@Override
public void onFrame(final byte[] data, final int width, final int height, int rotationDegrees) {
//Do things in another background thread!
Bitmap bitmap = Nv21Image.nv21ToBitmap(rs, data, width, height);
//Do whatever you want with this Bitmap
}
});
maximumPreviewWidth
will affect the width and height returned in onFrame()
.
As you can see, I am using Nv21Image.nv21ToBitmap()
to convert the byte[]
into a Bitmap
.
This is because the byte[]
of frames are in NV21 formatting instead of JPEG. This is a must for a high FPS; and the only format of Camera1 API.
The native BitmapFactory
cannot decode NV21 byte array.
I made use of this library (EasyRS) to do the conversion from NV21 to Bitmap
(ARGB).
For any reason, if you want to fallback to Camera1 even for devices that supports Camera2 API, Before the construction of CameraView, (i.e. before onCreate()
of the Activity
that contains CameraView
):
CameraViewConfig.isForceCamera1 = true;
(Release Notes are not avaiable before v0.9.4.)
v0.9.5
bytes[]
instead of Bitmap
. (#9)v0.9.4
In the original Google's CameraView, it uses OS's orientation (Whether the activity is portrait or landscape) to determine's the output image's orientation.
But this approach really sucks. All camera apps I know are defaulted to portrait mode (so that the activity will not be re-created when your rotate your device) to provide smoother UX.
At first (v0.7.0) I used EXIF attributes to read orientation from byte[]
. But later I found that this does not always works.
So in v0.9.0 I abandoned the old method and implemented SensorEventListener
to observe real time device orientation change. By reading the orientation value from the sensor, together with reading the camera's default orientation, a rotation degrees value is passed to you in both OnPictureTaken()
and onFrame()
. You can then rotate the bitmap by yourself using this value.
As far as I know, this happens if your device has API Level >= 21, but still using Camera1 API.
This happens if
As far as I know, you can do nothing about it.
Please let me know if you know there is a way to turn it off!
Since this is a fork of Google's CameraView, license follows the original one.