
com.aimmac23.node.RecordVideoCallable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of selenium-video-node Show documentation
Show all versions of selenium-video-node Show documentation
Selenium Grid plugin that records videos of running tests
package com.aimmac23.node;
import java.io.File;
import java.util.concurrent.Callable;
import java.util.logging.Logger;
import com.aimmac23.exception.MissingFrameException;
import com.aimmac23.node.jna.EncoderInterface;
import com.aimmac23.node.jna.JnaLibraryLoader;
import com.sun.jna.Pointer;
public class RecordVideoCallable implements Callable {
private static final Logger log = Logger.getLogger(RecordVideoCallable.class.getSimpleName());
private final int targetFramerate;
// how long we should sleep before recording another frame, if everything else took zero time
private final int targetFramerateSleepTime;
private volatile boolean shouldStop = false;
private ScreenshotSource screenshotSource;
static {
JnaLibraryLoader.init();
}
public RecordVideoCallable(int targetFramerate, ScreenshotSource screenshotSource) {
this.targetFramerate = targetFramerate;
this.screenshotSource = screenshotSource;
this.targetFramerateSleepTime = (int)((1.0 / targetFramerate) * 1000.0);
}
@Override
public File call() throws Exception {
int frames = 0;
File outputFile = File.createTempFile("screencast", ".webm");
EncoderInterface encoder = JnaLibraryLoader.getEncoder();
log.info("Starting new recording at " + targetFramerate + " with resolution "
+ screenshotSource.getWidth() + "x" + screenshotSource.getHeight());
Pointer context = encoder.create_context(outputFile.getCanonicalPath());
int result = encoder.init_encoder(context, screenshotSource.getWidth(),
screenshotSource.getHeight(), targetFramerate);
handleVPXError(result, "Failed to create VPX Context", context);
result = encoder.init_codec(context);
handleVPXError(result, "Failed to create init VPX codec", context);
result = encoder.init_image(context);
if(result != 0) {
throw new IllegalStateException("Failed to allocate memory for image buffer.");
}
log.info("Started recording to file: " + outputFile.getCanonicalPath());
long excessTime = 0;
long videoStartTime = System.currentTimeMillis();
while(!shouldStop) {
// how long to use the next frame for - should be 1 if we're not falling behind
int frameDuration = 1 + (int)(excessTime / targetFramerateSleepTime);
// if excessTime > TARGET_FRAMERATE_TIME. then we've just taken that into account
excessTime = excessTime % targetFramerateSleepTime;
long start = System.currentTimeMillis();
try {
result = screenshotSource.applyScreenshot(context);
if(result != 0) {
throw new IllegalStateException("Failed to convert frame to YUV format");
}
result = encoder.encode_next_frame(context, frameDuration);
handleVPXError(result, "Failed to encode next VPX frame", context);
}
catch(MissingFrameException e) {
log.warning("Had to drop frame - reason: " + e.getMessage());
}
long finish = System.currentTimeMillis();
frames++;
long timeTaken = finish - start;
// we're keeping up
if(timeTaken < targetFramerateSleepTime) {
Thread.sleep(targetFramerateSleepTime - timeTaken);
}
else {
// we're falling behind, take that into account for the next frame
excessTime += timeTaken;
}
}
result = encoder.encode_finish(context);
handleVPXError(result, "Failed to finalize video", context);
long videoEndTime = System.currentTimeMillis();
long duration = ((videoEndTime - videoStartTime) / 1000);
if(duration != 0) {
log.info("Finished recording - frames: " + frames + " duration: " + duration +
" seconds targetFps: " + targetFramerate + " actualFps: " + frames / duration);
}
else {
log.warning("Finished recording - video was zero length!");
}
return outputFile;
}
public void stopRecording() {
shouldStop = true;
}
private void handleVPXError(int errorCode, String message, Pointer context) {
if(errorCode != 0) {
throw new IllegalStateException(message + ": "
+ JnaLibraryLoader.getLibVPX().vpx_codec_err_to_string(errorCode)
+ " due to: " + JnaLibraryLoader.getEncoder().codec_error_detail(context));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy