美文网首页Android开发经验谈Android技术知识Android开发
Android音视频(二)预览摄像头画面

Android音视频(二)预览摄像头画面

作者: zhangke3016 | 来源:发表于2019-05-16 11:53 被阅读58次

    利用OpenGL生成纹理并绑定到SurfaceTexture上,然后把Camera的预览数据设置到SurfaceTexture中,OpenGL拿到摄像头数据并显示出来。

    1. 顶点与片元着色器

    片元着色器:

    #extension GL_OES_EGL_image_external:require
    precision mediump float;
    varying vec2 a_position;
    uniform samplerExternalOES vTexture;
    void main(){
        gl_FragColor = texture2D(vTexture,a_position);
    }
    
    1. 注意前面要加 #extension GL_OES_EGL_image_external:require声明。
    2. 之前的sampler2D 变成了 samplerExternalOES

    顶点着色器:

    attribute vec4 v_position;
    attribute vec2 f_position;
    varying vec2 a_position;
    void main(){
        gl_Position = v_position;
        a_position = f_position;
    }
    

    2. 创建Program

            mProgram = ShaderUtil.createProgram(ShaderUtil.getRawResource(mContext, R.raw.demo2_vertex_shader),
                    ShaderUtil.getRawResource(mContext, R.raw.demo2_fragment_shader));
            if (mProgram == 0){
                throw new RuntimeException("create program fail");
            }
            v_position = GLES20.glGetAttribLocation(mProgram, "v_position");
            f_positon = GLES20.glGetAttribLocation(mProgram, "f_position");
    

    3. 生成并绑定纹理

            int[] textures = new int[1];
            GLES20.glGenTextures(1, textures, 0);
            textureId = textures[0];
    
            GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
    
            GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
            GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
            GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
            GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
            //创建Camera预览的SurfaceTexture
            mSurfaceTexture = new SurfaceTexture(textureId);
            if (mOnSurfaceCreateListener != null){
                mOnSurfaceCreateListener.onSurfaceCreate(mSurfaceTexture);
            }
    
            GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
    

    需要注意使用的是GLES11Ext.GL_TEXTURE_EXTERNAL_OES:

    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
    

    在创建纹理后我们将纹理id作为SurfaceTexture的构造参数传入,并通过回调通知外层处理Camera:

      mSurfaceTexture = new SurfaceTexture(textureId);
     if (mOnSurfaceCreateListener != null){
             mOnSurfaceCreateListener.onSurfaceCreate(mSurfaceTexture);
      }
    

    4. 刷新显示

      //将纹理图像更新为图像流中的最新帧。只有在拥有纹理的OpenGL ES上下文在调用线程上是最新的时,才      可以调用此方法。
    //它将隐式地将其纹理绑定到GL_TEXTURE_EXTERNAL_OES纹理目标
           mSurfaceTexture.updateTexImage();
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
            GLES20.glClearColor(1.0f, 0, 0, 1f);
    
            GLES20.glUseProgram(mProgram);
    
            GLES20.glEnableVertexAttribArray(v_position);
            GLES20.glVertexAttribPointer(v_position, 2, GLES20.GL_FLOAT, false, 8, mVertexBuffer);
    
            GLES20.glEnableVertexAttribArray(f_positon);
            GLES20.glVertexAttribPointer(f_positon, 2, GLES20.GL_FLOAT, false, 8, mFragmentBuffer);
    
            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
            GLES20.glBindTexture(mProgram, 0);
    

    注意要先调用纹理图像更新为图像流中的最新帧。:

    mSurfaceTexture.updateTexImage();
    

    外部GLSurfaceView处理逻辑:

    public Demo2SurfaceView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setEGLContextClientVersion(2);
            Demo2Renderer demo2Renderer = new Demo2Renderer(context);
            //Camera 管理类
            mDemo2Camera = new Demo2Camera(context);
            demo2Renderer.setOnSurfaceCreateListener(new Demo2Renderer.OnSurfaceCreateListener() {
                @Override
                public void onSurfaceCreate(SurfaceTexture surfaceTexture) {
                    mDemo2Camera.initCamera(surfaceTexture, cameraId);
                    //注册当一个新的图像帧可用于SurfaceTexture时要调用的回调。
                    surfaceTexture.setOnFrameAvailableListener(Demo2SurfaceView.this);
                }
            });
            setRenderer(demo2Renderer);
            setRenderMode(RENDERMODE_WHEN_DIRTY);
        }
    
        public void onDestory() {
            if (mDemo2Camera != null){
                mDemo2Camera.stopPreview();
            }
        }
    
        @Override
        public void onFrameAvailable(SurfaceTexture surfaceTexture) {
            //手动请求刷新
            requestRender();
        }
    

    再来看下这个Demo2Camera管理类:

        public Demo2Camera(Context context){
            this.width = DisplayUtil.getScreenWidth(context);
            this.height = DisplayUtil.getScreenHeight(context);
        }
    
        public void initCamera(SurfaceTexture surfaceTexture,int cameraId){
            this.mSurfaceTexture = surfaceTexture;
            setCameraParm(cameraId);
        }
    
        private void setCameraParm( int cameraId) {
            try {
                mCamera = Camera.open(cameraId);
               //将SurfaceTexture设置为预览surface
                mCamera.setPreviewTexture(mSurfaceTexture);
    
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setFlashMode("off");
                parameters.setPreviewFormat(ImageFormat.NV21);
    
                Camera.Size size = getFitSize(parameters.getSupportedPictureSizes());
                parameters.setPictureSize(size.width, size.height);
    
                size = getFitSize(parameters.getSupportedPreviewSizes());
                parameters.setPreviewSize(size.width, size.height);
                
                mCamera.setParameters(parameters);
                //开始预览
                mCamera.startPreview();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        private Camera.Size getFitSize(List<Camera.Size> sizes) {
            if(width < height) {
                int t = height;
                height = width;
                width = t;
            }
    
            for(Camera.Size size : sizes) {
                if(1.0f * size.width / size.height == 1.0f * width / height) {
                    return size;
                }
            }
            return sizes.get(0);
        }
    

    关键处理逻辑在:

    mCamera.setPreviewTexture(mSurfaceTexture);
    

    此时将Camera、SurfaceTexture、OpenGL连接在一起了,看下预览的画面。


    显示图像

    what? 现在显示怎么是这样的?方向歪到姥姥家了,咳咳,那下面开始调整方向。

    5. 调整预览方向

    改造顶点着色器:

    attribute vec4 v_position;
    attribute vec2 f_position;
    uniform mat4 uMatrix;
    varying vec2 a_position;
    void main(){
        gl_Position = v_position * uMatrix;
        a_position = f_position;
    }
    

    增加矩阵:

    uniform mat4 uMatrix;
    

    Renderer构造方法中置空矩阵:

    Matrix.setIdentityM(matrix, 0);
    

    查找到矩阵对应着色器中的id:

     uMatrix = GLES20.glGetUniformLocation(mProgram, "uMatrix");
    
    @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
            setAngle(-90, 0, 0, 1);
            setAngle(180, 1, 0, 0);
            setAngle(180, 0, 1, 0);
        }
    
        public void setAngle(float angle, float x, float y, float z){
            Matrix.rotateM(matrix, 0, angle, x, y, z);
        }
    

    Matrix.rotateM(matrix, o, a, x, y, z):
    a:
    正数:逆时针旋转
    负数:顺时针旋转
    x、y、z:分别表示相应坐标轴

    矩阵赋值:

    ...
    GLES20.glUseProgram(mProgram);
    GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0);
    ...
    
    摄像头显示

    相关文章

      网友评论

        本文标题:Android音视频(二)预览摄像头画面

        本文链接:https://www.haomeiwen.com/subject/bsijaqtx.html