Repository metrics
- Stars
- (6,985 stars)
- PR merge metrics
- (No merged PRs in 30d)
Description
I have created a simple player with FFMPEGFrameGrabber on normal videos its working fine, but on few high quality videos its stucking.
I have attached the src and the videos to replicate the issue.
`package com.mi6.videoviewer;
import java.awt.image.BufferedImage; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.nio.ByteBuffer; import java.nio.ShortBuffer; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;
import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine;
import org.bytedeco.ffmpeg.global.avutil; import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.FrameGrabber.Exception; import org.bytedeco.javacv.Java2DFrameConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
public class FFmpegPlayer extends Thread implements PlayerControls{
final Logger log = LoggerFactory.getLogger(FFmpegPlayer.class);
private FFmpegFrameGrabber grabber;
private final Java2DFrameConverter converter;
private AudioFormat audioFormat;
private PropertyChangeSupport listener;
private ExecutorService executor;
private SourceDataLine soundLine = null;
private boolean audio = true;
public FFmpegPlayer() {
avutil.av_log_set_level(avutil.AV_LOG_TRACE);
converter = new Java2DFrameConverter();
listener = new PropertyChangeSupport(this);
executor = Executors.newSingleThreadExecutor();
}
public void setVideo(String videoFilename) {
grabber = new FFmpegFrameGrabber(videoFilename);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
listener.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
listener.removePropertyChangeListener(l);
}
@Override
public void run() {
try {
grabber.start();
} catch (Exception e) {
log.error("Cannot Start Grabber", e);
endVideo();
return;
}
audioFormat = new AudioFormat(grabber.getSampleRate(), 16, grabber.getAudioChannels(), true, true);
final DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
try {
soundLine = (SourceDataLine) AudioSystem.getLine(info);
soundLine.open(audioFormat);
soundLine.start();
} catch (LineUnavailableException e) {
log.error("No Audio Found", e);
} catch(IllegalArgumentException e) {
log.error("No Audio Found", e);
}
while (!Thread.interrupted()) {
Frame frame = null;
try {
frame = grabber.grabFrame(true, true, true, false, true);
} catch (Exception e) {
log.error("No Frame Found", e);
}
if (frame == null) {
break;
}
if (frame.image != null) {
BufferedImage image = converter.convert(frame);
listener.firePropertyChange("Image", null, image);
} else if (frame.samples != null && audio) {
final ShortBuffer channelSamplesShortBuffer = (ShortBuffer) frame.samples[0];
channelSamplesShortBuffer.rewind();
final ByteBuffer outBuffer = ByteBuffer.allocate(channelSamplesShortBuffer.capacity() * 2);
for (int i = 0; i < channelSamplesShortBuffer.capacity(); i++) {
short val = channelSamplesShortBuffer.get(i);
outBuffer.putShort(val);
}
try {
if(soundLine == null)
break;
executor.submit(new Runnable() {
public void run() {
soundLine.write(outBuffer.array(), 0, outBuffer.capacity());
outBuffer.clear();
}
}).get();
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (NullPointerException e) {
break;
}
}
}
executor.shutdownNow();
try {
executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error("Cannot stop the sound thread", e);
}
if(soundLine!=null)
soundLine.stop();
try {
grabber.stop();
} catch (Exception e) {
log.error("Cannot stop grabber", e);
}
try {
grabber.release();
} catch (Exception e) {
log.error("Cannot release grabber", e);
}
endVideo();
}
public void endVideo() {
listener.firePropertyChange("Stop", null, null);
}
public void playVideo() {
start();
}
public void muteVideo() {
audio = !audio;
}
public void stopVideo() {
interrupt();
}
} `
`package com.mi6.videoviewer;
import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List;
import javax.swing.JComponent; import javax.swing.JFrame;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
public class VideoPanel extends JComponent implements PlayerControls, PropertyChangeListener {
final Logger log = LoggerFactory.getLogger(VideoPanel.class);
private static final long serialVersionUID = 1L;
private volatile FFmpegPlayer processor;
private BufferedImage image;
private List<String> videos;
private int index = 0;
public VideoPanel(List<String> videos) {
this.videos = videos;
}
public void setVideos() {
if(videos == null || videos.isEmpty()) {
log.error("Playlist is Empty");
return;
}
String videoFilename = videos.get(index++);
if(index > videos.size() -1) {
log.info("Resetting Playlist Order. ");
index = 0;
}
log.info("Setting Video to Play, Video Path is: "+ videoFilename);
if(processor!=null) {
processor.stopVideo();
processor.removePropertyChangeListener(this);
}
processor = new FFmpegPlayer();
processor.addPropertyChangeListener(this);
processor.setVideo(videoFilename);
playVideo();
}
public void playVideo() {
log.info("Play Video Event arise");
processor.playVideo();
}
public void stopVideo() {
log.info("Stop Video Event arise. ");
processor.stopVideo();
}
public void muteVideo() {
log.info("Mute Video Event arise");
processor.muteVideo();
}
public void cleanMemory() {
log.info("Memory Cleaning Event arise. ");
Object obj = new Object();
WeakReference<Object> ref = new WeakReference<>(obj);
obj = null;
while (ref.get() != null) {
System.gc();
}
}
@Override
protected void paintComponent(Graphics g) {
if (image == null)
return;
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.dispose();
}
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("Image")) {
image = (BufferedImage) evt.getNewValue();
repaint();
}
if (evt.getPropertyName().equals("Stop")) {
log.info("Stop Playing Video. ");
setVideos();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
int rows = 1;
int cols = 1;
int size = rows * cols;
List<String> videos = new ArrayList<>();
//videos.add("C:\\Users\\Mi6\\Desktop\\test\\1.mp4");
videos.add("C:\\Users\\Mi6\\Desktop\\test\\Video257.mp4");
videos.add("C:\\Users\\Mi6\\Desktop\\test\\Video264.mp4");
videos.add("C:\\Users\\Mi6\\Desktop\\test\\Video258.mp4");
VideoPanel[] video = new VideoPanel[size];
for (int i = 0; i < size; i++)
video[i] = new VideoPanel(videos);
JFrame frame = new JFrame();
frame.setPreferredSize(new Dimension(1920, 1024));
frame.setLayout(new GridLayout(rows, cols, 1, 1));
for (int i = 0; i < size; i++)
frame.add(video[i]);
frame.pack();
frame.setVisible(true);
for (int i = 0; i < size; i++)
video[i].setVideos();
});
}
} `
[https://1drv.ms/u/s!As2o7s_R_SwDgg31nsERCTgd414A?e=dMPe5d](Test Videos)