libvlc-go icon indicating copy to clipboard operation
libvlc-go copied to clipboard

Deadlock on player actions after MediaPlayerEndReached

Open Heathland opened this issue 3 years ago • 3 comments

Hi,

I'm trying to make it that instead of the player re-initializing when using loop (and thus causing the player to close and open), I thought to work around this by using the event manager and resetting the media position once it reaches the end of the video.

So I made the following:

	eventCallback := func(event vlc.Event, userData interface{}) {
		log.Println("Restarting video")

		if player.IsPlaying() {
			log.Println("Stop")
			if err := player.Stop(); err != nil {
				log.Fatalf("Unable to stop player: %s", err)
			}
		}
		log.Println("Setting media position")
		if err := player.SetMediaTime(0); err != nil {
			log.Fatalf("Unable to reset media position: %s", err)
		}

		log.Println("Play")
		if err := player.Play(); err != nil {
			log.Fatalf("Unable to start player: %s", err)
		}
	}
....
	eventID, err := manager.Attach(vlc.MediaPlayerEndReached, eventCallback, nil)

However, once I hit SetMediaTime, I hit a deadlock. It just hangs there. Can you give me any pointers on why this is ?

Kind regards

Heathland avatar Jan 18 '22 09:01 Heathland

Hi @Heathland. Thank you for your interest in the library.

Can you provide more information regarding the logic of your application? Are you trying to achieve 'gapless' playback? That's not possible in libVLC 3 unfortunately.

The deadlock is caused by the fact that you are calling libVLC functionality that mutates the state of the player from the event manager thread. That's not a limitation of this library. It's a known issue of libVLC.

A workaround for that is to wrap your code in a Go routine:

go func() {
    // your code here.
}()

That won't solve the issue though, because when the MediaPlayerEndReached is triggered, the media instance is finished, it cannot be replayed. You can retrieve the media instance from the player and call SetMedia with it again and then play, but that would cause the player to close and then reopen.

One option that does work, but it's not very configurable, is to pass --input-repeat when initializing the library.

if err := vlc.Init("--input-repeat=999999", "--quiet"); err != nil {
    log.Fatal(err)
}
defer vlc.Release()

But that's quite limiting in many scenarios. Another option, is to use the vlc.MediaPlayerTimeChanged event and set the media time a bit before it ends.

package main

import (
	"log"

	vlc "github.com/adrg/libvlc-go/v3"
)

func main() {
	// Initialize libVLC. Additional command line arguments can be passed in
	// to libVLC by specifying them in the Init function.
	if err := vlc.Init("--input-repeat=999999", "--quiet"); err != nil {
		log.Fatal(err)
	}
	defer vlc.Release()

	// Create a new player.
	player, err := vlc.NewPlayer()
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		player.Stop()
		player.Release()
	}()

	// Add a media file from path.
	media, err := player.LoadMediaFromPath("sample.mp4")
	if err != nil {
		log.Fatal(err)
	}
	defer media.Release()

	// Retrieve player event manager.
	manager, err := player.EventManager()
	if err != nil {
		log.Fatal(err)
	}

	// Register the media end reached event with the event manager.
	quit := make(chan struct{})
	eventCallback := func(event vlc.Event, userData interface{}) {
		mediaLength, _ := player.MediaLength()
		mediaTime, _ := player.MediaTime()

		if mediaLength > 0 && mediaTime >= mediaLength-500 {
			go func() {
				if err := player.SetMediaTime(0); err != nil {
					log.Fatalf("unable to reset media position: %s", err)
				}
			}()
		}
	}

	eventID, err := manager.Attach(vlc.MediaPlayerTimeChanged, eventCallback, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer manager.Detach(eventID)

	// Start playing the media.
	err = player.Play()
	if err != nil {
		log.Fatal(err)
	}

	<-quit
}

adrg avatar Jan 18 '22 12:01 adrg

Hi @adrg ,

You hit the nail right on the head. Yeah I'm trying to do exactly as you mention. In the past I've used omxplayer. But due to it not being supported anymore in the latest Raspberry OS version (Buster), and their push to VLC, I thought I'd give VLC a go and then using bindings to boot. However, I noticed that VLC kept restarted the player, causing it to close and reopen for a short moment and not behaving as omxplayer does, which just smoothly loops. Also I noticed it redownloads the media everytime it restarted. I get why, as omxplayer doesn't have a list media feature, and VLC does. But due to that it needs to reinitialize with each asset. So I thought I could work around by just resetting the playback position to the start of the video once it ended.

I already had a feeling the fact that the media had the state Ended that was the cause. I'll give your suggestion a shot. Thank you for the answer and this awesome lib!

Heathland avatar Jan 18 '22 13:01 Heathland

I'm glad to help. Let me know how it goes.

adrg avatar Jan 18 '22 13:01 adrg

Closing this issue as it has been addressed. Please reopen if the provided solution did not work for you.

adrg avatar Nov 09 '22 09:11 adrg