当前位置: 首页 > 工具软件 > FX Player > 使用案例 >

javafx 使用VLC media player播放器及加入视频缓冲区优化性能

狄旻
2023-12-01

在做的我的开源项目IceoLogy壁纸时,由于要做视频壁纸,javafx自带的mediaView性能并不理想,支持的格式也挺少的,几经搜索发现开源的VLC media player播放器有口皆碑,挺不错的,使用开源的demo运行发现cpu占用异常的高,我i7-9750H的CPU也占到了40%,稳定在25%左右,几经搜寻在外网找到了加入视屏缓冲区来优化的解决方案,加入缓冲区后播放同一视频cpu使用率稳定在8%左右

         <!-- vlc 播放器-->
        <dependency>
            <groupId>uk.co.caprica</groupId>
            <artifactId>vlcj-javafx</artifactId>
            <version>1.0.2</version>
        </dependency>

 <!-- https://mvnrepository.com/artifact/org.openjfx/javafx -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx</artifactId>
            <version>16</version>
            <type>pom</type>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-fxml -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>16</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-controls -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>16</version>
        </dependency>

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelBuffer;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import ui.util.CommonConst;
import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.base.MediaPlayer;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
import uk.co.caprica.vlcj.player.embedded.videosurface.CallbackVideoSurface;
import uk.co.caprica.vlcj.player.embedded.videosurface.VideoSurfaceAdapters;
import uk.co.caprica.vlcj.player.embedded.videosurface.callback.BufferFormat;
import uk.co.caprica.vlcj.player.embedded.videosurface.callback.BufferFormatCallback;
import uk.co.caprica.vlcj.player.embedded.videosurface.callback.RenderCallback;
import uk.co.caprica.vlcj.player.embedded.videosurface.callback.format.RV32BufferFormat;

import java.nio.ByteBuffer;

/**
 * This is a very simple example of a video player which uses the new WritableImage
 * of JavaFX with support for Buffers. The idea is to let VLC directly render into
 * this buffer and use the image directly in an ImageView without any explicit rendering
 * into a canvas or such thing. Only this brings the desired performance boost.
 * <p>
 * What I have not yet considered yet is any kind of synchronization.
 * I think an extension of the PixelBuffer to support some kine of double-buffering
 * would be the right thing to do.
 * <p>
 * The following dependencies should be enough to compile this example with OpenJDK 12.
 *
 * <dependencies>
 * <dependency>
 * <groupId>uk.co.caprica</groupId>
 * <artifactId>vlcj</artifactId>
 * <version>4.2.0</version>
 * </dependency>
 * <dependency>
 * <groupId>org.openjfx</groupId>
 * <artifactId>javafx-controls</artifactId>
 * <version>13-ea+12</version>
 * </dependency>
 * </dependencies>
 * <!-- vlc 播放器-->
 * <dependency>
 * <groupId>uk.co.caprica</groupId>
 * <artifactId>vlcj-javafx</artifactId>
 * <version>1.0.2</version>
 * </dependency>
 *
 * @author Michael Paus
 */
public class WritableImageVideoDemo extends Application {

    private MediaPlayerFactory mediaPlayerFactory;

    private EmbeddedMediaPlayer embeddedMediaPlayer;

    private WritableImage videoImage;

    private PixelBuffer<ByteBuffer> videoPixelBuffer;

    private ImageView videoImageView;

    @Override
    public void init() {
        mediaPlayerFactory = new MediaPlayerFactory();
        embeddedMediaPlayer = mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer();
        embeddedMediaPlayer.videoSurface().set(new FXCallbackVideoSurface());
    }

    @Override
    public final void start(Stage primaryStage) throws Exception {
        StackPane root = new StackPane();
        root.setStyle("-fx-background-color: black;");
        videoImageView = new ImageView();
        videoImageView.setPreserveRatio(true);
        videoImageView.fitWidthProperty().bind(root.widthProperty());
        videoImageView.fitHeightProperty().bind(root.heightProperty());
        root.getChildren().add(videoImageView);
        setMedia("D:\\desserts\\2.mp4");
        Scene scene = new Scene(root, 1920, 1080);
        primaryStage.setScene(scene);
        primaryStage.initStyle(StageStyle.TRANSPARENT);
        primaryStage.show();
       

    }

    @Override
    public final void stop() throws Exception {
        embeddedMediaPlayer.controls().stop();
        embeddedMediaPlayer.release();
        mediaPlayerFactory.release();
    }

    private class FXCallbackVideoSurface extends CallbackVideoSurface {
        FXCallbackVideoSurface() {
            super(new FXBufferFormatCallback(), new FXRenderCallback(), true, VideoSurfaceAdapters.getVideoSurfaceAdapter());
        }
    }

    private class FXBufferFormatCallback implements BufferFormatCallback {
        private int sourceWidth;
        private int sourceHeight;

        @Override
        public BufferFormat getBufferFormat(int sourceWidth, int sourceHeight) {
            this.sourceWidth = sourceWidth;
            this.sourceHeight = sourceHeight;
            return new RV32BufferFormat(sourceWidth, sourceHeight);
        }

        @Override
        public void allocatedBuffers(ByteBuffer[] buffers) {
            assert buffers[0].capacity() == sourceWidth * sourceHeight * 4;
            PixelFormat<ByteBuffer> pixelFormat = PixelFormat.getByteBgraPreInstance();
            videoPixelBuffer = new PixelBuffer<>(sourceWidth, sourceHeight, buffers[0], pixelFormat);
            videoImage = new WritableImage(videoPixelBuffer);
            videoImageView.setImage(videoImage);
        }
    }

    private class FXRenderCallback implements RenderCallback {
        @Override
        public void display(MediaPlayer mediaPlayer, ByteBuffer[] nativeBuffers, BufferFormat bufferFormat) {
            Platform.runLater(() -> {
                videoPixelBuffer.updateBuffer(pb -> null);
            });
        }
    }

    public void setMedia(String path) {
        embeddedMediaPlayer.media().play(path
                , ":no-audio"
                , ":rtsp=tcp"
                , ":codec=ffmpeg"
                , ":vout=any"
                , ":avcodec-threads=1"
                , ":avcodec-hw=any"
                , ":network-caching=200"
                , ":prefetch-buffer-size=1024"
                , ":prefetch-read-size=65535"
                , ":file-caching=1000"
        );
        //循环播放
        embeddedMediaPlayer.controls().setRepeat(true);
    }

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

}

// Launch via this class to avoid module system headaches.
class WritableImageVideoDemoLauncher {
    public static void main(String[] args) {
        WritableImageVideoDemo.main(args);
    }
}

但是有个缺点就是需要安装VLC media player播放器,没有安装是不能播放的,可以使用以下代码通过扫描注册表检测是否安装VLC media player播放器。

 <!-- https://mvnrepository.com/artifact/com.jianggujin/JRegistry -->
        <dependency>
            <groupId>com.jianggujin</groupId>
            <artifactId>JRegistry</artifactId>
            <version>1.0.0</version>
        </dependency>
import com.jianggujin.registry.JExecResult;
import com.jianggujin.registry.JQueryOptions;
import com.jianggujin.registry.JRegistry;

import java.io.IOException;

public class JTest {
    public static void main(String[] args) throws IOException, InterruptedException {
        JExecResult result = JRegistry.query("HKEY_CLASSES_ROOT\\Applications\\",
                new JQueryOptions().useF("\"vlc.exe\""));
        JRegistry.dump(result);

        System.out.println("is install vlc video player:"+result.isSuccess());

    }
}

 类似资料: