© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
K. Sharan, P. SpäthLearn JavaFX 17https://doi.org/10.1007/978-1-4842-7848-2_25

25. Playing Audios and Videos

Kishori Sharan1   and Peter Späth2
(1)
Montgomery, AL, USA
(2)
Leipzig, Sachsen, Germany
 
In this chapter, you will learn:
  • What the Media API is

  • How to play short audio clips

  • How to play back media (audios and videos) and how to track different aspects of the playback such as playback rate, volume, playback time, repeating the playback, and media errors

The examples of this chapter lie in the com.jdojo.media package. In order for them to work, you must add a corresponding line to the module-info.java file:
...
opens com.jdojo.media to javafx.graphics, javafx.base;
...

Understanding the Media API

JavaFX supports playing audio and video through the JavaFX Media API. HTTP live streaming of static media files and live feeds are also supported. A number of media formats are supported, including AAC, AIFF, WAV, and MP3. FLV containing VP6 video and MP3 audio and MPEG-4 multimedia container with H.264/AVC video formats are also supported. The support for a specific media format is platform dependent. Some media playback features and formats do not require any additional installations; some require third-party software to be installed. Please refer to the web page at https://openjfx.io/javadoc/17/javafx.media/javafx/scene/media/package-summary.html#SupportedMediaTypes for details on the system requirements and supported media formats in JavaFX.

The Media API consists of several classes. Figure 25-1 shows a class diagram that includes only the core classes in the Media API. All classes in the API are included in the javafx.scene.media package.
Figure 25-1

A class diagram for core classes in the Media API

AudioClip is used to play a short audio clip with minimal latency. Typically, this is useful for sound effects, which are usually short audio clips. Use the Media, MediaPlayer, and MediaView classes for playing audios and videos of longer length.

The Media and MediaPlayer classes are used to play audios as well as videos. An instance of the Media class represents a media resource, which could be an audio or a video. It provides the information about the media, for example, the duration of the media. An instance of the MediaPlayer class provides controls for playing a media.

An instance of the MediaView class provides the view of a media being played by a MediaPlayer. A MediaView is used for viewing a video.

Several things can go wrong when you attempt to play a media, for example, the media format may not be supported or the media content may be corrupt. An instance of the MediaException class represents a specific type of media error that may occur during media playback. When a media-related error occurs, a MediaErrorEvent is generated. You can handle the error by adding an appropriate event handler to the media objects.

I will cover the details of using these classes and other supporting classes in the Media API in this chapter.

Playing Short Audio Clips

An instance of the AudioClip class is used to play a short audio clip with minimal latency. Typically, this is useful for playing short audio clips, for example, a beep sound when the user makes an error or producing short sound effects in gaming applications.

The AudioClip class provides only one constructor that takes a URL in string form, which is the URL of the audio source. The audio clip is immediately loaded into memory in raw, uncompressed form. This is the reason why you should not use this class for long-playing audio clips. The source URL could use the HTTP, file, and JAR protocols. This means that you can play an audio clip from the Internet, the local file system, and a JAR file.

The following snippet of code creates an AudioClip using the HTTP protocol:
String clipUrl = "http://www.jdojo.com/myaudio.wav";
AudioClip audioClip = new AudioClip(clipUrl);
When an AudioClip object is created, the audio data are loaded into the memory, and they are ready to be played immediately. Use the play() method to play the audio and the stop() method to stop the playback:
// Play the audio
audioClip.play();
...
// Stop the playback
audioClip.stop();
The program in Listing 25-1 shows how to play an audio clip using the AudioClip class. It declares an instance variable to store the AudioClip reference. The AudioClip is created in the init() method to make sure the clip is ready to be played when the window is shown in the start() method. You could have also created the AudioClip in the constructor. The start() method adds Start and Stop buttons. Their action event handlers start and stop the playback, respectively.
// AudioClipPlayer.java
package com.jdojo.media;
import com.jdojo.util.ResourceUtil;
import java.net.URL;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.media.AudioClip;
import javafx.stage.Stage;
public class AudioClipPlayer extends Application {
        private AudioClip audioClip;
        public static void main(String[] args) {
            Application.launch(args);
        }
        @Override
        public void init() {
                URL mediaUrl =
                    ResourceUtil.getResourceURL("media/chimes.wav");
            // Create an AudioClip, which loads the audio data
                // synchronously
            audioClip = new AudioClip(mediaUrl.toExternalForm());
        }
        @Override
        public void start(Stage stage) {
            Button playBtn = new Button("Play");
            Button stopBtn = new Button("Stop");
            // Set event handlers for buttons
            playBtn.setOnAction(e -> audioClip.play());
            stopBtn.setOnAction(e -> audioClip.stop());
            HBox root = new HBox(5, playBtn, stopBtn);
            root.setStyle("-fx-padding: 10;");
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.setTitle("Playing Short Audio Clips");
            stage.show();
        }
}
Listing 25-1

