Matrix video on TextureView Android
I am trying to play video from ExoPlaye
to TextureView
. To scale and crop the video for viewing, I use a matrix. My custom view extends "TextureView". Here is my code:
@Override
public void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio) {
updateTextureViewSize(width, height);
Log.v("EXO", "onVideoSizeChanged() " + width + "x" + height);
}
private void updateTextureViewSize(float videoWidth, float videoHeight) {
float viewWidth = getWidth();
float viewHeight = getHeight();
float scaleX = 1.0f;
float scaleY = 1.0f;
float viewRatio = viewWidth / viewHeight;
float videoRatio = videoWidth / videoHeight;
if (viewRatio > videoRatio) {
// video is higher than view
scaleY = videoHeight / videoWidth;
} else {
//video is wider than view
scaleX = videoWidth / videoHeight;
}
Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY, viewWidth / 2, viewHeight / 2);
setTransform(matrix);
}
I had a rectangular view and it worked fine. But now the views have 2 states: expanded (the old ones that are still rectangular) and collapsed (have a lower height.
So now the video is stretched vertically in these collapsed views.
For example, I have a preview 1080x480
and a video 360x640
, but it looks like the video is being scaled and cropped to 1080x1080
, rather than stretched to 1080x480
.
What am I doing wrong here?
source to share
check updateTextureViewSize
:
/**
* Set the display options
*
* @param layout <ul>
* <li>{@link #VIDEO_LAYOUT_ORIGIN}
* <li>{@link #VIDEO_LAYOUT_SCALE}
* <li>{@link #VIDEO_LAYOUT_STRETCH}
* <li>{@link #VIDEO_LAYOUT_ZOOM}
* </ul>
*/
public void updateTextureViewSize(int layout,float videoWidth, float videoHeight) {
RelativeLayout.LayoutParams lp = (android.widget.RelativeLayout.LayoutParams) getLayoutParams();
DisplayMetrics disp = m_Context.getResources().getDisplayMetrics();
int windowWidth = disp.widthPixels, windowHeight = disp.heightPixels;
float windowRatio = windowWidth / (float) windowHeight;
float videoRatio = (float) videoWidth / (float) videoHeight;
m_iSurfaceHeight = videoHeight;
m_iSurfaceWidth = videoWidth;
if (VIDEO_LAYOUT_ORIGIN == layout && m_iSurfaceWidth < windowWidth && m_iSurfaceHeight < windowHeight) {
lp.width = (int) (m_iSurfaceHeight * videoRatio);
lp.height = m_iSurfaceHeight;
} else if (layout == VIDEO_LAYOUT_ZOOM) {
lp.width = windowRatio > videoRatio ? windowWidth : (int) (videoRatio * windowHeight);
lp.height = windowRatio < videoRatio ? windowHeight : (int) (windowWidth / videoRatio);
} else {
boolean full = layout == VIDEO_LAYOUT_STRETCH;
lp.width = (full || windowRatio < videoRatio) ? windowWidth : (int) (videoRatio * windowHeight);
lp.height = (full || windowRatio > videoRatio) ? windowHeight : (int) (windowWidth / videoRatio);
lp.leftMargin = (disp.widthPixels - lp.width) / 2;
lp.topMargin = (disp.heightPixels - lp.height) / 2;
}
lp.leftMargin = (disp.widthPixels - lp.width) / 2;
lp.topMargin = (disp.heightPixels - lp.height) / 2;
getHolder().setFixedSize(m_iSurfaceWidth, m_iSurfaceHeight);
setLayoutParams(lp);
m_iVideoLayout = layout;
}
source to share
I fixed this question by the following by multiplying or dividing the scale factors by viewRatio
( width / height
):
if (viewRatio > videoRatio) {
// video is higher than view
scaleY = videoHeight / videoWidth * viewRatio;
} else {
//video is wider than view
scaleX = videoWidth / videoHeight / viewRatio;
}
But I didn't understand why this works. According to my calculations, if I have, for example, a view 1080x480
and the video the 360x640
video should be scaled with width x' = 1080
and proportional in height. So the height should be y' = 640*1080/360
( videoHeight * viewHeight / videoWidth
) andx' = 1080
sx * 360 = 1080
=> sx = 1080 / 360
sy * 640 = 640*1080/360
=> sy = 1080 / 360
It sounds like it makes sense. If we want to keep the aspect ratio, the width and height must be multiplied by the same factor. But it doesn't work. Where is the mistake? Is there a good document on this?
source to share