2017-01-01 9 views
2

スクリーンショットをたくさん取ってから、すべてのスクリーンショットをmovファイルに変換してスクリーンレコーダーを作成しました。JavaFX:通常よりも2倍速いビデオ

しかし、movファイルの再生速度は通常よりも約2倍速く、複数のコンピュータで試してみましたが、同じ結果がまだ発生しました。

ここに私のコードは

メインクラス

package recorder; 

import javafx.application.Application; 
import static javafx.application.Application.launch; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.stage.Stage; 

public class Recorder extends Application { 

    public Stage stage; 

    @Override 
    public void start(Stage stage) throws Exception { 
     Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml")); 

     Scene scene = new Scene(root); 
     stage.setResizable(false); 
     stage.setScene(scene); 
     stage.show(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 

} 

FXMLDocument

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.scene.control.Button?> 
<?import javafx.scene.control.TextArea?> 
<?import javafx.scene.control.TextField?> 
<?import javafx.scene.layout.ColumnConstraints?> 
<?import javafx.scene.layout.GridPane?> 
<?import javafx.scene.layout.RowConstraints?> 

<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="639.0" prefWidth="578.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="recorder.FXMLDocumentController"> 
    <columnConstraints> 
     <ColumnConstraints hgrow="SOMETIMES" minWidth="578.0" prefWidth="578.0" /> 
    </columnConstraints> 
    <rowConstraints> 
     <RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" prefHeight="95.0" vgrow="SOMETIMES" /> 
     <RowConstraints maxHeight="245.0" minHeight="9.0" prefHeight="12.0" vgrow="SOMETIMES" /> 
     <RowConstraints maxHeight="481.0" minHeight="10.0" prefHeight="478.0" vgrow="SOMETIMES" /> 
    </rowConstraints> 
    <children> 
     <Button fx:id="start" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#startOnAction" prefHeight="220.0" prefWidth="578.0" text="Start Recording" /> 
     <TextArea fx:id="console" editable="false" prefHeight="200.0" prefWidth="200.0" promptText="Console" wrapText="true" GridPane.rowIndex="2" /> 
     <TextField fx:id="input" prefHeight="105.0" prefWidth="578.0" promptText="File Name" GridPane.rowIndex="1" /> 
    </children> 
</GridPane> 

FXMLDocumentController

package recorder; 

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.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.OutputStream; 
import java.net.MalformedURLException; 
import java.net.URL; 
import java.util.ResourceBundle; 
import java.util.Timer; 
import java.util.TimerTask; 
import java.util.Vector; 
import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.ThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
import javafx.beans.value.ObservableValue; 
import javafx.event.ActionEvent; 
import javafx.fxml.FXML; 
import javafx.fxml.Initializable; 
import javafx.scene.control.Button; 
import javafx.scene.control.TextArea; 
import javafx.scene.control.TextField; 
import javafx.stage.Stage; 
import javax.imageio.ImageIO; 
import javax.media.MediaLocator; 
import javax.swing.JOptionPane; 

public class FXMLDocumentController implements Initializable { 

    ExecutorService imageSavingService = new ThreadPoolExecutor(2, 2, 
      0L, TimeUnit.MILLISECONDS, 
      new ArrayBlockingQueue<Runnable>(20), new ThreadPoolExecutor.DiscardPolicy()); 

    boolean recording; 

    File file; 

    int num; 
    /** 
    * Screen Width. 
    */ 
    public static int screenWidth = (int) Toolkit.getDefaultToolkit() 
      .getScreenSize().getWidth(); 

    /** 
    * Screen Height. 
    */ 
    public static int screenHeight = (int) Toolkit.getDefaultToolkit() 
      .getScreenSize().getHeight(); 

    /** 
    * Interval between which the image needs to be captured. 
    */ 
    public static int captureInterval = 25; 

    /** 
    * Temporary folder to store the screenshot. 
    */ 
    public static String store = "tmp"; 

    /** 
    * Status of the recorder. 
    */ 
    public static boolean record = false; 

    @FXML 
    private Button start; 

    @FXML 
    private TextArea console; 

    @FXML 
    private TextField input; 

    public static void startRecord() { 
     FXMLDocumentController t = new FXMLDocumentController(); 
//  Thread recordThread = new Thread() { 
//   @Override 
//   public void run() { 
//    Robot rt; 
//    int cnt = 0; 
//    try { 
//     rt = new Robot(); 
//     while (cnt == 0 || record) { 
//      BufferedImage img = rt.createScreenCapture(new Rectangle(screenWidth, screenHeight)); 
//      ImageIO.write(img, "jpeg", new File("./" + store + "/" 
//        + System.currentTimeMillis() + ".jpeg")); 
//      if (cnt == 0) { 
//       record = true; 
//       cnt = 1; 
//      } 
//      t.wait(captureInterval); 
//     } 
//    } catch (Exception e) { 
//     e.printStackTrace(); 
//    } 
//   } 
//  }; 
//  recordThread.start(); 
     Timer timer = new Timer(); 
     timer.scheduleAtFixedRate(new TimerTask() { 
      @Override 
      public void run() { 
       try { 
        Robot rt; 
        int cnt = 0; 
        rt = new Robot(); 

        BufferedImage img = rt.createScreenCapture(new Rectangle(screenWidth, screenHeight)); 
        final long timeStemp = System.currentTimeMillis(); 
        t.imageSavingService.submit(() -> { 
         try { 
          ImageIO.write(img, "jpeg", new File("./" + store + "/" 
            + timeStemp + ".jpeg")); 
         } catch (IOException e) { 
          //handle exception (e.g. via callback) 
         } 
        }); 
        if (cnt == 0) { 
         record = true; 
         cnt = 1; 
        } 
       } catch (AWTException ex) { 
       } 
      } 
     }, captureInterval, captureInterval); 
    } 

    public void makeVideo(String movFile) throws MalformedURLException { 
     imageSavingService.shutdown(); 
     try { 
      while (!imageSavingService.awaitTermination(5, TimeUnit.SECONDS)) { 
       // waiting another 5 seconds for the service to terminate 
      } 
     } catch (InterruptedException e1) { 
      e1.printStackTrace(); 
     } 
     this.console.appendText("Processing Video... please wait"); 
     JpegImagesToMovie imageToMovie = new JpegImagesToMovie(); 
     Vector<String> imgLst = new Vector<String>(); 
     File f = new File(store); 
     File[] fileLst = f.listFiles(); 
     for (int i = 0; i < fileLst.length; i++) { 
      imgLst.add(fileLst[i].getAbsolutePath()); 
     } 
     // Generate the output media locators. 
     MediaLocator oml; 
     if ((oml = imageToMovie.createMediaLocator(movFile)) == null) { 
      this.console.appendText("Error in processing"); 
      System.exit(0); 
     } 
     imageToMovie.doIt(screenWidth, screenHeight, (1000/captureInterval), imgLst, oml); 
     f.deleteOnExit(); 
     System.exit(0); 

    } 

    @FXML 
    void startOnAction(ActionEvent event) { 
     recording = true; 
     Timer timer = new Timer(); 
     this.console.setText("######### Starting Screen Recorder #########\n"); 
     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); 
     this.console.appendText("Your Screen [Width, Height]: " + "[" + screen.getWidth() + "," + screen.getHeight() + "]\nWhen the Recording starts, this window will minimize, to stop them recording, open this window."); 
     try { 
      Thread.sleep(500); 
     } catch (InterruptedException ex) { 
     } 
     this.console.appendText("The recording will start NOW\n"); 
     num = 5; 

     File f = new File(store); 
     if (!f.exists()) { 
      f.mkdir(); 
     } 
     startRecord(); 
     Stage stage = (Stage) console.getScene().getWindow(); 
     stage.setIconified(true); 
     stage.focusedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> { 
      focusState(newValue); 
     }); 
    } 

    private void focusState(boolean value) { 
     if (recording) { 
      try { 
       makeVideo(input.getText() + ".mov"); 
       recording = false; 
//  if (value) { 
//   record = false; 
//   try { 
//    makeVideo(input.getText() + ".mov"); 
//   } catch (MalformedURLException ex) { 
//   } 
//  } else { 
//  } 
      } catch (MalformedURLException ex) { 
      } 
     } 
    } 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 

    } 

} 

だJpegImagesToMovie(私によって作成されていない)

package recorder; 

/* 
* @(#)JpegImagesToMovie.java 1.3 01/03/13 
* 
* Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. 
* 
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, 
* modify and redistribute this software in source and binary code form, 
* provided that i) this copyright notice and license appear on all copies of 
* the software; and ii) Licensee does not utilize the software in a manner 
* which is disparaging to Sun. 
* 
* This software is provided "AS IS," without a warranty of any kind. ALL 
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY 
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR 
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE 
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING 
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS 
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, 
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER 
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF 
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 
* POSSIBILITY OF SUCH DAMAGES. 
* 
* This software is not designed or intended for use in on-line control of 
* aircraft, air traffic, aircraft navigation or aircraft communications; or in 
* the design, construction, operation or maintenance of any nuclear 
* facility. Licensee represents and warrants that it will not use or 
* redistribute the Software for such purposes. 
*/ 

