adrg / libvlc-go

Handcrafted Go bindings for libVLC and high-level media player interface
https://pkg.go.dev/github.com/adrg/libvlc-go/v3
MIT License
437 stars 50 forks source link

Https video streams don't play #47

Closed matejbizjak closed 4 years ago

matejbizjak commented 4 years ago

Player works fine with http streams, but it will not play streams over SSL.

I have the same problem with Python library... Is there a solution for playing over https?

adrg commented 4 years ago

Hi @matejbizjak. Is this a public stream you are trying to play? Can you share the link so I can test? The audio stream in the README usage section can also be played over https. I tested with that one and it works fine for me.

Have you tried playing the stream using the vlc application? Does it work with the application?

matejbizjak commented 4 years ago

@adrg, yes, it is a public stream. I tried to play Vimeo and Youtube videos (example1, example2). They all work in the vlc application, I only have problems in the code with the library. However, I checked with some other https streams now and it works fine.

On Vimeo streams I got this errors: [0000000028997960] main tls client error: connection error: Interrupted function call

matejbizjak commented 4 years ago

That might be a specific problem only for Youtube and Vimeo as I'm reading here. However, it works in the vlcapplication.

adrg commented 4 years ago

Yes, there are some Lua plugins used for Vimeo and Youtube as the real video URL is embedded somewhere in the page (for Youtube, I think the plugin actually runs some Javascript in order to obtain the video URL). In any case, there are no issues with the Lua plugins as far as I can tell.

It seems like the media item retrieved from these sources has some subitems. I think I need to implement the libvlc_media_subitems binding for this. Then, you would call this new method for the obtained media and play the first subitem. This is the case if you want to play the media using the regular player.

However, I found that I am able to play the Vimeo media using the list player, as the list player can play media with subitems.

package main

import (
    "log"

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

func main() {
    if err := vlc.Init(); err != nil {
        log.Fatal(err)
    }
    defer vlc.Release()

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

    // Create a new media list.
    list, err := vlc.NewMediaList()
    if err != nil {
        log.Fatal(err)
    }
    defer list.Release()

    err = list.AddMediaFromURL("https://vimeo.com/259411563")
    if err != nil {
        log.Fatal(err)
    }

    // Set player media list.
    if err = player.SetMediaList(list); err != nil {
        log.Fatal(err)
    }

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

    // 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{}) {
        close(quit)
    }

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

    <-quit
}

EDIT: I think the vlc application actually uses a list player by default as well.

adrg commented 4 years ago

I implemented the binding for retrieving media sub-items. However, the process for playing Youtube and Vimeo media using the regular player is kind of tedious.

Here are the steps:

  1. Create media instance.
  2. Register the vlc.MediaSubItemTreeAdded event with the media event manager.
  3. Parse the media item.
  4. The sub-items are only available once the media is parsed. So once, the vlc.MediaSubItemTreeAdded event is fired, retrieve the sub-item list and play the first one.

NOTE: for Vimeo, it's actually even worse as the first sub-item needs to be parsed as well, and its first sub-item is the actual video that can be played.

So my recommendation is using a list player when playing media from these two sources, as it is a lot easier.

package main

import (
    "log"

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

func main() {
    // Initialize libVLC.
    if err := vlc.Init(); 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()
    }()

    // Create media instance.
    media, err := vlc.NewMediaFromURL("https://youtube.com/watch?v=or1dAmUO8k0")
    if err != nil {
        log.Fatal(err)
    }
    defer media.Release()

    // Listen for sub-item change events for the media instace.
    mManager, err := media.EventManager()
    if err != nil {
        log.Fatal(err)
    }

    mEventCB := func(event vlc.Event, userData interface{}) {
        switch event {
        case vlc.MediaSubItemTreeAdded:
            subItems, err := media.SubItems()
            if err != nil {
                log.Fatal(err)
            }
            defer subItems.Release()

            m, err := subItems.MediaAtIndex(0)
            if err != nil {
                log.Fatal(err)
            }

            if err := player.SetMedia(m); err != nil {
                log.Fatal(err)
            }
            if err := player.Play(); err != nil {
                log.Fatal(err)
            }
        }
    }

    mEventID, err := mManager.Attach(vlc.MediaSubItemTreeAdded, mEventCB, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer mManager.Detach(mEventID)

    // Parse media.
    err = media.ParseWithOptions(0, vlc.MediaParseNetwork)
    if err != nil {
        log.Fatal(err)
    }

    // 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{})
    eventCB := func(event vlc.Event, userData interface{}) {
        close(quit)
    }

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

    <-quit
}

Also, make sure you have the latest versions of vimeo.lua and youtube.lua. You can find them on the vlc repository at https://github.com/videolan/vlc/tree/master/share/lua/playlist. Just copy the files in the Lua playlist plugin directory of your vlc installation.

matejbizjak commented 4 years ago

@adrg Thanks, it works great now!