camera2配合TextureView打开相机 关闭camera后清空最后一帧

描述:

打开相机后,关闭相机,TextureView会残留最后一帧的画面。

正常关闭相机代码:

private void closeCamera() {
	if (cameraManager != null){
		cameraManager = null;
	}
	flashAvailable = false;
	try {
		mCameraOpenCloseLock.acquire();
		if (null != mCaptureSession) {
			mCaptureSession.close();
			mCaptureSession = null;
		}
		if (null != mCameraDevice) {
			mCameraDevice.close();
			mCameraDevice = null;
		}
		if (camera2Listener != null) {
			camera2Listener.onCameraClosed();
		}
	} catch (InterruptedException e) {
		if (camera2Listener != null) {
			camera2Listener.onCameraError(e);
		}
	} finally {
		mCameraOpenCloseLock.release();
	}
}

关闭相机后,UI显示上会残留最后一帧,网上找了很多方法,都不好使。

方法一:

Canvas canvas = mCamera2.lockCanvas();
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
mCamera2.unlockCanvasAndPost(canvas);

用了此方法,确实可以清空残留,但是再次打开相机的时候就显示黑屏。故而此方法不适合。

方式二:使用GLES 来清除:

参考连接:

https://stackoverflow.com/questions/25660994/clear-video-frame-from-surfaceview-on-video-complete

https://github.com/autumnqin/grafika/blob/master/src/com/android/grafika/PlayMovieSurfaceActivity.java

实现代码:

import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLExt;
import android.opengl.EGLSurface;
import android.opengl.GLES20;


