Posted by: thomasbutter | May 27, 2010

Working around JavaFX 1.3 video issues

JavaFX1.3 has a quite nice media API, but still faces some important issues.

Quite visible are these:

  • FXM in MediaPlayer crashes very often on Windows ( http://javafx-jira.kenai.com/browse/RT-6415 )
  • There is no possiblity to seek in large videos without fully downloading them
  • You can only seek backwards or in specially crafted FXM files with KeyFrame positions in the header
  • Video does not restart after a stalled download / during buffering and onStalled/onBuffering does not work reliably (or at all?)
  • autostart in MediaPlayer does not work on Linux
  • some minor different behaviour in other methods and different OS
  • Video is flickering a lot (=not usable anymore) on Mac

Since it is Java you have a lot of possibilities for workarounds. Here is some code – probably useful in your projects – that implements a embedded server which sends the frames fast enough to not trigger the crasher bug and does all the HTTP byte-range stuff for fast seeking. Furthermore it reduces bandwidth usage since it throttles the download to the playback bitrate. It handles all the issues above besides the last one – if you are on a Mac (like me) you are out of luck…

A FLV/FXM file is a header and a series of “tags” each with a audio frame, video keyframe or other video frame. You can display any series of video frames starting with a keyframe. The embedded server only needs to know the positions of the keyframes and is then able to rewrite the individual flash tags to appear as one continuous video, even after seeking. The Player then simply plays one long video without knowing about the seek.

A config file for a video looks like this:

#http://www.butter.eu/filename.fxm

*640 480

0 430

5000 230000

The first line is a hash followed by the URL of the FXM file. It is followed by the dimensions of the video and then contains many lines with the timestamp of a keyframe followed by its byte position. This list can be easily created using this code. Next you will need to seek in a file over http which can be done with a HTTP GET Range request. Therefore we have an easy class implementing an InputStream with seek() here. Whenever you seek it will simply start a new request for the given range.

Furthermore we need the embedded server. It simply starts a pseudo http server. For each request it serves a header and then the current frame with the tag time changed to 0ms. Each following frame is the decreased by the same time. After a seek the incoming stream is repositioned and the frames at that position appended. The output stream is also throttled so that only some small number of seconds of additional frames after the current playing position is transferred. This allows fast seeking since it will play the frames already in the buffer anyway. The code for the embedded server is here and here. From JavaFX you set the Media URL to http://localhost:{fxmserver.getPort()}/dummy.fxm and get the file from there. Using this embedded server it is straightforward to add live streaming support from an rtmp server.

The last part is a quite large FX CustomNode which embeds the MediaView and does all the restarting, translation of real time and fake stream time, starting at a specific time, handling platform differences etc: VideoView.fx

All Files:

KeyFrameList.java

HTTPSeekStream.java

FLVTag.java

FXMServer.java

VideoView.fx

Advertisements

Categories