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); } } }