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

利用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);
...
摄像头显示
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342