Skip to content

Commit

Permalink
Merge pull request #2 from iamMehedi/camera_fix
Browse files Browse the repository at this point in the history
Camera preview scaling fix
  • Loading branch information
iamMehedi committed Oct 21, 2016
2 parents b8e8e21 + b7c813b commit c3b3a35
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 88 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Expand Up @@ -8,7 +8,7 @@ android {
applicationId "devliving.online.mvbarcodereadersample"
minSdkVersion 10
targetSdkVersion 24
versionCode 1
versionCode 2
versionName "1.0"
}
buildTypes {
Expand Down
4 changes: 2 additions & 2 deletions mvbarcodereader/build.gradle
Expand Up @@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: "com.jfrog.bintray"

version = "0.1.0"
version = "1.0.0"

android {
compileSdkVersion 24
Expand All @@ -11,7 +11,7 @@ android {
defaultConfig {
minSdkVersion 10
targetSdkVersion 24
versionCode 1
versionCode 3
versionName version
}
buildTypes {
Expand Down
Expand Up @@ -136,24 +136,6 @@ public void onClick(View v) {
gestureDetector = new GestureDetector(getActivity(), new CaptureGestureListener());
scaleGestureDetector = new ScaleGestureDetector(getActivity(), new ScaleListener());

topLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
topLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);

if (mCameraSource == null) {

int rc = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA);
if (rc == PackageManager.PERMISSION_GRANTED) {
createCameraSource();
startCameraSource();
} else {
requestCameraPermission();
}
} else startCameraSource();
}
});

content.setOnTouchListener(this);
return content;
}
Expand All @@ -162,9 +144,15 @@ public void onGlobalLayout() {
public void onResume() {
super.onResume();

if (mCameraSource != null) {
startCameraSource();
}
if (mCameraSource == null) {
int rc = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA);
if (rc == PackageManager.PERMISSION_GRANTED) {
createCameraSource();
startCameraSource();
} else {
requestCameraPermission();
}
} else startCameraSource();
}

/**
Expand Down Expand Up @@ -344,15 +332,13 @@ else if (mGraphicOverlay.getFirstGraphic() != null && mGraphicOverlay.getFirstGr
}
}

boolean isPortrait = mPreview.isPortraitMode();
//boolean isPortrait = mPreview.isPortraitMode();

// Creates and starts the camera. Note that this uses a higher resolution in comparison
// to other detection examples to enable the barcode detector to detect small barcodes
// at long distances.
CameraSource.Builder builder = new CameraSource.Builder(getActivity().getApplicationContext(), barcodeDetector)
.setFacing(CameraSource.CAMERA_FACING_BACK)
.setRequestedPreviewSize(isPortrait ? topLayout.getHeight() : topLayout.getWidth(),
isPortrait ? topLayout.getWidth() : topLayout.getHeight())
.setRequestedFps(15.0f);

// make sure that auto focus is an available option
Expand Down
Expand Up @@ -44,6 +44,8 @@
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -129,15 +131,13 @@ public class CameraSource {
* See {@link Frame.Metadata#getRotation()}.
*/
private int mRotation;
private int mCameraId;

private Size mPreviewSize;

// These values may be requested by the caller. Due to hardware limitations, we may need to
// select close, but not exactly the same values for these.
private float mRequestedFps = 30.0f;
private int mRequestedPreviewWidth = 1024;
private int mRequestedPreviewHeight = 768;


