Skip to content
Merged
Changes from all 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
41 changes: 40 additions & 1 deletion src/ui/Logic/VideoPlayers/LibMpvDynamic/LibMpvDynamicPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public sealed class LibMpvDynamicPlayer : IDisposable, IVideoPlayer
private IntPtr _mpv = IntPtr.Zero;
private IntPtr _renderContext = IntPtr.Zero;
private volatile bool _disposed;
private volatile bool _coreInitialized;
private string _fileName = string.Empty;
private double? _audioEndBound;

Expand Down Expand Up @@ -352,7 +353,13 @@ public int Initialize()
return -1;
}

return _mpvInitialize(_mpv);
var err = _mpvInitialize(_mpv);
if (err >= 0)
{
_coreInitialized = true;
}

return err;
}

private static byte[] GetUtf8Bytes(string s)
Expand Down Expand Up @@ -465,6 +472,10 @@ public void InitializeWithOpenGL(GetProcAddress getProcAddress)
{
Se.LogError(new InvalidOperationException(GetErrorString(err)), "LibMpvDynamicPlayer InitializeWithOpenGL mpv_initialize");
}
else
{
_coreInitialized = true;
}

// Create OpenGL init params
var initParams = new MpvOpenGlInitParams
Expand Down Expand Up @@ -554,6 +565,10 @@ public void InitializeWithMetal(IntPtr mtlDevice, IntPtr metalLayer)
{
Se.LogError(new InvalidOperationException(GetErrorString(err)), "LibMpvDynamicPlayer InitializeWithMetal mpv_initialize");
}
else
{
_coreInitialized = true;
}

// Build mpv_metal_init_params: device (required) + layer (optional).
// With a layer set, mpv handles nextDrawable / presentDrawable internally.
Expand Down Expand Up @@ -752,6 +767,24 @@ private void EnsureNotDisposed()

public string FileName => _fileName;

/// <summary>
/// The mpv core is initialized lazily by the rendering surface - e.g. the OpenGL
/// control calls InitializeWithOpenGL on its first render pass. Windows that open a
/// video right away (the burn-in and visual sync previews load the file from their
/// Loaded event) can therefore issue "loadfile" before mpv_initialize has run; the
/// command then fails with MPV_ERROR_UNINITIALIZED and is never retried, leaving the
/// preview black until some other action reloads the file (issue #12205). Wait
/// (bounded) for the core to come up before sending commands.
/// </summary>
private async Task WaitForCoreInitializedAsync(int timeoutMs = 5000)
{
var end = DateTime.UtcNow.AddMilliseconds(timeoutMs);
while (!_coreInitialized && !_disposed && DateTime.UtcNow < end)
{
await Task.Delay(25);
}
}

public async Task LoadFile(string path)
{
EnsureNotDisposed();
Expand All @@ -768,6 +801,8 @@ public async Task LoadFile(string path)
SetOptionString("end", "none");
_audioEndBound = null;

await WaitForCoreInitializedAsync();

var err = await Task.Run(() => DoMpvCommand("loadfile", path));
if (_disposed)
{
Expand Down Expand Up @@ -880,6 +915,8 @@ public async Task LoadAudio(string path)
{
EnsureNotDisposed();

await WaitForCoreInitializedAsync();

var err = await Task.Run(() => DoMpvCommand("loadfile", path));
if (_disposed)
{
Expand Down Expand Up @@ -1485,6 +1522,8 @@ public void InitializeWithSoftwareRendering()
throw new InvalidOperationException(GetErrorString(err));
}

_coreInitialized = true;

// Build render context params for software rendering
var apiTypeBytes = Encoding.UTF8.GetBytes(MPV_RENDER_API_TYPE_SW + "\0");
var apiTypePtr = Marshal.AllocHGlobal(apiTypeBytes.Length);
Expand Down