Playing Back an Audio Clip Using an AudioClip Instance

The AudioClip class supports setting some audio properties when the clip is played:
  • cycleCount

  • volume

  • rate

  • balance

  • pan

  • priority

All of the preceding properties, except the cycleCount, can be set on the AudioClip class. Subsequent calls to the play() method will use them as defaults. The play() method may also override the defaults for a specific playback. The cycleCount property must be specified on the AudioClip, and all subsequent playbacks will use the same value.

The cycleCount specifies the number of times the clip is played when the play() method is called. It defaults to one, which plays the clip only once. You can use one of the following three INDEFINITE constants as the cycleCount to play the AudioClip loop until stopped:
  • AudioClip.INDEFINITE

  • MediaPlayer.INDEFINITE

  • Animation.INDEFINITE

The following snippet of code shows how to play an audio clip five times and indefinitely:
// Play five times
audioClip.setCycleCount(5);
...
// Loop forever
audioClip.setCycleCount(AudioClip.INDEFINITE);

The volume specifies the relative volume of the playback. The valid range is 0.0 to 1.0. A value of 0.0 represents muted, whereas 1.0 represents full volume.

The rate specifies the relative speed at which the audio is played. The valid range is 0.125 to 8.0. A value of 0.125 means the clip is played eight times slower, and the value of 8.0 means the clip will play eight times faster. The rate affects the playtime and the pitch. The default rate is 1.0, which plays the clip at the normal rate.

The balance specifies the relative volume for the left and right channels. The valid range is –1.0 to 1.0. A value of –1.0 sets the playback in the left channel at normal volume and mutes the right channel. A value of 1.0 sets the playback in the right channel at normal volume and mutes the left channel. The default value is 0.0, which sets the playback in both channels at normal volume.

The pan specifies the distribution of the clip between the left and right channels. The valid range is –1.0 to 1.0. A value of –1.0 shifts the clip entirely to the left channel. A value of 1.0 shifts the clip entirely to the right channel. The default value is 0.0, which plays the clip normally. Setting the value for pan for a mono clip has the same effect of setting the balance. You should change the default for this property only for audio clips using stereo sound.

The priority specifies the priority of the clip relative to other clips. It is used only when the number of playing clips exceeds the system limits. The playing clips with the lower priority will be stopped. It can be set to any integer. The default priority is set to zero.

The play() method is overloaded. It has three versions:
  • Void play()

  • void play(double volume)

  • void play(double volume, double balance, double rate, double pan, int priority)

The no-args version of the method uses all of the properties set on the AudioClip. The other two versions can override the specified properties for a specific playback. Suppose the volume for the AudioClip is set to 1.0. Calling play() will play the clip at volume 1.0, and calling play(0.20) will play the clip at volume 0.20, leaving the volume property for the AudioClip unchanged at 1.0. That is, the play() method with parameters allows you to override the AudioClip properties on a per-playback basis.

The AudioClip class contains an isPlaying() method to check if the clip is still playing. It returns true if the clip is playing. Otherwise, it returns false.

Playing Media

JavaFX provides a unified API to work with audio and videos. You use the same classes to work with both. The Media API internally treats them as two different types of media that is transparent to the API users. From here onward, I will use the term media to mean both audio and video, unless specified otherwise.

The Media API contains three core classes to play back media:
  • Media

  • MediaPlayer

  • MediaView