import java.awt.Dimension; 
import java.io.File; 
import java.io.IOException; 
import java.io.RandomAccessFile; 
import java.net.MalformedURLException; 
import java.util.Vector; 

import javax.media.Buffer; 
import javax.media.ConfigureCompleteEvent; 
import javax.media.ControllerEvent; 
import javax.media.ControllerListener; 
import javax.media.DataSink; 
import javax.media.EndOfMediaEvent; 
import javax.media.Format; 
import javax.media.Manager; 
import javax.media.MediaLocator; 
import javax.media.PrefetchCompleteEvent; 
import javax.media.Processor; 
import javax.media.RealizeCompleteEvent; 
import javax.media.ResourceUnavailableEvent; 
import javax.media.Time; 
import javax.media.control.TrackControl; 
import javax.media.datasink.DataSinkErrorEvent; 
import javax.media.datasink.DataSinkEvent; 
import javax.media.datasink.DataSinkListener; 
import javax.media.datasink.EndOfStreamEvent; 
import javax.media.format.VideoFormat; 
import javax.media.protocol.ContentDescriptor; 
import javax.media.protocol.DataSource; 
import javax.media.protocol.FileTypeDescriptor; 
import javax.media.protocol.PullBufferDataSource; 
import javax.media.protocol.PullBufferStream; 

/** 
* This program takes a list of JPEG image files and convert them into a 
* QuickTime movie. 
*/ 
public class JpegImagesToMovie implements ControllerListener, DataSinkListener { 

    public boolean doIt(int width, int height, int frameRate, Vector inFiles, 
      MediaLocator outML) throws MalformedURLException { 
     ImageDataSource ids = new ImageDataSource(width, height, frameRate, 
       inFiles); 

     Processor p; 

     try { 
      //System.err 
      //  .println("- create processor for the image datasource ..."); 
      p = Manager.createProcessor(ids); 
     } catch (Exception e) { 
      System.err 
        .println("Yikes! Cannot create a processor from the data source."); 
      return false; 
     } 

     p.addControllerListener(this); 

     // Put the Processor into configured state so we can set 
     // some processing options on the processor. 
     p.configure(); 
     if (!waitForState(p, p.Configured)) { 
      System.err.println("Failed to configure the processor."); 
      return false; 
     } 

     // Set the output content descriptor to QuickTime. 
     p.setContentDescriptor(new ContentDescriptor(
       FileTypeDescriptor.QUICKTIME)); 

     // Query for the processor for supported formats. 
     // Then set it on the processor. 
     TrackControl tcs[] = p.getTrackControls(); 
     Format f[] = tcs[0].getSupportedFormats(); 
     if (f == null || f.length <= 0) { 
      System.err.println("The mux does not support the input format: " 
        + tcs[0].getFormat()); 
      return false; 
     } 

     tcs[0].setFormat(f[0]); 

     //System.err.println("Setting the track format to: " + f[0]); 

     // We are done with programming the processor. Let's just 
     // realize it. 
     p.realize(); 
     if (!waitForState(p, p.Realized)) { 
      System.err.println("Failed to realize the processor."); 
      return false; 
     } 

     // Now, we'll need to create a DataSink. 
     DataSink dsink; 
     if ((dsink = createDataSink(p, outML)) == null) { 
      System.err 
        .println("Failed to create a DataSink for the given output MediaLocator: " 
          + outML); 
      return false; 
     } 

     dsink.addDataSinkListener(this); 
     fileDone = false; 

     System.out.println("Generating the video : "+outML.getURL().toString()); 

     // OK, we can now start the actual transcoding. 
     try { 
      p.start(); 
      dsink.start(); 
     } catch (IOException e) { 
      System.err.println("IO error during processing"); 
      return false; 
     } 

     // Wait for EndOfStream event. 
     waitForFileDone(); 

     // Cleanup. 
     try { 
      dsink.close(); 
     } catch (Exception e) { 
     } 
     p.removeControllerListener(this); 

     System.out.println("Video creation completed!!!!!"); 
     return true; 
    } 

    /** 
    * Create the DataSink. 
    */ 
    DataSink createDataSink(Processor p, MediaLocator outML) { 

     DataSource ds; 

     if ((ds = p.getDataOutput()) == null) { 
      System.err 
        .println("Something is really wrong: the processor does not have an output DataSource"); 
      return null; 
     } 

     DataSink dsink; 

     try { 
      //System.err.println("- create DataSink for: " + outML); 
      dsink = Manager.createDataSink(ds, outML); 
      dsink.open(); 
     } catch (Exception e) { 
      System.err.println("Cannot create the DataSink: " + e); 
      return null; 
     } 

     return dsink; 
    } 

    Object waitSync = new Object(); 
    boolean stateTransitionOK = true; 

    /** 
    * Block until the processor has transitioned to the given state. Return 
    * false if the transition failed. 
    */ 
    boolean waitForState(Processor p, int state) { 
     synchronized (waitSync) { 
      try { 
       while (p.getState() < state && stateTransitionOK) 
        waitSync.wait(); 
      } catch (Exception e) { 
      } 
     } 
     return stateTransitionOK; 
    } 

    /** 
    * Controller Listener. 
    */ 
    public void controllerUpdate(ControllerEvent evt) { 

     if (evt instanceof ConfigureCompleteEvent 
       || evt instanceof RealizeCompleteEvent 
       || evt instanceof PrefetchCompleteEvent) { 
      synchronized (waitSync) { 
       stateTransitionOK = true; 
       waitSync.notifyAll(); 
      } 
     } else if (evt instanceof ResourceUnavailableEvent) { 
      synchronized (waitSync) { 
       stateTransitionOK = false; 
       waitSync.notifyAll(); 
      } 
     } else if (evt instanceof EndOfMediaEvent) { 
      evt.getSourceController().stop(); 
      evt.getSourceController().close(); 
     } 
    } 

    Object waitFileSync = new Object(); 
    boolean fileDone = false; 
    boolean fileSuccess = true; 

    /** 
    * Block until file writing is done. 
    */ 
    boolean waitForFileDone() { 
     synchronized (waitFileSync) { 
      try { 
       while (!fileDone) 
        waitFileSync.wait(); 
      } catch (Exception e) { 
      } 
     } 
     return fileSuccess; 
    } 

    /** 
    * Event handler for the file writer. 
    */ 
    public void dataSinkUpdate(DataSinkEvent evt) { 

     if (evt instanceof EndOfStreamEvent) { 
      synchronized (waitFileSync) { 
       fileDone = true; 
       waitFileSync.notifyAll(); 
      } 
     } else if (evt instanceof DataSinkErrorEvent) { 
      synchronized (waitFileSync) { 
       fileDone = true; 
       fileSuccess = false; 
       waitFileSync.notifyAll(); 
      } 
     } 
    } 

    /*public static void main(String args[]) { 

     if (args.length == 0) 
      prUsage(); 

     // Parse the arguments. 
     int i = 0; 
     int width = -1, height = -1, frameRate = 1; 
     Vector inputFiles = new Vector(); 
     String outputURL = null; 

     while (i < args.length) { 

      if (args[i].equals("-w")) { 
       i++; 
       if (i >= args.length) 
        prUsage(); 
       width = new Integer(args[i]).intValue(); 
      } else if (args[i].equals("-h")) { 
       i++; 
       if (i >= args.length) 
        prUsage(); 
       height = new Integer(args[i]).intValue(); 
      } else if (args[i].equals("-f")) { 
       i++; 
       if (i >= args.length) 
        prUsage(); 
       frameRate = new Integer(args[i]).intValue(); 
      } else if (args[i].equals("-o")) { 
       i++; 
       if (i >= args.length) 
        prUsage(); 
       outputURL = args[i]; 
      } else { 
       inputFiles.addElement(args[i]); 
      } 
      i++; 
     } 

     if (outputURL == null || inputFiles.size() == 0) 
      prUsage(); 

     // Check for output file extension. 
     if (!outputURL.endsWith(".mov") && !outputURL.endsWith(".MOV")) { 
      System.err 
        .println("The output file extension should end with a .mov extension"); 
      prUsage(); 
     } 

     if (width < 0 || height < 0) { 
      System.err.println("Please specify the correct image size."); 
      prUsage(); 
     } 

     // Check the frame rate. 
     if (frameRate < 1) 
      frameRate = 1; 

     // Generate the output media locators. 
     MediaLocator oml; 

     if ((oml = createMediaLocator(outputURL)) == null) { 
      System.err.println("Cannot build media locator from: " + outputURL); 
      System.exit(0); 
     } 

     JpegImagesToMovie imageToMovie = new JpegImagesToMovie(); 
     imageToMovie.doIt(width, height, frameRate, inputFiles, oml); 

     System.exit(0); 
    }*/ 

    static void prUsage() { 
     System.err 
       .println("Usage: java JpegImagesToMovie -w <width> -h <height> -f <frame rate> -o <output URL> <input JPEG file 1> <input JPEG file 2> ..."); 
     System.exit(-1); 
    } 

    /** 
    * Create a media locator from the given string. 
    */ 
    static MediaLocator createMediaLocator(String url) { 

     MediaLocator ml; 

     if (url.indexOf(":") > 0 && (ml = new MediaLocator(url)) != null) 
      return ml; 

     if (url.startsWith(File.separator)) { 
      if ((ml = new MediaLocator("file:" + url)) != null) 
       return ml; 
     } else { 
      String file = "file:" + System.getProperty("user.dir") 
        + File.separator + url; 
      if ((ml = new MediaLocator(file)) != null) 
       return ml; 
     } 

     return null; 
    } 

    // ///////////////////////////////////////////// 
    // 
    // Inner classes. 
    // ///////////////////////////////////////////// 

    /** 
    * A DataSource to read from a list of JPEG image files and turn that into a 
    * stream of JMF buffers. The DataSource is not seekable or positionable. 
    */ 
    class ImageDataSource extends PullBufferDataSource { 

     ImageSourceStream streams[]; 

     ImageDataSource(int width, int height, int frameRate, Vector images) { 
      streams = new ImageSourceStream[1]; 
      streams[0] = new ImageSourceStream(width, height, frameRate, images); 
     } 

     public void setLocator(MediaLocator source) { 
     } 

     public MediaLocator getLocator() { 
      return null; 
     } 

     /** 
     * Content type is of RAW since we are sending buffers of video frames 
     * without a container format. 
     */ 
     public String getContentType() { 
      return ContentDescriptor.RAW; 
     } 

     public void connect() { 
     } 

     public void disconnect() { 
     } 

     public void start() { 
     } 

     public void stop() { 
     } 

     /** 
     * Return the ImageSourceStreams. 
     */ 
     public PullBufferStream[] getStreams() { 
      return streams; 
     } 

     /** 
     * We could have derived the duration from the number of frames and 
     * frame rate. But for the purpose of this program, it's not necessary. 
     */ 
     public Time getDuration() { 
      return DURATION_UNKNOWN; 
     } 

     public Object[] getControls() { 
      return new Object[0]; 
     } 

     public Object getControl(String type) { 
      return null; 
     } 
    } 

    /** 
    * The source stream to go along with ImageDataSource. 
    */ 
    class ImageSourceStream implements PullBufferStream { 

     Vector images; 
     int width, height; 
     VideoFormat format; 

     int nextImage = 0; // index of the next image to be read. 
     boolean ended = false; 

     public ImageSourceStream(int width, int height, int frameRate, 
       Vector images) { 
      this.width = width; 
      this.height = height; 
      this.images = images; 

      format = new VideoFormat(VideoFormat.JPEG, new Dimension(width, 
        height), Format.NOT_SPECIFIED, Format.byteArray, 
        (float) frameRate); 
     } 

     /** 
     * We should never need to block assuming data are read from files. 
     */ 
     public boolean willReadBlock() { 
      return false; 
     } 

     /** 
     * This is called from the Processor to read a frame worth of video 
     * data. 
     */ 
     public void read(Buffer buf) throws IOException { 

      // Check if we've finished all the frames. 
      if (nextImage >= images.size()) { 
       // We are done. Set EndOfMedia. 
       //System.err.println("Done reading all images."); 
       buf.setEOM(true); 
       buf.setOffset(0); 
       buf.setLength(0); 
       ended = true; 
       return; 
      } 

      String imageFile = (String) images.elementAt(nextImage); 
      nextImage++; 

      //System.err.println(" - reading image file: " + imageFile); 

      // Open a random access file for the next image. 
      RandomAccessFile raFile; 
      raFile = new RandomAccessFile(imageFile, "r"); 

      byte data[] = null; 

      // Check the input buffer type & size. 

      if (buf.getData() instanceof byte[]) 
       data = (byte[]) buf.getData(); 

      // Check to see the given buffer is big enough for the frame. 
      if (data == null || data.length < raFile.length()) { 
       data = new byte[(int) raFile.length()]; 
       buf.setData(data); 
      } 

      // Read the entire JPEG image from the file. 
      raFile.readFully(data, 0, (int) raFile.length()); 

      //System.err.println(" read " + raFile.length() + " bytes."); 

      buf.setOffset(0); 
      buf.setLength((int) raFile.length()); 
      buf.setFormat(format); 
      buf.setFlags(buf.getFlags() | buf.FLAG_KEY_FRAME); 

      // Close the random access file. 
      raFile.close(); 
     } 

