Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions src/LibVLCSharp/Shared/MediaPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,7 @@ public void SetVideoFormat(string chroma, uint width, uint height, uint pitch)
}

LibVLCVideoFormatCb? _videoFormatCb;
LibVLCVideoMultiPlaneFormatCb? _videoMultiPlaneFormatCb;
LibVLCVideoCleanupCb? _videoCleanupCb;
IntPtr _videoUserData = IntPtr.Zero;

Expand All @@ -1317,6 +1318,24 @@ public void SetVideoFormat(string chroma, uint width, uint height, uint pitch)
public void SetVideoFormatCallbacks(LibVLCVideoFormatCb formatCb, LibVLCVideoCleanupCb? cleanupCb)
{
_videoFormatCb = formatCb ?? throw new ArgumentNullException(nameof(formatCb));
_videoMultiPlaneFormatCb = null;
_videoCleanupCb = cleanupCb;
Native.LibVLCVideoSetFormatCallbacks(NativeReference, VideoFormatCallbackHandle,
(cleanupCb == null)? null : _videoCleanupCb);
}

/// <summary>
/// Set decoded video chroma and dimensions with multi-plane format access.
/// This variant exposes pitches and lines as IntPtr to allow direct
/// multi-plane access to the arrays provided by libVLC.
/// This only works in combination with libvlc_video_set_callbacks().
/// </summary>
/// <param name="formatCb">callback to select the video format (cannot be NULL)</param>
/// <param name="cleanupCb">callback to release any allocated resources (or NULL)</param>
public void SetVideoFormatCallbacksMultiPlane(LibVLCVideoMultiPlaneFormatCb formatCb, LibVLCVideoCleanupCb? cleanupCb)
{
_videoMultiPlaneFormatCb = formatCb ?? throw new ArgumentNullException(nameof(formatCb));
_videoFormatCb = null;
_videoCleanupCb = cleanupCb;
Native.LibVLCVideoSetFormatCallbacks(NativeReference, VideoFormatCallbackHandle,
(cleanupCb == null)? null : _videoCleanupCb);
Expand Down Expand Up @@ -1822,7 +1841,25 @@ private static void VideoDisplayCallback(IntPtr opaque, IntPtr picture)
private static uint VideoFormatCallback(ref IntPtr opaque, IntPtr chroma, ref uint width, ref uint height, ref uint pitches, ref uint lines)
{
var mediaPlayer = MarshalUtils.GetInstance<MediaPlayer>(opaque);
if (mediaPlayer?._videoFormatCb != null)
if (mediaPlayer == null)
return 0;

// Route to multi-plane callback
if (mediaPlayer._videoMultiPlaneFormatCb != null)
{
unsafe
{
fixed (uint* p = &pitches)
fixed (uint* l = &lines)
{
return mediaPlayer._videoMultiPlaneFormatCb(
ref mediaPlayer._videoUserData,chroma,ref width,ref height,(IntPtr)p,(IntPtr)l);
}
}
}

// Otherwise standard callback
if (mediaPlayer._videoFormatCb != null)
{
return mediaPlayer._videoFormatCb(ref mediaPlayer._videoUserData, chroma, ref width, ref height, ref pitches, ref lines);
}
Expand Down Expand Up @@ -2008,13 +2045,19 @@ private static void AudioCleanupCallback(IntPtr opaque)
public delegate uint LibVLCVideoFormatCb(ref IntPtr opaque, IntPtr chroma, ref uint width,
ref uint height, ref uint pitches, ref uint lines);

/// <summary>Callback prototype to configure picture buffers format (multi-plane variant).</summary>
/// <remarks>Passes pitches and lines as IntPtr to allow direct multi-plane data access via Marshal or unsafe pointer arithmetic.</remarks>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint LibVLCVideoMultiPlaneFormatCb(ref IntPtr opaque, IntPtr chroma, ref uint width,
ref uint height, IntPtr pitches, IntPtr lines);

/// <summary>Callback prototype to configure picture buffers format.</summary>
/// <param name="opaque">
/// <para>private pointer as passed to libvlc_video_set_callbacks()</para>
/// <para>(and possibly modified by</para>
/// </param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void LibVLCVideoCleanupCb(ref IntPtr opaque);
public delegate void LibVLCVideoCleanupCb(IntPtr opaque);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct fix, but API break

Copy link
Copy Markdown
Author

@ashish-066 ashish-066 Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct fix, but API break

You are right I made this change is that the native libVLC signature for the cleanup callback is void (*cleanup)(void *opaque). According to the libVLC headers, the parameter is passed as void *opaque, which maps to IntPtr in .NET rather than ref IntPtr.

https://code.videolan.org/videolan/vlc/-/blob/master/include/vlc/libvlc_media_player.h

Using ref IntPtr would correspond to void**, which doesn't match the native API.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will change it


/// <summary>Callback prototype to setup the audio playback.</summary>
/// <param name="opaque">
Expand Down