= JavaFXでの利用2 ==
これも同じく
を参考にし、簡単化、かつ拡張した。ウインドウの大きさに応じてスクリーンも拡縮する。プレイバック位置を示し操作できるスライダを追加。
import java.nio.*; import com.google.common.eventbus.*; import com.sun.jna.*; import javafx.animation.*; import javafx.application.*; import javafx.application.Platform; import javafx.beans.property.*; import javafx.beans.value.*; import javafx.event.*; import javafx.geometry.*; import javafx.scene.*; import javafx.scene.control.*; import javafx.scene.image.*; import javafx.scene.layout.*; import javafx.stage.*; import javafx.util.*; import uk.co.caprica.vlcj.component.*; import uk.co.caprica.vlcj.discovery.*; import uk.co.caprica.vlcj.player.direct.*; import uk.co.caprica.vlcj.player.direct.format.*; public class Resizable extends Application { // "c:/users/admin/desktop/video.mp4"ではだめ、native(Windows)に渡されるため private static final String VIDEO_FILE = "c:\\users\\admin\\desktop\\video.mp4"; // mediaPlayerComponentはGUI階層に加えられず、機能が利用されるだけ。ここで保持しておく必要がある。 private DirectMediaPlayerComponent mediaPlayerComponent; private DirectMediaPlayer mediaPlayer; @Override public void start(Stage stage) throws Exception { // 動画スクリーンを作成する final MediaScreen mediaScreen = new MediaScreen(); // プレイバック位置スライダ PositionSlider positionSlider = new PositionSlider(); positionSlider.eventBus.register(new Object() { @Subscribe public void accept(Long value) { mediaPlayer.setTime(value); } }); // 定期的な処理を行うためのタイマー TimeInterpolator interpolator = new TimeInterpolator(); Timeline timer = new Timeline(new KeyFrame(new Duration(200), new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { long time = mediaPlayer.getTime(); time = interpolator.getCurrentTime(time); positionSlider.setTime(time); } })); // メディアプレイヤーコンポーネント mediaPlayerComponent = new CanvasPlayerComponent(mediaScreen.getPixelWriter(), new MediaCallback() { public void mediaCallback(int width, int height) { long length = mediaPlayer.getLength(); positionSlider.setLength(length); mediaScreen.setMediaSize(mediaPlayer, width, height); } }); mediaPlayer = mediaPlayerComponent.getMediaPlayer(); // レイアウト BorderPane borderPane = new BorderPane(); borderPane.setCenter(mediaScreen.getNode()); positionSlider.setHeight(30); borderPane.setBottom(positionSlider.getNode()); Scene scene = new Scene(borderPane); stage.setScene(scene); mediaPlayerComponent.getMediaPlayer().prepareMedia(VIDEO_FILE); mediaPlayerComponent.getMediaPlayer().start(); stage.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent event) { Platform.exit(); System.exit(0); } }); stage.show(); // タイマーの開始 timer.setCycleCount(Timeline.INDEFINITE); timer.play(); } /** 動画表示スクリーン */ public static class MediaScreen { Pane pane; ImageView imageView; WritableImage writableImage; private FloatProperty videoSourceRatioProperty; public MediaScreen() { this.pane = new Pane(); pane.widthProperty().addListener((observable, oldValue, newValue) -> { fitImageViewSize(newValue.floatValue(), (float) pane.getHeight()); }); pane.heightProperty().addListener((observable, oldValue, newValue) -> { fitImageViewSize((float) pane.getWidth(), newValue.floatValue()); }); Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds(); int screenWidth = (int) visualBounds.getWidth(); int screenHeight = (int) visualBounds.getHeight(); writableImage = new WritableImage(screenWidth, screenHeight); imageView = new ImageView(writableImage); pane.setPrefSize(screenWidth / 2, screenHeight / 2); pane.getChildren().add(imageView); videoSourceRatioProperty = new SimpleFloatProperty(0.4f); videoSourceRatioProperty.addListener((observable, oldValue, newValue) -> { fitImageViewSize((float) pane.getWidth(), (float) pane.getHeight()); }); } public Node getNode() { return pane; } public PixelWriter getPixelWriter() { return writableImage.getPixelWriter(); } public void setMediaSize(DirectMediaPlayer mp, int width, int height) { videoSourceRatioProperty.set((float) height / (float) width); } private void fitImageViewSize(float width, float height) { float fitHeight = videoSourceRatioProperty.get() * width; if (fitHeight > height) { imageView.setFitHeight(height); double fitWidth = height / videoSourceRatioProperty.get(); imageView.setFitWidth(fitWidth); imageView.setX((width - fitWidth) / 2); imageView.setY(0); } else { imageView.setFitWidth(width); imageView.setFitHeight(fitHeight); imageView.setY((height - fitHeight) / 2); imageView.setX(0); } } } /** プレイバック位置スライダ */ public static class PositionSlider { Slider slider; EventBus eventBus = new EventBus(); boolean setting = false; PositionSlider() { slider = new Slider(); slider.valueProperty().addListener((ObservableValue<? extends Number> ov, Number oldValue, Number newValue) -> { if (setting) return; eventBus.post(newValue.longValue()); }); } public Node getNode() { return slider; } public void setHeight(int height) { slider.setPrefHeight(30); slider.setMaxHeight(30); } public void setLength(long length) { setting = true; try { slider.setMax(length); } finally { setting = false; } } public void setTime(long time) { setting = true; try { slider.setValue(time); } finally { setting = false; } } } /** メディアプレーヤーコンポーネント */ static private class CanvasPlayerComponent extends DirectMediaPlayerComponent { private PixelWriter pixelWriter; PixelFormat<ByteBuffer> pixelFormat; public CanvasPlayerComponent(PixelWriter pixelWriter, final MediaCallback mediaCallback) { super(new BufferFormatCallback() { @Override public BufferFormat getBufferFormat(int sourceWidth, int sourceHeight) { Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds(); Platform.runLater(() -> { mediaCallback.mediaCallback(sourceWidth, sourceHeight); }); return new RV32BufferFormat((int) visualBounds.getWidth(), (int) visualBounds.getHeight()); } }); this.pixelWriter = pixelWriter; pixelFormat = PixelFormat.getByteBgraPreInstance(); } @Override public void display(DirectMediaPlayer mediaPlayer, Memory[] nativeBuffers, BufferFormat bufferFormat) { Platform.runLater(() -> { Memory nativeBuffer = mediaPlayer.lock()[0]; try { ByteBuffer byteBuffer = nativeBuffer.getByteBuffer(0, nativeBuffer.size()); pixelWriter.setPixels(0, 0, bufferFormat.getWidth(), bufferFormat.getHeight(), pixelFormat, byteBuffer, bufferFormat.getPitches()[0]); } finally { mediaPlayer.unlock(); } }); } } public interface MediaCallback { public void mediaCallback(int with, int height); } /** * MediaPlayerの返す「時刻」は荒い値なので補完する。 * * @see https://github.com/caprica/vlcj/issues/74 */ public static class TimeInterpolator { private long lastPlayTime = 0; private long lastPlayTimeGlobal = 0; public long getCurrentTime(long currentTime) { if (lastPlayTime == currentTime && lastPlayTime != 0) { currentTime += System.currentTimeMillis() - lastPlayTimeGlobal; } else { lastPlayTime = currentTime; lastPlayTimeGlobal = System.currentTimeMillis(); } return currentTime; } } public static void main(String[] args) { new NativeDiscovery().discover(); Application.launch(Resizable.class); } }