Reputation: 9415
I'm having issues with the aspect ratio of a MediaRecorder in my Android app. I'm having the issue specifically with the Samsung Galaxy S II, whose video camera seems to zoom in compared to the regular camera (this is a behavior I notice when using the default camera app on the phone as well).
You can see how the aspect ratio is stretched when I switch from using the camera to using the MediaRecorder in this video:
https://www.youtube.com/watch?v=U8vCwiNjCPU
and in the screenshots below:
Camera aspect ratio (correct):
Video aspect ratio (incorrect):
How can I ensure that the aspect ratio of the video preview is correct?
Here is my code:
CustomCamera activity:
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;
}
Upvotes: 2
Views: 4640
Reputation: 670
On some phones on measure returns different width and height than DisplayMatrics provide and that is why you get shrink-ed or widened picture when you press record.
specifically these values :
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measuredWidth = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
measuredHeight = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(measuredWidth, measuredHeight);
//setMeasuredDimension(mPreviewSize.height, mPreviewSize.width);
}
}
from your surface view may be different than:
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;
}
these values that return Camera.Size object. mPreviewSize in my case. You also need to set them on camera object in onSurfaceCreated and in onSurfaceChanged method in your SurfaceView class.
Be careful when setting the values because onMeasure on some phones returns height and width in reverse order.
Upvotes: 0
Reputation: 69
Are setting any Camera.Parameters? You need to use setPreviewSize(int width, int height) and set it to the width and height of your video.
In your MediaRecorder, you may also need to use setVideoSize(int,int) and (again) set to the size of your video.
I was having the same issue you are having and to get the correct aspect ratio for your video, the layout size, Camera Preview size, and MediaRecorder size should have the same aspect ratio. Errors usually occur when one of these is off.
Upvotes: 3