Search code examples
androidandroid-bitmapandroid-mediacodecbytebufferdecoder

How get an RGB Bitmap by using ByteBuffer (no color correction)?


I have an mp4 video in my sd card

MediaFormat = {
               repeat-previous-frame-after=66666,
               mime=video/avc,
               frame-rate=15,
               color-format=2130708361,
               height=720,
               width=1280,
               bitrate=1000000,
               i-frame-interval=1
    }

If I set Surface in MediaCodec.Decoder configure then image is created correctly.

If I don't and create Bitmap with ByteBuffer I get an incorrect color image.
I tried YuvImage, and method for converting YUV420 to RGB, and ScriptIntrinsicYuvToRGB but can't get a correct bitmap;

I need to create a Bitmap and not setting surface in MediaCodec.Decoder.configure!

private class PlayerThread extends Thread {
    private MediaExtractor extractor;
    private MediaCodec decoder;
    private Surface surface;
    private boolean needStop = false;
    final int TIMEOUT_USEC = 10000;

    PlayerThread(Surface surface) {
        this.surface = surface;
    }


    @Override
    public void run() {
        extractor = new MediaExtractor();
        try {
            extractor.setDataSource(SAMPLE);
        } catch (IOException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < extractor.getTrackCount(); i++) {
            MediaFormat format = extractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("video/")) {
                extractor.selectTrack(i);
                try {
                    decoder = MediaCodec.createDecoderByType(mime);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                decoder.configure(format, /*surface*/ null, null, 0);
                break;
            }
        }
        if (decoder == null) {
            return;
        }
        decoder.start();

        ByteBuffer[] inputBuffers = decoder.getInputBuffers();
        ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        long startMs = System.currentTimeMillis();
        boolean isEOS = false;
        while (!Thread.interrupted() && !needStop) {
            if (!isEOS) {
                int inIndex = -1;
                try {
                    inIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
                } catch (IllegalStateException e) {
                    e.printStackTrace();
                }

                if (inIndex >= 0) {
                    ByteBuffer buffer = inputBuffers[inIndex];
                    int sampleSize = extractor.readSampleData(buffer, 0);
                    if (sampleSize < 0) {
                        if (!needStop) {
                            decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                            isEOS = true;
                        }
                    } else {
                        try {
                            if (!needStop) {
                                decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
                                extractor.advance();
                            }
                        } catch (IllegalStateException e) {
                            e.printStackTrace();
                        }

                    }

                }
            }
            int outIndex = MediaCodec.INFO_TRY_AGAIN_LATER;
            try {
                if (!needStop) {
                    outIndex = decoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
                }
            } catch (IllegalStateException e) {
                e.printStackTrace();
            }

            switch (outIndex) {
                case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                    outputBuffers = decoder.getOutputBuffers();
                    break;
                case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                    break;
                case MediaCodec.INFO_TRY_AGAIN_LATER:
                    break;
                default:
                    ByteBuffer buffer = outputBuffers[outIndex];

                    buffer.position(info.offset);
                    buffer.limit(info.offset + info.size);
                    byte[] ba = new byte[buffer.remaining()];
                    buffer.get(ba);
                    //this i use many algorithm conversion for get bitmap

                    YuvImage yuvimage = new YuvImage(ba, ImageFormat.NV21, 1280, 720, null);
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    yuvimage.compressToJpeg(new Rect(0, 0, 1280, 720), 80, baos);
                    byte[] jdata = baos.toByteArray();
                    final Bitmap bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length);
                    if (bmp != null) {
                        srcRect.left = 0;
                        srcRect.top = 0;
                        srcRect.bottom = 720;
                        srcRect.right = 1280;
                        Canvas canvas = surface.lockCanvas(dstRect);
                        try {
                            if (canvas != null) {
                                canvas.drawBitmap(bmp, srcRect, dstRect, null);
                            }
                        } finally {
                            if (canvas != null) {
                                surface.unlockCanvasAndPost(canvas);
                            }
                        }
                    } else {
                        Log.e(TAG, "bmp = BAD");
                    }

                    while (info.presentationTimeUs / 1000 > System.currentTimeMillis() -
                            startMs && !needStop) {
                        try {
                            sleep(10);
                        } catch (InterruptedException e) {
                            PlayerThread.this.interrupt();
                            e.printStackTrace();
                            break;
                        }
                    }
                    decoder.releaseOutputBuffer(outIndex, false);
                    break;
            }

            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                break;
            }
        }
        decoder.stop();
        decoder.release();
        extractor.release();
    }
}

How to correct the conversion ByteBuffer (decoder) in Bitmap? Auto decoder render in surface

No correct color bitmap


