在 TextureView 中旋转 Video/MediaPlayer
Rotate Video/MediaPlayer in a TextureView
我正在使用 camera2 并在我的缩略图中长按后显示 photo/video 的预览。另外,我根据拍摄照片时相机的方向旋转它。例如,如果我以 90º 拍摄图片,我的预览也会旋转 90º。
一切正常,我正在使用 customContainer 并使用 onLayout 和 OnMeasure 根据屏幕大小、纵横比和方向创建预览。它适用于照片。当我尝试对视频做同样的事情时,我的问题出现了,它们只能在 0º 下工作。
我尝试旋转包含我的 MediaPlayer 的 TextureView,但此后我的 onLayout 变得疯狂,不可能找到 (l,t,r,b) 组合来正确测量它。
这是我的 XML:
<?xml version="1.0" encoding="utf-8"?>
<com.android.camera.ui.common.ThumbnailContainer xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/preview_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/rounded_rectangle_thumbnail_preview"
android:visibility="invisible">
<TextureView
android:id="@+id/show_video_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
<ImageView
android:id="@+id/image_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:visibility="invisible"
/>
</com.android.camera.ui.common.ThumbnailContainer>
这是我的 Surface 代码:
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.i(TAG, "InicializoSurface. Width: " + width + " HEIGHT:" + height);
Log.i(TAG, "InicializoSurface. Width: " + mVideoView.getMeasuredWidth() + " HEIGHT:" + mVideoView.getMeasuredHeight());
Log.i(TAG, "View transform. Width: " + mVideoView.getWidth() + " HEIGHT:" + mVideoView.getHeight());
mMediaSurface = new Surface(mVideoView.getSurfaceTexture());
initializeMediaPlayer();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
if (mMediaPlayer != null) {
// Make sure we stop video and release resources when activity is destroyed.
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
//////////
private void initializeMediaPlayer(){
mMediaPlayer = new CustomMediaPlayer();
Uri uri = Uri.parse(mCameraDataAdapter.getList().get(0).getPath());
try {
mMediaPlayer.setDataSource(mActivity, uri);
mMediaPlayer.setSurface(mMediaSurface);
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(mMediaPlayer);
mMediaPlayer.setOnCompletionListener(mMediaPlayer);
} catch (IOException e) {
e.printStackTrace();
}
}
///////////
mVideoView.setVisibility(View.VISIBLE);
// mVideoView.setTranslationX(-200);
// mVideoView.setTranslationY(-200);
Log.i(TAG, "X: " + mVideoView.getX() + "Y: " + mVideoView.getY());
if (mVideoView.isAvailable()) {
onSurfaceTextureAvailable(mVideoView.getSurfaceTexture(), mVideoView.getWidth(), mVideoView.getHeight());
}
if (mMediaPlayer == null) {
initializeMediaPlayer();
}
// mMediaPlayer.mVideoHolder = mVideoView.getHolder();
// mMediaPlayer.setDisplay(mMediaPlayer.mVideoHolder);
if (mMediaPrepared) {
Log.i(TAG,"Comienzo Video");
mMediaPlayer.start();
}
最后是我的 onMeasure/OnLayout 来自我的 CustomView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width;
int height;
int wantedWidth = 0;
int wantedHeight = 0;
if(mWidth == 0 && mHeight == 0 ){
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight =MeasureSpec.getSize(heightMeasureSpec);
}
width = mWidth;
height = mHeight;
if (mOrientation == 0 || mOrientation == 180) {
wantedWidth = width - (int)(mMargin * 2);
mVideo.measure(MeasureSpec.makeMeasureSpec(wantedWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) (wantedWidth * mVideoAspectRatio), MeasureSpec.EXACTLY));
wantedHeight = (mViewTop.getLayoutParams().height) * 2 + (int) (wantedWidth * mAspectRatio);
} else {
Log.e(TAG, "Real Width = " + width + " real Height = " + height);
wantedHeight = width - 2 * mViewTop.getLayoutParams().height - (int)(mMargin * 2);
mVideo.measure(MeasureSpec.makeMeasureSpec(wantedHeight, MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec((int) (wantedHeight * mAspectRatio), MeasureSpec.EXACTLY));
//
wantedWidth =(int) (wantedHeight * mAspectRatio) ;
wantedHeight = width - (int)(mMargin * 2);
}
Log.e(TAG, "onMeasure: " + wantedWidth + "x" + wantedHeight);
setMeasuredDimension(MeasureSpec.makeMeasureSpec(wantedWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(wantedHeight, MeasureSpec.EXACTLY));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int w = getMeasuredWidth();
int h = getMeasuredHeight();
int viewHeight = mViewBottom.getMeasuredHeight();
int imageViewHeight = mImage.getMeasuredHeight();
int wantedHeight = 0;
// w = w - (int) (2 * mMargin);
if (mOrientation == 0 || mOrientation == 180) {
mVideo.layout(0,wantedHeight,w,wantedHeight + imageViewHeight);
}else{
mVideo.layout(viewHeight,0,r-viewHeight - (int) mMargin,w);
}
}
我一直在寻找其他 post 作为 Android MediaRecorder making rotated video 并且我看到无法旋转 textureView,但我无法相信我可以如此轻松地旋转图像并拥有在此期间打一个旋转90度的视频。
感谢@pskink 在 post 中的评论,我找到了解决方案。最后,我使用矩阵来旋转视频容器(纹理视图)。 pskink给我的方法是下一个:
private void setupMatrix(int width, int height, int degrees, boolean isHorizontal) {
Log.d(TAG, "setupMatrix for " + degrees + " degrees");
Matrix matrix = new Matrix();
//The video will be streched if the aspect ratio is in 1,5(recording at 480)
RectF src;
if (isHorizontal)
//In my case, I changed this line, because with my onMeasure() and onLayout() methods my container view is already rotated and scaled, so I need to sent the inverted params to the src.
src = new RectF(0, 0,mThumbnailContainer.getmWidth(), mThumbnailContainer.getmHeight());
else
src = new RectF(0, 0, mThumbnailContainer.getmWidth(),mThumbnailContainer.getmHeight());
RectF dst = new RectF(0, 0, width, height);
RectF screen = new RectF(dst);
Log.d(TAG, "Matrix: " + width + "x" + height);
Log.d(TAG, "Matrix: " + mThumbnailContainer.getmWidth() + "x" + mThumbnailContainer.getmHeight());
matrix.postRotate(degrees, screen.centerX(), screen.centerY());
matrix.mapRect(dst);
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
matrix.mapRect(src);
matrix.setRectToRect(screen, src, Matrix.ScaleToFit.FILL);
matrix.postRotate(degrees, screen.centerX(), screen.centerY());
mVideoView.setTransform(matrix);
}
终于成功了,看起来棒极了。有了这个,我已经能够完全动态地旋转和缩放任何视频,具体取决于我的设备屏幕和用于录制视频或拍照的纵横比。
我正在使用 camera2 并在我的缩略图中长按后显示 photo/video 的预览。另外,我根据拍摄照片时相机的方向旋转它。例如,如果我以 90º 拍摄图片,我的预览也会旋转 90º。
一切正常,我正在使用 customContainer 并使用 onLayout 和 OnMeasure 根据屏幕大小、纵横比和方向创建预览。它适用于照片。当我尝试对视频做同样的事情时,我的问题出现了,它们只能在 0º 下工作。
我尝试旋转包含我的 MediaPlayer 的 TextureView,但此后我的 onLayout 变得疯狂,不可能找到 (l,t,r,b) 组合来正确测量它。
这是我的 XML:
<?xml version="1.0" encoding="utf-8"?>
<com.android.camera.ui.common.ThumbnailContainer xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/preview_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/rounded_rectangle_thumbnail_preview"
android:visibility="invisible">
<TextureView
android:id="@+id/show_video_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
<ImageView
android:id="@+id/image_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:visibility="invisible"
/>
</com.android.camera.ui.common.ThumbnailContainer>
这是我的 Surface 代码:
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.i(TAG, "InicializoSurface. Width: " + width + " HEIGHT:" + height);
Log.i(TAG, "InicializoSurface. Width: " + mVideoView.getMeasuredWidth() + " HEIGHT:" + mVideoView.getMeasuredHeight());
Log.i(TAG, "View transform. Width: " + mVideoView.getWidth() + " HEIGHT:" + mVideoView.getHeight());
mMediaSurface = new Surface(mVideoView.getSurfaceTexture());
initializeMediaPlayer();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
if (mMediaPlayer != null) {
// Make sure we stop video and release resources when activity is destroyed.
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
//////////
private void initializeMediaPlayer(){
mMediaPlayer = new CustomMediaPlayer();
Uri uri = Uri.parse(mCameraDataAdapter.getList().get(0).getPath());
try {
mMediaPlayer.setDataSource(mActivity, uri);
mMediaPlayer.setSurface(mMediaSurface);
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(mMediaPlayer);
mMediaPlayer.setOnCompletionListener(mMediaPlayer);
} catch (IOException e) {
e.printStackTrace();
}
}
///////////
mVideoView.setVisibility(View.VISIBLE);
// mVideoView.setTranslationX(-200);
// mVideoView.setTranslationY(-200);
Log.i(TAG, "X: " + mVideoView.getX() + "Y: " + mVideoView.getY());
if (mVideoView.isAvailable()) {
onSurfaceTextureAvailable(mVideoView.getSurfaceTexture(), mVideoView.getWidth(), mVideoView.getHeight());
}
if (mMediaPlayer == null) {
initializeMediaPlayer();
}
// mMediaPlayer.mVideoHolder = mVideoView.getHolder();
// mMediaPlayer.setDisplay(mMediaPlayer.mVideoHolder);
if (mMediaPrepared) {
Log.i(TAG,"Comienzo Video");
mMediaPlayer.start();
}
最后是我的 onMeasure/OnLayout 来自我的 CustomView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width;
int height;
int wantedWidth = 0;
int wantedHeight = 0;
if(mWidth == 0 && mHeight == 0 ){
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight =MeasureSpec.getSize(heightMeasureSpec);
}
width = mWidth;
height = mHeight;
if (mOrientation == 0 || mOrientation == 180) {
wantedWidth = width - (int)(mMargin * 2);
mVideo.measure(MeasureSpec.makeMeasureSpec(wantedWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) (wantedWidth * mVideoAspectRatio), MeasureSpec.EXACTLY));
wantedHeight = (mViewTop.getLayoutParams().height) * 2 + (int) (wantedWidth * mAspectRatio);
} else {
Log.e(TAG, "Real Width = " + width + " real Height = " + height);
wantedHeight = width - 2 * mViewTop.getLayoutParams().height - (int)(mMargin * 2);
mVideo.measure(MeasureSpec.makeMeasureSpec(wantedHeight, MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec((int) (wantedHeight * mAspectRatio), MeasureSpec.EXACTLY));
//
wantedWidth =(int) (wantedHeight * mAspectRatio) ;
wantedHeight = width - (int)(mMargin * 2);
}
Log.e(TAG, "onMeasure: " + wantedWidth + "x" + wantedHeight);
setMeasuredDimension(MeasureSpec.makeMeasureSpec(wantedWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(wantedHeight, MeasureSpec.EXACTLY));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int w = getMeasuredWidth();
int h = getMeasuredHeight();
int viewHeight = mViewBottom.getMeasuredHeight();
int imageViewHeight = mImage.getMeasuredHeight();
int wantedHeight = 0;
// w = w - (int) (2 * mMargin);
if (mOrientation == 0 || mOrientation == 180) {
mVideo.layout(0,wantedHeight,w,wantedHeight + imageViewHeight);
}else{
mVideo.layout(viewHeight,0,r-viewHeight - (int) mMargin,w);
}
}
我一直在寻找其他 post 作为 Android MediaRecorder making rotated video 并且我看到无法旋转 textureView,但我无法相信我可以如此轻松地旋转图像并拥有在此期间打一个旋转90度的视频。
感谢@pskink 在 post 中的评论,我找到了解决方案。最后,我使用矩阵来旋转视频容器(纹理视图)。 pskink给我的方法是下一个:
private void setupMatrix(int width, int height, int degrees, boolean isHorizontal) {
Log.d(TAG, "setupMatrix for " + degrees + " degrees");
Matrix matrix = new Matrix();
//The video will be streched if the aspect ratio is in 1,5(recording at 480)
RectF src;
if (isHorizontal)
//In my case, I changed this line, because with my onMeasure() and onLayout() methods my container view is already rotated and scaled, so I need to sent the inverted params to the src.
src = new RectF(0, 0,mThumbnailContainer.getmWidth(), mThumbnailContainer.getmHeight());
else
src = new RectF(0, 0, mThumbnailContainer.getmWidth(),mThumbnailContainer.getmHeight());
RectF dst = new RectF(0, 0, width, height);
RectF screen = new RectF(dst);
Log.d(TAG, "Matrix: " + width + "x" + height);
Log.d(TAG, "Matrix: " + mThumbnailContainer.getmWidth() + "x" + mThumbnailContainer.getmHeight());
matrix.postRotate(degrees, screen.centerX(), screen.centerY());
matrix.mapRect(dst);
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
matrix.mapRect(src);
matrix.setRectToRect(screen, src, Matrix.ScaleToFit.FILL);
matrix.postRotate(degrees, screen.centerX(), screen.centerY());
mVideoView.setTransform(matrix);
}
终于成功了,看起来棒极了。有了这个,我已经能够完全动态地旋转和缩放任何视频,具体取决于我的设备屏幕和用于录制视频或拍照的纵横比。