3

I am trying to play YouTube using the LibVLC on multiple instances, in a WPF app. This works fine maybe 75% of the time, but stream freeze for a second or 2 and get pixeleted the other 25%. This seems to be random.

Here is my code:

public async void PlayUri(string path, bool isMuted, bool repeat)
{
    await Dispatcher.InvokeAsync(new Action(async () =>
    {
        Core.Initialize();

        using (LibVLC libVLC = new LibVLC(MediaOptions(repeat)))
        {
            using (var media = new Media(libVLC, path, FromType.FromLocation))
            {
                await media.Parse(MediaParseOptions.ParseNetwork);
                vlcPlayer = new MediaPlayer(media.SubItems.FirstOrDefault());
                if (!repeat)
                    vlcPlayer.EndReached += (sender, args) => ThreadPool.QueueUserWorkItem(_ => waitHandle.Set());
                VideoView.MediaPlayer = vlcPlayer;
                VideoView.MediaPlayer.Play();
            }

            // Set the sound and audio output device            
            SetAudioToDirectsound(isMuted);
        }
    }));            
}

MediaOptions is just passing a string[]

mediaOptions = new[]
{
    "--input-repeat=5",
    "--sout-mux-caching=12000"
};

And (for completeness) SetAudioToDirectSount(bool)

var directsound = VideoView.MediaPlayer.SetAudioOutput("directsound");
IsMuted = isMuted;

This "--sout-mux-caching=12000" option should be caching the next 12 seconds of the video.
Is this correct, and do I need any other options? I look all over the cmd line options and cannot seem to find anything obvious.

Reading at the best practises here, it mentions that
"It is recommended by VLC core developers to only create a single instance of type LibVLC during your application's lifecycle. You may create as many MediaPlayer objects as you want from a single LibVLC object."

In my case it is nested inside a using statement, so I don't seem to be following the best practises. So should I declare:

Core.Initialize();
public LibVLC libVLC {get; set; } = new LibVLC();

only once, and only dispose of the MediaPlayer on unload?


Minimal Testing Solution

New WPF project called VLCStream

NuGet install:

  • VideoLAN.LibVLC.Windows
  • LibVLCSharp.WPF
  • LibVLCSharp

MainWindow.xaml

<Window x:Class="VLCStream.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF"
        xmlns:local="clr-namespace:VLCStream"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        Loaded="Window_Loaded">
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Button Content="Play" Click="Button_Click" />

        <vlc:VideoView x:Name="VideoView" Grid.Row="1" />
    </Grid>
</Window>

MainWindow.xaml.cs

using LibVLCSharp.Shared;
using LibVLCSharp.WPF;
using System;
using System.Linq;
using System.Windows;

namespace VLCStream
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Dispatcher.InvokeAsync(new Action(async () =>
            {
                using (LibVLC libVLC = new LibVLC("--sout-mux-caching=12000"))
                {
                    using (var media = new Media(libVLC, "https://youtu.be/pW-iVG0_D34", FromType.FromLocation))
                    {
                        await media.Parse(MediaParseOptions.ParseNetwork);
                        MediaPlayer vlcPlayer = new MediaPlayer(media.SubItems.FirstOrDefault());
                        VideoView.MediaPlayer = vlcPlayer;
                        VideoView.MediaPlayer.Play();
                    }
                }
            }));
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Core.Initialize();
        }
    }
}

Issue seem to be random, more than 1 run needed.

Franck E
  • 629
  • 8
  • 26
  • 1
    "*to only create a single instance of type LibVLC during your application's lifecycle*" seems like fairly straight forward wording to me. Try it an d see if you have the same issue – TheGeneral Oct 05 '21 at 06:29
  • Thanks for the suggestion. I moved the libvlc and initialization to the mainviewmodel, and create mediaplayers from different vms referencing it. That unfortunately did not help. It is about the same... – Franck E Oct 05 '21 at 09:04
  • where is the minimal repro code so we can try locally? – mfkl Oct 06 '21 at 02:02
  • Minimal code added to question. No dispose code used to make testing solution shorter. – Franck E Oct 06 '21 at 07:55
  • could you please setup a small repository so we can just clone and run? That will avoid other differences as well (csproj, nuget package versions, etc). thanks – mfkl Oct 07 '21 at 05:12
  • https://github.com/Franck-E/VLCStream – Franck E Oct 07 '21 at 08:55
  • 1
    remove `sout-mux-caching`, this is completely unrelated to your use case – mfkl Oct 08 '21 at 07:12

