sclass JFFMPEGVideoPlayer extends JSelfSwingable { //SurfaceShowingAutoZoomedImage imageSurface; ImageSurface imageSurface; JPanel buttons = rightAlignedLine(); JButton btnPlayPause = jDisabledButton("Pause", r playPause); ImageStreamFromVideoFile stream; long playStartTime; new BoolVar paused; void init { //imageSurface = swing(-> new SurfaceShowingAutoZoomedImage); imageSurface = imageSurface().setAutoZoomToDisplay(true); buttons.add(btnPlayPause); buttons.add(jbutton("Open video...", rThread openDialog)); bindToComponent(imageSurface, null, rThread closeStream); setComponent( centerAndSouth( imageSurface, withMargin(buttons) )); } *() { init(); } *(File videoFile) { init(); if (videoFile != null) onFirstShow(imageSurface, rThread { play(videoFile) }); } void openDialog swing { new JFileChooser fc; fc.setDialogTitle("Open video"); play(execFileChooser(fc)); } void play(File videoFile) { if (videoFile == null) ret; pcall-infobox { closeStream(); stream = new ImageStreamFromVideoFile; stream.open(videoFile); stream.onHaveImage((image, pos) -> { waitWhileTrue(paused); if (playStartTime == 0 || playStartTime+pos.toMillis() < sysNow()) playStartTime = sysNow()-pos.toMillis(); else sleepUntilSysTime(playStartTime+pos.toMillis()); imageSurface.setImage(image); }); paused.set(false); enableButton(btnPlayPause); stream.startDecodeThread(); } } void closeStream { disableButton(btnPlayPause); imageSurface.noImage(); dispose stream; playStartTime = 0; } void playPause() swing { paused.set(!paused!); setText(btnPlayPause, paused! ? "Play" : "Pause"); } }