Stop recording and save video output, without stopping the application

49 views
Skip to first unread message

Jhuliano

unread,
Aug 16, 2023, 5:07:59 PM8/16/23
to javacv
Hello, I have a webcam recording application, with javafx and javacv, and I want to stop recording and saving the video output, the recorder.stop() method stops recording the video, but the output file continues to be used until I close application, I want to stop recording and have the file available without having to close my application.

Here is my code(Sorry for the disorganization, I'm new to this):

public class VideoController {

static final private int WEBCAM_DEVICE_INDEX = 0;
int AUDIO_DEVICE_INDEX = 4;

Thread previewThread;
Frame capturedFrame = null;

private final int captureWidth = 1280;
private final int captureHeight = 720;
private final String filename = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"));
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("C:/Users/jhuliano-vasconcelos/Videos/" + filename + ".avi", captureWidth, captureHeight, 2);
static final OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(WEBCAM_DEVICE_INDEX);
static boolean isGrabberActive = false;
static volatile boolean isRecording;

private int FRAME_RATE = 30;

public static void startGrabber() throws FrameGrabber.Exception {
grabber.start();
setGrabberActive(true);
}

public static void setGrabberActive(boolean isGrabberActive) { VideoController.isGrabberActive = isGrabberActive; }
public boolean isGrabberActive() { return isGrabberActive; }
public void setIsRecording(boolean isRecording) { VideoController.isRecording = isRecording; }
public void startPreview(ImageView videoView) {
stopPreview();
previewThread = new Thread(() -> {
try {
Frame capturedFrame = null;
while (!Thread.currentThread().isInterrupted() && (capturedFrame = grabber.grab()) != null) {
videoView.setImage(new JavaFXFrameConverter().convert(capturedFrame));
}
} catch (FrameGrabber.Exception e) {
throw new RuntimeException(e);
}
});
previewThread.start();
}

public void stopPreview() {
if (previewThread != null) {
previewThread.interrupt();
try {
previewThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
previewThread = null;
}
}

Thread recordAudio = new Thread(() -> {
AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);

Mixer.Info[] minfoSet = AudioSystem.getMixerInfo();
Mixer mixer = AudioSystem.getMixer(minfoSet[AUDIO_DEVICE_INDEX]);
DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);

try {
TargetDataLine line = (TargetDataLine)AudioSystem.getLine(dataLineInfo);
line.open(audioFormat);
line.start();

int sampleRate = (int) audioFormat.getSampleRate();
int numChannels = audioFormat.getChannels();

int audioBufferSize = sampleRate * numChannels;
byte[] audioBytes = new byte[audioBufferSize];

ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
exec.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
int nBytesRead = 0;
while (nBytesRead == 0) {
nBytesRead = line.read(audioBytes, 0, line.available());
}
int nSamplesRead = nBytesRead / 2;
short[] samples = new short[nSamplesRead];

ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);
ShortBuffer sBuff = ShortBuffer.wrap(samples, 0, nSamplesRead);

recorder.recordSamples(sampleRate, numChannels, sBuff);
}
catch (org.bytedeco.javacv.FrameRecorder.Exception e) {
e.printStackTrace();
}
}
}, 0, (long) 1000 / FRAME_RATE, TimeUnit.MILLISECONDS);
}
catch (LineUnavailableException e1) {
e1.printStackTrace();
}
});

public void setupVideoRecorder() throws FFmpegFrameRecorder.Exception {
int GOP_LENGTH_IN_FRAMES = 60;

recorder.setInterleaved(true);
recorder.setVideoOption("tune", "zerolatency");
recorder.setVideoOption("preset", "ultrafast");
recorder.setVideoOption("crf", "28");
recorder.setVideoBitrate(2000000);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat("avi");
recorder.setFrameRate(FRAME_RATE);
recorder.setGopSize(GOP_LENGTH_IN_FRAMES);
recorder.setAudioOption("crf", "0");
recorder.setAudioQuality(0);
recorder.setAudioBitrate(192000);
recorder.setSampleRate(44100);
recorder.setAudioChannels(2);
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);

recorder.start();
}

Thread recordThread = new Thread(() -> {
try {
long startTime = 0;
long videoTS = 0;

while (!isRecording) Thread.onSpinWait();
setupVideoRecorder();
recordAudio.start();

while ((capturedFrame = grabber.grab()) != null && isRecording) {
if (startTime == 0)
startTime = System.currentTimeMillis();

videoTS = 1000 * (System.currentTimeMillis() - startTime);

if (videoTS > recorder.getTimestamp())
recorder.setTimestamp(videoTS);
recorder.record(capturedFrame);
}
} catch (FFmpegFrameRecorder.Exception | FrameGrabber.Exception e) {
throw new RuntimeException(e);
}
});

public void startRecording() throws FrameGrabber.Exception, FFmpegFrameRecorder.Exception, InterruptedException {
recordThread.start();
}

public void stopRecording() throws FrameRecorder.Exception, FrameGrabber.Exception {
isRecording = false; // Signal recording thread to stop

recordAudio.interrupt();
recordThread.interrupt();

// Wait for the threads to finish
try {
recordThread.join();
recordAudio.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

recorder.stop();
recorder.release();
recorder = null;

}

}

Samuel Audet

unread,
Aug 16, 2023, 5:18:47 PM8/16/23
to javacv, Jhuliano
FFmpeg in general isn't too thread-safe. Please try not to use it from multiple threads.

--

---
You received this message because you are subscribed to the Google Groups "javacv" group.
To unsubscribe from this group and stop receiving emails from it, send an email to javacv+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/javacv/f4cb9028-f2e6-4b93-ab09-296a3dbb203fn%40googlegroups.com.

Jhuliano

unread,
Aug 17, 2023, 1:16:22 PM8/17/23
to javacv
Thanks for the answer, I'll try to use less threads, but can you tell me if I can stop recording the video while my app is running ? or I can only save the video when I stop the application from running?

Samuel Audet

unread,
Aug 17, 2023, 7:52:23 PM8/17/23
to jav...@googlegroups.com, Jhuliano
FFmpegFrameRecorder.stop() closes the file, of course. If it doesn't
please open an issue with a minimal example that I can debug:
https://github.com/bytedeco/javacv/issues

Samuel Audet

unread,
Aug 17, 2023, 7:58:07 PM8/17/23
to jav...@googlegroups.com, Jhuliano
I'm sorry, we actually need to call release() or close() to get the file
closed, but it looks like you're already doing that. In any case, please
provide a small example of no more than a few lines that fails.

Jhuliano

unread,
Aug 18, 2023, 10:17:04 AM8/18/23
to javacv
The program does not give any error, I believe that the recording is ended, but the file is not released, and the ffmpeg output message does not appear, only when I stop the program.
Reply all
Reply to author
Forward
0 new messages