my capture picture output not same with my camera preview in landscape mode
before cpture
after capture
whats wrong ? and whats have i do. thanks
This is the AutoFitTextView class which I pulled from Google sample. You can take a look at here. It aims to show camera view and config the ratio base on the physical size of device.
public class AutoFitTextureView extends TextureView {
private int mRatioWidth = 0;
private int mRatioHeight = 0;
// Some codes here...
public void setAspectRatio(int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size cannot be negative.");
}
mRatioWidth = width;
mRatioHeight = height;
requestLayout();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
} else {
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
} else {
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
}
}
}
}
There are 2 points in this class:
You can't ensure the ratio works properly in every device. However, we are able to choose optimized size which is already defined in this class.
This condition is wrong: if (width < height * mRatioWidth / mRatioHeight). It should be > because when width is bigger than height, we calculate and set measure dimension base on width (not height).
UPDATED
If you just want every device will work properly in a particular ratio, then set hard ratio for it (for instance: 4/3)
You can achieve that by replacing those lines of code:
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
maxPreviewHeight, largest);
-> previewSize = Size(4, 3)
Related
I'm implementing Camera 2 API in my project. I'm using TextureView and these line of codes to set the camera fullscreen preview size:
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
mPreviewSize = map.getOutputSizes(SurfaceTexture.class)[0];
This seems to be the largest preview size that device support. I'm not sure if this size works with all devices and fit its device's aspect ratio without being stretched. Does anyone know?
If your Camera resolutions , texture view and your device's display dimensions are not same then you have to adjust the dimensions. For that you have to put your TextureView inside of FrameLayout. Below Code is applicable to all the devices with various Display resolutions.
Take your Display Dimetions if you are previewing in full screen.Take int DSI_height, int DSI_width global variable.
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
DSI_height = displayMetrics.heightPixels;
DSI_width = displayMetrics.widthPixels;
select your required resolutions from Camera2 API and assign to Size imageDimension, Take private Size imageDimension globally and use
setAspectRatioTextureView(imageDimension.getHeight(),imageDimension.getWidth());
and use below logic
private void setAspectRatioTextureView(int ResolutionWidth , int ResolutionHeight )
{
if(ResolutionWidth > ResolutionHeight){
int newWidth = DSI_width;
int newHeight = ((DSI_width * ResolutionWidth)/ResolutionHeight);
updateTextureViewSize(newWidth,newHeight);
}else {
int newWidth = DSI_width;
int newHeight = ((DSI_width * ResolutionHeight)/ResolutionWidth);
updateTextureViewSize(newWidth,newHeight);
}
}
private void updateTextureViewSize(int viewWidth, int viewHeight) {
Log.d(TAG, "TextureView Width : " + viewWidth + " TextureView Height : " + viewHeight);
textureView.setLayoutParams(new FrameLayout.LayoutParams(viewWidth, viewHeight));
}
There might be edge cases where that approach would fail, but I don't have a perfect answer to your question why.
In contrast, I have a proper approach on how to implement a version that will most certainly work:
Looking at the Google API demos for the Camera 2, I found some sample code that should be helpful to you to make sure it will fit all screen sized correctly:
/**
* Given {#code choices} of {#code Size}s supported by a camera, choose the smallest one that
* is at least as large as the respective texture view size, and that is at most as large as the
* respective max size, and whose aspect ratio matches with the specified value. If such size
* doesn't exist, choose the largest one that is at most as large as the respective max size,
* and whose aspect ratio matches with the specified value.
*
* #param choices The list of sizes that the camera supports for the intended output
* class
* #param textureViewWidth The width of the texture view relative to sensor coordinate
* #param textureViewHeight The height of the texture view relative to sensor coordinate
* #param maxWidth The maximum width that can be chosen
* #param maxHeight The maximum height that can be chosen
* #param aspectRatio The aspect ratio
* #return The optimal {#code Size}, or an arbitrary one if none were big enough
*/
private static Size chooseOptimalSize(Size[] choices, int textureViewWidth,
int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
// Collect the supported resolutions that are at least as big as the preview Surface
List<Size> bigEnough = new ArrayList<>();
// Collect the supported resolutions that are smaller than the preview Surface
List<Size> notBigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
for (Size option : choices) {
if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
option.getHeight() == option.getWidth() * h / w) {
if (option.getWidth() >= textureViewWidth &&
option.getHeight() >= textureViewHeight) {
bigEnough.add(option);
} else {
notBigEnough.add(option);
}
}
}
// Pick the smallest of those big enough. If there is no one big enough, pick the
// largest of those not big enough.
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
} else if (notBigEnough.size() > 0) {
return Collections.max(notBigEnough, new CompareSizesByArea());
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
Source
Also you should take a look at the whole Camera2BasicFragment.java and AutoFitTextureView.java classes for proper implementation.
I solved this problem via a different approach. I get the screen width and height and calculate how much wider or higher the preview would have to be to fill the whole screen and keep aspect ratio. It works pretty well for me without any distortions.
Add a class member variable:
public DisplayMetrics mMetrics = new DisplayMetrics();
Use the following as onMeasure:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
} else {
WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(mMetrics);
double ratio = (double)mRatioWidth / (double)mRatioHeight;
double invertedRatio = (double)mRatioHeight / (double)mRatioWidth;
double portraitHeight = width * invertedRatio;
double portraitWidth = width * (mMetrics.heightPixels / portraitHeight);
double landscapeWidth = height * ratio;
double landscapeHeight = (mMetrics.widthPixels / landscapeWidth) * height;
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension((int)portraitWidth, mMetrics.heightPixels);
} else {
setMeasuredDimension(mMetrics.widthPixels, (int)landscapeHeight);
}
}
}
Any feedback is greatly appreciated ;)
Best M
Change the AutoFitTextureView.java file and set value like below:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
} else {
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, height);
Log.d("rlijeolid1",String.valueOf(width)+"\t"+String.valueOf(height));
} else {
setMeasuredDimension(width , height);
Log.d("rlijeolid2",String.valueOf(height * mRatioWidth / mRatioHeight)+"\t"+String.valueOf(height));
}
}
}
I'm developing a camera2 app. Everything is working fine on Nexus 5 and Nexus 4, but on LG G2 preview for video is stretched (only for back camera, for front camera everything is fine). I'm setting SurfaceTexture the next way:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
float ratioSurface = width > height ? (float) width / height : (float) height / width;
float ratioPreview = (float) mRatioWidth / mRatioHeight;
int scaledHeight = height;
int scaledWidth = width;
if (ratioPreview > ratioSurface) {
scaledHeight = (int) (((float) mRatioWidth / mRatioHeight) * width);
} else if (ratioPreview < ratioSurface) {
scaledWidth = (int) (height / ((float) mRatioWidth / mRatioHeight));
}
setMeasuredDimension(scaledWidth, scaledHeight);
}
After setAspectRatio() I'm called setSurfaceTexture():
private void setSurfaceTexture(Size size) {
final Matrix matrix = new Matrix();
int width = resource.cameraTexture.getWidth();
int height = resource.cameraTexture.getHeight();
int rotation = controller.getRotation();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
matrix.postRotate(90 * (rotation - 2), width / 2, height / 2);
}
resource.mForegroundHandler.post(new Runnable() {
#Override
public void run() {
resource.cameraTexture.setTransform(matrix);
}
});
}
I was trying to use SurfaceView, but result is the same: when switches to video mode, preview is stretched. I tried many ways, but result is always the same.
Please, help me...
EDIT: Video is still stretched on LG G2 with CyanogenMod, but in phones with official Lollipop the issue was next: LEGACY devices not always support preview video 16:9, so I've changed video size to 4:3.
I'm trying this sample: https://github.com/googlesamples/android-Camera2Basic
When I change to Front Camera, the Camera is scaled. When I stop and resume the application, it can work perfect.
I'm using Nexus 7 2013 OS 5.0.2
I change to Front Camera by comment this code:
// We don't use a front facing camera in this sample.
if (characteristics.get(CameraCharacteristics.LENS_FACING)
== CameraCharacteristics.LENS_FACING_FRONT) {
// continue;
// }
I'm facing the same problem when trying to capture via front camera
My solution was to comment out some lines in AutoFitTextureView.java
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
/* } else {
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
} else {
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
}
}*/
}
So no matter what or when it'll set it's width&height again.
I have an Android app that opens the camera, starts preview and streams it on screen. Important note: there is no real SurfaceView associated with the camera. There's only a dummy SurfaceTexture:
m_previewTexture = new SurfaceTexture(58346);
camera.setPreviewTexture(m_previewTexture);
Now, I'm getting the image using the Camera.PreviewCallback. It is irrelevant what I'm doing with it further. So far I'm displaying it on the screen, but I might as well be saving it on the memory card.
Now, the problem. I set preview size to 320x240. I get the image of 320x240 size, all seems fine. But as soon as real life objects come into the frame, I can clearly see that the image is stretched.
My activity's orientation is locked, it doesn't rotate. As I rotate the device relative to a fixed object, I can very clearly see and confirm that the image is stretched. Why could this be and how to avoid stretching?
Does your screen aspect ratio correspond to your preview's frame ratio?
Assure correct aspect ratio in onMeasure:
#Override
protected void onMeasure(int widthSpec, int heightSpec) {
if (this.mAspectRatio == 0) {
super.onMeasure(widthSpec, heightSpec);
return;
}
int previewWidth = MeasureSpec.getSize(widthSpec);
int previewHeight = MeasureSpec.getSize(heightSpec);
int hPadding = getPaddingLeft() + getPaddingRight();
int vPadding = getPaddingTop() + getPaddingBottom();
previewWidth -= hPadding;
previewHeight -= vPadding;
boolean widthLonger = previewWidth > previewHeight;
int longSide = (widthLonger ? previewWidth : previewHeight);
int shortSide = (widthLonger ? previewHeight : previewWidth);
if (longSide > shortSide * mAspectRatio) {
longSide = (int) ((double) shortSide * mAspectRatio);
} else {
shortSide = (int) ((double) longSide / mAspectRatio);
}
if (widthLonger) {
previewWidth = longSide;
previewHeight = shortSide;
} else {
previewWidth = shortSide;
previewHeight = longSide;
}
// Add the padding of the border.
previewWidth += hPadding;
previewHeight += vPadding;
// Ask children to follow the new preview dimension.
super.onMeasure(MeasureSpec.makeMeasureSpec(previewWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(previewHeight, MeasureSpec.EXACTLY));
}
from this project
I'm currently working on playing a video (.mp4 file which works well in both two android tablets) in VideoView. First I used VideoView but the device (EKEN M003S) played the video in full screen, not within the VideoView which I set both width and height by 272 x 153 dp. So I tried to make an extended class of VideoView to override onMeasure() and changeVideoSize(). Like this:
public class EkenVideoView extends VideoView {
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Log.i("####", "onMeasure");
int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
if (mVideoWidth > 0 && mVideoHeight > 0) {
if ( mVideoWidth * height > width * mVideoHeight ) {
//Log.i("###", "image too tall, correcting");
height = width * mVideoHeight / mVideoWidth;
} else if ( mVideoWidth * height < width * mVideoHeight ) {
//Log.i("###", "image too wide, correcting");
width = height * mVideoWidth / mVideoHeight;
} else {
//Log.i("###", "aspect ratio is correct: " +
//width+"/"+height+"="+
//mVideoWidth+"/"+mVideoHeight);
}
}
if (screenMode == DisplayMode.ORIGINAL) {
if (mVideoWidth > 0 && mVideoHeight > 0) {
if ( mVideoWidth * height > width * mVideoHeight ) {
// video height exceeds screen, shrink it
height = width * mVideoHeight / mVideoWidth;
} else if ( mVideoWidth * height < width * mVideoHeight ) {
// video width exceeds screen, shrink it
width = height * mVideoWidth / mVideoHeight;
} else {
// aspect ratio is correct
}
}
}
else if (screenMode == DisplayMode.FULL_SCREEN) {
// just use the default screen width and screen height
}
else if (screenMode == DisplayMode.ZOOM) {
// zoom video
if (mVideoWidth > 0 && mVideoHeight > 0 && mVideoWidth < width) {
height = mVideoHeight * width / mVideoWidth;
}
}
//Log.i("##########", "setting size: " + width + 'x' + height);
setMeasuredDimension(272, 153);
}
public void changeVideoSize(int width, int height)
{
mVideoWidth = width;
mVideoHeight = height;
// not sure whether it is useful or not but safe to do so
getHolder().setFixedSize(272, 153);
requestLayout();
invalidate(); // very important, so that onMeasure will be triggered
}
I entered width 272 and height 153 to force width and height of the .mp4 video to be of that size, but EKEN M003S continues to play video in full screen mode. So when I run the app, everything works fine and the video plays in full screen on a layer below all other Views, making the Activity translucent with the video beneath it.
Other than EKEN M003S, I'm sure that some devices also have functions to force video to play in full screen by default, and also there is a way to override that default setting. If there is a way, please teach me how to do it. Thank you.
http://clseto.mysinablog.com/index.php?op=ViewArticle&articleId=2992625
I've tested this code on an Asus TF 101 (1280x800 res) and had the video running at a 640x480. Try setting the dimensions in your XML file and in your changeVideoSize() method, try setting screenMode = DisplayMode.ORIGINAL