Controlling Media Playback


Launch Application Download NetBeans Project

This article discusses creating a full-functional media player with graphical UI elements that control the media playback.

A media player with UI controls Figure 1: Media Player With Custom Controls

Both control panel and graphical elements are encoded by using the JavaFX Script programming language. For your media player, you can follow the same approach, or pre-render graphical controls as bitmaps in Adobe Photoshop.

To create a media player you need to implement the structure of the three nested media objects, encode graphical controls, and add some logic for playback functions.

Logical structure of an application that implements a media player with UI controls Figure 2: Structure of a Media Player With Custom Controls

Embedding Media Into the Web Page explains in detail how to create a JavaFX media player and integrate it into the application's scene, but this article focuses mainly on creating control elements. In this application, the media control panel consists of three elements: playButton, progress, and volumeControl, as shown in the following figure.

Media control panel Figure 3: Media Control Panel

Creating Controls


  1. To construct the playButton control, draw three graphical elements: circle, playShape, pauseShape.

    Play/Pause button


    circle A substrate element with an applied animated effect.
    playShape A control to process the pause action.
    pauseShape A control to process the play action.

    Refer to the PlayButton.fx file for implementation details.

  2. Add a pulsing effect to circle to visually enhance that element.
Source Code

var boomTimeline:Timeline = Timeline {
        repeatCount: Timeline.INDEFINITE
        autoReverse: true
        keyFrames: [
            at(0ms) {
                offset => 0.9
            },
            at(500ms) {
                offset => 0.7 tween Interpolator.EASEBOTH
            }
        ]
    }

    The boomTimeline animation construction in the PlayButton.fx file alters the offset variable, thus varying the offset of the radial gradient fill for the circle element. The animation is launched in the makeInitUI() function when the application starts, which produces an effect of perpetually pulsing button.

  1. Construct the progress control by drawing the following graphical elements: frame, bar, progressBar, button, status, and balloonLabel.

    The progress bar


    frame A substrate element.
    bar An element to handle a cursor style change.
    progressBar A bar to visualize the current position playback timeline.
    button A slider's knob to visualize a proportion of the elapsed to the remaining playback time. Can be dragged to enable playing the particular media fragment.
    status A text field to display the elapsed and the remaining playback time.
    ballonLabel An element to display the current media time when the button element is being dragged.

    Refer to the Slider.fx and the TextBalloon.fx files for implementation details.

  2. Draw the volumeControl element that consists of a speaker icon and of a vertical slider.

    Volume control


    speaker A control element with an applied animated effect to enable the mute and unmute function.
    slider A vertical version of the slider element to alter the volume level of the media. The element becomes visible when the speaker control is hovered over.

    Refer to the VolumeControl.fx, Slider.fx and SpeakerIcon.fx files for implementation details.

  3. Add a live effect to the speaker control. The following animation construction visualizes and hides, in turn, arc elements in blue and white to produce an effect of radio waves being emitted from a live radio speaker.
Source Code
var t: Timeline = Timeline {
        repeatCount: -1
        keyFrames: [
            KeyFrame {
                time: 0s
                action: function() {
                    arc1.visible = false;
                    arc2.visible = false;
                }
            },
            KeyFrame {
                time: 500ms
                action: function() {
                    arc1.visible = true;
                    arc2.visible = false;
                }
            },
            KeyFrame {
                time: 1000ms
                action: function() {
                    arc1.visible = true;
                    arc2.visible = true;
                }
            },
            KeyFrame {
                time: 1500ms
                action: function() {
                    arc1.visible = false;
                    arc2.visible = false;

                    arc1.stroke = if(arc1.stroke == Color.BLUE) Color.WHITE else Color.BLUE;
                    arc2.stroke = if(arc2.stroke == Color.BLUE) Color.WHITE else Color.BLUE;
                }
            }
        ]
}

  1. Add the created graphical elements to the media control panel. In this example, the BottomController object is a CustomNode extension. It sets values for all public variables of control elements and adds the variables to the panel's content.
