2

ユーザーが特定のアクティビティを実行しているときに、ユーザーの画面、ウェブカメラ、マイクを記録するアプリケーションに取り組んでいます。研究目的で利用させていただきます。このアプリケーションは Windows で正常にテストされていますが、Mac OS X (Maverick with ) では、記録を開始するとJava 7.0.45アプリケーションが遅くなり、応答しなくなります。

これが私がこれを理解するのが難しいと思う理由です:

  • 記録は別のスレッドで行われますが、別のスレッドの応答性にどのように影響するのでしょうか? 特に、各実行後にThread.yield()orThread.sleep(...)が呼び出されるように。
  • ログは、 で記録しようとしたとき15 FPSに、結果のフレーム レートが であったことを示してい2 FPSます。そのため、単一フレームのキャプチャを行うコードが遅すぎる可能性があります。しかし、なぜWindowsでうまく動作するのでしょうか?

簡単なメモ: このアプリケーションは、Windows の多数のユーザーによって正常にテストされましたが、1 台の 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();
4

1 に答える 1

4

あなたのコードには、アーキテクチャ上の問題がいくつか見られます。

まず、固定レートで何かを実行する場合は、ScheduledThreadPoolExecutor.scheduleAtFixedRate(...)関数を使用します。これにより、遅延コード部分全体が廃止されるだけでなく、特定の OS タイミングの問題がスケジューリングに干渉しないようになります。

次に、処理を高速化するには、コードを少し分解する必要があります。私が見る限り、キャプチャ、マウス描画/変換、ストリーム書き込みの 3 つのタスクがあります。キャプチャ部分をスケジュール化された Runnable に入れ、Callables として Executor に多並列実行に変換し、3 番目のスレッドで結果リストから結果を取得してストリームに書き込むと、マルチ並列実行をフルに活用できます。コア。

擬似コード:

グローバル宣言 (またはそれらをさまざまなクラスに渡します):

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

Capture Runnable (固定レートでスケジュール):

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 がレンダリングする画像でオーバーフローしないようにすることができます。 LinkedBlockingQueue のコンストラクターを参照してください。

于 2013-11-13T12:48:28.513 に答える