2

SurfaceView オブジェクトで RTMP ストリーミングを再生するときにズームインまたはズームアウトできる機能を実装しています。私はいくつかの調査を行い、最初にサイズと位置を計算し、onMeasure() メソッドを使用してビューを再構築することにしました。

現在、ズーム中に設定した位置にオブジェクトが実際には従わないという問題があります。実装したい効果は、このビデオ(私のアプリの iOS バージョンの動作を示しています) のようなもので、これまでに行ったことを示すために別のビデオを録画しました。iOS版アプリと同じ位置で使用すると、オブジェクトがより頻繁に移動します。まったくスムーズではなく、ユーザー エクスペリエンスが低下する可能性があります。

動作に対する私の観察では、ズーム機能がトリガーされると、SurfaceView のオブジェクトが固定位置 x と y で幅と高さのサイズを変更することがわかりました。次に、オブジェクトは後方に移動します。これは、その親のレイアウトに従うことに起因する可能性があります。ログ メッセージを出力したところ、onMeasure() がトリガーされたときに x と y の位置が変更されていることがわかりました。関数で位置を設定しましたが、プロセスが 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 でズームインズームアウト機能を実装できない場合、推奨される解決策はありますか? ありがとう。

4

1 に答える 1

1

SurfaceView の移動とサイズ変更は、おそらく正しい答えではありません。SurfaceView の Surface 部分は別のレイヤーであり、そのサイズと位置はアプリのビュー階層ではなく Window Manager によって管理されるため、SurfaceView の属性をアニメーション化するとぎこちなく見える傾向があります。

考えられるアプローチの 1 つは、SurfaceView から TextureView に切り替えることです。レイアウト コードを取り除き、TextureView の変換マトリックスのみを使用できます。たとえば、ビデオのアスペクト比が Grafika ムービー再生クラスで処理される方法の違いを参照してください。PlayMoveSurfaceActivity .clickPlayStop() は AspectFrameLayout を使用しますが、TextureView ベースのPlayMovieActivity .clickPlayStop() はビューをそのままにして、マトリックスを微調整するだけです。 .

もう 1 つの方法は、SurfaceView を保持し、カメラ プレビューを SurfaceTexture に送信し、出力を OpenGL ES テクスチャにレンダリングすることです。例については、 TextureFromCameraActivityを参照してください。テクスチャ座標を更新することによって、ズーム機能がどのように実装されているかに注意してください。これははるかに柔軟ですが、GLES セットアップ コードを組み込む必要があります。

于 2015-02-03T17:07:04.890 に答える