From 5a3df5f3593cd67f5888ff2a18d7b9ebd96d395a Mon Sep 17 00:00:00 2001 From: SuRGeoNix Date: Mon, 8 Sep 2025 00:13:36 +0300 Subject: [PATCH 1/2] - VideoDecoder: Adds D3D11/AV1 FFmpeg bug workaround - Engine: Fixes possible issues with ObservableCollections and EnableCollectionSynchronization - Samples.FlyleafPlayer: Fixes a UI freeze issue with ctrl+w for closing # Conflicts: # FlyleafLib/MediaFramework/MediaDemuxer/Demuxer.cs # Samples/FlyleafPlayer (WPF Control) (WPF)/MainWindow.xaml.cs --- FlyleafLib/Engine/Engine.Audio.cs | 19 ++-- FlyleafLib/Engine/Engine.Video.cs | 17 ++-- FlyleafLib/Engine/Engine.cs | 8 +- .../MediaDecoder/VideoDecoder.cs | 21 ++-- .../MediaFramework/MediaDemuxer/Demuxer.cs | 98 +++++++++---------- 5 files changed, 79 insertions(+), 84 deletions(-) diff --git a/FlyleafLib/Engine/Engine.Audio.cs b/FlyleafLib/Engine/Engine.Audio.cs index d4f5cbb..bdfee40 100644 --- a/FlyleafLib/Engine/Engine.Audio.cs +++ b/FlyleafLib/Engine/Engine.Audio.cs @@ -44,7 +44,7 @@ public ObservableCollection public event PropertyChangedEventHandler PropertyChanged; - public AudioEngine() + public AudioEngine() // We consider from UI here { if (Engine.Config.DisableAudio) { @@ -59,14 +59,17 @@ public AudioEngine() public void RefreshCapDevices() { - lock (lockCapDevices) + UI(() => { - Engine.Audio.CapDevices.Clear(); + lock (lockCapDevices) + { + Engine.Audio.CapDevices.Clear(); - var devices = MediaFactory.MFEnumAudioDeviceSources(); - foreach (var device in devices) - try { Engine.Audio.CapDevices.Add(new AudioDevice(device.FriendlyName, device.SymbolicLink)); } catch(Exception) { } - } + var devices = MediaFactory.MFEnumAudioDeviceSources(); + foreach (var device in devices) + try { Engine.Audio.CapDevices.Add(new(device.FriendlyName, device.SymbolicLink)); } catch(Exception) { } + } + }); } private void EnumerateDevices() @@ -166,7 +169,7 @@ private void RefreshDevices() { CurrentDevice.Id = defaultDevice.Id; CurrentDevice.Name = defaultDevice.FriendlyName; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentDevice))); + PropertyChanged?.Invoke(this, new(nameof(CurrentDevice))); } // Fall back to DefaultDevice *Non-UI thread otherwise will freeze (not sure where and why) during xaudio.Dispose() diff --git a/FlyleafLib/Engine/Engine.Video.cs b/FlyleafLib/Engine/Engine.Video.cs index de6bcc5..4a73d99 100644 --- a/FlyleafLib/Engine/Engine.Video.cs +++ b/FlyleafLib/Engine/Engine.Video.cs @@ -31,7 +31,7 @@ public Dictionary private readonly object lockCapDevices = new(); - internal VideoEngine() + internal VideoEngine() // We consider from UI here { if (DXGI.CreateDXGIFactory1(out Factory).Failure) throw new InvalidOperationException("Cannot create IDXGIFactory1"); @@ -42,14 +42,17 @@ internal VideoEngine() public void RefreshCapDevices() { - lock (lockCapDevices) + UI(() => { - Engine.Video.CapDevices.Clear(); + lock (lockCapDevices) + { + Engine.Video.CapDevices.Clear(); - var devices = MediaFactory.MFEnumVideoDeviceSources(); - foreach (var device in devices) - try { Engine.Video.CapDevices.Add(new VideoDevice(device.FriendlyName, device.SymbolicLink)); } catch(Exception) { } - } + var devices = MediaFactory.MFEnumVideoDeviceSources(); + foreach (var device in devices) + try { Engine.Video.CapDevices.Add(new(device.FriendlyName, device.SymbolicLink)); } catch(Exception) { } + } + }); } private Dictionary GetAdapters() diff --git a/FlyleafLib/Engine/Engine.cs b/FlyleafLib/Engine/Engine.cs index 25f4848..368d5af 100644 --- a/FlyleafLib/Engine/Engine.cs +++ b/FlyleafLib/Engine/Engine.cs @@ -42,7 +42,7 @@ public static class Engine /// /// List of active Players /// - public static List Players { get; private set; } + public static List Players { get; private set; } = []; public static event EventHandler Loaded; @@ -138,6 +138,7 @@ private static void StartInternalUI() SetOutput(); Log = new("[FlyleafEngine] "); Audio = new(); + Video = new(); } private static void StartInternalNonUI() @@ -146,13 +147,10 @@ private static void StartInternalNonUI() Log.Info($"FlyleafLib {version.Major }.{version.Minor}.{version.Build}"); FFmpeg = new(); - Video = new(); Plugins = new(); - Players = []; - IsLoaded= true; - if (Config.FFmpegLoadProfile == LoadProfile.All) // Cap Devices (TBR: if UI required) + if (Config.FFmpegLoadProfile == LoadProfile.All) { Audio.RefreshCapDevices(); Video.RefreshCapDevices(); diff --git a/FlyleafLib/MediaFramework/MediaDecoder/VideoDecoder.cs b/FlyleafLib/MediaFramework/MediaDecoder/VideoDecoder.cs index 3e3c5f2..d4ead94 100644 --- a/FlyleafLib/MediaFramework/MediaDecoder/VideoDecoder.cs +++ b/FlyleafLib/MediaFramework/MediaDecoder/VideoDecoder.cs @@ -18,7 +18,6 @@ public ConcurrentQueue Frames { get; protected set; } = []; public Renderer Renderer { get; private set; } public bool VideoAccelerated { get; internal set; } - public bool ZeroCopy { get; internal set; } public VideoStream VideoStream => (VideoStream) Stream; @@ -293,16 +292,12 @@ protected override int Setup(AVCodec* codec) else Log.Debug("VA Disabled"); - // Can't get data from here? - //var t1 = av_stream_get_side_data(VideoStream.AVStream, AVPacketSideDataType.AV_PKT_DATA_MASTERING_DISPLAY_METADATA, null); - //var t2 = av_stream_get_side_data(VideoStream.AVStream, AVPacketSideDataType.AV_PKT_DATA_CONTENT_LIGHT_LEVEL, null); - - // TBR: during swFallback (keyFrameRequiredPacket should not reset, currenlty saved in SWFallback) keyPacketRequired = false; // allow no key packet after open (lot of videos missing this) - ZeroCopy = false; filledFromCodec = false; - - lastFixedPts = 0; // TBR: might need to set this to first known pts/dts + lastFixedPts = 0; // TBR: might need to set this to first known pts/dts + startPts = VideoStream.StartTimePts; + codecCtx->apply_cropping + = 0; if (VideoAccelerated) { @@ -310,9 +305,10 @@ protected override int Setup(AVCodec* codec) codecCtx->hwaccel_flags |= HWAccelFlags.IgnoreLevel; if (Config.Decoder.AllowProfileMismatch) codecCtx->hwaccel_flags|= HWAccelFlags.AllowProfileMismatch; - codecCtx->get_format = getHWformat; - codecCtx->extra_hw_frames = Config.Decoder.MaxVideoFrames; + + // D3D11/AV1 Issue: https://trac.ffmpeg.org/ticket/10608 "Static surface pool size exceeded" | Workaround: we use +X margin for the initial_pool_size (not for us/extra frames) + codecCtx->extra_hw_frames = codecCtx->codec_id == AVCodecID.Av1 ? Config.Decoder.MaxVideoFrames + 3 : Config.Decoder.MaxVideoFrames; } else codecCtx->thread_count = Math.Min(Config.Decoder.VideoThreads, codecCtx->codec_id == AVCodecID.Hevc ? 32 : 16); @@ -320,9 +316,6 @@ protected override int Setup(AVCodec* codec) if (codecCtx->codec_descriptor != null) isIntraOnly = codecCtx->codec_descriptor->props.HasFlag(CodecPropFlags.IntraOnly); - startPts = VideoStream.StartTimePts; - CodecCtx->apply_cropping = 0; - return 0; } internal bool SetupSws() diff --git a/FlyleafLib/MediaFramework/MediaDemuxer/Demuxer.cs b/FlyleafLib/MediaFramework/MediaDemuxer/Demuxer.cs index d35cce7..0faa9c2 100644 --- a/FlyleafLib/MediaFramework/MediaDemuxer/Demuxer.cs +++ b/FlyleafLib/MediaFramework/MediaDemuxer/Demuxer.cs @@ -622,7 +622,8 @@ public string Open(string url, Stream stream) if (fmtCtx->pb != null) fmtCtx->pb->eof_reached = 0; - FillInfo(); + lock (lockStreams) + FillInfo(); if (Type == MediaType.Video && VideoStreams.Count == 0 && AudioStreams.Count == 0) return error = $"No audio / video stream found"; @@ -779,66 +780,63 @@ private void FillInfo() bool subsHasEng = false; AVStreamToStream= []; - lock (lockStreams) + for (int i = 0; i < fmtCtx->nb_streams; i++) { - for (int i = 0; i < fmtCtx->nb_streams; i++) + var stream = fmtCtx->streams[i]; + stream->discard = AVDiscard.All; + if (stream->codecpar->codec_id == AVCodecID.None) { - var stream = fmtCtx->streams[i]; - stream->discard = AVDiscard.All; - if (stream->codecpar->codec_id == AVCodecID.None) - { - AVStreamToStream.Add(stream->index, new MiscStream(this, stream)); - Log.Info($"#[Invalid #{i}] No codec"); - continue; - } + AVStreamToStream.Add(stream->index, new MiscStream(this, stream)); + Log.Info($"#[Invalid #{i}] No codec"); + continue; + } - switch (stream->codecpar->codec_type) - { - case AVMediaType.Audio: - AudioStreams.Add(new(this, stream)); - AVStreamToStream.Add(stream->index, AudioStreams[^1]); - audioHasEng = AudioStreams[^1].Language == Language.English; + switch (stream->codecpar->codec_type) + { + case AVMediaType.Audio: + AudioStreams.Add(new(this, stream)); + AVStreamToStream.Add(stream->index, AudioStreams[^1]); + audioHasEng = AudioStreams[^1].Language == Language.English; - break; + break; - case AVMediaType.Video: - if ((stream->disposition & DispositionFlags.AttachedPic) != 0) - { - AVStreamToStream.Add(stream->index, new MiscStream(this, stream)); - Log.Info($"Excluding image stream #{i}"); - } + case AVMediaType.Video: + if ((stream->disposition & DispositionFlags.AttachedPic) != 0) + { + AVStreamToStream.Add(stream->index, new MiscStream(this, stream)); + Log.Info($"Excluding image stream #{i}"); + } - // TBR: When AllowFindStreamInfo = false we can get valid pixel format during decoding (in case of remuxing only this might crash, possible check if usedecoders?) - else if (((AVPixelFormat)stream->codecpar->format) == AVPixelFormat.None && Config.AllowFindStreamInfo) - { - AVStreamToStream.Add(stream->index, new MiscStream(this, stream)); - Log.Info($"Excluding invalid video stream #{i}"); - } - else - { - VideoStreams.Add(new(this, stream)); - AVStreamToStream.Add(stream->index, VideoStreams[^1]); - } + // TBR: When AllowFindStreamInfo = false we can get valid pixel format during decoding (in case of remuxing only this might crash, possible check if usedecoders?) + else if (((AVPixelFormat)stream->codecpar->format) == AVPixelFormat.None && Config.AllowFindStreamInfo) + { + AVStreamToStream.Add(stream->index, new MiscStream(this, stream)); + Log.Info($"Excluding invalid video stream #{i}"); + } + else + { + VideoStreams.Add(new(this, stream)); + AVStreamToStream.Add(stream->index, VideoStreams[^1]); + } - break; + break; - case AVMediaType.Subtitle: - SubtitlesStreamsAll.Add(new(this, fmtCtx->streams[i])); - AVStreamToStream.Add(stream->index, SubtitlesStreamsAll[^1]); - subsHasEng = SubtitlesStreamsAll[^1].Language == Language.English; - break; + case AVMediaType.Subtitle: + SubtitlesStreamsAll.Add(new(this, fmtCtx->streams[i])); + AVStreamToStream.Add(stream->index, SubtitlesStreamsAll[^1]); + subsHasEng = SubtitlesStreamsAll[^1].Language == Language.English; + break; - case AVMediaType.Data: - DataStreams.Add(new(this, stream)); - AVStreamToStream.Add(stream->index, DataStreams[^1]); + case AVMediaType.Data: + DataStreams.Add(new(this, stream)); + AVStreamToStream.Add(stream->index, DataStreams[^1]); - break; + break; - default: - AVStreamToStream.Add(stream->index, new MiscStream(this, stream)); - Log.Info($"#[Unknown #{i}] {stream->codecpar->codec_type}"); - break; - } + default: + AVStreamToStream.Add(stream->index, new MiscStream(this, stream)); + Log.Info($"#[Unknown #{i}] {stream->codecpar->codec_type}"); + break; } } From 9b8552e53fcf8b1503c314c9834b50fd0a139e42 Mon Sep 17 00:00:00 2001 From: SuRGeoNix Date: Sun, 14 Sep 2025 16:38:15 +0300 Subject: [PATCH 2/2] Updates to v3.8.11 (FlyleafLib) / v1.4.11 (FlyleafME) / v1.1.12 (FlyleafHost.WinUI) - Engine: Fixes possible issues with ObservableCollections and EnableCollectionSynchronization - FlyleafHost.Wpf: Fixes an issue with KeepRatioOnResize (after crop DAR) - VideoDecoder: Adds D3D11/AV1 FFmpeg bug workaround - Samples.FlyleafPlayer: Fixes a lock issue with SlideShowGoTo - Samples.FlyleafPlayer: Fixes a UI freeze issue with ctrl+w for closing [Breaking Changes] - Globals.AspectRatio: Changes floats to doubles # Conflicts: # FlyleafLib.Controls.WPF/FlyleafLib.Controls.WPF.csproj # FlyleafLib.Controls.WinUI/FlyleafLib.Controls.WinUI.csproj # FlyleafLib/FlyleafLib.csproj # FlyleafLib/MediaFramework/MediaRenderer/Renderer.VideoProcessor.cs # Samples/FlyleafPlayer (WPF Control) (WPF)/MainWindow.xaml.cs --- FlyleafLib/Controls/WPF/FlyleafHost.cs | 24 ++++++++------ FlyleafLib/Engine/Engine.Audio.cs | 15 ++++----- FlyleafLib/Engine/Engine.Video.cs | 15 ++++----- FlyleafLib/Engine/Engine.cs | 31 ++++++++++--------- FlyleafLib/Engine/Globals.cs | 16 +++++----- FlyleafLib/FlyleafLib.csproj | 2 +- .../MediaDecoder/DecoderBase.cs | 2 +- .../MediaDecoder/VideoDecoder.cs | 5 +-- .../MediaRenderer/Renderer.Device.cs | 3 +- .../MediaRenderer/Renderer.VideoProcessor.cs | 6 +++- FlyleafLib/MediaPlayer/Video.cs | 24 +++++++++++--- 11 files changed, 83 insertions(+), 60 deletions(-) diff --git a/FlyleafLib/Controls/WPF/FlyleafHost.cs b/FlyleafLib/Controls/WPF/FlyleafHost.cs index 7bce2da..d049368 100644 --- a/FlyleafLib/Controls/WPF/FlyleafHost.cs +++ b/FlyleafLib/Controls/WPF/FlyleafHost.cs @@ -132,8 +132,8 @@ IsStandAlone [ReadOnly] static int idGenerator = 1; static nint NONE_STYLE = (nint) (WindowStyles.WS_MINIMIZEBOX | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN | WindowStyles.WS_VISIBLE); // WS_MINIMIZEBOX required for swapchain - float curResizeRatio; - float curResizeRatioIfEnabled; + double curResizeRatio; + double curResizeRatioIfEnabled; bool surfaceClosed, surfaceClosing, overlayClosed; int panPrevX, panPrevY; bool isMouseBindingsSubscribedSurface; @@ -599,17 +599,21 @@ private static void DropChanged(DependencyObject d, DependencyPropertyChangedEve } private void UpdateCurRatio() { + /* TODO (Keep ratio on resize) + * 1) Should 'monitor' renderer's curRatio (includes rotation logic and user cropping) + * 2) Should resize based on Viewport's logic (rounding/ceiling) to avoid 1 pixel more or less for width/height + * 3) Consider default ratio (16:9) as config? + */ + if (!KeepRatioOnResize || IsFullScreen) return; - if (Player != null && Player.Video.AspectRatio.Value > 0) - curResizeRatio = Player.Video.AspectRatio.Value; - else if (ReplicaPlayer != null && ReplicaPlayer.Video.AspectRatio.Value > 0) - curResizeRatio = ReplicaPlayer.Video.AspectRatio.Value; - else - curResizeRatio = (float)(16.0/9.0); + var player = Player ?? ReplicaPlayer; - curResizeRatioIfEnabled = curResizeRatio; + if (player != null && player.renderer != null && player.renderer.DAR.Value > 0) + curResizeRatioIfEnabled = curResizeRatio = player.renderer.DAR.Value; + else + curResizeRatioIfEnabled = curResizeRatio = 16.0 / 9.0; Rect screen; @@ -1112,7 +1116,7 @@ private void Host_LayoutUpdated(object sender, EventArgs e) } private void Player_Video_PropertyChanged(object sender, PropertyChangedEventArgs e) { - if (KeepRatioOnResize && e.PropertyName == nameof(Player.Video.AspectRatio) && Player.Video.AspectRatio.Value > 0) + if (KeepRatioOnResize && e.PropertyName == nameof(Player.Video.AspectRatio)) UpdateCurRatio(); } private void ReplicaPlayer_Video_PropertyChanged(object sender, PropertyChangedEventArgs e) diff --git a/FlyleafLib/Engine/Engine.Audio.cs b/FlyleafLib/Engine/Engine.Audio.cs index bdfee40..72237ca 100644 --- a/FlyleafLib/Engine/Engine.Audio.cs +++ b/FlyleafLib/Engine/Engine.Audio.cs @@ -59,17 +59,14 @@ public AudioEngine() // We consider from UI here public void RefreshCapDevices() { - UI(() => + lock (lockCapDevices) { - lock (lockCapDevices) - { - Engine.Audio.CapDevices.Clear(); + Engine.Audio.CapDevices.Clear(); - var devices = MediaFactory.MFEnumAudioDeviceSources(); - foreach (var device in devices) - try { Engine.Audio.CapDevices.Add(new(device.FriendlyName, device.SymbolicLink)); } catch(Exception) { } - } - }); + var devices = MediaFactory.MFEnumAudioDeviceSources(); + foreach (var device in devices) + try { Engine.Audio.CapDevices.Add(new(device.FriendlyName, device.SymbolicLink)); } catch(Exception) { } + } } private void EnumerateDevices() diff --git a/FlyleafLib/Engine/Engine.Video.cs b/FlyleafLib/Engine/Engine.Video.cs index 4a73d99..e9803d6 100644 --- a/FlyleafLib/Engine/Engine.Video.cs +++ b/FlyleafLib/Engine/Engine.Video.cs @@ -42,17 +42,14 @@ internal VideoEngine() // We consider from UI here public void RefreshCapDevices() { - UI(() => + lock (lockCapDevices) { - lock (lockCapDevices) - { - Engine.Video.CapDevices.Clear(); + Engine.Video.CapDevices.Clear(); - var devices = MediaFactory.MFEnumVideoDeviceSources(); - foreach (var device in devices) - try { Engine.Video.CapDevices.Add(new(device.FriendlyName, device.SymbolicLink)); } catch(Exception) { } - } - }); + var devices = MediaFactory.MFEnumVideoDeviceSources(); + foreach (var device in devices) + try { Engine.Video.CapDevices.Add(new(device.FriendlyName, device.SymbolicLink)); } catch(Exception) { } + } } private Dictionary GetAdapters() diff --git a/FlyleafLib/Engine/Engine.cs b/FlyleafLib/Engine/Engine.cs index 368d5af..df9b959 100644 --- a/FlyleafLib/Engine/Engine.cs +++ b/FlyleafLib/Engine/Engine.cs @@ -103,25 +103,28 @@ public static void TimeEndPeriod1() private static void StartInternal(EngineConfig config = null, bool async = false) { - lock (lockEngine) - { - if (isLoading) - return; + if (Application.Current == null) + _ = new Application(); - isLoading = true; + UIInvokeIfRequired(() => + { + lock (lockEngine) + { + if (isLoading) + return; - Config = config ?? new EngineConfig(); + isLoading = true; - if (Application.Current == null) - _ = new Application(); + Config = config ?? new EngineConfig(); - StartInternalUI(); + StartInternalUI(); - if (async) - Task.Run(() => StartInternalNonUI()); - else - StartInternalNonUI(); - } + if (async) + Task.Run(() => StartInternalNonUI()); + else + StartInternalNonUI(); + } + }); } private static void StartInternalUI() diff --git a/FlyleafLib/Engine/Globals.cs b/FlyleafLib/Engine/Globals.cs index d1de5b8..4aa2703 100644 --- a/FlyleafLib/Engine/Globals.cs +++ b/FlyleafLib/Engine/Globals.cs @@ -196,10 +196,10 @@ public struct AspectRatio : IEquatable public static implicit operator AspectRatio(string value) => new(value); - public float Num { get; set; } - public float Den { get; set; } + public double Num { get; set; } + public double Den { get; set; } - public float Value + public double Value { readonly get => Num / Den; set { Num = value; Den = 1; } @@ -211,8 +211,8 @@ public string ValueStr set => FromString(value); } - public AspectRatio(float value) : this(value, 1) { } - public AspectRatio(float num, float den) { Num = num; Den = den; } + public AspectRatio(double value) : this(value, 1) { } + public AspectRatio(double num, double den) { Num = num; Den = den; } public AspectRatio(string value) { Num = Invalid.Num; Den = Invalid.Den; FromString(value); } public readonly bool Equals(AspectRatio other) => Num == other.Num && Den == other.Den; @@ -240,11 +240,11 @@ public void FromString(string value) if (values.Length < 2) values = newvalue.ToString().Split('/'); - Num = float.Parse(values[0], NumberStyles.Any, CultureInfo.InvariantCulture); - Den = float.Parse(values[1], NumberStyles.Any, CultureInfo.InvariantCulture); + Num = double.Parse(values[0], NumberStyles.Any, CultureInfo.InvariantCulture); + Den = double.Parse(values[1], NumberStyles.Any, CultureInfo.InvariantCulture); } - else if (float.TryParse(newvalue.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out float result)) + else if (double.TryParse(newvalue.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out double result)) { Num = result; Den = 1; } else diff --git a/FlyleafLib/FlyleafLib.csproj b/FlyleafLib/FlyleafLib.csproj index 7f0991a..5e68d05 100644 --- a/FlyleafLib/FlyleafLib.csproj +++ b/FlyleafLib/FlyleafLib.csproj @@ -7,7 +7,7 @@ true Media Player .NET Library for WinUI 3/WPF/WinForms (based on FFmpeg/DirectX) - 3.8.10 + 3.8.11 SuRGeoNix SuRGeoNix © 2025 GPL-3.0-or-later diff --git a/FlyleafLib/MediaFramework/MediaDecoder/DecoderBase.cs b/FlyleafLib/MediaFramework/MediaDecoder/DecoderBase.cs index f084825..862be81 100644 --- a/FlyleafLib/MediaFramework/MediaDecoder/DecoderBase.cs +++ b/FlyleafLib/MediaFramework/MediaDecoder/DecoderBase.cs @@ -146,7 +146,7 @@ protected string Open2(StreamBase stream, StreamBase prevStream, bool openStream stream.Demuxer.EnableStream(stream); Status = Status.Stopped; - CodecChanged?.Invoke(this); + //CodecChanged?.Invoke(this); // We call this when we successfully decode one frame } return null; diff --git a/FlyleafLib/MediaFramework/MediaDecoder/VideoDecoder.cs b/FlyleafLib/MediaFramework/MediaDecoder/VideoDecoder.cs index d4ead94..d75d2f8 100644 --- a/FlyleafLib/MediaFramework/MediaDecoder/VideoDecoder.cs +++ b/FlyleafLib/MediaFramework/MediaDecoder/VideoDecoder.cs @@ -614,15 +614,16 @@ internal int FillFromCodec(AVFrame* frame) codecChanged = false; startPts = VideoStream.StartTimePts; skipSpeedFrames = speed * VideoStream.FPS / Config.Video.MaxOutputFps; - CodecChanged?.Invoke(this); - + DisposeFrame(Renderer.LastFrame); if (VideoStream.PixelFormat == AVPixelFormat.None || !Renderer.ConfigPlanes(frame)) { Log.Error("[Pixel Format] Unknown"); + CodecChanged?.Invoke(this); return -1234; } + CodecChanged?.Invoke(this); return ret; } } diff --git a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.Device.cs b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.Device.cs index 71c0dc4..897e7b8 100644 --- a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.Device.cs +++ b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.Device.cs @@ -400,7 +400,8 @@ public void Dispose() ReportLiveObjects(); #endif - curRatio = 1.0f; + DAR = new(0, 1); + curRatio = 1.0f; if (CanInfo) Log.Info("Disposed"); } } diff --git a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.VideoProcessor.cs b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.VideoProcessor.cs index c2698ef..4ad3d17 100644 --- a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.VideoProcessor.cs +++ b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.VideoProcessor.cs @@ -463,7 +463,11 @@ internal void UpdateCropping(bool refresh = true) VisibleWidth = textWidth - (cropRect.Left + cropRect.Right); VisibleHeight = textHeight - (cropRect.Top + cropRect.Bottom); - keepRatio = (double)(VisibleWidth * VideoStream.SAR.Num) / (VisibleHeight * VideoStream.SAR.Den); + + int x, y; + _ = av_reduce(&x, &y, VisibleWidth * VideoStream.SAR.Num, VisibleHeight * VideoStream.SAR.Den, 1024 * 1024); + DAR = new(x, y); + keepRatio = DAR.Value; if (Config.Video.AspectRatio == AspectRatio.Keep) curRatio = actualRotation == 90 || actualRotation == 270 ? 1 / keepRatio : keepRatio; diff --git a/FlyleafLib/MediaPlayer/Video.cs b/FlyleafLib/MediaPlayer/Video.cs index f115555..9b3b52b 100644 --- a/FlyleafLib/MediaPlayer/Video.cs +++ b/FlyleafLib/MediaPlayer/Video.cs @@ -29,7 +29,7 @@ public ObservableCollection public double BitRate { get => bitRate; internal set => Set(ref _BitRate, value); } internal double _BitRate, bitRate; - public AspectRatio AspectRatio { get => aspectRatio; internal set => Set(ref _AspectRatio, value); } + public AspectRatio AspectRatio { get => aspectRatio; internal set => Set(ref _AspectRatio, value, false); } // false: Updates FlyleafHost's ratio from renderer internal AspectRatio _AspectRatio, aspectRatio; @@ -127,15 +127,17 @@ internal void Reset() } internal void Refresh() { + /* TBR + * We call this at least twice (OnOpen and OnCodecChanged) + * To avoid keeping this updated twice+ should fix media streams (maybe fill once after analysed) + * Should also clarify AVStream values vs Codec/Renderer's that we need to keep here + */ if (decoder.VideoStream == null) { Reset(); return; } streamIndex = decoder.VideoStream.StreamIndex; codec = decoder.VideoStream.Codec; - aspectRatio = decoder.VideoStream.GetDAR(); fps = decoder.VideoStream.FPS; pixelFormat = decoder.VideoStream.PixelFormatStr; - width = (int)decoder.VideoStream.Width; - height = (int)decoder.VideoStream.Height; framesTotal = decoder.VideoStream.TotalFrames; videoAcceleration = decoder.VideoDecoder.VideoAccelerated; @@ -146,6 +148,20 @@ internal void Refresh() framesDisplayed = 0; framesDropped = 0; + var renderer = player.renderer; + if (renderer != null && renderer.DAR.Value > 0) + { + aspectRatio = renderer.DAR; + width = (int)renderer.VisibleWidth; + height = (int)renderer.VisibleHeight; + } + else + { + width = (int)decoder.VideoStream.Width; + height = (int)decoder.VideoStream.Height; + aspectRatio = decoder.VideoStream.GetDAR(); + } + player.UIAdd(uiAction); }