Solution

  • I found solution for my problem. Now i don't use ByteBuffer and get with decoder Image and it help me in may problem

    Now need refactoring code and implement variables such as video Size, surface size in dynamic mode

    Corrected code

      private class PlayerThread extends Thread {
        private MediaExtractor extractor;
        private MediaCodec decoder;
        private Surface surface;
        private boolean needStop = false;
        final int TIMEOUT_USEC = 10000;
    
    
        PlayerThread(Surface surface) {
            this.surface = surface;
        }
    
    
        @Override
        public void run() {
            extractor = new MediaExtractor();
            try {
                extractor.setDataSource(SAMPLE); //path MP4 file
            } catch (IOException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < extractor.getTrackCount(); i++) {
                MediaFormat format = extractor.getTrackFormat(i);
                String mime = format.getString(MediaFormat.KEY_MIME);
                if (mime.startsWith("video/")) {
                    extractor.selectTrack(i);
                    try {
                        decoder = MediaCodec.createDecoderByType(mime);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    decoder.configure(format, /*surface*/ null, null, 0);
                    break;
                }
            }
            if (decoder == null) {
                return;
            }
            decoder.start();
    
            ByteBuffer[] inputBuffers = decoder.getInputBuffers();
            ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            long startMs = System.currentTimeMillis();
            boolean isEOS = false;
            while (!Thread.interrupted() && !needStop) {
                if (!isEOS) {
                    int inIndex = -1;
                    try {
                        inIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
                    } catch (IllegalStateException e) {
                        e.printStackTrace();
                    }
    
                    if (inIndex >= 0) {
                        ByteBuffer buffer = inputBuffers[inIndex];
                        int sampleSize = extractor.readSampleData(buffer, 0);
                        if (sampleSize < 0) {
                            if (!needStop) {
                                decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                                isEOS = true;
                            }
                        } else {
                            try {
                                if (!needStop) {
                                    decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
                                    extractor.advance();
                                }
                            } catch (IllegalStateException e) {
                                e.printStackTrace();
                            }
    
                        }
    
                    }
                }
                int outIndex = MediaCodec.INFO_TRY_AGAIN_LATER;
                try {
                    if (!needStop) {
                        outIndex = decoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
                    }
                } catch (IllegalStateException e) {
                    e.printStackTrace();
                }
    
                switch (outIndex) {
                    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                        outputBuffers = decoder.getOutputBuffers();
                        break;
                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                        break;
                    case MediaCodec.INFO_TRY_AGAIN_LATER:
                        break;
                    default:
                        Image image = decoder.getOutputImage(outIndex);
                        Image.Plane[] plants = image.getPlanes();
                        Bitmap bmp = null;
                        if (plants != null && plants.length > 0) {
                            YuvImage yuvimage = new YuvImage(YUV_420_888toNV21(image), ImageFormat.NV21, 1280, 720, null);
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            yuvimage.compressToJpeg(new Rect(0, 0, 1280, 720), 80, baos);
                            byte[] jdata = baos.toByteArray();
                            bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length);
                        }
    
                        if (bmp != null) {
                            srcRect.left = 0;
                            srcRect.top = 0;
                            srcRect.bottom = 720;
                            srcRect.right = 1280;
                            Canvas canvas = surface.lockCanvas(dstRect);
                            try {
                                if (canvas != null) {
                                    canvas.drawBitmap(bmp, srcRect, dstRect /*0,0, surfaceChanged
                                    dstRect.right = width;
            dstRect.bottom = height;*/,
                                            null);
                                }
                            } finally {
                                if (canvas != null) {
                                    surface.unlockCanvasAndPost(canvas);
                                }
                            }
                        }
    
                        while (info.presentationTimeUs / 1000 > System.currentTimeMillis() -
                                startMs && !needStop) {
                            try {
                                sleep(10);
                            } catch (InterruptedException e) {
                                PlayerThread.this.interrupt();
                                e.printStackTrace();
                                break;
                            }
                        }
                        decoder.releaseOutputBuffer(outIndex, false);
                        break;
                }
    
                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    break;
                }
            }
            decoder.stop();
            decoder.release();
            extractor.release();
        }
    
    }
    private static byte[] YUV_420_888toNV21(Image image) {
        byte[] nv21;
        ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
        ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
        ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
        int ySize = yBuffer.remaining();
        int uSize = uBuffer.remaining();
        int vSize = vBuffer.remaining();
        nv21 = new byte[ySize + uSize + vSize];
        //U and V are swapped
        yBuffer.get(nv21, 0, ySize);
        vBuffer.get(nv21, ySize, vSize);
        uBuffer.get(nv21, ySize + vSize, uSize);
        return nv21;
    }
    

    enter image description here