bytedeco / javacv

Java interface to OpenCV, FFmpeg, and more
Other
7.53k stars 1.58k forks source link

Player is not smooth, Frame are skipping #1427

Open MachineIntelligence6 opened 4 years ago

MachineIntelligence6 commented 4 years ago

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)

saudet commented 4 years ago

Be sure to try JavaFX: https://groups.google.com/forum/#!topic/javacv/uTpd9FGRvyE