Screen recording within android using libgdx or not

点点圈 提交于 2021-02-19 04:48:07

问题


I managed to do this by taking a series of screenshots and convert them to video using ffmpeg (ffmpeg compiled for android and include all *so to asserts, copy them all to to data/data/my.package/ and execute ffmpeg from there)

But the main problem is the taking screenshots have big impact on screen rendering, it freezes a while (~0.1sec) when the app is executing this line of code:

Gdx.gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixels);

I tried to take screenshots from another thread, but i only get black screenshots. Why???

Do you know a more efficient way of taking screenshots?


回答1:


Screen capturing is a fairly expensive task, and more so when you want to turn it into a video.

The glReadPixels method is the method for reading back pixel values from the display buffer. Which is what you want; however, the limiting factor will be bandwidth to/from the GPU, especially for mobile devices. I would suggest creating a FrameBuffer with a lower resolution (maybe half your width/height) so that you can reduce the bandwidth requirements. You would then draw your app to that buffer while it is active and then read back the pixel values. Libgdx provides a convenience class for reading back the pixel values in the ScreenUtils class. (These methods use glReadPixels to do the reading).

Even then, it is likely to not work well on lower-end devices especially if you are thinking of recording video from the device at 60 frames a second AND actively rendering a complex scene. However, if someone else knows a way to efficiently do this on android devices I would be very interested in seeing a better solution.

To answer your other question, you can't access the OpenGL context concurrently and should be doing it only from the rendering thread. This is why you are getting black screenshots with multiple threads.

Edit:

To follow up with your question about how to export to a PNG I have code for that which you may use:

public boolean export(final FileHandle target, final Graphics g) {
        boolean error = false;
        final Pixmap picture = new Pixmap(g.getWidth(), g.getHeight(), Format.RGBA8888);
        final FrameBuffer buffer = new FrameBuffer(Format.RGBA8888, g.getWidth(), g.getHeight(), false);
        try {
            Gdx.graphics.getGL20().glViewport(0, 0, g.getWidth(), g.getHeight());
            buffer.begin();
            g.render(); // Or however you normally draw it
            final byte[] data = this.readData(g.getWidth(), g.getHeight());
            buffer.end();
            picture.getPixels().put(data, 0, data.length);
            PixmapIO.writePNG(target, picture);
        } catch (final Exception e) {
            e.printStackTrace();
            error = true;
        } finally {
            picture.dispose();
            buffer.dispose();
        }
        return error;
    }

    // Adapted from ScreenUtil class
    public byte[] readData(final int width, final int height) {
        final int numBytes = width * height * 4;
        final ByteBuffer pixels = BufferUtils.newByteBuffer(numBytes);
        Gdx.gl.glPixelStorei(GL20.GL_PACK_ALIGNMENT, 1);
        Gdx.gl.glReadPixels(0, 0, width, height, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, pixels);

        final byte[] lines = new byte[numBytes];
        final int numBytesPerLine = width * 4;
        for (int i = 0; i < height; i++) {
            pixels.position((height - i - 1) * numBytesPerLine);
            pixels.get(lines, i * numBytesPerLine, numBytesPerLine);
        }

        return lines;
    }



回答2:


Hello I managed to take a video @ 30fps or we can even get a video @60 fps from my idea and my video is super smooth

If anybody is intereseted please check this out.

The idea is simple we want screen shot of every frame with a 60fps or 29.97fps(my video).

Done in 3 steps: 1) render everything at 1/60 as delta time of screen

2) take screenhots of every frame

3) make a video of all those png files(i use adobe premiere pro)

Step 1) in libgdx i extend the Game class and make a new class MyGame extends Game or class MyGame extends ApplicationListener now change its render method for temporary use

ScreenShotUtils screenShotUtils=new ScreenShotUtils();
@Override
    public void render () {
        //if (screen != null) screen.render(Gdx.graphics.getDeltaTime());
        //write your video fps as 1f/29.97f or 1f/30f
        if (screen != null){
            screen.render(1f/60f);
            screenShotUtils.update();
        }
    }

Step 2) capture screenshots using the capture method of ScreenShotUtils class call the capture method when you need to capture just make a button in your game to start and stop recording

//call on start click
screenShotUtils.capture(true);
//call on stop click
screenShotUtils.capture(false);

Note: when you will start the capture the game will lag, it will be super slow, but the render delta time will not let the ui update less then 60 fps and you will get screenshots at 60 fps, now play the game during this lag and you will have smooth image sequence during video publishing

public class ScreenShotUtils {
    String filefolder="D:/capturefolder";// your folder to put the images in
    String filenameprefix="";
    int frameid=0,captureLimit=-1;
    private boolean isCapturing=false;
    public void setCaptureLimit(int frames){
        captureLimit=frames;
    }
    public boolean isCapturing(){
        return isCapturing;
    }

    public void capture(boolean capture){
        if(capture){
            if(!isCapturing) {
                isCapturing = true;
                filenameprefix="Demo"+System.currentTimeMillis();//Images Prefix
                frameid=0;
            }
        }else{
            isCapturing=false;
            captureLimit=-1;
            frameid=0;
        }
    }
    public void capture(boolean capture, int frames){
        if(capture){
            if(!isCapturing) {
                isCapturing = true;
                filenameprefix="Demo"+System.currentTimeMillis();//Images Prefix
                frameid=0;
            }
            captureLimit=frames;
        }else{
            isCapturing=false;
            captureLimit=-1;
            frameid=0;
        }
    }

    public void update() {

        // render before capturing

        if(isCapturing) {
            if(captureLimit<0)
                saveScreenshot();
            else
                if(captureLimit==0){
                    isCapturing = false;
                    captureLimit=-1;
                }else{
                    saveScreenshot();
                    captureLimit--;
                }
        }
    }
    private void saveScreenshot() {
        int MAX_DIGITS = 6;
        String fname = "" + (frameid++);
        int zeros = MAX_DIGITS - fname.length();
        for (int i = 0; i < zeros; i++) {
            fname = "0" + fname;
        }

        FileHandle file = new FileHandle(filefolder+"/"+filenameprefix +"-"+ fname+".png");
        Pixmap pixmap = getScreenshot(0, 0, Gdx.graphics.getWidth(),
                Gdx.graphics.getHeight(), true);
        PixmapIO.writePNG(file, pixmap);
        pixmap.dispose();
    }
    private Pixmap getScreenshot(int x, int y, int w, int h, boolean flipY) {
        Gdx.gl.glPixelStorei(GL20.GL_PACK_ALIGNMENT, 1);

        Pixmap pixmap = new Pixmap(w, h, Pixmap.Format.RGB888);
        ByteBuffer pixels = pixmap.getPixels();
        Gdx.gl.glReadPixels(x, y, w, h, GL20.GL_RGB, GL20.GL_UNSIGNED_BYTE, pixels);

        if (flipY) {
            final int numBytes = w * h * 3;
            byte[] lines = new byte[numBytes];
            final int numBytesPerLine = w * 3;
            for (int i = 0; i < h; i++) {
                pixels.position((h - i - 1) * numBytesPerLine);
                pixels.get(lines, i * numBytesPerLine, numBytesPerLine);
            }
            pixels.clear();
            pixels.put(lines);
        }     

        return pixmap;
    }
}

3) i used adobe premiere pro for video making, you can use other tools search google(convert image sequence to video) https://helpx.adobe.com/premiere-pro/using/importing-still-images.html Section: Import images as an image sequence

Note: i use screenshots of format RGB not RGBA because the alpha bits were messing with the borders of the images. you can try it

Aditionally you can modify the code to save files to android sdcard i used the desktop version, just change the saveScreenshot method's file handle

Disclaimer: i used the screnshot codes and modified them from this website http://www.wendytech.de/2012/07/opengl-screen-capture-in-real-time/



来源:https://stackoverflow.com/questions/15729338/screen-recording-within-android-using-libgdx-or-not

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!