How to run vlcj in a separate thread in background just to calculate the duration of the media file - javafx

I am making a media player using Javafx and VLCJ.
The app contains a list view in which all the Media files. i.e. audio and video that the user selected are listed. Its like a playlist.
I want to show the total Play time of the entire playlist.
I also want to display the duration of individual media at the side.
e.g.
1> my_visiting_video.mp4 | 01:21:35
2> avengers_end_game.flv | 02:13:40
TotalDuration: 03:35:15
above is the sample of my playlist that I would like to display in the app.
Following is what I did
DMediaPlayer.java
import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
import uk.co.caprica.vlcj.javafx.fullscreen.JavaFXFullScreenStrategy;
import uk.co.caprica.vlcj.javafx.videosurface.ImageViewVideoSurface;
import uk.co.caprica.vlcj.player.base.MediaPlayer;
import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
public class DMediaPlayer implements MediaPlayerInterface {
private ImageView mediaView;
private final MediaPlayerFactory mediaPlayerFactory;
private final EmbeddedMediaPlayer embeddedMediaPlayer;
public DMediaPlayer() {
this.mediaPlayerFactory = new MediaPlayerFactory();
this.embeddedMediaPlayer = mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer();
this.mediaView = new ImageView();
this.mediaView.setPreserveRatio(true);
embeddedMediaPlayer.videoSurface().set(new ImageViewVideoSurface(this.mediaView));
}
#Override
public void load(String filePath) throws FileNotFoundException {
embeddedMediaPlayer.media().startPaused(filePath);
embeddedMediaPlayer.controls().setPosition(0f);
}
#Override
public ImageView getMediaView(){
return mediaView;
}
}
The above class returns a ImageView i.e. DMediaPlayer().getMediaView()
and this ImageView is added inside another class that controls all the playbacks, As well as that class has the playlist. For now the playlist only displays the available media files audio/video.
It works fine.
But I want to add a new feature in which the list view not only displays the media files but also some additional information like total play time of the entire playlist and the play time of individual media file.
following is what I did.
File[] mediaList; // it is initialized in the constructor
private void updatePlayList(){
CompletableFuture.runAsync(() -> {
long totalDur = 0;
MediaPlayerFactory factory = new MediaPlayerFactory();
EmbeddedMediaPlayer player = factory.mediaPlayers().newEmbeddedMediaPlayer();
for (int i = 0; i < mediaList.length; i++){
long mediaDur = 0;
player.media().startPaused(mediaList[i].getAbsolutePath());
mediaDur = player.status().length();
totalDur += mediaDur;
drawer.addItem(
(i+1) + "> " +
mediaList[i].getName().substring(0, 25) + "..." +
" | " + millisToDuration(mediaDur)
);
}
drawer.getTotalDuration().setText("Total Duration: " + totalDur);
});
}
drawer is a class that controls ListView.
everything works fine except the vlcj.
It throws the following error.
../include/vlc_xlib.h:46:vlc_xlib_init: Xlib not initialized for threads.
This process is probably using LibVLC incorrectly.
Pass "--no-xlib" to libvlc_new() to fix this.
Exception in thread "ForkJoinPool.commonPool-worker-3" java.lang.IllegalStateException: Not on FX application thread; currentThread = ForkJoinPool.commonPool-worker-3
at javafx.graphics#19/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:299)
at javafx.graphics#19/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:458)
at javafx.graphics#19/javafx.scene.Parent$3.onProposedChange(Parent.java:474)
at javafx.base#19/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
at javafx.base#19/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
at javafx.controls#19/javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:282)
at javafx.controls#19/javafx.scene.control.skin.LabeledSkinBase.lambda$new$11(LabeledSkinBase.java:219)
at javafx.controls#19/com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler.lambda$new$1(LambdaMultiplePropertyChangeListenerHandler.java:88)
at javafx.base#19/javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:86)
at javafx.base#19/com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)
at javafx.base#19/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
at javafx.base#19/javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:104)
at javafx.base#19/javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:111)
at javafx.base#19/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:145)
at javafx.base#19/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:50)
at javafx.base#19/javafx.beans.property.StringProperty.setValue(StringProperty.java:71)
at javafx.controls#19/javafx.scene.control.Labeled.setText(Labeled.java:147)
at com.doruk.dplayer.controllers.PlayerController.lambda$updatePlayList$2(PlayerController.java:108)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Now how can I fix this error, or how can I run the vlcj in the background?
I mentioned background because, additional window opens that plays the video when I run the app.
or am I following the wrong approach...? is there better way...?
all I want is to display the playtime of each media in the list.
I went through several pages in this website but all I could find was to get the total time of the file currently being played. So I followed the above approach. i.e. create a separate media player and start it and then calculate the play time.

Related

JavaFX media volume bindings

