我在Android应用中遇到了MediaRecorder的宽高比问题。我特别关注三星Galaxy S II的问题,与常规相机相比,它的摄像机似乎放大了(这是我在手机上使用默认相机应用程序时注意到的一种行为)。
在此视频中,当我从使用相机切换到使用MediaRecorder时,您可以看到纵横比是如何拉伸的:
https://www.youtube.com/watch?v=U8vCwiNjCPU
并在下面的屏幕截图中:
相机宽高比(正确):
视频宽高比(不正确):
如何确保视频预览的宽高比正确?
这是我的代码:
CustomCamera活动:
public class CustomCamera extends SherlockActivity {
private boolean prepareVideoRecorder() {
Log.d(TAG, "in prepareVideoRecorder()");
// It is very important to unlock the camera before doing setCamera
// or it will results in a black preview
if (camera == null)
{
camera = getCameraInstance();
}
if (recorder == null){
recorder = new MediaRecorder();
}
//Have to stop preview before starting to record
camera.stopPreview();
// Step 1: Unlock and set camera to MediaRecorder
camera.unlock();
recorder.setCamera(camera);
// Step 2: Set sources
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
recorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
// Step 4: Set output file
recorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).getAbsolutePath());
// No limit. Don't forget to check the space on disk.
recorder.setMaxDuration(50000);
recorder.setVideoFrameRate(30);
recorder.setVideoEncodingBitRate(3000000);
recorder.setAudioEncodingBitRate(8000);
// Step 5: Set the preview output
recorder.setPreviewDisplay(cameraPreview.getHolder().getSurface());
//Setting the camera's orientation
int degree = 0;
// do not rotate image, just put rotation info in
switch (mOrientation) {
case ORIENTATION_LANDSCAPE_INVERTED:
degree = 180;
break;
case ORIENTATION_PORTRAIT_NORMAL:
degree = 90;
break;
case ORIENTATION_LANDSCAPE_NORMAL:
degree = 0;
break;
case ORIENTATION_PORTRAIT_INVERTED:
degree = 270;
break;
}
recorder.setOrientationHint(degree);
// Step 6: Prepare configured MediaRecorder
try {
recorder.prepare();
} catch (IllegalStateException e) {
// This is thrown if the previous calls are not called with the
// proper order
e.printStackTrace();
releaseMediaRecorder();
return false;
} catch (IOException e) {
releaseMediaRecorder();
e.printStackTrace();
return false;
}
//Everything went successfully
return true;
}
}
/**
* Method used to set the camera preview's parameters to match the
* phone's width and set the height accordingly to assure that there are
* no aspect ratio issues.
*/
private void setHolderParameters() {
Log.d(TAG, "setting camera layout parameters");
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
int width = metrics.widthPixels;
Size mPreviewSize = CameraPreview.getOptimalPreviewSize(camera.getParameters().getSupportedPreviewSizes(), width, height);
double ratio = ((double)mPreviewSize.width)/mPreviewSize.height;
FrameLayout.LayoutParams previewParams = new FrameLayout.LayoutParams(width, (int)(width*ratio));
cameraPreview.setLayoutParams(previewParams);
}
/**
* Open the camera asynchronously to reduce the lag when opening
* activity
*/
public void openCameraAsync(){
new AsyncTask<Object, Object, Object>(){
@Override
protected Object doInBackground(Object... arg0) {
if (!isFinishing()){
//Resuming camera and display when resuming
if(camera == null){
Log.d(TAG, "Resuming with a null camera");
camera = getCameraInstance();
}
}
return null;
}
@Override
protected void onPostExecute(Object result){
setHolderParameters();
cameraPreview.setCamera(camera);
//Calling surface created so that the preview of the camera is correct
cameraPreview.surfaceCreated(cameraPreview.getHolder());
}
}.execute();
}
CameraPreview.java:
public static Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
Log.d(TAG, "getOptimalPreviewSize");
final double ASPECT_TOLERANCE = 0.05;
double targetRatio = (double) w/h;
if (sizes==null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Find size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
Log.d(TAG, "targetRatio: " + targetRatio);
Log.d(TAG, "optimalSize: " + optimalSize);
return optimalSize;
}
public void setRecorder(MediaRecorder recorder){
this.recorder = recorder;
}
答案 0 :(得分:3)
是否设置了Camera.Parameters?您需要使用setPreviewSize(int width, int height)并将其设置为视频的宽度和高度。
在MediaRecorder中,您可能还需要使用setVideoSize(int,int)并(再次)设置视频大小。
我遇到了同样的问题,为了获得正确的视频宽高比,布局大小,相机预览尺寸和MediaRecorder尺寸应该具有相同的宽高比。当其中一个关闭时,通常会发生错误。
答案 1 :(得分:0)
在某些手机上,测量会返回与DisplayMatrics提供的不同的宽度和高度,这就是您按下记录时缩小或加宽图片的原因。
特别是这些值:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measuredWidth = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
measuredHeight = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(measuredWidth, measuredHeight);
//setMeasuredDimension(mPreviewSize.height, mPreviewSize.width);
}
}
表面视图中的可能与以下内容不同:
public static Camera.Size getDeviceSpecificOptimalPreviewSize(Context context, Camera camera, int w, int h) {
List<Camera.Size> mSupportedPreviewSizes = camera.getParameters().getSupportedPreviewSizes();
if (mSupportedPreviewSizes != null) {
final double ASPECT_TOLERANCE = 0.1;
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
double targetRatio = (double) height / width;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
for (Camera.Size size : mSupportedPreviewSizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - h) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - h);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : mSupportedPreviewSizes) {
if (Math.abs(size.height - h) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - h);
}
}
}
return optimalSize;
}
这些返回Camera.Size对象的值。 mPreviewSize在我的情况下。您还需要在onSurfaceCreated和SurfaceView类的onSurfaceChanged方法中的相机对象上设置它们。
设置值时要小心,因为某些手机上的onMeasure会以相反的顺序返回高度和宽度。