0

我正在使用 GLSurfaceView 创建一个录制应用程序,但是当我进入 PIP 模式时,GLSurfaceView 的 Surface 太大,因此无法很好地看到屏幕。

看图片。
[1]:https ://i.stack.imgur.com/GYebT.jpg [2]:https ://i.stack.imgur.com/XUlhA.jpg

两个屏幕都在拍摄同一个场景。如图所示,对于 PIP 模式,屏幕被放大。

VideoCallFragment.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".VideoCallFragment">

    <com.jypark.glsurfaceandremoteservice.gles.AspectRatioFrameLayout
        android:id="@+id/aspect_frame_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        app:resize_mode="fill"
        >
        <android.opengl.GLSurfaceView
            android:id="@+id/gl_surface_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </com.jypark.glsurfaceandremoteservice.gles.AspectRatioFrameLayout>

    <ImageButton
        android:id="@+id/ib_recording"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:background="?attr/selectableItemBackgroundBorderless"
        android:src="@drawable/ic_recording"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="32dp"
        android:contentDescription="@string/recording_image_button_desc"
        />
</RelativeLayout>

VideoCallFragment.java

public class VideoCallFragment extends Fragment implements SurfaceTexture.OnFrameAvailableListener{

    private AspectRatioFrameLayout aspectRatioFrameLayout;
    private GLSurfaceView glSurfaceView;
    private ImageButton imageButton;

    private Messenger messenger;

    private RenderHandler mRendererHandler;
    private VideoRenderAndRecorder mRenderer;

    private static final TextureMovieEncoder sVideoEncoder = new TextureMovieEncoder();

    private Camera mCamera;

    private int RECORD_WIDTH = 480;
    private int RECORD_HEIGHT = 640;

