/* * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javafx.scene.media; import com.sun.javafx.geom.BaseBounds; import com.sun.javafx.geom.transform.Affine3D; import com.sun.javafx.geom.transform.BaseTransform; import com.sun.javafx.scene.DirtyBits; import com.sun.javafx.scene.NodeHelper; import com.sun.javafx.scene.media.MediaViewHelper; import com.sun.javafx.sg.prism.MediaFrameTracker; import com.sun.javafx.sg.prism.NGNode; import com.sun.javafx.tk.Toolkit; import com.sun.media.jfxmediaimpl.HostUtils; import com.sun.media.jfxmedia.control.MediaPlayerOverlay; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.property.*; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableObjectValue; import javafx.beans.value.ObservableValue; import javafx.collections.ObservableMap; import javafx.event.EventHandler; import javafx.geometry.NodeOrientation; import javafx.geometry.Rectangle2D; import javafx.scene.Node; import javafx.scene.Parent; /** * A {@link Node} that provides a view of {@link Media} being played by a * {@link MediaPlayer}. * *
The following code snippet provides a simple example of an * {@link javafx.application.Application#start(javafx.stage.Stage) Application.start()} * method which displays a video: *
*{@code * public void start(Stage stage) { * // Create and set the Scene. * Scene scene = new Scene(new Group(), 540, 209); * stage.setScene(scene); * * // Name and display the Stage. * stage.setTitle("Hello Media"); * stage.show(); * * // Create the media source. * String source = getParameters().getRaw().get(0); * Media media = new Media(source); * * // Create the player and set to play automatically. * MediaPlayer mediaPlayer = new MediaPlayer(media); * mediaPlayer.setAutoPlay(true); * * // Create the view and add it to the Scene. * MediaView mediaView = new MediaView(mediaPlayer); * ((Group) scene.getRoot()).getChildren().add(mediaView); * } * }* The foregoing code will display the video as: *
MediaPlayer
error into a
* Bean
event.
*/
private class MediaErrorInvalidationListener implements InvalidationListener {
@Override public void invalidated(Observable value) {
ObservableObjectValueMediaPlayer
errors to events. */
private InvalidationListener errorListener = new MediaErrorInvalidationListener();
/** Listener which causes the geometry to be updated when the media dimension changes. */
private InvalidationListener mediaDimensionListener = value -> {
NodeHelper.markDirty(this, DirtyBits.NODE_VIEWPORT);
NodeHelper.geomChanged(this);
};
/** Listener for decoded frame rate. */
private com.sun.media.jfxmedia.events.VideoFrameRateListener decodedFrameRateListener;
private boolean registerVideoFrameRateListener = false;
/** Creates a decoded frame rate listener. Will return null
if
* the security manager does not permit retrieve system properties or if
* VIDEO_FRAME_RATE_PROPERTY_NAME is not set to "true."
*/
private com.sun.media.jfxmedia.events.VideoFrameRateListener createVideoFrameRateListener() {
String listenerProp = null;
try {
listenerProp = System.getProperty(VIDEO_FRAME_RATE_PROPERTY_NAME);
} catch (Throwable t) {
}
if (listenerProp == null || !Boolean.getBoolean(VIDEO_FRAME_RATE_PROPERTY_NAME)) {
return null;
} else {
return videoFrameRate -> {
Platform.runLater(() -> {
ObservableMap props = getProperties();
props.put(VIDEO_FRAME_RATE_PROPERTY_NAME, videoFrameRate);
});
};
}
}
/***************************************** Media Player Overlay support ***************************/
private MediaPlayerOverlay mediaPlayerOverlay = null;
private ChangeListenerMediaView
instance with no associated
* {@link MediaPlayer}.
*/
public MediaView() {
getStyleClass().add(DEFAULT_STYLE_CLASS);
setSmooth(Toolkit.getToolkit().getDefaultImageSmooth());
decodedFrameRateListener = createVideoFrameRateListener();
setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT);
}
/**
* Creates a MediaView
instance associated with the specified
* {@link MediaPlayer}. Equivalent to
*
* MediaPlayer player; // initialization omitted
* MediaView view = new MediaView();
* view.setMediaPlayer(player);
*
*
* @param mediaPlayer the {@link MediaPlayer} the playback of which is to be
* viewed via this class.
*/
public MediaView(MediaPlayer mediaPlayer) {
this();
setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT);
setMediaPlayer(mediaPlayer);
}
/**
* The mediaPlayer
whose output will be handled by this view.
*
* Setting this value does not affect the status of the MediaPlayer
,
* e.g., if the MediaPlayer
was playing prior to setting
* mediaPlayer
then it will continue playing.
*
* @see MediaException
* @see MediaPlayer
*/
private ObjectPropertyMediaPlayer
whose output will be handled by this view.
* @param value the associated MediaPlayer
.
*/
public final void setMediaPlayer (MediaPlayer value) {
mediaPlayerProperty().set(value);
}
/**
* Retrieves the MediaPlayer
whose output is being handled by
* this view.
* @return the associated MediaPlayer
.
*/
public final MediaPlayer getMediaPlayer() {
return mediaPlayer == null ? null : mediaPlayer.get();
}
public final ObjectPropertyMediaView
.
*
* @see MediaErrorEvent
*/
private ObjectPropertytrue
.
*/
private BooleanProperty preserveRatio;
/**
* Sets whether to preserve the media aspect ratio when scaling.
* @param value whether to preserve the media aspect ratio.
*/
public final void setPreserveRatio(boolean value) {
preserveRatioProperty().set(value);
};
/**
* Returns whether the media aspect ratio is preserved when scaling.
* @return whether the media aspect ratio is preserved.
*/
public final boolean isPreserveRatio() {
return preserveRatio == null ? true : preserveRatio.get();
}
public final BooleanProperty preserveRatioProperty() {
if (preserveRatio == null) {
preserveRatio = new BooleanPropertyBase(true) {
@Override
protected void invalidated() {
if (HostUtils.isIOS()) {
updateOverlayPreserveRatio();
}
else {
NodeHelper.markDirty(MediaView.this, DirtyBits.NODE_VIEWPORT);
NodeHelper.geomChanged(MediaView.this);
}
}
@Override
public Object getBean() {
return MediaView.this;
}
@Override
public String getName() {
return "preserveRatio";
}
};
}
return preserveRatio;
}
/**
* If set to true
a better quality filtering
* algorithm will be used when scaling this video to fit within the
* bounding box provided by fitWidth
and fitHeight
or
* when transforming.
*
* If set to false
a faster but lesser quality filtering
* will be used.
*
* The default value depends on platform configuration.
*/
private BooleanProperty smooth;
/**
* Sets whether to smooth the media when scaling.
* @param value whether to smooth the media.
*/
public final void setSmooth(boolean value) {
smoothProperty().set(value);
}
/**
* Returns whether to smooth the media when scaling.
* @return whether to smooth the media
*/
public final boolean isSmooth() {
return smooth == null ? false : smooth.get();
}
public final BooleanProperty smoothProperty() {
if (smooth == null) {
smooth = new BooleanPropertyBase() {
@Override
protected void invalidated() {
NodeHelper.markDirty(MediaView.this, DirtyBits.NODE_SMOOTH);
}
@Override
public Object getBean() {
return MediaView.this;
}
@Override
public String getName() {
return "smooth";
}
};
}
return smooth;
}
// PENDING_DOC_REVIEW
/**
* Defines the current x coordinate of the MediaView
origin.
*/
private DoubleProperty x;
/**
* Sets the x coordinate of the MediaView
origin.
* @param value the x coordinate of the origin of the view.
*/
public final void setX(double value) {
xProperty().set(value);
}
/**
* Retrieves the x coordinate of the MediaView
origin.
* @return the x coordinate of the origin of the view.
*/
public final double getX() {
return x == null ? 0.0 : x.get();
}
public final DoubleProperty xProperty() {
if (x == null) {
x = new DoublePropertyBase() {
@Override
protected void invalidated() {
if (HostUtils.isIOS()) {
updateOverlayX();
}
else {
NodeHelper.markDirty(MediaView.this, DirtyBits.NODE_GEOMETRY);
NodeHelper.geomChanged(MediaView.this);
}
}
@Override
public Object getBean() {
return MediaView.this;
}
@Override
public String getName() {
return "x";
}
};
}
return x;
}
// PENDING_DOC_REVIEW
/**
* Defines the current y coordinate of the MediaView
origin.
*/
private DoubleProperty y;
/**
* Sets the y coordinate of the MediaView
origin.
* @param value the y coordinate of the origin of the view.
*/
public final void setY(double value) {
yProperty().set(value);
}
/**
* Retrieves the y coordinate of the MediaView
origin.
* @return the y coordinate of the origin of the view.
*/
public final double getY() {
return y == null ? 0.0 : y.get();
}
public final DoubleProperty yProperty() {
if (y == null) {
y = new DoublePropertyBase() {
@Override
protected void invalidated() {
if (HostUtils.isIOS()) {
updateOverlayY();
}
else {
NodeHelper.markDirty(MediaView.this, DirtyBits.NODE_GEOMETRY);
NodeHelper.geomChanged(MediaView.this);
}
}
@Override
public Object getBean() {
return MediaView.this;
}
@Override
public String getName() {
return "y";
}
};
}
return y;
}
// PENDING_DOC_REVIEW
/**
* Determines the width of the bounding box within which the source media is
* resized as necessary to fit. If value ≤ 0
, then the width
* of the bounding box will be set to the natural width of the media, but
* fitWidth
will be set to the supplied parameter, even if
* non-positive.
*
* See {@link #preserveRatioProperty preserveRatio} for information on interaction
* between media views fitWidth
, fitHeight
and
* preserveRatio
attributes.
*
value ≤ 0
, then the height
* of the bounding box will be set to the natural height of the media, but
* fitHeight
will be set to the supplied parameter, even if
* non-positive.
*
* See {@link #preserveRatioProperty preserveRatio} for information on interaction
* between media views fitWidth
, fitHeight
and
* preserveRatio
attributes.
*
viewport
to null will clear the viewport.
*/
private ObjectProperty