简体   繁体   English

反复播放同一视频几个小时后,JavaFX 视频卡顿

[英]JavaFX video stutter after playing same video over and over for a couple of hours

On Ubuntu 18.04 I have a JavaFX 13 app that plays the same video over and over using javafx.scene.media.MediaPlayer and javafx.scene.media.MediaView.在 Ubuntu 18.04 上,我有一个 JavaFX 13 应用程序,它使用 javafx.scene.media.MediaPlayer 和 javafx.scene.media.MediaView 一遍又一遍地播放相同的视频。 In each iteration it loads the MediaPlayer with same media via url and then plays it.在每次迭代中,它通过 url 加载具有相同媒体的 MediaPlayer,然后播放它。 At first the video plays smoothly but after about an hour it starts stuttering in my machine with 8GB of RAM and does not happen (at least in a few hours) in my machine with 32GB of RAM.起初,视频播放流畅,但大约一个小时后,它在我的 8GB 内存机器上开始卡顿,并且在我的 32GB 内存机器上不会发生(至少在几个小时内)。

Since its plays smooth initially I am guessing its not a codec issue.由于它最初播放流畅,我猜测它不是编解码器问题。 Since it gets worse with time I am guessing its a memory leak somewhere.随着时间的推移它变得更糟,我猜测它是某处的内存泄漏。 Monitoring my JVM using visualvm it does not appear to be a memory or thread leak in my app.使用 visualvm 监控我的 JVM 它似乎不是我的应用程序中的内存或线程泄漏。

Below is a stripped down version of my app in a single java class file.下面是我的应用程序在单个 Java 类文件中的精简版本。 Any tips would be greatly appreciated.任何提示将非常感谢。

package javafxtest;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;

public class JavaFxVideoTestApp extends Application {
    private static final Logger LOGGER = LoggerFactory.getLogger(JavaFxVideoTestApp.class);

    MediaPlayer mediaPlayer;
    MediaView mediaView;

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Thread.setDefaultUncaughtExceptionHandler(JavaFxVideoTestApp::showError);

        BorderPane borderPane = new BorderPane();
        Scene scene = new Scene(borderPane);
        primaryStage.setScene(scene);

        mediaView = new MediaView();
        borderPane.setCenter(mediaView);

        primaryStage.setFullScreen(true);
        primaryStage.show();

        loadEvent();
    }

    private static void showError(Thread thread, Throwable throwable) {
        LOGGER.error("Unhandled error. Thread: {}", thread.getName(), throwable);
    }

    public void loadEvent() throws MalformedURLException, URISyntaxException {
        String videoFileURI = (new URL("https://dl.dropboxusercontent.com/s/b8lyrt5r61oxxnc/job-chasing-his-illusive-dreams-dec-2017.mp4")).toURI().toString();

        // Create a new MediaPlayer
        resetMediaPlayer("loadEvent");

        Media media = new Media(videoFileURI);
        LOGGER.debug("LoadEvent new video loaded : {}", media.getSource());
        mediaPlayer = new MediaPlayer(media);
        mediaPlayer.setStartTime(Duration.ZERO);
        mediaPlayer.errorProperty().addListener((observable, oldValue, newValue) ->
                LOGGER.error(String.format("MediaPlayer error. videoFileURI: %s error: '%s'", videoFileURI, newValue)));
        mediaPlayer.statusProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue == MediaPlayer.Status.READY) {
                LOGGER.info("MediaPlayer status: Ready");
                showEvent();
            }
        });
        mediaPlayer.setCycleCount(1);
        mediaPlayer.setMute(true);
        mediaPlayer.setOnEndOfMedia(() -> {
            try {
                loadEvent();
            } catch (MalformedURLException|URISyntaxException e) {
                LOGGER.error("Error loading event", e);
            }
        });
        mediaView.setMediaPlayer(mediaPlayer);
    }

    public void showEvent() {
        if (mediaPlayer != null) {
            MediaPlayer.Status status = mediaPlayer.getStatus();
            if (status == MediaPlayer.Status.READY || status == MediaPlayer.Status.STOPPED) {
                mediaPlayer.setStartTime(Duration.ZERO);
                mediaPlayer.setStopTime(mediaPlayer.getMedia().getDuration());
                mediaPlayer.setCycleCount(MediaPlayer.INDEFINITE);
                mediaPlayer.play();
            }
        } else {
            LOGGER.error("showEvent video mediaplayer NULL");
        }
    }

    private void resetMediaPlayer(String caller) {
        if (mediaPlayer != null) {
            LOGGER.debug("resetMediaPlayer stop. caller: {}", caller);
            if ((mediaPlayer.getStatus() != MediaPlayer.Status.UNKNOWN) && (mediaPlayer.getStatus() != MediaPlayer.Status.DISPOSED)) {
                try {
                    mediaPlayer.stop();
                    mediaPlayer.dispose();
                    mediaPlayer = null;
                } catch (Exception e) {
                    LOGGER.error("Error while stopping and disposing media player");
                }
            }
        }
    }
}

This seems to be a issue of the cycelingprocess and allocation of the Mediaplayer's Memory.这似乎是媒体播放器内存的循环过程和分配的问题。 I had a similar issue, sady I don't know why this happens, but I have a Workarround, to imitate a cycle.我有一个类似的问题,可悲的是我不知道为什么会发生这种情况,但是我有一个 Workarround 来模仿一个循环。

My workarround is this:我的解决方法是这样的:


    public void startPlayer(){
        mediaPlayer = new MediaPlayer(media);
        mediaView.setMediaPlayer(mediaPlayer);
        mediaPlayer.play();

        mediaPlayer.setOnEndOfMedia(()->{
                    //frees all the allocated Memory
                    mediaPlayer.dispose();
                    mediaView.setMediaPlayer(null);
                    startPlayer();
                });
    }

This will create a new Player each time the cycle has ended.这将在每次循环结束时创建一个新玩家。

I don't know if this will work for you in the aspect of smoothness of the "Video cycle".我不知道这在“视频周期”的流畅性方面是否对您有用。 But you can give it a try.但是你可以试一试。 Maybe you can to this after every 10 video cycles or so, to obtain smoothness.也许您可以在每 10 个视频循环后进行此操作,以获得平滑度。

mediaPlayer.setOnEndOfMedia(() -> {
            try {
                mediaPlayer.stop();
                mediaPlayer.dispose();
                mediaPlayer = null;
                loadEvent(); ...

I have tried running my app with gstreamer 1.16.2.我尝试使用 gstreamer 1.16.2 运行我的应用程序。 Since no backports for it was available for Ubuntu 18.04 I ran my app in a VM with Ubuntu 20 (latest nightly) which has gstreamer 1.16.2-2.由于没有可用于 Ubuntu 18.04 的向后移植,我在装有 gstreamer 1.16.2-2 的 Ubuntu 20(每晚)的 VM 中运行我的应用程序。 The VM has 4GB ram and 1 cpu. VM 有 4GB 内存和 1 个 CPU。 This is lesser resources than my original test machine.这比我原来的测试机资源少。

With gstreamer 1.16.2-2 in Ubuntu 20 I am not seeing a stutter after running my test for 1.25 hours.使用 Ubuntu 20 中的 gstreamer 1.16.2-2 在运行我的测试 1.25 小时后,我没有看到口吃。 To me this confirms that problem is in gstream 1.14 and fixed in gstreamer 1.16.对我来说,这证实了问题出在 gstream 1.14 中,并在 gstreamer 1.16 中修复。 Thanks to all who tried to help.感谢所有试图提供帮助的人。

UPDATE: Above was based on testing in an Ubuntu 20.04 VM.更新:以上基于在 Ubuntu 20.04 VM 中的测试。 I then upgraded my physical test machine to Ubuntu 20.04.然后我将我的物理测试机升级到 Ubuntu 20.04。

Much to my surprise the video stutter showed in my app immediately (not even waiting for 30 minutes as before).令我惊讶的是,视频卡顿立即显示在我的应用程序中(甚至不像以前那样等待 30 分钟)。 Reading up on it I found that some gnome shell extensions can cause video stutter. 阅读它我发现一些 gnome shell 扩展会导致视频卡顿。 So I removed the following gnome shell extension:所以我删除了以下 gnome shell 扩展:

# See what gnome shell extensions are installed
apt list --installed | grep -i extension

# Remove gnome shell extensions found
sudo apt-get remove gnome-shell-extension-appindicator gnome-shell-extension-desktop-icons gnome-shell-extension-ubuntu-dock

After that I restarted my app and the video stutter was gone.之后,我重新启动了我的应用程序,视频卡顿消失了。 I then waited 30 minutes to see if stutter appeared like before.然后我等了 30 分钟,看看口吃是否像以前一样出现。 The stutter is noticeable unfortunately.不幸的是,口吃很明显。 I have unmarked this answer as an accepted answer.我已取消将此答案标记为已接受的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM