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

Commit

Permalink
Rewrite the Wpf control and version bump to 3.0.0 (#365)
Browse files Browse the repository at this point in the history
New Wpf component using InteropBitmap
Fixes #249
  • Loading branch information
jeremyVignelles authored Jan 20, 2018
1 parent eb6de92 commit 516cca3
Show file tree
Hide file tree
Showing 20 changed files with 591 additions and 116 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# 2.2.2 (prerelease)
# 3.0.0 (prerelease)
- BREAKING CHANGE : Created a new WPF control using ImageSource [#365](https://github.com/ZeBobo5/Vlc.DotNet/pull/365) see how to use in wiki!
- FIXED the type of the `NewTitle` property (int -> string) in `VlcMediaPlayerTitleChangedEventArgs` [#364](https://github.com/ZeBobo5/Vlc.DotNet/pull/364)
- FIXED Dispose() in WPF project [#358](https://github.com/ZeBobo5/Vlc.DotNet/pull/358)
- CHANGED name of internal handle class from `SafeUnmanagedMemoryHandle` to `Utf8StringHandle` [#337](https://github.com/ZeBobo5/Vlc.DotNet/pull/337)
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@ It can work on any .net framework version starting from .net 2.0 and .net standa

On the front-end side, two components are currently available to make it easy to integrate in your apps. One is for WinForms, the other for WPF.

Migrating WPF control from 2.x
----------

The WPF control has been rewritten from scratch from 2.x.

The old WPF control was just a wrapper around the WinForms control.
This led to some issues (Airspace issue...) and lacked some WPF-ish features.

That's why a new control has been written. To be fair, first versions of Vlc.DotNet
were built with that techique, but back then, there were issues in the .net framework
causing the memory usage to explode. As of 2018, this issue is resolved.

You have in fact two options:
- Use the new WPF control. You might notice a performance impact when reading, for example, a 4k @ 60 fps video on a low-end computer. However, you can do whatever you like, just as a normal ImageSource in WPF.
- Wrap the Vlc.DotNet.WinForms control in a WinFormHost . It offers better performance, but you will experience Airspace issues (see [#296](https://github.com/ZeBobo5/Vlc.DotNet/issues/296)) if you need to write over the video.

The right option to use depends on your needs.

See the discussion [#249](https://github.com/ZeBobo5/Vlc.DotNet/issue/249) and pull request : [#365](https://github.com/ZeBobo5/Vlc.DotNet/pull/365)


How to use
----------
It all starts with those three steps :
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
environment:
version_prefix: 2.2.2
version_prefix: 3.0.0
version: $(version_prefix)-{branch}{build}

image: Visual Studio 2017
Expand Down
30 changes: 17 additions & 13 deletions src/Samples/Vlc.DotNet.Wpf.Samples/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,33 @@ public MainWindow()
vlcLibDirectory = new DirectoryInfo(Path.Combine(currentDirectory, @"..\..\..\lib\x64\"));
}
}


protected override void OnClosing(CancelEventArgs e)
{
this.control?.Dispose();
base.OnClosing(e);
}

private void OnPlayButtonClick(object sender, RoutedEventArgs e)
{
this.control?.Dispose();
this.control = new VlcControl();
this.control.MediaPlayer.VlcLibDirectory = this.vlcLibDirectory;
this.control.MediaPlayer.EndInit();

this.ControlContainer.Content = this.control;
this.control.SourceProvider.CreatePlayer(this.vlcLibDirectory);

// This can also be called before EndInit
this.control.MediaPlayer.Log += (_, args) =>
this.control.SourceProvider.MediaPlayer.Log += (_, args) =>
{
string message = string.Format("libVlc : {0} {1} @ {2}", args.Level, args.Message, args.Module);
string message = $"libVlc : {args.Level} {args.Message} @ {args.Module}";
System.Diagnostics.Debug.WriteLine(message);
};
control.MediaPlayer.Play(new Uri("http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_surround-fix.avi"));

control.SourceProvider.MediaPlayer.Play(new Uri("http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_surround-fix.avi"));
//control.MediaPlayer.Play(new FileInfo(@"..\..\..\Vlc.DotNet\Samples\Videos\BBB trailer.mov"));
}

private void OnStopButtonClick(object sender, RoutedEventArgs e)
{
this.control?.MediaPlayer.Stop(); // This isn't strictly needed if Dispose() is called, but this is a demo...
this.control?.Dispose();
this.control = null;
}
Expand All @@ -63,7 +67,7 @@ private void OnForwardButtonClick(object sender, RoutedEventArgs e)
return;
}

this.control.MediaPlayer.Rate = 2;
this.control.SourceProvider.MediaPlayer.Rate = 2;
}

private void GetLength_Click(object sender, RoutedEventArgs e)
Expand All @@ -73,7 +77,7 @@ private void GetLength_Click(object sender, RoutedEventArgs e)
return;
}

GetLength.Content = this.control.MediaPlayer.Length + " ms";
GetLength.Content = this.control.SourceProvider.MediaPlayer.Length + " ms";
}

private void GetCurrentTime_Click(object sender, RoutedEventArgs e)
Expand All @@ -83,7 +87,7 @@ private void GetCurrentTime_Click(object sender, RoutedEventArgs e)
return;
}

GetCurrentTime.Content = this.control.MediaPlayer.Time + " ms";
GetCurrentTime.Content = this.control.SourceProvider.MediaPlayer.Time + " ms";
}

private void SetCurrentTime_Click(object sender, RoutedEventArgs e)
Expand All @@ -93,8 +97,8 @@ private void SetCurrentTime_Click(object sender, RoutedEventArgs e)
return;
}

this.control.MediaPlayer.Time = 5000;
SetCurrentTime.Content = this.control.MediaPlayer.Time + " ms";
this.control.SourceProvider.MediaPlayer.Time = 5000;
SetCurrentTime.Content = this.control.SourceProvider.MediaPlayer.Time + " ms";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ namespace Vlc.DotNet.Core.Interops.Signatures
/// </remarks>
[LibVlcFunction("libvlc_video_format_cb")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint VideoFormatCallback(ref IntPtr userData, IntPtr chroma, ref uint width, ref uint height, ref uint pitches, ref uint lines);
public delegate uint VideoFormatCallback(out IntPtr userData, IntPtr chroma, ref uint width, ref uint height, ref uint pitches, ref uint lines);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ namespace Vlc.DotNet.Core.Interops.Signatures
/// Private pointer as passed to <see cref="SetVideoCallbacks"/>.
/// </param>
/// <param name="planes">
/// start address of the pixel planes (LibVLC allocates the array
/// of void pointers, this callback must initialize the array)
/// The pointer to the array of pointers to the pixel planes (LibVLC allocates the array
/// of pointers, this callback must initialize the array)
/// If you only need the first plane, then you have to initialize the address pointed by planes
/// </param>
/// <remarks>
/// Whenever a new video frame needs to be decoded, the lock callback is
Expand All @@ -21,5 +22,5 @@ namespace Vlc.DotNet.Core.Interops.Signatures
/// </remarks>
[LibVlcFunction("libvlc_video_lock_cb")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr LockVideoCallback(IntPtr userData, ref IntPtr planes);
public delegate IntPtr LockVideoCallback(IntPtr userData, IntPtr planes);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ namespace Vlc.DotNet.Core.Interops.Signatures
/// <param name="planes">
/// pixel planes as defined by the <see cref="LockVideoCallback"/>
/// callback (this parameter is only for convenience)
///
/// Its size is always PICTURE_PLANE_MAX, which is 5 in my experience. Only the number of planes required by chroma are usable, which is 1 for RV32.
/// </param>
[LibVlcFunction("libvlc_video_lock_cb")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void UnlockVideoCallback(IntPtr userData, IntPtr picture, IntPtr planes);
public delegate void UnlockVideoCallback(IntPtr userData, IntPtr picture, [MarshalAs(UnmanagedType.LPArray, SizeConst = 5)]IntPtr[] planes);
}
24 changes: 24 additions & 0 deletions src/Vlc.DotNet.Core.Interops/VlcManager.SetVideoCallbacks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using Vlc.DotNet.Core.Interops.Signatures;

namespace Vlc.DotNet.Core.Interops
{
public sealed partial class VlcManager
{
private LockVideoCallback _lockVideoCallbackReference;
private UnlockVideoCallback _unlockVideoCallbackReference;
private DisplayVideoCallback _displayVideoCallbackReference;

public void SetVideoCallbacks(VlcMediaPlayerInstance mediaPlayerInstance, LockVideoCallback lockVideoCallback, UnlockVideoCallback unlockVideoCallback, DisplayVideoCallback displayVideoCallback, IntPtr userData)
{
if (mediaPlayerInstance == IntPtr.Zero)
throw new ArgumentException("Media player instance is not initialized.");

this._lockVideoCallbackReference = lockVideoCallback;
this._unlockVideoCallbackReference = unlockVideoCallback;
this._displayVideoCallbackReference = displayVideoCallback;

GetInteropDelegate<SetVideoCallbacks>().Invoke(mediaPlayerInstance, this._lockVideoCallbackReference, this._unlockVideoCallbackReference, this._displayVideoCallbackReference, userData);
}
}
}
22 changes: 22 additions & 0 deletions src/Vlc.DotNet.Core.Interops/VlcManager.SetVideoFormatCallbacks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using Vlc.DotNet.Core.Interops.Signatures;

namespace Vlc.DotNet.Core.Interops
{
public sealed partial class VlcManager
{
private VideoFormatCallback _videoFormatCallbackReference;
private CleanupVideoCallback _cleanupCallbackReference;

public void SetVideoFormatCallbacks(VlcMediaPlayerInstance mediaPlayerInstance, VideoFormatCallback videoFormatCallback, CleanupVideoCallback cleanupCallback)
{
if (mediaPlayerInstance == IntPtr.Zero)
throw new ArgumentException("Media player instance is not initialized.");

this._videoFormatCallbackReference = videoFormatCallback;
this._cleanupCallbackReference = cleanupCallback;

GetInteropDelegate<SetVideoFormatCallbacks>().Invoke(mediaPlayerInstance, this._videoFormatCallbackReference, this._cleanupCallbackReference);
}
}
}

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion src/Vlc.DotNet.Core.Interops/VlcMediaPlayerInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Vlc.DotNet.Core.Interops
{
public sealed partial class VlcMediaPlayerInstance : InteropObjectInstance
public sealed class VlcMediaPlayerInstance : InteropObjectInstance
{
private readonly VlcManager myManager;

Expand Down
45 changes: 44 additions & 1 deletion src/Vlc.DotNet.Core/VlcMediaPlayer/VlcMediaPlayer.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Vlc.DotNet.Core.Interops;
using Vlc.DotNet.Core.Interops.Signatures;
Expand Down Expand Up @@ -143,6 +142,50 @@ public void Play()
Manager.Play(myMediaPlayerInstance);
}

/// <summary>
/// Overload, provided for convenience that calls <see cref="SetMedia(System.IO.FileInfo,string[])"/> before <see cref="Play()"/>
/// </summary>
/// <param name="file">The file to play</param>
/// <param name="options">The options to be given</param>
public void Play(FileInfo file, params string[] options)
{
this.SetMedia(file, options);
this.Play();
}

/// <summary>
/// Overload, provided for convenience that calls <see cref="SetMedia(System.Uri,string[])"/> before <see cref="Play()"/>
/// </summary>
/// <param name="uri">The uri to play</param>
/// <param name="options">The options to be given</param>
public void Play(Uri uri, params string[] options)
{
this.SetMedia(uri, options);
this.Play();
}

/// <summary>
/// Overload, provided for convenience that calls <see cref="SetMedia(string,string[])"/> before <see cref="Play()"/>
/// </summary>
/// <param name="mrl">The mrl to play</param>
/// <param name="options">The options to be given</param>
public void Play(string mrl, params string[] options)
{
this.SetMedia(mrl, options);
this.Play();
}

/// <summary>
/// Overload, provided for convenience that calls <see cref="SetMedia(System.IO.Stream,string[])"/> before <see cref="Play()"/>
/// </summary>
/// <param name="stream">The stream to play</param>
/// <param name="options">The options to be given</param>
public void Play(Stream stream, params string[] options)
{
this.SetMedia(stream, options);
this.Play();
}

public void Pause()
{
Manager.Pause(myMediaPlayerInstance);
Expand Down
56 changes: 56 additions & 0 deletions src/Vlc.DotNet.Core/VlcMediaPlayer/VlcMediaPlayerVideo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Vlc.DotNet.Core.Interops.Signatures;

namespace Vlc.DotNet.Core
{
using System;

public sealed partial class VlcMediaPlayer
{
/// <summary>
/// Sets the video callbacks to render decoded video to a custom area in memory.
/// The media player will hold a reference on the IVideoCallbacks parameter
/// </summary>
/// <remarks>
/// Rendering video into custom memory buffers is considerably less efficient than rendering in a custom window as normal.
/// See libvlc_video_set_callbacks for detailed explanations
/// </remarks>
/// <param name="lockVideo">
/// Callback to lock video memory (must not be NULL)
/// </param>
/// <param name="unlockVideo">
/// Callback to unlock video memory (or NULL if not needed)
/// </param>
/// <param name="display">
/// Callback to display video (or NULL if not needed)
/// </param>
/// <param name="userData">
/// Private pointer for the three callbacks (as first parameter).
/// This parameter will be overriden if <see cref="SetVideoFormatCallbacks"/> is used
/// </param>
public void SetVideoCallbacks(LockVideoCallback lockVideo, UnlockVideoCallback unlockVideo, DisplayVideoCallback display, IntPtr userData)
{
if (lockVideo == null)
{
throw new ArgumentNullException(nameof(lockVideo));
}

this.Manager.SetVideoCallbacks(this.myMediaPlayerInstance, lockVideo, unlockVideo, display, userData);
}

/// <summary>
/// Set decoded video chroma and dimensions. This only works in combination with
/// <see cref="SetVideoCallbacks" />
/// </summary>
/// <param name="videoFormat">Callback to select the video format (cannot be NULL)</param>
/// <param name="cleanup">Callback to release any allocated resources (or NULL)</param>
public void SetVideoFormatCallbacks(VideoFormatCallback videoFormat, CleanupVideoCallback cleanup)
{
if (videoFormat == null)
{
throw new ArgumentNullException(nameof(videoFormat));
}

this.Manager.SetVideoFormatCallbacks(this.myMediaPlayerInstance, videoFormat, cleanup);
}
}
}
Loading

0 comments on commit 516cca3

Please sign in to comment.