Camera2 camera transform - android

I'm having an issue with the Camera2 API, I'm basicly working with Google sample code (Camera2Basic), but I do changed the TextureView size so it does not span on the entire screen, the screen also have a ToolBar.
The preview display I'm getting is very skewed.
The TextureView size is w:1080 h:1620 (2/3)
The preview size is w:720 h:480 (3/2)
Here's the code for configureTransform
private void configureTransform(int viewWidth, int viewHeight) {
if (null == mTextureView || null == mPreviewSize || null == activity) {
return;
}
Log.i(CameraFragment.LOG_TAG, "viewWidth: " + viewWidth + " viewHeight: " + viewHeight + " ratio: " + (float)viewWidth/(float)viewHeight);
Log.i(CameraFragment.LOG_TAG, "previewWidth: " + mPreviewSize.getWidth() + " previewHeight: " + mPreviewSize.getHeight() + " ratio: " + (float)mPreviewSize.getWidth()/(float)mPreviewSize.getHeight());
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
mTextureView.setTransform(matrix);
}

Related

TextureVideo, media player and aspect ratio

I have a TextureView in which a video is showing, a video previously recorded with device camera. The image in the video is stretched, and that is not acceptable. I did some research, and found this method:
private void adjustAspectRatio(int videoWidth, int videoHeight) {
int viewWidth = m_TextureView.getWidth();
int viewHeight = m_TextureView.getHeight();
double aspectRatio = (double) videoHeight / videoWidth;
int newWidth, newHeight;
if (viewHeight > (int) (viewWidth * aspectRatio)) {
// limited by narrow width; restrict height
newWidth = viewWidth;
newHeight = (int) (viewWidth * aspectRatio);
} else {
// limited by short height; restrict width
newWidth = (int) (viewHeight / aspectRatio);
newHeight = viewHeight;
}
int xoff = (viewWidth - newWidth) / 2;
int yoff = (viewHeight - newHeight) / 2;
Log.v(TAG, "video=" + videoWidth + "x" + videoHeight +
" view=" + viewWidth + "x" + viewHeight +
" newView=" + newWidth + "x" + newHeight +
" off=" + xoff + "," + yoff);
Matrix txform = new Matrix();
m_TextureView.getTransform(txform);
txform.setScale((float) newWidth / viewWidth, (float) newHeight / viewHeight);
//txform.postRotate(10); // just for fun
txform.postTranslate(xoff, yoff);
m_TextureView.setTransform(txform);
}
Tried to use it, but I see a black background only.
This is mu onSurfaceTextureAvailable, in case it helps:
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
isVideoResultReady=true;
Surface s=new Surface(surface);
if(mPlayer==null){
mPlayer=new MediaPlayer();
}
try {
//mPlayer.setDataSource(videoPath);
mPlayer.setSurface(s);
mPlayer.prepare();
mPlayer.setOnBufferingUpdateListener(this);
mPlayer.setOnCompletionListener(this);
mPlayer.setOnPreparedListener(this);
//media player is started in other point of the app
} catch (IOException e) {
e.printStackTrace();
}
}
What is wrong with that method? How to keep aspect ratio? Thanks.

Need help in understanding Camera2Basic Sample from google

I am following camera2basic sample from google, to learn about camera2 API.
I am struggling in understanding the following method , specifically the requirement of this method and what is it doing ,as its not properly documented.
/**
* Configures the necessary {#link android.graphics.Matrix} transformation to `mTextureView`.
* This method should be called after the camera preview size is determined in
* setUpCameraOutputs and also the size of `mTextureView` is fixed.
*
* #param viewWidth The width of `mTextureView`
* #param viewHeight The height of `mTextureView`
*/
private void configureTransform(int viewWidth, int viewHeight) {
Activity activity = getActivity();
if (null == mTextureView || null == mPreviewSize || null == activity) {
return;
}
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
mTextureView.setTransform(matrix);
}
Any leads would be appreciated.
here is the link to repo :- Camera2BasicFragment.java
I added 0º and 180º
#Override public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
configCamara(width, height);
openCamera();
previewRotation(width, height);
}
private void previewRotation(int width, int height) {
int rotation = getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF textureRectF = new RectF(0, 0, width, height);
RectF previewRectF = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
float centerX = textureRectF.centerX();
float centery = textureRectF.centerY();
if (rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90) {
previewRectF.offset(centerX - previewRectF.centerX(), centery - previewRectF.centerY());
matrix.setRectToRect(textureRectF, previewRectF, Matrix.ScaleToFit.FILL);
float scale = Math.max((float) width / mTextureView.getWidth(), (float) height / mTextureView.getHeight());
matrix.postScale(scale, scale, centery, centerX);
matrix.postRotate(90 * (rotation - 2), centerX, centery);
} else if (rotation == Surface.ROTATION_0) {
matrix.postRotate(rotation, centerX, centery);
}else if (rotation == Surface.ROTATION_180) {
matrix.postRotate(180, centerX, centery);
}
mTextureView.setTransform(matrix);
}
When the device orientation is Surface.ROTATION_90 or Surface.ROTATION_270, there are two steps to construct the TextureView's transform: scaling and rotating. In the Android demo's code, the first step is scaling, and the second step is rotating. It is very hard to understand.
I change the order of the two steps. In my code, the first step is rotating, and the second step is scaling.
private Matrix mTempMatrix = new Matrix();
private void configureTransform(int viewWidth, int viewHeight) {
Activity activity = getActivity();
if (null == mTextureView || null == mPreviewSize || null == activity) {
return;
}
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
//1. scale
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
//2. rotate
RectF rotatedRect = new RectF(0, 0, viewHeight, viewWidth);
rotatedRect.offset(centerX - rotatedRect.centerX(), centerY - rotatedRect.centerY());
RectF bufferRect = new RectF(0, 0, mPreviewSize.getWidth(), mPreviewSize.getHeight());
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
mTempMatrix.setRectToRect(rotatedRect, bufferRect, Matrix.ScaleToFit.FILL);
matrix.postConcat(mTempMatrix);
float scale = Math.max(
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
mTextureView.setTransform(matrix);
}

Android camera2 surface with size 1920x1080 is not valid

I've implemented camera2 in my app and it is working on Nexus 6P and Nexus 5. Now I'm trying to test it on other devices, and the first one I tried on failed straight away. This is the error I get on HTC M7 running Lollipop:
Surface with size (w=1920, h=1080) and format 0x1 is not valid,
size not in valid set: [1920x1088, 1440x1088, 1456x832, 1088x1088,
1280x720, 960x720, 960x544, 720x720, 800x480, 768x464, 720x480,
768x432, 640x480, 544x544, 576x432, 640x384, 640x368, 480x480,
480x320, 384x288, 352x288, 320x240, 240x160, 176x144]
Any suggestions what should I do in this case? I've tried calculating the nearest resolution to my TextureView (which is 1280x720) and resizing TextureView accordingly, but that doesn't look particularly nice - too much unused space... Didn't see this problem on this device using old camera and SurfaceView
EDIT:
The problem seems to be inside my TextureView. This is my code:
inside a controller I have:
TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
int width, int height) {
startLollipopPreview(width, height);
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
int width, int height) {
configureTransform(width,height);
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return true;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
};
private void startLollipopPreview(int width, int height) {
CameraProxy camera = getCurrentCamera();
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
mPreviewSize = camera
.getOptimalPreviewSize((Activity) mContext,
camera.getSupportedPreviewSizes(
width, height), (double) width / height);
mPreviewTexture.setAspectRatio(mPreviewSize.width, mPreviewSize.height);
mPreviewTexture.getSurfaceTexture().setDefaultBufferSize(mPreviewSize.width,
mPreviewSize.height);
configureTransform(mPreviewSize.width, mPreviewSize.height);
camera.setStateAndHandler(getCameraStateCallback(), mBackgroundHandler);
camera.open();
} catch (CameraHardwareException e) {
Log.e(TAG, ".surfaceCreated() - Error opening camera", e);
((Activity) mContext).finish();
} catch (InterruptedException e) {
Log.e(TAG, ".surfaceCreated() - InterruptedException opening camera", e);
}
}
configureTransform() looks exactly like the Camera2 google sample so I don't think the problem is in there.
inside my TextureView I have the following:
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) {
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams();
params.gravity = Gravity.CENTER_VERTICAL;
setLayoutParams(params);
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
} else {
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
}
}
}
I believe you need to set the buffer size to a supported preview size:
textureView.getSurfaceTexture().setDefaultBufferSize(1280,720);
and then you can scale the TextureView so that it fits your screen, even if the preview size is smaller. The Camera2Video sample has an example. Specifically look at configureTransform in Camera2VideoFragment:
/**
* Configures the necessary {#link android.graphics.Matrix} transformation to `mTextureView`.
* This method should not to be called until the camera preview size is determined in
* openCamera, or until the size of `mTextureView` is fixed.
*
* #param viewWidth The width of `mTextureView`
* #param viewHeight The height of `mTextureView`
*/
private void configureTransform(int viewWidth, int viewHeight) {
Activity activity = getActivity();
if (null == mTextureView || null == mPreviewSize || null == activity) {
return;
}
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
}
mTextureView.setTransform(matrix);
}
The problem you facing change according to emulator/device that you testing the app.
This error occurs when the devices compatible sizes for the dimension of the screen and the MediaRecorder settings are not matched.
When the camera is open (openCamera(int w, int h) method) it's getting the sizes of supported resolutions by using chooseOptimalSize(.... .....) method. But before that, it will set videoSize().
Try to Logcat to get what are the supported sizes and according to that set the mMediaRecorder.setVideoSize(w,h);

