我正在开展一项应用程序记录用户屏幕,网络摄像头和麦克风,同时他/她正在执行某些活动。它将用于研究目的。该应用程序已在Windows上成功测试,但在Mac OS X(Maverick with Java 7.0.45
)上,应用程序在开始录制时变为慢和无响应。
这就是我觉得难以理解的原因:
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();
答案 0 :(得分:4)
我可以在您的代码中看到几个架构问题:
首先,如果您想以固定费率执行某些操作,请使用ScheduledThreadPoolExecutor.scheduleAtFixedRate(...)功能。它将使您的整个延迟代码部分过时,并确保某些操作系统时序问题不会干扰您的日程安排。
然后为了加快速度,您需要将代码分开一些。据我所知,你有3个任务:捕获,鼠标绘制/转换和流写。如果将捕获部分放在一个预定的Runnable中,将多个并行执行转换为Callables进入Executor,然后在第三个线程中从结果列表中获取结果并将其写入流中,则可以充分利用多个芯
伪代码:
全局声明(或将它们移交给各个类):
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);
}
}
Writer Runnable:
IVideoPicture frame;
while (this.done == false) {
frame = imageQueue.get();
writer.encodeVideo(0, frame);
}
您可以通过限制此队列的大小来确保如果CPU太慢,则imageQueue不会溢出要渲染的图像,请参阅constructor of LinkedBlockingQueue。