Как я могу плавно увеличить и уменьшить масштаб в Android? Относится к SurfaceView с потоковой передачей RTMP

Я реализую функцию, которая может увеличивать или уменьшать масштаб при воспроизведении потокового RTMP с объектом SurfaceView. Я провел некоторое исследование и решил сначала вычислить размер и положение и использовать метод onMeasure() для пересмотра представления.

Теперь у меня проблема в том, что объект не соответствует положению, которое я установил во время масштабирования. Эффект, который я хотел бы реализовать, похож на это видео (которое показывает поведение версии моего приложения для iOS), и я записал другое видео, чтобы показать, что я сделал до сих пор. Объект будет смещаться чаще, если я использую ту же позицию, что и приложение версии iOS. Это не совсем гладко и может привести к ухудшению работы пользователя.

Мое наблюдение за поведением состоит в том, что объект SurfaceView изменил бы размеры своей ширины и высоты с фиксированным положением x и y при срабатывании функции масштабирования. Затем объект будет сдвигаться назад, что может быть результатом следования макету его родителя. Я распечатал сообщения журнала и обнаружил, что положение x и y изменилось бы при запуске onMeasure(). Хотя я установил позицию в функции, она все равно будет изменена, когда процесс перейдет к onLayout(). Так как onLayout() вызывается, как только завершается onMeasure(), я не вижу возможного времени для установки позиции, чтобы избежать смещения.

Вот что я сделал: Макет:

<RelativeLayout
    android:id="@+id/RelaytiveView"
    android:layout_width="fill_parent"
    android:layout_height="200dp">
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_centerInParent="true"
        android:background="#000000"
        android:gravity="center"
        android:orientation="vertical" >
        <com.player.adapter.RtmpLiveSurfaceview
            android:id="@+id/goLiveView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
</RelativeLayout>

И код:

public class RtmpLiveSurfaceview extends SurfaceView 
{
    private static final int DEFAULT_PIXEL_WIDTH = 1280;
    private static final int DEFAULT_PIXEL_HEIGHT = 720;
    private float INIT_WIDTH;
    private float INIT_HEIGHT;
    private float WIDTH = 0;
    private float HEIGHT = 0;
    private float SCALER = (float) 1.0;
    private float TAG_POINT_X = 0;
    private float TAG_POINT_Y = 0;

    private float INIT_DIST = 0;
    SurfaceHolder mholder;

    static final int ZOOM = 2;

    @Override
    protected void onSizeChanged (int w, int h, int oldw, int oldh)
    {
    }
    @Override
    protected void onLayout (boolean changed, int left, int top, int right, int bottom)
    {
        setX(TAG_POINT_X);
        setY(TAG_POINT_Y);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    {
        int width = getDefaultSize(DEFAULT_PIXEL_WIDTH, widthMeasureSpec);
        int height = getDefaultSize(DEFAULT_PIXEL_HEIGHT, heightMeasureSpec);
        if (DEFAULT_PIXEL_WIDTH > 0 && DEFAULT_PIXEL_HEIGHT > 0)
        {
            if ( DEFAULT_PIXEL_WIDTH * height  > width * DEFAULT_PIXEL_HEIGHT )
            {
                height = width * DEFAULT_PIXEL_HEIGHT / DEFAULT_PIXEL_WIDTH;
            } 
            else if ( DEFAULT_PIXEL_WIDTH * height  < width * DEFAULT_PIXEL_HEIGHT )
            {
                width = height * DEFAULT_PIXEL_WIDTH / DEFAULT_PIXEL_HEIGHT;
            }
            else
            {
            }
        }
        INIT_WIDTH = width;
        INIT_HEIGHT = height;
        if(WIDTH == 0)
        {
            WIDTH = INIT_WIDTH;
            HEIGHT = INIT_HEIGHT;
        }
        int wmode = MeasureSpec.getMode(widthMeasureSpec);
        int hmode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSpec = MeasureSpec.makeMeasureSpec((int)WIDTH, wmode);
        int heightSpec = MeasureSpec.makeMeasureSpec((int)HEIGHT, hmode);
        setX(TAG_POINT_X);
        setY(TAG_POINT_Y);
        super.onMeasure(widthSpec, heightSpec);
    }
    public void init() 
    {
        mholder = getHolder();
        mholder.setSizeFromLayout();
        mholder.addCallback(new Callback() 
        {
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
            {
            }
        });
    }

    public boolean onTouchEvent(MotionEvent MEvent) 
    {
        switch (MEvent.getAction() & MotionEvent.ACTION_MASK) 
        {
            case MotionEvent.ACTION_POINTER_DOWN:
            {
                if(MEvent.getPointerCount() >= 2)
                {
                    mode = ZOOM;
                    INIT_DIST = spacing(MEvent);
                    break;
                }
            }
            case MotionEvent.ACTION_MOVE:
            {
                if (mode == ZOOM) 
                {
                    if(MEvent.getPointerCount() >= 2)
                    {
                        float newDist = spacing(MEvent);
                        float tmp_scaler = (float) (((newDist - INIT_DIST) * 0.1 + INIT_DIST) / INIT_DIST);
                        if((tmp_scaler * WIDTH) > (2.0 * INIT_WIDTH))
                            tmp_scaler = (float) (((newDist - INIT_DIST) * 0.01 + INIT_DIST) / INIT_DIST);
                        WIDTH = getWidth();
                        HEIGHT = getHeight();
                        if (((tmp_scaler * WIDTH) >= (1.0 * INIT_WIDTH)) && ((tmp_scaler * WIDTH) <= (4.0 * INIT_WIDTH)))
                        {
                            SCALER = tmp_scaler;
                            TAG_POINT_X = getX();
                            if((TAG_POINT_X + WIDTH * SCALER) < INIT_WIDTH)
                                TAG_POINT_X = INIT_WIDTH - WIDTH * SCALER;
                            else if(TAG_POINT_X > 0)
                                TAG_POINT_X = 0;
                            TAG_POINT_Y = getY();
                            if((TAG_POINT_Y + HEIGHT * SCALER) < INIT_HEIGHT)
                                TAG_POINT_Y = INIT_HEIGHT - HEIGHT * SCALER;
                            else if(TAG_POINT_Y > 0)
                                TAG_POINT_Y = 0;
                            WIDTH = WIDTH * SCALER;
                            HEIGHT = HEIGHT * SCALER;
                            requestLayout();
                        }
                    }
                }
                break;
            }

        }

        return true;
    }
}

Может кто-нибудь мне помочь? Если невозможно реализовать функцию увеличения-уменьшения-масштаба в SurfaceView, есть ли какое-либо предлагаемое решение? Благодарю.

1 ответ

Перемещение и изменение размера SurfaceView, вероятно, не правильный ответ. Часть Surface в SurfaceView - это отдельный слой, размером и положением которого управляет диспетчер окон, а не иерархия View приложения, поэтому анимация атрибутов SurfaceView выглядит неопрятно.

Одним из возможных подходов было бы переключиться с SurfaceView на TextureView. Вы можете избавиться от кода макета и просто использовать матрицу преобразования TextureView. Посмотрите, например, разницу в том, как обрабатывается соотношение сторон видео в классах воспроизведения фильмов Grafika - PlayMoveSurfaceActivity.clickPlayStop () использует AspectFrameLayout, в то время как PlayMovieActivity.clickPlayStop () на основе TextureView оставляет один только View и настраивает матрицу,

Другой подход заключается в том, чтобы сохранить SurfaceView, но отправить предварительный просмотр камеры в SurfaceTexture и визуализировать вывод в текстуру OpenGL ES. Посмотрите TextureFromCameraActivity для примера. Обратите внимание, как реализована функция масштабирования путем обновления координат текстуры. Это гораздо более гибко, но вам нужно будет включить установочный код GLES.

Другие вопросы по тегам