Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotate Video/MediaPlayer in a TextureView

I'm working with camera2 and I'm showing a preview of my photo/video after longclick in my thumbnail. Also, I'm rotating it depending of which orientation the camera had when the picture was taken. For example, if I did a picture in 90º, my preview will be also rotated 90º.

Everything is working fine, I'm using a customContainer and there I'm using onLayout and OnMeasure to create my preview depending of the size of the screen, aspect ratio and orientation. It works fine with photos. My problem appear when I try to do the same with videos, they only work in 0º.

I tried to rotate the TextureView which contain my MediaPlayer but after this my onLayout become crazy and Itś impossible find a (l,t,r,b) combination to measure it correctly.

Here is my 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>

Here is my Surface code:

    @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();
                    }

Finally here is my onMeasure/OnLayout from my 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);
        }
    }

I have been looking in other post as Android MediaRecorder making rotated video and I saw that it's not possible to rotate the textureView, but I can't believe that I can rotate a image so easily and have to fight during this to rotate 90 degrees a video.

like image 548
Francisco Durdin Garcia Avatar asked Mar 08 '16 16:03

Francisco Durdin Garcia


Video Answer


1 Answers

Thanks to @pskink for their comments in the post I found a solution with him. Finally I used a Matrix to rotate the Video Container(Texture View). The method that pskink give me is the next one:

  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);
}

Finally it worked and it looks totally awesome. With this I have been able to rotate and scale any video totally dynamically depending of the screen of my device and the Aspect Ratio used for record the video or take the picture.

like image 190
Francisco Durdin Garcia Avatar answered Sep 18 '22 16:09

Francisco Durdin Garcia