private String mFocusMode = null;
private String mFlashMode = null;
Expand All @@ -155,6 +155,8 @@ public class CameraSource {
private Thread mProcessingThread;
private FrameProcessingRunnable mFrameProcessor;

private boolean isSafeToTakePicture = false;

/**
* Map to convert between a byte array, received from the camera, and its associated byte
* buffer. We use byte buffers internally because this is a more efficient way to call into
Expand Down Expand Up @@ -211,25 +213,6 @@ public Builder setFlashMode(@FlashMode String mode) {
return this;
}

/**
* Sets the desired width and height of the camera frames in pixels. If the exact desired
* values are not available options, the best matching available options are selected.
* Also, we try to select a preview size which corresponds to the aspect ratio of an
* associated full picture size, if applicable. Default: 1024x768.
*/
public Builder setRequestedPreviewSize(int width, int height) {
// Restrict the requested range to something within the realm of possibility. The
// choice of 1000000 is a bit arbitrary -- intended to be well beyond resolutions that
// devices can support. We bound this to avoid int overflow in the code later.
final int MAX = 1000000;
if ((width <= 0) || (width > MAX) || (height <= 0) || (height > MAX)) {
throw new IllegalArgumentException("Invalid preview size: " + width + "x" + height);
}
mCameraSource.mRequestedPreviewWidth = width;
mCameraSource.mRequestedPreviewHeight = height;
return this;
}

/**
* Sets the camera to use (either {@link #CAMERA_FACING_BACK} or
* {@link #CAMERA_FACING_FRONT}). Default: back facing.
Expand Down Expand Up @@ -353,6 +336,7 @@ public CameraSource start() throws IOException {
mCamera.setPreviewDisplay(mDummySurfaceView.getHolder());
}
mCamera.startPreview();
isSafeToTakePicture = true;

mProcessingThread = new Thread(mFrameProcessor);
mFrameProcessor.setActive(true);
Expand All @@ -378,6 +362,7 @@ public CameraSource start(SurfaceHolder surfaceHolder) throws IOException {
mCamera = createCamera();
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
isSafeToTakePicture = true;

mProcessingThread = new Thread(mFrameProcessor);
mFrameProcessor.setActive(true);
Expand Down Expand Up @@ -412,6 +397,7 @@ public void stop() {

// clear the buffer to prevent oom exceptions
mBytesToByteBuffer.clear();
isSafeToTakePicture = false;

if (mCamera != null) {
mCamera.stopPreview();
Expand Down Expand Up @@ -496,7 +482,8 @@ public int doZoom(float scale) {
*/
public void takePicture(ShutterCallback shutter, PictureCallback jpeg) {
synchronized (mCameraLock) {
if (mCamera != null) {
if (mCamera != null && isSafeToTakePicture) {
isSafeToTakePicture = false;
PictureStartCallback startCallback = new PictureStartCallback();
startCallback.mDelegate = shutter;
PictureDoneCallback doneCallback = new PictureDoneCallback();
Expand Down Expand Up @@ -701,6 +688,7 @@ public void onPictureTaken(byte[] data, Camera camera) {
synchronized (mCameraLock) {
if (mCamera != null) {
mCamera.startPreview();
isSafeToTakePicture = true;
}
}
}
Expand Down Expand Up @@ -742,13 +730,13 @@ public void onAutoFocusMoving(boolean start, Camera camera) {
*/
@SuppressLint("InlinedApi")
private Camera createCamera() {
int requestedCameraId = getIdForRequestedCamera(mFacing);
if (requestedCameraId == -1) {
mCameraId = getIdForRequestedCamera(mFacing);
if (mCameraId == -1) {
throw new RuntimeException("Could not find requested camera.");
}
Camera camera = Camera.open(requestedCameraId);
Camera camera = Camera.open(mCameraId);

SizePair sizePair = selectSizePair(camera, mRequestedPreviewWidth, mRequestedPreviewHeight);
SizePair sizePair = selectSizePair(camera);
if (sizePair == null) {
throw new RuntimeException("Could not find suitable preview size.");
}
Expand All @@ -772,7 +760,7 @@ private Camera createCamera() {
previewFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
parameters.setPreviewFormat(ImageFormat.NV21);

setRotation(camera, parameters, requestedCameraId);
setRotation(camera, parameters, mCameraId);

if (mFocusMode != null) {
if (parameters.getSupportedFocusModes().contains(
Expand Down Expand Up @@ -842,29 +830,22 @@ private static int getIdForRequestedCamera(int facing) {
* image.
*
* @param camera the camera to select a preview size from
* @param desiredWidth the desired width of the camera preview frames
* @param desiredHeight the desired height of the camera preview frames
* @return the selected preview and picture size pair
*/
private static SizePair selectSizePair(Camera camera, int desiredWidth, int desiredHeight) {
private static SizePair selectSizePair(Camera camera) {
List<SizePair> validPreviewSizes = generateValidPreviewSizeList(camera);

// The method for selecting the best size is to minimize the sum of the differences between
// the desired values and the actual values for width and height. This is certainly not the
// only way to select the best size, but it provides a decent tradeoff between using the
// closest aspect ratio vs. using the closest pixel area.
SizePair selectedPair = null;
int minDiff = Integer.MAX_VALUE;
for (SizePair sizePair : validPreviewSizes) {
Size size = sizePair.previewSize();
int diff = Math.abs(size.getWidth() - desiredWidth) +
Math.abs(size.getHeight() - desiredHeight);
if (diff < minDiff) {
selectedPair = sizePair;
minDiff = diff;
Collections.sort(validPreviewSizes, new Comparator<SizePair>() {
@Override
public int compare(SizePair lhs, SizePair rhs) {
return (rhs.previewSize().getHeight() * rhs.previewSize().getWidth())
- (lhs.previewSize().getHeight() * lhs.previewSize().getWidth());
}
}
});

SizePair selectedPair = validPreviewSizes.get(0);
Log.d(TAG, "selected preview size: w:" + selectedPair.previewSize().getWidth()
+ ", h:" + selectedPair.previewSize().getHeight());
return selectedPair;
}

Expand Down Expand Up @@ -907,18 +888,18 @@ public Size pictureSize() {
*/
private static List<SizePair> generateValidPreviewSizeList(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
List<Camera.Size> supportedPreviewSizes =
List<android.hardware.Camera.Size> supportedPreviewSizes =
parameters.getSupportedPreviewSizes();
List<Camera.Size> supportedPictureSizes =
List<android.hardware.Camera.Size> supportedPictureSizes =
parameters.getSupportedPictureSizes();
List<SizePair> validPreviewSizes = new ArrayList<>();
for (Camera.Size previewSize : supportedPreviewSizes) {
for (android.hardware.Camera.Size previewSize : supportedPreviewSizes) {
float previewAspectRatio = (float) previewSize.width / (float) previewSize.height;

// By looping through the picture sizes in order, we favor the higher resolutions.
// We choose the highest resolution in order to support taking the full resolution
// picture later.
for (Camera.Size pictureSize : supportedPictureSizes) {
for (android.hardware.Camera.Size pictureSize : supportedPictureSizes) {
float pictureAspectRatio = (float) pictureSize.width / (float) pictureSize.height;
if (Math.abs(previewAspectRatio - pictureAspectRatio) < ASPECT_RATIO_TOLERANCE) {
validPreviewSizes.add(new SizePair(previewSize, pictureSize));
Expand All @@ -932,7 +913,7 @@ private static List<SizePair> generateValidPreviewSizeList(Camera camera) {
// still account for it.
if (validPreviewSizes.size() == 0) {
Log.w(TAG, "No preview sizes have a corresponding same-aspect-ratio picture size");
for (Camera.Size previewSize : supportedPreviewSizes) {
for (android.hardware.Camera.Size previewSize : supportedPreviewSizes) {
// The null picture size will let us know that we shouldn't set a picture size.
validPreviewSizes.add(new SizePair(previewSize, null));
}
Expand Down Expand Up @@ -974,6 +955,12 @@ private int[] selectPreviewFpsRange(Camera camera, float desiredPreviewFps) {
return selectedFpsRange;
}

public void updateRotation() {
if (mCamera != null) {
setRotation(mCamera, mCamera.getParameters(), mCameraId);
}
}

/**
* Calculates the correct rotation for the given camera id and sets the rotation in the
* parameters. It also sets the camera's display orientation and rotation.
Expand Down
Expand Up @@ -128,10 +128,22 @@ public void surfaceChanged(SurfaceHolder holder, int format, int width, int heig
}
}

@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);

if (mCameraSource != null) mCameraSource.updateRotation();
}


@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int width = 320;
int height = 240;
final int layoutWidth = right - left;
final int layoutHeight = bottom - top;

int width = layoutWidth;
int height = layoutHeight;

if (mCameraSource != null) {
Size size = mCameraSource.getPreviewSize();
if (size != null) {
Expand All @@ -148,19 +160,44 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
height = tmp;
}

final int layoutWidth = right - left;
final int layoutHeight = bottom - top;
final float aspectRatio = (float) width / (float) height;

// Computes height and width for potentially doing fit width.
int childWidth = layoutWidth;
int childHeight = (int) (((float) layoutWidth / (float) width) * height);
Log.d(TAG, "aspect ratio: " + aspectRatio);

// If height is too tall using fit width, does fit height instead.
if (childHeight > layoutHeight) {
int childWidth;
int childHeight;

if (layoutHeight > layoutWidth) {
//fit height
childHeight = layoutHeight;
childWidth = (int) (((float) layoutHeight / (float) height) * width);
childWidth = Math.round(childHeight * aspectRatio);
Log.d(TAG, "fit height -> cw: " + childWidth + ", ch: " + childHeight);

if (childWidth < layoutWidth) {
int diff = layoutWidth - childWidth;
childWidth = childWidth + diff;
childHeight = childHeight + Math.round(diff / aspectRatio);

Log.d(TAG, "fit height [nested block] -> cw: " + childWidth + ", ch: " + childHeight);
}
} else {
//fit width
childWidth = layoutWidth;
childHeight = Math.round(childWidth / aspectRatio);
Log.d(TAG, "fit width -> cw: " + childWidth + ", ch: " + childHeight);

if (childHeight < layoutHeight) {
int diff = layoutHeight - childHeight;
childHeight = childHeight + diff;
childWidth = childWidth + Math.round(diff * aspectRatio);

Log.d(TAG, "fit width [nested block] -> cw: " + childWidth + ", ch: " + childHeight);
}
}

Log.d(TAG, "layout size: w: " + layoutWidth + ", h: " + layoutHeight
+ " - fit size: w: " + childWidth + ", h: " + childHeight);

for (int i = 0; i < getChildCount(); ++i) {
getChildAt(i).layout(0, 0, childWidth, childHeight);
}
Expand Down
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_height="match_parent"
android:id="@+id/container">

</FrameLayout>

0 comments on commit c3b3a35

Please sign in to comment.