我正在使用Xuggler制作一个屏幕录制应用程序。我基本上将Java Code Geeks的Xuggler教程代码封装到一个可运行的类中进行实际录制。它应该像教程一样运行,但我得到一些(实际上是很多)错误。该代码的链接位于:JavaCodeGeeks。我并不想把这整段代码都归功于它。
这是我到目前为止所做的:
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.concurrent.TimeUnit;
import javax.swing.JOptionPane;
import src.dtf.gui.GUI;
import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.xuggler.ICodec;
public class ScreenRecorder implements Runnable {
//Booleans to run and to pause. (Pausing not implemented yet)
boolean running = true;
boolean paused = false;
//Some variables
private GUI gui;
private Toolkit tk;
private String path, name, outputFilename;
private int fps;
private long startTime;
private Rectangle recArea;
private Dimension bounds;
//Declare the MediaWriter
private IMediaWriter writer;
//Constructor
public ScreenRecorder(GUI gui) {
//Set the GUI to the one that I'm using (Another class
this.gui = gui;
//Initialize variables, based on previous user input.
tk = Toolkit.getDefaultToolkit();
path = gui.getPath();
name = JOptionPane.showInputDialog("Please enter a name for your video file:");
outputFilename = path + "\\" + name + ".mp4";
fps = gui.getFPS();
if (gui.fullscreenChecked()) {
recArea = new Rectangle(0, 0, tk.getScreenSize().width,
tk.getScreenSize().height);
} else {
recArea = gui.getArea();
}
bounds = new Dimension(recArea.width, recArea.height);
}
//Start method
public void start() {
gui.disableButtons();
gui.changeRecordButton(false);
running = true;
}
//Run method
public void run() {
//Initialize
init();
long lastTime = System.currentTimeMillis();
int updateTime = 1000 / fps;
startTime = System.nanoTime();
while (running) {
//Limit updates
if (System.currentTimeMillis() - lastTime >= updateTime) {
//Ensure the recording is not paused
if (!paused) {
//If the user has stopped, stop
if (!gui.isRecording()) {
stop();
}
//Take a screenshot and convert it
BufferedImage frame = takeScreenshot();
BufferedImage bgrScreen = convertImage(frame, BufferedImage.TYPE_3BYTE_BGR);
//Encode video
writer.encodeVideo(0, bgrScreen, System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
System.out.println("Recording...");
} else if (paused) {
System.out.println("Paused...");
}
}
}
}
private void init() {
//Make sure the given directory exists
checkFile();
//Ensure there is not already a file of the same name
checkFilename();
//Make the writer
writer = ToolFactory.makeWriter(outputFilename);
writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_MPEG4, bounds.width, bounds.height);
}
//Method for checking if the directory exists
private void checkFile() {
if (!(new File(path).exists())) {
gui.resetPath();
JOptionPane.showMessageDialog(gui, "ERROR: File path does not exist!");
System.out.println("ERRR");
stop();
}
}
//Method for checking if the given filename exists
private void checkFilename() {
if (new File(path + "\\" + name + ".mp4").exists()) {
JOptionPane.showMessageDialog(gui, "ERROR: File already exists!!");
stop();
}
}
//Method for converting the BufferedImage (Thanks JavaCodeGeeks)
private BufferedImage convertImage(BufferedImage b, int targetType) {
BufferedImage image;
if (b.getType() == targetType) {
image = b;
} else {
image = new BufferedImage(b.getWidth(), b.getHeight(), targetType);
image.getGraphics().drawImage(b, 0, 0, null);
}
return image;
}
//Method for taking a screenshot
private BufferedImage takeScreenshot() {
try {
Robot r = new Robot();
return r.createScreenCapture(recArea);
} catch (AWTException e) {
e.printStackTrace();
return null;
}
}
//Stop method
public void stop() {
gui.enableButtons();
gui.changeRecordButton(true);
//Make sure the writer has been initialized. (Not an incorrect filename or anything)
if (writer != null) {
//Close the writer
writer.close();
}
//End thread
running = false;
}
}
这是抛出的错误:
17:46:48.076 [Thread-2] ERROR org.ffmpeg - [mp4 @ 000000000028F660] no streams
17:46:48.123 [Thread-2] ERROR com.xuggle.xuggler - Error: could not write header for container (../../../../../../../csrc/com/xuggle/xuggler/Container.cpp:827)
我尝试通过在stop方法中添加isHeaderWritten()if语句来修复它,但是根本不会调用它,所以它必须在其他地方(或在if语句中)。我不知道我的代码中的哪一行会引发错误,因为它只给我这两个错误,这些错误指向Xuggler,而不是我的代码。执行此操作时,它会创建一个mp4文件,但其大小为0字节,文件将无法播放。我真的可以使用一些帮助,因为我不知道这些错误甚至意味着什么,所以很难调试它们。谢谢!
答案 0 :(得分:0)
我们可能会因为不尊重本网站的Q& A格式而遇到麻烦,所以我只想列出一些你可以尝试的东西。
当您没有专门致电writeHeader()
时,也会引发您对标题的错误。您还收到有关“缺少流”的错误。这表明Xuggler缺少正确添加视频流和打开编写器所需的一些信息。因此,首先调试Java应用程序,找出导致错误的特定行。
此外,尝试在编写之前将每个输出帧渲染为JFrame
。这样您就可以验证要写的BufferedImages
是否具有正确的内容。
你写的文件是MP4,这将使Xuggler对所需的输出参数得出几个结论,但你最好不要依赖它。尝试自行设置像素格式,比特率,帧率和时基。这就是为什么建议你使用缓冲区:你可以以特定的间隔写帧,这将保证正确的帧速率。您现在设置它的方式将导致可变帧速率,某些编解码器和容器将无法理解。缓冲区的类型不相关,您甚至可以使用ArrayList
。但自然有些数据结构会比其他数据结构更有效。
有些编解码器和文件容器比其他容器更宽容。因此,尝试其他一些编解码器,例如H.264。您也可以尝试更改文件容器,但MP4通常在Xuggler中正常工作。
这与您当前的问题无关,因为这与输出文件为空无关。但是你应该注意你编写帧的时间戳。第一个视频帧应位于时间戳0,但由于您在相同的while
- 循环中捕获和编码,因此第一帧将具有更高的时间戳。此外,当您暂停时,您的应用程序将不会写任何帧,但时间戳仍将增加。当您稍后恢复录制时,这会在您的视频中产生“漏洞”,这是一个没有任何视频数据的小时间窗口。