Creating a Media Object

An instance of the Media class represents a media resource, which could be an audio or a video. It provides the information related to the media, for example, the duration, metadata, data, and so forth. If the media is a video, it provides the width and height of the video. A Media object is immutable. It is created by supplying a string URL of the media resource, as in the following code:
// Create a Media
String mediaUrl = "http://www.jdojo.com/mymusic.wav";
Media media = new Media(mediaUrl);
The Media class contains the following properties, all (except onError) of which are read-only:
  • duration

  • width

  • height

  • error

  • onError

The duration specifies the duration of the media in seconds. It is a Duration object. If the duration is unknown, it is Duration.UNKNOWN.

The width and height give the width and height of the source media in pixels, respectively. If the media does not have width and height, they are set as zero.

The error and onError properties are related. The error property represents the MediaException that occurs during the loading of the media. The onError is a Runnable object that you can set to get notified when an error occurs. The run() method of the Runnable is called when an error occurs:
// When an error occurs in loading the media, print it on the console
media.setOnError(() -> System.out.println(player.getError().getMessage()));

Creating a MediaPlayer Object

A MediaPlayer provides the controls, for example, play, pause, stop, seek, play speed, volume adjustment, for playing the media. The MediaPlayer provides only one constructor that takes a Media object as an argument:
// Create a MediaPlayer
MediaPlayer player = new MediaPlayer(media);

You can get the reference of the media from the MediaPlayer using the getMedia() method of the MediaPlayer class.

Like the Media class, the MediaPlayer class also contains error and onError properties to report errors. When an error occurs on the MediaPlayer, the same error is also reported on the Media object.

The MediaPlayer class contains many properties and methods. I will discuss them in subsequent sections.

Creating a MediaView Node

A MediaView is a node. It provides the view of a media being played by a MediaPlayer. Note that an audio clip does not have visuals. If you try creating a MediaView for an audio content, it would be empty. To watch a video, you create a MediaView and add it to a scene graph.

The MediaView class provides two constructors, one no-args constructor and one that takes a MediaPlayer as an argument:
  • public MediaView()

  • public MediaView(MediaPlayer mediaPlayer)

The no-args constructor creates a MediaView that is attached to any MediaPlayer. You will need to set a MediaPlayer using the setter for the mediaPlayer property:
// Create a MediaView with no MediaPlayer
MediaView mediaView = new MediaView();
mediaView.setMediaPlayer(player);
The other constructor lets you specify a MediaPlayer for the MediaView:
// Create a MediaView
MediaView mediaView = new MediaView(player);

Combining Media, MediaPlayer, and MediaView

The content of a media can be used simultaneously by multiple Media objects . However, one Media object can be associated with only one media content in its lifetime.

A Media object can be associated with multiple MediaPlayer objects. However, a MediaPlayer is associated with only one Media in its lifetime.

A MediaView may optionally be associated with a MediaPlayer. Of course, a MediaView that is not associated with a MediaPlayer does not have any visuals. The MediaPlayer for a MediaView can be changed. Changing the MediaPlayer for a MediaView is similar to changing the channel on a television. The view for the MediaView is provided by its current MediaPlayer . You can associate the same MediaPlayer with multiple MediaViews . Different MediaViews may display different parts of the same media during the playback. This relationship between the three types of objects involved in a media playback is shown in Figure 25-2.
Figure 25-2

Roles of different media-related objects in a media playback and the relation among them

A Media Player Example

