Multithreading example with video play by JavaFx

docvominh

Pham Duc Minh

Posted on May 30, 2022

Multithreading example with video play by JavaFx

When start research about Java multithread, I saw an example how it should use:

when downloading a large file (e.g., an image, an audio clip or a video clip) over the Internet, the user may not want to wait until the entire clip downloads before starting the playback.

It just normal thing nowadays, like what Youtube did

Youtube allow you to downloading and play at the same time<br>

So i start implement an Video Play app to get understand about it

1.Range HTTP request header

You want to download a portion of video file, your server need to support Range HTTP header

In this example, I store media file on AWS S3 server, which one supported Range header request

Sample file: Sting+-+Shape+of+My+Heart+(Leon).mp4

Here is sample Download task that extend javafx.concurrent.Task

Input URL and range define in String, output a byte[]

package com.vominh.example.thread.fx.task;

import javafx.concurrent.Task;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class Download extends Task {

    private String url;
    private String range;

    public Download(String url, String range) {
        this.url = url;
        this.range = range;
    }

    @Override
    public byte[] call() throws Exception {
        URL publicUrl = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) publicUrl.openConnection();
        conn.setRequestProperty("Range", "bytes=" + range);
        conn.connect();
        byte[] data;
        try (InputStream inputStream = conn.getInputStream()) {
            data = inputStream.readAllBytes();
        }

        return data;
    }
}
Enter fullscreen mode Exit fullscreen mode

2.How to download and play at the sametime

2.1. Get content-length and calculate chunk size in byte

This simple snippet code use java.net.HttpURLConnection allow me to get file information without download it

URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.getContentType();
conn.getContentLengthLong()
Enter fullscreen mode Exit fullscreen mode

Depends on content-length, I calculate chunk size = contentLength/10. First download byte[] has range: 0-{chunk size}

Ex: a mp4 file with size = 20MB (20.000.000 Byte)
Chunk size = 20.000.000/10 = 2.000.000 Byte
First Range header: "bytes=0-2000000"
Next Range header: "bytes=2000001-4000000"
Next Range header: "bytes=4000001-6000000"

2.2. Create file from first download byte[]

When first part of file downloaded, I create a file to temp directory of Operation System

One more luxury feature is try to grab thumbnail from video file
There's a library from org.bytedeco support to do it.
Load button calculate and download first part of file, extract thumbnail and setup javafx.scene.media.MediaPlay ready to play<br>

2.3. When next download happen?

javafx.scene.media.MediaPlay support event listener every second when media file playing

MediaPlayer player = new MediaPlayer(media);
player.currentTimeProperty().addListener(observable -> {
    // Event fire every 1/4 second
    int currentSecond = (int) player.getCurrentTime().toSeconds();
});
Enter fullscreen mode Exit fullscreen mode

Base on this, I submit Download task when played amount = x percent of downloaded
x should < 70% of total downloaded to make video play without pause.
This step is quite complex cause it depends on file size, internet speed... And it hard to optimize for large file

When new part downloaded, I just append byte[] to the temp file created on first step

The next download point

3.Thread information

I put some log in applications to see how many threads were created and what it is, and here is result
Three threads created

Main thread -> created automatically when our program is started, your Java code execute by this thread
JavaFX Application Thread -> Thread to perform GUI tasks
pool-2-thread-1 -> Thread open to download data

4. Remaining issues of the app

  • Can not resume play when progress rearch the end of downloaded data (network error or download point not optimize)
  • Seed not support
  • MediaPlay not fully support media type, for example I could not play .mkv file. See Supported media type here Source is available at my Github repo
💖 💪 🙅 🚩
docvominh
Pham Duc Minh

Posted on May 30, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related