#Video decoding using JCodec

1 messages · Page 1 of 1 (latest)

low meadow
#

After a long while of headscratching, I managed to put together a video decoder for format MP4, however I am quite far off of what it's supposed to look like.

The bright blue image is what it's currently looking like, and the regular one is what it's supposed to look like. I don't know where it goes wrong in terms of decoding, but I am 90% sure it has to do with how I decode the video.

public class VideoPlayer extends Screen {
    private final File videoFile;
    private final BlockingQueue<NativeImage> frameQueue = new LinkedBlockingQueue<>(100);
    private DynamicTexture dynamicTexture;
    private ResourceLocation textureLocation;

    public VideoPlayer(ResourceLocation resourceLocation) {
        super(Component.literal("Video player"));
        this.videoFile = this.convertToFile(resourceLocation);
        new Thread(this::decodeFrames).start();
    }

    @Override
    public void render(@NotNull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
        NativeImage frame;

        while ((frame = this.frameQueue.poll()) != null) {
            if (this.dynamicTexture == null) {
                this.dynamicTexture = new DynamicTexture(frame);
                this.textureLocation = Minecraft.getInstance().getTextureManager().register("video_player_texture", this.dynamicTexture);
            } else {
                this.dynamicTexture.setPixels(frame);
                this.dynamicTexture.upload();
            }

            frame.close();
        }

        if (this.dynamicTexture != null && this.textureLocation != null) {
            NativeImage pixels = this.dynamicTexture.getPixels();

            if (pixels != null) {
                guiGraphics.blit(this.textureLocation, 0, 0, this.width, this.height, 0, 0, pixels.getWidth(), pixels.getHeight(), pixels.getWidth(), pixels.getHeight());
            }
        }
    }

    @Override
    public void onClose() {
        if (this.dynamicTexture != null) {
            this.dynamicTexture.close();
            this.dynamicTexture = null;
        }

        this.frameQueue.forEach(NativeImage::close);
        this.frameQueue.clear();
        super.onClose();
    }

    private void decodeFrames() {
        try {
            FrameGrab frameGrab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(this.videoFile));
            Picture picture;

            while ((picture = frameGrab.getNativeFrame()) != null) {
                int width = picture.getWidth();
                int height = picture.getHeight();
                NativeImage nativeImage = new NativeImage(width, height, false);
                byte[] rPlane = picture.getPlaneData(0);
                byte[] gPlane = picture.getPlaneData(1);
                byte[] bPlane = picture.getPlaneData(2);

                for (int y = 0; y < height; y++) {
                    for (int x = 0; x < width; x++) {
                        int idx = y * width + x;

                        int r = rPlane[idx] & 0xFF;
                        int g = gPlane[idx] & 0xFF;
                        int b = bPlane[idx] & 0xFF;

                        int argb = (255 << 24) | (r << 16) | (g << 8) | b;
                        nativeImage.setPixelRGBA(x, y, argb);
                    }
                }

                this.frameQueue.put(nativeImage);
            }
        } catch (JCodecException | IOException | InterruptedException ignored) {
            throw new RuntimeException("There was an error playing the video!");
        }
    }
}
fresh shuttleBOT
#

<@&987246652869971988> please have a look, thanks.

low meadow
#

I also tried messing around with the values inside my decoding, but nothing really worked out, either breaking it more or just shifting the colour away from blue to like yellow or green

white furnace
#

it doesn't feel like a straight "you did color math wrong" thing

#

but maybe you got the order of argb wrong?

low meadow
#

I did mess around with that for a while when my video still had one plane, I could try to toy around with it, give me on moment

magic birch
#

It look like you do not have all the colors.

low meadow
#

It does not have to do with missing colors, printing the color gets me the actual value of whatever the pixel is and they do set it to the 'correct' color. Swapping the ARGB value around to for example RGBA shifts the color to for example red or yellow

#

trying YUV values makes worsens it so I don't think It's an incorrect colorspace either

#

Okay, quick update on this, manually setting the colors to 0xFF000000, 0, 0, 0 makes it opaque black, which is correct so it definitely has to do with the color outputted

thick patio
#

That image is YUV

#

You have to decode those to RGB it looks like

#

and U and V are half size in both directions

#

You also have to figure out which primaries to use

#

And what colorspace it actually is

viral moth
#

I hope that that code performs well

thick patio
#

YUV, YCbCr, BT709 etc

low meadow