那里!我尝试显示视频流,该视频流来自服务器,为字节数组。
这个数组中的数据是h264编码的图像,我用bytedeco javacpp-presets库以这种方式对其进行解码:

public class DMDecoder {

private static final String LOG_TAG = "DMDecoder";

private AVCodec avCodec;
private AVCodecContext avCodecContext;
private AVFrame avFrame;
private AVPacket avPacket;
private boolean wasIFrame;
private long IFrameTimeStampMs;
private int maxFps;
private int codecId;

private DMDecoderCallback callback;

public DMDecoder(DMDecoderCallback cb) {
    this.callback = cb;
    this.codecId = AV_CODEC_ID_H264;
    avcodec_register_all();
    restart();
}

public void restart() {
    stop();
    start();
}

public void stop() {
    frames = 0;
    if (avCodecContext != null) {
        avcodec_close(avCodecContext);
        avcodec_free_context(avCodecContext);
        avCodecContext = null;
    }

    if (avCodec != null) {
        av_free(avCodec);
        avCodec = null;
    }

    if (avFrame != null) {
        av_frame_free(avFrame);
        avFrame = null;
    }

    if (avPacket != null) {
        av_free_packet(avPacket);
        avPacket = null;
    }
}

public void start() {
    avCodec = avcodec_find_decoder(codecId);

    avCodecContext = avcodec_alloc_context3(avCodec);
    AVDictionary opts = new AVDictionary();
    avcodec_open2(avCodecContext, avCodec, opts);

    avFrame = av_frame_alloc();
    avPacket = new AVPacket();
    av_init_packet(avPacket);
}

public VideoFrame decode(byte[] data, int dataOffset, int dataSize) {
    avPacket.pts(AV_NOPTS_VALUE);
    avPacket.dts(AV_NOPTS_VALUE);
    avPacket.data(new BytePointer(data).position(dataOffset));
    avPacket.size(dataSize);
    avPacket.pos(-1);

    IntBuffer gotPicture = IntBuffer.allocate(1);

    int processedBytes = avcodec_decode_video2(
            avCodecContext, avFrame, gotPicture, avPacket);

    if (avFrame.width() == 0 || avFrame.height() == 0) return null;

    VideoFrame frame = new VideoFrame();

   frame.colorPlane0 = new byte[avFrame.width() * avFrame.height()];
   frame.colorPlane1 = new byte[avFrame.width() / 2 * avFrame.height() / 2];
   frame.colorPlane2 = new byte[avFrame.width() / 2 * avFrame.height() / 2];

    if (avFrame.data(0) != null) avFrame.data(0).get(frame.colorPlane0);
    if (avFrame.data(1) != null) avFrame.data(1).get(frame.colorPlane1);
    if (avFrame.data(2) != null) avFrame.data(2).get(frame.colorPlane2);

    frame.lineSize0 = avFrame.width();
    frame.lineSize1 = avFrame.width() / 2;
    frame.lineSize2 = avFrame.width() / 2;

    frame.width = avFrame.width();
    frame.height = avFrame.height();

    return frame;
  }
}


VideoFrame类只是简单的POJO:

public class VideoFrame {
    public byte[] colorPlane0;
    public byte[] colorPlane1;
    public byte[] colorPlane2;
    public int lineSize0;
    public int lineSize1;
    public int lineSize2;
    public int width;
    public int height;
    public long presentationTime;
}


解码后,我将此帧发送到我的GLRenderer类

public class GLRenderer implements GLSurfaceView.Renderer {

    private static final String LOG_TAG = "GLRenderer";

    private TexturePlane plane;

    private ConcurrentLinkedQueue<VideoFrame> frames;
    private int maxFps = 30;
    private VideoFrame currentFrame;
    private long startTime, endTime;
    private int viewWidth, viewHeight;
    private boolean isFirstFrameProcessed;

    public GLRenderer(int viewWidth, int viewHeight) {
        frames = new ConcurrentLinkedQueue<>();
        this.viewWidth = viewWidth;
        this.viewHeight = viewHeight;
    }

    // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
    private final float[] mMVPMatrix = new float[16];
    private final float[] mProjectionMatrix = new float[16];
    private final float[] mViewMatrix = new float[16];

    @Override

    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // Set the background frame color
        GLES20.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);

        plane = new TexturePlane();
    }

    public void setMaxFps(int maxFps) {
        this.maxFps = maxFps;
    }

    @Override
    public void onDrawFrame(GL10 unused) {


        // Draw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        // Set the camera position (View matrix)
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

        // Calculate the projection and view transformation
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

        if (!isFirstFrameProcessed) checkViewPort(viewWidth, viewHeight);

        if (maxFps > 0 && startTime > 0) {
            endTime = System.currentTimeMillis();
            long time = endTime - startTime;
            //
            long wantedTime = 1000 / maxFps;
            //
            long wait;
            if (time < wantedTime) {
                wait = wantedTime - time;
                //
                try {
                    Thread.sleep(wait);
                } catch (InterruptedException e) {
                    Log.e(LOG_TAG, "thread interrupted exception");
                }
            }
        }
        startTime = System.currentTimeMillis();
        tick();
        plane.draw(mMVPMatrix);
    }

    private void updateFrame(VideoFrame frame) {
        plane.updateTexture(frame.colorPlane0, frame.width, frame.height, 0);
        plane.updateTexture(frame.colorPlane1, frame.width / 2, frame.height / 2, 1);
        plane.updateTexture(frame.colorPlane2, frame.width / 2, frame.height / 2, 2);
        plane.setTextureWidth(frame.width);
        plane.setTextureHeight(frame.height);
    }

    private void tick() {

        if (frames.isEmpty()) return;

        VideoFrame frame = frames.peek();
        if (frame == null) return;

        long tms = System.currentTimeMillis();
        if (frame.presentationTime <= tms) {
            updateFrame(frame);
            currentFrame = frame;
            frames.remove(frame);
        }
    }

    @Override
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        checkViewPort(width, height);
        viewWidth = width;
        viewHeight = height;
        plane.setTextureWidth(width);
        plane.setTextureHeight(height);
    }

    private void checkViewPort(int width, int height) {
        float viewRatio = (float) width / height;
        if (currentFrame != null) {
            float targetRatio = (float) currentFrame.width / currentFrame.height;
            int x, y, newWidth, newHeight;
            if (targetRatio > viewRatio) {
                newWidth = width;
                newHeight = (int) (width / targetRatio);
                x = 0;
                y = (height - newHeight) / 2;
            } else {
                newHeight = height;
                newWidth = (int) (height * targetRatio);
                y = 0;
                x = (width - newWidth) / 2;
            }
            GLES20.glViewport(x, y, newWidth, newHeight);
        } else {
            GLES20.glViewport(0, 0, width, height);
        }

        Matrix.frustumM(mProjectionMatrix, 0, 1, -1, -1, 1, 3, 4);
    }

    public void addFrame(VideoFrame frame) {
        if (frame != null) {
            frames.add(frame);
        }
    }
}


GLRenderer使用简单的openGL多边形,在上面绘制所有纹理

    public class TexturePlane {

    private static final String LOG_TAG = "TexturePlane";

    private final String vertexShaderCode = "" +
    "uniform mat4 uMVPMatrix;" +
    "attribute vec4 vPosition;" +
    "attribute vec2 a_TexCoordinate;" +
    "varying vec2 v_TexCoordinate;" +

    "void main() {" +
    "  gl_Position = uMVPMatrix * vPosition;" +
    "  v_TexCoordinate = a_TexCoordinate;" +
    "}";

    private final String fragmentShaderCode = "" +
    "precision mediump float;" +
    "varying vec2 v_TexCoordinate;" +
    "uniform sampler2D s_texture_y;" +
    "uniform sampler2D s_texture_u;" +
    "uniform sampler2D s_texture_v;" +

    "void main() {" +
    "   float y = texture2D(s_texture_y, v_TexCoordinate).r;" +
    "   float u = texture2D(s_texture_u, v_TexCoordinate).r - 0.5;" +
    "   float v = texture2D(s_texture_v, v_TexCoordinate).r - 0.5;" +

    "   float r = y + 1.13983 * v;" +
    "   float g = y - 0.39465 * u - 0.58060 * v;" +
    "   float b = y + 2.03211 * u;" +

    "   gl_FragColor = vec4(r, g, b, 1.0);" +

    "}";

    private final FloatBuffer vertexBuffer;
    private final FloatBuffer textureBuffer;
    private final ShortBuffer drawListBuffer;
    private final int mProgram;
    private int mPositionHandle;
    private int mMVPMatrixHandle;

        // number of coordinates per vertex in this array
    private static final int COORDS_PER_VERTEX = 3;
    private static final int COORDS_PER_TEXTURE = 2;

    private static float squareCoords[] = {
        -1f, 1f, 0.0f,
        -1f, -1f, 0.0f,
        1f, -1f, 0.0f,
        1f, 1f, 0.0f
    };

    private static float uvs[] = {
        0.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
        1.0f, 0.0f
    };

    private final short drawOrder[] = {0, 1, 2, 0, 2, 3}; // order to draw vertices
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

    private int textureWidth = 640;
    private int textureHeight = 480;

    private int yTextureUniformHandle;
    private int uTextureUniformHandle;
    private int vTextureUniformHandle;

    private int yTextureHandle;
    private int uTextureHandle;
    private int vTextureHandle;

    private int mTextureCoordinateHandle;

    public void setTextureWidth(int textureWidth) {
        this.textureWidth = textureWidth;
    }

    public int getTextureWidth() {
        return textureWidth;
    }

    public void setTextureHeight(int textureHeight) {
        this.textureHeight = textureHeight;
    }

    public int getTextureHeight() {
        return textureHeight;
    }

    /**
     * Sets up the drawing object data for use in an OpenGL ES context.
     */
    public TexturePlane() {
            // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

            // initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);

            // initialize byte buffer for the draw list
        ByteBuffer tbb = ByteBuffer.allocateDirect(uvs.length * 4);
        tbb.order(ByteOrder.nativeOrder());
        textureBuffer = tbb.asFloatBuffer();
        textureBuffer.put(uvs);
        textureBuffer.position(0);

            mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
            compileShaders();
            setupTextures();
        }

        public void setupTextures() {
            yTextureHandle = setupTexture(null, textureWidth, textureHeight, 0);
            uTextureHandle = setupTexture(null, textureWidth, textureHeight, 1);
            vTextureHandle = setupTexture(null, textureWidth, textureHeight, 2);
        }

        public int setupTexture(ByteBuffer data, int width, int height, int index) {
            final int[] textureHandle = new int[1];

            GLES20.glGenTextures(1, textureHandle, 0);

            if (textureHandle[0] != 0) {
                    // Bind to the texture in OpenGL
                GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + index);
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);

                updateTexture(data, width, height, index);

                    // Set filtering
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);

                    // Set wrapping mode
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
                GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
            }

            if (textureHandle[0] == 0) {
                Log.e(LOG_TAG, "Error loading texture.");
            }

            return textureHandle[0];
        }

        public void updateTexture(byte[] data, int width, int height, int index) {

            if (data == null) {
                if (width == 0 || height == 0) {
                    width = textureWidth;
                    height = textureHeight;
                }

                data = new byte[width * height];
                if (index == 0) {
                    Arrays.fill(data, y);
                } else if (index == 1) {
                    Arrays.fill(data, u);
                } else {
                    Arrays.fill(data, v);
                }
            }

            byteBuffer.wrap(data);
            byteBuffer.position(0);

            GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + index);

            GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
                width, height, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, byteBuffer);
        }

        private void compileShaders() {
            // prepare shaders and OpenGL program
            int vertexShader = loadShader(
                GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
            int fragmentShader = loadShader(
                GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

            GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
            GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
            GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables
            checkGlError("glLinkProgram");

            // Add program to OpenGL environment
            GLES20.glUseProgram(mProgram);

            mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
            mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TexCoordinate");

            GLES20.glEnableVertexAttribArray(mPositionHandle);
            GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);

            yTextureUniformHandle = GLES20.glGetUniformLocation(mProgram, "s_texture_y");
            uTextureUniformHandle = GLES20.glGetUniformLocation(mProgram, "s_Texture_u");
            vTextureUniformHandle = GLES20.glGetUniformLocation(mProgram, "s_Texture_v");

            mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
            checkGlError("glGetUniformLocation");
        }

    /**
     * Utility method for compiling a OpenGL shader.
     * <p/>
     * <p><strong>Note:</strong> When developing shaders, use the checkGlError()
     * method to debug shader coding errors.</p>
     *
     * @param type       - Vertex or fragment shader type.
     * @param shaderCode - String containing the shader code.
     * @return - Returns an id for the shader.
     */
    public int loadShader(int type, String shaderCode) {

            // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
            // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

            // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }

    /**
     * Utility method for debugging OpenGL calls. Provide the name of the call
     * just after making it:
     * <p/>
     * <pre>
     * mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
     * MyGLRenderer.checkGlError("glGetUniformLocation");</pre>
     *
     * If the operation is not successful, the check throws an error.
     *
     * @param glOperation - Name of the OpenGL call to check.
     */
    public void checkGlError(String glOperation) {
        int error;
        String errorString;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            errorString = GLU.gluErrorString(error);
            String message = glOperation + ": glError " + error + ": " + errorString;
            Log.e(LOG_TAG, message);
            throw new RuntimeException(message);
        }
    }

    public void draw(float[] mvpMatrix) {

            // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(
            mPositionHandle, COORDS_PER_VERTEX,
            GLES20.GL_FLOAT, false,
            vertexStride, vertexBuffer);

        GLES20.glVertexAttribPointer(
            mTextureCoordinateHandle, COORDS_PER_TEXTURE,
            GLES20.GL_FLOAT, false,
            0, textureBuffer);

        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
        checkGlError("glUniformMatrix4fv");

        GLES20.glUniform1i(yTextureUniformHandle, 0);
        GLES20.glUniform1i(uTextureUniformHandle, 1);
        GLES20.glUniform1i(vTextureUniformHandle, 2);

            // Draw the square
        GLES20.glDrawElements(
            GLES20.GL_TRIANGLES, drawOrder.length,
            GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
    }
}


