2

I try to open a MOV video file with JavaFX:

Media media = new Media ("file:/tmp/file.MOV");

But it throws a MediaException: MEDIA_UNSUPPORTED.

However, if I simply change the file extension from .MOV to .MP4, it works perfectly, and the video is played with no error.

How can I force JavaFX to play my file without having to rename it?

Patrick
  • 3,578
  • 5
  • 31
  • 53
  • Can you post more of your stack-trace? Edit: NVM found the cause. `Media` calls this method of `Locator` which throws that exception you're getting http://pastebin.com/NkTFT2Tk – Display Name Oct 26 '16 at 00:47

1 Answers1

4

Well that was a fun two hours. Here you go! Just make sure you have a zero byte mp4 file like in the example. It's pretty key in how it works.

public class TestFrame extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        StackPane root = new StackPane();
        File actualFile = new File("shelter.mov");
        File emptyfile = new File("blank.mp4");
        Media media = new Media(emptyfile.toURI().toString());
        copyData(media, actualFile);
        MediaPlayer mediaPlayer = null;
        try {
            mediaPlayer = new MediaPlayer(media);
        } catch (Exception e) {
            e.printStackTrace();
        }
        mediaPlayer.setAutoPlay(true);
        MediaView mediaView = new MediaView(mediaPlayer);
        root.getChildren().add(mediaView);
        Scene scene = new Scene(root, 720, 480);
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void copyData(Media media, File f) {
        try {
            Field locatorField = media.getClass().getDeclaredField("jfxLocator");
            // Inside block credits:
            // http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection
            {
                Field modifiersField = Field.class.getDeclaredField("modifiers");
                modifiersField.setAccessible(true);
                modifiersField.setInt(locatorField, locatorField.getModifiers() & ~Modifier.FINAL);
                locatorField.setAccessible(true);
            }
            CustomLocator customLocator = new CustomLocator(f.toURI());
            customLocator.init();
            customLocator.hack("video/mp4", 100000, f.toURI());
            locatorField.set(media, customLocator);
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    static class CustomLocator extends Locator {
        public CustomLocator(URI uri) throws URISyntaxException {
            super(uri);
        }

        @Override
        public void init() {
            try {
                super.init();
            } catch (Exception e) {
            }
        }

        public void hack(String type, long length, URI uri){
            this.contentType = type;
            this.contentLength = length;
            this.uri = uri;
            this.cacheMedia();
        }
    }
}

How it works: Normally the default Locator throws an exception about the content type and it all ends there. Replacing the Media's locator with a custom one then manually setting the contentType, length, and uri gets it to play without a peep.

Display Name
  • 942
  • 1
  • 10
  • 20
  • This is a hack, but a clever hack! – Patrick Oct 26 '16 at 08:57
  • 1
    As always, private fields and methods are subject to change from one version of Java to the next, so this may not work with future Java releases. – VGR Oct 26 '16 at 16:00
  • @VGR Yes, it would be better to fix the Java source code. I should maybe add a bug report. – Patrick Oct 26 '16 at 16:54
  • @VGR It was the simplest solution I could find, but in reality MOV is mostly supported. It should just be added to the allowed MIME types. That or the classes should be extensible rather than being final. – Display Name Oct 26 '16 at 18:41
  • Unfortunately I have an OutOfMemoryError on Windows VirtualBox: java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Unknown Source) at java.nio.DirectByteBuffer.(Unknown Source) at TestFrame$CustomLocator.hack at java.nio.ByteBuffer.allocateDirect(Unknown Source) at com.sun.media.jfxmedia.locator.Locator.cacheMedia(Locator.java:285) at TestFrame$CustomLocator.hack – Patrick Oct 27 '16 at 13:36
  • What kind of video are you playing? Also in this I used an arbitrary length that you may want to change, it may be the issue. I can test this after my exams today and edit the example accordingly. – Display Name Oct 27 '16 at 14:23
  • The size of the video is 500Mb. I tried different length values but it didn't help. – Patrick Oct 27 '16 at 21:52
  • Is it crashing in the middle of playback? Is it making it through a video or two before the crash occurs? I don't have any videos on-hand that aren't < 50MB to test with. You may just need to allocate more ram with JVM tags. – Display Name Oct 28 '16 at 01:40