Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 739
Roman Irtov Avatar asked Dec 01 '25 06:12

Roman Irtov


1 Answers

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

like image 167
Roman Irtov Avatar answered Dec 03 '25 20:12

Roman Irtov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!