Source Code
public class BottomController extends CustomNode {

...

//Sets the public variables of the control elements

    override function create(): Node {
        Group {
            content: [
                playButton,
                progress,
                volumeControl,
            ]
        }
    }
}

Adding Functional Logic


After all the controls have been created and added to the control panel, add functional logic to manage the media playback and make your application interactive.

  1. To enable your graphical elements to process the mouse events, add the corresponding event-handling functions to your code.

    Control Function Action
    playShape
    onMouseClicked
    Enables media playback and sets the "pause" view for the play/pause button.
    See the PlayButton.fx file.
    pauseShape
    onMouseClicked
    Disables media playback and sets the "play" view for the play/pause button.
    See the PlayButton.fx file.
    bar
    onMouseClicked
    Defines the progress bar position for vertical and horizontal sliders.
    See the Slider.fx file.
    onMouseEntered
    Sets the hand cursor style.
    See the Slider.fx file.
    onMouseExited
    Sets the default cursor style.
    See the Slider.fx file.
    button
    onMouseDragged
    Defines the position for the dragged knob and passes it to the media player.
    See the Slider.fx file.
    onMouseEntered
    Enables showing the ballonLabel element.
    See the Slider.fx file.
    onMouseDragged
    Disables media playback and sets the "play" view for the play/pause button.
    See the Slider.fx file.
    slider
    onMouseEntered
    Keeps the volume slider visible.
    See the VolumeControl.fx file.
    onMouseExited
    Hides the volume slider.
    See the VolumeControl.fx file.
    speaker
    onMouseEntered
    Enables showing the volume slider.
    See the VolumeControl.fx file.
    onMouseExited
    Hides the volume slider.
    See the VolumeControl.fx file.
    onMouseClicked
    Mute and unmute the audio output.
    See the VolumeControl.fx file.

  2. Invoke playback control functions for the media player object, myPlayer.

Volume and Mute

The following code fragment in the BottomController file binds the volume level and the mute status retrieved from the control elements to the corresponding variables of the myPlayer object.

Source Code
		
var volumeControl = VolumeControl {
        x: bind width-30
        y: bind (height-20)/2.0 + 20
        volume: bind myPlayer.volume with inverse
        mute: bind myPlayer.mute with inverse
   ...
}

Current Time

The current position of the progress bar slider is bound to the current time of the playback through the progress variable of myPlayer. The following code fragment from the BottomController file binds the slider's value to the progress variable.

Source Code
var progress: Slider = Slider {
        orientation: Slider.HORIZONTAL
        x: bind playButton.centerX+playButton.radius+30
        y: bind (height-ProgressSlider_height)/2.0
        width: bind width-(progress.x+80)
        value: bind myPlayer.progress with inverse;

}

The progress is taken into account when the currentTime variable is calculated. See the myPlayer file.

Source Code
public var progress: Number = 0 on replace {
        if(media != null) {
            if(media.duration.toMillis() != java.lang.Double.NEGATIVE_INFINITY and
               media.duration.toMillis() != java.lang.Double.POSITIVE_INFINITY) {
                skip = true;
                currentTime = media.duration.mul(progress);
                skip = false;
            }
        }
}

Play and Pause

The play and pause actions fired by clicking the playButton element are bound to the BottomController functions of the same name. See the BottomController file.

Source Code
public var playButton = PlayButton {
        ...
        playAction: bind playAction
        pauseAction: bind pauseAction

These functions invoke correspondingly the play() and pause() functions of the myPlayer object.

Source Code

var bottomController:BottomController = BottomController {
   playAction: function() {
            if(myPlayer.media != null) {
              ...
              myPlayer.play();
              ...
            }
   }

   pauseAction: function() {
              ...
              myPlayer.pause();
   }
}

Press the Launch Application button at the top of the page to run the sample by using Java Web Start software. Download the NetBeans project and explore other capabilities of the JavaFX media functionality provided in this application.

In order to play the media content used in this application, ensure that the required codecs are installed in your system.

 

English
日本語
한국어
简体中文
русский
Português do Brasil