-
-
Save ExpensiveKoala/561347881d37827e7a9ac06920603ef1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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