Android使用OpengGL Es2.0技术(GLSurfaceView含缩放功能)yuv视频流展示总结

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/Android_Technology/article/details/52143291

因为项目需求的更改,不得不将以前用SurfaceView的监控视频显示更改为Openg GL ES2.0技术实现,通过使用GLSurfaceView来展示监控视频流的显示。首先在这里感谢 @李狗蛋52635 通过他提供的资料让我成功的完成了这一项技术,这里给出他写的Android上使用OpenGLES2.0显示YUV数据
文章链接 http://blog.csdn.net/ueryueryuery/article/details/17608185

项目流程分析:我调用了底层JNI封装了的视频流,通过底层传递过来的yuv在java层使用Openg GL ES2.0技术将监控实时视频绘制出来。简单的流程就是这样,下面我将提供技术实现的代码。
其中通过OpengGlES2.0显示yuv数据的代码我就不提供了,按照我给出了狗蛋兄的文章一步一步来完全能够实现功能,下面我主要说一些关于使用OpengGLES的一些注意事项和缩放功能的实现。
1.视频显示出来的时候记得在onSurfaceChanged中调用update这个方法来适配视频在手机上的显示界面,否则可能会视频太大或太小。
    /**
     * this method will be called from native code, it happens when the video is about to play or
     * the video size changes.
     */
    public void update(int w, int h) {
        if (w > 0 && h > 0) {
            // 调整比例
            if (mScreenWidth > 0 && mScreenHeight > 0) {
                float f1 = 1f * mScreenHeight / mScreenWidth;
                float f2 = 1f * h / w;
                if (f1 == f2) {
                    prog.createBuffers(GLProgram.squareVertices);
                } else if (f1 < f2) {
                    float widScale = f1 / f2;
                    prog.createBuffers(new float[]{-widScale, -1.0f, widScale, -1.0f, -widScale, 1.0f, widScale,
                            1.0f,});
                } else {
                    float heightScale = f2 / f1;
                    prog.createBuffers(new float[]{-1.0f, -heightScale, 1.0f, -heightScale, -1.0f, heightScale, 1.0f,
                            heightScale,});
                }
            }
            // 初始化容器
            if (w != mVideoWidth && h != mVideoHeight) {
                this.mVideoWidth = w;
                this.mVideoHeight = h;
                int yarraySize = w * h;
                int uvarraySize = yarraySize / 4;
                synchronized (this) {
                    y = ByteBuffer.allocate(yarraySize);
                    u = ByteBuffer.allocate(uvarraySize);
                    v = ByteBuffer.allocate(uvarraySize);
                }
            }
        }
    }

2.另外如果有切换横竖屏的项目需求的话,可以通过新建一个方法msurfaceChangeing,在其中实现对视屏流的重新调整。在Renderer的onSurfaceChanged方法中调用这个方法但是传递的参数恰好将width和height对调即可,例如 msurfaceChangeing(height,width);

 /**
     * 横竖屏切换界面适应
     * @param w
     * @param h
     */
    private void msurfaceChangeing(int w,int h){
        // 调整比例
        if (mScreenWidth > 0 && mScreenHeight > 0) {
            float f1 = 1f * mScreenHeight / mScreenWidth;
            float f2 = 1f * h / w;
            if (f1 == f2) {
                prog.createBuffers(GLProgram.squareVertices);
            } else if (f1 < f2) {
                float widScale = f1 / f2;
                prog.createBuffers(new float[]{-widScale, -1.0f, widScale, -1.0f, -widScale, 1.0f, widScale,
                        1.0f,});
            } else {
                float heightScale = f2 / f1;
                prog.createBuffers(new float[]{-1.0f, -heightScale, 1.0f, -heightScale, -1.0f, heightScale, 1.0f,
                        heightScale,});
            }
        }

3.在更新GLSurface数据的时候需要根据对应的底层RGB格式转换调用我们Java层的yuv的转换,因为我们产品的底层是RGB565所以我们的转换方法则是:

    /**
     * 更新GlSufaceView数据
     */
    public void update(byte[] src, int w, int h) {
        synchronized (this) {
            y.clear();
            u.clear();
            v.clear();
            y.put(src, 0, w*h);
            u.put(ArrayUtil.copyOfRange(src,w * h,w * h* 5 / 4), 0, w*h/4);
            v.put(ArrayUtil.copyOfRange(src,w * h * 5 / 4,w * h * 3 / 2), 0, w*h/4);
        }
        // request to render
        mTargetSurface.requestRender();
    }

4.最后是实现视频的缩放功能,别小看这个功能,为了实现这个功能我可纠结了好久555….差点都要使用texture来重构代码了,后来终于在查询资料的过程中找到了他http://www.2cto.com/kf/201410/345619.html,虽然提供的并不是视频流而是图片,但是给自己提供了一个很好的思路以及计算方法。
核心方法代码并不多,贴出来

    private int height = 0;
    private long frameSeq = 0;
    private int viewportOffset = 0;
    private int maxOffset = 400;
    private int width = 0;
    //通过改变gl的视角获取
    private void changeGLViewport(GL10 gl) {
        System.out.println("time=" + System.currentTimeMillis());
        frameSeq++;
        viewportOffset++;
        // The
        // Current
        if (frameSeq % 100 == 0) {// 每隔100帧,重置
            gl.glViewport(0, 0, width, height);
            viewportOffset = 0;
        } else {
            int k = 4;
            gl.glViewport(-maxOffset + viewportOffset * k, -maxOffset+ viewportOffset * k, this.width - viewportOffset * 2 * k+ maxOffset * 2, this.height - viewportOffset * 2 * k+ maxOffset * 2);
     }
 }

注意,这个方法中的width和heigh是在onSurfaceChanged中赋值的,另外这个方法是在onDrawFrame onDrawFrame onDrawFrame中调用的哦,重要的事情说三遍!
不过这个只能是让图像自动的放大,其中k的正负能够决定图像是放大还是缩小,如果想要实现手指的控制我的解决思路是通过设定变量值来改变对应k值以及viewportOffset 值的大小实现对图像的控制,如果有更好的实现方式欢迎大家的讨论哦。

猜你喜欢

转载自blog.csdn.net/Android_Technology/article/details/52143291