How to set Width fit to Screen in Landscape Orientation (Using TouchImageView) in android

I m using the MikeOrtiz TouchImageView From the GitHub. Here is the link TouchImageView
I m trying to set the width fit to Screen in landscape Orientation only
here is an image to show what i want to do.
i want this
Instead of this which I m getting now in landscape
Here is some code.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
//
// Rescales image on rotation
//
if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
|| viewWidth == 0 || viewHeight == 0)
return;
oldMeasuredHeight = viewHeight;
oldMeasuredWidth = viewWidth;
if (saveScale == 1) {
// Fit to screen.
float scale;
Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0
|| drawable.getIntrinsicHeight() == 0)
return;
int bmWidth = drawable.getIntrinsicWidth();
int bmHeight = drawable.getIntrinsicHeight();
Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
float scaleX = (float) viewWidth / (float) bmWidth;
float scaleY = (float) viewHeight / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale , scale );
// Center the image
float redundantYSpace = (float) viewHeight
- (scale * (float) bmHeight);
float redundantXSpace = (float) viewWidth
- (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = viewWidth - 2 * redundantXSpace;
origHeight = viewHeight - 2 * redundantYSpace;
setImageMatrix(matrix);
}
fixTrans();
}
Thank you

Video Crop or zoom with a specific coordinate?

Is there any way to zoom the video from any specific coordinate on touch. I have tried all the way from mediaplayer and surfaceview and making both custom and accessing window manager and all.If any one has idea so please explain.
You can do this using SurfaceView. Check out my article Surface View - Video Cropping.
Code to crop SurfaceView.
private void updateTextureViewSize(int viewWidth, int viewHeight) {
float scaleX = 1.0f;
float scaleY = 1.0f;
if (mVideoWidth > viewWidth && mVideoHeight > viewHeight) {
scaleX = mVideoWidth / viewWidth;
scaleY = mVideoHeight / viewHeight;
} else if (mVideoWidth < viewWidth && mVideoHeight < viewHeight) {
scaleY = viewWidth / mVideoWidth;
scaleX = viewHeight / mVideoHeight;
} else if (viewWidth > mVideoWidth) {
scaleY = (viewWidth / mVideoWidth) / (viewHeight / mVideoHeight);
} else if (viewHeight > mVideoHeight) {
scaleX = (viewHeight / mVideoHeight) / (viewWidth / mVideoWidth);
}
// Calculate pivot points, in our case crop from center
int pivotPointX = viewWidth / 2;
int pivotPointY = viewHeight / 2;
Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY, pivotPointX, pivotPointY);
mTextureView.setTransform(matrix);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(viewWidth, viewHeight));
}

Categories

Resources