Exoplayer 在视频上使用缩放功能

问题描述 投票:0回答:1

我已经使用 ScaleGesture 在屏幕中实现了缩放和平移功能,但工作并不顺利。在全屏模式下,我使用TextureView.SurfaceTextureListener 实现了此功能。我需要在屏幕中放大和平移功能而不破坏当前活动,因为使用

TextureView.SurfaceTextureListener
我们需要破坏屏幕来加载新视频。有什么方法可以使用像TextureView.SurfaceTextureListener这样的矩阵来实现缩放和平移功能。使用TextureView.SurfaceTextureListener功能工作非常顺利

这是我的代码

override fun onTouch(v: View?, event: MotionEvent?): Boolean {
//        binding.txtScaleVideo.visibility = View.VISIBLE
        if (event!=null){
            val maxDx:Float = 0f
            val maxDy:Float = 0f
            when(event.action and MotionEvent.ACTION_MASK){
                MotionEvent.ACTION_DOWN -> {
                    Log.d("ModTouchEvent:::","ACTION_DOWN ------ $mode")
                    if (scale > MIN_ZOOM){
                        mode = Mode.DRAG
                        startX = event.x - previousDx
                        startY = event.y - previousDy
                    }
                }
                MotionEvent.ACTION_MOVE -> {
                    Log.d("ModTouchEvent:::","ACTION_MOVE ------ $mode")
                    isEnable = false
                    if (mode == Mode.DRAG) {
                        dx = event.x - startX
                        dy = event.y - startY
                    }

                }
                MotionEvent.ACTION_POINTER_DOWN ->{
                    Log.d("ModTouchEvent:::","ACTION_POINTER_DOWN ------ $mode")
                    mode = Mode.ZOOM
                }

                MotionEvent.ACTION_UP ->{

                    Log.d("ModTouchEvent:::","ACTION_UP ------ $mode")
                    previousDx = dx
                    previousDy = dy
                    mode = Mode.NONE
                }

                else -> {}
            }

            scaleGestureDetector?.onTouchEvent(event)
            gestureDetector?.onTouchEvent(event)

            if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM){
                binding.zoomLayout.requestDisallowInterceptTouchEvent(true)


                val maxDx = ((child().width * scale - child().width) ).coerceAtLeast(0f)
                val maxDy = ((child().height * scale - child().height) ).coerceAtLeast(0f)

                dx = dx.coerceIn(-maxDx, maxDx)
                dy = dy.coerceIn(-maxDy, maxDy)

                Log.d("ZoomInValues:::","DX -> $dx   DY -> $dy")



                applyScaleAndTranslation()
            }
        }



        return true
    }

    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
    override fun onScale(detector: ScaleGestureDetector): Boolean {

        val scaleFactor = detector.scaleFactor
        if (lastScaleFactor == 0f || (sign(scaleFactor) == sign(lastScaleFactor))){
            scale *= scaleFactor
            scale = scale.coerceIn(MIN_ZOOM, MAX_ZOOM)

            val threshold = 1f
            dx = if (abs(dx) < threshold) 0f else dx
            dy = if (abs(dy) < threshold) 0f else dy

            var scaleVal = AppGlobal.roundTwoPlaces(scale.toDouble(), "#.#").toString()
            if (!scaleVal.contains(".")){
                scaleVal = "$scaleVal.0"
            }

            val height=binding.layoutPlayerView.measuredHeight
            val width=binding.layoutPlayerView.measuredWidth
            val drawBB= scale== 1.0.toFloat()
            binding.layoutPlayerView.removeAllViews()

            if (drawBB && (seekToPositions.toDouble() == videoStartseekToPositions)){
                val bBoxList = startBbox
                bBoxList?.let { bBoxList ->
                    val drawView = PaintView(requireActivity(), bBoxList, height, width);
                    binding.layoutPlayerView.removeAllViews()
                    binding.layoutPlayerView.addView(drawView)
                }
            }
            val scaleText = "${scaleVal}"+" x"
            binding.txtScaleVideo.text = scaleText.toString()
            lastScaleFactor = scaleFactor
        }
        else{
            lastScaleFactor = 0f
        }
        applyScaleAndTranslation()
        return true

    }

    override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
        return true
    }

    override fun onScaleEnd(detector: ScaleGestureDetector) {

    }



    private fun applyScaleAndTranslation(){

        child().scaleX = scale
        child().scaleY = scale
        child().translationX = dx
        child().translationY = dy

    }

    private fun child() : View{
        return zoomLayout(0)
    }
    private fun zoomLayout(i: Int):View{
        return binding.videoView
    }

    enum class Mode{
        DRAG,
        ZOOM,
        NONE
    }
    private inner class GestureDetector : android.view.GestureDetector.SimpleOnGestureListener() {

        override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
            if (isEnable){
                isEnable = false
               // binding.videoView?.useController = false
            }
            else{
                isEnable = true
                //videoView?.useController = true
            }
            return super.onSingleTapConfirmed(e)
        }

        override fun onDoubleTap(event: MotionEvent): Boolean {
            if (event.x < (sWidth/2)){
                intLeft = true
                intRight = false
            }
            else if (event.x > (sWidth/2)){
                intLeft = false
                intRight = true
            }
            return super.onDoubleTap(event)
        }
    }

在此代码中,平移不像使用 TextureView.SurfaceTextureListener 和自定义

ZoomableTextureView
那样平滑。为了加载新视频,每次需要加载新视频时,我都需要调用 onSurfaceTextureDestroyed,但 onSurfaceTextureDestroyed 仅在 Activity 中调用 onDestroy 时调用。

public class ZoomableTextureView extends TextureView {
   private static final String SUPERSTATE_KEY = "superState";
   private static final String MIN_SCALE_KEY = "minScale";
   private static final String MAX_SCALE_KEY = "maxScale";
   private Context context; 
   private float minScale = 1f;
   private float maxScale = 5f;
   private float saveScale = 1f;  
   public void setMinScale(float scale) {
    if (scale < 1.0f || scale > maxScale)
        throw new RuntimeException("minScale can't be lower than 1 or larger than maxScale(" + maxScale + ")");
    else minScale = scale;
}
public void setMaxScale(float scale) {
    if (scale < 1.0f || scale < minScale)
        throw new RuntimeException("maxScale can't be lower than 1 or minScale(" + minScale + ")");
    else minScale = scale;
}
 private static final int NONE = 0;
 private static final int DRAG = 1;
 private static final int ZOOM = 2;
 private int mode = NONE;
 private Matrix matrix = new Matrix();
 private ScaleGestureDetector mScaleDetector;
private float[] m;
private PointF last = new PointF();
private PointF start = new PointF();
private float right, bottom;
public ZoomableTextureView(Context context) {
    super(context);
    this.context = context;
    initView(null);
}
public ZoomableTextureView(final Context context, final AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    initView(attrs);
}
public ZoomableTextureView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    this.context = context;
    initView(attrs);
}
@Override
protected Parcelable onSaveInstanceState() {
    Bundle bundle = new Bundle();
    bundle.putParcelable(SUPERSTATE_KEY, super.onSaveInstanceState());
    bundle.putFloat(MIN_SCALE_KEY, minScale);
    bundle.putFloat(MAX_SCALE_KEY, maxScale);
    return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
    if (state instanceof Bundle) {
        Bundle bundle = (Bundle) state;
        this.minScale = bundle.getInt(MIN_SCALE_KEY);
        this.minScale = bundle.getInt(MAX_SCALE_KEY);
        state = bundle.getParcelable(SUPERSTATE_KEY);
    }
    super.onRestoreInstanceState(state);
}    
private void initView(AttributeSet attrs) {
    TypedArray a = context.getTheme().obtainStyledAttributes(
            attrs,
            R.styleable.ZoomableTextureView,
            0, 0);
    try {
        minScale = a.getFloat(R.styleable.ZoomableTextureView_minScale, minScale);
        maxScale = a.getFloat(R.styleable.ZoomableTextureView_maxScale, maxScale);
    } finally {
        a.recycle();
    }
    setOnTouchListener(new ZoomOnTouchListeners());
}
public void deAttachFromWindow() {
    onDetachedFromWindow();
    onFinishTemporaryDetach();
}
public void clearWindow() {
    onDetachedFromWindow();
    onFinishTemporaryDetach();
}
private class ZoomOnTouchListeners implements View.OnTouchListener {
    public ZoomOnTouchListeners() {
        super();
        m = new float[9];
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    }    
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        mScaleDetector.onTouchEvent(motionEvent);
        matrix.getValues(m);
        float x = m[Matrix.MTRANS_X];
        float y = m[Matrix.MTRANS_Y];
        PointF curr = new PointF(motionEvent.getX(), motionEvent.getY());
        switch (motionEvent.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                last.set(motionEvent.getX(), motionEvent.getY());
                start.set(last);
                mode = DRAG;
                break;
            case MotionEvent.ACTION_UP:
                mode = NONE;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                last.set(motionEvent.getX(), motionEvent.getY());
                start.set(last);
                mode = ZOOM;
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == ZOOM || (mode == DRAG && saveScale > minScale)) {
                    float deltaX = curr.x - last.x;// x difference
                    float deltaY = curr.y - last.y;// y difference
                    if (y + deltaY > 0)
                        deltaY = -y;
                    else if (y + deltaY < -bottom)
                        deltaY = -(y + bottom);
                    if (x + deltaX > 0)
                        deltaX = -x;
                    else if (x + deltaX < -right)
                        deltaX = -(x + right);
                    matrix.postTranslate(deltaX, deltaY);
                    last.set(curr.x, curr.y);
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                break;
        }
        ZoomableTextureView.this.setTransform(matrix);
        ZoomableTextureView.this.invalidate();
        return true;
    }
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            mode = ZOOM;
            return true;
        }
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float mScaleFactor = detector.getScaleFactor();
            float origScale = saveScale;
            saveScale *= mScaleFactor;
            if (saveScale > maxScale) {
                saveScale = maxScale;
                mScaleFactor = maxScale / origScale;
            } else if (saveScale < minScale) {
                saveScale = minScale;
                mScaleFactor = minScale / origScale;
            }
            MutableLiveData<Float> zoomScale = AppGlobal.Companion.getZoomScale();
            zoomScale.setValue(saveScale); // Set the new value for zoomScale
            right = getWidth() * saveScale - getWidth();
            bottom = getHeight() * saveScale - getHeight();
            if (0 <= getWidth() || 0 <= getHeight()) {              matrix.postScale(mScaleFactor,mScaleFactor,detector.getFocusX(),detector.getFocusY());
                if (mScaleFactor < 1) {
                    matrix.getValues(m);
                    float x = m[Matrix.MTRANS_X];
                    float y = m[Matrix.MTRANS_Y];
                    if (mScaleFactor < 1) {
                        if (0 < getWidth()) {
                            if (y < -bottom)
                                matrix.postTranslate(0, -(y + bottom));
                            else if (y > 0)
                                matrix.postTranslate(0, -y);
                        } else {
                            if (x < -right)
                                matrix.postTranslate(-(x + right), 0);
                            else if (x > 0)
                                matrix.postTranslate(-x, 0);
                        }
                    }
                }
            } else {
                matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
                matrix.getValues(m);
                float x = m[Matrix.MTRANS_X];
                float y = m[Matrix.MTRANS_Y];
                if (mScaleFactor < 1) {
                    if (x < -right)
                        matrix.postTranslate(-(x + right), 0);
                    else if (x > 0)
                        matrix.postTranslate(-x, 0);
                    if (y < -bottom)
                        matrix.postTranslate(0, -(y + bottom));
                    else if (y > 0)
                        matrix.postTranslate(0, -y);
                }
            }
            return true;
        }
    }
}
}
java android kotlin exoplayer panning
1个回答
0
投票