I am working on small game in JavaFX.
I have an utility class to manage music:
private static Map<SongEnum, Media> songs = new EnumMap<>(SongEnum.class);
private static MediaPlayer currentSong;
public static void playSong(SongEnum song) {
if(songs == null && currentSong != null) {
currentSong.stop();
currentSong = null;
}
Media media = songs.get(song);
if(media == null) {
String path = ApplicationUtils.class.getResource(song.getPath()).toExternalForm();
media = new Media(path);
songs.put(song, media);
}
if(currentSong != null) {
if(currentSong.getMedia() == media)
return;
currentSong.stop();
}
currentSong = new MediaPlayer(media);
currentSong.setCycleCount(MediaPlayer.INDEFINITE);
currentSong.play();
}
I want to be able to play many different songs - is above approach correct?
Also I am not sure how to implement volume-management system in this case. My first thought was to bind some slider property (which lies in another class) with MediaPlayer property, but in this case it will change everytime the songs changes. So, what is the best way to achieve it?
In the game we're currently working on, we just used the volume property of the MediaPlayer. We put 0.3 on the background theme, and 0.8or 1on effects as they should be higher than the background theme. Test it out see how it works best by using currentSong.setVolume("0 to 1");. As of using a slider, why don't just use a setOnEndOfMediato loop the song. With this, the volume shouldn't change. This only works if you of course are only looping the same song.
currentSong.setOnEndOfMedia(() -> {
currentSong.seek(Duration.ZERO);
currentSong.play();
});
If not, I would add make currentSong static, and then access it like "YourMediaClass.currentSong.setVolume("slider.getSliderValue or whatever you use"). This is probably what you're looking for.

Codename One Android build upload issue - calendar button disconfigured

I very much like working with CN1. Unfortunately, last week an issue appeared, that has not existed before and I can't figure out a workaround.
When I upload my app up to the build server, the interface of my application gets strangely disconfigured. This happens during the upload process and affects both the uploaded app, as well as the app that is on my machine.
The app contains a calendar, a table, buttons and labels, but only the calendar is affected. There are several day buttons that are styled through css, but during the upload the day buttons lose their format, and so does the text of the remaining buttons.
The upload of the same app with identical configuration has worked seemlessly in the past and the problem with disconfiguration only appeared over the last days.
Am I doing something wrong?
#Override
protected void updateButtonDayDate(Button dayButton, int currentYear, int currentMonth, int day ) {
ArrayList<Integer> list = new ArrayList<Integer>();
//List <Integer>list = new List<>();
list.add(1);
list.add(12);
list.add(13);
list.add(14);
list.add(21);
for (int day_Number : list) {
if (day_Number == day) {
dayButton.setText("" + day);
//dayButton.setUIID("mycalenderday");
}
}
}
};

applying autosave features with javafx

I am creating an application for project management which has various features including saving and opening the saved file. My application is running smoothly but i want to add another feature in the application which will allow it to save the data after some time.
Here is my code for the save and save as functions.
#FXML
private void handleSave() {
File userstoryFile = mainApp.getUserStoryFilePath();
if (userstoryFile != null) {
mainApp.saveUserStoryDataToFile(userstoryFile);
} else {
handleSaveAs();
}
}
/**
* Opens a FileChooser to let the user select a file to save to.
*/
#FXML
private void handleSaveAs() {
FileChooser fileChooser = new FileChooser();
// Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
"XML files (*.xml)", "*.xml");
fileChooser.getExtensionFilters().add(extFilter);
// Show save file dialog
File file = fileChooser.showSaveDialog(mainApp.getPrimaryStage());
if (file != null) {
// Make sure it has the correct extension
if (!file.getPath().endsWith(".xml")) {
file = new File(file.getPath() + ".xml");
}
mainApp.saveUserStoryDataToFile(file);
}
}
Is it possible to add autosave feature here ( using timer function )? if yes, How?
click here to get complete application code
Something like this should be easy to implement using ScheduledExecutorService:
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(() -> mainApp.saveUserStoryDataToFile(file), 0, 1, TimeUnit.MINUTES);
For saving the data with a delay of one minute between saves (no initial delay).
Note that using this approach you need to take care of a few things:
Synchronize access to the data.
Prevent concurrent access to the file.
Making sure shutdown is called when (or before) the JavaFX platform exits.

child of child movie clip are null in imported object from flex to flash right after being created

I have an Movie Clip in Flash that have subobject of button type which has subobject of input text and movie clips. Right after creation core Moveclip all subobject are set to null, when I expect them to be valid objects.
// hierarchy:
// core:MC_Core_design
// button_1:B_Mybutton
// text_name // dynamic text with instance name
// mc_icon // movie clip with instance name
var core:MC_Core_design = new MC_Core_design();
addChild(core);
core.button_1.text_name.text = "hello world"; // error: text_name is null
core.button_1.mc_icon.visible = false; // error: mc_icon is null
MC_Core_design was created in Flash and exported to Actionscript. I've done this for button_1 class aswell. The code was written using Flex.
When I comment out both lines that result in error I get correct view of the core Movie clip with all subobject.
How can I set subobject properties right after object creation?
You need to listen for the Event.INIT from the class when it is created. (If you are not embedding a symbol using the Embed metatag then Flash takes a few milliseconds to initialize the loaded movieclip). This does not seem to be a problem if the Flash IDE swf/swc does not contain any actionscript)
The issue is sometimes it can be really quick, so it fires the INIT event before you get a chance to attach the event listener to the object. so you can't just attach it after you instantiate the object.
A work around is to embed the swf as a byte array, then use the loader class to load the embedded bytes (This lets you set the event listener before calling load).
e.g.
[Embed(source="assets.swf", mimeType="application/octet-stream")]
private var assetBytes:Class;
private var clip:MovieClip;
private var loader:Loader;
public function LoadBytesExample()
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, onAssetLoaded);
loader.loadBytes(new assetBytes());
}
private function onAssetLoaded(e:Event):void
{
var loader:Loader = (e.currentTarget as LoaderInfo).loader;
(e.currentTarget as LoaderInfo).removeEventListener(Event.INIT, onAssetLoaded);
clip = loader.content as MovieClip;
this.addChild(clip);
clip.someTextField.text = "HELLO WORLD";
}
Sorry for the formatting, just wrote that off the top of my head
And the syntax for embedding the symbol (You won't need to load this via a loader as the actionscript in the external swf/swc is stripped).
[Embed(source="assets.swf", symbol="somesymbol")]
private var assetSymbol:Class;
private var clip:MovieClip;
public function LoadSymbolExample()
{
clip = new assetSymbol();
clip.sometext.text = "Hello World";
}
If I see it right, button_1:B_Mybutton is not yet initialized.
I mean something like : button_1:B_Mybutton = new B_Mybutton();
About the other two variables text_name & mc_icon as you describe if they have been initialized already (as you term them as instance names), Iguess they should not give you any problem.
Also I asssume that you are setting access modifiers to all as public.
If you still have problem... pls share how all the required variables are defined. Just the relevant part would be enough.

Flex 3 multiple upload progress monitoring

I have a Flex3 application which has to be capable of uploading multiple files and monitoring each files individual progress using a label NOT a progress bar.
My problem is that a generic progress handler for the uploads has no way (that I know of) of indicating WHICH upload it is that is progressing. I know that a file name is available to check but in the case of this app the file name might be the same for multiple uploads.
My question: With a generic progress handler how does one differentiate between 2 multiple uploads with the same file name?
EDIT: answerers may assume that I am a total newb to Flex... because I am.
I use this:
private function _addFileListeners(dispatcher:IEventDispatcher):void {
dispatcher.addEventListener(Event.OPEN, this._handleFileOpen);
dispatcher.addEventListener(Event.SELECT, this._handleFileOpen);
dispatcher.addEventListener(Event.CANCEL, this._handleFileCancel);
dispatcher.addEventListener(ProgressEvent.PROGRESS, this._handleFileProgress);
dispatcher.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA,this._handleFileComplete);
dispatcher.addEventListener(IOErrorEvent.IO_ERROR, this._handleError);
dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this._handleError);
}
where "dispatcher" is the file:
for (var i:uint = 0; i < fileList.length; i++) {
file = FileReference(fileList[i]);
this._addFileListeners(file);
this._pendingFiles.push(file);
}
and a sample handler:
private function _handleFileOpen(e:Event):void {
var file:FileReference = FileReference(e.target);
...
}
I'm not sure how you want to differentiate between two files with the same name. In my case, I send the files in a queue. So there's only ever 1 file being uploaded at a time. (pendingFiles).
If you are listening for ProgressEvents, these events have a currentTarget attribute that would have a reference to the object that has registered the event listener.
I'm assuming you know which file-uploading object goes with each object in the first place.
EDIT: Example using FileReference:
import flash.net.FileReference;
import flash.events.ProgressEvent;
import flash.utils.Dictionary;
public var files:Dictionary = new Dictionary(); // This will hold all the FileReference objects
public function loadFile(id:String):void
{
var file:FileReference = new FileReference();
// Listen for the progress event on this FileReference... will call the same function for every progress event
file.addEventListener(ProgressEvent.PROGRESS, onProgress);
// TODO: listen for errors and actually upload a file, etc.
// Add file to the dictionary (as key), with value set to an object containing the id
files[file] = { 'id': id };
}
public function onProgress(event:ProgressEvent):void
{
// Determine which FileReference dispatched thi progress event:
var file:FileReference = FileReference(event.target);
// Get the ID of the FileReference which dispatched this function:
var id:String = files[file].id;
// Determine the current progress for this file (in percent):
var progress:Number = event.bytesLoaded / event.bytesTotal;
trace('File "' + id + '" is ' + progress + '% done uploading');
}
// Load some files:
loadFile('the first file');
loadFile('the second file');
I ended up creating my own class that manages events for each uploading file

Resources