java - 单独线程中的屏幕捕获导致 Java 应用程序缓慢/无响应

标签 java multithreading performance macos xuggler

我正在开发一个应用程序,该应用程序可以在用户执行某些 Activity 时记录用户屏幕、网络摄像头和麦克风。它将用于研究目的。该应用程序已在 Windows 上成功测试,但在 Mac OS X(使用 Java 7.0.45 的 Maverick)上,应用程序会变得缓慢并且无响应录音开始。

这就是为什么我觉得这很难理解:

  • 录制是在单独的线程中完成的,那么它如何影响另一个线程的响应能力?特别是在每次运行之后,都会调用 Thread.yield()Thread.sleep(...)
  • 日志显示,尝试以 15 FPS 录制时,最终帧率为 2 FPS。所以看来捕获单帧的代码可能太慢了。但为什么它在 Windows 上运行得很好呢?

简单说明一下:该应用程序已在 Windows 上经过大量用户的成功测试,但我只在一台 Mac 上进行了测试。然而,那个刚刚格式化并干净安装了 OS X Maverick、Java(和 Netbeans)。

下面您将找到使用 Xuggler 录制屏幕并将其写入视频的代码。录制网络摄像头的代码是类似的,我怀疑录制音频与它有什么关系。我的问题是:

应用程序无响应的原因可能是什么?

如何提高代码效率并提高 FPS?

IMediaWriter writer = ToolFactory.makeWriter(file.getAbsolutePath());
Dimension size = Globals.sessionFrame.getBounds().getSize();
Rectangle screenRect;
BufferedImage capture;
BufferedImage mousePointImg;


writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_H264, size.width, size.height);

int i = 0;

while (stop == false) {

    // Get mouse cursor to draw over screen image.
    PointerInfo mousePointer = MouseInfo.getPointerInfo();
    Point mousePoint = mousePointer.getLocation();
    Point screenPoint = new Point((int) (mousePoint.getX() - 
        Globals.sessionFrame.getBounds().getX()), (int) (mousePoint.getY() - 
        Globals.sessionFrame.getBounds().getY()));

    // Get the screen image.
    try {
        screenRect = new Rectangle(Globals.sessionFrame.getBounds());
        capture = new Robot().createScreenCapture(screenRect);
    } catch ( ... ) { ... }

    // Convert and resize the screen image.
    BufferedImage image = ConverterFactory.convertToType(capture, 
        BufferedImage.TYPE_3BYTE_BGR);
    IConverter converter = ConverterFactory.createConverter(image, 
        IPixelFormat.Type.YUV420P);

    // Draw the mouse cursor if necessary.
    if (mouseWithinScreen()) {
        Graphics g = image.getGraphics();
        g.drawImage(mousePointImg, (int) screenPoint.getX(), 
            (int) screenPoint.getY(), null);
    }

    // Prepare the frame.
    IVideoPicture frame = converter.toPicture(image, (System.currentTimeMillis() - 
        startTimeMillis()) * 1000);
    frame.setKeyFrame(i % (getDesiredFPS() * getDesiredKeyframeSec()) == 0);

    // Write to the video
    writer.encodeVideo(0, frame);

    // Delay the next capture if we are at the desired FPS.
    try {
        if (atDesiredFPS()) {
            Thread.yield();
        } else {
            Thread.sleep(1000 / getDesiredFPS());
        }
    } catch ( ... ) { ... }

    i++;
}

writer.close();

最佳答案

我在您的代码中看到了几个架构问题:

首先,如果您想以固定速率执行某些操作,请使用 ScheduledThreadPoolExecutor.scheduleAtFixedRate(...)功能。它将使您的整个延迟代码部分过时,并确保某些操作系统计时问题不会干扰您的调度。

然后,为了让事情变得更快,您需要稍微分解一下代码。据我所知,你有 3 个任务:捕获、鼠标绘图/转换和流写入。如果将捕获部分放入调度的 Runnable 中,将其作为 Callables 转换为多并行执行到 Executor 中,然后在第 3 个线程中从结果列表中取出结果并将其写入流中,则可以充分利用多并行执行核心。

伪代码:

全局声明(或将它们交给各个类):

final static Executor converterExecutor = Executors.newFixedThreadPoolExecutor(Runtime.getRuntime().availableProcessors());
final static LinkedBlockingQueue<Future<IVideoPicture>> imageQueue = new LinkedBlockingQueue<>();
// ...

捕获可运行(按固定速率安排):

capture = captureScreen();
final Converter converter = new Converter(capture);
final Future<IVideoPicture> conversionResult = converterExecutor.submit(converter);
imageQueue.offer(conversionResult); // returns false if queue is full

可调用转换:

class Converter implements Callable<IVideoPicture> {
  // ... variables and constructor

  public IVideoPicture call() {
    return convert(this.image);
  }
}

编写器可运行:

IVideoPicture frame;
while (this.done == false) {
  frame = imageQueue.get();
  writer.encodeVideo(0, frame);
}

如果 CPU 太慢,您可以通过限制此队列的大小来确保 imageQueue 不会因要渲染的图像而溢出,请参阅 constructor of LinkedBlockingQueue .

关于java - 单独线程中的屏幕捕获导致 Java 应用程序缓慢/无响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19949683/

相关文章:

c - 在程序的任何地方单独分离一个线程是否正确?

java - 在外部修改时流式传输并发集合是否安全?

sql - 如何获取每个设备的第一个和最后一个元素?

android - Android L 与 Kitkat 中的 RAM 使用情况

java - 用 Java 实现的最佳模式匹配算法

java https 客户端很慢

c# - 如何计算数组中的事件线程并避免 "collection was modified"错误?

ASP.NET 网站与 ASP.NET WebApp : What's the performance difference?

java - Java 中的 MinHeap 和 MaxHeap 实现

JavaFx WebView 和 JxBrowser 替代方案