你把事情搞得太复杂了,使用下面的代码。

xml 文件

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".zz">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="200dp">

        <com.otaliastudios.zoom.ZoomSurfaceView
            android:id="@+id/surface_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="bottom|center_horizontal"
            app:alignment="center_horizontal"
            app:flingEnabled="true"
            app:horizontalPanEnabled="true"
            app:maxZoom="8"
            app:maxZoomType="zoom"
            app:minZoom="1"
            app:minZoomType="zoom"
            app:oneFingerScrollEnabled="true"
            app:overPinchable="false"
            app:overScrollHorizontal="false"
            app:overScrollVertical="false"
            app:scrollEnabled="true"
            app:transformation="centerInside"
            app:transformationGravity="auto"
            app:twoFingersScrollEnabled="true"
            app:verticalPanEnabled="true"
            app:zoomEnabled="true" />

        <androidx.media3.ui.PlayerControlView
            android:id="@+id/player_control_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </RelativeLayout>

</RelativeLayout>

java 文件

import android.os.Bundle;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.OptIn;
import androidx.appcompat.app.AppCompatActivity;
import androidx.media3.common.MediaItem;
import androidx.media3.common.VideoSize;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.ui.PlayerControlView;
import com.otaliastudios.zoom.ZoomSurfaceView;
import org.jetbrains.annotations.NotNull;




public class zz extends AppCompatActivity {

        private ExoPlayer mediaPlayer;
        private ZoomSurfaceView surfaceView;
        private PlayerControlView playerControlView;




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.zz);

        surfaceView = findViewById(R.id.surface_view);
        playerControlView = findViewById(R.id.player_control_view);

         mediaPlayer = new ExoPlayer.Builder(this).build();
        playerControlView.setPlayer(mediaPlayer);
        MediaItem mediaItem=             
MediaItem.fromUri("https://firebasestorage.googleapis.com/v0/b/test2- 
5bbd8.appspot.com/o/chat%2Fvideoplayback.mp4?alt=media");
        mediaPlayer.setMediaItem(mediaItem);
        mediaPlayer.prepare();
        mediaPlayer.play();






        surfaceView.addCallback(new ZoomSurfaceView.Callback() {
            @Override
            public void onZoomSurfaceCreated(@NotNull ZoomSurfaceView      
view) {
                Surface surface = view.getSurface();
                mediaPlayer.setVideoSurface(surface);


            }

            @Override
            public void onZoomSurfaceDestroyed(@NotNull ZoomSurfaceView 
view) {
            }
        });

        mediaPlayer.addListener(new ExoPlayer.Listener() {
            @Override
            public void onVideoSizeChanged(@NonNull VideoSize videoSize) 
{
                surfaceView.setContentSize(videoSize.width, 
videoSize.height);
            }
        });


    };



    };

基本上你使用一个名为 com.otaliastudios.zoom.ZoomSurfaceView 的库,它的作用就像一个可以缩放的表面视图,并设置此表面播放器控制视图,一切都完成了。

© www.soinside.com 2019 - 2024. All rights reserved.