Skip to content

Instantly share code, notes, and snippets.

@ExpensiveKoala
Created June 3, 2023 06:08
Show Gist options
  • Save ExpensiveKoala/561347881d37827e7a9ac06920603ef1 to your computer and use it in GitHub Desktop.
Save ExpensiveKoala/561347881d37827e7a9ac06920603ef1 to your computer and use it in GitHub Desktop.
public class VideoDecoder implements Runnable {
protected FFmpegInput ffmpegInput;
protected FFmpegSourceStream sourceStream;
protected volatile VideoSourceSubstream vss;
protected volatile AudioSourceSubstream ass;
protected File currentFile;
protected boolean noMoreVideoFrames = false;
protected boolean noMoreAudioFrames = false;
protected ArrayBlockingQueue<VideoFrame> videoFrames = new ArrayBlockingQueue<>(25);
protected ArrayBlockingQueue<AudioFrame> audioFrames = new ArrayBlockingQueue<>(25);
protected Thread decodeThread;
protected AtomicBoolean stopped = new AtomicBoolean(false);
public VideoDecoder(File file) {
currentFile = file;
try {
ffmpegInput = FFmpegIO.openInput(currentFile, FFmpegIO.DEFAULT_BUFFER_SIZE * 4);
AVInputFormat inputFormat = detectInputFormat(currentFile.getName());
sourceStream = ffmpegInput.open(inputFormat);
sourceStream.registerStreams();
vss = (VideoSourceSubstream) sourceStream.getSubstreams(VideoSourceSubstream.class).stream().findFirst()
.orElseThrow(() -> new Exception("File does not contain a video substream"));
LOGGER.debug("FPS: {}", vss.getFormat().getFramesPerSecond());
ass = (AudioSourceSubstream) sourceStream.getSubstreams(AudioSourceSubstream.class).stream().findFirst()
.orElse(null);
} catch (Exception e) {
LOGGER.error("Error reading video file", e);
}
}
public void startDecode() {
try {
while (!stopped.get()) {
if(vss.peek() == null) {
break;
}
VideoFrame frame = vss.next();
if (frame == null) {
break;
}
videoFrames.put(frame);
if (ass != null && !noMoreAudioFrames) {
if(ass.peek() == null) {
noMoreAudioFrames = true;
continue;
}
AudioFrame audioFrame = ass.next();
audioFrames.put(audioFrame);
}
}
noMoreVideoFrames = true;
} catch (Exception e) {
LOGGER.error("Error reading video file", e);
}
}
private AVInputFormat detectInputFormat(String fileName) throws FFmpegException {
if(fileName == null || !fileName.contains(".")) {
throw new IllegalArgumentException("Video file does not have video extension. (MP4, MKV, ...)");
}
return FFmpeg.getInputFormatByExtension(fileName.substring(fileName.indexOf(".") + 1));
}
public VideoFormat getVideoFormat() {
if (vss == null) {
return null;
}
return vss.getFormat();
}
public VideoFrame getCurrentVideoFrame() {
try {
return videoFrames.poll(50L, TimeUnit.MICROSECONDS);
} catch (InterruptedException e) {
}
return null;
}
public AudioFrame getCurrentAudioFrame() {
if(ass == null || (noMoreAudioFrames && audioFrames.isEmpty())) {
return null;
}
try {
return audioFrames.poll(50L, TimeUnit.MICROSECONDS);
} catch (InterruptedException e) {
}
return null;
}
public boolean isVideoDone() {
return noMoreVideoFrames && videoFrames.isEmpty();
}
@Override
public void run() {
if (currentFile == null) {
return;
}
if (!currentFile.exists()) {
LOGGER.error("Could not find file: " + currentFile.getPath());
return;
}
stopped.set(false);
decodeThread = new Thread(this::startDecode);
decodeThread.start();
}
public void stop() {
stopped.set(true);
decodeThread.interrupt();
try {
//vss.close();
if(ass != null) {
// ass.close();
}
//sourceStream.close();
ffmpegInput.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment