B4J Question Handling _Finished event in jVLC library

bdunkleysmith

Active Member
Licensed User
Longtime User
Despite trying many ideas unsuccessfully to fix the problem raised in this thread Can I do more than MediaView.dispose to make MediaView ready again?, I thought I'd try the jVLC library released by @moster67 in the hope that its video player would be more resilient than the JavaFX one.

I thought the swap would be relatively simple, with my existing use of the MediaView event _Complete being replaced by the jVLC _Finished event, however I've encountered a fatal error in the handling of _Finished.

The code which determines each video file name in sequence, calls the player sub and waits for the video to complete/finish before proceeding to the next is:

B4X:
Private vlc As B4JVlcj
vlc.Initialize("vpp")
Dim jo As JavaObject = Display.GridPane1
jo.RunMethodJo("getChildren", Null).RunMethod("add", Array(vlc.player))



Sub PlayerIntros
    ' Show videos or images of players in that preferential order
    For Each k As Object In mapHomeTeam.Keys
        Dim playerId As String = mapHomeTeam.Get(k)
        For a = 0 To mapHomeTeam.Size - 1
        If playerId = arrayHome(a, 0) Then
            playerNumberForDisplay = arrayHome(a, 1).SubString2(0, 2).Trim
            mediaFile = arrayHome(a, 1).SubString(arrayHome(a, 1).IndexOf(" ") + 2)   
            If IntroVideos(mediaFile & ".mp4", playerNumberForDisplay) = True Then
                Timer5.Enabled = True
                wait For video_Complete
                Display.GridPane1.Visible = False
                Display.playernumber.As(B4XView).SendToBack
                Timer5.Enabled = False
            Else if IntroImages(mediaFile & ".png", playerNumberForDisplay) = True Then
                Sleep(txtIntrosDelay.text * 1000)
                Display.ivGraphics.visible = False
            Else
                IntroImages("noImageFile.png", playerNumberForDisplay)
                Sleep(txtIntrosDelay.text * 1000)
                Display.ivGraphics.visible = False
            End If
        End If
        Next
    Next
    Display.playerNumber.Visible = False
End Sub

This is the video player code called from the sequencer:

B4X:
Sub IntroVideos(videoFile As String, pNum As String ) As Boolean    'Player - first initial dot space secondname dot MP4, player singlet number / Coaches & Team Managers - firstname dot space secondname dot MP4, ""
    Display.playerNumber.text = pNum
    Display.playerNumber.visible = True
    If File.Exists(PublicApplicationDataFolder & "\Intros\" & cmbOrganisationID.Value, videoFile) = True Then
        vlc.Play(PublicApplicationDataFolder & "\Intros\" & cmbOrganisationID.Value & "\" & videoFile)
        Log(PublicApplicationDataFolder & "\Intros\" & cmbOrganisationID.Value & "\" & videoFile)
        Display.GridPane1.Visible = True
        vlc.mute
        Display.playernumber.As(B4XView).BringToFront
        Return True
    Else
        Display.playernumber.As(B4XView).BringToFront
        Return False
    End If
End Sub

And here is the code for handling the jVLC _Finished event and the watchdog timer (Timer5) in case a video stalls so that the PlayerIntros sub resumes:

B4X:
Sub vpp_Finished
    Log("VLC video finished")
    CallSubDelayed(Me, "video_Complete")
End Sub

Sub Timer5_tick
    Log("Video watchdog timer error for file: " & mediaFile & ".mp4")
    CallSubDelayed(Me, "video_Complete")
End Sub

Now when Timer5_tick occurs, the PlayerIntros sub resumes and the video player moves onto the next video file as expected/intended, however if the video finishes before Timer5_tick occurs and the vpp_Finished event occurs as indicated by the "VLC video finished" entry in the log, the application crashes after a couple of seconds with no further error entry in the log.

If I change that _Finished event handler to:

B4X:
Sub vpp_Finished
    Log("VLC video finished")
    CallSub(Me, "video_Complete")
End Sub

ie. change CallSubDelayed to CallSub, then after the "VLC video finished" entry, there is an error entry in the log before the application crashes:

C:\Users\Public\Documents\BDSConsulting\BDSScoreboard\Intros\NBL1\K. Vitale.mp4
Video watchdog timer error for file: K. Vitale.mp4
C:\Users\Public\Documents\BDSConsulting\BDSScoreboard\Intros\NBL1\A. Bandilovska.mp4
VLC video finished
main._vpp_finished (java line: 9584)
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.IllegalStateException: Not on FX application thread; currentThread = pool-3-thread-1
at anywheresoftware.b4a.keywords.Common.CallSub4(Common.java:523)
at anywheresoftware.b4a.keywords.Common.CallSubNew(Common.java:461)
at b4j.example.main._vpp_finished(main.java:9584)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:577)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:111)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:98)
at com.tillekesoft.vlcj.B4JVlcj$1.finished(B4JVlcj.java:351)
at uk.co.caprica.vlcj.player.events.MediaPlayerEndReachedEvent.notify(MediaPlayerEndReachedEvent.java:41)
at uk.co.caprica.vlcj.player.DefaultMediaPlayer$NotifyEventListenersRunnable.run(DefaultMediaPlayer.java:2253)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.RuntimeException: java.lang.IllegalStateException: Not on FX application thread; currentThread = pool-3-thread-1
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:140)
at anywheresoftware.b4a.keywords.Common.CallSub4(Common.java:514)
... 14 more
Caused by: java.lang.IllegalStateException: Not on FX application thread; currentThread = pool-3-thread-1
at [email protected]/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:297)
at [email protected]/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:458)
at [email protected]/javafx.scene.Parent$3.onProposedChange(Parent.java:474)
at [email protected]/com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:328)
at [email protected]/com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:220)
at [email protected]/javafx.scene.Parent.toBack(Parent.java:749)
at [email protected]/javafx.scene.Node.toBack(Node.java:2004)
at anywheresoftware.b4a.objects.B4XViewWrapper.SendToBack(B4XViewWrapper.java:728)
at b4j.example.main$ResumableSub_PlayerIntros.resume(main.java:6435)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:156)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:105)
... 15 more