/**
* Clears the playback surface to black.
*/
private void clearSurface(Surface surface) {
	EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
	int[] version = new int[2];
	EGL14.eglInitialize(display, version, 0, version, 1);

	int[] attribList = {
			EGL14.EGL_RED_SIZE, 8,
			EGL14.EGL_GREEN_SIZE, 8,
			EGL14.EGL_BLUE_SIZE, 8,
			EGL14.EGL_ALPHA_SIZE, 8,
			EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
			EGL14.EGL_NONE, 0,
			EGL14.EGL_NONE
	};
	EGLConfig[] configs = new EGLConfig[1];
	int[] numConfigs = new int[1];
	EGL14.eglChooseConfig(display, attribList, 0, configs, 0, configs.length, numConfigs, 0);

	EGLConfig config = configs[0];
	EGLContext context = EGL14.eglCreateContext(display, config, EGL14.EGL_NO_CONTEXT, new int[]{
			EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
			EGL14.EGL_NONE
	}, 0);

	EGLSurface eglSurface = EGL14.eglCreateWindowSurface(display, config, surface,
			new int[]{
				EGL14.EGL_NONE
			}, 0);

	EGL14.eglMakeCurrent(display, eglSurface, eglSurface, context);
	GLES20.glClearColor(0, 0, 0, 1);
	GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
	EGL14.eglSwapBuffers(display, eglSurface);
	EGL14.eglDestroySurface(display, eglSurface);
	EGL14.eglMakeCurrent(display, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
	EGL14.eglDestroyContext(display, context);
	EGL14.eglTerminate(display);
}

调用方式:

1. 清空TextureView最后一帧,
clearSurface(new Surface(getSurfaceTexture()));

2. 清空surfaceview最后一帧,
clearSurface(getHolder().getSurface());

关于在SurfaceViewRenderer中的清空最后一帧画面的使用方式:

/*
 *  Copyright 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

package org.webrtc;

import android.content.Context;
import android.content.res.Resources.NotFoundException;
import android.graphics.Point;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.rtc_sdk.RenderFrameDoneCallback;

import java.util.concurrent.CountDownLatch;

/**
 * Implements org.webrtc.VideoRenderer.Callbacks by displaying the video stream on a SurfaceView.
 * renderFrame() is asynchronous to avoid blocking the calling thread.
 * This class is thread safe and handles access from potentially four different threads:
 * Interaction from the main app in init, release, setMirror, and setScalingtype.
 * Interaction from C++ rtc::VideoSinkInterface in renderFrame.
 * Interaction from the Activity lifecycle in surfaceCreated, surfaceChanged, and surfaceDestroyed.
 * Interaction with the layout framework in onMeasure and onSizeChanged.
 */
public class SurfaceViewRenderer
        extends SurfaceView implements SurfaceHolder.Callback, VideoRenderer.Callbacks {
    private static final String TAG = "SurfaceViewRenderer";

    // Cached resource name.
    private final String resourceName;
    private final RendererCommon.VideoLayoutMeasure videoLayoutMeasure =
            new RendererCommon.VideoLayoutMeasure();
    private final EglRenderer eglRenderer;

    // Callback for reporting renderer events. Read-only after initilization so no lock required.
    private RendererCommon.RendererEvents rendererEvents;

    private final Object layoutLock = new Object();
    private boolean isFirstFrameRendered;
    private int rotatedFrameWidth;
    private int rotatedFrameHeight;
    private int frameRotation;

    private int frames = 0;
    private int fps = 0;
    private long lastTime = System.currentTimeMillis();
    private long lastRenderTime = 0;

    public RenderFrameDoneCallback getRenderFrameDoneCallback() {
        return renderFrameDoneCallback;
    }

    public void setRenderFrameDoneCallback(RenderFrameDoneCallback renderFrameDoneCallback) {
        this.renderFrameDoneCallback = renderFrameDoneCallback;
    }

    private RenderFrameDoneCallback renderFrameDoneCallback;


    /**
     * Standard View constructor. In order to render something, you must first call init().
     */
    public SurfaceViewRenderer(Context context) {
        super(context);
        this.resourceName = getResourceName();
        eglRenderer = new EglRenderer(resourceName);
        getHolder().addCallback(this);
    }

    /**
     * Standard View constructor. In order to render something, you must first call init().
     */
    public SurfaceViewRenderer(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.resourceName = getResourceName();
        eglRenderer = new EglRenderer(resourceName);
        getHolder().addCallback(this);
    }

    /**
     * Initialize this class, sharing resources with |sharedContext|. It is allowed to call init() to
     * reinitialize the renderer after a previous init()/release() cycle.
     */
    public void init(EglBase.Context sharedContext, RendererCommon.RendererEvents rendererEvents) {
        init(sharedContext, rendererEvents, EglBase.CONFIG_PLAIN, new GlRectDrawer());
    }

    /**
     * Initialize this class, sharing resources with |sharedContext|. The custom |drawer| will be used
     * for drawing frames on the EGLSurface. This class is responsible for calling release() on
     * |drawer|. It is allowed to call init() to reinitialize the renderer after a previous
     * init()/release() cycle.
     */
    public void init(final EglBase.Context sharedContext,
                     RendererCommon.RendererEvents rendererEvents, final int[] configAttributes,
                     RendererCommon.GlDrawer drawer) {
        ThreadUtils.checkIsOnMainThread();
        this.rendererEvents = rendererEvents;
        isInit = true;
        synchronized (layoutLock) {
            rotatedFrameWidth = 0;
            rotatedFrameHeight = 0;
            frameRotation = 0;
        }
        eglRenderer.init(sharedContext, configAttributes, drawer);
    }

    private boolean isInit = false;//是否调用了init函数

    /**
     * Block until any pending frame is returned and all GL resources released, even if an interrupt
     * occurs. If an interrupt occurs during release(), the interrupt flag will be set. This function
     * should be called before the Activity is destroyed and the EGLContext is still valid. If you
     * don't call this function, the GL resources might leak.
     */
    public void release() {
        //TODO 推流不需要init,调用clearSurface可以设置黑屏。播流的时候需要init,调用eglRenderer.clearImage()可以设置黑屏
        if (!isInit) {
            clearSurface(getHolder().getSurface());
        } else {
            eglRenderer.clearImage();
        }
        eglRenderer.release();
        isInit = false;
    }

    /**
     * 关闭camera的时候,清空TextureView最后一帧,设置颜色为黑色(再次打开相机可正常显示出画面)
     */
    private void clearSurface(Surface surface) {
        EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        int[] version = new int[2];
        EGL14.eglInitialize(display, version, 0, version, 1);
        int[] attribList = {
                EGL14.EGL_RED_SIZE, 8,
                EGL14.EGL_GREEN_SIZE, 8,
                EGL14.EGL_BLUE_SIZE, 8,
                EGL14.EGL_ALPHA_SIZE, 8,
                EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
                EGL14.EGL_NONE, 0,
                EGL14.EGL_NONE
        };
        EGLConfig[] configs = new EGLConfig[1];
        int[] numConfigs = new int[1];
        EGL14.eglChooseConfig(display, attribList, 0, configs, 0, configs.length, numConfigs, 0);

        EGLConfig config = configs[0];
        EGLContext context = EGL14.eglCreateContext(display, config, EGL14.EGL_NO_CONTEXT, new int[]{
                EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL14.EGL_NONE
        }, 0);
        EGLSurface eglSurface = EGL14.eglCreateWindowSurface(display, config, surface,
                new int[]{
                        EGL14.EGL_NONE
                }, 0);
        EGL14.eglMakeCurrent(display, eglSurface, eglSurface, context);
        GLES20.glClearColor(0, 0, 0, 1);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        EGL14.eglSwapBuffers(display, eglSurface);
        EGL14.eglDestroySurface(display, eglSurface);
        EGL14.eglMakeCurrent(display, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
        EGL14.eglDestroyContext(display, context);
        EGL14.eglTerminate(display);
    }

    /**
     * Set if the video stream should be mirrored or not.
     */
    public void setMirror(final boolean mirror) {
        eglRenderer.setMirror(mirror);
    }

    public void setLayoutAspectRatio(float layoutAspectRatio) {
        eglRenderer.setLayoutAspectRatio(layoutAspectRatio);
    }

    /**
     * Set how the video will fill the allowed layout area.
     */
    public void setScalingType(RendererCommon.ScalingType scalingType) {
        ThreadUtils.checkIsOnMainThread();
        videoLayoutMeasure.setScalingType(scalingType);
    }

    public void setScalingType(RendererCommon.ScalingType scalingTypeMatchOrientation,
                               RendererCommon.ScalingType scalingTypeMismatchOrientation) {
        ThreadUtils.checkIsOnMainThread();
        videoLayoutMeasure.setScalingType(scalingTypeMatchOrientation, scalingTypeMismatchOrientation);
    }

    // VideoRenderer.Callbacks interface.
    @Override
    public void renderFrame(VideoRenderer.I420Frame frame) {
        if (getRenderFrameDoneCallback() != null) {
            renderFrameDoneCallback.done();
            renderFrameDoneCallback = null;
        }
        //旋转
        // frame.rotationDegree = 270;
        updateFrameDimensionsAndReportEvents(frame);
        eglRenderer.renderFrame(frame);

        //liuhongxian
        frames++;
        long current = System.currentTimeMillis();
        lastRenderTime = current;
        if (current - lastTime >= 1000) {
            fps = frames * 1000 / (int) (current - lastTime);
            frames = 0;
            lastTime = current;
        }
    }

    public int getFps() {
        if (System.currentTimeMillis() - lastRenderTime > 1000) {
            fps = 0;
        }
        return fps;
    }

    // View layout interface.
    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        ThreadUtils.checkIsOnMainThread();
        final Point size;
        synchronized (layoutLock) {
            size =
                    videoLayoutMeasure.measure(widthSpec, heightSpec, rotatedFrameWidth, rotatedFrameHeight);
        }
        setMeasuredDimension(size.x, size.y);
        logD("onMeasure(). New size: " + size.x + "x" + size.y);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        ThreadUtils.checkIsOnMainThread();
        eglRenderer.setLayoutAspectRatio((right - left) / (float) (bottom - top));
    }

    // SurfaceHolder.Callback interface.
    @Override
    public void surfaceCreated(final SurfaceHolder holder) {
        ThreadUtils.checkIsOnMainThread();
        eglRenderer.createEglSurface(holder.getSurface());
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        ThreadUtils.checkIsOnMainThread();
        final CountDownLatch completionLatch = new CountDownLatch(1);
        eglRenderer.releaseEglSurface(new Runnable() {
            @Override
            public void run() {
                completionLatch.countDown();
            }
        });
        ThreadUtils.awaitUninterruptibly(completionLatch);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        ThreadUtils.checkIsOnMainThread();
        eglRenderer.surfaceSizeChanged(width, height);
    }

    private String getResourceName() {
        try {
            return getResources().getResourceEntryName(getId()) + ": ";
        } catch (NotFoundException e) {
            return "";
        }
    }

    // Update frame dimensions and report any changes to |rendererEvents|.
    private void updateFrameDimensionsAndReportEvents(VideoRenderer.I420Frame frame) {
        synchronized (layoutLock) {
            if (!isFirstFrameRendered) {
                isFirstFrameRendered = true;
                logD("Reporting first rendered frame.");
                if (rendererEvents != null) {
                    rendererEvents.onFirstFrameRendered();
                }
            }
            if (rotatedFrameWidth != frame.rotatedWidth() || rotatedFrameHeight != frame.rotatedHeight()
                    || frameRotation != frame.rotationDegree) {
                logD("Reporting frame resolution changed to " + frame.width + "x" + frame.height
                        + " with rotation " + frame.rotationDegree);
                if (rendererEvents != null) {
                    rendererEvents.onFrameResolutionChanged(frame.width, frame.height, frame.rotationDegree);
                }
                rotatedFrameWidth = frame.rotatedWidth();
                rotatedFrameHeight = frame.rotatedHeight();
                frameRotation = frame.rotationDegree;
                post(new Runnable() {
                    @Override
                    public void run() {
                        requestLayout();
                    }
                });
            }
        }
    }

    private void logD(String string) {
        Logging.d(TAG, resourceName + string);
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
要预览摄像头角度,可以通过设置TextureView的transform matrix来实现。首先,需要获取当前设备方向和相机传感器方向的差异,可以使用以下代码: ```java int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); int deviceOrientation = getWindowManager().getDefaultDisplay().getRotation(); int surfaceRotation = ORIENTATIONS.get(deviceOrientation); int rotation = (sensorOrientation - surfaceRotation + 360) % 360; ``` 其中,characteristics是当前相机设备的CameraCharacteristics实例,ORIENTATIONS是一个常量数组,包含了设备方向和Surface方向之间的映射关系。 接下来,根据计算出的rotation值,设置TextureView的transform matrix: ```java Matrix matrix = new Matrix(); RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth()); float centerX = viewRect.centerX(); float centerY = viewRect.centerY(); if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); float scale = Math.max( (float) viewHeight / previewSize.getHeight(), (float) viewWidth / previewSize.getWidth()); matrix.postScale(scale, scale, centerX, centerY); matrix.postRotate(90 * (rotation - 2), centerX, centerY); } else if (rotation == Surface.ROTATION_180) { matrix.postRotate(180, centerX, centerY); } textureView.setTransform(matrix); ``` 上述代码中,viewWidth和viewHeight分别是TextureView的宽高,previewSize是预览尺寸。根据当前方向的不同,使用不同的变换方式设置transform matrix。最后,将变换矩阵应用到TextureView上即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值