但是我在那里有一个问题。我的GL表面显示颜色错误的图像。 image

我做错了什么?

更新:

作为Ronald S. Bultje say,我在代码中添加了glBindTexture(...)函数。现在updateTexture(...)方法如下所示:

public void updateTexture(byte[] data, int width, int height, int index) {

    if (data == null) {
        if (width == 0 || height == 0) {
            width = textureWidth;
            height = textureHeight;
        }

        data = new byte[width * height];
        if (index == 0) {
            Arrays.fill(data, y);
        } else if (index == 1) {
            Arrays.fill(data, u);
        } else {
            Arrays.fill(data, v);
        }
    }

    byteBuffer.wrap(data);
    byteBuffer.position(0);

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + index);

    int textureHandle = index == 0 ? yTextureHandle : index == 1 ? uTextureHandle : vTextureHandle;
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle);

    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
        width, height, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, byteBuffer);
}

最佳答案

您的updateTexture()函数不会调用GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureHandle [index]);调用GLES20.glActiveTexture(GLES20.GL_TEXTURE0 +索引)之后;

[edit]实际上给出了您的代码,它应该是index == 0?yTextureHandle:index == 1?uTextureHandle?vTextureHandle,我敢肯定您可以弄清楚如何重构您的代码来简化此工作。

关于java - Android bytedeco javacpp ffmpeg将h264字节解码为yuv,并使用openGL ES 2.0进行渲染。颜色错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30628126/

10-10 23:07