You now have enough background to understand the mechanism used to play an audio and a video. The program in Listing 25-2 plays a video clip using the ResourceUtil to find the file location. The program uses a video file resources/media/gopro.mp4. This file maybe is not included in the source code because it is approximately 50MB. You can substitute your own media file in this program if it is in a format supported by JavaFX.
// QuickMediaPlayer.java
package com.jdojo.media;
import com.jdojo.util.ResourceUtil;
import java.net.URL;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;
import static javafx.scene.media.MediaPlayer.Status.PLAYING;
public class QuickMediaPlayer extends Application {
        public static void main(String[] args) {
            Application.launch(args);
        }
        @Override
        public void start(Stage stage) {
            // Locate the media content
                URL mediaUrl = ResourceUtil.getResourceURL("media/gopro.mp4");
            String mediaStringUrl = mediaUrl.toExternalForm();
            // Create a Media
            Media media = new Media(mediaStringUrl);
            // Create a Media Player
            MediaPlayer player = new MediaPlayer(media);
            // Automatically begin the playback
            player.setAutoPlay(true);
            // Create a 400X300 MediaView
            MediaView mediaView = new MediaView(player);
            mediaView.setFitWidth(400);
            mediaView.setFitHeight(300);
            // Create Play and Stop player control buttons and add action
            // event handlers to them
            Button playBtn = new Button("Play");
            playBtn.setOnAction(e -> {
                    if (player.getStatus() == PLAYING) {
                            player.stop();
                            player.play();
                    } else {
                            player.play();
                    }
            });
            Button stopBtn = new Button("Stop");
            stopBtn.setOnAction(e -> player.stop());
            // Add an error handler
            player.setOnError(() ->
                    System.out.println(player.getError().getMessage()));
            HBox controlBox = new HBox(5, playBtn, stopBtn);
            BorderPane root = new BorderPane();
            // Add the MediaView and player controls to the scene graph
            root.setCenter(mediaView);
            root.setBottom(controlBox);
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.setTitle("Playing Media");
            stage.show();
        }
}
Listing 25-2

Using the Media, MediaPlayer, and MediaView Classes to Play a Media

The first two statements in the start() method prepare a string URL for the media file:
// Locate the media content
URL mediaUrl = ResourceUtil.getResourceURL("media/gopro.mp4");
String mediaStringUrl = mediaUrl.toExternalForm();
If you want to play a media from the Internet, you can replace the three statements with a statement similar to the following:
String mediaStringUrl = "http://www.jdojo.com/video.flv";
The program creates a Media, a MediaPlayer, and a MediaView. It sets the autoPlay property for the MediaPlayer to true, which will start playing the media as soon as possible:
// Automatically begin the playback
player.setAutoPlay(true);

The size of the MediaView is set 400px wide by 300px tall. If the media is a video, the video will be scaled to fit in this size. You will see an empty area for the audios. You can enhance the MediaView later, so it will take as much space as the media needs.

The Play and Stop buttons are created. Event handlers are added to them. They can be used to begin and stop the playback, respectively. When the media is already playing, clicking the Play button stops the playback and plays the media again.

A number of things can go wrong when playing a media. The program sets the onError property for the MediaPlayer, which is a Runnable. Its run() method is called when an error occurs. The run() method prints the error message on the console:
// Add an error handler
player.setOnError(() -> System.out.println(player.getError().getMessage()));

When you run the program, the video should play automatically. You can stop and replay it using the buttons at the bottom of the screen. If there is an error, you will see an error message on the console.

Tip

The QuickMediaPlayer class can play audios as well as videos. All you need to do is change the URL of the source to point to the media you want to play.

Handling Playback Errors

An instance of the MediaException class , which inherits from the RuntimeException class, represents a media error that may occur in a Media, MediaPlayer, and MediaView. Media playback may fail for a number of reasons. The API users should be able to identify specific errors. The MediaException class defines a static enum MediaException.Type whose constants identify the type of error. The MediaException class contains a getType() method that returns one of the constants of the MediaException.Type enum.
  • MEDIA_CORRUPTED

  • MEDIA_INACCESSIBLE

  • MEDIA_UNAVAILABLE

  • MEDIA_UNSPECIFIED

  • MEDIA_UNSUPPORTED

  • OPERATION_UNSUPPORTED

  • PLAYBACK_HALTED

  • PLAYBACK_ERROR

  • UNKNOWN

The MEDIA_CORRUPTED error type indicates that the media is corrupted or invalid. The MEDIA_INACCESSIBLE error type indicates that the media is inaccessible. However, the media may exist. The MEDIA_UNAVAILABLE error type indicates that the media does not exist or it is unavailable. The MEDIA_UNSPECIFIED error type indicates that the media has not been specified. The MEDIA_UNSUPPORTED error type indicates that the media is not supported by the platform. The OPERATION_UNSUPPORTED error type indicates that the operation performed on the media is not supported by the platform. The PLAYBACK_HALTED error type indicates an unrecoverable error that has halted the playback. The PLAYBACK_ERROR error type indicates a playback error that does not fall into any other described categories. The UNKNOWN error type indicates that an unknown error has occurred.

The Media and MediaPlayer classes contain an error property that is a MediaException. All three classes—Media, MediaPlayer, and MediaView—contain an onError property, which is an event handler that is invoked when an error occurs. The types of the onError properties in these classes are not consistent. It is a Runnable for the Media and MediaPlayer classes and the MediaErrorEvent for the MediaView class. The following snippet of code shows how to handle errors on a Media, MediaPlayer, and MediaView. They print the error details on the console:
player.setOnError(() -> {
        System.out.println(player.getError().getMessage());
});
media.setOnError(() -> {
        System.out.println(player.getError().getMessage());
});
mediaView.setOnError((MediaErrorEvent e) ->  {
        MediaException error = e.getMediaError();
        MediaException.Type errorType = error.getType();
        String errorMsg = error.getMessage();
        System.out.println("Error Type:" + errorType +
              ", error mesage:" + errorMsg);
});

Media error handlers are invoked on the JavaFX Application Thread. Therefore, it is safe to update the scene graph from the handlers.

It is recommended that you enclose the creation of the Media, MediaPlayer, and MediaView objects in a try-catch block and handle the exception appropriately. The onError handlers for these objects are involved after the objects are created. If an error occurs during the creation of these objects, those handlers will not be available. For example, if the media type you are trying to use is not supported, creating the Media object results in an error:
try {
        Media media = new Media(mediaStringUrl);
        ...
}
catch (MediaException e) {
        // Handle errors here
}

State Transitions of the MediaPlayer

A MediaPlayer always has a status. The current status of a MediaPlayer is indicated by the read-only status property. The status changes when an action is performed on the MediaPlayer. It cannot be set directly. The status of a MediaPlayer is defined by one of the eight constants in the MediaPlayer.Status enum:
  • UNKNOWN

  • READY

  • PLAYING

  • PAUSED

  • STALLED

  • STOPPED

  • HALTED

  • DISPOSED

The MediaPlayer transitions from one status to another when one of the following methods is called:
  • play()

  • pause()

  • stop()

  • dispose()

Figure 25-3 shows the status transition for a MediaPlayer. Figure 25-3 excludes the HALTED and DISPOSED statuses as these two statuses are terminal statuses.
Figure 25-3

Statuses of a MediaPlayer and the transition among them

When a MediaPlayer is created, its status is UNKNOWN. Once the media is prerolled and it is ready to be played, the MediaPlayer transitions from UNKNOWN to READY. Once the MediaPlayer exits the UNKNOWN status, it cannot reenter it in its lifetime.

The MediaPlayer transitions to the PLAYING status when the play() method is called. This status indicates that the media is playing. Note if the autoPlay property is set to true, the MediaPlayer may enter the PLAYING status without calling the play() method explicitly after it is created.

When the MediaPlayer is playing, it may enter the STALLED status if it does not have enough data in its buffer to play. This status indicates that the MediaPlayer is buffering data. When enough data are buffered, it goes back to the PLAYING status. When a MediaPlayer is stalled, calling the pause() and stop() methods, it transitions to the PAUSED and STOPPED status, respectively. In that case, the buffering continues; however, the MediaPlayer does not transition to the PLAYING status once enough data are buffered. Rather, it stays in the PAUSED or STOPPED status.

Calling the pause() method transitions the MediaPlayer to the PAUSED status. Calling the stop() method transitions the MediaPlayer to the STOPPED status.

In cases of an unrecoverable error, the MediaPlayer transitions to the HALTED terminal status. This status indicates that the MediaPlayer cannot be used again. You must create a new MediaPlayer if you want to play the media again.

The dispose() method frees all of the resources associated with the MediaPlayer. However, the Media object used by the MediaPlayer can still be used. Calling the dispose() method transitions the MediaPlayer to the terminal status DISPOSED.

It is common to display the status of the MediaPlayer in an application. Add a ChangeListener to the status property to listen for any status changes.

Typically, you will be interested in receiving a notification when the status of the MediaPlayer changes. There are two ways to get the notifications:
  • By adding a ChangeListener to the status property

  • By setting status change handlers

The first method is suitable if you are interested in listening for any type of status change. The following snippet of code shows this method:
MediaPlayer player = new MediaPlayer(media);
// Add a ChangeListener to the player
player.statusProperty().addListener((prop, oldStatus, newStatus) -> {
        System.out.println("Status changed from " + oldStatus +
               " to " + newStatus);
});
The second method is suitable if you are interested in handling a specific type of status change. The MediaPlayer class contains the following properties that can be set to Runnable objects:
  • onReady

  • onPlaying

  • onRepeat

  • onStalled

  • onPaused

  • onStopped

  • onHalted

The run() method of the Runnable object is called when the MediaPlayer enters into the specific status. For example, the run() method of the onPlaying handler is called when the player enters the PLAYING status. The following snippet of code shows how to set handlers for a specific type of status change:
// Add a handler for PLAYING status
player.setOnPlaying(() -> {
        System.out.println("Playing...");
});
// Add a handler for STOPPED status
player.setOnStopped(() -> {
        System.out.println("Stopped...");
});

Repeating Media Playback

A media can be played repeatedly for a specified number of times or even indefinitely. The cycleCount property specifies the number of times a playback will be repeated. By default, it is set to one. Set it to MediaPlayer.INDEFINITE to repeat the playback indefinitely until the player is paused or stopped. The read-only currentCount property is set to the number of completed playback cycles. It is set to zero when the media is playing the first cycle. At the end of the first cycle, it is set to one; it is incremented to two at the end of the second cycle; and so on. The following code would set a playback cycle of four times:
// The playback should repeat 4 times
player.setCycleCount(4);
You can receive a notification when the end of media for a cycle in playback is reached. Set a Runnable for the onEndOfMedia property of the MediaPlayer class to get the notification. Note that if a playback continues for four cycles, the end of the media notification will be sent four times:
player.setOnEndOfMedia(() -> {
        System.out.println("End of media...");
});
You can add an onRepeat event handler that is called when the end of media for a playback cycle is reached and the playback is going to repeat. It is called after the onEndOfMedia event handler:
player.setOnRepeat(() -> {
        System.out.println("Repeating...");
});

Tracking Media Time

Displaying the media duration and the elapsed time for a playback is an important feedback for the audience. A good understanding of these duration types is important in developing a good media playback dashboard. Different types of duration can be associated with a media:
  • The current duration of a media playing media

  • The duration of the media playback

  • The duration of the media play for one cycle

  • The start offset time

  • The end offset time

By default, a media plays for its original duration. For example, if the duration of the media is 30 minutes, the media will play for 30 minutes in one cycle. The MediaPlayer lets you specify the length of the playback, which can be anywhere in the duration of the media. For example, for each playback cycle, you can specify that only the middle 10 minutes (11th to 12th) of the media should be played. The length of the media playback is specified by the following two properties of the MediaPlayer class:
  • startTime

  • stopTime

Both properties are of the Duration type. The startTime and stopTime are the time offsets where the media should start and stop playing for each cycle, respectively. By default, the startTime is set to Duration.ZERO, and the stopTime is set to the duration of the media. The following snippet of code sets these properties, so the media will be played from the 10th minute to the 21st minute:
player.setStartTime(Duration.minutes(10));
player.setStartTime(Duration.minutes(21));
The following constraints are applicable to the startTime and stopTime values :
0 ≤ startTime < stopTime
startTime < stopTime ≤ Media.duration

The read-only currentTime property is the current time offset in the media playback. The read-only cycleDuration property is the difference between the stopTime and startTime. It is the length of playback for each cycle. The read-only totalDuration property specifies the total duration of the playback if the playback is allowed to continue until finished. Its value is the cycleDuration multiplied by the cycleCount. If the cycleCount is INDEFINITE, the totalDuration will be INDEFINITE. If the media duration is UNKNOWN, the totalDuration will be UNKNOWN.

