3

画面を記録するJavaアプリケーションに取り組んでいます。ロボットを使用していくつかのスクリーンショットを撮り、それらを一時フォルダーに保存してから、JpegImagesToMovie.java を使用してこれらを QuickTime ムービー ファイルに作成します。

私が経験している問題は、スクリプトを 20fps で実行するように開発しているにもかかわらず、約 5fps しか達成できないことです。画像をディスクに保存するのに時間がかかりすぎて、スクリプトの残りの部分が遅れているという点で、これをディスク速度まで追跡しました。

次に、スクリプトを変更して、画像を BufferedImages の配列に保存し、記録が停止したらディスクに書き込みます。これにより、フレーム レートが修正されますが、記録時に Java はすぐにメモリ不足になります (記録の数秒後)。 .

誰でもこれを行うアイデアや経験を持っていますか? 私が考えることができる 1 つの解決策は、JPEG 画像の圧縮率を上げる方法があるかどうかですが、これを行う方法がわかりません。

どんな助けでも大歓迎です!

4

2 に答える 2

3

考慮すべきオプションの 1 つは、複数のスレッドで処理を行うことです。1 つのスレッドをスクリーンショットの撮影専用にすることができ、他の多くのスレッドをディスクに書き込むことができます。ディスクへの書き込みは CPU を集中的に使用する操作ではないため、それらの多くを同時に実行して、それぞれが異なるファイルに書き込むことができます。次のプログラムは、ヒープ サイズが 512M のマシンで正常に動作します。

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ImageWritingMain
{
  public static void main(String[] args) throws Exception
  {
    // a queue
    final BlockingQueue<BufferedImage> queue = 
        new LinkedBlockingQueue<BufferedImage>();

    // schedule a thread to take 20 images per second and put them in 
    // the queue
    int fps = 20;
    final ScreenShotRecorder recorder = 
        new ScreenShotRecorder(new Robot(), queue);
    Timer timer = new Timer();
    timer.scheduleAtFixedRate(recorder, 0, (1000L/fps));

    // make a directory to hold the screenshot images
    String id = new Date().toString().replace(' ', '-').replace(':', '-');
    File imageDir = new File("images-" + id);
    imageDir.mkdirs();

    // start 10 threads, and each thread reads from the queue and 
    // writes the image to a file
    int nWriterThreads = 10;
    ExecutorService threadPool = Executors.newFixedThreadPool(nWriterThreads);
    for (int i = 0; i < nWriterThreads; i++)
    {
      ImageWriter task = new ImageWriter(queue, imageDir);
      threadPool.submit(task);
    }
    System.out.println("Started all threads ..");

    // wait as long as you want the program to run (1 minute, for example) ...
    Thread.sleep(60 * 1000L);
    // .. and shutdown the threads
    System.out.println("Shutting down all threads");
    threadPool.shutdownNow();
    timer.cancel();

    if (! queue.isEmpty())
    {
      System.out.println("Writing " + queue.size() + " remaining images");
      // write the remaining images to disk in the main thread
      ImageWriter writer = new ImageWriter(queue, imageDir);
      BufferedImage img = null;
      while ((img = queue.poll()) != null)
      {
        writer.writeImageToFile(img);
      }
    }
  }
}

class ScreenShotRecorder extends TimerTask
{
  private static final Rectangle screenRect = 
      new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
  private static final AtomicInteger counter = new AtomicInteger();
  private final Robot robot;
  private final BlockingQueue<BufferedImage> imageQueue;

  ScreenShotRecorder(Robot robot, BlockingQueue<BufferedImage> imageQueue)
  {
    this.robot = robot;
    this.imageQueue = imageQueue;
  }

  @Override
  public void run()
  {
    try
    {
      BufferedImage image = robot.createScreenCapture(screenRect);
      imageQueue.put(image);
      System.out.println(Thread.currentThread() + 
          ": Took screenshot #" + counter.incrementAndGet());
    }
    catch (InterruptedException e)
    {
      System.out.println("Finishing execution of " + Thread.currentThread());
      return;
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}

class ImageWriter implements Runnable
{
  private static final AtomicInteger counter = new AtomicInteger();
  private final BlockingQueue<BufferedImage> imageQueue;
  private final File dir;

  ImageWriter(BlockingQueue<BufferedImage> imageQueue, File dir)
  {
    this.imageQueue = imageQueue;
    this.dir = dir;
  }

  @Override
  public void run()
  {
    while (true)
    {
      try
      {
        BufferedImage image = imageQueue.take();
        writeImageToFile(image);
      }
      catch (InterruptedException e)
      {
        System.out.println("Finishing execution of " + Thread.currentThread());
        return;
      }
      catch (Exception e)
      {
        e.printStackTrace();
      }
    }
  }

  public void writeImageToFile(BufferedImage image) throws IOException
  {
    File file = new File(dir, "screenshot-" + counter.incrementAndGet());
    ImageIO.write(image, "JPG", file);
    System.out.println(Thread.currentThread() + 
        ": Wrote " + file.getCanonicalPath());
  }
}
于 2012-07-06T21:52:25.647 に答える
1

ミリムースが言ったこと。JPEG の解像度を下げてメモリを少し増やすこともできますが、おそらくビデオ コーデックを使用する必要があります。マウスを動かしたときだけ記録するハンドラを作成したり、本当に JPEG で記録したい場合はタイプしたりすることもできます。

于 2012-07-05T22:14:23.617 に答える