我设法通过拍摄一系列屏幕截图并使用ffmpeg将其转换为视频(ffmpeg为android编译并包含所有*所以断言,将它们全部复制到data / data / my.package /并执行ffmpeg)从那里)
但主要问题是截屏对屏幕渲染有很大影响,当应用程序执行这行代码时它会冻结一段时间(~0.1秒):
Gdx.gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixels);
我试图从另一个帖子中截取屏幕截图,但我只获得黑色屏幕截图。为什么???
您是否知道更有效的截屏方式?
答案 0 :(得分:3)
屏幕截图是一项相当昂贵的任务,当您想将其转换为视频时更是如此。
glReadPixels方法是从显示缓冲区读回像素值的方法。这是你想要的;然而,限制因素将是GPU的带宽,特别是对于移动设备。我建议创建一个分辨率较低的FrameBuffer(可能是宽度/高度的一半),这样可以减少带宽需求。然后,您可以在应用程序处于活动状态时将其绘制到该缓冲区,然后读回像素值。 Libgdx提供了一个便捷类,用于回读ScreenUtils类中的像素值。 (这些方法使用glReadPixels进行读取)。
即使这样,它也可能不适用于低端设备,特别是如果您考虑以每秒60帧的速度从设备录制视频并主动渲染复杂场景。但是,如果其他人知道在Android设备上有效地执行此操作的方法,我会非常有兴趣看到更好的解决方案。
要回答您的其他问题,您无法同时访问OpenGL上下文,而应仅从呈现线程执行此操作。这就是为什么你要获得多线程的黑色截图。
修改强>
要跟进有关如何导出到PNG的问题,我有您可能使用的代码:
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;
}
答案 1 :(得分:0)
您好我设法拍摄了@ 30fps的视频,或者我甚至可以从我的想法中获得@ 60 fps的视频,我的视频非常流畅
如果有人参与其中,请检查一下。
这个想法很简单,我们希望每帧的屏幕截图为60fps或29.97fps(我的视频)。
完成3个步骤: 1)将所有内容渲染为1/60作为屏幕的增量时间
2)拍摄每一帧的屏幕
3)制作所有png文件的视频(我使用adobe premiere pro)
步骤1)在libgdx中我扩展了Game类并创建了一个新类MyGame extends Game 或类MyGame扩展ApplicationListener 现在更改其临时使用的渲染方法
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();
}
}
步骤2)使用ScreenShotUtils类的捕获方法捕获屏幕截图 当您需要捕捉游戏中的按钮以开始和停止录制时调用捕获方法
//call on start click
screenShotUtils.capture(true);
//call on stop click
screenShotUtils.capture(false);
注意:当你开始捕捉时游戏会滞后,它会超级慢,但是渲染增量时间不会让ui更新低于60 fps,你会得到60 fps的截图,现在玩游戏在此延迟期间,您将在视频发布期间获得平滑的图像序列
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)我使用adobe premiere pro进行视频制作,你可以使用其他工具搜索google(将图像序列转换为视频)
https://helpx.adobe.com/premiere-pro/using/importing-still-images.html
部分:将图像导入为图像序列
注意:我使用格式RGB而非RGBA的屏幕截图,因为alpha位正在弄乱图像的边框。你可以试试吗
你可以修改代码将文件保存到android sdcard我使用的是桌面版,只需更改saveScreenshot方法的文件句柄
免责声明:我使用了screnshot代码并在本网站http://www.wendytech.de/2012/07/opengl-screen-capture-in-real-time/
中对其进行了修改