    private int VIDEO_WIDTH = 1080;
    private int VIDEO_HEIGHT = 1920;

    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            showLog("onServiceConnected");
            messenger = new Messenger(service);
            try{
                Message reply = new Message();
                reply.replyTo = new Messenger(replyHandler);
                reply.what = MSG_SERVICE_BIND;
                messenger.send(reply);
            }
            catch (RemoteException e){
                showLog("service error: " + e.getMessage());
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            showLog("onServiceDisconnected");
        }
    };

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        createGLRenderer();

        bindToService();
    }

    private void createGLRenderer(){
        mRendererHandler = new RenderHandler(this);
        mRenderer = new VideoRenderAndRecorder(mRendererHandler, sVideoEncoder);
    }

    private void bindToService(){
        ComponentName cn = new ComponentName("com.jypark.glsurfaceandremoteservice", "com.jypark.glsurfaceandremoteservice.VideoRecordService");
        Intent intent = new Intent();
        intent.setComponent(cn);


        requireActivity().bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private final Handler replyHandler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case MSG_SET_SURFACE_TEXTURE:
                    showLog("MSG_SET_SURFACE_TEXTURE");

                    break;

            }
        }
    };

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_video_call, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        initViewBinding(view);

        setGlSurfaceView();

        setSize();
        
        imageButton.setOnClickListener(v->{
            
        });
    }

    private void initViewBinding(View view){
        glSurfaceView = view.findViewById(R.id.gl_surface_view);
        imageButton = view.findViewById(R.id.ib_recording);
        aspectRatioFrameLayout = view.findViewById(R.id.aspect_frame_layout);
    }

    private void setGlSurfaceView(){
        glSurfaceView.setEGLContextClientVersion(2);     // select GLES 2.0
        glSurfaceView.setRenderer(mRenderer);
        glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }

    private void setSize(){
        RECORD_WIDTH = isLandscape()? 640 : 480;
        RECORD_HEIGHT = isLandscape()? 480 : 640;

        DisplayMetrics metrics = getResources().getDisplayMetrics();
        VIDEO_HEIGHT = metrics.heightPixels;
        VIDEO_WIDTH = metrics.widthPixels;

    }

    @Override
    public void onResume() {
        super.onResume();
        if(mCamera==null) openCamera();

        glSurfaceView.onResume();
    }

    private void openCamera() {
        if (mCamera != null) {
            throw new RuntimeException("camera already initialized");
        }

        Camera.CameraInfo info = new Camera.CameraInfo();

        // Try to find a front-facing camera (e.g. for videoconferencing).
        int numCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numCameras; i++) {
            Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                mCamera = Camera.open(i);
                break;
            }
        }
        if (mCamera == null) {
            showLog("No front-facing camera found; opening default");
            mCamera = Camera.open();    // opens first back-facing camera
        }
        if (mCamera == null) {
            throw new RuntimeException("Unable to open camera");
        }

        Camera.Parameters parms = mCamera.getParameters();

        CameraUtils.choosePreviewSize(parms, VIDEO_WIDTH, VIDEO_HEIGHT);

        // Give the camera a hint that we're recording video.  This can have a big
        // impact on frame rate.
        parms.setRecordingHint(true);

        // leave the frame rate set to default
        mCamera.setParameters(parms);

        int[] fpsRange = new int[2];
        Camera.Size mCameraPreviewSize = parms.getPreviewSize();
        parms.getPreviewFpsRange(fpsRange);

        int mCameraPreviewWidth = mCameraPreviewSize.width;
        int mCameraPreviewHeight = mCameraPreviewSize.height;

        if(!isLandscape()){
            showLog("Portrait");
            mCamera.setDisplayOrientation(90);
            aspectRatioFrameLayout.setAspectRatio((float) mCameraPreviewHeight / mCameraPreviewWidth);
        }
        else{
            showLog("Landscape");
            mCamera.setDisplayOrientation(0);

            aspectRatioFrameLayout.setAspectRatio((float) mCameraPreviewWidth / mCameraPreviewHeight);
        }

    }

    private boolean isLandscape(){
        int rotation = requireActivity().getWindowManager().getDefaultDisplay().getRotation();
        return rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270;
    }

    @Override
    public void onPause() {
        super.onPause();

    }

    private void releaseCamera() {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
            showLog("releaseCamera -- done");
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mRendererHandler.invalidateHandler();     // paranoia

        releaseCamera();

        glSurfaceView.queueEvent(new Runnable() {
            @Override public void run() {
                // Tell the renderer that it's about to be paused so it can clean up.
                mRenderer.notifyPausing();
            }
        });
        glSurfaceView.onPause();
    }

    public void showLog(String msg){
        String TAG = "VideoCallFragment";
        Log.d("확인", TAG + " -> " + msg);
    }

    private void handleSetSurfaceTexture(SurfaceTexture st) {
        st.setOnFrameAvailableListener(this);
        try {
            mCamera.setPreviewTexture(st);
        } catch (IOException ioe) {
            throw new RuntimeException(ioe);
        }
        mCamera.startPreview();
    }

    @Override
    public void onFrameAvailable(SurfaceTexture st) {
        glSurfaceView.requestRender();
    }

    public static class RenderHandler extends Handler{

        // Weak reference to the Activity; only access this from the UI thread.
        private final WeakReference<VideoCallFragment> mWeakFragment;

        public RenderHandler(VideoCallFragment fragment) {
            mWeakFragment = new WeakReference<VideoCallFragment>(fragment);
        }
        /**
         * Drop the reference to the activity.  Useful as a paranoid measure to ensure that
         * attempts to access a stale Activity through a handler are caught.
         */
        public void invalidateHandler() {
            mWeakFragment.clear();
        }

        @Override  // runs on UI thread
        public void handleMessage(Message inputMessage) {
            int what = inputMessage.what;
            VideoCallFragment fragment = mWeakFragment.get();
            if (fragment == null) {
                return;
            }
            switch (what){
                case MSG_SET_SURFACE_TEXTURE:
                    fragment.handleSetSurfaceTexture((SurfaceTexture) inputMessage.obj);
                    break;
                case MSG_SURFACE_CHANGE:
//                    fragment.setGLSurfaceSizeChange(inputMessage.arg1, inputMessage.arg2);
                    break;
                default:
                    throw new RuntimeException("unknown msg " + what);
            }
        }
    }

    @Override
    public void onPictureInPictureModeChanged(boolean isPip) {
        super.onPictureInPictureModeChanged(isPip);
        showLog("onPictureInPictureModeChanged isPip:" + isPip);

        if(isPip){
            imageButton.setVisibility(View.INVISIBLE);
        }
        else{
            imageButton.setVisibility(View.VISIBLE);
        }
    }

}

清单.xml

<activity android:name=".MainActivity"
        android:supportsPictureInPicture="true"
        android:launchMode="singleTask"
        android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
        />

如何使屏幕在 PIP 模式下看起来保持原始比例?

4

0 回答 0