     /** 
     * Return the format of each video frame. That will be JPEG. 
     */ 
     public Format getFormat() { 
      return format; 
     } 

     public ContentDescriptor getContentDescriptor() { 
      return new ContentDescriptor(ContentDescriptor.RAW); 
     } 

     public long getContentLength() { 
      return 0; 
     } 

     public boolean endOfStream() { 
      return ended; 
     } 

     public Object[] getControls() { 
      return new Object[0]; 
     } 

     public Object getControl(String type) { 
      return null; 
     } 
    } 
} 

答えて

2

問題は、画像をディスクに書き込むことが非常に遅いことです。これはあなたの場合、スクリーンショットを撮ること+ディスクに書き込むことは約50ミリ秒かかります - スクリーンショットが保存されるたびに約100ミリ秒(50ミリ秒のキャプチャ+保存、50ミリ秒のスリープ)

考えられる解決策:すべての

まず、タスクにかかる時間、考慮されていませんscreencapturesの代わりに、単なるThread.sleep()をスケジュールするためのjava.util.Timerを使用する必要があります。タイマーを使用すると、定期的に50ミリ秒の固定遅延でタスクをスケジュールできます。

次の手順は、キャプチャされた画像をより速く保存する方法を見つけることです。 これを行う1つの方法は、イメージを一時的にスレッドセーフキューに保存し、「イメージをディスクに」プロセスをスクリーンキャプチャのプロセスと並行して実行される別のスレッドにアウトソースすることです。理想的には、この独立したスレッドは画像を直接ビデオ形式にエンコードする必要がありますが、これはあなた自身のバージョンのJpegImagesToMovie(Java Media Frameworkを直接扱うことを意味します)を必要とします。画像節約のプロセスをスピードアップ

は、次のように行うことができる:

は、画面を保存するためのExecutorServiceを作成して記録開始時にキャプチャ:

imageSavingService = new ThreadPoolExecutor(2, 2, 
       0L, TimeUnit.MILLISECONDS, 
       new ArrayBlockingQueue<Runnable>(20), new ThreadPoolExecutor.DiscardPolicy()); 

これは、プールサイズ2とのexecutorを作成しますワーキングキューの容量を20に制限すると、指定された容量にワーキングキューをバインドすると、メモリを超えることはありません。キューが満杯の場合、新しく送信されたフレームは破棄されます(発生しない)。通常、1つのスレッドがIO操作に最適な選択肢になるはずですが、何らかの理由で2つのスレッドがテストを行ったときのパフォーマンスがはるかに向上しています。

あなたはこのようなサービスにタスクを保存する画像提出することができます:記録がキャンセルされた場合、あなたがシャットダウンし、このようなサービス

final long timeStemp = System.currentTimeMillis(); 
imageSavingService.submit(() -> { 
    try { 
     ImageIO.write(img, "jpeg", new File("./" + store + "/" 
      + timeStemp + ".jpeg")); 
    } catch (IOException e) { 
     //handle exception (e.g. via callback) 
    } 
}); 

をすることができます

imageSavingService.shutdown(); 
try { 
    while(!imageSavingService.awaitTermination(5, TimeUnit.SECONDS)){ 
     // waiting another 5 seconds for the service to terminate 
    } 
} catch (InterruptedException e1) { 
    e1.printStackTrace(); 
} 

この方法であなたシャットダウンサービスの秩序を最後に保留中のイメージ保存タスクが処理されるまで待ちます。

このサービスを使用して、提案されたタイマーとともに画像保存を高速化すると、フレームロスなしで50ミリ秒のキャプチャ間隔で録画することができました。

+0

"スケジュールされたタスクの所要時間が50ミリ秒以下になると、正確に50ミリ秒ごとにスクリーンショットが保存されます。":タイムは何もガーメントしません。スレッドのための機能 " - これはあなたの好きな文です。 – gpasch

+1

@gpaschこれを指摘していただきありがとうございます。私は文章をより適切な記述で置き換えました。 – Calculator

+0

@gpasch私の編集を確認してください、私はあなたが提案した(タイマー)を行いましたが、同じことがまだ発生します。 –

関連する問題