Как найти позицию для видео перед игрой с JavaFX

#java #javafx #video-player

Вопрос:

Мне нужно было бы иметь возможность искать позицию до того, как начнется воспроизведение видео с помощью JavaFX 16

Когда медиаплееры ищут() или setStartTime() вызываются перед воспроизведением (), это нарушает вывод видео, и кадры больше не обновляются (звук все еще воспроизводится).

Продолжительность видео известна и не является неопределенной. Прослушиватель не печатает ошибок и не создает каких-либо существенных задержек (только иногда в начале, когда видео загружается по протоколу https), но та же проблема возникает и для локальных файлов. Поэтому я думаю, что это можно исключить. Я создаю и запускаю приложение с помощью Maven, mvn clean javafx:run .

Я попытался вызвать эти методы отдельно, а также один за другим внутри готового прослушивателя и снаружи в методе запуска.

Я использую JDK 11 (11.0.11 9-Ubuntu-0ubuntu2), maven 3.6.3 и openjfx 16 в GNU/Linux (Ubuntu 21.04)).

Я собрал минимальный пример и делаю ли я что-нибудь не так в следующем коде? Знаете ли вы, как это можно было бы решить или обойти? Заранее спасибо.

App.java

 package org.openjfx;

import javafx.application.Application;
import javafx.scene.control.Label;
import javafx.scene.Group;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.event.EventHandler;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.KeyCode;


public class App extends Application {

    private Scene scene;
    private MediaPlayer mediaPlayer;

    private final Runnable onReadyStateListener = new Runnable() {
        @Override
        public void run() {
            registerMinimalKeyEventHandling();

            // Calling seek or setStartTime will break video playback, frames won't be updated anymore (and only the sound keeps playing)
            Duration startTime = new Duration(6000L);
            mediaPlayer.seek(startTime);
            mediaPlayer.setStartTime(startTime);
            mediaPlayer.play();
        }
    };


    @Override
    public void start(Stage stage) {
        scene = new Scene(new Group(), 1600, 900);
        stage.setScene(scene);

        // The same issue occurs to local as well as remote videos
        //Media media = new Media("file:///tmp/big_buck_bunny_720p_10mb.mp4");
        Media media = new Media("https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_10mb.mp4");
        
        mediaPlayer = new MediaPlayer(media);
        mediaPlayer.setOnReady(onReadyStateListener);
        mediaPlayer.setOnStalled(onStalledListener);
        mediaPlayer.setOnError(onErrorListener);

        MediaView mediaView = new MediaView(mediaPlayer);
        mediaView.setFitWidth(1600);
        mediaView.setFitHeight(900);
        ((Group) scene.getRoot()).getChildren().add(mediaView);

        stage.show();
    }

    private void registerMinimalKeyEventHandling() {
        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                if (event.getCode() == KeyCode.SPACE) {
                    if (mediaPlayer.getStatus() == MediaPlayer.Status.PLAYING) {
                        mediaPlayer.pause();
                        return;
                    }
                    
                    mediaPlayer.play();
                } else if (event.getCode() == KeyCode.RIGHT) {
                    Duration seekTo = mediaPlayer.getCurrentTime().add(new Duration(1000L));
                    if (seekTo.greaterThan(mediaPlayer.getMedia().getDuration()) ) {
                        mediaPlayer.stop();
                        return;
                    }
                    mediaPlayer.seek(seekTo);
                }
            }
        });
    }

    private final Runnable onStalledListener = new Runnable() {
        @Override
        public void run() { System.out.println(mediaPlayer.getCurrentTime().toString()   ": Video stalled"); }    
    };

    private final Runnable onErrorListener = new Runnable() {
        @Override
        public void run() { System.out.println( mediaPlayer.getCurrentTime().toString()   ": Error occurred ("   mediaPlayer.getError().getLocalizedMessage()   ")"); }
    };

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

}
 

Тестовое видео big_buck_bunny_720p_10mb.mp4 от sample-videos.com.
Это видео воспроизводится без каких-либо проблем в VLC (3.0.12 Vetinari) и mplayer (1.4), и возможен поиск других позиций.

Output of ffprobe:

 Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/tmp/SampleVideo_1280x720_10mb.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    creation_time   : 1970-01-01T00:00:00.000000Z
    encoder         : Lavf53.24.2
  Duration: 00:01:02.32, start: 0.000000, bitrate: 1347 kb/s
    Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 
                             959 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
    Metadata:
      creation_time   : 1970-01-01T00:00:00.000000Z
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, 5.1, fltp, 383 kb/s (default)
    Metadata:
      creation_time   : 1970-01-01T00:00:00.000000Z
      handler_name    : SoundHandler
 

Настройка проекта:

pom.xml

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.openjfx</groupId>
    <artifactId>sample</artifactId>
    <version>1.0.0</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>16</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-media</artifactId>
            <version>16</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.3</version>
                <configuration>
                    <mainClass>org.openjfx.App</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

 

module-info.java

 module org.openjfx {
    requires javafx.controls;
    requires javafx.media;
    exports org.openjfx;
}
 

Ответ №1:

Это известная ошибка, которая является ошибкой регрессии, введенной в JavaFX 14. Это было решено в JavaFX 17. Лучшее решение — изменить версию JavaFX на 17 (или более позднюю; 17-текущая версия на момент написания статьи). Если по какой-либо причине это невозможно, будет работать возврат к версии 13 или более ранней, хотя вы увидите более длительное время ожидания, прежде чем видео будет готово.

В пом измените на

 <dependencies>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>17</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-media</artifactId>
        <version>17</version>
    </dependency>
</dependencies>