Android 相机预览在全屏上被拉伸
Android Camera Preview is stretched on Full screen
我正在为我的 AR View 应用使用自定义相机。相机预览在全屏纵向模式下被拉伸。
我在 Whosebug 上尝试了很多解决方案,但它不适用于我的代码。
谁能告诉我如何解决这个问题?
我在下面添加了我的相机工作代码和屏幕截图,其中预览被拉伸。
这是我的自定义相机Class:
public class ARCamera extends ViewGroup implements SurfaceHolder.Callback {
private final static float Z_NEAR = 0.5f;
private final static float Z_FAR = 2000;
private final String TAG = ARCamera.class.getSimpleName();
SurfaceView surfaceView;
SurfaceHolder surfaceHolder;
Camera camera;
Activity activity;
float[] projectionMatrix = new float[16];
int cameraWidth;
int cameraHeight;
int screenWidth, screenHeight;
int rotation;
public ARCamera(Context context, SurfaceView surfaceView) {
super(context);
this.surfaceView = surfaceView;
this.activity = (Activity) context;
surfaceHolder = this.surfaceView.getHolder();
surfaceHolder.addCallback(this);
DisplayMetrics displayMetrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
rotation = windowManager.getDefaultDisplay()
.getRotation();
this.screenWidth = displayMetrics.widthPixels;
this.screenHeight = displayMetrics.heightPixels;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
public void surfaceCreated(SurfaceHolder holder) {
try {
if (camera != null) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, info);
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
camera.setDisplayOrientation((info.orientation - degrees + 360) % 360);
camera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (camera != null) {
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (camera != null) {
this.cameraWidth = width;
this.cameraHeight = height;
Camera.Parameters parameters = camera.getParameters();
try {
List<Camera.Size> supportedSizes = camera.getParameters().getSupportedPreviewSizes();
for (Camera.Size element : supportedSizes) {
element.width -= cameraWidth;
element.height -= cameraHeight;
}
Collections.sort(supportedSizes, new ResolutionOrders());
parameters.setPreviewSize(width + supportedSizes.get(supportedSizes.size() - 1).width, height + supportedSizes.get(supportedSizes.size() - 1).height);
} catch (Exception ex) {
parameters.setPreviewSize(screenWidth, screenHeight);
}
this.camera.setParameters(parameters);
this.camera.startPreview();
generateProjectionMatrix();
}
}
private void generateProjectionMatrix() {
float ratio = (float) this.screenWidth / this.screenHeight;
final int OFFSET = 0;
final float LEFT = -ratio;
final float RIGHT = ratio;
final float BOTTOM = -1;
final float TOP = 1;
Matrix.frustumM(projectionMatrix, OFFSET, LEFT, RIGHT, BOTTOM, TOP, Z_NEAR, Z_FAR);
}
public float[] getProjectionMatrix() {
return projectionMatrix;
}
class ResolutionOrders implements java.util.Comparator<Camera.Size> {
public int compare(Camera.Size left, Camera.Size right) {
return Float.compare(left.width + left.height, right.width + right.height);
}
}
}
在表面改变块中,您必须根据您的设备以编程方式选择最佳尺寸。
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
这将为您提供支持的相机总尺寸。你必须从中选择合适的尺寸。我使用了名为“chooseOptimalSize”的函数。
Size aspectRatio = new Size(matrix.widthPixels, matrix.heightPixels);
Camera.Size previewSize = chooseOptimalSize(previewSizes, PREFERRED_PREVIEW_WIDTH, PREFERRED_PREVIEW_HEIGHT, aspectRatio, this);
这是函数“chooseOptimalSize”:-
public static Camera.Size chooseOptimalSize(List<Camera.Size> choices, int width, int height, Size aspectRatio, Context c) {
// Collect the supported resolutions that are at least as big as the preview Surface
List<Size> bigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
double ratio = (double) h / w;
int loopCounter=0;
for (Camera.Size size : choices) {
int orientation = c.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
//if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=720) {
//if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=3840 ) {
//if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=5120 ) {//Retina 5K
if((size.width/16) == (size.height/9) && size.width <=7680 ) {//8K UHDTV Super Hi-Vision
Log.e(TAG, "chooseOptimalSize:"+size+"--LoopPosition---==>"+loopCounter);
return size;
}
} else {
Log.e(TAG, "chooseOptimalSize:--given--"+size);
if((size.width/16) == (size.height/9) && ((size.width <=1280)||(size.height<=1920))) {
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=4320 ) ) {//8K UHDTV Super Hi-Vision
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=2880 ) ) {//Retina 5K
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=2160 ) ) {
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=1280 ) ) {
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=4480 && size.getWidth() >=1280) ) {
Log.e(TAG, "chooseOptimalSize:"+size+"-16:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((size.width/18) == (size.height/9) && ((size.width <=3840)||(size.height<=2160))) {
Log.e(TAG, "chooseOptimalSize:"+size+"-18:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((size.width/18.5) == (size.height/9) && ((size.width <=3840)||(size.height<=2160))) {
Log.e(TAG, "chooseOptimalSize:"+size+"-18.5:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((width/19) == (height/9) && ((width <=3840)||(height<=2160))) {
/*if((size.getWidth()/19) == (size.getHeight()/9) && ((size.getWidth() <=3840)||(size.getHeight()<=2160))) {*/
Log.e(TAG, "chooseOptimalSize:"+size+"-19:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((size.width/19.5) == (size.height/9) && ((size.width<=3840)||(size.height<=2160))) {
Log.e(TAG, "chooseOptimalSize:"+size+"-19.5:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else{
Log.e(TAG, "chooseOptimalSize"+" not proper aspect resolution");
}
//2340
}
// if(screenWidth==size.getWidth()){
// Log.e(TAG1, loopCounter+".choose:width Matched:"+screenWidth+"="+size.getWidth());
// }else{
// Log.e(TAG1, loopCounter+".choose:width Not Matched:"+screenWidth+"="+size.getWidth());
// }
//
// if(screenHeight==size.getHeight()){
// Log.e(TAG1, loopCounter+".choose:height Matched:"+screenHeight+"="+size.getHeight());
// }else{
// Log.e(TAG1, loopCounter+".choose:height Not Matched:"+screenHeight+"="+size.getHeight());
// }
loopCounter++;
}
// Pick the smallest of those, assuming we found any
// if (bigEnough.size() > 0) {
// return Collections.min(bigEnough, new CompareSizesByArea());
// } else {
// Log.e(TAG, "Couldn't find any suitable preview size");
return choices.get(0);
// }
}
/*
* Compares two {@code Size}s based on their areas.
*/
static class CompareSizesByArea implements Comparator<Size> {
@Override
public int compare(Size lhs, Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
(long) rhs.getWidth() * rhs.getHeight());
}
}
如果您愿意,可以取消注释代码,在我的情况下不需要该代码。
此功能将为您提供设备相机的最佳尺寸。您可以使用 :
进行设置
parameters.setPreviewSize(previewSize.width,previewSize.height);
大功告成!!!
我正在为我的 AR View 应用使用自定义相机。相机预览在全屏纵向模式下被拉伸。
我在 Whosebug 上尝试了很多解决方案,但它不适用于我的代码。 谁能告诉我如何解决这个问题?
我在下面添加了我的相机工作代码和屏幕截图,其中预览被拉伸。
这是我的自定义相机Class:
public class ARCamera extends ViewGroup implements SurfaceHolder.Callback {
private final static float Z_NEAR = 0.5f;
private final static float Z_FAR = 2000;
private final String TAG = ARCamera.class.getSimpleName();
SurfaceView surfaceView;
SurfaceHolder surfaceHolder;
Camera camera;
Activity activity;
float[] projectionMatrix = new float[16];
int cameraWidth;
int cameraHeight;
int screenWidth, screenHeight;
int rotation;
public ARCamera(Context context, SurfaceView surfaceView) {
super(context);
this.surfaceView = surfaceView;
this.activity = (Activity) context;
surfaceHolder = this.surfaceView.getHolder();
surfaceHolder.addCallback(this);
DisplayMetrics displayMetrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
rotation = windowManager.getDefaultDisplay()
.getRotation();
this.screenWidth = displayMetrics.widthPixels;
this.screenHeight = displayMetrics.heightPixels;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
public void surfaceCreated(SurfaceHolder holder) {
try {
if (camera != null) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, info);
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
camera.setDisplayOrientation((info.orientation - degrees + 360) % 360);
camera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (camera != null) {
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (camera != null) {
this.cameraWidth = width;
this.cameraHeight = height;
Camera.Parameters parameters = camera.getParameters();
try {
List<Camera.Size> supportedSizes = camera.getParameters().getSupportedPreviewSizes();
for (Camera.Size element : supportedSizes) {
element.width -= cameraWidth;
element.height -= cameraHeight;
}
Collections.sort(supportedSizes, new ResolutionOrders());
parameters.setPreviewSize(width + supportedSizes.get(supportedSizes.size() - 1).width, height + supportedSizes.get(supportedSizes.size() - 1).height);
} catch (Exception ex) {
parameters.setPreviewSize(screenWidth, screenHeight);
}
this.camera.setParameters(parameters);
this.camera.startPreview();
generateProjectionMatrix();
}
}
private void generateProjectionMatrix() {
float ratio = (float) this.screenWidth / this.screenHeight;
final int OFFSET = 0;
final float LEFT = -ratio;
final float RIGHT = ratio;
final float BOTTOM = -1;
final float TOP = 1;
Matrix.frustumM(projectionMatrix, OFFSET, LEFT, RIGHT, BOTTOM, TOP, Z_NEAR, Z_FAR);
}
public float[] getProjectionMatrix() {
return projectionMatrix;
}
class ResolutionOrders implements java.util.Comparator<Camera.Size> {
public int compare(Camera.Size left, Camera.Size right) {
return Float.compare(left.width + left.height, right.width + right.height);
}
}
}
在表面改变块中,您必须根据您的设备以编程方式选择最佳尺寸。
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
这将为您提供支持的相机总尺寸。你必须从中选择合适的尺寸。我使用了名为“chooseOptimalSize”的函数。
Size aspectRatio = new Size(matrix.widthPixels, matrix.heightPixels);
Camera.Size previewSize = chooseOptimalSize(previewSizes, PREFERRED_PREVIEW_WIDTH, PREFERRED_PREVIEW_HEIGHT, aspectRatio, this);
这是函数“chooseOptimalSize”:-
public static Camera.Size chooseOptimalSize(List<Camera.Size> choices, int width, int height, Size aspectRatio, Context c) {
// Collect the supported resolutions that are at least as big as the preview Surface
List<Size> bigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
double ratio = (double) h / w;
int loopCounter=0;
for (Camera.Size size : choices) {
int orientation = c.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
//if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=720) {
//if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=3840 ) {
//if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=5120 ) {//Retina 5K
if((size.width/16) == (size.height/9) && size.width <=7680 ) {//8K UHDTV Super Hi-Vision
Log.e(TAG, "chooseOptimalSize:"+size+"--LoopPosition---==>"+loopCounter);
return size;
}
} else {
Log.e(TAG, "chooseOptimalSize:--given--"+size);
if((size.width/16) == (size.height/9) && ((size.width <=1280)||(size.height<=1920))) {
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=4320 ) ) {//8K UHDTV Super Hi-Vision
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=2880 ) ) {//Retina 5K
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=2160 ) ) {
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=1280 ) ) {
//if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=4480 && size.getWidth() >=1280) ) {
Log.e(TAG, "chooseOptimalSize:"+size+"-16:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((size.width/18) == (size.height/9) && ((size.width <=3840)||(size.height<=2160))) {
Log.e(TAG, "chooseOptimalSize:"+size+"-18:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((size.width/18.5) == (size.height/9) && ((size.width <=3840)||(size.height<=2160))) {
Log.e(TAG, "chooseOptimalSize:"+size+"-18.5:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((width/19) == (height/9) && ((width <=3840)||(height<=2160))) {
/*if((size.getWidth()/19) == (size.getHeight()/9) && ((size.getWidth() <=3840)||(size.getHeight()<=2160))) {*/
Log.e(TAG, "chooseOptimalSize:"+size+"-19:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((size.width/19.5) == (size.height/9) && ((size.width<=3840)||(size.height<=2160))) {
Log.e(TAG, "chooseOptimalSize:"+size+"-19.5:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else{
Log.e(TAG, "chooseOptimalSize"+" not proper aspect resolution");
}
//2340
}
// if(screenWidth==size.getWidth()){
// Log.e(TAG1, loopCounter+".choose:width Matched:"+screenWidth+"="+size.getWidth());
// }else{
// Log.e(TAG1, loopCounter+".choose:width Not Matched:"+screenWidth+"="+size.getWidth());
// }
//
// if(screenHeight==size.getHeight()){
// Log.e(TAG1, loopCounter+".choose:height Matched:"+screenHeight+"="+size.getHeight());
// }else{
// Log.e(TAG1, loopCounter+".choose:height Not Matched:"+screenHeight+"="+size.getHeight());
// }
loopCounter++;
}
// Pick the smallest of those, assuming we found any
// if (bigEnough.size() > 0) {
// return Collections.min(bigEnough, new CompareSizesByArea());
// } else {
// Log.e(TAG, "Couldn't find any suitable preview size");
return choices.get(0);
// }
}
/*
* Compares two {@code Size}s based on their areas.
*/
static class CompareSizesByArea implements Comparator<Size> {
@Override
public int compare(Size lhs, Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
(long) rhs.getWidth() * rhs.getHeight());
}
}
如果您愿意,可以取消注释代码,在我的情况下不需要该代码。
此功能将为您提供设备相机的最佳尺寸。您可以使用 :
进行设置parameters.setPreviewSize(previewSize.width,previewSize.height);
大功告成!!!