Android MediaRecorder宽高比不正确

时间:2014-02-28 20:32:51

标签: android video camera mediarecorder

我在Android应用中遇到了MediaRecorder的宽高比问题。我特别关注三星Galaxy S II的问题,与常规相机相比,它的摄像机似乎放大了(这是我在手机上使用默认相机应用程序时注意到的一种行为)。

在此视频中,当我从使用相机切换到使用MediaRecorder时,您可以看到纵横比是如何拉伸的:

https://www.youtube.com/watch?v=U8vCwiNjCPU

并在下面的屏幕截图中:

相机宽高比(正确):

Camera aspect ratio

视频宽高比(不正确):

Video aspect ratio

如何确保视频预览的宽高比正确?

这是我的代码:

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;
    }

2 个答案:

答案 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会以相反的顺序返回高度和宽度。