Multithreading example with video play by JavaFx

java http

Dec 31 2023 14:51

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 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<byte[]> {

    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;
    }
}

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()

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

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();
});

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 reach the end of downloaded data (network error or download point not optimized)
  • 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

me

Pham Duc Minh

Da Nang, Vietnam