2012-07-05 122 views
3

我工作在一個Java應用程序來記錄畫面。我正在使用一個Robot來做這個截圖,將它們保存到一個臨時文件夾,然後使用JpegImagesToMovie.java將它們構建到一個QuickTime電影文件中。拍攝屏幕的Java磁盤速度

我遇到的問題是,儘管發展我的腳本在運行的20fps,我只達到5fps的周圍。我已經將這種情況跟蹤到磁盤速度,因爲將圖像保存到磁盤需要很長的時間,並且這阻礙了腳本的其餘部分。接下來,我修改腳本以將圖像存儲在BufferedImages數組中,然後在錄製停止後將其寫入磁盤,這會修復幀頻,但是當重新編碼時,Java將快速耗盡內存(在幾次秒的記錄)。

有沒有人有這樣做的任何想法或經驗。我能想到的一個解決方案是,如果有一種方法可以增加JPEG圖像上的壓縮,但我不確定如何執行此操作。

任何幫助將不勝感激!

+2

爲了得到20fps的,你應該可以在驅動器 – millimoose 2012-07-05 22:06:08

+0

使用一個真正的視頻編解碼器,而不是爆破JPEG格式的看到的屏幕錄像[蒙媒體庫] (http://www.randelshofer.ch/monte/)可以以20 FPS的速度進行全屏(1920x1080像素)視頻拍攝。它使用JMF&直接編碼到MOV或AVI。 – 2012-07-05 23:19:23

+0

這看起來像我想要的!當我下班回家並報告回來時,我們會有一些樂趣。 – camerongray 2012-07-06 10:45:11

回答

3

一種選擇是做多線程的處理。一個線程可以專門用於截屏,而其他線程可以寫入磁盤。由於寫入磁盤不是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()); 
    } 
} 
1

什麼millimoose說。也許你可以嘗試降低JPEGS的分辨率以給你更多的內存,但是你可能不得不使用視頻編解碼器。您也可以嘗試創建一個處理程序,以便只在移動鼠標時進行錄製,或者如果您確實有意以JPEG格式進行錄製,請輸入該處理程序。你可能要考慮

+0

編解碼器的想法聽起來很有希望,但我從來沒有用Java或任何其他語言處理它們,如果你能給我任何鏈接/指針,這將是一個很大的幫助!如果我沿着編解碼器路線走下去了,我還會使用機器人還是需要使用不同的東西?最後,我計劃將這個應用程序分發給新手用戶(我也在考慮webstart),所以我真的需要將所有內容都包含在應用程序中,而無需用戶在他們的計算機上安裝額外的軟件。 感謝您的持續幫助! – camerongray 2012-07-05 22:22:17

+0

對不起,我沒有真正使用編解碼器,所以我不太瞭解它們。我只知道拍攝屏幕截圖並將其轉換爲視頻非常耗費內存。我建議讀一下他們,然後問一個關於他們的新問題。 – 2012-07-05 22:30:22