2 Answers2

1

General feedback on your code.

  • Keep track of the mediaplayer as long as you are using it, and dispose it when you're done.
  • Don't dispose LibVLC while you're using it.
  • Integrate each mediaplayer with a view, right now you're just calling play on mediaplayers who don't have a drawing target. LibVLC will create random windows to draw on then. Probably not what you want.

Now, playing your stream in the VLC desktop app with logs open will provide some insights:

main error: ES_OUT_SET_(GROUP_)PCR is called too late (pts_delay increased to 2926 ms)
main debug: Received first picture
main error: Timestamp conversion failed for 9480001: no reference clock
main error: Could not convert timestamp 0 for FFmpeg
main debug: Buffering 0%
main debug: Buffering 8%
main debug: Buffering 17%
main debug: Buffering 25%
main debug: Buffering 34%
main debug: Buffering 42%
main debug: Buffering 51%
main debug: Buffering 59%
main debug: Buffering 68%
main debug: Buffering 76%
main debug: Buffering 85%
main debug: Buffering 93%
main debug: Stream buffering done (3000 ms in 7056 ms)
main debug: Decoder wait done in 0 ms
main debug: inserting 307 zeroes
mmdevice debug: state changed: 1
main warning: buffer too late (-217851 us): dropped
main warning: buffer too late (-194631 us): dropped
main error: ES_OUT_SET_(GROUP_)PCR is called too late (pts_delay increased to 3774 ms)
main debug: ES_OUT_RESET_PCR called
main warning: buffer too late (-171411 us): dropped
main debug: Received first picture
main error: Timestamp conversion failed for 15800001: no reference clock
main error: Could not convert timestamp 0 for FFmpeg
mmdevice debug: state changed: 0

This doesn't look good. Maybe YouTube badly encoded the video? Or more likely a problem with LibVLC? I'm not sure.

mfkl
  • 1,914
  • 1
  • 11
  • 21
  • I found a recent page https://forum.videolan.org/viewtopic.php?p=516017 that pointed me to https://forum.videolan.org/viewtopic.php?p=483226#p483226. Creating the youtube.luac and putting it in VLC folder does fix the VLC Desktop. Now, how do we do the same for libvlc? – Franck E Oct 10 '21 at 11:10
  • ok, the lua goes there: \packages\VideoLAN.LibVLC.Windows.3.0.16\build\x64\lua\playlist and added the following options to LibVLC initialization: "--network-caching=8000", "--directx-use-sysmem". That creates video buffers in system memory instead of video memory, and increase caching, which initially seemed to have solved the initial issue. On a longer test, this is exactely the same... Errors you showed are still there.. – Franck E Oct 11 '21 at 07:50
1

Issue seem to have been linked to the youtube.luac file.
The file, updated around October 19 2021, seems to work perfectly. Thank you Martin for the assistance.

A basic project is available here.

The 3 packages installed are:

enter image description here

Before starting, delete the .vs in project folder, as well as the bin and obj folders in the solution.

Go to here, copy the block of text and then paste it into Notepad. Click File ---> Save As and give it the name youtube.luac Underneath that field change "Text documents" (txt) into All Files via the drop down menu and then click Save.

Replace the file in the packages folder for both 32 and 64.
e.g. \packages\VideoLAN.LibVLC.Windows.3.0.16\build\x64\lua\playlist

Rebuild your project, and voila.
That solved it for me, and as little documentation is available, hopefully this will help someone.. :)

Franck E
  • 629
  • 8
  • 26