Question

I'm using VideoSurfaceView to render filtered video. I'm doing it buy changing the fragment shader according to my needs. Now I would like to save/render the video after the changes to a file of the same format(Ex. mp4 - h264) but couldn't find how to do it.

PS - saving texture as bitmap and the bitmap to a file is easy but I could find how to do it with videos..

Any experts here?

Was it helpful?

Solution

As you already found out and said in the comments, OpenGL can't export multiple frames as a video.

Though if you simply want to filter/process each frame of a video, then you don't need OpenGL at all, and you don't need a Fragment Shader, you can simply loop through all the pixels yourself.

Now let's say that you process your video one frame at a time, and each frame is a BufferedImage, you can of course use whatever you want or get provided with, as long as you have the option to get and set pixels.

I'm simply supplying you with a way of calculating and applying a filter, you will have to do the decoding and encoding of the video file yourself.

But back to the BufferedImage, first we want to get all the pixels in our BufferedImage, we do that using the following.

BufferedImage bi = ...; // Here you would get a frame from the video

int width = bi.getWidth();
int height = bi.getHeight();

int[] pixels = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData();

Be aware that depending on the type of image and if the image contains transparency, the DataBuffer might vary between a DataBufferInt to DataBufferByte, etc. You can read about the different DataBuffers in the Oracle Docs, click here.

Now simply by looping through the pixels from the image, then we can apply and create any kind of effect and filtering.

Let's say we want to create a grayscale effect also called a black-and-white effect, you would then do that by the following.

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

        final int pixel = pixels[index];

        final int alpha = (pixel >> 24) & 0xFF;
        final int red = (pixel >> 16) & 0xFF;
        final int green = (pixel >> 8) & 0xFF;
        final int blue = pixel & 0xFF;

        final int gray = (red + green + blue) / 3;

        pixels[index] = alpha << 24 | gray << 16 | gray << 8 | gray;
    }
}

Now you can simply save the image again, or do anything else you would like to do. Though you can also use and draw the BufferedImage, because the pixel array provided by the BufferedImage will of course change the BufferedImage as well.

Important if you want to perform a blur effect, then after you calculate each pixel store it into another array, because performing a blur effect, requires the surrounding pixels. Therefore it you replace the old once while you calculate all the pixels, some of the pixels will use the calculated values instead of the actual value.

The above code also works for images as well of course.

Extra

If you want to get RGBA values which is stored in a single int then you can do the following.

int pixel = 0xFFFF8040; // This is a random testing value

int alpha = (pixel >> 24) & 0xFF; // Would equal 255 using the testing value
int red = (pixel >> 16) & 0xFF; // ... 255 ...
int green = (pixel >> 8) & 0xFF; // ... 128 ...
int blue = pixel & 0xFF; // ... 64 ...

Then if you have the RGBA values and want to combine them to a single int then you can do the following.

int alpha = 255;
int red = 255;
int green = 128;
int blue = 64;

int pixel = alpha << 24 | red << 16 | green << 8 | blue;

If you only have the RGB values then you just do, either red << 16 | green << 8 | blue or you do 255 << 24 | red << 16 | green << 8 | blue

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top