Files
video-v1/vav1/Vav1Player/Decoder/Dav1dDecoder.cs
2025-09-19 04:42:07 +09:00

145 lines
5.3 KiB
C#

using System;
using System.Runtime.InteropServices;
using Vav1Player.Native;
namespace Vav1Player.Decoder
{
public class Dav1dDecoder : IDisposable
{
private IntPtr _context = IntPtr.Zero;
private bool _disposed = false;
public bool Initialize(int threads = 0)
{
if (_context != IntPtr.Zero)
return false;
try
{
// Initialize all fields to zero before calling dav1d_default_settings
var settings = new Dav1dSettings();
// Now call dav1d_default_settings to populate with library defaults
Dav1dNative.dav1d_default_settings(ref settings);
// Override with user-specified values
if (threads > 0)
{
settings.n_threads = Math.Min(threads, 256); // Cap at library maximum
}
else if (settings.n_threads <= 0)
{
settings.n_threads = Math.Min(Environment.ProcessorCount, 8); // Conservative default
}
settings.max_frame_delay = 1; // Low latency
System.Diagnostics.Debug.WriteLine($"Final settings - Threads: {settings.n_threads}, Frame Delay: {settings.max_frame_delay}");
System.Diagnostics.Debug.WriteLine(Dav1dSettingsValidator.GetSettingsSummary(settings));
int result = Dav1dNative.dav1d_open(out _context, ref settings);
if (result != 0)
{
string errorMsg = $"dav1d_open failed with error code: {result}\n" +
$"Description: {Dav1dErrorCodes.GetOpenErrorDescription(result)}\n" +
$"Suggestion: {Dav1dErrorCodes.GetTroubleshootingSuggestion(result)}\n" +
$"Recoverable: {Dav1dErrorCodes.IsRecoverable(result)}";
System.Diagnostics.Debug.WriteLine(errorMsg);
Console.WriteLine($"[DAV1D ERROR] {errorMsg}");
return false;
}
return true;
}
catch (DllNotFoundException ex)
{
System.Diagnostics.Debug.WriteLine($"dav1d.dll not found: {ex.Message}");
return false;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Decoder initialization failed: {ex.Message}");
return false;
}
}
public unsafe bool DecodeFrame(byte[] data, out DecodedFrame? frame)
{
frame = null;
if (_context == IntPtr.Zero || data == null || data.Length == 0)
return false;
fixed (byte* dataPtr = data)
{
var dav1dData = new Dav1dData();
int result = Dav1dNative.dav1d_data_wrap(out dav1dData, (IntPtr)dataPtr, (nuint)data.Length, IntPtr.Zero, IntPtr.Zero);
if (result != 0)
return false;
result = Dav1dNative.dav1d_send_data(_context, ref dav1dData);
if (result != 0 && result != -11) // -11 is EAGAIN (need more data)
{
string errorMsg = $"[Dav1dDecoder] dav1d_send_data failed with error: {result}";
errorMsg += $"\n[Dav1dDecoder] Data size: {data.Length} bytes";
var hexData = string.Join(" ", data.Take(Math.Min(32, data.Length)).Select(b => b.ToString("X2")));
errorMsg += $"\n[Dav1dDecoder] Data prefix: {hexData}";
errorMsg += $"\n[Dav1dDecoder] Error details: {Dav1dErrorCodes.GetErrorDescription(result)}";
System.Diagnostics.Debug.WriteLine(errorMsg);
Console.WriteLine(errorMsg); // Also print to console so it shows in test output
Dav1dNative.dav1d_data_unref(ref dav1dData);
return false;
}
var picture = new Dav1dPicture();
result = Dav1dNative.dav1d_get_picture(_context, out picture);
if (result == 0)
{
frame = new DecodedFrame
{
Picture = picture,
Width = picture.p.w,
Height = picture.p.h,
PixelLayout = picture.p.layout,
BitDepth = picture.p.bpc
};
return true;
}
else
{
System.Diagnostics.Debug.WriteLine($"[Dav1dDecoder] dav1d_get_picture failed with error: {result}");
}
}
return false;
}
public void Dispose()
{
if (!_disposed && _context != IntPtr.Zero)
{
Dav1dNative.dav1d_close(ref _context);
_context = IntPtr.Zero;
_disposed = true;
}
}
}
public struct DecodedFrame
{
public Dav1dPicture Picture;
public int Width;
public int Height;
public Dav1dPixelLayout PixelLayout;
public int BitDepth;
public void Release()
{
Dav1dNative.dav1d_picture_unref(ref Picture);
}
}
}