Xuggler"无法写标题"错误

时间:2014-05-18 22:07:58

标签: java screenshot screen-capture xuggler xuggle

我正在使用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字节,文件将无法播放。我真的可以使用一些帮助,因为我不知道这些错误甚至意味着什么,所以很难调试它们。谢谢!

1 个答案:

答案 0 :(得分:0)

我们可能会因为不尊重本网站的Q& A格式而遇到麻烦,所以我只想列出一些你可以尝试的东西。

  • 当您没有专门致电writeHeader()时,也会引发您对标题的错误。您还收到有关“缺少流”的错误。这表明Xuggler缺少正确添加视频流和打开编写器所需的一些信息。因此,首先调试Java应用程序,找出导致错误的特定行。

  • 此外,尝试在编写之前将每个输出帧渲染为JFrame。这样您就可以验证要写的BufferedImages是否具有正确的内容。

  • 你写的文件是MP4,这将使Xuggler对所需的输出参数得出几个结论,但你最好不要依赖它。尝试自行设置像素格式比特率帧率时基。这就是为什么建议你使用缓冲区:你可以以特定的间隔写帧,这将保证正确的帧速率。您现在设置它的方式将导致可变帧速率,某些编解码器和容器将无法理解。缓冲区的类型不相关,您甚至可以使用ArrayList。但自然有些数据结构会比其他数据结构更有效。

  • 有些编解码器和文件容器比其他容器更宽容。因此,尝试其他一些编解码器,例如H.264。您也可以尝试更改文件容器,但MP4通常在Xuggler中正常工作。

  • 这与您当前的问题无关,因为这与输出文件为空无关。但是你应该注意你编写帧的时间戳。第一个视频帧应位于时间戳0,但由于您在相同的while - 循环中捕获和编码,因此第一帧将具有更高的时间戳。此外,当您暂停时,您的应用程序将不会写任何帧,但时间戳仍将增加。当您稍后恢复录制时,这会在您的视频中产生“漏洞”,这是一个没有任何视频数据的小时间窗口。