Skip to content
This repository has been archived by the owner on Jan 21, 2022. It is now read-only.

Commit

Permalink
Reworked VlcManager architecture (#490)
Browse files Browse the repository at this point in the history
to ensure proper loading/unloading of libvlc*.dll

Fixes #482
  • Loading branch information
jeremyVignelles authored Jan 30, 2019
1 parent 8b10ba0 commit fb42e9a
Show file tree
Hide file tree
Showing 127 changed files with 304 additions and 337 deletions.
8 changes: 4 additions & 4 deletions src/Samples/Samples.WinForms.MultiplePlayers/Form1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,23 @@ private void button1_Click(object sender, EventArgs e)
/// <returns></returns>
private async Task AddPlayerAsync()
{
await Task.Run(() =>
await Task.Run(async () =>
{
AutoResetEvent uiUpdated = new AutoResetEvent(false);
var player = new VlcControl
{
VlcLibDirectory = new DirectoryInfo(this.vlcLibraryPath),
Size = new Size(200, 200)
};

var uiUpdatedTask = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
panel.BeginInvoke( (MethodInvoker) delegate
{
panel.Controls.Add(player);
player.EndInit();
uiUpdated.Set();
uiUpdatedTask.SetResult(true);
});

uiUpdated.WaitOne();
await uiUpdatedTask.Task.ConfigureAwait(false);

lock (listOfControls)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,38 @@

namespace Vlc.DotNet.Core.Interops
{
public abstract class VlcInteropsManager : IDisposable
/// <summary>
/// This class loads the required libvlc libraries.
/// Only one instance per directory will be created.
///
/// Use <see cref="GetOrCreateLoader" /> and <see cref="ReleaseLoader"/> to get a VlcLibraryLoader instance and release it properly.
/// Do not call Dispose() by yourself, it will be called as needed by ReleaseLoader.
/// </summary>
internal class VlcLibraryLoader: IDisposable
{
private static Dictionary<string, VlcLibraryLoader> myLoaderInstances = new Dictionary<string, VlcLibraryLoader>();

private readonly DirectoryInfo myDynamicLinkLibrariesPath;

/// <summary>
/// Caches of the delegates that were resolved from the libvlc.
/// These delegates are cast to the correct delegate type when a query is made
/// Number of objects that are using this loader
/// </summary>
private readonly Dictionary<string, object> myInteropDelegates = new Dictionary<string, object>();
private int myRefCount;

private IntPtr myLibGccDllHandle;
private IntPtr myLibVlcDllHandle;
private IntPtr myLibVlcCoreDllHandle;

protected VlcInteropsManager(DirectoryInfo dynamicLinkLibrariesPath)
/// <summary>
/// Caches of the delegates that were resolved from the libvlc.
/// These delegates are cast to the correct delegate type when a query is made
/// </summary>
private readonly Dictionary<string, object> myInteropDelegates = new Dictionary<string, object>();

private VlcLibraryLoader(DirectoryInfo dynamicLinkLibrariesPath)
{
if (!dynamicLinkLibrariesPath.Exists)
throw new DirectoryNotFoundException();
myDynamicLinkLibrariesPath = dynamicLinkLibrariesPath;
myRefCount = 1;

var libGccDllPath = Path.Combine(dynamicLinkLibrariesPath.FullName, "libgcc_s_seh-1.dll");
if (File.Exists(libGccDllPath))
Expand Down Expand Up @@ -65,7 +82,7 @@ internal T GetInteropDelegate<T>()
vlcFunctionName = attr.FunctionName;
if (myInteropDelegates.ContainsKey(vlcFunctionName))
{
return (T) myInteropDelegates[attr.FunctionName];
return (T)myInteropDelegates[attr.FunctionName];
}

var procAddress = Win32Interops.GetProcAddress(myLibVlcDllHandle, attr.FunctionName);
Expand All @@ -82,13 +99,7 @@ internal T GetInteropDelegate<T>()
}
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

public virtual void Dispose(bool disposing)
void IDisposable.Dispose()
{
if (myLibVlcDllHandle != IntPtr.Zero)
{
Expand All @@ -107,9 +118,47 @@ public virtual void Dispose(bool disposing)
}
}

~VlcInteropsManager()
/// <summary>
/// Creates a new VlcLibraryLoader, or get the existing one for that folder from the dictionary (increment its reference count).
/// </summary>
/// <param name="dynamicLinkLibrariesPath">The path from the dictionary</param>
/// <returns>The loader</returns>
public static VlcLibraryLoader GetOrCreateLoader(DirectoryInfo dynamicLinkLibrariesPath)
{
if (!dynamicLinkLibrariesPath.Exists)
throw new DirectoryNotFoundException();

lock (myLoaderInstances)
{
if (myLoaderInstances.ContainsKey(dynamicLinkLibrariesPath.FullName))
{
var instance = myLoaderInstances[dynamicLinkLibrariesPath.FullName];
instance.myRefCount++;
return instance;
}

var returnedInstance = new VlcLibraryLoader(dynamicLinkLibrariesPath);
myLoaderInstances[dynamicLinkLibrariesPath.FullName] = returnedInstance;
return returnedInstance;
}
}

/// <summary>
/// Decrements the reference counter of the specified loader.
/// If this reference counter reaches 0, it is destroyed
/// </summary>
/// <param name="loader"></param>
public static void ReleaseLoader(VlcLibraryLoader loader)
{
Dispose(false);
lock (myLoaderInstances)
{
loader.myRefCount--;
if (loader.myRefCount == 0)
{
((IDisposable)loader).Dispose();
myLoaderInstances.Remove(loader.myDynamicLinkLibrariesPath.FullName);
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public void AddOptionFlagToMedia(VlcMediaInstance mediaInstance, string option,

using (var handle = Utf8InteropStringConverter.ToUtf8StringHandle(option))
{
GetInteropDelegate<AddOptionFlagToMedia>().Invoke(mediaInstance, handle, flag);
myLibraryLoader.GetInteropDelegate<AddOptionFlagToMedia>().Invoke(mediaInstance, handle, flag);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public void AddOptionToMedia(VlcMediaInstance mediaInstance, string option)

using (var handle = Utf8InteropStringConverter.ToUtf8StringHandle(option))
{
GetInteropDelegate<AddOptionToMedia>().Invoke(mediaInstance, handle);
myLibraryLoader.GetInteropDelegate<AddOptionToMedia>().Invoke(mediaInstance, handle);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Vlc.DotNet.Core.Interops/VlcManager.AttachEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public int AttachEvent(VlcEventManagerInstance eventManagerInstance, EventTypes
throw new ArgumentException("Event manager instance is not initialized.");
if (callback == null)
throw new ArgumentException("Callback for event is not initialized.");
return GetInteropDelegate<AttachEvent>().Invoke(eventManagerInstance, eventType, callback, IntPtr.Zero);
return myLibraryLoader.GetInteropDelegate<AttachEvent>().Invoke(eventManagerInstance, eventType, callback, IntPtr.Zero);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public sealed partial class VlcManager
{
public void ClearLastErrorMessage()
{
GetInteropDelegate<ClearLastErrorMessage>().Invoke();
myLibraryLoader.GetInteropDelegate<ClearLastErrorMessage>().Invoke();
}
}
}
2 changes: 1 addition & 1 deletion src/Vlc.DotNet.Core.Interops/VlcManager.CloneMedia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public VlcMediaInstance CloneMedia(VlcMediaInstance mediaInstance)
{
if (mediaInstance == IntPtr.Zero)
throw new ArgumentException("Media instance is not initialized.");
return VlcMediaInstance.New(this, GetInteropDelegate<CloneMedia>().Invoke(mediaInstance));
return VlcMediaInstance.New(this, myLibraryLoader.GetInteropDelegate<CloneMedia>().Invoke(mediaInstance));
}
}
}
2 changes: 1 addition & 1 deletion src/Vlc.DotNet.Core.Interops/VlcManager.CouldPlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public bool CouldPlay(VlcMediaPlayerInstance mediaPlayerInstance)
{
if (mediaPlayerInstance == IntPtr.Zero)
throw new ArgumentException("Media player instance is not initialized.");
return GetInteropDelegate<CouldPlay>().Invoke(mediaPlayerInstance) == 1;
return myLibraryLoader.GetInteropDelegate<CouldPlay>().Invoke(mediaPlayerInstance) == 1;
}
}
}
4 changes: 1 addition & 3 deletions src/Vlc.DotNet.Core.Interops/VlcManager.CreateMediaPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ public sealed partial class VlcManager
{
public VlcMediaPlayerInstance CreateMediaPlayer()
{
EnsureVlcInstance();

return new VlcMediaPlayerInstance(this, GetInteropDelegate<CreateMediaPlayer>().Invoke(myVlcInstance));
return new VlcMediaPlayerInstance(this, myLibraryLoader.GetInteropDelegate<CreateMediaPlayer>().Invoke(myVlcInstance));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public VlcMediaPlayerInstance CreateMediaPlayerFromMedia(VlcMediaInstance mediaI
{
if (mediaInstance == IntPtr.Zero)
throw new ArgumentException("Media instance is not initialized.");
return new VlcMediaPlayerInstance(this, GetInteropDelegate<CreateMediaPlayerFromMedia>().Invoke(mediaInstance));
return new VlcMediaPlayerInstance(this, myLibraryLoader.GetInteropDelegate<CreateMediaPlayerFromMedia>().Invoke(mediaInstance));
}
}
}
38 changes: 0 additions & 38 deletions src/Vlc.DotNet.Core.Interops/VlcManager.CreateNewInstance.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ public sealed partial class VlcManager
{
public VlcMediaInstance CreateNewMediaFromFileDescriptor(int fileDescriptor)
{
EnsureVlcInstance();

return VlcMediaInstance.New(this, GetInteropDelegate<CreateNewMediaFromFileDescriptor>().Invoke(myVlcInstance, fileDescriptor));
return VlcMediaInstance.New(this, myLibraryLoader.GetInteropDelegate<CreateNewMediaFromFileDescriptor>().Invoke(myVlcInstance, fileDescriptor));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ public sealed partial class VlcManager
{
public VlcMediaInstance CreateNewMediaFromLocation(string mrl)
{
EnsureVlcInstance();

using (var handle = Utf8InteropStringConverter.ToUtf8StringHandle(mrl))
{
return VlcMediaInstance.New(this, GetInteropDelegate<CreateNewMediaFromLocation>().Invoke(myVlcInstance, handle));
return VlcMediaInstance.New(this, myLibraryLoader.GetInteropDelegate<CreateNewMediaFromLocation>().Invoke(myVlcInstance, handle));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ public sealed partial class VlcManager
{
public VlcMediaInstance CreateNewMediaFromPath(string mrl)
{
EnsureVlcInstance();

using (var handle = Utf8InteropStringConverter.ToUtf8StringHandle(mrl))
{
return VlcMediaInstance.New(this, GetInteropDelegate<CreateNewMediaFromPath>().Invoke(myVlcInstance, handle));
return VlcMediaInstance.New(this, myLibraryLoader.GetInteropDelegate<CreateNewMediaFromPath>().Invoke(myVlcInstance, handle));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public VlcMediaInstance CreateNewMediaFromStream(Stream stream)
if (opaque == IntPtr.Zero)
return null;

var result = VlcMediaInstance.New(this, GetInteropDelegate<CreateNewMediaFromCallbacks>().Invoke(
var result = VlcMediaInstance.New(this, myLibraryLoader.GetInteropDelegate<CreateNewMediaFromCallbacks>().Invoke(
myVlcInstance,
CallbackOpenMediaDelegate,
CallbackReadMediaDelegate,
Expand Down
2 changes: 1 addition & 1 deletion src/Vlc.DotNet.Core.Interops/VlcManager.DetachEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public void DetachEvent(VlcEventManagerInstance eventManagerInstance, EventTypes
throw new ArgumentException("Event manager is not initialized.");
if (callback == null)
return;
GetInteropDelegate<DetachEvent>().Invoke(eventManagerInstance, eventType, callback, IntPtr.Zero);
myLibraryLoader.GetInteropDelegate<DetachEvent>().Invoke(eventManagerInstance, eventType, callback, IntPtr.Zero);
}
}
}
Loading

0 comments on commit fb42e9a

Please sign in to comment.