When you play a media from the network, the MediaPlayer may get stalled because it does not have enough data to continue the playback. The read-only bufferProgressTime property gives you the duration for which the media can be played without stalling.

Controlling the Playback Rate

The rate property of the MediaPlayer specifies the rate of the playback. The valid range is 0.0 to 8.0. For example, a rate of 2.0 plays the media two times faster than the normal rate. The default value is 1.0, which plays the media at the normal rate. The read-only currentRate property is the current rate of the playback. The following code would set the rate at three times the normal rate:
// Play the media at 3x
player.setRate(3.0);

Controlling the Playback Volume

Three properties in the MediaPlayer class control the volume of the audio in the media:
  • volume

  • mute

  • balance

The volume specifies the volume of the audio. The range is 0.0 to 1.0. A value of 0.0 makes the audio inaudible, whereas a value of 1.0 plays it at full volume. The default value is 1.0.

The mute specifies whether the audio is produced by the MediaPlayer. By default, its value is false and the audio is produced. Setting it to true does not produce audio. Note that setting the mute property does not affect the volume property. Suppose the volume is set to 1.0, and the muted is set to true. There is no audio being produced. When the mute is set to false, the audio will use the volume property that is 1.0, and it will play at full volume. The following code would set the volume at half:
// Play the audio at half the full volumne
player.setVolumne(0.5);
...
// Mute the audio
player.setMute(true)

The balance specifies the relative volume for the left and right channels. The valid range is –1.0 to 1.0. A value of –1.0 sets the playback in the left channel at normal volume and mutes the right channel. A value of 1.0 sets the playback in the right channel at normal volume and mutes the left channel. The default value is 0.0, which sets the playback in both channels at normal volume.

Positioning the MediaPlayer

You can position a MediaPlayer at a specific playback time using the seek(Duration position) method :
// Position the media at the fifth minutes play time
player.seek(Duration.minutes(5.0));
Calling the seek() method has no effect if
  • The MediaPlayer is in the STOPPED status.

  • The media duration is Duration.INDEFINITE.

  • You pass null or Duration.UNKNOWN to the seek() method.

  • In all other cases, the position is clamped between the startTime and stopTime of the MediaPlayer.

Marking Positions in the Media

You can associate markers with specific points on the media timeline. Markers are simply text that are useful in a number of ways. You can use them to insert advertisements. For example, you can insert a URL as the marker text. When the marker is reached, you can pause playing the media and play another media. Note that playing another media involves creating new Media and MediaPlayer objects. You can reuse a MediaView. When you are playing the advertisement video, associate the MediaView with the new MediaPlayer. When the advertisement playback is finished, associate the MediaView back to the main MediaPlayer.

The Media class contains a getMarkers() method that returns an ObservableMap<String, Duration>. You need to add the (key, value) pairs in the map to add markers. The following snippet of code adds three markers to a media:
Media media = ...
ObservableMap<String, Duration> markers = media.getMarkers();
markers.put("START", Duration.ZERO);
markers.put("INTERVAL", media.getDuration().divide(2.0));
markers.put("END", media.getDuration());
The MediaPlayer fires a MediaMarkerEvent when a marker is reached. You can register a handler for this event in the onMarker property of the MediaPlayer. The following snippet of code shows how to handle the MediaMarkerEvent. The getMarker() method of the event returns a Pair<String, Duration> whose key and value are the marker text and marker duration, respectively:
// Add a marker event handler
player.setOnMarker((MediaMarkerEvent e) -> {
        Pair<String, Duration> marker = e.getMarker();
        String markerText = marker.getKey();
        Duration markerTime = marker.getValue();
        System.out.println("Reached the marker " + markerText +
               " at " + markerTime);
});

Showing Media Metadata

Some metadata may be embedded into a media that describe the media. Typically, the metadata contains the title, artist name, album name, genre, year, and so forth. The following snippet of code displays the metadata for the media when the MediaPlayer enters the READY status. Do not try reading the metadata just after creating the Media object, as the metadata may not be available:
Media media = ...
MediaPlayer player = new MediaPlayer(media);
// Display the metadata data on the console
player.setOnReady(() -> {
        ObservableMap<String, Object> metadata = media.getMetadata();
        for(String key : metadata.keySet()) {
            System.out.println(key + " = " + metadata.get(key));
        }
});

You cannot be sure whether there are metadata in a media or the type of metadata a media may contain. In your application, you can just look for the title, artist, album, and year. Alternatively, you could read all of the metadata and display them in a two-column table. Sometimes, the metadata may contain an embedded image of the artist. You would need to check the class name of the value in the map to use the image.

Customizing the MediaView

If the media has a view (e.g., a video), you can customize the size, area, and quality of the video using the following properties:
  • fitHeight

  • fitWidth

  • preserveRatio

  • smooth

  • viewport

  • x

  • y

The fitWidth and fitHeight properties specify the resized width and height of the video, respectively. By default, they are zero, which means that the original width and height of the media will be used.

The preserveRatio property specifies whether to preserve the aspect ratio of the media while resizing. By default, it is false.

The smooth property specifies the quality of the filtering algorithm to be used in resizing the video. The default value is platform dependent. If it is set to true, a better-quality filtering algorithm is used. Note that a better-quality filtering takes more processing time. For smaller-sized videos, you may set it to false. For bigger-sized videos, it is recommended to set the property to true.

A viewport is a rectangular region to view part of a graphic. The viewport, x, and y properties together let you specify the rectangular area in the video that will be shown in the MediaView. The viewport is a Rectangle2D that is specified in the coordinate system of the original media frame. The x and y properties are the coordinates of the upper-left corner of the viewport. Recall that you can have multiple MediaViews associated with a MediaPlayer. Using multiple MediaViews with viewports, you can give the audience the impression of splitting the video. Using one MediaView with a viewport, you can let the audience view only part of the viewable area of the video.

A MediaView is a node. Therefore, to give a better visual experience to the audience, you can also apply effects and transformations to the MediaView.

Developing a Media Player Application

It requires a careful design to develop a good-looking, customizable media player application. I have covered most of the features offered by the Media API in JavaFX. Combining your knowledge of developing a user interface and the Media API, you can design and develop your own media player application. Keep the following points in mind while developing the application:
  • The application should have the ability to specify a media source.

  • The application should provide a UI to control the media playback.

  • When the media source changes, you will need to create a new Media object and a MediaPlayer. You can reuse the MediaView by setting the new MediaPlayer using its setMediaPlayer() method.

Summary

JavaFX supports playing audio and video through the JavaFX Media API. HTTP live streaming of static media files and live feeds are also supported. A number of media formats are supported, such as AAC, AIFF, WAV, and MP3. FLV containing VP6 video and MP3 audio and MPEG-4 multimedia container with H.264/AVC video formats are supported. The support for a specific media format is platform dependent. Some media playback features and formats do not require any additional installations; but some require third-party software to be installed. The Media API consists of several classes. All classes in the API are included in the javafx.scene.media package.

An AudioClip is used to play a short audio clip with minimal latency. Typically, this is useful for sound effects, which are usually short audio clips. Use the Media, MediaPlayer, and MediaView classes for playing audios and videos of longer length.

The Media and MediaPlayer classes are used to play audios as well as videos. An instance of the Media class represents a media resource, which could be an audio or a video. It provides the information about the media, for example, the duration of the media. An instance of the MediaPlayer class provides controls for playing a media. A MediaPlayer always indicates the status of the playback. The current status of a MediaPlayer is indicated by the read-only status property. The status changes when an action is performed on the MediaPlayer. The status can be unknown, ready, playing, paused, stalled, stopped, halted, or disposed.

An instance of the MediaView class provides the view of a media being played by a MediaPlayer. A MediaView is used for viewing a video.

Several things can go wrong when you attempt to play a media, for example, the media format may not be supported or the media content may be corrupt. An instance of the MediaException class represents a specific type of media error that may occur during media playback. When a media-related error occurs, a MediaErrorEvent is generated. You can handle the error by adding an appropriate event handler to the media objects.

The next chapter will discuss FXML, which is an XML-based language to build user interfaces for a JavaFX application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.222.178