I'm out of my depth here, but is it to do with VLC and the main application not being on the same thread? If so, how should I be handling the event to achieve my objective? Or if I'm on the wrong tack, then any suggestions/guidance would be appreciated.
 

stevel05

Expert
Licensed User
Longtime User
ie. change CallSubDelayed to CallSub, then after the "VLC video finished" entry, there is an error entry in the log before the application crashes

That is to be expected as CallSubDelayed queues the sub on the main Thread, whereas CallSub will call the event immediately on the existing thread.

I am unfamiliar with jVLC, you don't show the Video_Complete sub, and without being able to try it, This is just a suggestion:

Is the problem that when vpp_finished is called, the Timer_Tick is still called after. Could you not disable Timer_5 tick in vpp_finished, and restart it when a new video is played.
 
Last edited:
Upvote 0

bdunkleysmith

Active Member
Licensed User
Longtime User
Thanks @stevel05 for your response.

There is no Video_Complete sub as such. I just have the line:

B4X:
wait For video_Complete

in the PlayerIntros sub so that the sequencing can continue based on the vpp_Finished, Timer5_tick and vpp_Error event handlers. Code for the first two is above and here's the code for the vpp_Error event handler:

B4X:
Sub vpp_Error (Message As String)
    Log("Intro video file " & mediaFile & ".mp4 error: " & Message)
    If IntroImages(mediaFile & ".png", playerNumberForDisplay) = True Then
        Sleep(txtIntrosDelay.text * 1000)
        Display.ivGraphics.visible = False
        Log("Therefore use " & mediaFile & ".png")
    Else
        IntroImages("noImageFile.png", playerNumberForDisplay)   
        Sleep(txtIntrosDelay.text * 1000)
        Display.ivGraphics.visible = False
        Log("Therefore use noImageFile.png")
    End If
    CallSubDelayed(Me, "video_Complete")
End Sub

This shows a named image in lieu of the video if there's an error with the video or the noImageFile.png image if in addition the named image is missing.

Timer5_Tick shouldn't be called after vpp_finished is called because Timer5 is reset just after the wait for:

B4X:
                wait For video_Complete
                Display.GridPane1.Visible = False
                Display.playernumber.As(B4XView).SendToBack
                Timer5.Enabled = False

But I have disabled the watchdog timer (Timer5) to allow debugging in case that was an issue and put some more logging into the code to try and pinpoint where the error's occurring. That's work in progress and I'll report back later.

However I'm beginning to think I'll have to return to my original JavaFX MediaView based solution in lieu of VLC because there seems quite a delay in loading the video file before it plays in VLC and given this app typically sequences 12 video files of around 6 seconds each, I can't afford any significant delay between them. I may be able to deal with it if you could load the video file, wait for the _Prepared event before playing it, but loading the file is combined in the Play method (and I've been unable to find a definition of when the _Prepared event is triggered.

This is a case where my motto of "If the solution is becoming complicated, then you're probably taking the wrong approach" is coming into play and so I probably need to go back and find out what now, after a couple of years of trouble-free use, I'm encountering random invalid media errors with JavaFX MediaView.
 
Upvote 0
Top