Skip to content

Commit c3b3a35

Browse files
authored
Merge pull request #2 from iamMehedi/camera_fix
Camera preview scaling fix
2 parents b8e8e21 + b7c813b commit c3b3a35

File tree

6 files changed

+98
-88
lines changed

6 files changed

+98
-88
lines changed

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ android {
88
applicationId "devliving.online.mvbarcodereadersample"
99
minSdkVersion 10
1010
targetSdkVersion 24
11-
versionCode 1
11+
versionCode 2
1212
versionName "1.0"
1313
}
1414
buildTypes {

mvbarcodereader/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
22
apply plugin: 'com.github.dcendents.android-maven'
33
apply plugin: "com.jfrog.bintray"
44

5-
version = "0.1.0"
5+
version = "1.0.0"
66

77
android {
88
compileSdkVersion 24
@@ -11,7 +11,7 @@ android {
1111
defaultConfig {
1212
minSdkVersion 10
1313
targetSdkVersion 24
14-
versionCode 1
14+
versionCode 3
1515
versionName version
1616
}
1717
buildTypes {

mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/BarcodeCaptureFragment.java

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -136,24 +136,6 @@ public void onClick(View v) {
136136
gestureDetector = new GestureDetector(getActivity(), new CaptureGestureListener());
137137
scaleGestureDetector = new ScaleGestureDetector(getActivity(), new ScaleListener());
138138

139-
topLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
140-
@Override
141-
public void onGlobalLayout() {
142-
topLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
143-
144-
if (mCameraSource == null) {
145-
146-
int rc = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA);
147-
if (rc == PackageManager.PERMISSION_GRANTED) {
148-
createCameraSource();
149-
startCameraSource();
150-
} else {
151-
requestCameraPermission();
152-
}
153-
} else startCameraSource();
154-
}
155-
});
156-
157139
content.setOnTouchListener(this);
158140
return content;
159141
}
@@ -162,9 +144,15 @@ public void onGlobalLayout() {
162144
public void onResume() {
163145
super.onResume();
164146

165-
if (mCameraSource != null) {
166-
startCameraSource();
167-
}
147+
if (mCameraSource == null) {
148+
int rc = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA);
149+
if (rc == PackageManager.PERMISSION_GRANTED) {
150+
createCameraSource();
151+
startCameraSource();
152+
} else {
153+
requestCameraPermission();
154+
}
155+
} else startCameraSource();
168156
}
169157

170158
/**
@@ -344,15 +332,13 @@ else if (mGraphicOverlay.getFirstGraphic() != null && mGraphicOverlay.getFirstGr
344332
}
345333
}
346334

347-
boolean isPortrait = mPreview.isPortraitMode();
335+
//boolean isPortrait = mPreview.isPortraitMode();
348336

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

358344
// make sure that auto focus is an available option

mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSource.java

Lines changed: 37 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import java.lang.annotation.RetentionPolicy;
4545
import java.nio.ByteBuffer;
4646
import java.util.ArrayList;
47+
import java.util.Collections;
48+
import java.util.Comparator;
4749
import java.util.HashMap;
4850
import java.util.List;
4951
import java.util.Map;
@@ -129,15 +131,13 @@ public class CameraSource {
129131
* See {@link Frame.Metadata#getRotation()}.
130132
*/
131133
private int mRotation;
134+
private int mCameraId;
132135

133136
private Size mPreviewSize;
134137

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

142142
private String mFocusMode = null;
143143
private String mFlashMode = null;
@@ -155,6 +155,8 @@ public class CameraSource {
155155
private Thread mProcessingThread;
156156
private FrameProcessingRunnable mFrameProcessor;
157157

158+
private boolean isSafeToTakePicture = false;
159+
158160
/**
159161
* Map to convert between a byte array, received from the camera, and its associated byte
160162
* buffer. We use byte buffers internally because this is a more efficient way to call into
@@ -211,25 +213,6 @@ public Builder setFlashMode(@FlashMode String mode) {
211213
return this;
212214
}
213215

214-
/**
215-
* Sets the desired width and height of the camera frames in pixels. If the exact desired
216-
* values are not available options, the best matching available options are selected.
217-
* Also, we try to select a preview size which corresponds to the aspect ratio of an
218-
* associated full picture size, if applicable. Default: 1024x768.
219-
*/
220-
public Builder setRequestedPreviewSize(int width, int height) {
221-
// Restrict the requested range to something within the realm of possibility. The
222-
// choice of 1000000 is a bit arbitrary -- intended to be well beyond resolutions that
223-
// devices can support. We bound this to avoid int overflow in the code later.
224-
final int MAX = 1000000;
225-
if ((width <= 0) || (width > MAX) || (height <= 0) || (height > MAX)) {
226-
throw new IllegalArgumentException("Invalid preview size: " + width + "x" + height);
227-
}
228-
mCameraSource.mRequestedPreviewWidth = width;
229-
mCameraSource.mRequestedPreviewHeight = height;
230-
return this;
231-
}
232-
233216
/**
234217
* Sets the camera to use (either {@link #CAMERA_FACING_BACK} or
235218
* {@link #CAMERA_FACING_FRONT}). Default: back facing.
@@ -353,6 +336,7 @@ public CameraSource start() throws IOException {
353336
mCamera.setPreviewDisplay(mDummySurfaceView.getHolder());
354337
}
355338
mCamera.startPreview();
339+
isSafeToTakePicture = true;
356340

357341
mProcessingThread = new Thread(mFrameProcessor);
358342
mFrameProcessor.setActive(true);
@@ -378,6 +362,7 @@ public CameraSource start(SurfaceHolder surfaceHolder) throws IOException {
378362
mCamera = createCamera();
379363
mCamera.setPreviewDisplay(surfaceHolder);
380364
mCamera.startPreview();
365+
isSafeToTakePicture = true;
381366

382367
mProcessingThread = new Thread(mFrameProcessor);
383368
mFrameProcessor.setActive(true);
@@ -412,6 +397,7 @@ public void stop() {
412397

413398
// clear the buffer to prevent oom exceptions
414399
mBytesToByteBuffer.clear();
400+
isSafeToTakePicture = false;
415401

416402
if (mCamera != null) {
417403
mCamera.stopPreview();
@@ -496,7 +482,8 @@ public int doZoom(float scale) {
496482
*/
497483
public void takePicture(ShutterCallback shutter, PictureCallback jpeg) {
498484
synchronized (mCameraLock) {
499-
if (mCamera != null) {
485+
if (mCamera != null && isSafeToTakePicture) {
486+
isSafeToTakePicture = false;
500487
PictureStartCallback startCallback = new PictureStartCallback();
501488
startCallback.mDelegate = shutter;
502489
PictureDoneCallback doneCallback = new PictureDoneCallback();
@@ -701,6 +688,7 @@ public void onPictureTaken(byte[] data, Camera camera) {
701688
synchronized (mCameraLock) {
702689
if (mCamera != null) {
703690
mCamera.startPreview();
691+
isSafeToTakePicture = true;
704692
}
705693
}
706694
}
@@ -742,13 +730,13 @@ public void onAutoFocusMoving(boolean start, Camera camera) {
742730
*/
743731
@SuppressLint("InlinedApi")
744732
private Camera createCamera() {
745-
int requestedCameraId = getIdForRequestedCamera(mFacing);
746-
if (requestedCameraId == -1) {
733+
mCameraId = getIdForRequestedCamera(mFacing);
734+
if (mCameraId == -1) {
747735
throw new RuntimeException("Could not find requested camera.");
748736
}
749-
Camera camera = Camera.open(requestedCameraId);
737+
Camera camera = Camera.open(mCameraId);
750738

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

775-
setRotation(camera, parameters, requestedCameraId);
763+
setRotation(camera, parameters, mCameraId);
776764

777765
if (mFocusMode != null) {
778766
if (parameters.getSupportedFocusModes().contains(
@@ -842,29 +830,22 @@ private static int getIdForRequestedCamera(int facing) {
842830
* image.
843831
*
844832
* @param camera the camera to select a preview size from
845-
* @param desiredWidth the desired width of the camera preview frames
846-
* @param desiredHeight the desired height of the camera preview frames
847833
* @return the selected preview and picture size pair
848834
*/
849-
private static SizePair selectSizePair(Camera camera, int desiredWidth, int desiredHeight) {
835+
private static SizePair selectSizePair(Camera camera) {
850836
List<SizePair> validPreviewSizes = generateValidPreviewSizeList(camera);
851837

852-
// The method for selecting the best size is to minimize the sum of the differences between
853-
// the desired values and the actual values for width and height. This is certainly not the
854-
// only way to select the best size, but it provides a decent tradeoff between using the
855-
// closest aspect ratio vs. using the closest pixel area.
856-
SizePair selectedPair = null;
857-
int minDiff = Integer.MAX_VALUE;
858-
for (SizePair sizePair : validPreviewSizes) {
859-
Size size = sizePair.previewSize();
860-
int diff = Math.abs(size.getWidth() - desiredWidth) +
861-
Math.abs(size.getHeight() - desiredHeight);
862-
if (diff < minDiff) {
863-
selectedPair = sizePair;
864-
minDiff = diff;
838+
Collections.sort(validPreviewSizes, new Comparator<SizePair>() {
839+
@Override
840+
public int compare(SizePair lhs, SizePair rhs) {
841+
return (rhs.previewSize().getHeight() * rhs.previewSize().getWidth())
842+
- (lhs.previewSize().getHeight() * lhs.previewSize().getWidth());
865843
}
866-
}
844+
});
867845

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

@@ -907,18 +888,18 @@ public Size pictureSize() {
907888
*/
908889
private static List<SizePair> generateValidPreviewSizeList(Camera camera) {
909890
Camera.Parameters parameters = camera.getParameters();
910-
List<Camera.Size> supportedPreviewSizes =
891+
List<android.hardware.Camera.Size> supportedPreviewSizes =
911892
parameters.getSupportedPreviewSizes();
912-
List<Camera.Size> supportedPictureSizes =
893+
List<android.hardware.Camera.Size> supportedPictureSizes =
913894
parameters.getSupportedPictureSizes();
914895
List<SizePair> validPreviewSizes = new ArrayList<>();
915-
for (Camera.Size previewSize : supportedPreviewSizes) {
896+
for (android.hardware.Camera.Size previewSize : supportedPreviewSizes) {
916897
float previewAspectRatio = (float) previewSize.width / (float) previewSize.height;
917898

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

958+
public void updateRotation() {
959+
if (mCamera != null) {
960+
setRotation(mCamera, mCamera.getParameters(), mCameraId);
961+
}
962+
}
963+
977964
/**
978965
* Calculates the correct rotation for the given camera id and sets the rotation in the
979966
* parameters. It also sets the camera's display orientation and rotation.

mvbarcodereader/src/main/java/devliving/online/mvbarcodereader/camera/CameraSourcePreview.java

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,22 @@ public void surfaceChanged(SurfaceHolder holder, int format, int width, int heig
128128
}
129129
}
130130

131+
@Override
132+
protected void onConfigurationChanged(Configuration newConfig) {
133+
super.onConfigurationChanged(newConfig);
134+
135+
if (mCameraSource != null) mCameraSource.updateRotation();
136+
}
137+
138+
131139
@Override
132140
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
133-
int width = 320;
134-
int height = 240;
141+
final int layoutWidth = right - left;
142+
final int layoutHeight = bottom - top;
143+
144+
int width = layoutWidth;
145+
int height = layoutHeight;
146+
135147
if (mCameraSource != null) {
136148
Size size = mCameraSource.getPreviewSize();
137149
if (size != null) {
@@ -148,19 +160,44 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
148160
height = tmp;
149161
}
150162

151-
final int layoutWidth = right - left;
152-
final int layoutHeight = bottom - top;
163+
final float aspectRatio = (float) width / (float) height;
153164

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

158-
// If height is too tall using fit width, does fit height instead.
159-
if (childHeight > layoutHeight) {
167+
int childWidth;
168+
int childHeight;
169+
170+
if (layoutHeight > layoutWidth) {
171+
//fit height
160172
childHeight = layoutHeight;
161-
childWidth = (int) (((float) layoutHeight / (float) height) * width);
173+
childWidth = Math.round(childHeight * aspectRatio);
174+
Log.d(TAG, "fit height -> cw: " + childWidth + ", ch: " + childHeight);
175+
176+
if (childWidth < layoutWidth) {
177+
int diff = layoutWidth - childWidth;
178+
childWidth = childWidth + diff;
179+
childHeight = childHeight + Math.round(diff / aspectRatio);
180+
181+
Log.d(TAG, "fit height [nested block] -> cw: " + childWidth + ", ch: " + childHeight);
182+
}
183+
} else {
184+
//fit width
185+
childWidth = layoutWidth;
186+
childHeight = Math.round(childWidth / aspectRatio);
187+
Log.d(TAG, "fit width -> cw: " + childWidth + ", ch: " + childHeight);
188+
189+
if (childHeight < layoutHeight) {
190+
int diff = layoutHeight - childHeight;
191+
childHeight = childHeight + diff;
192+
childWidth = childWidth + Math.round(diff * aspectRatio);
193+
194+
Log.d(TAG, "fit width [nested block] -> cw: " + childWidth + ", ch: " + childHeight);
195+
}
162196
}
163197

198+
Log.d(TAG, "layout size: w: " + layoutWidth + ", h: " + layoutHeight
199+
+ " - fit size: w: " + childWidth + ", h: " + childHeight);
200+
164201
for (int i = 0; i < getChildCount(); ++i) {
165202
getChildAt(i).layout(0, 0, childWidth, childHeight);
166203
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
33
android:layout_width="match_parent"
4-
android:layout_height="fill_parent"
4+
android:layout_height="match_parent"
55
android:id="@+id/container">
66

77
</FrameLayout>

0 commit comments

Comments
 (0)